多客户端 udp 服务器正确处理错误代码 10054
Multiclient udp server handling error code 10054 properly
我有一个可以处理多个客户端的 UDP 服务器,现在关于 UDP 的主要问题是它连接较少,所以当我收到以下错误时我感到非常惊讶:
An existing connection was forcibly closed by the remote host.
我很快了解到这是因为我试图发送到一个已关闭的 IPEndpoint。后来才知道这是因为网络层会发回一个ICMP报文说端口已经关闭,ICMP报文就是报错的原因。现在显然我开始寻找这个问题的解决方案,但是,虽然我在堆栈溢出上发现了很多关于这个的问题,但我找不到一个有正确答案的问题。 (有些甚至有 0 个答案)。
当我得到这个错误时,我不会再收到任何东西,因为我的 BeginReceiveFrom
方法在抛出异常后位于 try 部分。然后我也将它放在 catch 部分,但这只会导致再次抛出相同的错误。
所以问题是,一旦抛出错误:"An existing connection was forcibly closed by the remote host.",我就不能再使用套接字了(或者在我看来是这样)
我的问题是:我该如何处理这个异常,这样我的服务器才能保持 运行?
这是我的代码:
public void Listen()
{
if (mDisposing == true)
{
throw new ObjectDisposedException(null, "This instance is already disposed");
}
if (mListening == false)
{
try
{
mListening = true;
ServerEndPoint = new IPEndPoint(ServerAddress, Port);
mServerSocket = new Socket(ServerAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
mServerSocket.Bind(ServerEndPoint);
if (ServerAddress.AddressFamily == AddressFamily.InterNetworkV6)
{
OperatingSystem os = Environment.OSVersion;
Version version = os.Version;
// NOTE: Windows Vista or higher have one IP stack for IPv4 and IPv6
// Therefore they can be combined and used as one socket for IPv6
// The socket must then accept both IPv4 and IPv6 connections.
if (version.Major > 5)
{
// NOTE: IPV6_V6ONLY socket option is equivalent to 27 in the winsock snippet below
// This is available in Framework 4.0. A lower version can implement (SocketOptionName)27
mServerSocket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0);
}
}
var ipeSender = new IPEndPoint(IPAddress.Any, 0);
var endPointSender = (EndPoint)ipeSender;
mServerSocket.BeginReceiveFrom(mByteData, 0, mByteData.Length, SocketFlags.None, ref endPointSender, new AsyncCallback(OnDataReceived), null);
}
catch (Exception exception)
{
mListening = false;
DoError(exception);
}
}
else
{
var ipeSender = new IPEndPoint(IPAddress.Any, 0);
var endPointSender = (EndPoint)ipeSender;
mServerSocket.BeginReceiveFrom(mByteData, 0, mByteData.Length, SocketFlags.None, ref endPointSender, new AsyncCallback(OnDataReceived), null);
}
}
public void Close()
{
if (mDisposing == true)
{
throw new ObjectDisposedException(null, "This instance is already disposed");
}
if (mListening == true)
{
mListening = false;
try
{
foreach (ClientInformation client in mClients)
{
Disconnect(client.ID);
}
if (mServerSocket != null)
{
mServerSocket.Close();
}
}
catch (Exception exception)
{
DoError(exception);
}
}
}
private void WaitForData()
{
if (mListening == true)
{
try
{
var ipeSender = new IPEndPoint(IPAddress.Any, 0);
var endPointSender = (EndPoint)ipeSender;
mServerSocket.BeginReceiveFrom(mByteData, 0, mByteData.Length, SocketFlags.None, ref endPointSender, new AsyncCallback(OnDataReceived), null);
}
catch (Exception exception)
{
DoError(exception);
}
}
}
private void OnDataReceived(IAsyncResult asyncResult)
{
if (mListening == true)
{
try
{
IPEndPoint ipeSender = new IPEndPoint(IPAddress.Any, 0);
EndPoint remoteEndPoint = ipeSender;
int iRx = mServerSocket.EndReceiveFrom(asyncResult, ref remoteEndPoint);
var clientInfo = new ClientInformation(remoteEndPoint);
mClients.Add(clientInfo);
var chars = new byte[iRx];
Buffer.BlockCopy(mByteData, 0, chars, 0, iRx);
WaitForData();
DoReceived(clientInfo, chars);
}
catch (Exception exception)
{
WaitForData();
DoError(exception);
}
}
}
public void Send(string remoteEndPoint, byte[] data)
{
if (mListening == true)
{
var clientInfo = ActiveConnections.Find(remoteEndPoint);
if (clientInfo != null)
{
try
{
lock (LockSend)
{
clientInfo.DataOut = data;
mServerSocket.BeginSendTo(
clientInfo.DataOut,
0,
clientInfo.DataOut.Length,
SocketFlags.None,
clientInfo.RemoteEndPoint,
new AsyncCallback(OnDataSent),
clientInfo);
}
}
catch (Exception exception)
{
DoError(exception);
}
}
else
{
mLogger.ErrorFormat("Trying to send to client {0} which does not exist", remoteEndPoint);
}
}
}
private void OnDataSent(IAsyncResult asyncResult)
{
if (mListening == true)
{
var clientInfo = (ClientInformation)asyncResult.AsyncState;
try
{
lock (LockSend)
{
int iRx = mServerSocket.EndSendTo(asyncResult);
if (iRx == clientInfo.DataOut.Length)
{
byte[] chars = new byte[iRx];
Buffer.BlockCopy(clientInfo.DataOut, 0, chars, 0, iRx);
DoSent(clientInfo, chars);
}
}
}
catch (Exception exception)
{
DoError(exception);
}
}
}
我很乐意在需要时提供额外的信息,并希望这个问题能够得到解决。
微软提供的错误描述:
WSAECONNRESET 10054 Connection reset by peer. An existing connection
was forcibly closed by the remote host. This normally results if the
peer application on the remote host is suddenly stopped, the host is
rebooted, the host or remote network interface is disabled, or the
remote host uses a hard close (see setsockopt for more information on
the SO_LINGER option on the remote socket). This error may also result
if a connection was broken due to keep-alive activity detecting a
failure while one or more operations are in progress. Operations that
were in progress fail with WSAENETRESET. Subsequent operations fail
with WSAECONNRESET.
我已经找到了解决此问题的方法,即以下代码:
var sioUdpConnectionReset = -1744830452;
var inValue = new byte[] { 0 };
var outValue = new byte[] { 0 };
mServerSocket.IOControl(sioUdpConnectionReset, inValue, outValue);
据我所知,它只是抑制了错误,我不得不在我的 Listen 方法中实现它,现在看起来像这样:
public void Listen()
{
if (mDisposing == true)
{
throw new ObjectDisposedException(null, "This instance is already disposed");
}
if (mListening == false)
{
try
{
mListening = true;
ServerEndPoint = new IPEndPoint(ServerAddress, Port);
mServerSocket = new Socket(ServerAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
var sioUdpConnectionReset = -1744830452;
var inValue = new byte[] { 0 };
var outValue = new byte[] { 0 };
mServerSocket.IOControl(sioUdpConnectionReset, inValue, outValue);
mServerSocket.Bind(ServerEndPoint);
if (ServerAddress.AddressFamily == AddressFamily.InterNetworkV6)
{
OperatingSystem os = Environment.OSVersion;
Version version = os.Version;
// NOTE: Windows Vista or higher have one IP stack for IPv4 and IPv6
// Therefore they can be combined and used as one socket for IPv6
// The socket must then accept both IPv4 and IPv6 connections.
if (version.Major > 5)
{
// NOTE: IPV6_V6ONLY socket option is equivalent to 27 in the winsock snippet below
// This is available in Framework 4.0. A lower version can implement (SocketOptionName)27
mServerSocket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0);
}
}
var ipeSender = new IPEndPoint(IPAddress.Any, 0);
var endPointSender = (EndPoint)ipeSender;
mServerSocket.BeginReceiveFrom(mByteData, 0, mByteData.Length, SocketFlags.None, ref endPointSender, new AsyncCallback(OnDataReceived), null);
}
catch (Exception exception)
{
mListening = false;
DoError(exception);
}
}
else
{
var ipeSender = new IPEndPoint(IPAddress.Any, 0);
var endPointSender = (EndPoint)ipeSender;
mServerSocket.BeginReceiveFrom(mByteData, 0, mByteData.Length, SocketFlags.None, ref endPointSender, new AsyncCallback(OnDataReceived), null);
}
}
我有一个可以处理多个客户端的 UDP 服务器,现在关于 UDP 的主要问题是它连接较少,所以当我收到以下错误时我感到非常惊讶:
An existing connection was forcibly closed by the remote host.
我很快了解到这是因为我试图发送到一个已关闭的 IPEndpoint。后来才知道这是因为网络层会发回一个ICMP报文说端口已经关闭,ICMP报文就是报错的原因。现在显然我开始寻找这个问题的解决方案,但是,虽然我在堆栈溢出上发现了很多关于这个的问题,但我找不到一个有正确答案的问题。 (有些甚至有 0 个答案)。
当我得到这个错误时,我不会再收到任何东西,因为我的 BeginReceiveFrom
方法在抛出异常后位于 try 部分。然后我也将它放在 catch 部分,但这只会导致再次抛出相同的错误。
所以问题是,一旦抛出错误:"An existing connection was forcibly closed by the remote host.",我就不能再使用套接字了(或者在我看来是这样)
我的问题是:我该如何处理这个异常,这样我的服务器才能保持 运行?
这是我的代码:
public void Listen()
{
if (mDisposing == true)
{
throw new ObjectDisposedException(null, "This instance is already disposed");
}
if (mListening == false)
{
try
{
mListening = true;
ServerEndPoint = new IPEndPoint(ServerAddress, Port);
mServerSocket = new Socket(ServerAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
mServerSocket.Bind(ServerEndPoint);
if (ServerAddress.AddressFamily == AddressFamily.InterNetworkV6)
{
OperatingSystem os = Environment.OSVersion;
Version version = os.Version;
// NOTE: Windows Vista or higher have one IP stack for IPv4 and IPv6
// Therefore they can be combined and used as one socket for IPv6
// The socket must then accept both IPv4 and IPv6 connections.
if (version.Major > 5)
{
// NOTE: IPV6_V6ONLY socket option is equivalent to 27 in the winsock snippet below
// This is available in Framework 4.0. A lower version can implement (SocketOptionName)27
mServerSocket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0);
}
}
var ipeSender = new IPEndPoint(IPAddress.Any, 0);
var endPointSender = (EndPoint)ipeSender;
mServerSocket.BeginReceiveFrom(mByteData, 0, mByteData.Length, SocketFlags.None, ref endPointSender, new AsyncCallback(OnDataReceived), null);
}
catch (Exception exception)
{
mListening = false;
DoError(exception);
}
}
else
{
var ipeSender = new IPEndPoint(IPAddress.Any, 0);
var endPointSender = (EndPoint)ipeSender;
mServerSocket.BeginReceiveFrom(mByteData, 0, mByteData.Length, SocketFlags.None, ref endPointSender, new AsyncCallback(OnDataReceived), null);
}
}
public void Close()
{
if (mDisposing == true)
{
throw new ObjectDisposedException(null, "This instance is already disposed");
}
if (mListening == true)
{
mListening = false;
try
{
foreach (ClientInformation client in mClients)
{
Disconnect(client.ID);
}
if (mServerSocket != null)
{
mServerSocket.Close();
}
}
catch (Exception exception)
{
DoError(exception);
}
}
}
private void WaitForData()
{
if (mListening == true)
{
try
{
var ipeSender = new IPEndPoint(IPAddress.Any, 0);
var endPointSender = (EndPoint)ipeSender;
mServerSocket.BeginReceiveFrom(mByteData, 0, mByteData.Length, SocketFlags.None, ref endPointSender, new AsyncCallback(OnDataReceived), null);
}
catch (Exception exception)
{
DoError(exception);
}
}
}
private void OnDataReceived(IAsyncResult asyncResult)
{
if (mListening == true)
{
try
{
IPEndPoint ipeSender = new IPEndPoint(IPAddress.Any, 0);
EndPoint remoteEndPoint = ipeSender;
int iRx = mServerSocket.EndReceiveFrom(asyncResult, ref remoteEndPoint);
var clientInfo = new ClientInformation(remoteEndPoint);
mClients.Add(clientInfo);
var chars = new byte[iRx];
Buffer.BlockCopy(mByteData, 0, chars, 0, iRx);
WaitForData();
DoReceived(clientInfo, chars);
}
catch (Exception exception)
{
WaitForData();
DoError(exception);
}
}
}
public void Send(string remoteEndPoint, byte[] data)
{
if (mListening == true)
{
var clientInfo = ActiveConnections.Find(remoteEndPoint);
if (clientInfo != null)
{
try
{
lock (LockSend)
{
clientInfo.DataOut = data;
mServerSocket.BeginSendTo(
clientInfo.DataOut,
0,
clientInfo.DataOut.Length,
SocketFlags.None,
clientInfo.RemoteEndPoint,
new AsyncCallback(OnDataSent),
clientInfo);
}
}
catch (Exception exception)
{
DoError(exception);
}
}
else
{
mLogger.ErrorFormat("Trying to send to client {0} which does not exist", remoteEndPoint);
}
}
}
private void OnDataSent(IAsyncResult asyncResult)
{
if (mListening == true)
{
var clientInfo = (ClientInformation)asyncResult.AsyncState;
try
{
lock (LockSend)
{
int iRx = mServerSocket.EndSendTo(asyncResult);
if (iRx == clientInfo.DataOut.Length)
{
byte[] chars = new byte[iRx];
Buffer.BlockCopy(clientInfo.DataOut, 0, chars, 0, iRx);
DoSent(clientInfo, chars);
}
}
}
catch (Exception exception)
{
DoError(exception);
}
}
}
我很乐意在需要时提供额外的信息,并希望这个问题能够得到解决。
微软提供的错误描述:
WSAECONNRESET 10054 Connection reset by peer. An existing connection was forcibly closed by the remote host. This normally results if the peer application on the remote host is suddenly stopped, the host is rebooted, the host or remote network interface is disabled, or the remote host uses a hard close (see setsockopt for more information on the SO_LINGER option on the remote socket). This error may also result if a connection was broken due to keep-alive activity detecting a failure while one or more operations are in progress. Operations that were in progress fail with WSAENETRESET. Subsequent operations fail with WSAECONNRESET.
我已经找到了解决此问题的方法,即以下代码:
var sioUdpConnectionReset = -1744830452;
var inValue = new byte[] { 0 };
var outValue = new byte[] { 0 };
mServerSocket.IOControl(sioUdpConnectionReset, inValue, outValue);
据我所知,它只是抑制了错误,我不得不在我的 Listen 方法中实现它,现在看起来像这样:
public void Listen()
{
if (mDisposing == true)
{
throw new ObjectDisposedException(null, "This instance is already disposed");
}
if (mListening == false)
{
try
{
mListening = true;
ServerEndPoint = new IPEndPoint(ServerAddress, Port);
mServerSocket = new Socket(ServerAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
var sioUdpConnectionReset = -1744830452;
var inValue = new byte[] { 0 };
var outValue = new byte[] { 0 };
mServerSocket.IOControl(sioUdpConnectionReset, inValue, outValue);
mServerSocket.Bind(ServerEndPoint);
if (ServerAddress.AddressFamily == AddressFamily.InterNetworkV6)
{
OperatingSystem os = Environment.OSVersion;
Version version = os.Version;
// NOTE: Windows Vista or higher have one IP stack for IPv4 and IPv6
// Therefore they can be combined and used as one socket for IPv6
// The socket must then accept both IPv4 and IPv6 connections.
if (version.Major > 5)
{
// NOTE: IPV6_V6ONLY socket option is equivalent to 27 in the winsock snippet below
// This is available in Framework 4.0. A lower version can implement (SocketOptionName)27
mServerSocket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0);
}
}
var ipeSender = new IPEndPoint(IPAddress.Any, 0);
var endPointSender = (EndPoint)ipeSender;
mServerSocket.BeginReceiveFrom(mByteData, 0, mByteData.Length, SocketFlags.None, ref endPointSender, new AsyncCallback(OnDataReceived), null);
}
catch (Exception exception)
{
mListening = false;
DoError(exception);
}
}
else
{
var ipeSender = new IPEndPoint(IPAddress.Any, 0);
var endPointSender = (EndPoint)ipeSender;
mServerSocket.BeginReceiveFrom(mByteData, 0, mByteData.Length, SocketFlags.None, ref endPointSender, new AsyncCallback(OnDataReceived), null);
}
}