一、網上常用方法
1、當Socket.Conneted == false時,調用如下函數進行判斷
點擊(此處)折疊或打開
- /// <summary>
- /// 當socket.connected為false時,進一步確定下當前連接狀態
- /// </summary>
- /// <returns></returns>
- private bool IsSocketConnected()
- {
- #region remarks
- /********************************************************************************************
- * 當Socket.Conneted為false時, 如果您需要確定連接的當前狀態,請進行非阻塞、零字節的 Send 調用。
- * 如果該調用成功返回或引發 WAEWOULDBLOCK 錯誤代碼 (10035),則該套接字仍然處於連接狀態;
- * 否則,該套接字不再處於連接狀態。
- * 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
- ********************************************************************************************/
- #endregion
-
- #region 過程
- // This is how you can determine whether a socket is still connected.
- bool connectState = true;
- bool blockingState = socket.Blocking;
- try
- {
- byte[] tmp = new byte[1];
-
- socket.Blocking = false;
- socket.Send(tmp, 0, 0);
- //Console.WriteLine("Connected!");
- connectState = true; //若Send錯誤會跳去執行catch體,而不會執行其try體里其之后的代碼
- }
- catch (SocketException e)
- {
- // 10035 == WSAEWOULDBLOCK
- if (e.NativeErrorCode.Equals(10035))
- {
- //Console.WriteLine("Still Connected, but the Send would block");
- connectState = true;
- }
-
- else
- {
- //Console.WriteLine("Disconnected: error code {0}!", e.NativeErrorCode);
- connectState = false;
- }
- }
- finally
- {
- socket.Blocking = blockingState;
- }
-
- //Console.WriteLine("Connected: {0}", client.Connected);
- return connectState;
- #endregion
- }
2、根據socket.poll判斷
點擊(此處)折疊或打開
- /// <summary>
- /// 另一種判斷connected的方法,但未檢測對端網線斷開或ungraceful的情況
- /// </summary>
- /// <param name="s"></param>
- /// <returns></returns>
- static bool IsSocketConnected(Socket s)
- {
- #region remarks
- /* As zendar wrote, it is nice to use the Socket.Poll and Socket.Available, but you need to take into conside ration
- * that the socket might not have been initialized in the first place.
- * This is the last (I believe) piece of information and it is supplied by the Socket.Connected property.
- * The revised version of the method would looks something like this:
- * from:http://stackoverflow.com/questions/2661764/how-to-check-if-a- socket-is-connected-disconnected-in-c */
- #endregion
-
- #region 過程
-
- return !((s.Poll(1000, SelectMode.SelectRead) && (s.Available == 0)) || !s.Connected);
-
- /* The long, but simpler-to-understand version:
-
- bool part1 = s.Poll(1000, SelectMode.SelectRead);
- bool part2 = (s.Available == 0);
- if ((part1 && part2 ) || !s.Connected)
- return false;
- else
- return true;
-
- */
- #endregion
- }
總結:--1--此兩種方法出處可在函數體中的remark中找到鏈接
--2--此兩種方法適用於對端正常關閉socket下的本地socket狀態檢測,在非正常關閉如斷電、拔網線的情況下不起作用
因為Socket.Conneted存在bug,詳見.Net Bugs
二、支持物理斷線重連功能的類
利用BeginReceive + KeepAlive實現物理斷線重連,初步測驗了一下,正常。(部分代碼參考帖子#26及blog在C#中利用keep-alive處理socket網絡異常斷開)
Keep-Alive機制的介紹請看TCP Keepalive HOWTO
以此備忘,同時希望能幫助到有需要的同學。
點擊(此處)折疊或打開
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Net.Sockets;
- using System.Net;
- using System.Threading;
-
- namespace MySocket
- {
- public class Socket_wrapper
- {
- //委托
- private delegate void delSocketDataArrival(byte[] data);
- static delSocketDataArrival socketDataArrival = socketDataArrivalHandler;
-
- private delegate void delSocketDisconnected();
- static delSocketDisconnected socketDisconnected = socketDisconnectedHandler;
-
- public static Socket theSocket = null;
- private static string remoteHost = "192.168.1.71";
- private static int remotePort = 6666;
-
- private static String SockErrorStr = null;
- private static ManualResetEvent TimeoutObject = new ManualResetEvent(false);
- private static Boolean IsconnectSuccess = false; //異步連接情況,由異步連接回調函數置位
- private static object lockObj_IsConnectSuccess = new object();
-
- /// <summary>
- /// 構造函數
- /// </summary>
- /// <param name="strIp"></param>
- /// <param name="iPort"></param>
- public Socket_wrapper(string strIp, int iPort)
- {
- remoteHost = strIp;
- remotePort = iPort;
- }
-
- /// <summary>
- /// 設置心跳
- /// </summary>
- private static void SetXinTiao()
- {
- //byte[] inValue = new byte[] { 1, 0, 0, 0, 0x20, 0x4e, 0, 0, 0xd0, 0x07, 0, 0 };// 首次探測時間20 秒, 間隔偵測時間2 秒
- byte[] inValue = new byte[] { 1, 0, 0, 0, 0x88, 0x13, 0, 0, 0xd0, 0x07, 0, 0 };// 首次探測時間5 秒, 間隔偵測時間2 秒
- theSocket.IOControl(IOControlCode.KeepAliveValues, inValue, null);
- }
-
- /// <summary>
- /// 創建套接字+異步連接函數
- /// </summary>
- /// <returns></returns>
- private static bool socket_create_connect()
- {
- IPAddress ipAddress = IPAddress.Parse(remoteHost);
- IPEndPoint remoteEP = new IPEndPoint(ipAddress, remotePort);
- theSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- theSocket.SendTimeout = 1000;
-
- SetXinTiao();//設置心跳參數
-
- #region 異步連接代碼
-
- TimeoutObject.Reset(); //復位timeout事件
- try
- {
- theSocket.BeginConnect(remoteEP, connectedCallback, theSocket);
- }
- catch (Exception err)
- {
- SockErrorStr = err.ToString();
- return false;
- }
- if (TimeoutObject.WaitOne(10000, false))//直到timeout,或者TimeoutObject.set()
- {
- if (IsconnectSuccess)
- {
- return true;
- }
- else
- {
- return false;
- }
- }
- else
- {
- SockErrorStr = "Time Out";
- return false;
- }
- #endregion
- }
-
- /// <summary>
- /// 同步receive函數
- /// </summary>
- /// <param name="readBuffer"></param>
- /// <returns></returns>
- public string socket_receive(byte[] readBuffer)
- {
- try
- {
- if (theSocket == null)
- {
- socket_create_connect();
- }
- else if (!theSocket.Connected)
- {
- if (!IsSocketConnected())
- Reconnect();
- }
-
- int bytesRec = theSocket.Receive(readBuffer);
-
- if (bytesRec == 0)
- {
- //warning 0 bytes received
- }
- return Encoding.ASCII.GetString(readBuffer, 0, bytesRec);
- }
- catch (SocketException se)
- {
- //print se.ErrorCode
- throw;
- }
- }
-
- /// <summary>
- /// 同步send函數
- /// </summary>
- /// <param name="sendMessage"></param>
- /// <returns></returns>
- public bool socket_send(string sendMessage)
- {
- if (checkSocketState())
- {
- return SendData(sendMessage);
- }
- return false;
- }
-
- /// <summary>
- /// 斷線重連函數
- /// </summary>
- /// <returns></returns>
- private static bool Reconnect()
- {
- //關閉socket
- theSocket.Shutdown(SocketShutdown.Both);
-
- theSocket.Disconnect(true);
- IsconnectSuccess = false;
-
- theSocket.Close();
-
- //創建socket
- return socket_create_connect();
- }
-
- /// <summary>
- /// 當socket.connected為false時,進一步確定下當前連接狀態
- /// </summary>
- /// <returns></returns>
- private bool IsSocketConnected()
- {
- #region remarks
- /********************************************************************************************
- * 當Socket.Conneted為false時, 如果您需要確定連接的當前狀態,請進行非阻塞、零字節的 Send 調用。
- * 如果該調用成功返回或引發 WAEWOULDBLOCK 錯誤代碼 (10035),則該套接字仍然處於連接狀態;
- * 否則,該套接字不再處於連接狀態。
- * 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
- ********************************************************************************************/
- #endregion
-
- #region 過程
- // This is how you can determine whether a socket is still connected.
- bool connectState = true;
- bool blockingState = theSocket.Blocking;
- try
- {
- byte[] tmp = new byte[1];
-
- theSocket.Blocking = false;
- theSocket.Send(tmp, 0, 0);
- //Console.WriteLine("Connected!");
- connectState = true; //若Send錯誤會跳去執行catch體,而不會執行其try體里其之后的代碼
- }
- catch (SocketException e)
- {
- // 10035 == WSAEWOULDBLOCK
- if (e.NativeErrorCode.Equals(10035))
- {
- //Console.WriteLine("Still Connected, but the Send would block");
- connectState = true;
- }
-
- else
- {
- //Console.WriteLine("Disconnected: error code {0}!", e.NativeErrorCode);
- connectState = false;
- }
- }
- finally
- {
- theSocket.Blocking = blockingState;
- }
-
- //Console.WriteLine("Connected: {0}", client.Connected);
- return connectState;
- #endregion
- }
-
- /// <summary>
- /// 另一種判斷connected的方法,但未檢測對端網線斷開或ungraceful的情況
- /// </summary>
- /// <param name="s"></param>
- /// <returns></returns>
- public static bool IsSocketConnected(Socket s)
- {
- #region remarks
- /* As zendar wrote, it is nice to use the Socket.Poll and Socket.Available, but you need to take into consideration
- * that the socket might not have been initialized in the first place.
- * This is the last (I believe) piece of information and it is supplied by the Socket.Connected property.
- * The revised version of the method would looks something like this:
- * from:http://stackoverflow.com/questions/2661764/how-to-check-if-a- socket-is-connected-disconnected-in-c */
- #endregion
-
- #region 過程
-
- if (s == null)
- return false;
- return !((s.Poll(1000, SelectMode.SelectRead) && (s.Available == 0)) || !s.Connected);
-
- /* The long, but simpler-to-understand version:
-
- bool part1 = s.Poll(1000, SelectMode.SelectRead);
- bool part2 = (s.Available == 0);
- if ((part1 && part2 ) || !s.Connected)
- return false;
- else
- return true;
-
- */
- #endregion
- }
-
- /// <summary>
- /// 異步連接回調函數
- /// </summary>
- /// <param name="iar"></param>
- static void connectedCallback(IAsyncResult iar)
- {
- #region <remarks>
- /// 1、置位IsconnectSuccess
- #endregion </remarks>
-
- lock (lockObj_IsConnectSuccess)
- {
- Socket client = (Socket)iar.AsyncState;
- try
- {
- client.EndConnect(iar);
- IsconnectSuccess = true;
- StartKeepAlive(); //開始KeppAlive檢測
- }
- catch (Exception e)
- {
- //Console.WriteLine(e.ToString());
- SockErrorStr = e.ToString();
- IsconnectSuccess = false;
- }
- finally
- {
- TimeoutObject.Set();
- }
- }
- }
-
- /// <summary>
- /// 開始KeepAlive檢測函數
- /// </summary>
- private static void StartKeepAlive()
- {
- theSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(OnReceiveCallback), theSocket);
- }
-
- /// <summary>
- /// BeginReceive回調函數
- /// </summary>
- static byte[] buffer = new byte[1024];
- private static void OnReceiveCallback(IAsyncResult ar)
- {
- try
- {
- Socket peerSock = (Socket)ar.AsyncState;
- int BytesRead = peerSock.EndReceive(ar);
- if (BytesRead > 0)
- {
- byte[] tmp = new byte[BytesRead];
- Array.ConstrainedCopy(buffer, 0, tmp, 0, BytesRead);
- if (socketDataArrival != null)
- {
- socketDataArrival(tmp);
- }
- }
- else//對端gracefully關閉一個連接
- {
- if (theSocket.Connected)//上次socket的狀態
- {
- if (socketDisconnected != null)
- {
- //1-重連
- socketDisconnected();
- //2-退出,不再執行BeginReceive
- return;
- }
- }
- }
- //此處buffer似乎要清空--待實現 zq
- theSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(OnReceiveCallback), theSocket);
- }
- catch (Exception ex)
- {
- if (socketDisconnected != null)
- {
- socketDisconnected(); //Keepalive檢測網線斷開引發的異常在這里捕獲
- return;
- }
- }
- }
-
- /// <summary>
- /// 異步收到消息處理器
- /// </summary>
- /// <param name="data"></param>
- private static void socketDataArrivalHandler(byte[] data)
- {
- }
-
- /// <summary>
- /// socket由於連接中斷(軟/硬中斷)的后續工作處理器
- /// </summary>
- private static void socketDisconnectedHandler()
- {
- Reconnect();
- }
-
- /// <summary>
- /// 檢測socket的狀態
- /// </summary>
- /// <returns></returns>
- public static bool checkSocketState()
- {
- try
- {
- if (theSocket == null)
- {
- return socket_create_connect();
- }
- else if (IsconnectSuccess)
- {
- return true;
- }
- else//已創建套接字,但未connected
- {
- #region 異步連接代碼
-
- TimeoutObject.Reset(); //復位timeout事件
- try
- {
- IPAddress ipAddress = IPAddress.Parse(remoteHost);
- IPEndPoint remoteEP = new IPEndPoint(ipAddress, remotePort);
- theSocket.BeginConnect(remoteEP, connectedCallback, theSocket);
-
- SetXinTiao();//設置心跳參數
- }
- catch (Exception err)
- {
- SockErrorStr = err.ToString();
- return false;
- }
- if (TimeoutObject.WaitOne(2000, false))//直到timeout,或者TimeoutObject.set()
- {
- if (IsconnectSuccess)
- {
- return true;
- }
- else
- {
- return false;
- }
- }
- else
- {
- SockErrorStr = "Time Out";
- return false;
- }
-
- #endregion
- }
-
- }
- catch (SocketException se)
- {
- SockErrorStr = se.ToString();
- return false;
- }
- }
-
-
- /// <summary>
- /// 同步發送
- /// </summary>
- /// <param name="dataStr"></param>
- /// <returns></returns>
- public static bool SendData(string dataStr)
- {
- bool result = false;
- if (dataStr == null || dataStr.Length < 0)
- return result;
- try
- {
- byte[] cmd = Encoding.Default.GetBytes(dataStr);
- int n = theSocket.Send(cmd);
- if (n < 1)
- result = false;
- }
- catch (Exception ee)
- {
- SockErrorStr = ee.ToString();
- result = false;
- }
- return result;
- }
- }
- }