c# UDP服务器异步多客户端|客户端断开连接时的SocketException
本文关键字:客户端 连接 SocketException 断开 服务器 异步 UDP | 更新日期: 2023-09-27 18:02:43
我一直在c#的套接字服务器程序上工作(我从这篇文章中得到了启发),我的问题是,当客户端断开异常"一个现有的连接被远程主机强制关闭"出现在调用EndReceiveFrom()并返回0时,ref clienttep 成为客户端正常关闭。我不明白为什么我的DoReceiveFrom()函数被调用,如果没有什么可读的。我可能漏掉了什么。怎么了?
出现问题:
int dataLen = this.serverSocket.EndReceiveFrom(iar, ref clientEP);
完整源代码:
class UDPServer
{
private Socket serverSocket = null;
private List<EndPoint> clientList = new List<EndPoint>();
private List<Tuple<EndPoint, byte[]>> dataList = new List<Tuple<EndPoint, byte[]>>();
private byte[] byteData = new byte[1024];
private int port = 4242;
public List<Tuple<EndPoint, byte[]>> DataList
{
private set { this.dataList = value; }
get { return (this.dataList); }
}
public UDPServer(int port)
{
this.port = port;
}
public void Start()
{
this.serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
this.serverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
this.serverSocket.Bind(new IPEndPoint(IPAddress.Any, this.port));
EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
this.serverSocket.BeginReceiveFrom(this.byteData, 0, this.byteData.Length, SocketFlags.None, ref newClientEP, DoReceiveFrom, newClientEP);
}
private void DoReceiveFrom(IAsyncResult iar)
{
try
{
EndPoint clientEP = new IPEndPoint(IPAddress.Any, 0);
int dataLen = this.serverSocket.EndReceiveFrom(iar, ref clientEP);
byte[] data = new byte[dataLen];
Array.Copy(this.byteData, data, dataLen);
if (!this.clientList.Any(client => client.Equals(clientEP)))
this.clientList.Add(clientEP);
EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
this.serverSocket.BeginReceiveFrom(this.byteData, 0, this.byteData.Length, SocketFlags.None, ref newClientEP, DoReceiveFrom, newClientEP);
DataList.Add(Tuple.Create(clientEP, data));
}
catch (ObjectDisposedException)
{
}
}
public void SendTo(byte[] data, EndPoint clientEP)
{
try
{
this.serverSocket.SendTo(data, clientEP);
}
catch (System.Net.Sockets.SocketException)
{
this.clientList.Remove(clientEP);
}
}
public void SendToAll(byte[] data)
{
foreach (var client in this.clientList)
{
this.SendTo(data, client);
}
}
public void Stop()
{
this.serverSocket.Close();
this.serverSocket = null;
this.dataList.Clear();
this.clientList.Clear();
}
}
异常:An existing connection was forcibly closed by the remote host
更新:我试图在另一台pc上运行我的客户端(netcat),即使在SendTo()时也不再出现异常,这也是在我的clientList中删除我的客户端的问题。我还是不明白发生了什么。
一切顺理成章
这是所有 Async方法的工作方式:您调用BeginDo()并将AsyncCallback委托的实现传递给它(在您的示例中是DoReceiveFrom)。你的实现在那之后立即开始执行- BeginDo()不是阻塞调用。
在你的实现中,你必须调用EndDo(),它将阻塞,直到以下两种情况之一发生:你调用BeginDo()的对象实际上做了的事情,或者它抛出了一个异常。当客户端断开连接时,
Async方法的源代码。
你需要做的是
- 确保正确处理客户端断开连接的异常
- 确保你调用BeginReceiveFrom不管EndReceiveFrom结束的方式。最好在调用EndReceiveFrom之后立即调用BeginReceiveFrom。这是必需的,因为当您的服务器处于这些调用之间时,它实际上并不侦听套接字。
我将在EndReceiveFrom周围放置另一个try-catch。
:
private void DoReceiveFrom(IAsyncResult iar)
{
try
{
EndPoint clientEP = new IPEndPoint(IPAddress.Any, 0);
int dataLen = 0;
byte[] data = null;
try
{
dataLen = this.serverSocket.EndReceiveFrom(iar, ref clientEP);
data = new byte[dataLen];
Array.Copy(this.byteData, data, dataLen);
}
catch(Exception e)
{
}
finally
{
EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
this.serverSocket.BeginReceiveFrom(this.byteData, 0, this.byteData.Length, SocketFlags.None, ref newClientEP, DoReceiveFrom, newClientEP);
}
if (!this.clientList.Any(client => client.Equals(clientEP)))
this.clientList.Add(clientEP);
DataList.Add(Tuple.Create(clientEP, data));
}
catch (ObjectDisposedException)
{
}
}