C# Socket连接状态的判定以及断线重连

一、网上常用方法
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.  
  16.     #region 过程
  17.             // This is how you can determine whether a socket is still connected.
  18.             bool connectState = true;
  19.             bool blockingState = socket.Blocking;
  20.             try
  21.             {
  22.                 byte[] tmp = new byte[1];
  23.  
  24.                 socket.Blocking = false;
  25.                 socket.Send(tmp, 0, 0);
  26.                 //Console.WriteLine("Connected!");
  27.                 connectState = true; //若Send错误会跳去执行catch体,而不会执行其try体里其之后的代码
  28.             }
  29.             catch (SocketException e)
  30.             {
  31.                 // 10035 == WSAEWOULDBLOCK
  32.                 if (e.NativeErrorCode.Equals(10035))
  33.                 {
  34.                     //Console.WriteLine("Still Connected, but the Send would block");
  35.                     connectState = true;
  36.                 }
  37.  
  38.                 else
  39.                 {
  40.                     //Console.WriteLine("Disconnected: error code {0}!", e.NativeErrorCode);
  41.                     connectState = false;
  42.                 }
  43.             }
  44.             finally
  45.             {
  46.                 socket.Blocking = blockingState;
  47.             }
  48.  
  49.             //Console.WriteLine("Connected: {0}", client.Connected);
  50.             return connectState;
  51.             #endregion
  52. }

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.  
  16.     #region 过程
  17.  
  18.             return !((s.Poll(1000, SelectMode.SelectRead) && (s.Available == 0)) || !s.Connected);
  19.  
  20.             /* The long, but simpler-to-understand version:
  21.  
  22.                     bool part1 = s.Poll(1000, SelectMode.SelectRead);
  23.                     bool part2 = (s.Available == 0);
  24.                     if ((part1 && part2 ) || !s.Connected)
  25.                         return false;
  26.                     else
  27.                         return true;
  28.  
  29.             */
  30.             #endregion
  31. }

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

猜你喜欢

转载自blog.csdn.net/qq_14914623/article/details/89600039