简体   繁体   中英

C# UDP Server Asynchronous Multiple Clients | SocketException When Client Disconnect

I've been working on a socket server program in C# (I was inspired from this post ) and my problem is that when a client disconnects an exception " An existing connection was forcibly closed by the remote host " appears when the call EndReceiveFrom() and returns 0 , the ref clientEP becomes the client normally close. I don't understand why my DoReceiveFrom() function is called if there is nothing to read. I probably missed something. What is wrong ?

Problem appear there :

int dataLen = this.serverSocket.EndReceiveFrom(iar, ref clientEP);

The full source code:

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();
    }
}

Exception:

An existing connection was forcibly closed by the remote host

Update: I tried to run my client (netcat) on another pc and the exception no longer appears, even when SendTo() , which is also problematic to delete my client in my clientList . I still do not understand what is happening.

Everything is as it should be.

This is the way all Async methods work: you invoke BeginDo() and pass into it your implementation of AsyncCallback delegate (in your example that's DoReceiveFrom ). You implementation starts executing immediately after that - BeginDo() is not a blocking call.

Inside your implementation, you must call EndDo() , which will block until one of two things happen: the object, on which you invoked BeginDo() , actually does something, or it throws an exception doing it. As it does in your case when client disconnects.

The source on the Async method.

What you need to do for the whole thing to work is

  1. Make sure that you handle that client-disconnected exception properly
  2. Make sure that you call BeginReceiveFrom regardless of the way EndReceiveFrom finishes. And preferably, call BeginReceiveFrom immediately after you call EndReceiveFrom . This is needed because while you server is in-between those calls, it does not actually listen to the socket.

I would put another try-catch around EndReceiveFrom.

UPDATE :

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)
    {
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM