TCP
TCP是Internet传输层的面向连接的可靠的运输协议。TCP定义在RFC 793、RFC 1122、RFC 2018、RFC 5681、RFC 7323中。
TCP 连接
基本介绍
TCP是connection-oriented,一个应用进程可以开始向另一个应用进程发送数据之前,这两个进程必须先相互“握手”,先相互发送某些预备报文段来建立确保数据传输的参数。
TCP连接是一条逻辑连接,其共同状态仅保留在两个通信端系统的TCP程序中。中间路由器只能看到数据报文,看不到连接。
TCP连接提供的是full-duplex service:两个应用进程之间如果有TCP连接,那么应用层数据可以双向流通。
TCP连接是point-to-point,单个发送单个接收。
TCP连接的建立
发起连接的进程称为客户进程,另一个进程称为服务器进程。
客户应用进程通知客户传输层,想与服务器上的一个进程建立TCP连接,这一过程中一个Python客户程序发出命令
1
clientSocket.connect((serverName, serverPort))
其中serverName 是服务器名字,serverPort标识服务器上进程
客户发送一个特殊的TCP报文段,服务器用另一个特殊的TCP报文段来响应
- 客户用第三个特殊报文段想响应
前两个报文段不包含应用层数据,没有“有效载荷”,第三个报文段可以承载“有效载荷”,这个连接建立过程被称为三次握手。
数据传输过程
客户进程通过套接字传递数据流,TCP将数据引导到发送缓存里,发送缓存在发起三次握手期间设置,接下来TCP不时从发送缓存里取一段数据传递到网络层。
每次取出并放入报文段的数据数量取决于最大报文段长度(MMS),MMS 通常由本地发送主机发送的最大链路层帧长度(MTU)来设置。TCP报文段+TCP/IP首部长度(40Bytes)适合单层链路层帧。
- TCP为每块数据配上一个TCP首部,形成多个TCP报文段
- 报文段下传给网络层,网络层将其分别封装在网络层IP数据报中
- IP数据发送到网络中
- TCP接受到一个报文段,数据放入TCP连接的接收缓存中
TCP报文段结构
TCP报文段由报文段首部(Header)和数据部分(Data)两部分组成。
报文段首部
源端口号(Source Port):16bits,发送方应用程序的端口号
目标端口号(Destination Port):16bits,目标应用程序的端口号
序号(Sequence Number):32bits,数据流的序号,帮助分割后发送的数据进行重组
确认号(Ackknowledgment Number):32bits,确认已经接收的数据
TCP只确认第一个丢失字节为止的字节,称为累计确认(cumulative acknowledgment)
首部长度(Header Length):4bits,表示首部长度,单位为4字节(通常选项部分为空,所以典型长度是20字节)
保留(Reserved):4bits,保留字段,暂未使用,val为0
控制位(Flags):8bits(实际使用中,不使用用PSH、URG,所以是4+6+6)
- CWR:明确拥塞通告
- ECE:明确拥塞通告
- URG:指示报文段存在被发送端的上层实体置为紧急的数据
- ACK:确认字段是否有效
- PSH:PSH被置位表示接收方应立即将数据传输给上层
- RST:用于TCP连接建立和拆除
- SYN:用于TCP连接建立和拆除
- FIN:用于TCP连接建立和拆除
- 接收窗口(Window Size):16bits,接收方可接收的数据窗口大小
- 校验和(Checksum):16bits,检测TCP报文段是否在传输过程中损坏
- 紧急指针(Urgnet Pointer):16bits,指向紧急数据的位置
- 选项(Options):可变,发送方和接收方协商MSS时或者窗口调节因子使用,时间戳
数据部分
TCP报文段的实际有效负载,大小受MSS限制,通常不超过IP数据包的MTU减去IP首部和TCP首部长度
RTT 估计和超时
TCP采用超时/重传机制来处理报文段的丢失问题。超时间隔长度需要大于RTT。
估计RTT
通过这样一个迭代算法,当前的RTT占更大比重,形成一个RTT的光滑曲线。这种平均叫做指数加权移动平均(EWMA)。
设置和管理重传超时间隔
,其中DevRTT是RTT偏差,同样通过一个EWMA计算。可靠数据传输
TCP在IP的不可靠服务上创建了一种可靠数据传输服务(reliable data transfer service),它确保一个进程从其接收缓存中读取出的数据流是无损坏、无间隙、非冗余、按序的数据流。
如何完成这样一个机制?
一个基本的想法是:发送方传递一个报文段给IP的时候,启动计时器,然后等待接收方传输的报文段ACK,如果TimeoutInterval之后还没有接收到,那就重新发送。
这个基本的想法当然可以做到无损坏(每一段报文段都确认收到)、无间隙。但是显然没办法做到非冗余和按序。那么为了达到我们的目标,我们需要加入一些新的机制。
超时间隔加倍
思考一下冗余是如何产生的,发送方发送了报文段A,但是由于网络比较拥塞或者其他原因,接收方接收到了A,但是发送确认报文段给发送方时,发送方还没有收到就超时了,这时发送方就会再发送一个同样的报文段。
那么有一个很简单粗暴的办法,既然是由于网络拥塞或者各种原因导致时间长导致的,那就每一次连续超时就把TimeoutInterval加倍,不停加倍之后来减少冗余,然后在网络恢复畅通之后再用来继续计算TimeoutInterval。
这样的方法显然不足以完全解决问题,因为超时周期可能会变到一个很长的水平,如果是真的丢失了某一个报文段的话,单纯使用这种方法会显著地增加端到端时延。所以引入了下面一个机制:
快速重传
定义一个冗余ACK的概念,意思是接收方反复发送报文段要求确认同一个ACK。
接收方如果
- 接收到具有期望序号的报文段,而且期望序号及以前的数据都已经被确认,那么延迟ACK,等待一个时间之后,如果没有下一个报文段到达,那么发送一个ACK
- 接收到具有期望序号的报文段,另一个按序报文段在等待ACK传输,那么立即发送累积ACK来确认两个报文段
- 接收到一个比期望大的失序报文段,说明出现间隔,发送冗余ACK,指示期望序号
- 接收到能填充接收数据间隔的报文段,如果处于间隔低端,那么立即发送ACK指示下一个期望序号
完成这个机制之后,我们就基本做到了要求,但是为了保险起见,又引入TCP差错恢复机制。
差错恢复机制
对TCP提出的一种修改意见是选择确认。允许TCP接收方来选择确认时序报文段,将这种机制和选择重传机制结合,让TCP看起来更像是SR协议。
流量控制
TCP为它的应用程序提供流量控制服务(flow-control service),来防止接收方的接收缓存溢出。
同时为了让发送方合理的发送数据,而不是大量无意义的重传,需要对发送方进行拥塞控制(congestion control)。
TCP让发送方维护一个接收窗口(receive window)来实现流量控制。
用一个最朴素的式子来表示:
接收方在发回的报文中不停的在接收窗口部分更新自己的缓存剩余容量即可。
TCP 连接管理
TCP连接的建立
TCP连接的建立采用三次握手的模式:
- 客户端的TCP向服务端TCP发送一个特殊的TCP报文段。这个报文段不包括应用层数据,报文段首部的SYN位为1,称为SYN报文段。SYN报文段的序号是由客户端随机生成的client_isn。
- 服务器收到包含SYN报文段的IP数据报,为该TCP连接分配TCP缓存和变量,然后向客户端发送允许连接的报文段,这个报文段同样不包含应用层数据,SYN位被置为1,确认号为client_isn+1,序号是有服务器选择的初始序号server_isn,称为SYNACK报文段。
- 在收到SYNACK报文段后,客户端分配TCP缓存和变量。客户向服务端再次发送一个报文段,报文段对SYNACK报文段进行确认,SYN置0。
在三次握手完成后就可以开始相互传输数据。
TCP连接的拆除
在TCP连接中,两个进程中的任何一个都可以终止该连接。连接关闭的过程如下:
- 客户进程发送一个关闭连接命令。
- 客户TCP接收到命令后,向服务器发送一个特殊的报文段,FIN置1。
- 服务器接收到特殊报文段之后,向客户端发送一个确认报文段。
- 服务器向客户端再发送一个终止报文段,FIN置1。
- 客户端对服务器发送的终止报文段进行确认。此时TCP缓存和变量都已经被释放。