如何优雅地重新连接到套接字 [英] How to reconnect to a socket gracefully

查看:29
本文介绍了如何优雅地重新连接到套接字的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下方法可以在我的程序启动时连接到端点

I have a following method that connects to an end point when my program starts

ChannelSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
var remoteIpAddress = IPAddress.Parse(ChannelIp);
ChannelEndPoint = new IPEndPoint(remoteIpAddress, ChannelPort);
ChannelSocket.Connect(ChannelEndPoint);

我还有一个计时器,它设置为每 60 秒触发一次以调用 CheckConnectivity,它会尝试向端点发送任意字节数组以确保连接仍然有效,并且如果发送失败,它将尝试重新连接.

I also have a timer that is set to trigger every 60 seconds to call CheckConnectivity, that attempts to send an arbitrary byte array to the end point to make sure that the connection is still alive, and if the send fails, it will attempt to reconnect.

public bool CheckConnectivity(bool isReconnect)
{
    if (ChannelSocket != null)
    {
        var blockingState = ChannelSocket.Blocking;
        try
        {
            var tmp = new byte[] { 0 };
            ChannelSocket.Blocking = false;
            ChannelSocket.Send(tmp);
        }
        catch (SocketException e)
        {
            try
            {
                ReconnectChannel();
            }
            catch (Exception ex)
            {
                return false;
            }
        }
    }
    else
    {
        ConnectivityLog.Warn(string.Format("{0}:{1} is null!", ChannelIp, ChannelPort));
        return false;
    }

    return true;
} 

private void ReconnectChannel()
{
    try
    {
        ChannelSocket.Shutdown(SocketShutdown.Both);
        ChannelSocket.Disconnect(true);
        ChannelSocket.Close();
    }
    catch (Exception ex)
    {
        ConnectivityLog.Error(ex);
    }

    ChannelSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    var remoteIpAddress = IPAddress.Parse(ChannelIp);
    ChannelEndPoint = new IPEndPoint(remoteIpAddress, ChannelPort);
    ChannelSocket.Connect(ChannelEndPoint);
    Thread.Sleep(1000);

    if (ChannelSocket.Connected)
    {
        ConnectivityLog.Info(string.Format("{0}:{1} is reconnected!", ChannelIp, ChannelPort));
    }
    else
    {
        ConnectivityLog.Warn(string.Format("{0}:{1} failed to reconnect!", ChannelIp, ChannelPort));
    }
}

因此,我如何测试上述内容,是从我的以太网设备上物理拔下 LAN 电缆,允许我的代码尝试重新连接(显然失败)并重新连接回 LAN 电缆.

So how I'd test the above, is to physically unplug the LAN cable from my ethernet device, allowing my code to attempt to reconnect (which fails obviously) and reconnect back the LAN cable.

但是,即使在重新连接 LAN 电缆(能够 ping)之后,我的 Reconnect 方法中的 ChannelSocket.Connect(ChannelEndPoint) 始终会引发此错误

However, even after reconnecting the LAN cable (able to ping), ChannelSocket.Connect(ChannelEndPoint) in my Reconnect method always throws this error

No connection could be made because the target machine actively refused it 192.168.168.160:4001

如果我要重新启动整个应用程序,它会成功连接.如何调整重新连接方法,以便不必重新启动应用程序即可重新连接回以太网设备?

If I were to restart my whole application, it connects successfully. How can I tweak my reconnect method such that I don't have to restart my application to reconnect back to my Ethernet device?

推荐答案

如果应用程序关闭了 TCP/IP 端口,则协议规定该端口在一段时间内保持 TIME_WAIT 状态(默认在 Windows 机器上为 240 秒).请参阅以下参考资料 -

If an application closes a TCP/IP port, the protocol dictates that the port stays in TIME_WAIT state for a certain duration (default of 240 seconds on a windows machine). See following for references -

http://en.wikipedia.org/wiki/Transmission_Control_Protocol

http://support.microsoft.com/kb/137984

http://www.pctools.com/guides/registry/detail/878/

这对您的场景意味着什么 - 您不能期望在短时间内(甚至几秒钟)关闭(有意或无意)并重新打开端口.尽管您可以在互联网上找到一些注册表调整.. 该端口将无法用于 Windows 上的任何应用程序,至少 30 秒.(同样,默认是 240 秒)

What this means for your scenario - is that you cannot expect to close (willingly or unwillingly) and re-open a port within a short period of time (even several seconds). Despite some registry tweaks which you'd find on internet.. the port will be un-available for any app on windows, for a minimum of 30 seconds. (Again, default is 240 seconds)

您的选择 - 这里是有限的...

Your options - here are limited...

  1. 来自 http://msdn.microsoft.com/上的文档en-us/library/4xzx2d41(v=vs.110).aspx -

"如果套接字先前已断开连接,则不能使用此 (Connect) 方法来恢复连接.使用异步 BeginConnect 方法之一重新连接.这是底层提供者的限制."

"If the socket has been previously disconnected, then you cannot use this (Connect) method to restore the connection. Use one of the asynchronous BeginConnect methods to reconnect. This is a limitation of the underlying provider."

文档建议必须使用 BeginConnect 的原因是我上面提到的..它只是不希望能够立即建立连接..因此唯一的选择是以异步方式进行调用,并在等待几分钟后建立连接时,请做好预期并计划它失败.基本上,可能不是一个理想的选择.

The reason why documentation suggests that BeginConnect must be used is what I mentioned above.. It simply doesn't expect to be able to establish the connection right away.. and hence the only option is to make the call asynchronously, and while you wait for the connection to get established in several minutes, do expect and plan for it to fail. Essentially, likely not an ideal option.

  1. 如果长时间的等待和不确定性是不可接受的,那么您的另一个选择是以某种方式在客户端和服务器之间协商不同的端口.(例如,理论上您可以使用无连接的 UDP 来协商您要重新建立连接的新 TCP 端口).使用UDP进行通信,理论上当然本身并没有设计上的保证.但在大多数情况下应该可以工作(今天,典型组织中的网络并不是那么脆弱/不可靠).视情况/意见而定,可能比选项 1 更好,但需要更多的工作,而且不工作的可能性较小但有限.

  1. If the long wait and uncertainty is not acceptable, then your other option is to somehow negotiate a different port between the client and server. (For example, in theory you could use UDP, which is connectionless, to negotiate the new TCP port you'd re-establish the connection on). Communication using UDP, in theory of course, itself is not guaranteed by design. But should work most of the times (Today, networking in typical org is not that flaky / unreliable). Subjective to scenario / opinion, perhaps better than option 1, but more work and smaller but finite chance of not working.

正如其中一条评论所建议的,这就是应用层协议(如 http 和 http 服务)的优势所在.如果可以,请使用它们,而不是低级套接字.如果可以接受,这是最好的选择.

As suggested in one of the comments, this is where application layer protocols like http and http services have an advantage. Use them, instead of low level sockets, if you can. If acceptable, this is the best option to go with.

(PS - 仅供参考 - 对于 HTTP,OS 内置了很多特殊处理,包括 windows - 例如,有一个专用驱动程序 Http.sys,专门用于处理多个应用程序尝试侦听同一个端口 80 等.这里的详细信息是另一个主题.重点是,当涉及到 HTTP)

(PS - FYI - For HTTP, there is a lot of special handling built into OS, including windows - For example, there is a dedicated driver Http.sys, specially for dealing with multiple apps trying to listen on same port 80 etc.. The details here are a topic for another time.. point is, there is lots of goodness and hard work done for you, when it comes to HTTP)

这篇关于如何优雅地重新连接到套接字的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