C# Socket关于心跳包判断连接的疑惑

时间:2022-10-13 19:30:44
一个服务端与客户端通信的程序,想在里面添加心跳来确保连接以及能断线重连,我先在客户端添加了如下代码定时向服务端发送一个包:
    public void RunKeepAlive()
        {
            KeepAliveThread = new Thread(new ThreadStart(RunKeepAliveThread));
            KeepAliveThread.IsBackground = true;
            KeepAliveThread.Start();
        }

        /// <summary>
        /// 保持请求
        /// </summary>
        private static void RunKeepAliveThread()
        {
            while (true)
            {
                if (isNeedAlive)
                {
                    Thread.Sleep(90 * 1000);
                    SendKeepAliveCommand();
                }
            }
        }

        public static void SendKeepAliveCommand()
        {
            try
            {
                string message = string.Empty;
                message = Environment.UserName;
                message += "|" + Environment.UserDomainName;
                message += "|" + MessageSN;
                message += "|" + "1";
                message += "|" + "1";
                Send(client, message);
            }
            catch (Exception ex)
            {
                log.Error("发送保持请求错误",ex);
            }
        }

而在服务端会在如下代码中接收:
 private void AnalysisLinkData(string recv, Socket handler)
        {
            log.Info((handler.RemoteEndPoint as IPEndPoint).Address.ToString() + ": " + recv);//获取客户端的IP地址和端口信息
            Console.WriteLine("count:" + serverLinkClientGroup.ClientGroup.Count);
            this.str = string.Empty;
            string[] strArray = recv.Split(new char[] { '|' });
            serverLinkClient client;
            client = new serverLinkClient(handler);
            if ((strArray.Length >= 3) && (strArray[2] == "0"))//只有在客户端连接时,才会进入此循环
            {
                //serverLinkClient client;
                StringBuilder builder = new StringBuilder("1=1");
                builder.Append(" AND USER_ID = '" + strArray[0] + "'");
                builder.Append(" AND USER_DOMAIN =  '" + strArray[1] + "'");
                List<Maticsoft.Model.USER_IP> modelList = this.currentuserBll.GetModelList(builder.ToString());
                if ((modelList == null) || (modelList.Count == 0))
                {
                    if (strArray[3] == "0")
                    {
                        this.userModel = new Maticsoft.Model.USER_IP();
                        this.userModel.ID = this.currentuserBll.GetMaxId();
                        this.userModel.USER_ID = strArray[0];
                        this.userModel.USER_DOMAIN = strArray[1];
                        this.userModel.TOUSER_IP = (handler.RemoteEndPoint as IPEndPoint).Address.ToString();
                        this.userModel.USER_PORT = new decimal?((handler.RemoteEndPoint as IPEndPoint).Port);
                        this.userModel.SYS_TIME = DateTime.Now.ToString();
                        this.currentuserBll.Add(this.userModel);
                        this.userModelList.Add(this.userModel);
                        client = new serverLinkClient(handler)
                        {
                            CLIENTIPADDRESS = this.userModel.TOUSER_IP,
                            CLIENTPORT = this.userModel.USER_PORT.ToString(),
                            CLIENTTOUSERID = this.userModel.USER_ID
                        };
                        if (serverLinkClientGroup.CheckClient(this.userModel.TOUSER_IP + ":" + this.userModel.USER_PORT) == null)
                        {
                            serverLinkClientGroup.AddNewClient(client);
                            client.CLIENTIPADDRESS = (handler.RemoteEndPoint as IPEndPoint).Address.ToString();
                            client.CLIENTPORT = (handler.RemoteEndPoint as IPEndPoint).Port.ToString();
                        }
                    }
                }
                else if (modelList.Count == 1)
                {
                    modelList[0].TOUSER_IP = (handler.RemoteEndPoint as IPEndPoint).Address.ToString();
                    modelList[0].USER_PORT = new decimal?((handler.RemoteEndPoint as IPEndPoint).Port);
                    this.currentuserBll.Update(modelList[0]);
                    if (serverLinkClientGroup.CheckClient(modelList[0].TOUSER_IP + ":" + modelList[0].USER_PORT) == null)
                    {
                        //client = new serverLinkClient(handler);
                        serverLinkClientGroup.AddNewClient(client);
                        client.CLIENTTOUSERID = strArray[0];
                        client.CLIENTIPADDRESS = (handler.RemoteEndPoint as IPEndPoint).Address.ToString();
                        client.CLIENTPORT = (handler.RemoteEndPoint as IPEndPoint).Port.ToString();
                    }
                }
            }
            Console.WriteLine("countnum:" + serverLinkClientGroup.ClientGroup.Count);
            //else
            //{
            //    Console.WriteLine("接收到数据为null{0}"+recv);
            //    serverLinkClientGroup.RemoveClient(client);
              
            //}
        }

