目录
一、TCP简介
TCP(Transmission Control Protocol),又叫传输控制协议。 TCP协议是面向连接的,可靠的,基于字节流的传输协议。在基于 TCP 进行通信时,通信双方需要先建立一个 TCP 连接,建立连接需要经过三次握手,断开连接的时候需要经过四次挥手。
对于 TCP 头部来说,以下几个字段是很重要的:
- 序列号 (Sequence number),这个序号保证了 TCP 传输的报文都是有序的,对端可以通过序号顺序的拼接报文
- 确认号 (Acknowledgement Number),这个序号表示数据接收端期望接收的下一个字节的编号是多少,同时也表示上一个序号的数据已经收到
- 窗口大小 (Window Size),表示还能接收多少字节的数据,用于流量控制
- 标识符
- ACK=1 :该字段为一表示确认号字段有效。此外,TCP 还规定在连接建立后传送的所有报文段都必须把 ACK 置为一。
- SYN=1:当SYN=1,ACK=0时,表示当前报文段是一个连接请求报文。当SYN=1,ACK=1时,表示当前报文段是一个同意建立连接的应答报文。
- FIN=1:该字段为一表示此报文段是一个释放连接的请求报文。
- URG=1 : 该字段为一表示本数据报的数据部分包含紧急信息,是一个高优先级数据报文,此时紧急指针有效。紧急数据一定位于当前数据包数据部分的最前面,紧急指针标明了紧急数据的尾部。
- PSH=1 :该字段为一表示接收端应该立即将数据 push 给应用层,而不是等到缓冲区满后再提交。
- RST=1:该字段为一表示当前 TCP 连接出现严重问题,可能需要重新建立 TCP 连接,也可以用于拒绝非法的报文段和拒绝连接请求。
二、UDP简介
UDP(User Datagram Protocol),又叫用户数据报协议。 UDP是一个无连接的、不可靠、基于数据报的传输协议。UDP只是报文(报文可以理解为一段段的数据)的搬运工,不会对报文进行任何拆分和拼装操作。
具体来说
- 在发送端,应用层将数据传递给传输层,UDP只会给数据怎加一个UDP头标识一下这是UDP,然后就传递给网络层了,不进行任何拆分。
- 在接收端,网络层将数据传递给传输层,UDP只取出IP报文头就传递给应用层,不进行任何拼装。
特点:
- 面向报文
- 不可靠传输
- 高效
应用场景: 当强调输出性能而非完整性时,如音频和多媒体的实时传输。有个视频流传输协议RTP的实时传输就是基于UDP封装而来的。
不可靠性
- UDP是无连接的,也就是说不需要建立和断开链接。
- UDP是不可靠的。它不会去备份数据,也不关心对方是否能收到数据。
- UDP没有拥塞控制,一直以恒定的速度发送数据,即使网络条件不好,也不进行速率调整。 造成的弊端就是在网络条件不好时可能导致丢包。
高效性
因为 UDP 没有 TCP 那么复杂,不需要保证数据不丢失且有序到达。所以 UDP 的头部开销小,只有八字节,相比 TCP 的至少二十字节要少得多,在传输数据报文时是很高效的。
UDP包头部包含了以下几个数据
- 两个十六位的端口号,分别为源端口(可选字段)和目标端口
- 整个数据报文的长度
- 整个数据报文的检验和(IPv4 可选字段),该字段用于发现头部信息和数据中的错误
三、TCP三次握手四次挥手
参考链接
https://blog.csdn.net/zengrenyuan/article/details/80313449
https://blog.csdn.net/qq_38950316/article/details/81087809
前置知识
位码即TCP标志位,有6种标示:
ACK(acknowledgement 确认)
PSH(push传送)
FIN(finish结束)
RST(reset重置)
URG(urgent紧急)
SYN(synchronous建立联机)
Sequence Number(顺序号码)
Acknowledge Number(确认号码)
大写单词表示标志位,值为1或0;小写单词表示序号如ack、seq
协议数据单元PDU(Protocol Data Unit)是指对等层次之间传递的数据单位。 协议数据单元(Protocol Data Unit )
物理层的 PDU是数据位(bit),
数据链路层的 PDU是数据帧(frame),
传输层的 PDU是数据段(segment),
网络层的PDU是数据包(packet),
其他更高层次的PDU是报文(message)
百度PDU
三次握手
下面贴出《计算机网络 第7版 谢仁希》原文:蓝色是自己加的
假设主机A运行的是TCP客户程序,而B运行TCP服务器程序。最初两端的TCP进程都处于CLOSED(关闭)状态。图中在主机下面的方框分别是TCP进程所处的状态。在本例中,A主动打开连接,而B被动打开连接。
一开始,B的TCP服务器进程先创建传输控制块TCB(存储了每一个连接中的一些重要信息,如TCP连接表,指向发送和接受缓存的指针,当前的发送和接受序号等),准备接受客户进程的连接请求。然后服务器进程就处于LISTEN(收听)状态,等待客户的连接请求,如有则作出响应。
A的TCP客户进程也是首先创建TCB。然后,在打算建立TCP连接时,向B发出连接请求报文段,这时首部中的同步位SYN=1,同时选择一个初始序号seq = x,TCP规定,SYN报文段(即SYN=1的报文段)不能携带数据,但要消耗掉一个序号。这时,TCP客户进程进入SYN-SENT(同步已发送)状态。(step1:客户端发送SYN报文段)
B收到连接请求报文段后,如同意建立连接,则向A发送确认。在确认报文段中应把SYN位和ACK位都置1,确认号是ack = x + 1,同时也为自己选择一个初始序号seq = y。请注意,这个报文段也不能携带数据,但同样要消耗掉一个序号。这时TCP服务器进程进入SYN-RCVD(同步收到)状态(step2:服务器发送SYN+ACK报文段)
TCP客户进程收到B的确认后,还要向B给出确认。确认报文段的ACK置1,确认号ack = y + 1,而自己的序号seq = x + 1。
至此TCP连接已经建立,A进入ESTABLISHED(已建立连接)状态。(step3:客户端发送ACK报文段)
当B收到A的确认后,也进入ESTABLISHED状态。
四次挥手
下面是自己的白话...假设客户端是A,服务器是B,FIN = FIN报文段(请求断开连接的也可以是服务器)
1.先由A向B发送一个FIN=1并带上自己的序列号seq = u,请求关闭数据传输。
(step1:客户端发送FIN报文段请求关闭数据传输)
2.当B接收到A的FIN时,向A发送一个ACK,其中ack的值等于seq = u + 1,并带上自己的序列号seq = v
……服务器会看是否还有东西需要传输
(step2:服务器回一个ACK)
3.若B没有要传输的数据了,则B向A发送一个FIN=1,ACK=1, ack = u + 1,seq = w,然后等待客户端的最终确认。
(step3:服务器确认没有要传输的数据了,再回一个FIN+ACK)
4.当A收到B的FIN时,回复一个ACK给B。其中ack的值等于seq = w + 1,然后,B就进入了TIME-WAIT(时间等待)状态。
(step4:客户端回一个ACK)
注意此时TCP连接还没有释放,必须经过2MSL(最长报文段寿命)的时间后,才进入CLOSED状态。
服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
Maximum segment Lifetime:最长报文段寿命,RFC 793建议设为2分钟。TCP允许不同的实现可根据具体情况使用更小的MSL值。
为什么必须等待2MSL?
答:保证A发送的最后一个ACK报文段能够到达B。
这个ACK报文段有可能丢失,从而使处在LAST-ACK状态的B收不到对已发送的FIN+ACK报文段的确认。B会超时重传这个FIN+ACK报文段,而A就能在2MSL时间内收到这个重传的FIN+ACK报文段。接着A重传一次确认,重新启动2MSL计时器。直到A和B都正常进入CLOSED状态。
如果没有TIME-WAIT状态,B收不到确认就无法按正常步骤进入CLOSED状态。
除了等待计时器外,TCP还设有一个保活计时器(keepalive timer)。设想有这样的情况:客户已主动与服务器建立了TCP连接,但后来客户端的主机突然出现故障。显然,服务器之后就不能再收到客户发来的数据。因此就需要措施使服务器不白等。
服务器每收到一次客户的数据,就重置保活计时器,时间的设置一般是两小时。若两小时没有收到客户的数据,服务器就发送一个探测报文段,以后每隔75秒发送一次,若一连发送10个探测报文段后仍无客户的响应,服务器就认为客户端出现了故障,接着就关闭这个连接。
四、TCP拥塞控制概述
cwnd:由发送方维持的一个叫拥塞窗口的状态变量
TCP进行拥塞控制的算法有四种:慢开始(slow-start)、拥塞避免(congestion avoidance)、快重传(fast retransmit)、快恢复(fast recovery)
1.慢开始:为了防止拥塞窗口cwnd的增长引起网络阻塞,还需要另外一个变量------慢开始门限ssthresh.
i.当cwnd < ssthresh时,使用上述慢开始算法;
ii.当cwnd >ssthresh时,停止使用慢开始算法,改用拥塞避免算法;
iii.当cwnd = ssthresh时,既可使用慢开始算法,也可使用拥塞避免算法
慢开始的“慢”并不是指cwnd的增长速率慢,而是指在TCP开始发送报文段时先设置cwnd = 1, 试探一下网络,然后再逐渐增大cwnd(每轮加倍)
2.拥塞避免:思路是使cwnd缓慢地增大,每经过一个往返时间RTT就把发送方的cwnd加1,因此有“加法增大”的特点。
3.快重传:快重传可以让发送方尽早知道发生了个别报文段的丢失
快重传算法要求接收方立即发送确认,而不是等自己发送数据时才捎带确认(注意理解这句话)
即使收到了失序的报文段也要立即发出对已收到的报文段的重复确认。
比如图中确认了M1和M2后,M3没有收到却收到了M4,按照快重传算法,接收方必须立即发送对M2的重复确认,以便让发送方及早知道接收方没有收到报文段M3。发送方接着发送M5和M6,接收方收到后仍然要分别发出对M2的重复确认。这样,发送方共收到了接收方的4个对M2的确认,其中后3个都是重复确认。快重传算法规定,发送方只要一连收到3个重复确认,就应当立即重传(即“快重传”),这样就不会出现超时,发送方也不就不会误认为出现了网络拥塞。
4.快恢复:Reno版本
当发送发连续接收到三个重复确认时,就执行“乘法减小”算法,把慢启动开始门限(ssthresh)和cwnd减半,然后执行拥塞避免算法,使拥塞窗口缓慢增大。
之前的Tahoe版本是又从慢开始算法开始,已经废弃。
再附上一段rwnd的介绍:对端通告的接收窗口,用于流量控制;
五、面试题整理
【问题1】为什么连接的时候是三次握手,关闭的时候却是四次挥手?
答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步挥手。
【问题2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出最后的ACK回复,但该ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。
【问题3】为什么不能用两次握手进行连接?
答:3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。
现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S 是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分 组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。
【问题4】如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75分钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。