我有一个可以处理多个客户端的UDP服务器,现在关于UDP的主要问题是它的连接较少,所以当我收到以下错误时我感到非常惊讶:
远程主机强行关闭现有连接。
我很快就知道这是因为我试图发送到已关闭的IPEndpoint。后来我了解到这是因为网络层将发回ICMP消息,说明端口已关闭,ICMP消息是抛出错误的原因。现在显然我开始寻找这个问题的解决方案但是,虽然我在堆栈溢出时发现了很多关于这个的问题,但我找不到一个有正确答案的问题。 (有些人甚至有0个答案)。
当我收到此错误时,我将不再收到任何内容,因为在抛出异常后我的BeginReceiveFrom
方法在try部分中。然后我将它放在catch部分中,但这只会导致再次抛出相同的错误。
所以问题是,一旦出现错误:“远程主机强行关闭现有连接。”扔了我不能再使用套接字了(或者在我看来)
我的问题是:如何处理此异常,以便我的服务器可以继续运行?
这是我的代码:
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);
}
}
}
我很乐意在需要时提供额外信息,并希望能够解决这个问题。
microsoft提供的错误描述:
WSAECONNRESET 10054通过对等方重置连接。现有连接 被远程主机强行关闭。这通常会导致 远程主机上的对等应用程序突然停止,主机是 重新启动,主机或远程网络接口被禁用,或者 远程主机使用硬关闭(有关更多信息,请参阅setsockopt) 远程套接字上的SO_LINGER选项)。也可能导致此错误 如果由于保持活动检测到连接而断开连接 一个或多个操作正在进行时失败。操作 正在进行WSAENETRESET失败。后续操作失败 与WSAECONNRESET。
答案 0 :(得分:0)
我已经找到了解决方法,即以下代码:
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);
}
}