TCP心跳消息

客户端主动断开连接

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Lesson10 : MonoBehaviour
{void Start(){#region 知识点一 目前的客户端主动断开连接//目前在客户端主动退出时//我们会调用socket的 ShutDown和Close方法//但是通过调用这两个方法后 服务器端无法得知客户端已经主动断开//举例说明#endregion#region 知识点二 解决目前断开不及时的问题//1.客户端尝试使用Disconnect方法主动断开连接//Socket当中有一个专门在客户端使用的方法//Disconect方法//客户端调用该方法和服务器端断开连接//看是否是因为之前直接Close而没有调用Disconet造成服务器端无法及时获取状态//主要修改的逻辑://客户端://主动断开连接//服务端://1.收发消息时判断socket是否已经断开//2.处理删除记录的socket的相关逻辑(会用到线程锁)//2.自定义退出消息 //让服务器端收到该消息就知道是客户端想要主动断开//然后服务器端处理释放socket相关工作#endregion#region 总结 //客户端可以通过Disconnect方法主动和服务器端断开连接//服务器端可以通过Conected属性判断连接状态决定是否释放Socket //但是由于服务器端Conected变量表示的是上一次收发消息是否成功//所以服务器端无法准确判断客户端的连接状态//因此 我们需要自定义一条退出消息 用于准确断开和客户端之间的连接#endregion }void Update(){}
}

服务器中封装三个方法

       //添加待移除的socketpublic void AddDelSocket(ClientSocket socket){if(!delList .Contains (socket)){delList.Add(socket);}}//判断有没有断开连接的 将他移除public void CloseDelListSocket(){for (int i = 0; i < delList.Count; i++)CloseClientSocket(delList[i]);delList.Clear();}//关闭客户端的连接,从字典中移除public void CloseClientSocket(ClientSocket socket){lock (clientDic){this.socket.Close();if (clientDic.ContainsKey(socket.clientID))clientDic.Remove(socket.clientID);}}

自定义消息类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class quitMsg : BaseMsg
{public override int GetBytesNum(){return 8;}public override int GetID(){return 1003;}public override int Reading(byte[] bytes, int beginIndex = 0){return base.Reading(bytes, beginIndex);}public override byte[] Writing(){int index = 0;byte[] bytes = new byte[GetBytesNum()];WriteInt(bytes, GetID(), ref index);WriteInt(bytes, 0, ref index);return bytes;}
}

实现心跳消息

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Lesson11 : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){#region 知识点一 什么是心跳消息?//所谓心跳消息,就是在长连接中,客户端和服务端之间定期发送的一种特殊的数据包 //用于通知对方自己还在线,以确保长连接的有效性//由于其发送的时间间隔往往是固定的持续的,就像是心跳一样一直存在//所以我们称之为心跳消息#endregion  #region 知识点二 为什么需要心跳消息?//1.避免非正常关闭客户端时,服务器无法正常收到关闭连接消息   //通过心跳消息我们可以自定义超时判断,如果超时没有收到客户端消息,证明客户端已经断开连接  //2.避免客户端长期不发送消息,防火墙或者路由器会断开连接,我们可以通过心跳消息一直保持活跃状态#endregion     #region 知识点三 实现心跳消息 //客户端  //主要功能:定时发送消息    //服务器 //主要功能:不停检测上次收到某客户端消息的时间,如果超时则认为连接已经断开#endregion #region 总结     //心跳消息是长连接项目中必备的一套逻辑规则   //通过它可以帮助我们在服务器端及时的释放掉失效的socket    //可以有效避免当客户端非正常关闭时,服务器端不能及时判断连接已断开#endregion}// Update is called once per framevoid Update(){}
}

自定义心跳消息类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class HeartMsg : BaseMsg
{public override int GetBytesNum(){return 8;}public override int GetID(){return 999;}public override int Reading(byte[] bytes, int beginIndex = 0){return 0;}public override byte[] Writing(){int index = 0;byte[] bytes = new byte[GetBytesNum()];WriteInt(bytes, GetID(), ref index);WriteInt(bytes, 0, ref index);return bytes;}
}

