UDP
,在传送数据前不需要先建立连接,远地的主机在收到UDP
报文后也不需要给出任何确认。虽然UDP
不提供可靠交付,但是正是因为这样,省去和很多的开销,使得它的速度比较快,比如一些对实时性要求较高的服务,就常常使用的是UDP
。对应的应用层的协议主要有 DNS
,TFTP
,DHCP
,SNMP
,NFS
等。
TCP
,提供面向连接的服务,在传送数据之前必须先建立连接,数据传送完成后要释放连接。因此TCP
是一种可靠的的运输服务,但是正因为这样,不可避免的增加了许多的开销,比如确认,流量控制等。对应的应用层的协议主要有 SMTP
,TELNET
,HTTP
,FTP
等。
TCP连接的建立(三次握手)
-
TCP
服务器进程先创建传输控制块TCB
,时刻准备接受客户进程的连接请求,此时服务器就进入了LISTEN
(监听)状态; -
TCP
客户进程也是先创建传输控制块TCB
,然后向服务器发出连接请求报文,这是报文首部中的同部位SYN=1
,同时选择一个初始序列号seq=x
,此时,TCP
客户端进程进入了SYN-SENT
(同步已发送状态)状态。TCP
规定,SYN
报文段(SYN=1
的报文段)不能携带数据,但需要消耗掉一个序号。 -
TCP服务器
收到请求报文后,如果同意连接,则发出确认报文。确认报文中应该ACK=1
,SYN=1
,确认号是ack=x+1
,同时也要为自己初始化一个序列号seq=y
,此时,TCP
服务器进程进入了SYN-RCVD
(同步收到)状态。这个报文也不能携带数据,但是同样要消耗一个序号。 -
TCP
客户进程收到确认后,还要向服务器给出确认。确认报文的ACK=1
,ack=y+1
,自己的序列号seq=x+1
,此时,TCP
连接建立,客户端进入ESTABLISHED
(已建立连接)状态。TCP
规定,ACK
报文段可以携带数据,但是如果不携带数据则不消耗序号。 - 当服务器收到客户端的确认后也进入
ESTABLISHED
状态,此后双方就可以开始通信了。
为什么TCP客户端最后还要发送一次确认呢?
一句话,主要防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误。
采用三次握手是为了防止失效的连接请求报文段突然又传送到主机B
,因而产生错误。失效的连接请求报文段是指:主机A
发出的连接请求没有收到主机B
的确认,于是经过一段时间后,主机A
又重新向主机B
发送连接请求,且建立成功,顺序完成数据传输。考虑这样一种特殊情况,主机A
第一次发送的连接请求并没有丢失,而是因为网络节点导致延迟达到主机B
,主机B
以为是主机A
又发起的新连接,于是主机B
同意连接,并向主机A
发回确认,但是此时主机A
根本不会理会,主机B
就一直在等待主机A
发送数据,导致主机B
的资源浪费。
如果采用的是三次握手,就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。
TCP连接的释放(四次挥手)
- 客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,
FIN=1
,其序列号为seq=u
(等于前面已经传送过来的数据的最后一个字节的序号加1
),此时,客户端进入FIN-WAIT-1
(终止等待1
)状态。TCP
规定,FIN
报文段即使不携带数据,也要消耗一个序号。 - 服务器收到连接释放报文,发出确认报文,
ACK=1
,ack=u+1
,并且带上自己的序列号seq=v
,此时,服务端就进入了CLOSE-WAIT
(关闭等待)状态。TCP
服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态
,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT
状态持续的时间。 - 客户端收到服务器的确认请求后,此时,客户端就进入
FIN-WAIT-2
(终止等待2
)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。 - 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,
FIN=1
,ack=u+1
,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w
,此时,服务器就进入了LAST-ACK
(最后确认)状态,等待客户端的确认。 - 客户端收到服务器的连接释放报文后,必须发出确认,
ACK=1
,ack=w+1
,而自己的序列号是seq=u+1
,此时,客户端就进入了TIME-WAIT
(时间等待)状态。注意此时TCP
连接还没有释放,必须经过2∗∗*MSL
(最长报文段寿命)的时间后,当客户端撤销相应的TCB
后,才进入CLOSED
状态。 - 服务器只要收到了客户端发出的确认,立即进入
CLOSED
状态。同样,撤销TCB
后,就结束了这次的TCP
连接。可以看到,服务器结束TCP
连接的时间要比客户端早一些。
为什么客户端最后还要等待2MSL?
第一,保证客户端发送的最后一个ACK
报文能够到达服务器,因为这个ACK
报文可能丢失,站在服务器的角度看来,我已经发送了FIN+ACK
报文请求断开了,客户端还没有给我回应,应该是我发送的请求断开报文它没有收到,于是服务器又会重新发送一次,而客户端就能在这个2MSL
时间段内收到这个重传的报文,接着给出回应报文,并且会重启2MSL
计时器。
第二,防止类似与“三次握手”中提到了的“已经失效的连接请求报文段”出现在本连接中。客户端发送完最后一个确认报文后,在这个2MSL
时间中,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样新的连接中不会出现旧连接的请求报文。
为什么建立连接是三次握手,关闭连接确是四次挥手呢?
建立连接的时候, 服务器在LISTEN
状态下,收到建立连接请求的SYN
报文后,把ACK
和SYN
放在一个报文里发送给客户端。
而关闭连接时,服务器收到对方的FIN
报文时,仅仅表示对方不再发送数据了但是还能接收数据,而自己也未必全部数据都发送给对方了,所以己方可以立即关闭,也可以发送一些数据给对方后,再发送FIN
报文给对方来表示同意现在关闭连接,因此,己方ACK
和FIN
一般都会分开发送,从而导致多了一次。