如何在断开连接后重新使用或重新连接到同一端口上的套接字?

7

我正在做一个简单的项目,涉及到一个服务器程序和一个客户端程序。需要检查客户端是否连接(从服务器端),反之亦然。

当客户端失去互联网连接时,服务器需要知道它已经断开连接。然后,当客户端恢复互联网连接时,需要重新连接到服务器。

当客户端失去互联网连接并恢复互联网连接时,我无法使用相同的端口重新连接。

我尝试保持服务器监听并不关闭套接字,但也没有起作用。我已经尝试了许多有关套接字重用的属性,并且还尝试了残留的东西。

我发现它可能会被操作系统在注册表中设置的一些TIME_WAIT属性所卡住(在Windows的情况下)。我的问题是,能否在客户端失去和恢复互联网连接后仍然使用相同的套接字(更重要的是相同的端口)进行重新连接并继续监听等待重新连接。

就像我说的,我可以在服务器端和客户端检测到它何时断开连接,但是当我尝试使用相同的端口和相同的套接字或重新启动的套接字重新连接时,它仍然无法连接并且根本不会显示。有什么建议可以帮助解决这个问题吗?我已经搜索了很长时间来解决这个问题。

场景:

  1. 服务器正在监听
  2. 客户端连接到服务器
  3. 拔掉客户端的互联网连接
  4. 将客户端互联网电缆重新插入
  5. 客户端自动重新连接到服务器(当前无法在同一端口上执行此操作)

TL;DR:在客户端-服务器模型中失去和恢复互联网,但无法使用相同的套接字和端口连接到服务器。

代码:

    private void button2_Click(object sender, EventArgs e)
    {

        // This will stop the threads/connections and toggle the button back to its original state
        if (button2.Text == "Stop Listening")
        {

            listener.Close();
            stop = true;
            threadsActive = false;
            button2.Text = "Start Listening";
            textBox1.AppendText("Manually Closed Threads/Connections" + Environment.NewLine);

        }
        else
        {

            listenThread = new Thread(listenLoop);
            listenThread.IsBackground = true;
            status = new Thread(checkIfOnline);
            status.IsBackground = true;
            stop = false;
            threadsActive = true;
            button2.Text = "Stop Listening";
            localEndPoint = new IPEndPoint(IPAddress.Parse("129.59.79.65"), 3000);
            listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            listenThread.Start();
            status.Start();

        }

    }

====================================================

private void listenLoop()
    {

        try
        {

            listener.Bind(localEndPoint);
            listener.Listen(100);

            textBox1.AppendText("Waiting for a client..." + Environment.NewLine);

            listener = listener.Accept();
            textBox1.AppendText("Client Connected!!" + Environment.NewLine);

            status.Start();

            while (!close)
            {

                if (stop)
                    return;
                // server connection loop

            }

            if(close)
                return;

        }
        catch (Exception excp)
        {



        }

    }

====================================================

private void ResetSocket()
    {

        // stop all threads and connections
        stop = true;
        listener.Close();

        textBox1.AppendText("Attempting to kill threads..." + Environment.NewLine);
        //while (listenThread.IsAlive == true || status.IsAlive == true) { /*loop until the threads are killed*/ textBox1.AppendText("Closing Threads..."); }
        //listener.Close();
        threadsActive = false;

        textBox1.AppendText("All Threads/Connections Closed" + Environment.NewLine + "Restarting Threads/Connections..." + Environment.NewLine);

        // re-establish and start threads and connections again
        stop = false;
        listenThread = new Thread(listenLoop);
        listenThread.IsBackground = true;
        status = new Thread(checkIfOnline);
        status.IsBackground = true;
        threadsActive = true;
        localEndPoint = new IPEndPoint(IPAddress.Parse("129.59.79.65"), 3000);
        listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        listenThread.Start();
        status.Start();

        textBox1.AppendText("Threads/Connections Restarted Successfully" + Environment.NewLine);

    }

