如何优雅地关闭异步服务器套接字?C#.

本文关键字:服务器 套接字 异步 何优雅 | 更新日期: 2023-09-27 18:34:13

我已经看到了很多关于在没有对象处置异常的情况下处理套接字的问题,所以我决定尝试一下,看看是否可以做到。以下是我的发现。

问题?

您有一段代码使用来自 System.Net.Sockets 的套接字,该套接字是服务器套接字。问题是你想关闭套接字,每次你尝试时你都会得到一个ObjectDisposedException。

您的代码可能如下所示:

    private static ManualResetEvent allDone = new ManualResetEvent(false);
    private Socket ear;
    public void Start()
    {
        new Thread(() => StartListening()).Start();
    }
    private void StartListening()
    {
        IP = Dns.GetHostAddresses(Dns.GetHostName()).Where(address => address.AddressFamily == AddressFamily.InterNetwork).First();
        port = 11221;
        IPEndPoint localEndPoint = new IPEndPoint(IP, Port);
        ear = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        ear.Bind(localEndPoint);
        ear.Listen(100);
        try
        {
            while (true)
            {
                allDone.Reset();
                ear.BeginAccept(new AsyncCallback(AcceptCallback), ear);
                allDone.WaitOne();
            }
        }
        catch (ObjectDisposedException e)
        {
            Console.WriteLine("Socket Closed");
        }
    }
    private void AcceptCallback(IAsyncResult ar)
    {
        allDone.Set();
        Socket listener = (Socket)ar.AsyncState;
        Socket handler = listener.EndAccept(ar);
        StateObject state = new StateObject();
        state.workSocket = handler;
        handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
    }
    public static void ReadCallback(IAsyncResult ar) 
    {
        String content = String.Empty;
        StateObject state = (StateObject) ar.AsyncState;
        Socket handler = state.workSocket;
        int bytesRead = handler.EndReceive(ar);
        if (bytesRead > 0) {
            state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));
            content = state.sb.ToString();
            if (content.IndexOf("<EOF>") > -1) {
                Console.WriteLine("Read {0} bytes from socket. 'n Data : {1}", content.Length, content );
            } 
            else 
            {
                handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                new AsyncCallback(ReadCallback), state);
            }
        }
    }
    public void Stop()
    {
        ear.Close();
    }

运行类似于上面的代码会在尝试结束接收的 AcceptCallback 中出现错误。即使你发现了这一点,你仍然会在 StartListening() 方法中得到一个错误。

如何优雅地关闭异步服务器套接字?C#.

所以这是我修复它的方法:

首先,我添加了一个名为 IsListenening 的布尔值。

接下来,我改变了让耳朵听 While(IsListenening) 的条件。

在 stop 方法中,我在调用 ear.close() 之前将 IsListen 变量设置为 false,最后在手动 SetEvent 之后检查 AcceptCallback 内部的耳朵。

最终结果如下所示:

    private static ManualResetEvent allDone = new ManualResetEvent(false);
    private Socket ear;
    public void Start()
    {
        new Thread(() => StartListening()).Start();
    }
    private void StartListening()
    {
        IP = Dns.GetHostAddresses(Dns.GetHostName()).Where(address => address.AddressFamily == AddressFamily.InterNetwork).First();
        port = 11221;
        IPEndPoint localEndPoint = new IPEndPoint(IP, Port);
        ear = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        ear.Bind(localEndPoint);
        ear.Listen(100);
        IsListening = true;
        while (IsListening)
        {
            allDone.Reset();
            ear.BeginAccept(new AsyncCallback(AcceptCallback), ear);
            allDone.WaitOne();
        }
        Console.WriteLine("Socket Closed");
    }
    private void AcceptCallback(IAsyncResult ar)
    {
        allDone.Set();
        if (IsListening == false) return;
        Socket listener = (Socket)ar.AsyncState;
        Socket handler = listener.EndAccept(ar);
        StateObject state = new StateObject();
        state.workSocket = handler;
        handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
    }
    public static void ReadCallback(IAsyncResult ar) 
    {
        String content = String.Empty;
        StateObject state = (StateObject) ar.AsyncState;
        Socket handler = state.workSocket;
        int bytesRead = handler.EndReceive(ar);
        if (bytesRead > 0) {
            state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));
            content = state.sb.ToString();
            if (content.IndexOf("<EOF>") > -1) {
                Console.WriteLine("Read {0} bytes from socket. 'n Data : {1}", content.Length, content );
            } 
            else 
            {
                handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                new AsyncCallback(ReadCallback), state);
            }
        }
    }
    public void Stop()
    {
        IsListening = false;
        ear.Close();
    }

例外现在消失了。我希望这对其他人有所帮助!