本身服务端是接收到客户端的消息后,然后从数据库中读取数据发送给客户端,现在我不是很明白服务端在接收到客户端的心跳后如何返回,并且设置重连的方法,希望有做过的帮忙解答下,谢谢!

13 个解决方案

#1


心跳包一般是用来踢客户端的把,客户端为了防止被服务器当作闲置客户端占用资源而踢掉,要不停发心跳包给服务器

#2


如果服务器接受到心跳包,而且发现客户端是个新连接,就维护这个链接,直到断开,客户端发现断开后自己重连

#3


首先,你要自己看自己的“服务器端”到底有没有解析各种命令。假设你的服务器端并不能解析处理几个、几十个、上百个命令的,而是想当然地认为只会有“一个”数据库查询命令,那么你这个服务器端代码就缺少逻辑基础。从0 倒1是最难的,这是一个飞跃,需要悟性。而从1到2到......则比较容易理解。

其次,心跳包是一个命令,需要你现在信令文档中定义出来心跳命令跟数据库查询命令的格式有什么不同。服务器端解析到心跳请求,那么返回给(根据你信令设计,比如说)“OK”就行了,跟什么数据库毫无关系。

所谓的“重新连接”是指客户端在发送数据出错、或者没有得到服务器端返回(比如说)“OK”的时候,它去重新建立对服务器的连接。你是过分地抠字眼儿,而没有去理解。

#4


为什么要发 心跳包?也就是说一定要维持连接是通的?
只有一种请境况:服务端会毫无规则的向客户端发送数据
你确实有这种需求吗?

那么,定时发送心跳包来维持长连接 和 定时用短连接询问服务器 的区别在哪里?

#5


引用 4 楼 xuzuning 的回复:
为什么要发 心跳包?也就是说一定要维持连接是通的?
只有一种请境况:服务端会毫无规则的向客户端发送数据
你确实有这种需求吗?

那么,定时发送心跳包来维持长连接 和 定时用短连接询问服务器 的区别在哪里?
对,要保证连接时通的,服务端是从数据库取数据发送给客户端,因为数据库中的数据不知确定时间写入,所以需要保证客户端在登陆正常的情况下,能和服务端保持连接,这样数据库中来数据了,服务端就可以发送给客户端。

#6


你想清楚了吗:服务端是如何知道数据库来数据了?

如果在两次心跳包之间发生了断线,而此时正好数据库有数据来,你有该如何处理?

#7


引用 6 楼 xuzuning 的回复:
你想清楚了吗:服务端是如何知道数据库来数据了?

如果在两次心跳包之间发生了断线,而此时正好数据库有数据来,你有该如何处理?
服务端如何知道来数据,我是开了一个线程每个20s执行一次来获取数据以及发送数据到客户端;如果出现版主说的那种断线,等客户端和服务端重新连接再发送。

#8


引用 5 楼 cwt19902010 的回复:
对,要保证连接时通的,服务端是从数据库取数据发送给客户端,因为数据库中的数据不知确定时间写入,所以需要保证客户端在登陆正常的情况下,能和服务端保持连接,这样数据库中来数据了,服务端就可以发送给客户端。


服务器端有什么消息要通知客户端,直接发送数据给客户端就行了,怎么又跟数据扯在一起?

服务器端有数据发给客户端,跟服务器端把数据异步备份起来(也许将来需要重发一次),这个是没有直接关系的。你都会长连接了,又来搞什么轮询数据库,这等于说你完全没有搞懂即时通讯的基本原理。

#9


引用 8 楼 sp1234 的回复:
Quote: 引用 5 楼 cwt19902010 的回复:
对,要保证连接时通的,服务端是从数据库取数据发送给客户端,因为数据库中的数据不知确定时间写入,所以需要保证客户端在登陆正常的情况下,能和服务端保持连接,这样数据库中来数据了,服务端就可以发送给客户端。


服务器端有什么消息要通知客户端,直接发送数据给客户端就行了,怎么又跟数据扯在一起?

服务器端有数据发给客户端,跟服务器端把数据异步备份起来(也许将来需要重发一次),这个是没有直接关系的。你都会长连接了,又来搞什么轮询数据库,这等于说你完全没有搞懂即时通讯的基本原理。
你好,我的意思,是服务端收到了客户端发来的心跳包,怎么返回给客户端确认连接正常。

#10


怎么又跟数据扯在一起?     -->   怎么又跟数据库扯在一起?


对于一些小OA程序开发者,他不理解服务器即时发送数据给前端的必要性,所以他只知道轮询数据库进行 select 查询。这基本上就等于说,腾讯有20万台服务器,服务于几亿并发用户,腾讯必须做到一个消息立刻发给前端的应用;而假设是轮询数据库的,那么它需要8千万台服务器,而且还经常卡死,腾讯早就倒闭了,只能做点小OA了。

