C#UDP服务器异步多客户端|客户端断开连接时出现SocketException

时间:2014-05-11 17:58:49

标签: c# sockets udp

我一直在研究C#中的套接字服务器程序(我的灵感来自于这个post),我的问题是当客户端断开异常时“现有连接被强制关闭了当呼叫 EndReceiveFrom()并返回 0 时,会显示远程主机 ref clientEP 会成为客户端正常关闭。我不明白为什么我的 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

更新 我试图在另一台电脑上运行我的客户端(netcat)并且不再出现异常,即使 SendTo(),这在我的 clientList 中删除我的客户端也存在问题。 我仍然不明白发生了什么。

1 个答案:

答案 0 :(得分:0)

一切都应该如此。

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

在您的实施中,您必须调用 EndDo(),这将阻止,直到发生以下两种情况之一:您调用的对象 BeginDo( ),实际上某事,或者它会抛出异常。正如您在客户端断开连接时所做的那样。

Async方法上的source

整个工作需要做的是

  1. 确保正确处理该客户端断开连接的异常
  2. 无论 EndReceiveFrom 的完成方式如何,请务必致电 BeginReceiveFrom 。最好在致电 EndReceiveFrom 后立即致电 BeginReceiveFrom 。这是必需的,因为当你的服务器介于这些调用之间时,它实际上并没有监听套接字。
  3. 我会在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)
        {
        }
    }