C#之Socket断线重连

来源:互联网 发布:淘宝店主工具 编辑:程序博客网 时间:2024/05/16 19:12
一、网上常用方法

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

////// 当socket.connected为false时,进一步确定下当前连接状态/// /// 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判断

////// 另一种判断connected的方法,但未检测对端网线断开或ungraceful的情况/// /// /// 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实现物理断线重连,初步测验了一下,正常。
(部分

代码参考帖子#26blog在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();        ///        /// 构造函数        ///         ///         ///         public Socket_wrapper(string strIp, int iPort)        {            remoteHost = strIp;            remotePort = iPort;        }        ///        /// 设置心跳        ///         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);        }        ///        /// 创建套接字+异步连接函数        ///         ///         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        }        ///        /// 同步receive函数        ///         ///         ///         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;            }        }        ///        /// 同步send函数        ///         ///         ///         public bool socket_send(string sendMessage)        {            if (checkSocketState())            {                return SendData(sendMessage);            }            return false;        }        ///        /// 断线重连函数        ///         ///         private static bool Reconnect()        {            //关闭socket            theSocket.Shutdown(SocketShutdown.Both);            theSocket.Disconnect(true);            IsconnectSuccess = false;            theSocket.Close();            //创建socket            return socket_create_connect();        }        ///        /// 当socket.connected为false时,进一步确定下当前连接状态        ///         ///         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        }        ///        /// 另一种判断connected的方法,但未检测对端网线断开或ungraceful的情况        ///         ///         ///         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        }        ///        /// 异步连接回调函数        ///         ///         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();                }            }        }        ///        /// 开始KeepAlive检测函数        ///         private static void StartKeepAlive()        {            theSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(OnReceiveCallback), theSocket);        }        ///        /// BeginReceive回调函数        ///         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;                }            }        }        ///        /// 异步收到消息处理器        ///         ///         private static void socketDataArrivalHandler(byte[] data)        {        }        ///        /// socket由于连接中断(软/硬中断)的后续工作处理器        ///         private static void socketDisconnectedHandler()        {            Reconnect();        }        ///        /// 检测socket的状态        ///         ///         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;            }        }        ///        /// 同步发送        ///         ///         ///         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;        }    }}


1 0
原创粉丝点击