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中删除我的客户端的问题。我还是不明白发生了什么。

c# UDP服务器异步多客户端|客户端断开连接时的SocketException

一切顺理成章

这是所有 Async方法的工作方式:您调用BeginDo()并将AsyncCallback委托的实现传递给它(在您的示例中是DoReceiveFrom)。你的实现在那之后立即开始执行- BeginDo()不是阻塞调用。

在你的实现中,你必须调用EndDo(),它将阻塞,直到以下两种情况之一发生:你调用BeginDo()的对象实际上做了的事情,或者它抛出了一个异常。当客户端断开连接时,

Async方法的源代码。

你需要做的是

  1. 确保正确处理客户端断开连接的异常
  2. 确保你调用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)
    {
    }
}