tcp报文
报文结构
- 4字节端口号 16位源端口号,16位目的端口号
- 4字节序列号,即seq
- 4字节ACK确认序列号,即ACK
- 4位数据偏移(也叫首部长度),代表了TCP报文头部的长度,即数据从哪里开始,每一位的单位是32位,而4位(1111)转化成10进制为15,即15个32位,即15*4字节,即报文头部长度最多位60字节。
12位标志位
- URG紧急标志位 配合紧急指针使用
- ACK确认标志位
- PSH推送标志位 即希望对方立刻向上交付,而不是等待这一次的数据全部缓存完再一起交付
- RST复位标志位 表示tcp连接出现很重大的错误,需要释放再重新进行连接,或者用来拒绝非法报文或打开一个连接
- SYN同步标志位 SYN=1就表示这是一个连接请求或连接接受报文。
FIN结束标志位
加上上面的数据偏移一共2个字节
2字节窗口,表示己方期望接收的数据大小,从ACK确认序列开始计算,假如发送的报文中确认序列号为2000,窗口为2000,即期望接收seq为2000-3999的报文,大小在2000以内。
- 2字节校验和
- 2字节紧急指针,仅在URG=1时才有意义,如果有紧急数据,就会排在这一次发送的数据的前面,紧急指针指出紧急数据在数据中的位置,在紧急数据处理完时,就恢复正常操作顺序
- 选项,长度可变,最大为40字节
Seq(sequence number序列号)
用来标定tcp数据包的顺序,使得资源可以按正确的顺序还原,如果发生丢包,可以知道是哪一个包丢了。
第一个包的seq是随机数(假设第一个是0),因为一个包的大小大概为1400字节左右(先假设是1400字节),那么下一个包的编号就是1400,每一个包中都有两个seq,一个是自己的编号,一个是下一个包的编号,这样就可以按正确的顺序排列包。
ACK(acknowledge确认)与发送速率
由于不同的线路的网络状况不同,因此发送的速率要求就不同,发送速率过快会导致丢包更容易发生,因此tcp协议规定了慢启动机制,一开始发送的较慢,在发送的过程中确认对方的接收状态,然后调整发送的速率。一般一开始发送速率是10个包,每收到两个tcp数据包,接收方就会发回一个确认报文。
ACK确认报文包含两个内容
- 期待下一个收到的包的编号
- 接收方的接收窗口的容量大小(就是能接收多少个包)
举个例子
- 左边发送的包中,随机生成了seq为1的包,大小为100字节,并且期待对方发回一个编号为1的包(ACK)
- 对方接收到之后,发回一个编号为1的包,大小为200字节,并且期待收到一个101编号的包(因为上一个大小是100,1+100)
- 左边收到右边的包后,发送一个101号的包过去,并且期待收到一个201编号的包(上一个是1,大小为200,1+200)
- 对方收到后,发回201编号的包,大小为50,并且期待收到一个151编号的包
可以得到的信息
- 因为tcp的发送和接收都是双向的,因此seq、ack的信息也是双向的,通过seq标定己方发送的包,通过ack来标定期待对方发送的包
接收方每一次收到期待的包,都会更新下一次发送包的ack信息,比如接收方如果现在ack信息为101,表示期待收到101的包,如果发送方发送了101编号的包,并且下一个编号为201,那么接收方的ack信息就会更新成201
丢包的处理
接上方,如果接收方收到了101号包,就会更新ack信息为201,如果没有收到101号包,那么下一次发送包的ack信息就还是101号,tcp协议规定,如果连续收到三个相同ack(基于重复累计确认的重传),或者超时了还没有收到任何ack(超时重传),那么就会重新发送ack信息中的包,这就保证了,丢包后数据的重新发送。
具体过程
上面列举了最重要的seq和ack的工作原理,来看一下具体的建立的过程。
建立连接
三次握手
第一次握手 客户端(C)向服务端(S)第一次发送连接请求,会发送一个携带连接信息的报文SYN设置为1,随机生成一个seq=x,这个时候C进入SYN-SENT(同步已发送)状态
注意:第一次握手的报文是不携带数据的,因为tcp规定SYN为1的报文不能携带数据。
- 第二次握手 S收到C的请求连接的报文后,会返回一个用来确认的报文,这个报文的SYN、ACK标志位都为1(因此也不能携带数据),并且随机生成一个seq=y,ack确认序列为第一次握手的报文的x+1,这时S进入SYN-RCVD(同步已接收)状态
第三次握手 C收到S的确认报文后进入ESTABLISHED(已建立连接)状态,会再次返回一个确认报文,这个报文的ACK标志位为1,ack确认序列为y+1,这时已经可以携带数据,如果携带数据,那么这个报文的seq为x+1,当然也可以不携带数据,这个时候不会消耗序列号,因此下一个报文的seq为x+1。S收到这个确认报文后也进入ESTABLISHED(已建立连接)状态
注意:上述行为是tcp规定的,ACK确认报文可以携带数据,也可以不携带数据,不携带数据的时候,就不会消耗序列号。
为什么需要三次握手?为了确认双方的接收、发送数据的功能正常
为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。例如,第一次建立连接的报文在某个网络节点停滞了,从而S没有收到这个请求连接的报文,也就不会发回确认报文,超过一定时长C会重发请求连接的报文,而第二个报文没有停滞,假如不使用三次握手(即S返回确认报文就建立连接)这时C和S就已经建立连接了。如果在这次连接结束后,第一个请求连接的报文又传到了S,S会认为连接建立,一直等待C的数据,而C认为没有发送过请求报文,不予理睬,会导致S一直等待。而使用三次握手,需要C再次返回确认报文,这样S就不会自认为建立了连接了。
关闭连接
四次挥手
由于tcp是全双工通信的,即每一方都同时具有发送和接收的能力,因此每一方都要告知对方自己已经发送完毕,并且返回确认报文
假如C先发送完毕第一次挥手 C发送一个FIN(finish)标志位为1的报文,seq为x,告诉S我已经发送完了,并进入FIN_WAIT_1(中止等待1)状态
- 第二次挥手 S发送一个确认报文,ACK标志位为1,ack确认序列为x+1,seq为y,告诉C我知道你发送完了,并进入CLOSE_WAIT(等待关闭)状态,客户端收到报文,进入FIN_WAIN_2(中止等待2)状态
- 第三次挥手 假如现在S也接收完了数据,会发送一个FIN标志位为1的报文,ACK标志位为1,seq为w,ack确认序列为x+1,进入LAST_ACK(最后确认)状态
- 第四次握手 C接收到S发来的FIN报文,发送一个确认报文,ACK标志位为1,seq为x+1,ack确认序列为w+1,并且进入TIME_WAIT(时间等待)状态,S接收到这个确认报文,进入CLOSED(已关闭)状态,经过2MSL时间后,C进入CLOSED(已关闭)状态
注意:从 TIME_WAIT 进入 CLOSED 需要经过 2MSL,其中 MSL 就叫做 最长报文段寿命(Maxinum Segment Lifetime),根据 RFC 793 建议该值这是为 2 分钟,也就是说需要经过 4 分钟,才进入 CLOSED 状态。