TCP/IP协议
在TCP/IP的四层模型中,IP协议对应网际互联层,是负责利用IP地址来定位到网络中的主机位置,相当于快递地址。而TCP协议对应着模型中的传输层,是用来定义数据在网络中的传输规则的。
在传输层中有两种常用的协议,TCP(Transmission Control Protocol 传输控制协议)和UDP(User Data Protocol 用户数据报协议)。其中TCP协议是面向连接的,需要通过三次握手来建立发送端和接收端的连接,数据传输可靠。而UDP是面向无连接的,在得到目标主机地址后就直接传送数据,因此数据有可能出现丢失,但是传输速率较TCP要高。这两种协议的应用场景为:TCP适用于对可靠性要求高,传输大量数据,传输速度要求没这么高的场景;UDP适用于对可靠性要求不高,允许出现部分数据丢失,传输数据量较少的场景下。QQ使用的就是UDP协议。
然后重点说一下TCP协议的三次握手和四次挥手过程。
首先理解TCP中的两个序号和三个标志位的含义:
1.seq:(sequence number)数据的序号,TCP传输的每一个字节都有一个序号,这个序号可以用于检测数据传输的完整性。
2.ack:(acknowledgement number)确认号,接受端用它来表明已经接受到的数据的序号(ack值-1),而ack本身的值代表的是它希望下一次接受的数据的起始序号。即通过ack的值可以知道接受端成功接受到了哪个序号的数据,这样在发送端接收到另一端传过来的ack值时,就可以知道上一次传过去的数据有没有完整接收到。
3.ACK:确认位,只有在ACK=1的时候,ack才有用。正常进行通信的时候ACK=1,而第一次握手的时候因为没有需要确认接收的数据,所以ACK=0。
4.SYN:同步位,在建立连接的时候用于同步序号。因为在前两次握手的时候之前并没有接收过数据,所以ack的值设置不了。SYN就是用来告诉接收端现在还是在同步阶段,因此ack的值可以直接设置为接收到的seq+1。
5.FIN:终止位,用来在数据传输完成后释放连接。
三次握手示意图(摘自《看透springMvc源代码分析与实践》图中有点错误已改正)
说一下三次握手的过程:
第一次握手:发送端向服务端发起一次请求,带有的标识为,ACK=0(ack不起作用,不需要设置),SYN=1(此时处于同步状态,在服务端收到客户端发过来的SYN=1,所以服务端下次发送的ack的值就设置为接收到的seq+1,即ack=x+1),seq=x(用于服务端发起第二次握手时设置ack的值),此时客户端处于SYN_SENT状态,等待服务端确认。
第二次握手:服务端接收到第一次握手传过来的数据,根据SYN=1,设置ack=seq+1,即ack=x+1,同时也要设置SYN=1,因此服务端接收到了客户端的数据,也返回了ack=x+1给客户端用于证明自己接收到了客户端的数据,但是还不知道客户端能不能接收到它返回去的数据,所以它也需要确认,因此跟第一次握手一样,服务端也设置了一个seq的值y,用于验证客户端能否接收到数据,同时为了ack起作用,ACK=1。此时服务端处于SYN_RECV状态,等待客户端的确认。
第三次握手:客户端接收到第二次握手服务端发送过来的数据,通过检查到ack=x+1,证明服务端是接收到了它第一次发送过去的数据,不然服务端怎么知道要请求x+1的数据。同时服务端发送了一个seq=y过来,所以为了证明自己接收到了,就要将ack设置成ack=y+1。然后向服务端发送数据,服务端在接收到ack=y+1后就知道上次服务端发送过去的SYN包客户端接收到了,不然客户端怎么知道回复一个ack=y+1。
至此,两端都经过了确认,知道对方能接收到自己的数据,两端的状态都变为ESTABLISHED,然后就可以进行数据传输了。
接下来就是四次挥手的过程:
四次挥手示意图(摘自《看透springMvc源代码分析与实践》)
当有一端传输完毕后想关闭连接,就会发送FIN包,告诉对方自己已经发完了,然后主动关闭端进入FIN_WAIT_1状态,等待对方的FIN包。
而被动关闭一方在接收到主动关闭方的FIN包后,回复一个确认包(ACK包)给主动关闭端,说明它收到了主动端的关闭请求,但是此时被动关闭端可能还有数据需要发送过去,因此没有马上发送FIN包过去。此时被动端进入CLOSE_WAIT状态,这时处理的看看是否还有数据待发送,有则继续发送完成。
主动端在接受到被动端的ACK包后进入FIN_WAIT_2状态,继续等待被动方发送FIN报文。
当被动端确定所有数据发送完毕后,就会发送一个FIN包给主动端,然后进入LAST_ACK状态,等待主动端确认包。
主动端在接收到了被动端的FIN包后,就知道被动端也准备关闭连接了,就会发送一个ACK包给被动端,告诉它我收到你的关闭请求了,我不会再等你的FIN包了,可以关闭了。但是这时主动端也不会马上关闭,而是进入TIME_WAIT状态,等待2MSL(2个最报文段生存时间)再关闭。因为有可能最后这个ACK包丢失了,这样被动端就会以为主动端没有接收到它发的FIN包,就会再次发送FIN包。而如果主动端在等待的时间内没有再次接收到被动端发送的FIN包,就证明被动端接收到了最后一个ACK包,进入CLOSED状态,已经关闭连接了。这是主动端也由TIME_WAIT状态变成CLOSED状态。至此,两端都已经关闭了连接。