一、TCP
1.1、头部
1)序列号:本报文段所发送的数据的第一个字节的序号
例如,一报文段的序号字段值是301,而携带的数据共有100字节。这就表明:本报文段的数据的第一个字节的序号是 301,最后一个字节的序号是400。显然,
下一个报文段(如果还有的话)的数据序号应当从401开始,即下一个报文段的序号字段值应为401。这个字段的名称也叫做“报文段序号”。
2)确认号:期待收到对方下一个报文段的第一个数据字节的序号。
例如,B正确收到了A发送过来的一个报文段,其序号字段值是501,而数据长度是200字节(序号501~700),这表明B正确收到了A发送的到序号700为止的数据。因此,
B期望收到A的下一个数据序号是701,于是B在发送给A的确认报文段中把确认号置为701。请注意,现在的确认号不是501,也不是700,而是701。总之,应当记住:
3)数据偏移占4位,它指出TCP报文段的数据起始处距离TCP报文段的起始处有多远。这个字段实际上是指出TCP报文段的首部长度。由于首部中还有长度不确定的选项字段,
因此数据偏移字段是必要的。但应注意,“数据偏移”的单位是32位字(即以4字节长的字为计算单位)。由于4位二进制数能够表示的最大十进制数字是15,因此数据偏移的最大值是60字节,
这也是TCP首部的最大长度(即洗项长度不能超过40字节)。
4)保留:占6位,保留为今后使用,但目前应该置为0。
5)6个控制位:
1、紧急URG(URGent):当URG = 1时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据),而不要按原来的排队顺序来传送。
2、确认ACK(ACKnowledgment):仅当ACK= 1时确认号字段才有效。当ACK = 0时,确认号无效。TCP规定,在连接建立后所有传送的报文段都必须把ACK置1。
3、推送 PSH (PuSH):当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能够收到对方的响应。在这种情况下,TCP 就可以使用推送(push)操作。
这时,发送方TCP把 PSH 置1,并立即创建一个报文段发送出去。接收方TCP收到PSH=1的报文段,就尽快地(即“推送”向前)交付接收应用进程,而不再等到整个缓存都填满了后再向上交付。
4、复位RST (ReSeT):当RST = 1时,表明TCP连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接。RST 置1还用来拒绝一个非法的报文段或
拒绝打开一个连接。RST也可称为重建位或重置位。
5、同步SYN (SYNchronization):在连接建立时用来同步序号。当SYN = 1而ACK= 0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使sYN=1和ACK= 1。
因此,SYN置为1就表示这是一个连接请求或连接接受报文。关于连接的建立和释放,在后面的5.9节还要进行详细讨论。
6、终止FIN (FINis,意思是“完”、“终”):用来释放一个连接。当FIN= 1时,表明此报文段的发送方的数据已发送完毕,并要求释放运输连接。
6)窗口:占2字节。窗口值是[0,216 - 1]之间的整数。窗口指的是发送本报文段的一方的接收窗口(而不是自己的发送窗口)。窗口值告诉对方:从本报文段首部中的确认号算起,
接收方目前允许对方发送的数据量(以字节为单位)。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。总之,窗口值作为接收方让发送方设置其发送窗口的依据。
例如,发送了一个报文段,其确认号是701,窗口字段是1000。这就是告诉对方:“从701号算起,我(即发送此报文段的一方)的接收缓存空间还可接收1000个字节数据(字节序号是701~1700),
你在给我发送数据时,必须考虑到这一点。总之记住:
7)检验和:占2字节。检验和字段检验的范围包括首部和数据这两部分。
8)紧急指针:占2字节。紧急指针仅在URG = 1时才有意义,它指出本报文段中的紧急数据的字节数(紧急数据结束后就是普通数据)。因此,紧急指针指出了紧急数据的末尾在报文段中的位置。
当所有紧急数据都处理完时,TCP 就告诉应用程序恢复到正常操作。值得注意的是,即使窗口为零时也可发送紧急数据。
9)选项:长度可变,最长可达40字节。当没有使用“选项“时,TCP的首部长度是20字节。
1.2、特性
1)面向连接:应用程序在使用 TCP 协议之前,必须先建立 TCP 连接。在传送数据完毕后,必须释放已经建立的 TCP 连接。
2)点对点:每一条TCP连接只能有两个端点,每一条 TCP 连接只能是点对点的(一对一)。
3)可靠性:通过选择确认、超时重传、滑动窗口机制,保证了数据包的无差错、不丢失、不重复,并且按序到达。
4)全双工:TCP 允许通信双方的应用进程在任何时候都能发送数据。TCP 连接的两端都设有发送缓存和接受缓存,用来临时存放双向通信的数据。16384 : tcp 发送缓冲区的默认值。131071:tcp 或 udp 接收缓冲区最大可设置值的一半。
5)面向字节流:TCP中的“流”指的是流入到进程或从进程流出的字节序列。
6)报文首部:20字节固定长度+选项
二、UDP
2.1、头部
(1)源端口:源端口号。在需要对方回信时选用。不需要时可用全0。
(2)目的端口:目的端口号。这在终点交付报文时必须使用。
(3)长度:UDP用户数据报的长度,其最小值是8(仅有首部)。
(4)检验和:检测UDP用户数据报在传输中是否有错。有错就丢弃。
UDP计算检验和的方法和计算IP数据报首部检验和的方法相似。但不同的是:IP数据报的检验和只检验P数据报的首部,但UDP的检验和是把首部和数据部分一起都检验。
2.2、特性
1)UDP 是无连接的,即发送数据之前不需要建立连接(发送数据结束时也没有连接可释放),减少了开销和发送数据之前的时延
2)UDP 使用尽最大努力交付,即不保证可靠交付,主机不需要维持复杂的连接状态表
3)UDP 是面向报文的,发送方的 UDP 对应用程序交下来的报文,在添加首部后就向下交付 IP 层。UDP 对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界
4)UDP 没有拥塞控制,网络出现的拥塞不会使源主机的发送速率降低。这对某些实时应用是很重要的
5)UDP 支持一对一、一对多、多对一和多对多的交互通信
6)UDP 的首部开销小,只有8个字节,比 TCP 的20个字节的首部要短
三、一些问题
3.1、为什么说TCP是面向字节流?UDP面向数据报?
TCP面向字节流:
消息可能会被操作系统分组成多个的 TCP 报文,也就是一个完整的用户消息被拆分成多个 TCP 报文进行传输。
这时,接收方的程序如果不知道发送方发送的消息的长度,也就是不知道消息的边界时,是无法读出一个有效的用户消息的,因为用户消息被拆分成多个 TCP 报文后,
并不能像 UDP 那样,一个 UDP 报文就能代表一个完整的用户消息。
UDP面向数据报:
操作系统不会对消息进行拆分,在组装好 UDP 头部后就交给网络层来处理,所以发出去的 UDP 报文中的数据部分就是完整的用户消息,也就是每个 UDP 报文
就是一个用户消息的边界,这样接收方在接收到 UDP 报文后,读一个 UDP 报文就能读取到完整的用户消息。
3.2、TCP粘包是什么?怎么解决粘包问题?
粘包:
接收端只收到一个数据包,但是这一个数据包中包含了发送端发送的两个数据包的信息,这种现象即为粘包。
拆包:
接收端只收到一个数据包,但是这一个数据包中包含了发送端发送的一个数据包的不完整信息,这种现象即为拆包。
一般有三种方式分包的方式:
1)固定长度的消息;
这种是最简单方法,即每个用户消息都是固定长度的,比如规定一个消息的长度是 64 个字节,当接收方接满 64 个字节,就认为这个内容是一个完整且有效的消息。
缺点是灵活性不高。
2)特殊字符作为边界;
我们可以在两个用户消息之间插入一个特殊的字符串,这样接收方在接收数据时,读到了这个特殊字符,就把认为已经读完一个完整的消息。
3)自定义消息结构。
自定义一个消息结构,由包头和数据组成,其中包头包是固定大小的,而且包头里有一个字段来说明紧随其后的数据有多大。
3.3、TCP的长连接和短连接是什么?
TCP短连接:
Client 向 Server 发送消息,Server 回应 Client,然后一次读写就完成了,这时候双方任何一个都可以发起 close 操作,不过一般都是 Client 先发起 close 操作。
短连接一般只会在 Client/Server 间传递一次读写操作。
TCP长连接:
Client 与 Server 完成一次读写之后,它们之间的连接并不会主动关闭,后续的读写操作会继续使用这个连接。
3.4、TCP和UDP分别适用于什么场景?
TCP:
电影下载,视频、卡牌类游戏,对于实时性要求不高的游戏,答案是大部分时候,TCP打开 NODELAY(禁用Nagle算法)即可,现在网络情况好了很多,
即便是“帧锁定算法”上线的多人实时格斗游戏,也有在用 TCP跑着的。
UDP:
实时性高的游戏用的UDP,因为TCP的特性,一旦丢包就会重发,阻塞住后续的数据包,因而可能会产生一个较大的瞬时延迟。例如CS、英雄联盟这些游戏,
混合:
游戏战斗过程用的UDP,支付,建房间等用TCP。
3.5、UDP中使用connect的作用?
TCP中调用connect会引起三次握手,client与server建立连结。UDP中调用connect内核仅仅把对端ip和port记录下来。
UDP多次调用connect有两种用途:
1)指定一个新的ip和port连接,直接设置connect第二个参数即可
2)断开和之前的ip&port的连接,需要将connect第二个参数中的sin_family设置成 AF_UNSPEC即可。
UDP中使用connect可以提高效率.原因如下:
1)sendto的参数到内核空间以后,内核需要分配内存来存储这些参数值,当数据包从网卡发出去之后,内核还需要释放掉这块内存;下次再调用sendto的时候,
内核就需要再次分配内存存放这些临时的数据结构,周而复始,就会形成一个 不断地分配和释放临时内存的过程。普通的UDP发送两个报文内核做了如下操作:
#1:建立连结#2:发送报文#3:断开连结#4:建立连结#5:发送报文#6:断开连结。
2)调用了connect,内核中就已经维护了一个”连接”,就可以调用send函数来进行发送数据了,内核中维护的这个 连接,包含了 发送端和接收端的地址,
并且永久维护一个数据结构,存储这些地址信息,后面每次进行发送数据,内核就不需要再分配删除内存了。采用connect方式的UDP发送两个报文内核做如下处理:
#1:建立连结#2:发送报文#3:发送报文。另外一点,每次发送报文内核都有可能要做路由查询。
3.6、UDP如何实现可靠连接?
为什么需要基于UDP实现可靠传输,主要是为了解决当前TCP连接成本高,队首阻塞。连接成本主要是三次握手;队首阻塞指的是TCP是通过滑动窗口进行数据发送,
第一个数据丢失了,那么会超时重传,后面的数据将无法发送。
传输层无法保证数据的可靠性,只能通过应用层来保证,实现的方式参考tcp的可靠传输的方式,只是实现不在传输层,实现转移到了应用层。实现确认机制,实现重传机制,实现窗口确认机制。
1)添加seq/ack机制,确保数据发送到对端。
2)添加发送和接收缓冲区,主要是用户超时重传
3)添加定时器,实现超时重传机制
4)增加选择确认机制,收到后面的数据,而前面的数据没有收到,将后面的数据做缓存,确认已收到的后面的数据,重传前面丢失的数据。
3.7、状态同步和帧同步是什么?怎么实现?
状态同步:
状态同步顾名思义就是同步各个客户端的状态,可能过程不一定同步,但是能保证每一次操作后的状态是一致的,即客户端发送操作给服务端,
服务端接收所有客户端操作数据,处理计算逻辑得出结果,发送数据更新所有客户端。
1)结果同步法:
结果同步往往比较简单,位置即使全部错乱或者延迟很久都没有关系,因为游戏过程完全不在乎位置,只在乎最后的结果。
帧同步:
客户端发送操作给服务端,服务端转发所有客户端操作数据,所有客户端都通过随机因子保证每次得出结果一样。
1)乐观帧锁定:
实践中线上动作游戏通常用“定时不等待”的乐观方式在每次Interval时钟发生时固定将操作广播给所有用户,不依赖具体每个玩家是否有操作更新。
状态同步和帧同步的区别:
1)最大的区别就是战斗核心逻辑写在哪?状态同步的战斗逻辑在服务端,帧同步的战斗逻辑在客户端。
2)状态同步比帧同步流量消耗大,因为服务器负责计算,如果英雄有多条属性,每次改变都要同步,那么将消耗巨大。
3)帧同步的回放&观战比状态同步好做得多。
4)状态同步的安全性比帧同步高很多。
3.8、四元组是什么?五元组是什么?七元组是什么?
四元组:源IP地址、目的IP地址、源端口、目的端口。
五元组:源IP地址、目的IP地址、协议号、源端口、目的端口。
七元组:源IP地址、目的IP地址、协议号、源端口、目的端口,服务类型以及接口索引。
本文来自博客园,作者:快牵着我的袜子,转载请注明原文链接:https://www.cnblogs.com/socks/p/16146335.html