如何正常重新连接到套接字

我有一个以下方法连接到程序启动时的终点

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

我还有一个定时器,设置为每60秒触发一次调用CheckConnectivity ,它试图将任意字节数组发送到终点,以确保连接仍然存在,如果发送失败,它将尝试重新连接。

 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电缆。

但是,即使重新连接LAN电缆(能够ping)后,我的重新连接方法中的ChannelSocket.Connect(ChannelEndPoint)也会抛出此错误

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

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

如果应用程序关闭TCP / IP端口,则协议规定端口在TIME_WAIT状态下保持一定的持续时间(在Windows机器上默认为240秒)。 请参阅以下参考 –

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秒)

你的选择 – 这里有限……

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

如果套接字先前已断开连接,则无法使用此( Connect )方法恢复连接。请使用其中一个异步BeginConnect方法重新连接。这是底层提供程序的限制。

文档建议必须使用BeginConnect的原因是我上面提到的..它根本不希望能够立即建立连接..因此唯一的选择是异步调用,而你等待为了在几分钟内建立连接,请期望并计划失败。 基本上,可能不是一个理想的选择。

  1. 如果漫长的等待和不确定性是不可接受的,那么您的另一个选择是以某种方式协商客户端和服务器之间的不同端口。 (例如,从理论上讲,您可以使用无连接的 UDP来协商您重新建立连接的新TCP端口)。 当然,理论上使用UDP的通信本身并不能保证设计。 但是大部分时间都应该工作(今天,典型组织中的网络不是那么片状/不可靠)。 主观的情景/意见,或许比选项1更好,但更多的工作和更小但有限的机会不工作。

  2. 正如其中一条评论中所建议的那样,这就是http和http服务等应用层协议具有优势的地方。 如果可以的话,使用它们而不是低级sockets。 如果可以接受,这是最好的选择。

(PS – 仅供参考 – 对于HTTP,操作系统内置了很多特殊处理,包括windows – 例如,有一个专用驱动程序Http.sys ,专门用于处理试图在同一端口80上侦听的多个应用程序等。这里的细节是另一个时间的主题..重点是,当涉及到HTTP时,为你做了很多善事和辛勤工作)

也许你应该切换到更高的抽象类,这更好地处理所有这些漂亮的小细节?

我将用于这些网络连接TcpListener和TcpClient类。 这些类的使用非常简单:

客户方:

 public void GetInformationAsync(IPAddress ipAddress) { _Log.Info("Start retrieving informations from address " + ipAddress + "."); var tcpClient = new TcpClient(); tcpClient.BeginConnect(ipAddress, _PortNumber, OnTcpClientConnected, tcpClient); } private void OnTcpClientConnected(IAsyncResult asyncResult) { try { using (var tcpClient = (TcpClient)asyncResult.AsyncState) { tcpClient.EndConnect(asyncResult); var ipAddress = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address; var stream = tcpClient.GetStream(); stream.ReadTimeout = 5000; _Log.Debug("Connection established to " + ipAddress + "."); var formatter = new BinaryFormatter(); var information = (MyInformation)formatter.Deserialize(stream); _Log.Info("Successfully retrieved information from address " + ipAddress + "."); InformationAvailable.FireEvent(this, new InformationEventArgs(information)); } } catch (Exception ex) { _Log.Error("Error in retrieving informations.", ex); return; } } 

服务器端:

 public void Start() { ThrowIfDisposed(); if (_TcpServer != null;) _TcpServer.Stop(); _TcpServer = new TcpListener(IPAddress.Any, _PortNumber); _TcpServer.Start(); _TcpServer.BeginAcceptTcpClient(OnClientConnected, _TcpServer); _Log.Info("Start listening for incoming connections on " + _TcpServer.LocalEndpoint + "."); } private void OnClientConnected(IAsyncResult asyncResult) { var tcpServer = (TcpListener)asyncResult.AsyncState; IPAddress address = IPAddress.None; try { if (tcpServer.Server != null && tcpServer.Server.IsBound) tcpServer.BeginAcceptTcpClient(OnClientConnected, tcpServer); using (var client = tcpServer.EndAcceptTcpClient(asyncResult)) { address = ((IPEndPoint)client.Client.RemoteEndPoint).Address; _Log.Debug("Client connected from address " + address + "."); var formatter = new BinaryFormatter(); var informations = new MyInformation() { // Initialize properties with desired values. }; var stream = client.GetStream(); formatter.Serialize(stream, description); _Log.Debug("Sucessfully serialized information into network stream."); } } catch (ObjectDisposedException) { // This normally happens, when the server will be stopped // and their exists no other reliable way to check this state // before calling EndAcceptTcpClient(). } catch (Exception ex) { _Log.Error(String.Format("Cannot send instance information to {0}.", address), ex); } } 

此代码有效,并且不会在客户端丢失连接时出现任何问题。 如果你在服务器端丢失连接,你必须重新建立监听器,但这是另一个故事。

在ReconnectChannel中只需配置ChannelSocket对象。

 try { `//ChannelSocket.Shutdown(SocketShutdown.Both); //ChannelSocket.Disconnect(true); //ChannelSocket.Close(); ChannelSocket.Dispose();` } 

这对我有用。 如果它不适合你,请告诉我。