Unity C# Scoke 如何实现网络通讯

2024-08-22 14:48

本文主要是介绍Unity C# Scoke 如何实现网络通讯,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

强联网在我们的游戏开发中所占比重越来越大,尤其是开发MMO游戏时,更需要强联网来进行实时更新,所以我们就有了强联网的需要。

首先我们得清楚强联网的工作原理,说到强联网,我们就必须说到socket。

socket是对tcp/ip协议的封装和应用,是面向程序员的,给我们提供了操作网络的接口,但是我们也必须基本了解其工作原理:

强联网我们主要使用的是TCP和UDP,首先我们说一下TCP。

一说到TCP,必然会想到三次握手和四次挥手,建立连接和断开连接的原理虽然在编程过程中不会涉及太多,但还是有了解的必要。

三次握手:客户端和服务端建立连接需要三次握手

    第一次:客户端向服务端发送报文,向服务器发送连接请求;

    第二次:服务端向客户端返回ACK报文,通知客户端可以连接;

    第三次:客户端收到服务端报文,正式连接服务端。

三次握手完成。

四次挥手:客户端要与服务器断开连接,需要四次挥手

    第一次:客户端向服务端发送FIN报文,向服务器发送中断连接请求;

    第二次:服务器收到客户端中断请求,向服务器发送已得知中断请求,但服务器还有资源未处理,需要等待;

    第三次:服务器处理完数据后,再次向客户端发送报文,告诉客户端可以断开连接了;

    第四次:客户端收到服务端断开连接的确认信息后,最后发送信息看是否真的断开连接了,如果服务器没有一段时间没有回应,则说明已经断开,中断过程完成;

四次挥手完成。

那么我们如何使用强联网呢?

在c#中我们可以通过使用Socket来进行连接:

在服务端的流程:

    创建Socket->绑定IP端口->设置排队连接请求数量->启动监听->收发消息->关闭

在客户端的流程:

    创建Socket->连接对应IP端口->开始收发->关闭

我们会发现,服务端的流程比客户端长,那是因为客户端只需要连接一个服务器就好了,而服务端要接收不同的客户端。

具体Socket接口的使用在网上都有教程,在这里不做多述,只需要注意一点就是选择TCP,UDP使用不同的参数:

serverSocket =new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);

这个是TCP的连接,三个参数分别是地址簇(ipv4),传输模式(流模式),协议(TCP)

而如果我们要UDP连接,则要如下:

serverSocket =new Socket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp);

UDP所需要的传输模式是数据报模式,所以我们在改变协议的同时,也要记得改变传输类型。

接下来就进入了我们的正题,有一定开发经验的游戏开发者一定知道,在网络传输数据的过程中会出现分包粘包的现象,我们首先来说一下什么叫分包、粘包。

    分包:传输数据不完整,一条信息被分成多次发送。

比如我们发送了一条信息:“你好”,如果分包现象发生,我们可能只收到了“你”,却没有收到“好”,这样就会导致数据的不完整。

    粘包:传输的多条数据粘在一起,比如我发了两句话“你好”和“我是小李”,我们可能会收到“你好我是小李”,也可能收到“你好我是”“你好我”“你好我是小”,后几种情况是分包粘包同时发生,我们肯定不期望这种现象发生,所以我们就有必要对我们发送的数据进行编辑。

解决方案: 我们每发送的一条数据就是一个数据包,我们是通过使用字节流来传输数据的,所以当我们每发送一个数据包,就顺便附带上数据包的长度,这样就形成了“数据包头”+“包体”的结构,包头用来存储数据包长度,包体用来存储具体的数据,每当我们接受数据时,首先读取数据包头,得到数据长度,再和已经传过来的长度对比,如果长度足够,说明至少传过来一个完整的包,我们就可以根据长度来取包体,如果不够,我们先不读取,直到长度满足时,我们再把这条数据进行读取。

这样就能方便的解决分包粘包的问题,但是我们之前接受到的不完整的信息放到哪呢?我们就需要一个缓存区,如果数据不完整,我们先放在缓存,当数据完整时,再取出读取。这里有一种建立缓存区的方案,通过内存流来读取。

    缓存区:我们使用内存流MemoryStream来进行读写操作,如果我们收到数据,我们就将数据写入内存流,当内存流的长度满足包头长度时,就将消息取出读取,流的操作比较基本,可以查阅资料了解,这里只提供一条思路。

    既然说到了数据包,那我们接着把数据包说完,数据包头+包体是我们最基本的结构,但我们的数据不可能就这样来传输,因为一旦有人截获我们的数据,就能轻易的修改我们的数据值,对游戏造成影响,所以我们必须要对数据包进行加密。

     一般情况下,我们会使用CRC进行冗余验证,看数据包是否传输完整,然后自定义自己的加密方式,将数据包加密以后再发出,有的项目还会对数据包进行压缩,所以我们这里给出一种通用的结构:数据头(长度)+冗余验证(CRC)+是否压缩+包体(加密后)。

