C#:System.Net.Sockets.TcpListener有时没有正确关闭Socket

时间:2014-06-30 11:52:53

标签: 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)检查套接字的状态时,我可以看到套接字仍然处于LISTENING状态。由此产生的问题是,当我再次重新启动HTTP服务器时,我得到一个带有消息&#34的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? 不幸的是,这种方法对我来说不起作用(它改变了关于侦听套接字状态的任何内容)。

由于我对我能做的其他事情有点无能为力,我在这里问是否有人知道可能导致所述问题的原因或解决方法。任何帮助将不胜感激。

亲切的问候, 克里斯

4 个答案:

答案 0 :(得分:2)

您应该几乎总是单独留下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();
}

答案 1 :(得分:1)

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

TcpListener类足以提供类似

的方法

Start(), AcceptTcpClient()Stop()

您可以创建List<TcpClient>并循环浏览每个客户端,并在client.close()实例上调用Stop()后致电TcpListener

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

此致

答案 2 :(得分:-1)

关闭它们后,我会处理你的套接字连接。文档说他们应该在关闭后处理,但我个人想说什么时候处理任何使用IDisposable接口的东西。

答案 3 :(得分:-1)

在服务器套接字上,关闭&amp;监听套接字不需要断开连接。只有连接的套接字才需要这些调用。用以下代码替换插座停止:

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

}