tcplistener有时不能正确关闭Socket

本文关键字:Socket 不能 tcplistener | 更新日期: 2023-09-27 18:10:44

在我的一个项目中,我实现了一个小型HTTP服务器来流式传输连接的网络摄像头的视频数据。对于这项任务,我利用。net Framework 4.5中的System.Net.Sockets.TcpListener,它侦听预配置的端点并使用AcceptSocketAsync()方法等待传入的请求。您可以在下面看到相关的代码部分:

this.server = new TcpListener(endpoint);
this.server.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, new LingerOption(true, 0));
this.server.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
this.server.Start();
...
this.listenTask = Task.Factory.StartNew(this.Listen);
...
private async void Listen()
{
    try
    {
        while (this.server.Server.IsBound)
        {
            Socket socket = await this.server.AcceptSocketAsync();
            if (socket == null)
            {
                break;
            }
            Task.Factory.StartNew(() => this.ClientThread(socket));
        }
    }
    catch (ObjectDisposedException)
    {
        Log.Debug("Media HttpServer closed.");
    }
}

这工作正常,当我启动应用程序和HTTP服务器是第一次启动。但是,当我停止HTTP服务器(通过应用程序设置中的CheckBox完成)时,底层侦听套接字有时不会关闭。当我通过控制台(netstat -ano)检查套接字的状态时,我可以看到套接字仍然处于侦听状态。由此产生的问题是,当我重新启动HTTP服务器时,我得到System.Net.Sockets.SocketException消息"每个套接字地址通常只允许使用一次",这并不奇怪。

停止HTTP服务的相关代码部分如下:

...
if (this.server != null)
{
    if (this.server.Server != null)
    {
        if (this.server.Server.Connected)
        {
            this.server.Server.Shutdown(SocketShutdown.Both);
            this.server.Server.Disconnect(true);
        }
        this.server.Server.Close();
    }
    this.server.Stop();
}
...

我还跟踪我打开的连接,并在完成数据传输和停止服务器时关闭它们。没有一个连接套接字保持打开状态,所以我认为只有侦听套接字应该与此问题相关。

在停止服务器时,我已经尝试了Shutdown(), Disconnect(), Close()和Stop()方法的各种组合/顺序,以及在启动Linger和ReuseAddress等服务器时设置/取消设置几个选项,这有时似乎一开始就解决了问题,但几天后问题又发生了。

我还试图"强制"监听套接字关闭时,使用getcptable(…)和SetTcpEntry(…)从iphlpapi.dll停止服务器,如在这个问题中所述:如何关闭TCP连接端口?不幸的是,这种方法对我不起作用(它根本没有改变侦听套接字的状态)。

因为我有点不知道我还能做什么,所以我在这里问一下,如果有人知道什么可能导致所描述的问题或如何解决它。如有任何帮助,我将不胜感激。

亲切的问候,克里斯。

tcplistener有时不能正确关闭Socket

您几乎应该始终让TcpListener.Server单独存在。你可以在上面设置套接字选项,而不是用它来控制。

所以你的Listen应该是:
private async void Listen()
{
    try
    {
        while (true)
        {
            Socket socket = await this.server.AcceptSocketAsync();
            if (socket == null)
            {
                break;
            }
            Task.Run(() => this.ClientThread(socket));
        }
    }
    catch (ObjectDisposedException)
    {
        Log.Debug("Media HttpServer closed.");
    }
}

(假设您确实需要每个客户端一个线程,我通常不推荐这种架构)。

当你想停下来的时候,就停下来:

if (this.server != null)
{
    this.server.Stop();
}

如果您没有特殊要求,建议仅使用TcpListener类及其方法,或者如果您有特殊要求,建议不使用TcpListener,从Raw套接字开始。

TcpListener类是自满足的,提供类似

的方法

Start(), AcceptTcpClient() and Stop() .

可以创建一个List<TcpClient>,循环遍历每个客户端,在TcpListener实例上调用Stop()后再调用client.close()

一个很好的客户端服务器通信的例子是在MSDN:http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener (v = vs.110) . aspx

服务端socket已关闭&监听套接字不需要断开连接。只有连接的套接字才需要这些调用。用下面的代码替换套接字停止:

if (this.server != null)
{
    if (this.server.Server != null)
    {
        this.server.Server.Close();
        this.server.Server = NULL;
    }
}

一旦您关闭了套接字连接,我就会处理掉它们。文档说它们应该在关闭后处理,但我个人喜欢说什么时候处理使用IDisposable接口的任何东西。