心跳机制讲解及实例

什么是心跳机制

心跳机制出现在tcp长连接中,客户端和服务器之见定时发送一种特殊的数据包通知对方还在线,以确保tcp链接地可靠性,有可能tcp链接由于某些原因(列入网线被拔了,突然断电)导致客户端断了,但是服务器不知道客户端断了,服务器还保持与客户端连接的状态,所以为了不浪费资源,需要知道客户端非正常中断,服务器把断开客户端断开链接,需要加入心跳包机制

tcp 需不需要心跳?

需要心跳机制tcp本身内置了keeplive心跳机制,但是这种内置的心跳机制不足以满足所有的情况,所以有必要自己写心跳机制
有必要自己写心跳机制

3 那些网络情况下不满足keepalive心跳机制 

1 tcp 属于 keeepalive心跳机制 有些设备不会处理keepalive心跳包
2 keeepalive心跳机制只能说明连接是活的,应用实现心跳机制,可以保持连接是活的应用正常工作

心跳检测步骤

1.客户端每隔一个时间间隔发生一个探测包给服务器
2.客户端发包时启动一个超时定时器
3.服务器端接收到检测包,应该回应一个包
4. 如果客户机收到服务器的应答包,则说明服务器正常,删除超时定时器
5. 如果客户端的超时定时器超时,依然没有收到应答包,则说明服务器挂了

实例

拥有单发群发指定部分功能

客户端:

搭建客户端连接的界面

代码如下:

public partial class Form1 : Form
{public Form1(){InitializeComponent();}// 点击链接按钮// 1 创建客户端对象// 2 连接服务器// 3 创建网络基础流发消息,write发消息// 4 创建网络基础流接消息,read接消息// 5 断开链接close()TcpClient client;private void button1_Click(object sender, EventArgs e){if(button1.Text == "连接"){try{// 开始连接client = new TcpClient();client.Connect(comboBox1.Text, int.Parse(comboBox2.Text));button1.Text = "断开";// 读取数据StartRead();// 启动心跳机制HeartBeat();}catch(Exception ex){MessageBox.Show("连接失败");}}else{// 断开连接client.Close();timer1.Stop(); // 关闭心跳包// 把心跳定时器关闭button1.Text = "连接";}}void StartRead(){byte[] bs = new byte[1024];Task.Run(() =>{try {while (true){int count = client.GetStream().Read(bs,0,bs.Length);string msg = Encoding.UTF8.GetString(bs,0,count);richTextBox1.Invoke((Action)(() =>{richTextBox1.AppendText(msg+"\t\n");}));}}catch(Exception ex){button1.Text = "连接";}});}// 开启心跳方法Timer timer1;void HeartBeat(){timer1 = new Timer();timer1.Interval = 3000;timer1.Tick += Timer_Tick;timer1.Start();}// 定时器方法 定时发送心跳包private void Timer_Tick(object sender, EventArgs e){// 心跳包发送数据的内容需要跟后台事先约定好,什么数据是心跳包// client.GetStream().Write(new byte[] { 1 }, 0, 1);}// 点击发送按钮设置普通的消息包private void button2_Click(object sender, EventArgs e){timer1.Stop();timer1.Interval = 3000;timer1.Tick += Timer_Tick;timer1.Start();byte[] bs = Encoding.UTF8.GetBytes(textBox1.Text);// 普通消息在发送时候,需要字节数组的第一位置为0 byte[] bs1 = new byte[bs.Length+1];bs1[0] = 0;// 字节数组的第一位设置为0bs.CopyTo(bs1, 1); // 把消息复制到新数组的第一位开始client.GetStream().Write(bs1,0,bs1.Length);}
}

服务端:

Server类:

