【问题标题】:C# UDP Server Asynchronous Multiple Clients | SocketException When Client DisconnectC# UDP 服务器异步多客户端 |客户端断开连接时的 SocketException
【发布时间】:2014-06-29 01:10:32
【问题描述】:

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

【问题讨论】:

  • 遇到同样的问题,你解决了吗?
  • 我认为,在关闭时,调用 DoReceiveFrom 是一种返回用户代码的方法,错误代码(断开连接)(可能)被存储,当调用 EndReceiveFrom 时,它将检查错误代码,如果错误代码 0 则将抛出异常。 (但是..就像我说的,只是猜测)我宁愿不要出现异常,断开连接不应该是异常。

标签: c# sockets udp


【解决方案1】:

一切都应该如此。

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

在您的实现中,您必须调用 EndDo(),这将 阻塞,直到发生以下两种情况之一:您调用 BeginDo( ),实际上某事,或者它抛出异常。就像客户端断开连接时的情况一样。

Async 方法上的source

你需要做的是让整个事情发挥作用

  1. 确保正确处理客户端断开连接的异常
  2. 无论 EndReceiveFrom 以何种方式结束,请确保调用 BeginReceiveFrom。最好在调用 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)
    {
    }
}

【讨论】:

  • 感谢您的回答,但问题依旧。
  • @user3626081,好的,但你至少明白为什么在没有可读取的内容时调用 DoReceiveFrom() 函数吗?另外,我用代码示例更新了我的答案 - 看看这是否对你有帮助
猜你喜欢
  • 2015-06-26
  • 2016-02-16
  • 1970-01-01
  • 2012-03-27
  • 2020-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-18
相关资源
最近更新 更多