2014/10/22TCP协议–kiterunner_t
http://kiterunner.com.cn/blog/tcp-protocol.html1/8
kiterunner_tTOTHEHAPPYFEW
本文描述了TCP协议,首先简单介绍了TCP完成了一些什么功能;介绍了TCP报文格式,以及
典型报文的数据格式;接着从链路控制和数据传输两个方面进行了介绍,在TCP中链路控制和
数据传输是通过同一个通道进行的,并没有区分控制通道和数据通道;在网络中传输数据(控
制或真实数据),网络可能发生拥堵,因此接下来简单描述了主机端进行拥塞控制所采取的方
法,也简单提及了中间路由器/交换机进行拥塞避免所采取的主动措施;最后介绍了在TCP中
性能分析的一些基本概念点,在开发网络应用程序的时候,需要对应用的网络需求进行一个估
计。
同时我也用perl写了一个ksock.pl(https://github.com/kiterunner-t/ksock)的小程序演示
TCP的基本功能。需要注意的是,本文所涉及到的所有测试都在Linux上进行,也主要关注
Linux中对TCP的实现。另,本文的图片主要来自《TCP/IP详解——卷一:协议》一书中的截
图。
本文不是TCP的入门资料,阅读之前需要对TCP有一些基本认识,如三次握手、四次挥手、滑
动窗口等。
1TCP概述
通常说到TCP,我们都会很自然的想到其为上层提供了一个面向连接、可靠的、端到端的数据
流服务,也通常用电话线路来类比一个TCP连接。但这种类比对刚接触到TCP的人来说极易造
成误会,我们需要仔细审视这些概念,你将会发现TCP并不是那么面向连接的、也不是那么可
靠的、数据也仍然是通过报文的方式进行传输的。实际上TCP是提供了一种“尽力而为”的数据
传输模型;同时,它也提供了防止网络拥塞的主机端拥塞控制,试图去了解整个网络的状况,
并采取合适的策略(貌似不是TCP应该干的事?)。
TCP的连接并不是指一条实际的或虚拟的链路存在于数据交换的两端,而是指连接的双方都维
护了一些资源(如输入输出缓冲区、多种定时器)以及链路状态的信息,并通过双方的控制报
文交互管理状态、向用户提供接口修改这些资源的分配。在“连接控制”小节,我们将会仔细审
视资源和状态(包括控制状态的报文),若控制报文丢失,那么连接就会处于一种不一致状
态,TCP通过一些手段去试图解决这些问题(如持活定时器、保持定时器等等)。
TCP提供了可靠的数据传输服务,其采取的措施是对控制报文和数据报文进行确认、并在超时
之后进行重传;并利用滑动窗口协议解决数据数传乱序、收发双方进行流量控制。具体来说就
是,对于发送方,TCP按照其认为最合适的长度发送数据报文,并在发出报文之后,启动一个
定时器,等待数据的确认报文,若定时器超时后仍没有收到确认报文,则重传该报文;对于接
收方,收到数据后,首先检查报文校验和,错误则直接丢弃该报文,不确认(发送端会注意到
这个事实,从而重传);收到重复报文,丢弃,不确认;通过双方维护的滑动窗口,TCP会将
乱序报文排好序后才提交给上层应用程序。需要注意的是,流量控制与拥塞控制并不是同一回
事,流量控制的目的在于防止发送端发送大量数据,超过接收端的处理能力,从而导致丢包
TCP协议
Oct11,2014
2014/10/22TCP协议–kiterunner_t
http://kiterunner.com.cn/blog/tcp-protocol.html2/8
等;拥塞控制则在于防止网络中发生拥堵,中间路由器或交换机丢弃报文的情况。
TCP提供数据流服务,上层应用传给TCP的数据,TCP并不加以区分,仅仅是按照自己的需求
组合、拆分数据,然后传送给对端,对端TCP协议栈再将数据以发送的顺序递交给上层应用。
TCP的数据传送仍然是以IP报文的方式发送到对端的,每次尽力发送MSS大小的报文,在“数
据传输”小节我们会看到诸如Nagle算法、TCP_CORK等对流中报文的控制。
TCP本身并不提供报文边界之类的东西,但提供了紧急数据、PUSH标志(并没有提供对外接
口)等方式可以模拟报文。通常,TCP数据流的划分是应用程序的事,应用程序定义好格式,
并自己解析,常见的方案有:
先传输固定大小的报文长度字段;
按行进行分割(或其他分隔符);
固定长度记录;
各种编码方案,如xml、json、ASN.1、tlv等。
2TCP报文
2.1TCP报文格式
TCP报文最终是由IP层发送出去的,封装报文如下:
TCP报文格式如下:
2014/10/22TCP协议–kiterunner_t
http://kiterunner.com.cn/blog/tcp-protocol.html3/8
通常使用一个四元组唯一的表示一个TCP连接(client-ip,client-port,server-ip,server-port),
但需要注意以下事实:
监听服务器在server-port窗口接受来自客户端的连接,并fork一个子进程处理连接,此
时,该四元组却在服务器对应了两个进程(监听进程只处理SYN报文,而子进程却只能处
理数据报文和FIN等);
连接的化身,这在后面会详细描述。
在TCP首部的图中,我们主要关注以下几个字段:序号、首部长度、6个标志位、选项,窗口
大小、紧急指针都是以字节单位,这里并不关注。
不含选项的TCP首部为20字节,在首部选项中指明了首部有多少个4字节,由于其占了4位,
因此首部最多为60字节。
序号字段用来标识TCP数据流中的数据字节流,在建立连接时会以一个ISN进行初始化,每个
SYN、FIN等都会消耗掉一个序号。我们并不用太关心这个字段,只是需要知道序号为32位,
在长肥管道(容量较大的网络中)序号可能会出现回绕,TCP需要识别。TCP对该字段也进行
了相应的扩充(增加选项)。
TCP选项是以1字节类型、1字节长度(可选)、内容(可选)来表示的,可以只有类型,长度
字段包含类型和长度本身。常见选项如下图所示,无操作选项通常用来作为填充以满足选项对
齐的要求,tcpdump连接建立的输出中通常会包含这些选项:
2014/10/22TCP协议–kiterunner_t
http://kiterunner.com.cn/blog/tcp-protocol.html4/8
TCP首部中定义了6个字段,在一个报文中,通常只会出现一个标志,但也允许多个标志同时
出现。
URG,紧急指针标志位。
ACK,确认序号标志位,关于ACK有几点需要注意:a)ACK是累积的,表示接收方已经正
确收到了一直到确认序号减一的所有字节;b)TCP通常并不会对每个数据包进行确认,而
是采用了捎带确认和延迟确认的技术,捎带确认是指将ACK报文合并到数据报文中去,而
延迟确认是TCP维持了一个200ms的定时器,在定时器过期前,若有多个数据需要确认,
则一块进行确认,通常是两个报文确认一次,若200ms到了,仍没有新数据需要确认,则
不再等待,直接确认该报文;c)ACK报文本身并不会被确认,当ACK丢失时,需要依靠对
端超时机制发现(后文详述)。
PSH,该标志由TCP自动设置(曾经允许通过接口进行设置,当前多数实现不提供),多
数实现在发送者将清空发送缓冲区时设置该标志,即发送者一次将当前发送缓冲区的数据
都发送出去了。
RST,连接重置标志位。
SYN,同步标志位,用来发起一个新建连接。
FIN,发送端已经完成了所有的数据发送,不会再发送新的数据,关闭了其发送端,若对
端也发送该标志,则完全关闭连接。
2.2常见报文
TCP中除了通常的数据交换报文(数据报文详见后文“数据传输”小节),还有以下一些特殊的
控制报文:SYN报文、ACK报文、FIN报文、RST报文、窗口探测报文、持活报文。这里将常
见报文列出来,一是为了强调一个事实,TCP的状态变更大部分是通过报文交互进行的;二是
对各种控制报文的使用场景进行简单归纳。
2014/10/22TCP协议–kiterunner_t
http://kiterunner.com.cn/blog/tcp-protocol.html5/8
SYN报文,用于发起一个新连接,只包含TCP首部,没有数据。一个典型报文输出如下:
10:23:17.543837IP192.168.47.1.55366>krt.9876:Flags[S],seq2289863414,win8192,options[mss1460,nop,wscale2,nop,nop,sackOK],length0
ACK报文,用于对控制报文(不包括RST)和数据报文进行确认,参考上一小节关于ACK的一
些注意点。该报文可以与其他报文结合在一起,如SYN、数据报文、FIN报文等。单独的ACK
本身不含任何数据,只有首部,典型报文输出如下:
10:23:17.544135IP192.168.47.1.55366>krt.9876:Flags[.],ack1,win16425,length0
FIN报文,用于通知对端已经发送完了所有数据,将发送缓冲区中数据发送完成后,可以关闭
连接。详细参考后文“连接控制”,用于有序释放连接或者连接半打开。
RST报文,当向一个出现错误的连接发送一个报文的时候,就收到RST报文。以下是几种典型
情况(详细情景在后文“连接控制”小节表述):
对端的相应端口上没有监听程序
异常终止连接,SO_LINGER。使用异常方式终止连接,而不是正常的有序终止连接
orderlyrelease,可以迫使连接直接丢失未发送的数据;而接收方收到RST报文后,read
返回reset错误,从而终止该连接,同时不会进行ACK。
检测半打开连接,一方已经关闭或异常终止连接而另一方还不知道。造成这种状况的原因
在于通信的一方没有正确将相应的状态报文成功发送给对端,如主机突然掉电导致FIN报
文没有发出去,此时再写该socket,则会得到RST报文。
窗口探测报文(持久探测报文),TCP并不会对不包含数据的ACK报文进行确认,因此可能出
现ACK丢失,从而导致窗口通告失败。TCP使用persist定时器,定期的发送一个字节的窗口探
测报文。探测报文总是在5~60s之间,也是采用指数后退算法,但不超过60s,该探测报文会
一直持续。实现时,使用500ms的定时器。
持活报文,用于查看连接是否仍然存活的控制报文。报文只带有ACK标志,且序号字段为将要
发送的序号减一,这样引起对端进行一个ACK,表示接收到重复序号,对端期望的序号为下一
个值。
3连接控制
TCP是面向连接的协议,正如前面所描述的,并不存在真实的物理或虚拟的链路,TCP的连接
是指在通信的双方分配了资源和维护了状态,并通过控制报文控制连接,通过API协调资源。
本小节将详细描述实现中对TCP的连接控制。需要注意的是,连接的拥塞控制在本小节不过多
涉及,后面单独小节描述。
4个定时器:重传定时器、2MSL定时器、persist定时器、keepalive定时器
连接双方通过以下一些事件来维持连接的状态,发送方:发送缓冲区、4个定时器、发送窗
口、拥塞状态计数器接收方:接收缓冲区、4个定时器、接收窗口、拥塞状态计数器
TCP连接更详细的描述通信双方通过一些内部状态保持了彼此的信息,连接关系始终保持,
并通过报文交换来进行连接状态的变更。由于是通过报文进行连接状态的维护,所以报文没有
正确发出或被接收到时,连接状态就会变成意料之外了;除确认报文本身不被确认,其他报文
都会有确认报文进行确认;报文(包括确认报文)可能超时、需要重传。下面是通信过程中一
2014/10/22TCP协议–kiterunner_t
http://kiterunner.com.cn/blog/tcp-protocol.html6/8
些重要报文的交互序列。
发送方网络(中间路由器等组成)接收方
发送方为Windows,接收方为Linux,操作如下
krt@krt:~$perlksock.pl--sleep-before-listen=1
Windows下
C:\User\win-krt\Desktop>telnet192.168.47.1209876
krt@krt:~$sudotcpdump-n-ieth0tcpport9876tcpdump:verboseoutputsuppressed,use-vor-vvforfullprotocoldecode
listeningoneth0,link-typeEN10MB(Ethernet),capturesize262144bytes13:59:16.575626IP192.168.47.1.65281>192.168.47.120.9876:Flags[S],seq258
8085696,win8192,options[mss1460,nop,wscale2,nop,nop,sackOK],length013:59:16.575678IP192.168.47.120.9876>192.168.47.1.65281:Flags[S.],seq37
51549776,ack2588085697,win29200,options[mss1460,nop,nop,sackOK,nop,wscale7],length0
13:59:16.577107IP192.168.47.1.65281>192.168.47.120.9876:Flags[.],ack1,win16425,length0
13:59:19.564526IP192.168.47.1.65281>192.168.47.120.9876:Flags[P.],seq1:2,ack1,win16425,length1
13:59:19.564747IP192.168.47.120.9876>192.168.47.1.65281:Flags[.],ack2,win229,length0
13:59:19.565023IP192.168.47.120.9876>192.168.47.1.65281:Flags[P.],seq1:13,ack2,win229,length12
13:59:19.763747IP192.168.47.120.9876>192.168.47.1.65281:Flags[P.],seq1:13,ack2,win229,length12
13:59:19.763849IP192.168.47.1.65281>192.168.47.120.9876:Flags[.],ack13,win16422,length0
13:59:19.764023IP192.168.47.1.65281>192.168.47.120.9876:Flags[.],ack13,win16422,options[nop,nop,sack1{1:13}],length0
13:59:23.688209IP192.168.47.1.65281>192.168.47.120.9876:Flags[F.],seq2,ack13,win16422,length0
13:59:23.688372IP192.168.47.120.9876>192.168.47.1.65281:Flags[F.],seq13,ack3,win229,length0
13:59:23.689053IP192.168.47.1.65281>192.168.47.120.9876:Flags[.],ack14,win16422,length0
3.1连接建立
连接建立过程中会经历被称为“三次握手”的报文交互。
2014/10/22TCP协议–kiterunner_t
http://kiterunner.com.cn/blog/tcp-protocol.html7/8
连接建立过程主要目的在于协商双方通信的细节,双方的初始序列号、窗口大小、最大报文段
MSS大小等。
3.2连接断开
4数据传输
数据交换(基本的确认、超时、重传,滑动窗口)
交互数据和批量数据交换
5拥塞控制
6性能和常用网络工具
tcpdumplsofnetstatss/proc
iputils包net-tools包iproute2包
7参考资料
本文参考了以下资料,仅仅是对TCP按照自己的理解重新梳理一下,图片直接来自于这些资料
的截图。
TCP/IP详解——卷一:协议,W.RichardStevens著,范建华、胥光辉等译。
计算机网络——系统方法,第4版,LarryL.PetersonandBruceS.Davie著,薛静锋、胡
2014/10/22TCP协议–kiterunner_t
http://kiterunner.com.cn/blog/tcp-protocol.html8/8
晶晶等译。
TCPImplementationinLinux:ABriefTutorial,byHelaliBhuiyan,MarkMcGinleyetc.
TCP的那些事儿(上),http://coolshell.cn/articles/11564.html。
TCP的那些事儿(下),http://coolshell.cn/articles/11609.html。
再次谈谈TCP的Nagle算法与TCP_CORK选
项,http://dog250.blog.51cto.com/2466061/1377408。
|
|