• C#之Socket断线和重连


    一、网上常用方法

    1、当Socket.Conneted == false时,调用如下函数进行判断

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

    转载:https://www.cnblogs.com/wzd24/

  • 相关阅读:
    简单背包问题
    拓扑排序
    SPFA--P3905 道路重建
    Floyd--P1119 灾后重建
    Kmp--P3375 【模板】KMP字符串匹配
    练习 后缀数组
    BZOJ1036: [ZJOI2008]树的统计Count(树链剖分)
    BZOJ1503: [NOI2004]郁闷的出纳员(Splay)
    BZOJ2733: [HNOI2012]永无乡(线段树合并)
    BZOJ4196: [Noi2015]软件包管理器(树链剖分)
  • 原文地址:https://www.cnblogs.com/X-Jonney/p/12698673.html
Copyright © 2020-2023  润新知