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

相关文章

基于Python实现一个图片拆分工具

《基于Python实现一个图片拆分工具》这篇文章主要为大家详细介绍了如何基于Python实现一个图片拆分工具,可以根据需要的行数和列数进行拆分,感兴趣的小伙伴可以跟随小编一起学习一下... 简单介绍先自己选择输入的图片,默认是输出到项目文件夹中,可以自己选择其他的文件夹,选择需要拆分的行数和列数,可以通过

Python中将嵌套列表扁平化的多种实现方法

《Python中将嵌套列表扁平化的多种实现方法》在Python编程中,我们常常会遇到需要将嵌套列表(即列表中包含列表)转换为一个一维的扁平列表的需求,本文将给大家介绍了多种实现这一目标的方法,需要的朋... 目录python中将嵌套列表扁平化的方法技术背景实现步骤1. 使用嵌套列表推导式2. 使用itert

C#读写文本文件的多种方式详解

《C#读写文本文件的多种方式详解》这篇文章主要为大家详细介绍了C#中各种常用的文件读写方式,包括文本文件,二进制文件、CSV文件、JSON文件等,有需要的小伙伴可以参考一下... 目录一、文本文件读写1. 使用 File 类的静态方法2. 使用 StreamReader 和 StreamWriter二、二进

Python使用pip工具实现包自动更新的多种方法

《Python使用pip工具实现包自动更新的多种方法》本文深入探讨了使用Python的pip工具实现包自动更新的各种方法和技术,我们将从基础概念开始,逐步介绍手动更新方法、自动化脚本编写、结合CI/C... 目录1. 背景介绍1.1 目的和范围1.2 预期读者1.3 文档结构概述1.4 术语表1.4.1 核

在Linux中改变echo输出颜色的实现方法

《在Linux中改变echo输出颜色的实现方法》在Linux系统的命令行环境下,为了使输出信息更加清晰、突出,便于用户快速识别和区分不同类型的信息,常常需要改变echo命令的输出颜色,所以本文给大家介... 目python录在linux中改变echo输出颜色的方法技术背景实现步骤使用ANSI转义码使用tpu

C#中Guid类使用小结

《C#中Guid类使用小结》本文主要介绍了C#中Guid类用于生成和操作128位的唯一标识符,用于数据库主键及分布式系统,支持通过NewGuid、Parse等方法生成,感兴趣的可以了解一下... 目录前言一、什么是 Guid二、生成 Guid1. 使用 Guid.NewGuid() 方法2. 从字符串创建

Python使用python-can实现合并BLF文件

《Python使用python-can实现合并BLF文件》python-can库是Python生态中专注于CAN总线通信与数据处理的强大工具,本文将使用python-can为BLF文件合并提供高效灵活... 目录一、python-can 库:CAN 数据处理的利器二、BLF 文件合并核心代码解析1. 基础合

Python使用OpenCV实现获取视频时长的小工具

《Python使用OpenCV实现获取视频时长的小工具》在处理视频数据时,获取视频的时长是一项常见且基础的需求,本文将详细介绍如何使用Python和OpenCV获取视频时长,并对每一行代码进行深入解析... 目录一、代码实现二、代码解析1. 导入 OpenCV 库2. 定义获取视频时长的函数3. 打开视频文

golang版本升级如何实现

《golang版本升级如何实现》:本文主要介绍golang版本升级如何实现问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录golanwww.chinasem.cng版本升级linux上golang版本升级删除golang旧版本安装golang最新版本总结gola

SpringBoot中SM2公钥加密、私钥解密的实现示例详解

《SpringBoot中SM2公钥加密、私钥解密的实现示例详解》本文介绍了如何在SpringBoot项目中实现SM2公钥加密和私钥解密的功能,通过使用Hutool库和BouncyCastle依赖,简化... 目录一、前言1、加密信息(示例)2、加密结果(示例)二、实现代码1、yml文件配置2、创建SM2工具