Socket套接字TCP通信
- 客户端在SocketTCP编程时做了什么:
- 创建套接字Socket
- 用Connect方法与服务端相连
- 用Send和Receive相关方法收发数据
- 用Shutdown方法释放连接
- 关闭套接字
- 服务端在SocketTCP编程时做了什么:
- 创建套接字Socket
- 用Bind方法将套接字与本地地址绑定
- 用Listen方法监听
- 用Accept方法等待客户端连接
- 建立连接,Accept返回新套接字
- 用Send和Receive相关方法收发数据
- 用Shutdown方法释放连接
- 关闭套接字
- TCP编程流程:
- 三次握手在Socket的体现:
- 四次挥手在Socket的体现:
- TCP协议的三次握手和四次挥手被Socket封装在了内部,不需要我们进行额外处理
- 当序列化的2进制数据发给对象时,对方如何区分?
- 为发送的消息增加标识,比如添加消息ID(int、short、byte、long都可以,根据实际情况选择)
- 比如选择int类型作为消息ID的类型则前4字节为消息ID,后面为数据类的内容,每次接收到消息时,先把前4字节取出来解析为消息ID再根据ID进行消息的反序列化即可
- 分包(拆包)、粘包:指在网络通信中由于各种因素(网络环境、API规则)等造成的消息与消息之间出现的两种状态
- 分包:一个消息分成了多个消息发送
- 粘包:一个消息和另一个消息黏在了一起,被合并发送了
- 分包和粘包可以同时发生,比如一个数据包前半部分是消息A的后半部分,后半部分是消息B
- MTU:
- 最大传输单元,用来通知对方所能接受数据服务单元的最大尺寸。
- 不同操作系统会提供用户一个默认值,以太网和802.3对数据帧的长度限制,其最大值分别是1500字节和1492字节
- 为什么会出现粘包?
- 当发送端缓冲区的长度大于网卡的MTU时,TCP会将这次发送的数据拆成几个数据包发送出去。
- 如果本机的MTU比网关的MTU大,大的数据包就会被拆开来传送,这样会产生很多数据包碎片,增加丢包率,降低网络速度。
- TCP的协议数据不会丢,没有收完包,下次接收,会继续上次继续接收,己端总是在收到ack时才会清除缓冲区内容。数据是可靠的,但是会粘包。
- 解决粘包拆包问题的思路:
- 发定长数据:接收方拿固定长度的数据,发送方发送固定长度的数据即可。但是这样的缺点也是显而易见的:如果发送方的数据长度不足,需要补位,浪费空间。
- 在包尾部增加特殊字符进行分割:发送方发送数据时,增加特殊字符;在接收方以特殊字符为准进行分割
- 自定义协议:类似于HTTP协议中的HEAD信息,比如我们也可以在HEAD中,告诉接收方数据的元信息(数据类型、数据长度等)
- 如何判断是否分包或者粘包(思路3):我们为消息增加头部,头部记录消息的长度,当我们接收到消息后,通过消息长度进行判断,对消息进行拆分、合并处理,每次只处理完成的消息。
- 如何解决粘包?(思路3)
- 首先将所有接收到的字节流,塞入缓冲池
- 根据包头信息,判断是否能获取当前完整协议,若能获取完整,则根据协议头提取数据。
- 若此时缓冲池还有其他协议数据(说明粘包了),能继续提取就继续,不能则跳过,继续等待接收新字节流(等待传来完整的数据包),塞入缓冲池。
- 循环1、2、3
- 心跳包:在TCP中,客户端与服务端之间定期发送(比如5s)的一种特殊的数据包,用于通知对方自己在线,以确保长连接的有效性,由于是间隔固定且持续的所以被称为心跳包。
- 心跳包的作用:
- 避免非正常的关闭客户端时,服务器无法正常收到关闭连接,通过心跳包来进行自定义超时判断,如果长时间没有收到客户端发来信息,说明客户端已经断开。
- 避免客户端长时间不发消息,防火墙或者路由器会断开连接,通过心跳包来一直保持活跃状态。
- 心跳包客户端与服务端的工作:客户端定时发送消息,服务端不听检测上次收到某客户端的时间,如果超时则认为已经断开
- 什么时候会断线重连:
- Socket Connect(创建)时超时或异常
- Socket Receive/Send(收发)时异常
- 客户端“丢包”时,指定时间内收不到回包
- 加解密时发现客户端服务器Summary校验码不一致
- 如何实现断线重连?
- TCP不是可靠的协议吗,为什么TCP协议会丢包?如何避免丢包?
- 在客户端断线时,可能发送了一些本地的消息协议出去,但是因为客户端断线了,服务器没收到,导致你认为“丢包”了。
- 解决办法(记得排除掉心跳包,因为收发太频繁了):
- C2S发送出去,务必返回对应的S2C协议
- 建立一个字典容器,S2C协议作为Key,C2S作为Value,
- 收到对应的S2C后,移除该值
- 若未收到,则游戏界面转圈,等待一定时长后,启动断线重连,此时,将你这个字典内现有的协议,再重新发送一次
- SocketTCP网络架构设计UML图
Socket套接字UDP通信
- 客户端在SocketUDP编程时做了什么:
- 创建套接字Socket
- 用Bind方法将套接字与本地地址进行绑定
- 用ReceiveFrom和SendTo方法在套接字上收发消息
- 用Shutdown方法释放连接
- 关闭套接字
- UDP编程流程:
- 粘包问题:UDP本身作为无连接的不可靠的传输协议(适合频繁发送较小的数据包),他不会对数据包进行合并发送,一端发送什么数据,直接就发出去了,他不会对数据合并,因此在UDP当中不会出现黏包问题(除非你觉得消息太小可以手动进行黏包)
- 分包问题:
- 由于UDP是不可靠的连接,消息传递过程中可能出现无序、丢包等情况,所以如果允许UDP进行分包,那后果将会是灾难性的,比如分包的后半段丢包或者比上半段先发来,我们在处理消息时将会非常困难
- 因此为了避免其分包,我们建议在发送UDP消息时控制消息的大小在MTU(最大传输单元)范围内。
- UDP的MTU:
- 由于UDP包头本身带有一些信息,因此建议:
- 局域网环境下:1472字节以内(1500减去UDP头部28为1472)
- 互联网环境下:548字节以内(老的ISP拨号网络的标准值为576减去UDP头部28为548)
- 只要遵守这个规则,就不会出现自动分包的情况。
- 如果想要发送的消息确实比较大,要大于548字节或1472字节这个限制,我们可以进行手动分包,将数据拆分成多个消息,每个消息不超过限制,但是手动分包的前提是要解决UDP的丢包和无序问题。
- 我们可以将不可靠的UDP通信实现为可靠的UDP通信来解决丢包和无序的问题,比如:在消息中加入序号、消息总包数、自己的包ID、长度等等信息并且实现消息确认、消息重发等功能
- 由于UDP包头本身带有一些信息,因此建议:
FTP工作原理
- FTP:文件传输协议,是支持Internet文件传输的各种规则所组成的集合,这些规则使Internet用户可以把文件从一台主机拷贝到另一台主机上,除此之外,FTP还提供登录、目录查询以及其他会话控制等功能
- FTP的本质是TCP通信:通过FTP传输文件,双发至少需要建立两个TCP连接一个称为控制连接,用于传输FTP命令,一个称为数据连接,用于传输文件数据
- FTP的数据连接和控制连接方向一般是相反的:
- 比如用户使用FTP客户端连接FTP服务器请求下载文件
- 控制连接方向:客户端主动连接服务器告知其下载命令
- 数据连接方向:服务端主动连接客户端下发数据
- 当客户端和FTP服务器建立控制连接后需要告诉服务器采用那种传输模式:
- 主动模式(Port模式):服务器主动连接客户端,然后传输文件
- 被动模式(Passive模式):客户端主动连接服务器即控制连接和数据连接都由客户端发起
- 一般情况下主动模式会受到客户端防火墙影响,所以被动模式使用较多
- 在使用FTP进行数据传输时有两种数据传输方式:
- ASCII传输方式:以ASCII编码方式传输数据,适用于传输,仅包含英文的命令和参数或者英文文本文件
- 二进制传输方式(建议使用该方式):可以指定采用哪种编码传输命令和文件数据,如果传输的文件不是英文文件则应该采用该方式
- 一般情况下使用FTP传输文件时,客户端必须先登录服务器,获得相应权限后才能上传或下载文件,当然服务器也可以允许用户匿名登录FTP不需要都拥有一个合法账号
HTTP工作原理
- HTTP:
- 超文本传输协议,是因特网上应用最为广泛的一种网络传输协议。
- 最初设计HTTP的目的是为了提供一种发布和接收由文本文件组成的HTML页面的方法,后来发展到除了文本数据外,还可以传输图片、音频、视频、压缩文件以及各种程序文件等。
- HTTP主要用于超文本传输,因此相对FTP显得更简单一些,目前常见的HTTP标准是HTTP/1.1.
- HTTP的本质也是TCP通信:
- HTTP定义了Web客户端(一般指浏览器)如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。
- HTTP客户端首先与服务器建立TCP连接,然后客户端通过套接字发送HTTP请求,并通过套接字接收HTTP响应,由于HTTP采用TCP传输数据,因此不会丢包、不会乱序。
- HTTP以TCP方式工作:
- 在HTTP/1.0中,客户端和服务器建立TCP连接后,发送一个请求到服务器,服务器发送一个应答给客户端,然后立即断开TCP连接(因此十分适合做短连接,浏览器请求就是一个典型的短连接),他们的主要步骤为:
- 客户端与服务端建立TCP连接
- 客户端向服务端发出请求
- 若服务端接受请求,则回送响应码和所需的信息
- 客户端与服务端断开TCP连接
- 需要注意,HTTP/1.1 支持持久连接,即客户端和服务端建立连接后,可以发送请求和接收应答,然后迅速地发送另一个请求和接收另一个应答。
- 持久连接也使得在得到上一个请求的应答之前能够发送多个请求,这就是HTTP/1.1与HTTP/1.0的明显不同之处,除此之外,HTTP/1.1可以发送的请求类型也比HTTP/1.0多。
- 目前市面上的Web服务器软件和浏览器软件基本都是支持HTTP/1.1版本的,目前使用的基本上都是HTTP/1.1版本。
- 在HTTP/1.0中,客户端和服务器建立TCP连接后,发送一个请求到服务器,服务器发送一个应答给客户端,然后立即断开TCP连接(因此十分适合做短连接,浏览器请求就是一个典型的短连接),他们的主要步骤为:
- HTTP是无状态的:
- 客户端发送一次请求后,服务端并没有存储关于该客户端的任何状态信息即使客户端再次请求同一个对象,服务端仍会重新发送这个对象,不会在意之前是否已经向客户端发送过这个对象
- 也就是说HTTP通信就是客户端要什么来什么,想要多少来多少,服务端不会因为你要过了而不给你,不会记录你要过的状态
- HTTP使用元信息作为标头:
- HTTP通过添加标头(header)的方式向服务端提供本次HTTP请求的相关信息,即在主要数据前添加一部分额外信息,称为元信息(metainformation)元信息里主要包含传送的对象属于哪种类型,采用的是哪种编码等等
- 也就是说HTTP的元信息有标头,类似我们Socket通信时用于区分消息类型、处理分包黏包时,在消息体前方加的自定义信息。在HTTP协议中,它也定义了类似的规则,在头部包含了一些额外信息。
- HTTP协议的请求类型:
- 请求类型:
- HTTP/1.0中:GET、POST、HEAD
- HTTP/1.1中:GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT
- 各个请求类型代表的含义:
- 我们主要使用GET和POST方法来获取或者上传数据
- 内容发送的格式:
- 请求类型:
- HTTP协议中的响应状态码:
- 响应状态码:1xx、2xx、3xx、4xx、5xx
- 状态行中主要内容有:
- HTTP版本号
- 3位数字组成的状态码
- 1xx消息:请求已被服务端接收,继续处理
- 2xx成功:请求已成功被服务端理解并接收
- 3xx重定向:需要后续操作才能完成这一请求
- 4xx请求错误:请求含有语法错误或者无法被执行
- 5xx服务器错误:服务端在处理某个正确请求时发生错误
- 常用状态码代表的含义:
- HTTP响应的一般格式:
- 相对HTTP请求的格式,只有第一部分不同,由请求行变成了状态行
协议生成工具
- 协议生成工具:一般指消息(协议)生成工具,是专门用于自动化生成消息的程序。
- 有什么用:当需要一个新消息需要传输的时候,我们需要手动的按照规则来声明新的类,费时而繁琐,且如果是前后端用的不是统一的语言,容易造成沟通的不一致,申明不统一等问题。因此我们在进行网络游戏开发的时候就需要协议生成工具来自动化声明消息类。
- 好处:提升开发效率,降低沟通成本,避免前后端消息不匹配的问题。
- 如何制作协议生成工具:
- 我们对协议生成工具的需求如下:
- 通过配置文件配置消息或者数据类型、名字、变量等
- 工具根据该配置文件信息动态生成类文件
- 可以在开发中直接使用生成文件中声明好的消息和数据结构类进行开发
- 具体如何做:
- 确定协议格式:可以使用json、xml、自定义格式(比如protobuf)来进行协议配置,主要是通过配置文件确定消息或数据结构类的名字、字段名等
- 确定生成格式:因为最终我们要自动生成类的声明文件,所以具体类应该确定如何生成需要的格式,比如继承关系的固定写法,序列化、反序列化的固定写法,提取出共同点
- 制作生成工具:基于配置文件和生成格式,动态的生成对应类文件
- 我们对协议生成工具的需求如下:
- Protobuf:
- 一个谷歌开源的协议生成工具,可以基于协议配置文件生成C++、Java、C#、Objective-C、PHP、Python、Ruby、Go等语言的代码文件,很多商业游戏开发会使用他作为协议生成工具,他的通用性强,稳定性高,可以自己开发协议生成工具的时间。
- 具有良好的兼容性,易于扩展,并且具有很高的序列化和反序列化的效率,能极大的减小传输数据量的大小。压缩大小,大约是json格式的1/10,xml格式的1/20。
- lua-protobuf是一个纯C的protobuff协议实现,可以和Lua相互绑定
- GitHub链接:https://github.com/protocolbuffers/protobuf
- 官网:https://developers.google.com/protocol-buffers/
- lua-protobuf 使用说明:https://zhuanlan.zhihu.com/p/26014103
- Unity中protobuf的使用方法:https://www.cnblogs.com/wbaoqing/p/7725533.html
- 协议头结构:数据总长度(4byte)+协议类型(2byte)+校验码大小(2byte)+数据(Nbyte)
- Protobuf使用流程:
- 下载对应语言要使用Protobuf的相关内容
- 准备DLL文件
- 在官网中下载protobuf-csharp
- 解压后打开csharp\src中的Google.Protobuf.sln
- 选择Google.Protobuf右键生成dll文件
- 在csharp\src\Google.Protobuf\bin\Debug路径下找到对应.net版本的DLL文件(用4.5即可)
- 将net45中的dll文件导入到Unity工程中的Plugins插件文件夹中
- 准备编译器
- 在官网中下载protoc-版本-win64
- 解压后获取bin文件中的protoc.exe文件,可放到Untiy工程中方便之后使用
- 准备DLL文件
- 根据配置规则编辑协议配置文件
- 用Protobuf编译器,利用协议配置工具生成对应语言的代码文件
- 将代码文件导入工程中进行使用
- 下载对应语言要使用Protobuf的相关内容
- Protobuf-net:早期的protobuf不支持C#,protobuf-net是其他人在protobuf的基础上进行.net环境的移植,虽然现在的protobuf已经支持了.net环境,但是并不支持.net3.5以下版本,所以想要在老版本使用protobuf就要使用protobuf-net,如果把protobuf的dll包导入后能够正常使用不报错就说明兼容性没问题
大小端模式
- 大端模式:
- 指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的储存模式类似于把数据当做字符串顺序处理,地址由小向大增加,数据从高位往低位存放,符合人们的阅读习惯。
- 比如16进制数据(高字节)0x11223344(低字节)->(高地址)11223344(低地址)
- 小端模式:
- 指数据的高字节保存在高地址中,而数据的低字节保存在低地址中。
- 比如16进制数据(高字节)0x11223344(低字节)->(低地址)44332211(高地址)
- 大小端模式的作用:
- 大小端模式实际上是计算机硬件的两种存储数据的方式,我们也可以称大小端模式为大小端字节序
- 对于我们来说大端字节序读起来更加方便,但是计算机在处理字节序都是使用小端字节序处理的。
- 一般情况下,操作系统中都使用小端模式,而通信协议中都使用大端模式,但是具体的模式要根据硬件平台、开发语言来决定。
- 大小端对我们的影响:
- 只有读取的时候才必须区分大小端字节序。
- 在网络传输中传输的是字节数组,所以我们在收到字节数组进行解析时,就必须考虑大小端问题,虽然TCP/IP协议规定了在网络上必须采用网络字节顺序(大端模式),但是具体传输时采用哪种模式,都是根据前后端语言、设备决定的。
- 一般情况下:C#和Java、Erlang、AS3通讯需要大小端转换,因为C#是小端模式,Java、Erlang、AS3是大端模式,C#和C++通信不需要特殊处理,因为他们都是小端模式
消息加密解密
- 消息加密解密:我们在网络传输的时候,会把数据转换为字节数组以2进制的形式进行传输,如果有人篡改了消息,或者在前端发假消息给后端就可能作弊,消息的加密解密,可以有效的避免作弊行为的产生。
- 加密的安全性:加密只能提高破解的门槛,只能提升一定的安全性。
- 明文:待加密的报文
- 密文:加密后的报文
- 密钥:加密过程中或解密过程中输入的数据
- 加密算法:将明文和秘钥相结合进行处理,生成密文的方法
- 解密算法:将密文和秘钥相结合进行处理,生成明文的方法
- 单向加密:
- 将数据进行计算变成另一种固定长度的值,这种加密是不可逆的
- 常用算法:MD5、SHA1、SHA256等
- 用途:这种加密在网络传输中不会使用,主要用于密码的单向加密(比如热更新的时候对比文件是否一致)
- 对称加密:
- 使用同一个秘钥,对数据镜像加密和解密(用秘钥对明文进行加密,用秘钥对密文进行解密)
- 常用算法:DES、3DES、IDEA、AES等
- 优点:计算量小,加密速度快,效率高
- 缺点:如果知道了秘钥和加密算法,就可以进行解密
- 用途:网络通讯中可以使用非对称加密技术,这个秘钥可以是从后端下发的,每次建立通讯后都会变化。
- 非对称加密技术:
- 在加密过程中,需要一对秘钥,不公开的秘钥称为私钥,公开的哪一个秘钥称为公钥,也被称为公开秘钥加密。
- 从一对秘钥中的任何一个秘钥中都不能计算出另一个秘钥。使用一对秘钥中的任何一个进行加密,只有拥有另一个秘钥才能解密,如果截获公钥加密数据,没有私钥也无法解密。
- 常见算法:RSA、DSA等
- 优点:安全性高,即使获取到了公钥,没有私钥也无法尽心解密
- 缺点:算法复杂,加密速度较慢
- 用途:对安全性要求较高的场景,并且可以接受较慢的加密速度,在对接一些支付SDK时平台提供的一般就是非对称加密技术。
- 使用:我们一般也是直接用别人写好的第三方加密算法库,我们也可基于加密算法的原理来设计自己的规则。
Comments | 0 条评论