zoukankan      html  css  js  c++  java
  • C#之Socket断线重连

    一、网上常用方法

    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;
            }
        }
    }

    转自 https://blog.csdn.net/thebestleo/article/details/52354126
  • 相关阅读:
    02-30 线性可分支持向量机
    02-28 scikit-learn库之线朴素贝叶斯
    02-27 朴素贝叶斯
    02-26 决策树(鸢尾花分类)
    047 选项模式
    第二节:师傅延伸的一些方法(复习_总结)
    第一节:登录流程
    第一节:对应拼音编码查询(后续更新)
    前端对象
    Form表单
  • 原文地址:https://www.cnblogs.com/asdyzh/p/9945192.html
Copyright © 2011-2022 走看看