你能提供一些监听端口的代码吗?通常情况下,当客户端连接时,你会监听端口并创建一个新的套接字。 - Rhys Bevilaqua
你有阅读这里的文档吗:http://msdn.microsoft.com/en-us/library/fx6588te(v=vs.110).aspx 这段代码将允许你很好地处理重新连接。 - Rhys Bevilaqua
定义“无法重新连接到相同的端口”。相反会发生什么? - user207421
3个回答

4
不要绑定客户端套接字。只有服务器端口很重要。
在服务器端,只需保持监听套接字的监听。当监听套接字接受连接时,它会返回一个代表该连接的新套接字。当检测到连接丢失时,关闭相应的连接套接字但保持监听套接字运行。
顺便说一下,我认为检测连接丢失的最佳方法是定期发送数据(双向)。
我在我的博客上有一个 TCP/IP .NET FAQ。这是.NET,但通用概念适用于任何TCP/IP场景。

我按照你说的做了,看起来好像可以工作了。我修复的函数已经发布在这个Dropbox链接中(除了本问题提到的部分,可能还有其他主题的问题,但是我需要帮助的部分已经修复):https://dl.dropboxusercontent.com/u/12438255/simple_server/functions.zip - Zach
你好,我知道这篇文章已经有些年头了,但我想知道绑定客户端套接字是否是问题的一部分。在我的情况下,最好也将客户端套接字绑定到特定的端口,因为我有来自同一台机器的不同客户端尝试连接并需要区分它们。 - snus74
@snus74:你不需要显式地绑定端口来区分它们。一旦建立连接,操作系统将为您分配一个端口,您可以使用该端口来区分它们。 - Stephen Cleary
我需要在执行时间之前进行区分,所以唯一的方法是分配一个端口。我的问题是,在关闭和重新打开连接时,关于端口复用是否会出现问题。 - snus74
如果需要重复使用特定端口,则可以使用“reuse”选项;否则,在绑定新的套接字时,需要使用新的端口。附带说明一下,请确保您的代码不依赖于临时范围内的特定端口。 - Stephen Cleary

1
为了详细说明Stephen Cleary的出色回答。我们创建并绑定后,服务器开始监听。控制进入无限循环。客户端连接到服务器。如果我们现在拔掉互联网,客户端会抛出异常,将我们返回到无限循环的顶部,在那里我们等待网络连接再尝试接受另一个连接。
private void ListenForIncomingHttpRequests()
{
    AwaitNetworkAvailability();

    // create
    var serverSocket = 
        new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    // bind
    var localEndpoint = new IPEndPoint(IPAddress.Any, port: 12000);
    serverSocket.Bind(localEndpoint);

    // listen
    serverSocket.Listen(backlog: 25);

    while (true)
    {
        AwaitNetworkAvailability();

        try
        {
            // client connects to the server
            using (var clientSocket = serverSocket.Accept())
            {
                ProcessServerSocketRequest(clientSocket);
            }
        }
        catch (Exception ex)
        {
            _logger.Write(ex.ToString());
        }
    }
}

0

这篇文章似乎清楚地解释了原则: 迭代服务器程序中的主动和被动关闭

程序A发起主动关闭,而程序B发起被动关闭。当一个程序调用关闭套接字函数时,TCP协议层会发送一个称为FIN(结束)的段。当程序B接收到最终确认段时,它知道所有数据已经成功传输,并且程序A已经接收并处理了FIN段。程序B的TCP协议层可以安全地删除被程序套接字占用的资源。程序A的TCP协议层向它从程序B接收到的FIN段发送确认,但是程序A的TCP协议层不知道该ACK段是否到达了程序B的TCP协议层。它必须等待一定时间来查看程序B是否重传了FIN段,表明程序B从程序A没有接收到最终的ACK段。在这种情况下,程序A必须能够重新传输最终的ACK段。在此时间段过去之前,程序A套接字不能被释放。该时间段定义为两倍的最大段寿命,通常在1到4分钟范围内,具体取决于TCP实现。

1
你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接