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.     #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. }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM