TCP协议2
序列号
前文提到TCP为发送的每个字节数据分配一个序列号,主要有以下几个目的:
- 帮助唯一识别每个字节数据
- 帮助把数据分割到TCP报文段中以及后续重组
- 帮助追踪发送和接受了多少数据
- 帮助将接收的错误顺序的数据正确排序
- 帮助请求丢失的数据
虽然TCP序列号是32位,但是不代表TCP只能传送bytes=4GB数据。假设序列号初始化为,则用到时再从0开始用到。然后TCP可以继续从继续之前的操作(warp around)。完成一轮的时间 称为warp around time。假设网络带宽是 bytes/sec。 因为TCP报文段的寿命是3分钟,序列号循环一轮的时间远大于这个时间,所以在同一个时间所有字节数据的序列号肯定是唯一的。
还可以将warp around time降到一个TCP报文段的寿命长短。这是由于一个TCP报文段结束之后,那么这个报文段使用的序列号将可以重用。假设带宽1GBps,所以180s内传送的数据有bytes,如果要将每一个数据都分配一个唯一序列号,且这些序列号为一轮。假设需要 bit 序列号,则,解得。但是TCP首部的序列号字段才32位,所以剩下的6位可以放在选项字段里。
三次握手
- 假设客户端想和服务器进行TCP连接,首先客户端发起连接请求报文段(只有首部没有数据),这个报文段里包含了:
- 客户端使用的初始的序列号
- SYN置1
- 最大报文段大小(Maximum Segment Size, MSS)。客户端发送和接收的最大数据块大小,包含在选项字段里(首部没有相应字段)
- 接收窗口大小,指明可以接受的未确认数据的大小。
- 服务器在接收到请求连接的报文段之后,发送响应报文段给客户端,将服务器的一些参数告诉客户端。响应报文段里包含:
- 服务器使用的初始的序列号
- SYN置1
- 确认序列号,将接收到的序列号+1作为确认序列号,指明下次服务器想接收的字节数据的序列号
- ACK置1
- MSS
- 接收窗口大小
- 客户端发送一个纯确认报文段。(实际情况可能是直接发送数据并捎带ACK)
除了第一步,所有的TCP报文段的ACK都是置1的。第一步和第二步建立了客户端这一边的参数,第二步和第三步建立了服务器这一边的参数。连接的建立对于双方来说,都消耗了一个序列号。纯确认报文段不消耗序列号,除非是数据捎带ACK。纯确认不是必须的,连接在于双方的参数建立,第一步和第二步已经告知对方自己的参数信息了,若第三步客户端直接按照服务器给的参数发送数据,实际也是确认了服务器的参数,所以实际经常是数据捎带ACK。在双方三次握手建立连接过程中,协商的参数包括MSS、接受窗口大小和一些定时器信息等。
MSS只需要告诉对方一次即可,所以没有在首部中专门设计一个字段。对方也不一定采纳你的MSS,若是发送方发送报文段的大小大于接收方MSS,则接收方会分段,造成额外开销。一般来说,如果没有发生分段,MSS还是越大越好(也不总是这样)。
四次挥手
由于TCO连接时全双工的,因此每个方向都必须单独进行关闭。当一方完成数据传输时发送一个FIN来终止这个方向的连接,另一端收到FIN时必须通知应用层对方已经终止了那个方向的数据传输。
- 假设客户端要终止连接。客户端发送一个TCP报文段,将FIN置1。然后等待服务器的确认。
- 服务器收到报文段之后,发送该报文段的确认给客户端。客户端接收到确认之后,客户端到服务器的连接被关闭,服务器释放对应的缓冲等,客户端将不能再发送数据到服务器上,但是仍然可以发送纯确认段。服务器到客户端不受影响。
- 假设服务器要关闭连接,向客户端发送FIN置1的报文段,等待客户端确认。(可以将FIN置1的报文段和前面的ACK报文段合在一起发送)
- 客户端收到FIN报文段后,释放它的缓存,发送ACK确认报文段给服务器(不是强制的)。此时客户端也许还没有完全结束,如果ACK报文段丢失会继续重发,这取决于实现。经过一段等待后,完全关闭。
参考
[1] TCP/IP详解卷1
[2] GateVidyalay