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/

posted @ 2020-04-14 16:10  X-Jonney  阅读(6580)  评论(1编辑  收藏  举报