一. TCP协议
TCP(Transmission Control Protocol)传输控制协议,是TCP/IP协议族中最重要的协议之一,主要工作在运输层,和UDP不同,TCP提供面向流服务面向连接的可靠传输服务,虽然是面向字节流的,但TCP的传输单元却是报文段,一个报文分为首部信息和有效数据信息两部分,其中的首部信息才是在传输过程中起到至关重要的作用;
-------------------------------------------------------------------------------------------
二. TCP报文的首部格式
源端口号:标识着发送端是从上一层应用层的哪一个应用程序端口接收下来的信息,是16位大小;
目的端口号:标识着要上交给目的主机上应用层的哪一个应用进程,为16位;和MAC帧协议一样其中有帧 类型标识表明要上交给上一层的哪一方,IP数据报中有协议类型决定上交的对象是UDP还是 TCP类型;
32位序号:在一个TCP连接中传送的字节流中的每一个字节都按照顺序编号的,一个字节占用一个序号, 因此首部信息中的序号表示本报文中所发送的数据的第一个字节的序号;
32位的确认序号:表示希望收到对方下一个报文的第一个数据字节的序号,也表示在确认序号之前发送 方发送的序号标识的字节已全部收到;
数据偏移:4位,表示报文中首部所占用的长度,也就是从报文开始部分往后偏移多少为有效数据的开 始,也是数据距离报文首部有多远;
保留:为以后保留空余以供使用,目前置为0;
URG(urgent):紧急字段,当被置为1时,表明后面的紧急指针有效;
ACK(acknowlegment):确认字段,当被置为1时,前面的确认序号才有效;
PSH(push):推送字段,当置为1时,用于及时将收到的报文向上交付处理;
RST(reset):复位字段,当置为1时,表中TCP链接出现严重差错,不许释放连接然后再重新建立;
SYN(synchronization):同步字段,当被置为1时,表明这是一个连接请求报文;
FIN(finish):终止字段,当被置为1时,表示要求释放运输连接;
窗口:指的是自身能接收的最大数据量,是发送本报文段的接收窗口,而不是发送窗口;
检验和:进行差错检验,占2个字节;
紧急指针:当URG=1时才有效,它指出报文中的紧急数据的字节数,优先处理紧急数据;
选项:长度可变,最长可达40字节;
填充:当报文低于46个字节时用来填充;
-------------------------------------------------------------------------------------------
三. TCP连接建立过程(三次握手)
TCP连接采用客户服务器方式,主动发起连接请求的一方为客户端进程,接收连接请求的一方为服务器进程;
- 首先,客户端和服务器都处于CLOSED(关闭)状态,客户端主动请求连接;
- 服务器进程会先创建出一个传输控制块TCB,准备接收客户端的请求,然后服务器进程就会一直处于LISTEN(监听)状态,随时等待客户端发送请求;
- 客户端进程也会先创建出一个传输控制块TCB,向服务器端发送连接请求报文,这时报文中的SYN字段置为1,表示请求连接,并且选择一个初始序号seq置入报文首部中,比如seq = n;并且这个时候,客户端会处于SYN_SENT(同步已发送)状态;
- 当服务器端接收到客户端发送的请求连接报文的时候,若同意连接就会做出响应,向客户端发送确认报文,报文中SYN同步字段和ACK确认字段都应设置为1,并且选择自己的一个初始序号比如seq = k,同时将确认序号部分设置为ack=n+1,表示客户端下一次发送的数据序号开始处,并且表示n+1之前的数据已全部收到,这时候服务器就处于SYN_RCVD(同步并且收到)状态;
- 当客户端收到了服务器端发过来的确认建立连接的响应之后,会再次给服务器端发送确认,表明已收到服务器端同意建立连接的确认报文,这时候客户端发送的报文中,ACK确认字段置为1,seq = n+1,表示这次数据字节的开始处,同时对服务器的确认序号为ack=k+1,表示已收到服务器k+1之前的所有数据,这时候客户端就进入了ENSTABLISHED(连接已建立)状态;
- 当服务器收到客户端响应过来的确认报文的时候,也进入ENSTABLISHED(连接已建立)状态;
注意:上面的连接过程并不携带数据信息,因为连接还没有建立好,但是像SYN和ACK这样的状态字段 仍然会消耗一个序号,因此,每一次对上一次接收的报文的确认序号中,都应该加上1,表示这 个序号之前所有的序号代表的数据都已接收到;如果携带有相应大小的数据,确认序号中就应该 加上数据的大小和状态字段的1个序号;
上面过程表示为如下图:
这里要提出为什么是三次握手而不是两次四次或者五次握手:
首先,一次握手是肯定不行的,因为连接并不是一方说连接就连接;其次,如果是两次握手的话,在这种情况中每一次服务器端接到客户端发来的连接请求,只要同意连接给客户端发送了确认连接应答就算是连接好了,会出现这样的情况:如果客户端第一次请求连接的报文并没有及时到达服务器端,而是在中途被丢失或者滞留等待了,那么客户端就会第二次发送请求,当第二次成功到达服务器端之后,只要服务器端同意建立连接,那么当客户端收到服务器端发送的确认响应之后连接就建立好了,那么如果数据传输完毕连接释放之后,客户端第一次的请求才到达服务器端,那么服务器端就又会同意建立连接,此时又有一条连接被建立了,但是实际上客户端并没有再次需要建立连接,那么也就不会传输数据,这样的话,一条连接就会被白白浪费了,如果有多种这样的情况,就会浪费很多资源;最后,如果是四次五次握手根本就没有必要,这样就相当于蓝军白军的问题了,总有最后的依次确认应答是不可靠的;
因此,三次握手是最佳的次数,当客户端请求连接,如果服务器端同意连接,就发送确认连接应答,这时候客户端接收到再次回复给服务器端一个确认应答,表明收到了同意建立连接的报文,这样双方就可以建立连接了;
-------------------------------------------------------------------------------------------
四. TCP连接断开过程(四次挥手)
连接释放的过程要比连接建立的过程稍微复杂了一些,仍然需要报文首部中状态字段和双方状态的变化来描述:
- 当数据传输结束后,双方都可以释放连接,这时候客户端先向服务器端发起一个释放连接的报文,其中FIN字段设置为1,序号为上一次自己发送数据的最后一个字节的序号加1,(TCP规定,即使FIN报文并不携带数据也会消耗掉一个序号)比如为seq = x,因为要断开连接所以并不会携带有效数据,这时候客户端就会进入FIN_WAIT_1(终止等待1)的状态,等待服务器端的确认;
- 服务器端接收到客户端发来的断开连接报文时会做出回应,回应一个确认报文,ACK字段置为1,序号为自身前一个发送报文最后一个字节序号加1,比如seq = y,确认序号为ack = x+1,然后服务器端就会进入一个CLOSE_WAIT(关闭等待)的状态,此时客户端到服务器端的连接就断开了,但如果服务器端的数据还没有处理完,仍然可以给客户端发送数据,客户端仍然会接收数据,因为只有一方断开了连接;
- 当客户端接收到服务器端发送的确认断开连接的回应后,就会进入FIN_WAIT_2(终止等待2)的状态,因为只是一方断开了连接,还要等待服务器端处理完毕数据之后再发送的请求断开连接的报文才能真正断开连接;
- 当服务器端将所有数据处理完毕之后,就会向客户端发送一个请求断开连接的报文,报文中FIN字段设置为1,确认序号假如为seq = i,因为在半关闭状态可能服务器端又向客户端发送了一些数据,这时候服务器端就会进入一个LAST_ACK(最后确认)状态,等待客户端的确认;
- 当客户端收到了服务器端发来的请求断开连接的报文时,就会回应一个确认报文,报文中ACK字段设置为1,序号seq = x+1,确认序号ack = i+1,这时候客户端就会进入到一个TIME_WAIT(时间等待)的状态;
- 当服务器端收到了客户端发过来的确认断开连接的回应报文之后,就会断开之前只有一方的连接,这时候服务器端就会进入CLOSED(关闭)的状态,同时撤销相应的传输控制块TCB;
- 当客户端超过TIME_WAIT(时间等待)的时间,就会进入CLOSED(关闭)的状态,同时撤销传输控制块TCB;
断开连接的过程可用下图表示:
这里要谈论一下TIME_WAIT(时间等待)状态:
当客户端进入到TIME_WAIT状态后,必须经过时间等待计时器设计的时间2MSL后,客户端才能够进入到CLOSED状态,而时间MSL叫做最长报文段寿命(Maximum Segment Lifetime),RFC793建议设为2分钟,TCP允许不同的而实现可根据具体情况使用更小的MSL值。因此,从客户端进入到TIME_WAIT状态后要经过4分钟的等待才能进入到CLOSED状态,才能开始建立下一次新的连接。
那么客户端为什么要等待规定的2MSL时间后才能进入CLOSED状态呢:
是为了保证客户端发送的ACK确认报文能够到达服务器端;当服务器端向客户端发送了请求断开连接的报文而没有收到响应,如果客户端并不等待时间就直接进入CLOSED状态释放了连接,那么服务器端就无法接收到客户端的确认报文,也就一直无法进入CLOSED状态了;因此,当客户端发送给服务器端的确认ACK报文丢失了时,服务器端等待超时就会重新发送一次请求断开连接的FIN报文,这样客户端在等待的2MSL时间里就会重新接收到服务器端发送的FIN请求断开报文,也就会重新发送一次ACK确认断开连接报文,这样一来,就能够保证服务器端收到了确认断开连接报文正确进入CLOSED状态了。
《完》