网站banner背景/应用商店下载

网站banner背景,应用商店下载,wordpress logo制作教程,潍坊专职消防员待遇对UDP服务器的要求 如同TCP通信一样让UDP服务端可以服务多个客户端 需要具备的条件: 1.区分消息类型(不需要处理分包、黏包) 2.能够接收多个客户端的消息 3.能够主动给自己发过消息的客户端发消息(记录客户端信息)…

对UDP服务器的要求

            如同TCP通信一样让UDP服务端可以服务多个客户端
            需要具备的条件:
            1.区分消息类型(不需要处理分包、黏包)
            2.能够接收多个客户端的消息
            3.能够主动给自己发过消息的客户端发消息(记录客户端信息)
            4.主动记录上次收到客户端消息的时间,如果长时间没有收到消息,主动移除记录的客户端信息

            分析:
            1.UDP是无连接的,我们如何记录连入的客户端
            2.UDP收发消息都是通过一个Socket来处理,我们应该如何和处理收发消息
            3.如果不使用心跳消息,如何记录上次收到消息的时间

基本数据类--封装序列化和反序列化等方法

此代码定义了一个抽象基类BaseData,其中包含抽象方法用于获取字节数组容器大小、序列化和反序列化成员变量,还提供了一系列受保护的方法用于在字节数组和不同数据类型(如intshortlong等)及字符串、BaseData子类对象之间进行读写操作。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;public abstract class BaseData
{//用于子类重写的 获取字节数组容器大小的方法public abstract  int GetBytesNum();//把成员变量序列化为对应的字节数组public abstract byte[] Writing();public abstract int Reading(byte[] bytes, int beginIndex=0);//bytes指定的字节数组//value具体的int值//index索引位置的变量protected void WriteInt(byte []bytes,int value,ref int index){BitConverter.GetBytes(value).CopyTo(bytes, index);index += sizeof(int);}protected void WriteShort(byte[]bytes,short value,ref int index){BitConverter.GetBytes(value).CopyTo(bytes, index);index += sizeof(short);}protected void WriteLong(byte[]bytes,long value,ref int index){BitConverter.GetBytes(value).CopyTo(bytes, index);index += sizeof(long);}protected void WriteFloat(byte[] bytes, float value, ref int index){BitConverter.GetBytes(value).CopyTo(bytes, index);index += sizeof(float);}protected void WriteByte(byte[]bytes,byte value,ref int index){bytes[index] = value;index += sizeof(byte);}protected void WriteBool(byte[] bytes, bool value, ref int index){BitConverter.GetBytes(value).CopyTo(bytes, index);index += sizeof(bool);}protected void WriteString(byte[]bytes,string value,ref int index){//先存储string字节数组的长度byte[] strBytes = Encoding.UTF8.GetBytes(value);//BitConverter.GetBytes(strBytes.Length).CopyTo(bytes, index);//index += sizeof(int);WriteInt(bytes, strBytes.Length, ref index);//再存string字节数组strBytes.CopyTo(bytes, index);index += strBytes.Length;}protected void WriteData(byte[]bytes,BaseData data,ref int index){data.Writing().CopyTo(bytes, index);index += data.GetBytesNum();}protected int ReadInt(byte[]bytes,ref int index){int value = BitConverter.ToInt32(bytes, index);index += 4;return value;}protected short ReadShort(byte[] bytes, ref int index){short value = BitConverter.ToInt16(bytes, index);index += 2;return value;}protected long ReadLong(byte[] bytes, ref int index){long value = BitConverter.ToInt64(bytes, index);index += 8;return value;}protected float ReadFloat(byte[] bytes, ref int index){float value = BitConverter.ToSingle(bytes, index);index += sizeof(float);return value;}protected byte ReadByte(byte[] bytes, ref int index){byte value = bytes[index];index += 1;return value;}protected bool ReadBool(byte[] bytes, ref int index){bool value = BitConverter.ToBoolean(bytes, index);index += sizeof(bool);return value;}protected string ReadString(byte[] bytes, ref int index){int length = ReadInt(bytes, ref index);string value = Encoding.UTF8.GetString(bytes, index, length);index += length;return value;}protected T ReadData<T>(byte[] bytes, ref int index) where T : BaseData, new(){T value = new T();index+= value.Reading(bytes,index);return value;}
}

基本消息类

