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实现物理断线重连,初步测验了一下,正常。(部分代码参考帖子#26及blog在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/