internal class Server
{TcpListener listen;//1通过构造函数创建服务器对象public Server(IPAddress ip,int port) {listen = new TcpListener(ip, port);}//2 封装开启监听的方法public void Start(){listen.Start(100);//开启监听//接受客户端的连接StartConnect();//调用扫描心跳方法SaoMiao();}//3接受客户端的连接 封装一监听客户端连接的方法//保存所有的客户端字典, 键是ip 值是客户端,Dictionary<string,TcpClient> clientDic = new Dictionary<string,TcpClient>();//字典保存客户端和当前连接服务器时间点Dictionary<string,DateTime> heartDic  = new Dictionary<string,DateTime>();public event Action<TcpClient> 有客户端连入的事件; //当客户端连入触发。绑定事件,void StartConnect(){Task.Run(() =>{while (true) //接入多个客户端{TcpClient client = listen.AcceptTcpClient();string ip = client.Client.RemoteEndPoint.ToString(); //获取远程ip//保存当前客户端clientDic.Add(ip, client);//记录当前客户端心跳 连接成功时候记录当前客户端时间点heartDic.Add(ip, DateTime.Now);//调用事件函数 触发事件,有客户端连入的事件?.Invoke(client);//4 接收客户端发来的消息ReceiveMsg(client);}});}//4 接收客户端发来的消息  //封装接受的消息public event Action<string> 客户端断开事件; //当客户端断开时候调用事件public event Action<TcpClient, byte[]> 接受到消息的事件;//接收到消息调用void ReceiveMsg(TcpClient t1){NetworkStream stream = t1.GetStream();string ip = t1.Client.RemoteEndPoint.ToString() ;byte[] bs = new byte[1024];Task.Run(() =>{try{while (true){int count = stream.Read(bs, 0, bs.Length);if(count == 0){//客服端断开throw new Exception("客户端断开连接");}//如果接收到数据长度不为0,//必须判断是否是心跳包 事先约定好:如果数据第一位是0的时候,当成普通数据包//如果数据第一位是1说明是心跳包switch (bs[0]) //判断第一位数据是不是0{case 0: //普通数据 取出来的时候不需要显示第一位标识符//skip 从第一位开始截取// take 到指定位置的元素为止//第一位0、1代表是否是心跳包标识符,//最后一位占位符byte[] body= bs.Skip(1).Take(count - 1).ToArray();//要么群发 要么单发接受到消息的事件?.Invoke(t1, body);break;case 1: //发的是心跳包//修改是心跳包发的时间点heartDic[ip] = DateTime.Now;break;}}}catch(Exception e){//从字典把客户端清除掉clientDic.Remove(ip);//如果客户端断开了,打印客户断开客户端断开事件?.Invoke(ip);//删除心跳记录heartDic.Remove(ip);}});}//遍历所有客户端 扫描是否在未超时的时间内void SaoMiao(){Task.Run(() =>{while (true){Thread.Sleep(4000);//线程休眠4sDateTime now1 = DateTime.Now;foreach (var item in heartDic)//遍历所有心跳记录{//now1 当前时间点// item.Value 服务器接收客户端发来的心跳包时间//new TimeSpan(0, 0, 4) 时分秒if (now1 - item.Value > new TimeSpan(0, 0, 4)){Console.WriteLine(item.Key + "掉线了");if (clientDic.Keys.Contains(item.Key)){Send("你已经掉线了骚年:" + item.Key, item.Key);clientDic.Remove(item.Key);}else{Console.WriteLine(item.Key + "在线");}}}}});}// 无参数的构造函数public Server(){}//群发方法 向所有的客户端发消息public void Send(string content){byte[] bs = Encoding.UTF8.GetBytes(content);foreach (var item in clientDic) //遍历所有的客户端{item.Value.GetStream().Write(bs, 0, bs.Length);}}//指定给谁发public void Send(string content, string ip){byte[] bs = Encoding.UTF8.GetBytes(content);//根据ip取出客户端,从字典取clientDic[ip].GetStream().Write(bs, 0, bs.Length);}//指定给哪些客户端发//send("你好", ["192.","127"])public void Send(string content, string[] ips){byte[] bs = Encoding.UTF8.GetBytes(content);foreach (var item in clientDic) //所有客户端{//item.key 键 ip字符串//item.value 值 客户端对象if (ips.Contains(item.Key)){//如果ips数组包含目标客户端item.Value.GetStream().Write(bs, 0, bs.Length);}}}}

Program:

internal class Program
{static Server server;static void Main(string[] args){server = new Server(IPAddress.Any,3333);server.Start(); // 除了服务器监听方法, 监听客户连接的方法 扫描客户端是否在线的方法//如果监听到有客户端连接的时候,打印哪个终端连入到服务器了 使用事件封装server.有客户端连入的事件 += 有客户端连入服务器方法; //绑定事件server.客户端断开事件 += f2;server.接受到消息的事件 += f3;Console.ReadKey();}//相当于点击之后的回调方法,再客户端连接成功之后调用这个方法public static void 有客户端连入服务器方法(object obj){TcpClient t1 = obj as TcpClient;Console.WriteLine(t1.Client.RemoteEndPoint+"连接到服务器");}public static void f2(object obj){Console.WriteLine(obj.ToString()+"断开连接");}public static void f3(TcpClient t1, byte[] b1){// t1.GetStream().Write(b1, 0, b1.Length);string content = Encoding.UTF8.GetString(b1);// 如果群发// server.Send(content);// 如果单发// server.Send(content, "这里是端口号");// 如果指定部分string[] ips = new string[] { "这里是端口号", "这里是端口号" };server.Send(content, ips);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/29599.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SQL注入-中篇

SQL盲注 一、时间盲注 模拟环境&#xff1a;Less-9 概述 延迟注入&#xff0c;一种盲注的手法&#xff0c;提交对执行时间敏感的sql语句&#xff0c;通过执行时间的长短来判断是否执行成功。 时间注入函数 sleep() if() ascii() substring() length() mid()判断是否存在延…

存储文件夹下所有.cpp和.h的代码到对应的txt文件里

最近大半年刷了160多天的题&#xff0c;每次刷的时候都要新建一个VS文件&#xff0c;所以文件内存太大了&#xff0c;又舍不得删&#xff0c;就用ai整了一个脚本&#xff0c;可将当前路径下的所有文件里的.cpp和.h文件储存到相应名字的txt文件里&#xff0c;若文件夹下还有文件…

SpringBoot + Maven 项目的创建

文章目录 1、Maven2、SpringBoot3、二者之间的联系4、项目的创建 在创建项目之前&#xff0c;肯定要知道他们之间的区别 1、Maven maven是一个跨平台的项目管理工具。它是Apache的一个开源项目&#xff0c;主要服务于基于Java平台的项目构建、依赖管理和项目信息管理。 比如说…

Spring注解----------@Deprecated

情景&#xff1a; 在我们开发过程中&#xff0c;有时候会遇到我们需要将几个类中的方法集中到一个类中&#xff0c;但是我们又不希望把我们的原来的类删掉&#xff08;就是单纯的不想删除&#xff0c;都是我写的代码我不想杀死我的结晶&#xff0c;不能说我写的是shi&#xff…

为什么要选择华为 HCIE-Security 课程?

2020 年我国网络安全市场规模达到 680 亿元&#xff0c;同比增长 25%。随着对网络安全的愈加重视及布局&#xff0c;市场规模将持续扩大。 近年来&#xff0c;随着“云大物工移智”等新兴技术的快速发展和普及应用&#xff0c;数字化已经融入社会经济生活的方方面面&#xff0c…

自我激励学习提升语言模型的推理能力

随着人工智能技术的快速发展&#xff0c;语言模型&#xff08;LMs&#xff09;在各种下游任务中展现出了卓越的能力。特别是在少样本&#xff08;few-shot&#xff09;和零样本&#xff08;zero-shot&#xff09;学习环境中&#xff0c;通过吸收特定任务的指令和示例&#xff0…

CLIP: Learning Transferable Visual Models From Natural Language Supervision

1、引言 论文链接&#xff1a;ReadPaper 现在最先进的计算机视觉系统都是训练模型来预测一组固定的、预定义好的目标类别&#xff08;如 ImageNet 的 1000 类和 COCO 的 80 类&#xff09;。这种受限制的监督形式限制了它们的通用性和可用性&#xff0c;因为需要额外的标记数据…

光纤通信基础(光纤的构造、工作原理、色散、工作频段、损耗、分类、不同标准及应用、接口类型、常见标示方法、熔接)

文章目录 光纤的构造&#xff1a;纤芯、包层、涂覆层光纤的工作原理&#xff1a;利用全反射来传输光信号光纤的色散光纤的工作频段光纤的损耗光纤的分类光纤的不同标准及应用光纤的接口类型&#xff08;SC、LC、ST、FC&#xff09;光纤的常见标示方法&#xff1a;如“FC/PC”&a…

项目准备和启动

1.什么是项目建议书&#xff1f; 2.项目建议书的内容 3.可行性分析方法 4.项目组织结构&#xff08;职能型 项目型 矩阵型&#xff09; 5.项目管理层决策层执行层之间的关系 6.软件项目的可行性分析包括哪几个方面&#xff1f;影响决策的关键因素又是什么&#xff1f; 软件项目…

鸿蒙开发实战:灵活定制Tabs组件,实现个性化页签布局

闪客 沉默的闪客 2024-06-16 20:01 陕西 大家好&#xff0c;又一个项目已经基本完成 是一个元服务英语单词卡片项目&#xff0c;后面一步一步的进行分析拆解&#xff0c;今天来实现一个Tabs组件自定义界面开发。 鸿蒙ArkUI 开发的时候&#xff0c;Tabs 组件很常用&#xff0c;…

CDA二级(Level II)数据分析师——考试内容梳理三(简单重点案例)

PR曲线是基于召回率的准确性来进行判断的&#xff1b; 混淆矩阵&#xff1a; ROC曲线以真阳性率&#xff08;敏感性、召回率&#xff09;为纵坐标&#xff0c;假阳性率&#xff08;1-特异性&#xff09;为横坐标 假阳性率&#xff1a;负样本中被误认为正样本的概率 FP/FPTN 真…

基于B/S版java语言+SpringBoot技术开发的云HIS系统源码 HIS系统住院业务模块常见问题及解决方案

基于B/S版java语言SpringBoot技术开发的云HIS系统源码 HIS系统住院业务模块常见问题及解决方案 随着医疗技术的不断提高&#xff0c;住院治疗已成为许多病人的常规选择。但是&#xff0c;住院治疗不仅需要医护人员的精心照顾&#xff0c;也需要个高效的信息系统来保证整个治疗过…

Unity制作透明材质直接方法——6.15山大软院项目实训

之前没有在unity里面接触过材质的问题&#xff0c;一般都是在maya或这是其他建模软件里面直接得到编辑好材质的模型&#xff0c;然后将他导入Unity里面&#xff0c;然后现在碰到了需要自己在Unity制作透明材质的情况&#xff0c;所以先搜索了一下有没有现成的方法&#xff0c;很…

C++迈向精通:当我尝试修改虚函数表

尝试修改虚函数表 本期纯整活儿好吧&#xff01;&#xff01;&#xff01;&#xff01; 初衷 有一天我突然开始好奇虚函数表是否真的存在&#xff0c;于是我开始想是否能够从C中查看或者调用虚函数表中的内容。&#xff0c;于是有了下面的操作。 操作过程 起初我并没有思路…

神经网络模型---AlexNet

一、AlexNet 1.导入tensorflow库&#xff0c;这里给简称为tf库 import tensorflow as tf from tensorflow.keras import datasets, layers, modelsdatasets&#xff1a;是用于训练和测试机器学习模型的数据集合 layers&#xff1a;是构建神经网络模型的关键组成部分 models&a…

Golang并发控制的三种方案

Channel Channel是Go在语言层面提供的一种协程间的通信方式&#xff0c;我们可以通过在协程中向管道写入数据和在待等待的协程中读取对应协程的次数来实现并发控制。 func main() {intChan : make(chan int, 5)waitCount : 5for i : 0; i < waitCount; i {go func() {intC…

上海SNEC光伏展参展总结--安科瑞

安科瑞戴婷 Acrel-Fanny 今年的snec上海光伏展吸引了来自全球各地的光伏行业专业人士及爱好者&#xff0c;本次展会共有来自30多个国家和地区的超过2000家企业参展&#xff0c;展出的光伏产品涵盖了太阳能电池、太阳能组件、逆变器、太阳能辅助设备等众多领域。 随着近年来光…

《收获,不止oracle》读书笔记一:oracle体系结构

从图中可以看出,oracle数据库是由实例和一组数据库文件组成。实例是由oracle开辟的内存区和一组后台进程组成的。

2024.6.18

Python的网络编程 网络四层 在开始前,我们需要先了解一下我们在网络通信过程中的四个层次 我们上网产生的数据都是经过协议栈一层一层的封装然后经网卡发送到网络&#xff0c;经网络发送到服务端&#xff0c;然后服务端又是一层一层的解封装拿到自己想要的数据。 我们学习的…

YOLOv8目标检测算法在地平线Bernoulli2架构BPU上高效部署参考(PTQ方案)30fps!

—— 以RDK X3为例&#xff0c;修改Head部分&#xff0c;8ms疾速Python后处理程序&#xff0c;30fps稳稳当当 本文在地平线对YOLOv8s的Backbone修改的基础上&#xff0c;提出一种在地平线Bernoulli2架构BPU上部署YOLOv8的后处理思路。使用640640分辨率&#xff0c;80类别基于C…