#11


引用 9 楼 cwt19902010 的回复:
你好,我的意思,是服务端收到了客户端发来的心跳包,怎么返回给客户端确认连接正常。


客户端发一个(比如说)”Hello“,服务器回答一个”OK“。

不要翻来覆去地跟数据库什么的纠缠在一起,完全没有关系。即使是有些数据写到了数据库,同时又需要发到客户端端,那么不等花上千万倍的去写数据库,推送消息的动作也早就做完了,也是跟数据库没有关系。

#12


引用 11 楼 sp1234 的回复:
Quote: 引用 9 楼 cwt19902010 的回复:
你好,我的意思,是服务端收到了客户端发来的心跳包,怎么返回给客户端确认连接正常。


客户端发一个(比如说)”Hello“,服务器回答一个”OK“。

不要翻来覆去地跟数据库什么的纠缠在一起,完全没有关系。即使是有些数据写到了数据库,同时又需要发到客户端端,那么不等花上千万倍的去写数据库,推送消息的动作也早就做完了,也是跟数据库没有关系。


引用 11 楼 sp1234 的回复:
Quote: 引用 9 楼 cwt19902010 的回复:
你好,我的意思,是服务端收到了客户端发来的心跳包,怎么返回给客户端确认连接正常。


客户端发一个(比如说)”Hello“,服务器回答一个”OK“。

不要翻来覆去地跟数据库什么的纠缠在一起,完全没有关系。即使是有些数据写到了数据库,同时又需要发到客户端端,那么不等花上千万倍的去写数据库,推送消息的动作也早就做完了,也是跟数据库没有关系。
大哥说的是,就是数据库这里我没弄懂,我现在是接收到了客户端发来的消息,再原封不动的发回给客户端。

#13


“大哥说的是”...把握都看笑哒!!!好吧,不扯多了,你的问题非常简单,就是不知道怎么回传心跳包:
1、服务端需要记录每个客户端的最后发送数据时间,每次收到客户端请求就跟新这个时间,不管这个请求是心跳还是其他逻辑请求
2、服务端开一个后台线程定期检测这个最后记录时间,超过了限制(比如2分分钟都没有交互),就主动断开连接
3、服务端收到心跳后,回什么都可以,甚至可以不回,客户段也没有重发心跳的必要,定期发一次就是了,收不到多半是网络不通了,断了就断了呗。这个非常简单,你不要想复杂了

#1


心跳包一般是用来踢客户端的把,客户端为了防止被服务器当作闲置客户端占用资源而踢掉,要不停发心跳包给服务器

#2


如果服务器接受到心跳包,而且发现客户端是个新连接,就维护这个链接,直到断开,客户端发现断开后自己重连

#3


首先,你要自己看自己的“服务器端”到底有没有解析各种命令。假设你的服务器端并不能解析处理几个、几十个、上百个命令的,而是想当然地认为只会有“一个”数据库查询命令,那么你这个服务器端代码就缺少逻辑基础。从0 倒1是最难的,这是一个飞跃,需要悟性。而从1到2到......则比较容易理解。

其次,心跳包是一个命令,需要你现在信令文档中定义出来心跳命令跟数据库查询命令的格式有什么不同。服务器端解析到心跳请求,那么返回给(根据你信令设计,比如说)“OK”就行了,跟什么数据库毫无关系。

所谓的“重新连接”是指客户端在发送数据出错、或者没有得到服务器端返回(比如说)“OK”的时候,它去重新建立对服务器的连接。你是过分地抠字眼儿,而没有去理解。

#4


为什么要发 心跳包?也就是说一定要维持连接是通的?
只有一种请境况:服务端会毫无规则的向客户端发送数据
你确实有这种需求吗?

那么,定时发送心跳包来维持长连接 和 定时用短连接询问服务器 的区别在哪里?

#5


引用 4 楼 xuzuning 的回复:
为什么要发 心跳包?也就是说一定要维持连接是通的?
只有一种请境况:服务端会毫无规则的向客户端发送数据
你确实有这种需求吗?

那么,定时发送心跳包来维持长连接 和 定时用短连接询问服务器 的区别在哪里?
对,要保证连接时通的,服务端是从数据库取数据发送给客户端,因为数据库中的数据不知确定时间写入,所以需要保证客户端在登陆正常的情况下,能和服务端保持连接,这样数据库中来数据了,服务端就可以发送给客户端。

#6


你想清楚了吗:服务端是如何知道数据库来数据了?

如果在两次心跳包之间发生了断线,而此时正好数据库有数据来,你有该如何处理?

#7


