# TCP
和UDP
计算机网络体系结构
# TCP/IP
(传输控制协议/网际协议)
- 应用层
- 传输层
传输层(transport layer)的主要任务就是负责向两台主机进程之间的通信提供通用的数据传输服务。
UDP | TCP | |
---|---|---|
是否连接 | 无连接 | 面向连接 |
是否可靠 | 不可靠传输,不使用流量控制和拥塞控制 | 可靠传输,使用流量控制和拥塞控制 |
连接对象个数 | 支持一对一,一对多等多种方式通信 | 只能一对一 |
传输方式 | 面向报文 | 面向字节流 |
首部开销 | 小,仅8子节 | 首部最小20,最大60子节 |
场景 | 适用实时应用 | 适用可靠传输的应用,文件传输 |
TIP
TCP
和UDP
区别:TCP
提供可靠的服务。也就是说,通过TCP
连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP
尽最大努力交付,即不保证可靠交付。并且因为tcp
可靠,面向连接,不会丢失数据因此适合大数据量的交换。TCP
是面向字节流,UDP
面向报文,并且网络出现拥塞不会使得发送速率降低(因此会出现丢包,对实时的应用比如IP电话和视频会议等)。
我们知道 TCP
协议是面向连接的,其需要三次握手才可以连接连接,其还可以做到丢失重传、顺序控制、拥塞控制、流量控制等功能,并且保证连接的可靠性,而 UDP
协议不需要考虑那么多,其只需要尽快的将应用程序的报文传递给网络层便好,所以总结就是 TCP
协议主要用于那些需要可靠通信的,对连接的速度要求相对来说不那么高的情况,而 UDP
协议用在那些需要快速进行通信,不需要保证通信可靠的场合。
具体例子:
UDP
:DNS
服务(向DNS
服务器发送请求解析域名)、视频通话、QQ 聊天、多媒体广播TCP
:我们最常用的HTTP
协议,邮件,QQ 传文件,登录
UDP
的特点主要有UDP
能够支持容忍数据包丢失的带宽密集型应用程序UDP
具有低延迟的特点UDP
能够发送大量的数据包UDP
能够允许DNS
查找,DNS
是建立在UDP
之上的应用层协议。
TCP
的主要特点有TCP
能够确保连接的建立和数据包的发送TCP
支持错误重传机制TCP
支持拥塞控制,能够在网络拥堵的情况下延迟发送TCP
能够提供错误校验和,甄别有害的数据包。
# TCP
特点
TCP
提供全双工通信。TCP
允许通信双方的应用进程在任何时候都能发送数据。TCP
连接的两端都设有发送缓存和接收缓存,用来临时存放双向通信的数据。在发送时,应用程序在把数据传送给TCP
的缓存后,就可以做自己的事,而TCP
在合适的时候把数据发送出去。在接收时,TCP
把收到的数据放入缓存,上层的应用进程在合适的时候读取缓存中的数据。
TCP
连接的端点叫做套接字(IP
地址:端口号)
# 可靠传输的工作原理
- 理想传输条件特点:
- 传输信道不产生差错
- 不管发送方以多快速度发送数据,接收方总是来得及处理收到的数据。
实际的网络都不具备以上两个理想条件。但我们可以使用一些可靠传输协议,当出现差错时让发送方重传出现差错的数据,同时在接收方来不及处理收到的数据时,及时告诉发送方适当降低发送数据的速度。这样一来,本来是不可靠的传输信道就能够实现可靠传输了。
# 停止等待协议
- 可靠传输协议是这样设计的:发送方只要超过了一段时间仍然没有收到确认,就认为刚才发送的分组丢失了,因而重传前面发送过的分组。这就叫做超时重传。要实现超时重传,就要在每发送完一个分组设置一个超时计时器。如果在超时计时器到期之前收到了对方的确认,就撤销已设置的超时计时器。
# TCP的三次握手和四次挥手
一个
TCP
连接由一个4
元组构成,分别是两个IP
地址和两个端口号(源端口和目的端口号,各占2个字节)。一个TCP
连接通常分为三个阶段:连接、数据传输、退出(关闭)。通过三次握手建立一个链接,通过四次挥手来关闭一个连接。 当一个连接被建立或被终止时,交换的报文段只包含TCP
头部,而没有数据。
TCP
报文头部结构
TIP
TCP
报文段首部的前20
个字节是固定,后面有4n
字节是根据需要而增加的选项(n
是整数)。因此TCP
首部的最小长度是20
字节
- 重点字段
seq
序号:占32
位,用来标识TCP
源向目的端发送的字节流,发送方发送数据时对此进行标记- 确认序号:
ack
序号,占32
位,只有ACK
标志位为1
时,确认序号字段才有效,ack=seq+1
。(期望收到对方下一个报文段的第一个数据字节的序号) - 标志位
ACK(acknowledge)
:确认序号有效FIN(finish)
:释放一个连接PSH
:接收方应该尽快将这个报文交给应用层RST
:重置连接
当
RST = 1
时,表明TCP
连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接。RST
置1
还用来拒绝一个非法的报文段或拒绝打开一个连接。RST
也可称为重建位或重置位。SYN(synchronize)
:发起一个新连接URG
:紧急指针有效(URG
=1时)
# 三次握手
确认通信双方收发数据的能力
三次握手过程
- 第一次握手: 客户端向服务端发送请求,首先客户端随机生成一个起始序列号
ISN
(比如是100
),那客户端向服务端发送的报文段包含SYN
标志位(也就是SYN=1
,序列号seq=100
。要消耗一个序号seq
- 第二次握手: 服务端收到客户端发过来的报文后,发现
SYN=1
,知道这是一个连接请求,于是将客户端的起始序列号100
存起来,并且随机生成一个服务端的起始序列号(比如是300
)。然后给客户端回复一段报文,回复报文包含SYN
和ACK
标志(也就是SYN=1,ACK=1
)、序列号seq=300
、确认号ack=101
(客户端发过来的序列号+1
)。 - 第三次握手: 客户端收到服务端的回复后发现
ACK=1
并且ack=101
,于是知道服务端已经收到了序列号为100
的那段报文;同时发现SYN=1
,知道了服务端同意了这次连接,于是就将服务端的序列号300
给存下来。然后客户端再回复一段报文给服务端,报文包含ACK
标志位(ACK=1
)、ack=301
(服务端序列号+1
)、seq=101
(第一次握手时发送报文是占据一个序列号的,所以这次seq
就从101
开始,需要注意的是不携带数据的ACK
报文是不占据序列号的,所以后面第一次正式发送数据时seq
还是101
)。当服务端收到报文后发现ACK=1
并且ack=301
,就知道客户端收到序列号为300
的报文了,就这样客户端和服务端通过TCP
建立了连接。
# 四次挥手
四次挥手的目的是关闭一个连接
比如客户端初始化的序列号
ISA=100
,服务端初始化的序列号ISA=300
。TCP
连接成功后客户端总共发送了1000
个字节的数据,服务端在客户端发FIN
报文前总共回复了2000
个字节的数据。
- 第一次挥手: 当客户端的数据都传输完成后,客户端向服务端发出连接释放报文(当然数据没发完时也可以发送连接释放报文并停止发送数据),释放连接报文包含
FIN
标志位(FIN=1
)、序列号seq=1101
(100+1+1000
,其中的1
是建立连接时占的一个序列号)。需要注意的是客户端发出FIN
报文段后只是不能发数据了,但是还可以正常收数据;另外FIN
报文段即使不携带数据也要占据一个序列号。 - 第二次挥手: 服务端收到客户端发的
FIN
报文后给客户端回复确认报文,确认报文包含ACK
标志位(ACK=1
)、确认号ack=1102
(客户端FIN
报文序列号1101+1
)、序列号seq=2300
(300+2000
)。此时服务端处于关闭等待状态,而不是立马给客户端发FIN
报文,这个状态还要持续一段时间,因为服务端可能还有数据没发完。 - 第三次挥手: 服务端将最后数据(比如
50
个字节)发送完毕后就向客户端发出连接释放报文,报文包含FIN
和ACK
标志位(FIN=1,ACK=1
)、确认号和第二次挥手一样ack=1102
、序列号seq=2350(2300+50)
- 第四次挥手: 客户端收到服务端发的
FIN
报文后,向服务端发出确认报文,确认报文包含ACK
标志位(ACK=1
)、确认号ack=2351
、序列号seq=1102
。注意客户端发出确认报文后不是立马释放TCP
连接,而是要经过2MSL
(最长报文段寿命的2
倍时长)后才释放TCP
连接。而服务端一旦收到客户端发出的确认报文就会立马释放TCP
连接,所以服务端结束TCP
连接的时间要比客户端早一些。
TIP
SYN
报文:起标识作用的家伙。当SYN=1
而ACK=0
时,表明这是一个连接请求报文。对方若同意建立连接,则应在响应报文中使用SYN=1
和ACK=1
,因此SYN
置于1
就表示这是一个连接请求或连接接受报文。
ACK
报文:TCP
协议规定,只有ACK=1
时有效,也规定连接建立后所有发送的报文的ACK
必须为1
FIN
报文:FIN=1
表示此报文段的发送方的数据已经发送完毕,请求释放运输连接
# TCP
常见面试题
TCP
建立连接,客户端突然出现故障?
TCP
设有一个保活计时器,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2
小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75
秒钟发送一次。若一连发送10
个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
- 如果是三次挥手会有什么问题?等于说服务端将
ACK
和FIN
的发送合并为一次挥手,长时间的延迟可能会导致客户端误以为FIN
没有到达客户端,从而让客户端不断的重发FIN。