一 TCP 和 UDP 比较

wireshark tcp半双工_拆包

二 TCP协议特点

#1 TCP协议是面向连接的传输层协议,即在应用程序在使用TCP协议之前,必须先建立TCP连接,数据传输完毕后还需要释放连接

#2 每一个TCP连接只能由两个端点,每一个TCP连接只能进行一对一传输

#3 TCP协议提供数据可靠性,即数据无差错,不丢失,不重复,且按顺序发送

(可靠有序、不丢不重)

#4 TCP协议是全双工通信。TCP允许通信双方在应用进程任何时候都能发送数据。TCP连接的两端都设有接收缓存和发送缓存,用来临时存放双向通信数据。发送缓存:准备发送的数据 & 已发送但是还没有确认的数据(丢失可能会重传)

接收缓存:没有按顺序到达的数据或者按顺序到达的数据但是还没被应用程序读取

#5 TCP协议是面向字节流的

三TCP报文首部格式

wireshark tcp半双工_wireshark tcp半双工_02

3.1 源端口和目标端口

源端口和目标端口分别占16位,即2个字节,源端口代表发送方的应用程序端口;目标端口表示接收方目标端口

3.2 序号(sequence number)

wireshark tcp半双工_粘包_03

发送方应用程序使用TCP协议向接收方发送数据,数据可能是文本或者文件。因为文件很大,超过了TCP规定MSS(最大段大小),就需要将数据字节流进行分段传输。因为数据传输到目的地,可能顺序已经混乱了, 然后接收端的传输层对需要对传过来的数据排序,然后应用程序直接读取排序好的字节流。那怎么排序呢,排序总要有所依据吧。那么这个字节序号就是排序的依据。

假设TCP的Max Segment Size(MSS)为100个字节,那么表示我们每一次数据传输不能超过100个字节。应用程序写入字节流的时候,从第一个字节开始计算,到100个字节为止,然后封装为一个数据包,加上TCP首部,交付给网络层。这时候会将该数据包中第一个位置上字节流序号作为字节流序号,写入首部。

第一次发送数据,那么第一个字节序号也叫作初始序列号ISN(Initial Sequence Number)。

第二次发送的时候,会根据接收方返回的确认数据包中首部信息确认码作为依据,作为第二次发送时候的序列号,一般情况,这个序列号就是第一次发送的数据包中末尾字节的序列号加1. 比如第一次发送的数据包最后一个字节是第100个,那么返回的确认数据包中的确认码就是101,表示从101开始计算字节序列号,然后弄满100个再放到接收端。

3.3 确认号(acknowledge number)

wireshark tcp半双工_粘包_03

期望收到对方的下一个报文段数据的第一个字节序号,然后如果发送端数据还有报文段需要发送,则会以这个返回的确认号作为字节序号。

3.4 首部长度(数据偏移)(header length)

首部长度:占用4 位,单位是是字节。因为最大为15(1111),所以最大长度为60字节。TCP数据段的首部长度,因为TCP首部有可选字段,所以首部长度不固定,为了获取数据段中数据部分,需要指明首部长度。所以和IP协议一样,TCP报文最多60字节, 的首部固定长度是20字节,可选部分为非固定字段,最多允许(60-20)= 40字节。

3.5 标记位

3.5.1 紧急标记位(URG)

紧急标记位:占用1位,0或者1表示。

#1 URG=1: 表明紧急指针字段(urgent pointer)有效,告诉系统此报文有紧急数据,应尽快传输(相当于高优先级的报文),而不是按照原来的顺序来传送

#2 URG=0: 表明紧急指针字段(urgent pointer)五效

3.5.2 确认标记位(ACK)

确认标记位:占用1位,0或者1表示。

#1 ACK=1: 表示确认号字段(acknowledge number)有效

#2 ACK=0: 表示确认号字段(acknowledge number)无效。

TCP规定,连接建立后所有传送的报文段都必须把ACK置为1

3.5.3 推送标记位(PSH)

确认标记位:占用1位,0或者1表示。

通知接收方尽快接收数据,而不是等接收方缓冲区满了,应用程序再接收数据

3.5.4复位标记位(RST)
复位标记位:占用1位,0或者1表示。

#1: RST=1: 表示TCP连接出现严重差错(主机崩溃或者其他原因),必须释放连接,然后再重新建立连接

3.5.5 同步标志位(SYN)

同步标志位:占用1位,0或者1表示,在连接建立的时候同步序号.

#1 SYN=1 且ACK=0时,这是一个连接请求报文段,如果对方若同意建立连接,则应该响应的报文中使得SYN=1且ACK=1.所以SYN=1表示这是一个连接请求或连接接收报文

wireshark tcp半双工_粘包_05

3.5.6 终止标志位(FIN)

终止标志位: 占用1位,0或者1表示. 用来释放一个连接。

FIN=1: 表示此报文段的发送方的数据已发送完毕,并要求释放传输连接。

3.6 窗口(window)

窗口:占用16位。我们知道在发送TCP报文的时候,如果没有限制,就可以一下将很多TCP数据包发出去,也不管接收方的接受能力或者数据是否丢掉了。

3.7 校验和(checksum)

占用16位,和UDP一样,校验首部和数据部分,而IP只校验首部。校验数据传输过程是否坏了。

3.8 紧急指针(urgent pointer)

紧急指针占用16位,表示紧急

首先: URG标志位是1,这个紧急指针才起作用。

其次: 指定了紧急指针N,且URG=1,那么数据部分前N个字节是紧急数据

四 什么是TCP粘包和拆包问题,如何解决?UDP是否存在粘包和拆包问题?

4.1 TCP粘包和拆包问题

我们知道,TCP是面向字节流的,根据缓冲区大小或者MTU大小确定每次最大传输段(MSS)大小,如果要传输的数据大于这个大小,就需要将数据拆分成多个段进行传输。这就是拆包现象。

又或者发送端为了提升性能,欲将多个小包合成一个大的包传输到接收端。比如Nagle算法。这就会导致粘包现象。

4.2 TCP半包读写解决方案

方案一: 发送端关闭Nagle算法

方案二: 接收端TCP是无界的数据流,并没有处理粘包的机制,半包的读写的需要在应用层来处理

#1 设置定长消息(比如10字节或者5个字符) hello00000

#2 设置消息的边界(分隔符 ##)

#3 使用带消息头的协议(head+body), 带有消息长度的头部信息和body信息

4.3  UDP是否存在粘包和拆包问题

#1 UDP是面向报文,不是面向流的

#2 UDP发送是以一条一条的 报文发送;接收也是按照一条一条的接收;如果缓冲区满了,数据丢失

#3 接收端缓冲区采用链式结构存储到来的数据报文,每一个报文都包含对应的消息源地址和端口号等

所以,综上所述,UDP具有保护消息边界的特性,不存在拆包和粘包的问题