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

相关文章

基于 HTML5 Canvas 实现图片旋转与下载功能(完整代码展示)

《基于HTML5Canvas实现图片旋转与下载功能(完整代码展示)》本文将深入剖析一段基于HTML5Canvas的代码,该代码实现了图片的旋转(90度和180度)以及旋转后图片的下载... 目录一、引言二、html 结构分析三、css 样式分析四、JavaScript 功能实现一、引言在 Web 开发中,

SpringBoot中使用Flux实现流式返回的方法小结

《SpringBoot中使用Flux实现流式返回的方法小结》文章介绍流式返回(StreamingResponse)在SpringBoot中通过Flux实现,优势包括提升用户体验、降低内存消耗、支持长连... 目录背景流式返回的核心概念与优势1. 提升用户体验2. 降低内存消耗3. 支持长连接与实时通信在Sp

Conda虚拟环境的复制和迁移的四种方法实现

《Conda虚拟环境的复制和迁移的四种方法实现》本文主要介绍了Conda虚拟环境的复制和迁移的四种方法实现,包括requirements.txt,environment.yml,conda-pack,... 目录在本机复制Conda虚拟环境相同操作系统之间复制环境方法一:requirements.txt方法

Spring Boot 实现 IP 限流的原理、实践与利弊解析

《SpringBoot实现IP限流的原理、实践与利弊解析》在SpringBoot中实现IP限流是一种简单而有效的方式来保障系统的稳定性和可用性,本文给大家介绍SpringBoot实现IP限... 目录一、引言二、IP 限流原理2.1 令牌桶算法2.2 漏桶算法三、使用场景3.1 防止恶意攻击3.2 控制资源

springboot下载接口限速功能实现

《springboot下载接口限速功能实现》通过Redis统计并发数动态调整每个用户带宽,核心逻辑为每秒读取并发送限定数据量,防止单用户占用过多资源,确保整体下载均衡且高效,本文给大家介绍spring... 目录 一、整体目标 二、涉及的主要类/方法✅ 三、核心流程图解(简化) 四、关键代码详解1️⃣ 设置

Nginx 配置跨域的实现及常见问题解决

《Nginx配置跨域的实现及常见问题解决》本文主要介绍了Nginx配置跨域的实现及常见问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来... 目录1. 跨域1.1 同源策略1.2 跨域资源共享(CORS)2. Nginx 配置跨域的场景2.1

Python中提取文件名扩展名的多种方法实现

《Python中提取文件名扩展名的多种方法实现》在Python编程中,经常会遇到需要从文件名中提取扩展名的场景,Python提供了多种方法来实现这一功能,不同方法适用于不同的场景和需求,包括os.pa... 目录技术背景实现步骤方法一:使用os.path.splitext方法二:使用pathlib模块方法三

CSS实现元素撑满剩余空间的五种方法

《CSS实现元素撑满剩余空间的五种方法》在日常开发中,我们经常需要让某个元素占据容器的剩余空间,本文将介绍5种不同的方法来实现这个需求,并分析各种方法的优缺点,感兴趣的朋友一起看看吧... css实现元素撑满剩余空间的5种方法 在日常开发中,我们经常需要让某个元素占据容器的剩余空间。这是一个常见的布局需求

HTML5 getUserMedia API网页录音实现指南示例小结

《HTML5getUserMediaAPI网页录音实现指南示例小结》本教程将指导你如何利用这一API,结合WebAudioAPI,实现网页录音功能,从获取音频流到处理和保存录音,整个过程将逐步... 目录1. html5 getUserMedia API简介1.1 API概念与历史1.2 功能与优势1.3

Java实现删除文件中的指定内容

《Java实现删除文件中的指定内容》在日常开发中,经常需要对文本文件进行批量处理,其中,删除文件中指定内容是最常见的需求之一,下面我们就来看看如何使用java实现删除文件中的指定内容吧... 目录1. 项目背景详细介绍2. 项目需求详细介绍2.1 功能需求2.2 非功能需求3. 相关技术详细介绍3.1 Ja