区别1: 传输头部格式区别
tcp头部格式
序列号:建立连接的时候生成的随机数,通过SYN包传给主机,用来解决网络传输的乱序问题。
应答号:指下一次期望收到的数据序列号,配合上面的序列号一来解决丢包和乱序的问题。
控制位:(下面都是控制位的具体值,只能是0或者1)
ACK:表示此条消息位应答的消息,除了最初建立连接的SYN包之外的这个控制为都为1.
RST:表示TCP链接中出现了异常需要强制断开链接
SYN:表示希望建立连接,在序列号字段进行初始值的设定。
FIN:表示后面不会再传输数据希望断开连接。
UDP头部格式
相比之下UDP因为是面向无链接,同时不提供复杂的控制机制,所以协议十分的简短,头部只有8个字节(64位),仅包括目标和愿端口号,UDP首部和数据长度以及校验和(判断UDP是否完整)。
区别2: 是否面向连接
TCP是面向连接的、可靠的、基于字节流的传输通信协议
TCP必须通过连接完成之后一对一才能进行通信,而UDP是广播形式的,可以一对多进行通信,所以就不需要就行连接。
TCP的连接建立
TCP的三次握手
TCP通过三次握手来建立连接,具体过程如上面这张图。
一开始客户端和服务端都是close状态,服务端主动尖挺某个端口属于LISTEN状态
第一次握手:客户端这边首先发起连TCP连接建立的请求,客户端会随机初始化序列化号放到TCP的当中,吧SYN字段设置为1,表示这是一个请求建连接的SYN报文,然后发送出去。
第二次握手:服务端收到客户端的SYN报文后,也随即初始化一个自己的序列号,然后把SYN=1和ACK=1设置好之后发送确认连接的报文给客户端,对应的确认号为SYN报文的确认号+1
第三次握手:客户端收到服务端的报文之后还需要再回复一个应答的报文连接才能开始建立,ACK=1,确认应答号为回复报文序列号+1,发送给服务端即可。然后就进入到ESTABLESHED状态,服务端收到确认报文之后也进入到ESTABLESHED状态。同时第三次握手是可以直接携带数据的。
可以使用netstat -napt命令可以在Linux系统上面查看TCP的连接状态
为什么一定是三次握手不是两次或者四次
因为只有三次握手才能出实话Socket/序列号和窗口大小建立TCP连接。
原因1:避免历史链接
三次握手的最重要的原因就是为了防止旧的重复初始化连接造成混乱。
如果只有两次握手就建立连接的话,那么当客户端发请求到服务端,如果出现网络阻塞等意外情况导致请求没有到达,然后超时重传再发一了一个请求连接的请求,这个是最第一次发送的请求到达就直接建立连接,但是这次是历史连接导致的无用连接就造成了资源的浪费甚至是连接混乱。
原因2:同步双方初始序列号
TCP通信必须双方都维护一个序列号,保证消息按顺序不重复的接收。
只有通过一来一回双方都自己生成随机的序列号并且发送给对方之后,才能开始建立连接使用双方的序列号进行消息的发送。
原因3:避免资源的浪费
前面的三次请求就已经可以确定序列号并且保证连接的正确建立了,所以不需要在进行第四次的通信,防止建立多个冗余无效的连接,造成不必要的浪费。
SYN攻击
TCP连接需要三次握手,如果攻击者段时间伪造大量的IP地址同时发送SYN报文请求建立连接,那么服务端就会收到大量的SYN报文,同时进入到SYN_RCVD状态,然后发送对应的ACK+SYN报文,但是发送的报文久久没有人响应,就会沾满服务端的半连接队列,阻碍其他正常用户的TCP连接请求。
TCP三次握手的时候会维护两个队列:
1.半连接队列(SYN队列)收到客户端的SYN报文之后将对应的请求记录到半连接队列
2.全连接队列(accept队列)收到客户端的ACK确认连接建立的报文之后将请求放到全连接队列
所以当TCP半连接队列满了之后,在收到的SYN请求报文就会被丢弃,导致真正的用户请求无法建立对应的TCP连接。
解决
1.增大TCP半连接队列
2.开启net.ipv4.tcp_syncookies 从而实现不使用SYN半连接队列建立连接。(绕开半连接队列)
3.减少SYN+ACK的重传次数 也就是将队列当中的请求快速的实效,发现没有人回应快速失败
TCP的四次挥手
断开TCP连接的时候需要经历上面的四个步骤。这里假设客户端发起请求断开连接。
第一次握手:客户端发送一个TCP首部FIN标志位为1的报文,然后进入FIN-WAIT1状态。
第二次握手:服务端收到报文后发送ACK应答报文,然后进入CLOSE-WAIT状态
第三次握手:客户端收到ACK应答报文之后进入FIN-WAIT-2状态,这个时候服务端还会继续发送没有发送完成的数据,等到服务端发送处理完数据之后。服务端在此发送FIN报文,然后服务端进入LAST-ACK状态。
第四次握手:客户端收到FIN报文之后,回应一个ACK应答报文,然后进入TIME-WAIT状态。
服务端收到ACK报文之后就会进入到CLOSE状态,服务端完成连接的关闭,然后客户端在经过2MSL的事件后也会进入CLOSE状态,客户端也完成连接的关闭。
注意:只有主动关闭连接的一方才会有TIME-WAIT状态。
为什么2MSL的等待时间
MSL:报文最大生存时间,TCP报文是基于IP协议的,IP头当中有一个TTL字段,是经过的最大路有数,,没经过一次路由器就会-1,直到TTL为0的时候将数据报文丢弃,同时发送ICMP报文同志原主机。
MSL的单位是时间,TTL的单位是路由转发次数,,所以MSL应该大于等于TTL消耗为0的时间,保证报文已经死亡。
TTL的值一般是64,Linux将MSL的默认值设置为30秒,意味着数据经过64个路由器的时间不能超过30秒,如果超过就默认报文已经消失了。
所以我们报文一来一回最长的时间就是2MSL,如果过了两个2MSL的时间没有收到报文,即使报文存在也会消失在网络当中,从而保证所有的报文通信消失在断开链接,没有任何报文遗留。
服务器为什么会同时突然出现大量的TIME-WAIT状态
当tcp连接断开的最后时候会出现TIME-WAIT状态,所以如果服务器同时出现了大量的TIME-WAIT状态的tcp连接,说明服务器突然自己主动断开了很多的TCP连接,因为只有主动断开连接的一方才会出现TIME-WAIT状态。
1.HTTP没有使用长连接 (http1.0默认短连接)
2.HTTP长连接超时 (如果60秒内都没有任何的http请求的发送就会断开连接)
3.HTTP长连接的请求数量达到上限 (对一些QPS很高的场景,因为http长连接的默认请求数量是100,那么就会导致nginx频繁的关闭连接)
保活机制
为了防止客户端故障还一直占用连接资源搞了一种保活机制来预防。
如果在一段短时间内没有任何的http请求来往,就会出发TCP保活机制,每隔一段时间发送一个探测报文,没有正常回应就会断开连接。
如果当服务端崩溃的时候服务端会自动发送FIN报文玉客户端进行4次挥手。
UDP
UDP面向无连接,随时发送数据,简单高效。
区别3: 可靠性保障
TCP是可靠的交付数据,保证数据无差错、不丢失、不重复、按顺序到达
- 确认和重传机制:TCP协议通过确认和重传机制来确保数据的无差错到达。当接收方收到数据后,会发送一个确认消息(ACK)给发送方,告知其已经成功接收数据。如果发送方在规定时间内未收到确认消息,则会重传数据,直到接收方成功接收并发送确认消息。这种机制确保了数据的无差错和可靠传输。
- 序列号和确认号:TCP协议为每个数据段分配一个唯一的序列号和确认号。序列号用于标识数据段的位置,而确认号则用于确认已接收到的数据段。通过这种方式,TCP能够检测并避免数据的丢失、重复或乱序。接收方会根据序列号将数据包重新排序,确保数据的顺序性。
- 校验和:TCP在发送和接收数据时,都会计算一个校验和,用于检测数据是否在传输过程中发生了错误或损坏。如果校验和不匹配,说明数据有误,TCP会请求重传,从而保证数据的准确性。
- 流量控制和拥塞控制:TCP使用滑动窗口协议进行流量控制,接收方会通告发送方其缓冲区大小,发送方根据此信息控制发送速率,避免接收方因处理不及时而丢失数据。同时,TCP还使用拥塞窗口进行拥塞控制,当网络拥塞时,会减小发送速率,防止因过多数据包导致网络堵塞,进一步保证了数据的可靠传输。
- 超时重传与快速重传:TCP会设置一个超时时间,若在该时间内未收到确认应答,则认为数据丢失并触发重传。此外,当发送方连续收到三个相同的确认应答时,会触发快速重传机制,不等待超时时间就立即重传丢失的数据包。
- 选择性确认(SACK):允许接收方告诉发送方哪些数据已经收到、哪些数据还未收到,这样发送方就可以只重传丢失的数据部分,提高了传输效率。
UDP是尽最大努力去交付,但是并不保证可靠交付数据。
QUIC协议
在我前面的文章http3.0的介绍当中提到了这个协议,通过这个协议可以让UDP也实现可靠传输的效果。
区别4:分片不同
- TCP 的数据大小如果大于 MSS 大小,则会在传输层进行分片,目标主机收到后,也同样在传输层组装 TCP 数据包,如果中途丢失了一个分片,只需要传输丢失的这个分片。
- UDP 的数据大小如果大于 MTU 大小,则会在 IP 层进行分片,目标主机收到后,在 IP 层组装完数据,接着再传给传输层