当前位置: 首页 > news >正文

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

前言:本文将会以制作一个简易双端网络框架的目标,带领读者熟悉游戏开发中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协议来定义消息格式,以及添加重连、心跳检测、消息分发、消息队列等内容,搭建一个成熟的网络框架。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【Python机器学习】FP-growth算法——FP树:用于编码数据集的有效方式
  • 【区块链+食品安全】海南省市场监管局:进口冷链食品可信追溯平台 | FISCO BCOS应用案例
  • 树和森林的应用场景
  • 时间同步及LVS的安装配置
  • linux信号集
  • WPF窗体动态效果
  • 评价算法(topsis熵权法)
  • language model
  • php收银系统源码(收银pos+聚合支付+ai智能称重+连锁多门店+多商户入驻平台版商城)
  • 【笔记】MSPM0G3507移植RT-Thread——MSPM0G3507与RT_Thread(二)
  • 图书借阅会员员工管理小程序开发
  • Spring事务管理和事务传播机制详解
  • 项目成功的关键:10款需求文档管理工具
  • Android进阶之路 - res、raw、assets 资源解析、区别对比
  • 生态系统NPP及碳源、碳汇模拟(土地利用变化、未来气候变化、空间动态模拟)
  • 【跃迁之路】【585天】程序员高效学习方法论探索系列(实验阶段342-2018.09.13)...
  • Bytom交易说明(账户管理模式)
  • canvas实际项目操作,包含:线条,圆形,扇形,图片绘制,图片圆角遮罩,矩形,弧形文字...
  • HTTP 简介
  • JavaScript 基本功--面试宝典
  • JS创建对象模式及其对象原型链探究(一):Object模式
  • mysql 数据库四种事务隔离级别
  • npx命令介绍
  • Python十分钟制作属于你自己的个性logo
  • Redis学习笔记 - pipline(流水线、管道)
  • SpingCloudBus整合RabbitMQ
  • Traffic-Sign Detection and Classification in the Wild 论文笔记
  • 关于springcloud Gateway中的限流
  • 理解在java “”i=i++;”所发生的事情
  • 前端每日实战:70# 视频演示如何用纯 CSS 创作一只徘徊的果冻怪兽
  • 如何使用 JavaScript 解析 URL
  • 学习笔记DL002:AI、机器学习、表示学习、深度学习,第一次大衰退
  • d²y/dx²; 偏导数问题 请问f1 f2是什么意思
  • [地铁译]使用SSD缓存应用数据——Moneta项目: 低成本优化的下一代EVCache ...
  • PostgreSQL之连接数修改
  • 蚂蚁金服CTO程立:真正的技术革命才刚刚开始
  • ​香农与信息论三大定律
  • #define
  • (2)(2.10) LTM telemetry
  • (3)STL算法之搜索
  • (Bean工厂的后处理器入门)学习Spring的第七天
  • (笔记)M1使用hombrew安装qemu
  • (附源码)ssm基于web技术的医务志愿者管理系统 毕业设计 100910
  • (文章复现)基于主从博弈的售电商多元零售套餐设计与多级市场购电策略
  • (学习总结16)C++模版2
  • (一)模式识别——基于SVM的道路分割实验(附资源)
  • (转)EOS中账户、钱包和密钥的关系
  • .NET C# 配置 Options
  • .Net Core 笔试1
  • .NET delegate 委托 、 Event 事件,接口回调
  • .NET IoC 容器(三)Autofac
  • .NET/C# 将一个命令行参数字符串转换为命令行参数数组 args
  • .NET中使用Protobuffer 实现序列化和反序列化
  • @Query中countQuery的介绍
  • []C/C++读取串口接收到的数据程序