socket tcp断线重连

一、网上常用方法
1、当Socket.Conneted == false时,调用如下函数进行判断

点击(此处)折叠或打开

  1. /// <summary>
  2. /// 当socket.connected为false时,进一步确定下当前连接状态
  3. /// </summary>
  4. /// <returns></returns>
  5. private bool IsSocketConnected()
  6. {
  7.     #region remarks
  8.     /********************************************************************************************
  9.      * 当Socket.Conneted为false时, 如果您需要确定连接的当前状态,请进行非阻塞、零字节的 Send 调用。
  10.      * 如果该调用成功返回或引发 WAEWOULDBLOCK 错误代码 (10035),则该套接字仍然处于连接状态;
  11.      * 否则,该套接字不再处于连接状态。
  12.      * Depending on http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.connected.aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-2
  13.     ********************************************************************************************/
  14.     #endregion
  15.     #region 过程
  16.             // This is how you can determine whether a socket is still connected.
  17.             bool connectState = true;
  18.             bool blockingState = socket.Blocking;
  19.             try
  20.             {
  21.                 byte[] tmp = new byte[1];
  22.                 socket.Blocking = false;
  23.                 socket.Send(tmp, 0, 0);
  24.                 //Console.WriteLine("Connected!");
  25.                 connectState = true; //若Send错误会跳去执行catch体,而不会执行其try体里其之后的代码
  26.             }
  27.             catch (SocketException e)
  28.             {
  29.                 // 10035 == WSAEWOULDBLOCK
  30.                 if (e.NativeErrorCode.Equals(10035))
  31.                 {
  32.                     //Console.WriteLine("Still Connected, but the Send would block");
  33.                     connectState = true;
  34.                 }
  35.                 else
  36.                 {
  37.                     //Console.WriteLine("Disconnected: error code {0}!", e.NativeErrorCode);
  38.                     connectState = false;
  39.                 }
  40.             }
  41.             finally
  42.             {
  43.                 socket.Blocking = blockingState;
  44.             }
  45.             //Console.WriteLine("Connected: {0}", client.Connected);
  46.             return connectState;
  47.             #endregion
  48. }

2、根据socket.poll判断

点击(此处)折叠或打开

  1. /// <summary>
  2. /// 另一种判断connected的方法,但未检测对端网线断开或ungraceful的情况
  3. /// </summary>
  4. /// <param name="s"></param>
  5. /// <returns></returns>
  6. static bool IsSocketConnected(Socket s)
  7. {
  8.     #region remarks
  9.             /* As zendar wrote, it is nice to use the Socket.Poll and Socket.Available, but you need to take into conside                ration
  10.              * that the socket might not have been initialized in the first place.
  11.              * This is the last (I believe) piece of information and it is supplied by the Socket.Connected property.
  12.              * The revised version of the method would looks something like this:
  13.              * from:http://stackoverflow.com/questions/2661764/how-to-check-if-a- socket-is-connected-disconnected-in-c */
  14.             #endregion
  15.     #region 过程
  16.             return !((s.Poll(1000, SelectMode.SelectRead) && (s.Available == 0)) || !s.Connected);
  17.             /* The long, but simpler-to-understand version:
  18.                     bool part1 = s.Poll(1000, SelectMode.SelectRead);
  19.                     bool part2 = (s.Available == 0);
  20.                     if ((part1 && part2 ) || !s.Connected)
  21.                         return false;
  22.                     else
  23.                         return true;
  24.             */
  25.             #endregion
  26. }

总结:--1--此两种方法出处可在函数体中的remark中找到链接
         --2--此两种方法适用于对端正常关闭socket下的本地socket状态检测,在非正常关闭如断电、拔网线的情况下不起作用
               因为Socket.Conneted存在bug,详见.Net Bugs

