心跳机制讲解及实例

心跳机制讲解及实例心跳机制出现在 tcp 长连接中 客户端和服务器之见定时发送一种特殊的数据包通知对方还在线 以确保 tcp 链接地可靠性 有可能 tcp 链接由于某些原因 列入网线被拔了 突然断电 导致客户端断了 但是服

大家好,欢迎来到IT知识分享网。

什么是心跳机制

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

tcp 需不需要心跳?

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

心跳检测步骤

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;// 字节数组的第一位设置为0 bs.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);//线程休眠4s DateTime 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); } }

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/151696.html

(0)
上一篇 2025-03-12 14:10
下一篇 2025-03-12 14:20

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信