老大有个思路就是要在客户端长时间未操作连接的时候定时发送心跳脉冲数据包测试是否连接 
这个如何实现呀?求具体思路或者少许代码,拜谢拜谢

解决方案 »

  1.   

    也就是说如何判读socket的连接状态,求高手指教!
      

  2.   

    客户端:
    30秒发送一个心跳包到服务器服务器:
    接收到心跳包,更新LastHeartbeatTime
    并且有一个线程,一分钟扫描一次,如果LastHeartbeatTime超过一分钟没更新的视为下线
     
      

  3.   


    模拟心跳的机制
    使用直接调用函数模拟心跳,没有涉及到socket
    写得不好,不要太挑剔using System;
    using System.Collections.Generic;
    using System.Threading;namespace ConsoleApplication1
    {
        // 客户端离线委托
        public delegate void ClientOfflineHandler(ClientInfo client);    // 客户端上线委托
        public delegate void ClientOnlineHandler(ClientInfo client);    public class Program
        {
            /// <summary>
            /// 客户端离线提示
            /// </summary>
            /// <param name="clientInfo"></param>
            private static void ClientOffline(ClientInfo clientInfo)
            {
                Console.WriteLine(String.Format("客户端{0}离线,离线时间:\t{1}", clientInfo.ClientID, clientInfo.LastHeartbeatTime));
            }        /// <summary>
            /// 客户端上线提示
            /// </summary>
            /// <param name="clientInfo"></param>
            private static void ClientOnline(ClientInfo clientInfo)
            {
                Console.WriteLine(String.Format("客户端{0}上线,上线时间:\t{1}", clientInfo.ClientID, clientInfo.LastHeartbeatTime));
            }        static void Main()
            {
                // 服务端
                Server server = new Server();            // 服务端离线事件
                server.OnClientOffline += ClientOffline;            // 服务器上线事件
                server.OnClientOnline += ClientOnline;            // 开启服务器
                server.Start();            // 模拟100个客户端
                Dictionary<Int32, Client> dicClient = new Dictionary<Int32, Client>();
                for (Int32 i = 0; i < 100; i++)
                {
                    // 这里传入server只是为了方便而已
                    Client client = new Client(i + 1, server);
                    dicClient.Add(i + 1, client);                // 开启客户端
                    client.Start();
                }            System.Threading.Thread.Sleep(1000);            while (true)
                {
                    Console.WriteLine("请输入要离线的ClientID,输入0则退出程序:");
                    String clientID = Console.ReadLine();
                    if (!String.IsNullOrEmpty(clientID))
                    {
                        Int32 iClientID = 0;
                        Int32.TryParse(clientID, out iClientID);
                        if (iClientID > 0)
                        {
                            Client client;
                            if (dicClient.TryGetValue(iClientID, out client))
                            {
                                // 客户端离线
                                client.Offline = true;
                            }
                        }
                        else
                        {
                            return;
                        }
                    }
                }
            }
        }    /// <summary>
        /// 服务端
        /// </summary>
        public class Server
        {
            public event ClientOfflineHandler OnClientOffline;
            public event ClientOnlineHandler OnClientOnline;        private Dictionary<Int32, ClientInfo> _DicClient;        /// <summary>
            /// 构造函数
            /// </summary>
            public Server()
            {
                _DicClient = new Dictionary<Int32, ClientInfo>(100);            
            }        /// <summary>
            /// 开启服务端
            /// </summary>
            public void Start()
            {
                // 开启扫描离线线程
                Thread t = new Thread(new ThreadStart(ScanOffline));
                t.IsBackground = true;
                t.Start();
            }        /// <summary>
            /// 扫描离线
            /// </summary>
            private void ScanOffline()
            {
                while (true)
                {
                    // 一秒扫描一次
                    System.Threading.Thread.Sleep(1000);                lock (_DicClient)
                    {
                        foreach (Int32 clientID in _DicClient.Keys)
                        {
                            ClientInfo clientInfo = _DicClient[clientID];                        // 如果已经离线则不用管
                            if (!clientInfo.State)
                            {
                                continue;
                            }                        // 判断最后心跳时间是否大于3秒
                            TimeSpan sp = System.DateTime.Now - clientInfo.LastHeartbeatTime;
                            if (sp.Seconds >= 3)
                            {
                                // 离线,触发离线事件
                                if (OnClientOffline != null)
                                {
                                    OnClientOffline(clientInfo);
                                }                            // 修改状态
                                clientInfo.State = false;
                            }
                        }
                    }
                }
            }        /// <summary>
            /// 接收心跳包
            /// </summary>
            /// <param name="clientID">客户端ID</param>
            public void ReceiveHeartbeat(Int32 clientID)
            {
                lock (_DicClient)
                {
                    ClientInfo clientInfo;
                    if (_DicClient.TryGetValue(clientID, out clientInfo))
                    {
                        // 如果客户端已经上线,则更新最后心跳时间
                        clientInfo.LastHeartbeatTime = System.DateTime.Now;
                    }
                    else
                    {
                        // 客户端不存在,则认为是新上线的
                        clientInfo = new ClientInfo();
                        clientInfo.ClientID = clientID;
                        clientInfo.LastHeartbeatTime = System.DateTime.Now;
                        clientInfo.State = true;                    _DicClient.Add(clientID, clientInfo);                    // 触发上线事件
                        if (OnClientOnline != null)
                        {
                            OnClientOnline(clientInfo);
                        }
                    }
                }
            }
        }    /// <summary>
        /// 客户端
        /// </summary>
        public class Client
        {
            public Server Server;
            public Int32 ClientID;
            public Boolean Offline;        /// <summary>
            /// 构造函数
            /// </summary>
            /// <param name="clientID"></param>
            /// <param name="server"></param>
            public Client(Int32 clientID, Server server)
            {
                ClientID = clientID;
                Server = server;
                Offline = false;
            }        /// <summary>
            /// 开启客户端
            /// </summary>
            public void Start()
            {
                // 开启心跳线程
                Thread t = new Thread(new ThreadStart(Heartbeat));
                t.IsBackground = true;
                t.Start();
            }        /// <summary>
            /// 向服务器发送心跳包
            /// </summary>
            private void Heartbeat()
            {
                while (!Offline)
                {
                    // 向服务端发送心跳包
                    Server.ReceiveHeartbeat(ClientID);
                    
                    System.Threading.Thread.Sleep(1000);
                }
            }
        }    /// <summary>
        /// 客户端信息
        /// </summary>
        public class ClientInfo
        {
            // 客户端ID
            public Int32 ClientID;        // 最后心跳时间
            public DateTime LastHeartbeatTime;        // 状态
            public Boolean State;
        }
    }
      

  4.   

    判断连接断开分为两种情况:
    第一种,连接的一方应用程序关闭、主动断开等正常行为,连接的另一方的Receive会返回长度0,通过判断Receive的长度来获知对方是否已断开。
    第二种,意外断线、长时间没有数据交互导致一方的路由器抛弃了临时的端口映射等,这种在连接的两端都不会出现异常,但连接实际上已经不可用了。这种要通过心跳来判断,心跳在winsock本身已经实现,应用程序需要设置一下iocontrol.//每四个字节一个整数,第一个整数1表示启用,第二个整数0x1388表示设置以后过5000毫秒开始发送心跳,第三个整数0x1388表示每5000毫秒发送一次心跳。
    byte[] inValue = new byte[] { 1, 0, 0, 0, 0x88, 0x13, 0, 0, 0x88, 0x13, 0, 0 };
    //在接受方接受连接或者发起方连接成功之后,对该连接的套接字设置iocontrol
    socket.IOControl(IOControlCode.KeepAliveValues, inValue, null);当上述意外情况发生时,Receive的一方会产生error,通过try catch或者SocketError可以捕获,捕获后处理即可。
      

  5.   

    设置了IOControlCode.KeepAliveValues之后,如果发生意外断线或者长时间未通讯导致的路由器抛弃端口映射,设置一方会继续尝试发送大约10次心跳,上面设置为间隔5000毫秒,就是大约50秒之后receive一方会捕获到异常。
      

  6.   

    糊涂了口不择言,不好意思,纠正一下。在设置了IOControlCode.KeepAliveValues之后,“长时间未通讯导致的路由器抛弃端口映射”不会发生。
      

  7.   

    谢谢这个问题已经解决了 
    看您的名字一定是socket的大牛了 还想请教一个问题 :
    我在进行数据接收的时候遇到了一个问题:如果通讯中网线拔掉或者发送完成之后服务端没有发送给我信息,但是客户端依然执行了int i = socket.Receive(receiveBytes);,程序就会死在这里。据说可以通过异步调用的方法解决这个问题,有没有简单的例子供学习一下,谢谢!
      

  8.   

    你只看到我的名字
    没看到我名字下面的签名?
    北大出来的
    能有什么大牛
    caozhy才是大牛