这段代码定义了一个名为BaseMsg的类,它继承自BaseData类。BaseMsg类重写了BaseData的抽象方法GetBytesNumReadingWriting,但这些重写方法只是简单抛出NotImplementedException异常,表明目前未实现具体逻辑。此外,BaseMsg类还定义了一个虚方法GetID,默认返回 0。

BaseMsg类的设计目的主要是作为消息类的基类,为后续具体消息类的实现提供统一的接口和结构框架。

using System.Collections;
using System.Collections.Generic;public class BaseMsg : BaseData
{public override int GetBytesNum(){throw new System.NotImplementedException();}public override int Reading(byte[] bytes, int beginIndex = 0){throw new System.NotImplementedException();}public override byte[] Writing(){throw new System.NotImplementedException();}public virtual int GetID(){return 0;}
}

玩家信息类

这段代码定义了一个名为PlayerMsg的类,它继承自BaseMsg类。PlayerMsg类代表了与玩家相关的消息,并且实现了消息的序列化和反序列化功能。

using System.Collections;
using System.Collections.Generic;public class PlayerMsg : BaseMsg
{public int playerID;public PlayerData playerData;public override int GetBytesNum(){return 4 +//消息ID4 +//playerID长度playerData.GetBytesNum();//消息的长度}public override int GetID(){return 1001;}public override int Reading(byte[] bytes, int beginIndex = 0){//反序列化不需要去解析ID,因为在这一步之前,就应该将ID反序列化出来//用来判断到底使用哪一个自定义类来反序列化int index = beginIndex;playerID = ReadInt(bytes, ref index);playerData = ReadData<PlayerData>(bytes, ref index);return index - beginIndex;}public override byte[] Writing(){int index = 0;byte[] playerBytes = new byte[GetBytesNum()];//先写消息IDWriteInt(playerBytes, GetID(), ref index);WriteInt(playerBytes, playerID, ref index);WriteData(playerBytes, playerData, ref index);return playerBytes;}
}
using System.Collections;
using System.Collections.Generic;
using System.Text;public class PlayerData : BaseData 
{public string name; public int lev;public int atk;public override int GetBytesNum(){return 4 + 4 + 4 + Encoding.UTF8.GetBytes(name).Length;}public override int Reading(byte[] bytes, int beginIndex = 0){int index = beginIndex;name=ReadString(bytes, ref index);lev=ReadInt(bytes, ref index);atk=ReadInt(bytes, ref index);return index - beginIndex;}public override byte[] Writing(){int index = 0;byte[] bytes = new byte[GetBytesNum()];WriteString(bytes, name, ref index);WriteInt(bytes, lev, ref index);WriteInt(bytes, atk, ref index);return bytes;}
}

这段代码定义了一个名为PlayerData的类,它继承自BaseData类。PlayerData类的作用是用来表示玩家的相关数据,并且实现了这些数据的序列化与反序列化功能。

服务端类

这段代码定义了一个名为ServerSocket的类,用于构建基于 UDP 协议的服务器,它能通过绑定指定 IP 和端口启动服务,利用线程池实现消息接收与客户端超时检查,将客户端信息存储在字典中,可处理新客户端连接,接收客户端消息并交予对应客户端对象处理,支持向指定客户端发送消息、向所有客户端广播消息,还能移除超时或指定的客户端。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;namespace UDPServerExerise
{class ServerSocket{public Socket socket;private bool IsClose;//我们可以通过记录谁给我们发了消息 把它的IP和端口记录下来 这样就认为他是我的客户端了private Dictionary<string, Client> clientDic = new Dictionary<string, Client>();public void Start(string ip,int port){socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse(ip), port);try{socket.Bind(ipPoint);IsClose = false;}catch (Exception e){Console.WriteLine("UDP开启错误" + e.Message);}//接收消息,使用线程池ThreadPool.QueueUserWorkItem(ReceiveMsg);//检测超时的线程ThreadPool.QueueUserWorkItem(CheakTimeOut);}private void CheakTimeOut(object obj){long nowTime=0;List<string> delClient = new List<string>();while (true){//30秒检查一次Thread.Sleep(30000);//得到当前系统时间nowTime = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;foreach (Client c in clientDic .Values){//超过十秒没有 收到消息的客户端需要被移除if(nowTime -c.frontTime >=10){delClient.Add(c.clientID);}}//从待删除列表中删除超时客户端for (int i = 0; i < delClient.Count; i++)RemoveClient(delClient[i]);delClient.Clear();}}private void ReceiveMsg(object obj){byte[] bytes = new byte[512];//记录谁发的string strID = "";string ip;int port;EndPoint ipPoint = new IPEndPoint(IPAddress.Any, 0);while (!IsClose){if(socket.Available >0){lock(socket)socket.ReceiveFrom(bytes, ref ipPoint);//处理消息 最好不要直接在这里处理,而是交给客户端对象处理//收到消息时,我们要判断 是不是记录了这个客户端的信息(ip和端口)//出去发送消息给我的IP和端口ip = (ipPoint as IPEndPoint).Address.ToString();port = (ipPoint as IPEndPoint).Port;strID = ip + port;//拼接成唯一一个ID这是我们自定义的规则//判断有没有记录这个客户端的信息,如果有直接用它处理信息if(clientDic .ContainsKey (strID )){clientDic[strID].ReceiveMsg(bytes);}else//如果没有 直接添加并处理消息{clientDic.Add(strID, new Client(ip, port));clientDic[strID].ReceiveMsg(bytes);}}}}public void SendTo(BaseMsg msg,IPEndPoint ipPoint){try{lock (socket)socket.SendTo(msg.Writing(), ipPoint);}catch (SocketException s){Console.WriteLine("发消息出现问题" + s.SocketErrorCode + s.Message);}catch (Exception e){Console.WriteLine("发消息出现问题(可能是序列化的问题)" + e.Message);}}private void Close(){if(socket!=null){socket.Shutdown(SocketShutdown.Both);socket.Close();IsClose = true;socket = null;}}public void BoardCast(BaseMsg msg){//广播给谁foreach (Client c in clientDic .Values){SendTo(msg,c.ipAndPoint);}}public void RemoveClient(string clientID){if(clientDic .ContainsKey (clientID)){Console.WriteLine("客户端{0}被移除了", clientID);clientDic.Remove(clientID);}}}
}

客户端类

这段代码定义了Client类,用于处理 UDP 服务器端接收到的来自客户端的消息。Client类的构造函数通过传入的 IP 和端口创建IPEndPoint对象并生成唯一的客户端 ID;ReceiveMsg方法接收消息字节数组,拷贝消息到新数组,记录消息接收时间,并将消息处理任务放入线程池;ReceiceHandleMsg方法从消息字节数组中解析消息类型、长度和消息体,针对不同消息 ID(如 1001 对应PlayerMsg消息,1003 对应quitMsg消息)进行相应处理,如反序列化PlayerMsg并输出相关信息,处理quitMsg时移除对应客户端,若处理消息出错也会移除该客户端。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;namespace UDPServerExerise
{class Client{public IPEndPoint ipAndPoint;public string clientID;public float frontTime = -1;public Client (string ip,int port){//规则和外边一样 记录唯一ID 通过ip和port拼接的形式clientID = ip + port;//把客户端的信息记录下来ipAndPoint = new IPEndPoint(IPAddress.Parse(ip), port);}public void ReceiveMsg(byte[]bytes){//为了避免处理消息时又接收到了新的消息 所以我们需要在处理消息前 先把消息拷贝出来//处理消息和接收消息用不同容器 避免发生冲突byte[] cacheBytes = new byte[512];bytes.CopyTo(cacheBytes, 0);//记录发消息的系统时间frontTime = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;ThreadPool.QueueUserWorkItem(ReceiceHandleMsg, cacheBytes);}private void ReceiceHandleMsg(object obj){try{byte[] bytes = obj as byte[];int nowIndex = 0;//解析消息类型int msgID = BitConverter.ToInt32(bytes, nowIndex);nowIndex += 4;//解析消息长度int length = BitConverter.ToInt32(bytes, nowIndex);nowIndex += 4;//解析消息体switch (msgID){case 1001:PlayerMsg playerMsg = new PlayerMsg();playerMsg.Reading(bytes, nowIndex);Console.WriteLine(playerMsg.playerID);Console.WriteLine(playerMsg.playerData.lev);Console.WriteLine(playerMsg.playerData.atk);Console.WriteLine(playerMsg.playerData.name);break;case 1003:quitMsg quitMsg = new quitMsg();//由于它没有消息体 所以不用反序列化//quitMsg.Reading(bytes, nowIndex);//处理退出Program.serverSocket.RemoveClient(clientID);break;}}catch (Exception e){Console.WriteLine("处理消息出错" + e.Message);//如果出错了,就不用记录客户端的信息了Program.serverSocket.RemoveClient(clientID);}}}
}

退出消息类

这段代码定义了一个名为quitMsg的类,它继承自BaseMsg类,用于表示退出消息,重写了GetBytesNum方法指定消息字节数为 8,重写GetID方法返回消息唯一标识符 1003,重写Reading方法调用基类方法进行反序列化,重写Writing方法将消息 ID 和消息体长度(这里设为 0)序列化为字节数组。

using System.Collections;
using System.Collections.Generic;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;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace UDPServerExerise
{class Program{public static ServerSocket serverSocket;static void Main(string[] args){serverSocket = new ServerSocket();serverSocket.Start("127.0.0.1", 8080);Console.WriteLine("UDP服务器启动了");string input = Console.ReadLine();if(input.Substring (0,2)=="B:"){PlayerMsg msg = new PlayerMsg();msg.playerData = new PlayerData();msg.playerID = 1001;msg.playerData.atk = 999;msg.playerData.lev = 88;msg.playerData.name ="DamnF的服务器";serverSocket.BoardCast(msg);}}}
}

成功运行程序--等待客户端通信

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

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

相关文章

如何在 Postman 中发送 PUT 请求?

在 Postman 中发送 PUT 请求的步骤相对简单&#xff0c;包括新建接口、选择 PUT 方法、填写 URL 和参数等几个主要步骤。 Postman 发送 put 请求教程

卷积神经网络 - 参数学习

本文我们通过两个简化的例子&#xff0c;展示如何从前向传播、损失计算&#xff0c;到反向传播推导梯度&#xff0c;再到参数更新&#xff0c;完整地描述卷积层的参数学习过程。 一、例子一 我们构造一个非常简单的卷积神经网络&#xff0c;其结构仅包含一个卷积层和一个输出…

Redis实战常用二、缓存的使用

一、什么是缓存 在实际开发中,系统需要"避震器"&#xff0c;防止过高的数据访问猛冲系统,导致其操作线程无法及时处理信息而瘫痪. 这在实际开发中对企业讲,对产品口碑,用户评价都是致命的。所以企业非常重视缓存技术; 缓存(Cache)&#xff1a;就是数据交换的缓冲区&…

CentOS 7部署主域名服务器 DNS

1. 安装 BIND 服务和工具 yum install -y bind bind-utils 2. 配置 BIND 服务 vim /etc/named.conf 修改以下配置项: listen-on port 53 { any; }; # 监听所有接口allow-query { any; }; # 允许所有设备查询 3 . 添加你的域名区域配置 …

C++ 多线程简要讲解

std::thread是 C11 标准库中用于多线程编程的核心类&#xff0c;提供线程的创建、管理和同步功能。下面我们一一讲解。 一.构造函数 官网的构造函数如下&#xff1a; 1.默认构造函数和线程创建 thread() noexcept; 作用&#xff1a;创建一个 std::thread 对象&#xff0c;但…

Vscode HTML5新增元素及属性

一、‌HTML5 语义化标签 HTML5 语义化标签&#xff08;Semantic Elements&#xff09;是一组 ‌具有明确含义的 HTML 元素‌&#xff0c;通过标签名称直接描述其内容或结构的功能&#xff0c;而非仅作为样式容器&#xff08;如 <div> 或 <span>&#xff09;。它们旨…

Three学习入门(四)

9-Three.js 贴图与材质学习指南 环境准备 <!DOCTYPE html> <html> <head><title>Three.js Texture Demo</title><style> body { margin: 0; } </style> </head> <body><script src"https://cdnjs.cloudflare.…

前端NVM安装

https://v0.dev/chat/settings 本地启动环境 1安装 nvm 2安装node nvm install v18.19.0 nvm install v20.9.0 nvm use 18 node -v 3安装 pnpm npm install -g pnpm 或者 npm i -g pnpm 4启动 代码 目录下 执行 pnpm i pnpm run dev 4.1到代码目录下 4.2直接cmd…

cmd命令查看电脑的CPU、内存、存储量

目录 获取计算机硬件的相关信息的命令分别的功能结果展示结果说明获取计算机硬件的相关信息的命令 wmic cpu get name wmic memorychip get capacity wmic diskdrive get model,size,mediaType分别的功能 获取计算机中央处理器(CPU)的名称 获取计算机内存(RAM)芯片的容量…

如何监控 SQL Server

监控 SQL Server 对于维护数据库性能、确保数据可用性和最大限度地减少停机时间至关重要。随着企业越来越依赖数据驱动的决策&#xff0c;高效的SQL Server监控策略能显著提升组织生产力和用户满意度。 为什么要监控 SQL Server SQL Server 是许多关键应用程序的支柱&#xf…

画秒杀系统流程图

秒杀系统流程图 秒杀系统关键点 高并发处理: 使用网关&#xff08;如 Nginx&#xff09;进行流量限流&#xff0c;避免过载。分布式锁或 Redis 原子操作控制并发。 活动状态检查: Redis 存储活动状态&#xff08;如 seckill:activity:1:status&#xff09;&#xff0c;快速…

【js逆向入门】图灵爬虫练习平台 第九题

地址&#xff1a;aHR0cHM6Ly9zdHUudHVsaW5ncHl0b24uY24vcHJvYmxlbS1kZXRhaWwvOS8 f12进入了debugger&#xff0c;右击选择一律不在此处暂停&#xff0c; 点击继续执行 查看请求信息 查看载荷&#xff0c;2个加密参数&#xff0c;m和tt 查看启动器&#xff0c;打上断点 进来 往…

Vue中的状态管理器Vuex被Pinia所替代-上手使用指南

Pinia.js 是新一代的状态管理器&#xff0c;由 Vue.js团队中成员所开发的&#xff0c;因此也被认为是下一代的 Vuex&#xff0c;即 Vuex5.x&#xff0c;在 Vue3.0 的项目中使用也是备受推崇 Pinia.js 有如下特点&#xff1a; 完整的 typescript 的支持&#xff1b;足够轻量&…

向量数据库学习笔记(1) —— 基础概念

一、 嵌入模型 Embedding Models 嵌入模型是将复杂数据&#xff08;如文本、图像、音频等&#xff09;转换为向量表示的机器学习模型 1. 核心概念 嵌入(Embedding)&#xff1a;将高维、非结构化的数据映射到低维、稠密的向量空间 向量表示&#xff1a;输出固定长度的数值向量…

[NO-WX179]基于springboot+微信小程序的在线选课系统

[NO-WX179]基于springboot微信小程序的在线选课系统 1、管理员角色&#xff08;web端&#xff09;&#xff1a;2、教师角色&#xff08;web端&#xff09;&#xff1a;3、用户角色&#xff08;小程序或web端&#xff09;&#xff1a;4、部分运行截图管理端--教师管理管理端--学…

2025年渗透测试面试题总结-某 长亭(题目+回答)

网络安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 长亭 一、Spring SpEL表达式注入漏洞 1. 技术原理 2. 利用条件 3. 攻击方法 4. 防御策略 二、Jav…

conda环境下解决gitk乱码模糊

关键词 conda、git、gitk、git gui、模糊、linux、乱码 现象 操作系统&#xff1a;ubuntu24.04 conda版本&#xff1a;25.1.1 正常的终端里gitk显示不会模糊 但是在conda创建的python虚拟环境中使用gitk&#xff0c;字体开始变得模糊不清 分析 根据deepseek的原因原因分析…

【C++项目实战】:基于正倒排索引的Boost搜索引擎(1)

1. 项目的相关背景与目标 针对boost网站没有搜索导航功能&#xff0c;为boost网站文档的查找提供搜索功能 站内搜索&#xff1a;搜索的数据更垂直&#xff0c;数据量小 类似于cplusplus.com的搜索 2.搜索引擎的相关宏观原理 3.技术栈和项目环境 技术栈&#xff1a;C/C&am…

深入理解指针(1)(C语言版)

文章目录 前言一、内存和地址1.1 内存1.2 究竟该如何理解编址 二、指针变量和地址2.1 取地址操作符&2.2 指针变量和解引用操作符*2.2.1 指针变量2.2.2 如何拆解指针类型2.2.3 解引用操作符 2.3 指针变量的大小 三、指针变量类型的意义3.1 指针的解引用3.2 指针-整数3.3 voi…

第一天学爬虫

阅读提示&#xff1a;我今天才开始尝试爬虫&#xff0c;写的不好请见谅。 一、准备工具 requests库&#xff1a;发送HTTP请求并获取网页内容。BeautifulSoup库&#xff1a;解析HTML页面并提取数据。pandas库&#xff1a;保存抓取到的数据到CSV文件中。 二、爬取步骤 发送请求…