引用 6 楼 xuzuning 的回复:
你想清楚了吗:服务端是如何知道数据库来数据了?

如果在两次心跳包之间发生了断线,而此时正好数据库有数据来,你有该如何处理?
服务端如何知道来数据,我是开了一个线程每个20s执行一次来获取数据以及发送数据到客户端;如果出现版主说的那种断线,等客户端和服务端重新连接再发送。

#8


引用 5 楼 cwt19902010 的回复:
对,要保证连接时通的,服务端是从数据库取数据发送给客户端,因为数据库中的数据不知确定时间写入,所以需要保证客户端在登陆正常的情况下,能和服务端保持连接,这样数据库中来数据了,服务端就可以发送给客户端。


服务器端有什么消息要通知客户端,直接发送数据给客户端就行了,怎么又跟数据扯在一起?

服务器端有数据发给客户端,跟服务器端把数据异步备份起来(也许将来需要重发一次),这个是没有直接关系的。你都会长连接了,又来搞什么轮询数据库,这等于说你完全没有搞懂即时通讯的基本原理。

#9


引用 8 楼 sp1234 的回复:
Quote: 引用 5 楼 cwt19902010 的回复:
对,要保证连接时通的,服务端是从数据库取数据发送给客户端,因为数据库中的数据不知确定时间写入,所以需要保证客户端在登陆正常的情况下,能和服务端保持连接,这样数据库中来数据了,服务端就可以发送给客户端。


服务器端有什么消息要通知客户端,直接发送数据给客户端就行了,怎么又跟数据扯在一起?

服务器端有数据发给客户端,跟服务器端把数据异步备份起来(也许将来需要重发一次),这个是没有直接关系的。你都会长连接了,又来搞什么轮询数据库,这等于说你完全没有搞懂即时通讯的基本原理。
你好,我的意思,是服务端收到了客户端发来的心跳包,怎么返回给客户端确认连接正常。

#10


怎么又跟数据扯在一起?     -->   怎么又跟数据库扯在一起?


对于一些小OA程序开发者,他不理解服务器即时发送数据给前端的必要性,所以他只知道轮询数据库进行 select 查询。这基本上就等于说,腾讯有20万台服务器,服务于几亿并发用户,腾讯必须做到一个消息立刻发给前端的应用;而假设是轮询数据库的,那么它需要8千万台服务器,而且还经常卡死,腾讯早就倒闭了,只能做点小OA了。

#11


引用 9 楼 cwt19902010 的回复:
你好,我的意思,是服务端收到了客户端发来的心跳包,怎么返回给客户端确认连接正常。


客户端发一个(比如说)”Hello“,服务器回答一个”OK“。

不要翻来覆去地跟数据库什么的纠缠在一起,完全没有关系。即使是有些数据写到了数据库,同时又需要发到客户端端,那么不等花上千万倍的去写数据库,推送消息的动作也早就做完了,也是跟数据库没有关系。

#12


引用 11 楼 sp1234 的回复:
Quote: 引用 9 楼 cwt19902010 的回复:
你好,我的意思,是服务端收到了客户端发来的心跳包,怎么返回给客户端确认连接正常。


客户端发一个(比如说)”Hello“,服务器回答一个”OK“。

不要翻来覆去地跟数据库什么的纠缠在一起,完全没有关系。即使是有些数据写到了数据库,同时又需要发到客户端端,那么不等花上千万倍的去写数据库,推送消息的动作也早就做完了,也是跟数据库没有关系。


引用 11 楼 sp1234 的回复:
Quote: 引用 9 楼 cwt19902010 的回复:
你好,我的意思,是服务端收到了客户端发来的心跳包,怎么返回给客户端确认连接正常。


客户端发一个(比如说)”Hello“,服务器回答一个”OK“。

不要翻来覆去地跟数据库什么的纠缠在一起,完全没有关系。即使是有些数据写到了数据库,同时又需要发到客户端端,那么不等花上千万倍的去写数据库,推送消息的动作也早就做完了,也是跟数据库没有关系。
大哥说的是,就是数据库这里我没弄懂,我现在是接收到了客户端发来的消息,再原封不动的发回给客户端。

#13


“大哥说的是”...把握都看笑哒!!!好吧,不扯多了,你的问题非常简单,就是不知道怎么回传心跳包:
1、服务端需要记录每个客户端的最后发送数据时间,每次收到客户端请求就跟新这个时间,不管这个请求是心跳还是其他逻辑请求
2、服务端开一个后台线程定期检测这个最后记录时间,超过了限制(比如2分分钟都没有交互),就主动断开连接
3、服务端收到心跳后,回什么都可以,甚至可以不回,客户段也没有重发心跳的必要,定期发一次就是了,收不到多半是网络不通了,断了就断了呗。这个非常简单,你不要想复杂了