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的使用在游戏开发中所占比例以及重要程度不言而喻,这也是我们不断研究、不断探索、不断优化的原因。下一节我们就具体讲一讲观察者模式以及在网络模块中的应用。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值