修改服务器逻辑

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;namespace TeachServerTcpExe2
{class ClientSocket{private static int CLIENT_BEGIN_ID = 1;public int clientID;//是否是连接状态public bool Connect => this.socket.Connected;public Socket socket;//用于处理分包时缓存的字节数组和字节数组的长度private byte[] cacheBytes = new byte[1024 * 1024];private int cacheNum = 0;private long frontTime = -1;//上一次收到消息的时间private static int TIME_OUT_TIME = 10;//设置超时时间public ClientSocket (Socket socket){this.clientID = CLIENT_BEGIN_ID;this.socket = socket;++CLIENT_BEGIN_ID;//为了方便理解,所以开了一个线程专门计时,但是这种方式比较消耗性能,不建议这样使用//ThreadPool.QueueUserWorkItem(CheakTimeOut);}//间隔一段时间检测一次超时 如果超时就会主动断开客户端的连接public void CheakTimeOut(/*object obj*/){//while (Connect)//{if (frontTime == -1 && DateTime.Now.Ticks / TimeSpan.TicksPerSecond >= TIME_OUT_TIME){Program.socket.AddDelSocket(this);//break;}//Thread.Sleep(5000);//}}//我们应该封装一些方法//关闭public void Close(){if(socket!=null){socket.Shutdown(SocketShutdown.Both);socket.Close();socket = null;}}//发送public void Send(BaseMsg info){if (Connect){try{socket.Send(info.Writing());}catch (Exception e){Console.WriteLine("发消息失败:" + e.Message);Program.socket.AddDelSocket(this);}}elseProgram.socket.AddDelSocket(this);}//接收public void Receive(){if (!Connect){Program.socket.AddDelSocket(this);return;}try{if(socket .Available >0){byte[] result = new byte[1024 * 5];int receiveNum = socket.Receive(result);HandleReceiveMsg(result, receiveNum);收到数据后先解析字节数组前四个字节//int msgID = BitConverter.ToInt32(result, 0);//BaseMsg msg = null;//switch (msgID)//{//    case 1001://        msg = new PlayerMsg();//        msg.Reading(result, 4);//        break;//}//if (msg == null) return;//ThreadPool.QueueUserWorkItem(MsgHandle, msg);}//检测是否超时CheakTimeOut();}catch (Exception e){Console.WriteLine("收消息失败:"+e.Message);//解析消息出错,也认为把socket断开了Program.socket.AddDelSocket(this);}}private void MsgHandle(object obj){BaseMsg msg = obj as BaseMsg;if (msg is PlayerMsg){PlayerMsg playerMsg = msg as PlayerMsg;Console.WriteLine(playerMsg.playerID);Console.WriteLine(playerMsg.playerData .name);Console.WriteLine(playerMsg.playerData .lev);Console.WriteLine(playerMsg.playerData .atk);}else if(msg is quitMsg){//收到断开连接消息,将自己添加到待移除的列表Program.socket.AddDelSocket(this);}else if(msg is HeartMsg){//收到心跳消息,记录收到心跳消息的时间frontTime = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;Console.WriteLine("收到心跳消息");}}//处理分包、黏包问题的方法private void HandleReceiveMsg(byte[] receiveBytes, int receiveNum){int msgID = 0;int msgLength = 0;int nowIndex = 0;//收到消息时,应该看看之前有没有缓存的如果有的话 直接拼在后边receiveBytes.CopyTo(cacheBytes, cacheNum);cacheNum += receiveNum;//while循环解决黏包问题while (true){//每次将长度设置为-1,是为了避免上一次解析的数据 影响这一次的判断msgLength = -1;if (cacheNum - nowIndex >= 8){//解析IDmsgID = BitConverter.ToInt32(cacheBytes, nowIndex);nowIndex += 4;//解析消息长度msgLength = BitConverter.ToInt32(cacheBytes, nowIndex);nowIndex += 4;}if (cacheNum - nowIndex >= msgLength && msgLength != -1){//解析消息体BaseMsg baseMsg = null;switch (msgID){case 1001:baseMsg = new PlayerMsg();baseMsg .Reading(receiveBytes, nowIndex);break;case 1003:baseMsg = new quitMsg();//由于该消息没有消息体,不用进行反序列化 break;case 999:baseMsg = new HeartMsg();break;}if (baseMsg != null)ThreadPool.QueueUserWorkItem(MsgHandle,baseMsg);nowIndex += msgLength;if (nowIndex == cacheNum){break;}}else{//如果不满足,证明有分包//那么我们需要把当前收到的消息存下来//有待下次接收到消息后再做处理//receiveBytes.CopyTo(cacheBytes, 0);//cacheNum = receiveNum;//如果进行了id和长度的解析,但是没有成功解析消息体,nowindex需要减去移动的位置if (msgLength != -1)nowIndex -= 8;//就是把剩余没有解析的字节数组的内容 移到前面来 用于缓存下次继续解析Array.Copy(cacheBytes, nowIndex, cacheBytes, 0, cacheNum - nowIndex);cacheNum = cacheNum - nowIndex;break;}}}}
}

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

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

相关文章

【AIGC前沿】MiniMax海螺AI视频——图片/文本生成高质量视频

目录 1.MiniMax海螺AI视频简介 2.使用教程 1.MiniMax海螺AI视频简介 海螺视频&#xff0c;作为 MiniMax 旗下海螺 AI 平台精心打造的 AI 视频生成工具&#xff0c;致力于助力用户产出高品质视频内容。该工具依托 abab-video-1 模型&#xff0c;具备强大的文生视频功能。用户…

Kubeasz工具快速部署K8Sv1.27版本集群(二进制方式)

文章目录 一、基本信息二、服务器初始化操作三、使用Kubeasz部署K8S集群四、验证集群 一、基本信息 1、部署需要满足前提条件&#xff1a; 注意1&#xff1a;确保各节点时区设置一致、时间同步&#xff1b;注意2&#xff1a;确保在干净的系统上开始安装&#xff1b;注意3&…

在VMware上部署【Ubuntu】

镜像下载 国内各镜像站点均可下载Ubuntu镜像&#xff0c;下面例举清华网站 清华镜像站点&#xff1a;清华大学开源软件镜像站 | Tsinghua Open Source Mirror 具体下载步骤如下&#xff1a; 创建虚拟机 准备&#xff1a;在其他空间大的盘中创建存储虚拟机的目录&#xff0c…

2025年Postman的五大替代工具

虽然Postman是一个广泛使用的API测试工具&#xff0c;但许多用户在使用过程中会遇到各种限制和不便。因此&#xff0c;可能需要探索替代解决方案。本文介绍了10款强大的替代工具&#xff0c;它们能够有效替代Postman&#xff0c;成为你API测试工具箱的一部分。 什么是Postman&…

wow-rag—task5:流式部署

我们希望做一个流式输出的后端&#xff0c;然后让前端去捕获这个流式输出&#xff0c;并且在聊天界面中流式输出。 首先构造流式输出引擎。 # 构造流式输出引擎 query_engine index.as_query_engine(streamingTrue, similarity_top_k3,llmllm)然后生成response_stream&#x…

投资日记_道氏理论技术分析

主要用于我自己参考&#xff0c;我感觉我做事情的时候容易上头&#xff0c;忘掉很多事情。 技术分析有很多方法&#xff0c;但是我个人相信并实践的还是以道氏理论为根本的方法。方法千千万万只有适合自己价值观&#xff0c;习惯&#xff0c;情绪&#xff0c;性格的方法才是好的…

LangChain4j入门指南:Java开发者的AI应用新起点

什么是LangChain和LangChain4j&#xff1f; LangChain是⼀个⼤模型的开发框架&#xff0c;使⽤ LangChain 框架&#xff0c;程序员可以更好的利⽤⼤模型的能⼒&#xff0c;⼤⼤提⾼编 程效率。如果你是⼀个 Java 程序员&#xff0c;那么对 LangChain 最简单直观的理解就是&…

【实测闭坑】LazyGraphRAG利用本地ollama提供Embedding model服务和火山引擎的deepseek API构建本地知识库

LazyGraphRAG 2024年4月&#xff0c;为解决传统RAG在全局性的查询总结任务上表现不佳&#xff0c;微软多部门联合提出Project GraphRAG&#xff08;大模型驱动的KG&#xff09;&#xff1b;2024年7月&#xff0c;微软正式开源GraphRAG项目&#xff0c;引起极大关注&#xff0c…

压力测试实战指南:JMeter 5.x深度解析与QPS/TPS性能优化

一、压力测试基础概念 1.1 什么是压力测试&#xff1f; 定义&#xff1a;模拟极端负载场景验证系统性能极限 目的&#xff1a;发现性能瓶颈、评估系统可靠性、验证容错能力 常见类型&#xff1a;负载测试、压力测试、稳定性测试、峰值测试 1.2 核心性能指标解析 1.2.1 QP…

嵌入式4-Modbus

1.Modbus Modbus 是一种广泛应用于工业自动化领域的通信协议&#xff0c;用于在不同设备&#xff08;如传感器、PLC、变频器、仪表等&#xff09;之间交换数据。它支持串行通信&#xff08;如 RS232、RS485&#xff09;和以太网通信&#xff08;Modbus TCP&#xff09;&#x…

机器学习-手搓KNN算法

一、简介 K最近邻&#xff08;K-Nearest Neighbors, KNN&#xff09;​ 是一种简单且直观的监督学习算法&#xff0c;适用于分类和回归任务。其核心思想是&#xff1a;​相似的数据点在特征空间中彼此接近。KNN通过计算新样本与训练数据中各个样本的距离&#xff0c;找到最近的…

Linux|fork命令及其使用的写时拷贝技术

fork复制进程 fork通过以下步骤来复制进程&#xff1a; 分配新的进程控制块&#xff1a;内核为新进程分配一个新的进程控制块&#xff08;PCB&#xff09;&#xff0c;用于存储进程的相关信息&#xff0c;如进程 ID、状态、寄存器值、内存指针等。复制进程地址空间&#xff1…

Hoppscotch 开源API 开发工具

Hoppscotch 是一个开源的 API 开发工具&#xff0c;旨在为开发者提供一个轻量级、快速且功能丰富的 API 开发和调试平台。以下是对其主要特性和功能的详细介绍&#xff1a; 1. 轻量级与高效 Hoppscotch 采用简约的 UI 设计&#xff0c;注重易用性和高效性。它支持实时发送请求…

Datawhale大语言模型-Transformer以及模型详细配置

Datawhale大语言模型-Transformer以及模型详细配置 Transformer模型位置编码前馈层网络注意力机制多头自注意力编码器解码器 大语言模型的参数配置归一化激活函数位置编码旋转位置编码代码内容实现 注意力机制 参考资料 Transformer模型 当前主流的大语言模型都基于 Transform…

iPhone 16怎么编辑图片?图片编辑技巧、软件分享

在当今这个视觉信息爆炸的时代&#xff0c;一张经过精心编辑的图片往往能够瞬间抓住观众的眼球&#xff0c;而 iPhone 16凭借其卓越的硬件性能和丰富的软件生态&#xff0c;在图片编辑领域展现出了非凡的实力&#xff0c;成为众多摄影爱好者和创意工作者的得力助手。 一、编辑效…

代码随想录_动态规划

代码随想录 动态规划 509.斐波那契数 509. 斐波那契数 斐波那契数 &#xff08;通常用 F(n) 表示&#xff09;形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始&#xff0c;后面的每一项数字都是前面两项数字的和。也就是&#xff1a; F(0) 0&#xff0c;F(1) 1 F(n…

【虚幻引擎UE5】SpawnActor生成Character实例不执行AI Move To,未初始化AIController的原因和解决方法

虚幻引擎版本&#xff1a;5.5.4 问题描述 刚创建的Third Person项目里&#xff0c;定义一个BP_Enemy蓝图&#xff0c;拖拽到场景中产生的实例会追随玩家&#xff0c;但SpawnActor产生的实例会固定不动。BP_Enemy蓝图具体设计如下&#xff1a; BP_Enemy的Event Graph ​​ 又定义…

论文笔记(七十三)Gemini Robotics: Bringing AI into the Physical World

Gemini Robotics: Bringing AI into the Physical World 文章概括1. 引言2. Gemini 2.0的具身推理2.1. 具身推理问答&#xff08;ERQA&#xff09;基准测试2.2. Gemini 2.0的具身推理能力2.3. Gemini 2.0支持零样本和少样本机器人控制 3. 使用 Gemini Robotics 执行机器人动作3…

汇能感知高品质的多光谱相机VSC02UA

VSC02UA概要 VSC02UA是一款高品质的200万像素的光谱相机&#xff0c;适用于工业检测、农业、医疗等领域。VSC02UA 包含 1600 行1200 列有源像素阵列、片上 10 位 ADC 和图像信号处理器。它带有 USB2.0 接口&#xff0c;配合专门的电脑上位机软件使用&#xff0c;可进行图像采集…

VSCode创建VUE项目(三)使用axios调用后台服务

1. 安装axios,执行命令 npm install axios 2. 在 main.ts 中引入并全局挂载 Axios 实例 修改后的 代码&#xff08;也可以单独建一个页面处理Axios相关信息等&#xff0c;然后全局进行挂载&#xff09; import { createApp } from vue import App from ./App.vue import rou…