Unity搭建简易网络服务端与客户端--基础篇

embedded/2024/12/23 6:41:49/

前言:本文将会以制作一个简易双端网络框架的目标,带领读者熟悉游戏开发中Socket网络编程的概念和流程,知道是怎样从零去构建一个双端的网络


建议带着疑问去学习:

1. 什么是Socket?

2. 为什么需要用到Socket去实现网络编程?

3. Socket的实现原理是什么?

4. Socket通信的流程是什么?

5. TCP和UDP协议有什么区别?能否实现可靠的UDP协议?

并且需要了解以下知识:

0. Socket

1. 异步Socket

2. 状态检测Poll

3. 多路复用Select

服务端:

服务端的通信流程:

1. 创建socket

2. 绑定IP和端口

3. 监听

4. 接收

5. 分发消息

6. 发送

服务端的搭建流程:

NetServer: 负责创建socket和监听接收

NetSession:负责维护客户端的连接

服务端的代码实现:

服务端采用的是Select多路复用阻塞连接和接收


using System.Net;
using System.Net.Sockets;namespace Server
{public class NetSession{public Socket socket;//socket对象   public byte[] readBuffer;//用于接收的字节数组}public class NetServer{static Socket socket;  //用于监听的全局socket//用字典来维护每一个Socket连接static Dictionary<Socket, NetSession> clients = new Dictionary<Socket, NetSession>();public void Init(){//创建socket对象(参数分别是地址族:Ipv4,socket类型:字节流socket,通信协议:udpsocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Udp);//绑定服务器本地IP和端口IPAddress ipAdr = IPAddress.Parse("127.0.0.1");IPEndPoint ipEp = new IPEndPoint(ipAdr, 8888);socket.Bind(ipEp);//监听,参数是最多可容纳等待接受的连接数,0表示不限制socket.Listen(0);Console.WriteLine("服务器启动成功!");//可读的Socket列表,用于多路复用状态检测List<Socket> readSockets = new List<Socket>();while (true){#region 多路复用select//待监听的socket列表readSockets.Clear();readSockets.Add(socket);foreach (var kv in clients){readSockets.Add(kv.Value.socket);}/* 参数checkRead    检测是否有可读的Socket列表checkWrite   检测是否有可写的Socket列表  checkError   检测是否有出错的Socket列表m1croScconds 等待回应的时间,以微秒为单位,如果该参数为-1表示一直等待,如果为0表示非阻塞Select可以确定一个或多个Socket对象的状态,一个或多个套接字放入ILsit中。通过调用Select(将IList作为checkRead参数),可检查Socket是否具有可读性。若要检查套接字是否具有可写性,可使用checkWrite参数。若要检测错误条件,可使用checkError。 在调用Select之后,Select将修改ILsit列表 , 仅保留那些满足条件的套接字。把包含6个Socket的列表传给Select, Select方法将会阻塞 , 等到超时或某个(或多个)Socket可读时返同 ,并且修改checkRead列表 ,仅保存可诙的socket A和socket C。 当没有任何可读Socket时,程序将会阻塞 ,不占用CPU资源*/Socket.Select(readSockets, null, null, 1000);foreach (var kv in readSockets){if (kv == socket){this.ReadListenfd(kv);}else{this.ReadClientfd(kv);}}#endregion}}//当监听到客户端连接时的处理void ReadListenfd(Socket listen){/如果监听到客户端的连接,则维护该连接Socket temp = listen.Accept();NetSession state = new NetSession{socket = temp};clients.Add(temp, state);}//当接收到客户端消息时的处理bool ReadClientfd(Socket client){//判断字典中是否还包含该连接if (!clients.ContainsKey(client)){return false;}            NetSession state = clients[client];//Reveiveint count = 0;try{//接收count = client.Receive(state.readBuffer);}catch (SocketException ex){//如果发生异常则关闭并移除该连接client.Close();clients.Remove(client);Console.WriteLine("接收失败:" + ex.Message);return false;}//如果count为0,则说明客户端关闭了连接if (count == 0){client.Close();clients.Remove(client);Console.WriteLine("接收失败,count为0");return false;}//将字节数组转换为字符串string readStr = System.Text.Encoding.Default.GetString(state.readBuffer, 0, count);Console.WriteLine("服务区接收:" + readStr);this.sendMessage("服务器接收成功,返回给客户端:" + readStr);return true;}//发送消息void sendMessage(string message){byte[] sendMsg = System.Text.Encoding.Default.GetBytes(message);foreach (var kv in clients.Values){kv.socket.Send(sendMsg);}}}
}

客户端

客户端的通信流程:

1. 创建Socket对象

2. 连接服务器

3. 发送消息

5. 接收消息

6.关闭连接

客户端的代码实现:

客户端采用的是异步连接和接收

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using UnityEngine;
using UnityEngine.UI;public class Echo : MonoBehaviour
{public Button connectButton;//连接按钮public Button sendButton;//发送按钮public InputField input;//输入的消息private byte[] readBuffer = new byte[1024];//用于接收的字节数组private Socket socket;//客户端Socketpublic void Connect(){//创建socketsocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Udp);//开始连接socket.BeginConnect("127.0.0.1",8000,ConnectCallBack,socket);}public void ConnectCallBack(IAsyncResult result){try{Socket con = result.AsyncState as Socket;con.EndConnect(result);//链接成功的回调方法con.BeginReceive(readBuffer,0,1024,0,ReceiveCallBack,con);//开始接收Debug.Log("Connect Success!");}catch(SocketException e){Debug.Log("Connect Error:"+e.Message);}}private void ReceiveCallBack(IAsyncResult ar){try{Socket rec = ar.AsyncState as Socket;int count = rec.EndReceive(ar);string mes = System.Text.Encoding.Default.GetString(readBuffer,0,count);Debug.Log("接收服务器数据成功:"+mes);//接收完这窜数据后准备接收下一串rec.BeginReceive(readBuffer,0,1024,0,ReceiveCallBack,rec);}catch(SocketException ex){Debug.Log("Connect Error:"+ex.Message);}}void Send(string mes){string sendStr = input.text;byte[] bytes = System.Text.Encoding.Default.GetBytes(sendStr);socket.BeginSend(bytes,0,bytes.Length,0,SendCallBack,socket);}void SendCallBack(IAsyncResult ar){try{Socket sen = ar.AsyncState as Socket;int count = sen.EndSend(ar);Debug.Log("发送成功!count = "+count);}catch(SocketException ex){Debug.Log("发送失败! "+ex.Message);}}
}

需要说明的是,上述代码仅仅是为了熟悉基本的客户端和服务端网络通信,是一个非常简单的网络框架,建议动手亲自实战,后续将会使用protobuf协议来定义消息格式,以及添加重连、心跳检测、消息分发、消息队列等内容,搭建一个成熟的网络框架。


http://www.ppmy.cn/embedded/96993.html

相关文章

《区块链赋能游戏业:破解虚拟资产交易与确权难题》

在当今数字化的时代&#xff0c;游戏行业正以前所未有的速度发展&#xff0c;虚拟资产在游戏中的重要性日益凸显。然而&#xff0c;虚拟资产的交易和确权问题一直困扰着游戏开发者和玩家。随着区块链技术的引入&#xff0c;为解决这些问题带来了新的曙光。 首先&#xff0c;我…

Leetcode 第 407 场周赛题解

Leetcode 第 407 场周赛题解 Leetcode 第 407 场周赛题解题目1&#xff1a;3226. 使两个整数相等的位更改次数思路代码复杂度分析 题目2&#xff1a;3227. 字符串元音游戏思路代码复杂度分析 题目3&#xff1a;3228. 将 1 移动到末尾的最大操作次数思路代码复杂度分析 题目4&am…

工业互联网边缘计算实训室解决方案

一、引言 随着物联网&#xff08;IoT&#xff09;、5G通信技术的快速发展&#xff0c;工业互联网已成为推动制造业转型升级的重要力量。边缘计算作为云计算的延伸和补充&#xff0c;在实时数据分析、降低数据传输延迟、提升处理效率及增强数据安全性方面展现出巨大潜力。在此背…

四层负载企业实战

通过四层负载LVSkeepalived将请求转发到nginx代理服务器。通过代理服务器访问后端真实应用服务。 拓补图&#xff1a; 准备环境6台机器&#xff1b; lvskeepalived---两台 nginx代理 ---两台 tomcat----一台 php ----一台 所有机器关闭防火墙和selinux 所有机器解析如下…

Vue中下载内容为word文档

1.使用 html-docx-js&#xff1a;这是一个将 HTML 转换为 Word 文档的库。 2. 利用 Blob 和 FileSaver.js&#xff1a;创建并下载生成的 Word 文档。 在 Vue.js 中实现步骤如下: 1. npm 安装 html-docx-js 和 file-saver npm install html-docx-js npm install file-saver2.…

使用 ElementUI el-upload 一次性上传多个文件

在日常的前端开发中&#xff0c;文件上传是一个非常常见的需求&#xff0c;尤其是在用户需要一次性上传多个文件的场景下。ElementUI作为一款非常优秀的Vue.js 2.0组件库&#xff0c;为我们提供了丰富的UI组件&#xff0c;极大地提升了开发效率。其中&#xff0c;el-upload组件…

【网络编程】 TCP机械臂测试(C语言)

目录 前言&#xff1a; 代码实现&#xff1a; 输出结果如下&#xff1a; 前言&#xff1a; 1、通过以下操作实现机械臂控制 w(红色臂角度增大) s(红色臂角度减小) d(蓝色臂角度增大) a(蓝色臂角度减小)按键控制机械臂 >>需要对机械臂发…

第36讲:使用Prometheus监控系统全方面监控Ceph集群

文章目录 1.启用Manager组件的Prometheus模块2.部署并配置Prometheus2.1.部署Prometheus服务端2.2.配置Prometheus添加Ceph集群的监控信息2.3.观察Prometheus监控系统中是否能浏览Ceph集群的监控数据 3.部署Grafana用于展示Prometheus的监控数据3.1.部署Grafana仪表盘3.2.Grafa…