这样,我们就可以对包进行加密操作以及完整性验证,保证我们的数据正确性、保密性和完整性。

    接下来就是传输协议,我们传输的数据都是字节流,那么我们收到这些字节流后如何处理呢?这就需要我们对数据包进行进一步编辑,给数据包一个协议ID,即把包体分为两部分:包体=协议ID+内容;当我们收到数据后,首先进行拆包,获取到协议ID后,就进行对应ID的事件派发,这里我们会用到观察者模式,下节会说到;我们派发事件后,对应的功能就会被执行,并且返回新的数据发送到另一端。那么我们通常都会有那些协议呢?

    通常我们在Socket中,最不能缺少的协议就是心跳,心跳是检验客户端和服务器是否连接、是否断线的非常有效的方法。服务器会每隔一段时间给客户端发送一个心跳包,如果在一定时间内没有收到客户端的回应,即认为客户端已经掉线;同样,如果客户端在一定时间内没 有收到服务器的心跳包,则认为连接不可用。

    除了心跳包,我们还会定义一些通用的包,如邮件、聊天等,具体要根据我们的需要来制定,比如我们要做一个MMORPG游戏,那么我们就有必要定义关于玩家位置、行为等相关的协议。

    具体协议的实现,我们通常会用到类来接收包的数据,通常会有一个基类包,所有的协议类都继承这个基类,但如果数据的复杂度不是很大的话,我们可以使用结构体,将原来的基类改为使用接口,这也是一种不错的优化网络模块的方式。

    总之,我们在游戏开发中通常使用弱联网进行登录验证,强联网用于游戏内部,所以Socket的使用在游戏开发中所占比例以及重要程度不言而喻,这也是我们不断研究、不断探索、不断优化的原因。下一节我们就具体讲一讲观察者模式以及在网络模块中的应用。

这篇关于Unity C# Scoke 如何实现网络通讯的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1096575

相关文章

分布式锁在Spring Boot应用中的实现过程

《分布式锁在SpringBoot应用中的实现过程》文章介绍在SpringBoot中通过自定义Lock注解、LockAspect切面和RedisLockUtils工具类实现分布式锁,确保多实例并发操作... 目录Lock注解LockASPect切面RedisLockUtils工具类总结在现代微服务架构中,分布

Java使用Thumbnailator库实现图片处理与压缩功能

《Java使用Thumbnailator库实现图片处理与压缩功能》Thumbnailator是高性能Java图像处理库,支持缩放、旋转、水印添加、裁剪及格式转换,提供易用API和性能优化,适合Web应... 目录1. 图片处理库Thumbnailator介绍2. 基本和指定大小图片缩放功能2.1 图片缩放的

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

Python实现网格交易策略的过程

《Python实现网格交易策略的过程》本文讲解Python网格交易策略,利用ccxt获取加密货币数据及backtrader回测,通过设定网格节点,低买高卖获利,适合震荡行情,下面跟我一起看看我们的第一... 网格交易是一种经典的量化交易策略,其核心思想是在价格上下预设多个“网格”,当价格触发特定网格时执行买

python设置环境变量路径实现过程

《python设置环境变量路径实现过程》本文介绍设置Python路径的多种方法:临时设置(Windows用`set`,Linux/macOS用`export`)、永久设置(系统属性或shell配置文件... 目录设置python路径的方法临时设置环境变量(适用于当前会话)永久设置环境变量(Windows系统

Python对接支付宝支付之使用AliPay实现的详细操作指南

《Python对接支付宝支付之使用AliPay实现的详细操作指南》支付宝没有提供PythonSDK,但是强大的github就有提供python-alipay-sdk,封装里很多复杂操作,使用这个我们就... 目录一、引言二、准备工作2.1 支付宝开放平台入驻与应用创建2.2 密钥生成与配置2.3 安装ali

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、

C#中lock关键字的使用小结

《C#中lock关键字的使用小结》在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时,其他线程无法访问同一实例的该代码块,下面就来介绍一下lock关键字的使用... 目录使用方式工作原理注意事项示例代码为什么不能lock值类型在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时

C# $字符串插值的使用

《C#$字符串插值的使用》本文介绍了C#中的字符串插值功能,详细介绍了使用$符号的实现方式,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录$ 字符使用方式创建内插字符串包含不同的数据类型控制内插表达式的格式控制内插表达式的对齐方式内插表达式中使用转义序列内插表达式中使用