二、支持物理断线重连功能的类

        利用BeginReceive + KeepAlive实现物理断线重连,初步测验了一下,正常。(部分代码参考帖子#26blog在C#中利用keep-alive处理socket网络异常断开)
        Keep-Alive机制的介绍请看TCP Keepalive HOWTO
        以此备忘,同时希望能帮助到有需要的同学。

点击(此处)折叠或打开

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Net.Sockets;
  6. using System.Net;
  7. using System.Threading;
  8. namespace MySocket
  9. {
  10.     public class Socket_wrapper
  11.     {
  12.         //委托
  13.         private delegate void delSocketDataArrival(byte[] data);
  14.         static delSocketDataArrival socketDataArrival = socketDataArrivalHandler;
  15.         private delegate void delSocketDisconnected();
  16.         static delSocketDisconnected socketDisconnected = socketDisconnectedHandler;
  17.         public static Socket theSocket = null;
  18.         private static string remoteHost = "192.168.1.71";
  19.         private static int remotePort = 6666;
  20.         private static String SockErrorStr = null;
  21.         private static ManualResetEvent TimeoutObject = new ManualResetEvent(false);
  22.         private static Boolean IsconnectSuccess = false; //异步连接情况,由异步连接回调函数置位
  23.         private static object lockObj_IsConnectSuccess = new object();
  24.         /// <summary>
  25.         /// 构造函数
  26.         /// </summary>
  27.         /// <param name="strIp"></param>
  28.         /// <param name="iPort"></param>
  29.         public Socket_wrapper(string strIp, int iPort)
  30.         {
  31.             remoteHost = strIp;
  32.             remotePort = iPort;
  33.         }
  34.         /// <summary>
  35.         /// 设置心跳
  36.         /// </summary>
  37.         private static void SetXinTiao()
  38.         {
  39.             //byte[] inValue = new byte[] { 1, 0, 0, 0, 0x20, 0x4e, 0, 0, 0xd0, 0x07, 0, 0 };// 首次探测时间20 秒, 间隔侦测时间2 秒
  40.             byte[] inValue = new byte[] { 1, 0, 0, 0, 0x88, 0x13, 0, 0, 0xd0, 0x07, 0, 0 };// 首次探测时间5 秒, 间隔侦测时间2 秒
  41.             theSocket.IOControl(IOControlCode.KeepAliveValues, inValue, null);
  42.         }
  43.         /// <summary>
  44.         /// 创建套接字+异步连接函数
  45.         /// </summary>
  46.         /// <returns></returns>
  47.         private static bool socket_create_connect()
  48.         {
  49.             IPAddress ipAddress = IPAddress.Parse(remoteHost);
  50.             IPEndPoint remoteEP = new IPEndPoint(ipAddress, remotePort);
  51.             theSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  52.             theSocket.SendTimeout = 1000;
  53.             SetXinTiao();//设置心跳参数
  54.             #region 异步连接代码
  55.             TimeoutObject.Reset(); //复位timeout事件
  56.             try
  57.             {
  58.                 theSocket.BeginConnect(remoteEP, connectedCallback, theSocket);
  59.             }
  60.             catch (Exception err)
  61.             {
  62.                 SockErrorStr = err.ToString();
  63.                 return false;
  64.             }
  65.             if (TimeoutObject.WaitOne(10000, false))//直到timeout,或者TimeoutObject.set()
  66.             {
  67.                 if (IsconnectSuccess)
  68.                 {
  69.                     return true;
  70.                 }
  71.                 else
  72.                 {
  73.                     return false;
  74.                 }
  75.             }
  76.             else
  77.             {
  78.                 SockErrorStr = "Time Out";
  79.                 return false;
  80.             }
  81.             #endregion
  82.         }
  83.         /// <summary>
  84.         /// 同步receive函数
  85.         /// </summary>
  86.         /// <param name="readBuffer"></param>
  87.         /// <returns></returns>
  88.         public string socket_receive(byte[] readBuffer)
  89.         {
  90.             try
  91.             {
  92.                 if (theSocket == null)
  93.                 {
  94.                     socket_create_connect();
  95.                 }
  96.                 else if (!theSocket.Connected)
  97.                 {
  98.                     if (!IsSocketConnected())
  99.                         Reconnect();
  100.                 }
  101.                 int bytesRec = theSocket.Receive(readBuffer);
  102.                 if (bytesRec == 0)
  103.                 {
  104.                     //warning 0 bytes received
  105.                 }
  106.                 return Encoding.ASCII.GetString(readBuffer, 0, bytesRec);
  107.             }
  108.             catch (SocketException se)
  109.             {
  110.                 //print se.ErrorCode
  111.                 throw;
  112.             }
  113.         }
  114.         /// <summary>
  115.         /// 同步send函数
  116.         /// </summary>
  117.         /// <param name="sendMessage"></param>
  118.         /// <returns></returns>
  119.         public bool socket_send(string sendMessage)
  120.         {
  121.             if (checkSocketState())
  122.             {
  123.                 return SendData(sendMessage);
  124.             }
  125.             return false;
  126.         }
  127.         /// <summary>
  128.         /// 断线重连函数
  129.         /// </summary>
  130.         /// <returns></returns>
  131.         private static bool Reconnect()
  132.         {
  133.             //关闭socket
  134.             theSocket.Shutdown(SocketShutdown.Both);
  135.             theSocket.Disconnect(true);
  136.             IsconnectSuccess = false;
  137.             theSocket.Close();
  138.             //创建socket
  139.             return socket_create_connect();
  140.         }
  141.         /// <summary>
  142.         /// 当socket.connected为false时,进一步确定下当前连接状态
  143.         /// </summary>
  144.         /// <returns></returns>
  145.         private bool IsSocketConnected()
  146.         {
  147.             #region remarks
  148.             /********************************************************************************************
  149.              * 当Socket.Conneted为false时, 如果您需要确定连接的当前状态,请进行非阻塞、零字节的 Send 调用。
  150.              * 如果该调用成功返回或引发 WAEWOULDBLOCK 错误代码 (10035),则该套接字仍然处于连接状态;
  151.              * 否则,该套接字不再处于连接状态。
  152.              * Depending on http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.connected.aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-2
  153.             ********************************************************************************************/
  154.             #endregion
  155.             #region 过程
  156.             // This is how you can determine whether a socket is still connected.
  157.             bool connectState = true;
  158.             bool blockingState = theSocket.Blocking;
  159.             try
  160.             {
  161.                 byte[] tmp = new byte[1];
  162.                 theSocket.Blocking = false;
  163.                 theSocket.Send(tmp, 0, 0);
  164.                 //Console.WriteLine("Connected!");
  165.                 connectState = true; //若Send错误会跳去执行catch体,而不会执行其try体里其之后的代码
  166.             }
  167.             catch (SocketException e)
  168.             {
  169.                 // 10035 == WSAEWOULDBLOCK
  170.                 if (e.NativeErrorCode.Equals(10035))
  171.                 {
  172.                     //Console.WriteLine("Still Connected, but the Send would block");
  173.                     connectState = true;
  174.                 }
  175.                 else
  176.                 {
  177.                     //Console.WriteLine("Disconnected: error code {0}!", e.NativeErrorCode);
  178.                     connectState = false;
  179.                 }
  180.             }
  181.             finally
  182.             {
  183.                 theSocket.Blocking = blockingState;
  184.             }
  185.             //Console.WriteLine("Connected: {0}", client.Connected);
  186.             return connectState;
  187.             #endregion
  188.         }
  189.         /// <summary>
  190.         /// 另一种判断connected的方法,但未检测对端网线断开或ungraceful的情况
  191.         /// </summary>
  192.         /// <param name="s"></param>
  193.         /// <returns></returns>
  194.         public static bool IsSocketConnected(Socket s)
  195.         {
  196.             #region remarks
  197.             /* As zendar wrote, it is nice to use the Socket.Poll and Socket.Available, but you need to take into consideration
  198.              * that the socket might not have been initialized in the first place.
  199.              * This is the last (I believe) piece of information and it is supplied by the Socket.Connected property.
  200.              * The revised version of the method would looks something like this:
  201.              * from:http://stackoverflow.com/questions/2661764/how-to-check-if-a- socket-is-connected-disconnected-in-c */
  202.             #endregion
  203.             #region 过程
  204.             if (s == null)
  205.                 return false;
  206.             return !((s.Poll(1000, SelectMode.SelectRead) && (s.Available == 0)) || !s.Connected);
  207.             /* The long, but simpler-to-understand version:
  208.                     bool part1 = s.Poll(1000, SelectMode.SelectRead);
  209.                     bool part2 = (s.Available == 0);
  210.                     if ((part1 && part2 ) || !s.Connected)
  211.                         return false;
  212.                     else
  213.                         return true;
  214.             */
  215.             #endregion
  216.         }
  217.         /// <summary>
  218.         /// 异步连接回调函数
  219.         /// </summary>
  220.         /// <param name="iar"></param>
  221.         static void connectedCallback(IAsyncResult iar)
  222.         {
  223.             #region <remarks>
  224.             /// 1、置位IsconnectSuccess
  225.             #endregion </remarks>
  226.             lock (lockObj_IsConnectSuccess)
  227.             {
  228.                 Socket client = (Socket)iar.AsyncState;
  229.                 try
  230.                 {
  231.                     client.EndConnect(iar);
  232.                     IsconnectSuccess = true;
  233.                     StartKeepAlive(); //开始KeppAlive检测
  234.                 }
  235.                 catch (Exception e)
  236.                 {
  237.                     //Console.WriteLine(e.ToString());
  238.                     SockErrorStr = e.ToString();
  239.                     IsconnectSuccess = false;
  240.                 }
  241.                 finally
  242.                 {
  243.                     TimeoutObject.Set();
  244.                 }
  245.             }
  246.         }
  247.         /// <summary>
  248.         /// 开始KeepAlive检测函数
  249.         /// </summary>
  250.         private static void StartKeepAlive()
  251.         {
  252.             theSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(OnReceiveCallback), theSocket);
  253.         }
  254.         /// <summary>
  255.         /// BeginReceive回调函数
  256.         /// </summary>
  257.         static byte[] buffer = new byte[1024];
  258.         private static void OnReceiveCallback(IAsyncResult ar)
  259.         {
  260.             try
  261.             {
  262.                 Socket peerSock = (Socket)ar.AsyncState;
  263.                 int BytesRead = peerSock.EndReceive(ar);
  264.                 if (BytesRead > 0)
  265.                 {
  266.                     byte[] tmp = new byte[BytesRead];
  267.                     Array.ConstrainedCopy(buffer, 0, tmp, 0, BytesRead);
  268.                     if (socketDataArrival != null)
  269.                     {
  270.                         socketDataArrival(tmp);
  271.                     }
  272.                 }
  273.                 else//对端gracefully关闭一个连接
  274.                 {
  275.                     if (theSocket.Connected)//上次socket的状态
  276.                     {
  277.                         if (socketDisconnected != null)
  278.                         {
  279.                             //1-重连
  280.                             socketDisconnected();
  281.                             //2-退出,不再执行BeginReceive
  282.                             return;
  283.                         }
  284.                     }
  285.                 }
  286.                 //此处buffer似乎要清空--待实现 zq
  287.                 theSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(OnReceiveCallback), theSocket);
  288.             }
  289.             catch (Exception ex)
  290.             {
  291.                 if (socketDisconnected != null)
  292.                 {
  293.                     socketDisconnected(); //Keepalive检测网线断开引发的异常在这里捕获
  294.                     return;
  295.                 }
  296.             }
  297.         }
  298.         /// <summary>
  299.         /// 异步收到消息处理器
  300.         /// </summary>
  301.         /// <param name="data"></param>
  302.         private static void socketDataArrivalHandler(byte[] data)
  303.         {
  304.         }
  305.         /// <summary>
  306.         /// socket由于连接中断(软/硬中断)的后续工作处理器
  307.         /// </summary>
  308.         private static void socketDisconnectedHandler()
  309.         {
  310.             Reconnect();
  311.         }
  312.         /// <summary>
  313.         /// 检测socket的状态
  314.         /// </summary>
  315.         /// <returns></returns>
  316.         public static bool checkSocketState()
  317.         {
  318.             try
  319.             {
  320.                 if (theSocket == null)
  321.                 {
  322.                     return socket_create_connect();
  323.                 }
  324.                 else if (IsconnectSuccess)
  325.                 {
  326.                     return true;
  327.                 }
  328.                 else//已创建套接字,但未connected
  329.                 {
  330.                     #region 异步连接代码
  331.                     TimeoutObject.Reset(); //复位timeout事件
  332.                     try
  333.                     {
  334.                         IPAddress ipAddress = IPAddress.Parse(remoteHost);
  335.                         IPEndPoint remoteEP = new IPEndPoint(ipAddress, remotePort);
  336.                         theSocket.BeginConnect(remoteEP, connectedCallback, theSocket);
  337.                         SetXinTiao();//设置心跳参数
  338.                     }
  339.                     catch (Exception err)
  340.                     {
  341.                         SockErrorStr = err.ToString();
  342.                         return false;
  343.                     }
  344.                     if (TimeoutObject.WaitOne(2000, false))//直到timeout,或者TimeoutObject.set()
  345.                     {
  346.                         if (IsconnectSuccess)
  347.                         {
  348.                             return true;
  349.                         }
  350.                         else
  351.                         {
  352.                             return false;
  353.                         }
  354.                     }
  355.                     else
  356.                     {
  357.                         SockErrorStr = "Time Out";
  358.                         return false;
  359.                     }
  360.                     #endregion
  361.                 }
  362.             }
  363.             catch (SocketException se)
  364.             {
  365.                 SockErrorStr = se.ToString();
  366.                 return false;
  367.             }
  368.         }
  369.         /// <summary>
  370.         /// 同步发送
  371.         /// </summary>
  372.         /// <param name="dataStr"></param>
  373.         /// <returns></returns>
  374.         public static bool SendData(string dataStr)
  375.         {
  376.             bool result = false;
  377.             if (dataStr == null || dataStr.Length < 0)
  378.                 return result;
  379.             try
  380.             {
  381.                 byte[] cmd = Encoding.Default.GetBytes(dataStr);
  382.                 int n = theSocket.Send(cmd);
  383.                 if (n < 1)
  384.                     result = false;
  385.             }
  386.             catch (Exception ee)
  387.             {
  388.                 SockErrorStr = ee.ToString();
  389.                 result = false;
  390.             }
  391.             return result;
  392.         }
  393.     }
  394. }
  395. 参考
  396. https://www.cnblogs.com/MRRAOBX/articles/3601856.html
posted @ 2024-10-10 14:48  钢与铁  阅读(114)  评论(0编辑  收藏  举报