多客户端 udp 服务器正确处理错误代码 10054
本文关键字:错误代码 10054 正确处理 服务器 客户端 udp | 更新日期: 2023-09-27 18:32:20
我有一个可以处理多个客户端的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);
}
}
}
我很乐意在需要时提供额外的信息,并希望这个问题可以解决。
微软提供的错误描述:
WSAECONNSET 10054 对等方重置连接。现有连接 被远程主机强行关闭。这通常会导致 远程主机上的对等应用程序突然停止,主机 重新引导,主机或远程网络接口被禁用,或者 远程主机使用硬关闭(有关 Setsockopt 的更多信息,请参见 远程套接字上的SO_LINGER选项)。也可能导致此错误 如果由于保持活动活动检测到 一个或多个操作正在进行时失败。操作 正在进行失败,WSAENETRESET。后续操作失败 与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);
}
}