C#:System.Net.Sockets.TcpListener 有时没有正确关闭套接字

标签 c# sockets tcp tcplistener

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

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,有时一开始似乎可以解决问题,但几天后问题又出现了。

我还尝试在使用 iphlpapi.dll 中的 GetTcpTable(...) 和 SetTcpEntry(...) 停止服务器时“强制”关闭监听套接字,如以下问题所述:How to close a TCP connection by port? 不幸的是,这种方法对我不起作用(它完全改变了监听套接字的状态)。

由于我对我还能做些什么一无所知,我想问问是否有人知道可能导致所描述问题的原因或如何解决它。任何帮助将不胜感激。

亲切的问候, 克里斯

最佳答案

您几乎应该始终不理会 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();
}

关于C#:System.Net.Sockets.TcpListener 有时没有正确关闭套接字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24489539/

相关文章:

c# - C#源代码分析工具

c# - 如何创建一个圆形样式的 ProgressBar

sockets - 套接字接收(阻塞)失败并出现 errno EAGAIN - 资源暂时不可用

java - 如何从 apache mina 服务器的 MessageRecieved API 连接到另一台服务器

http - 谁负责 OSI 中的表示层和 session 层?

c# - 日期时间转换异常

c# - c# 是否有像缓存一样工作的集合?

c套接字多个send/recv调用

iphone - 从Inputstream读取Objective-C

sockets - 在我的 Node.JS 服务器负载很重的情况下,出现 ECONNRESET 套接字错误?