TCP协议提供的是面向连接的、可靠的字节流服务,这个特点从其名字(Transport Control Protocol传输控制协议)就能看出来。TCP所说的“面向连接”指的是通信双方必须首先协商建立一个链接才能进行通信,而“可靠的传输”则通过以下方式来实现:
对于应用程序产生的数据,为了便于发送,TCP会将其进行分割形成TCP报文段(UDP是不会分割应用数据的,这点有所不同)。报文段的最大长度被称为MSS。(MSS和MTU不是同一个概念,要注意区分)
TCP发出某个报文段后,如果在一定时间内未收到确认,那就会重发这个报文段。也就是说TCP收到某个报文段并对其校验正确的时候也必须发送确认信号。这样做会出现的一个问题就是接收重复的数据————由于IP网络中的传输的逐跳的方式,很可能有数据包在网络中被堵塞在某个单点,发送端到时间重发后,之前被堵塞的数据包和重发的数据包都到了接收端,这就造成数据重复或者前后顺序的不一致了。TCP提供了对重复数据进行丢弃和对乱序数据进行重排的功能,确保交给应用层的数据是顺序正确且内容无误的。
由于TCP连接是由双方协商建立的,所以可以提供比较灵活的流控功能,链接的每一方都有接收缓冲区,对方只会发送自己接收缓冲区能够容纳的数据,这样可以防止双方速度不一致时较慢终端的缓冲区溢出。
TCP报文段被封装在IP数据报中,分为TCP首部和TCP数据两个部分。TCP首部的结构如下图:
源端口号和目的端口号包含在TCP首部,它们和IP数据报中IP首部中的IP源地址和目的IP地址加在一起,唯一确定一个TCP链接。socket这个概念就是一个IP地址加上一个端口号。所以说socket与TCP链接一一对应的关系的没有错的。
32位的序号就是用来进行排序的,这个数是无符号数,最大值为2的32次方减1。链接刚建立时的序号叫做初始序号(ISN)。TCP传输中的每一个字节都要被计数,所以由于链接建立时要讲SYN标志加一消耗了一个字节序号,所以链接建立时发送的第一个序号的值是ISN+1。同样的,对于确认序号而言,确认序号应当是收到的序号(字节序号)加一,这个一是发送确认的一端希望收到的下一个序号,表示“我确认收到了N字节,正在等待收N+1字节”这样的信息。这个特点在讲三次握手的时候可以看得很清楚。
需要强调的是,在上图的诸多个标志位中,SYN和FIN标志是需要占用一个序号的,但ACK等其它几个标志不需要占用任何序号。这个问题其实很好理解,确认标志的意义是确认这个数据已经收到了,如果确认标志也占了一个字节,序列号加一了,那我们为了确认收到了这个确认序号,又要发新的确认序号,这样就没完没了了。那为什么要给SYN和FIN一个字节的长度呢?因为SYN用于标志开启连接,FIN用于标志终止连接,如果不给他们序号,收方收到置了FIN或SYN标志位的报文段,要去ACK这个FIN或SYN的时候,就无法和ACK普通数据的报文区分出来了,到时候发方收到了ACK,就不知道这个ACK是针对普通数据的还是开启/关闭信号的。既然收方没法确认,那收不到的时候发方怎么重传呢?整个重传机制就这么失效了。
各标志的说明如下: URG 紧急指针 ACK 确认该序号有效 PSH 接收方要尽快将报文段上交应用层 RST 重新连接标志 SYN 发起连接的同步序号 FIN 发送端将停止发送
窗口大小是流量控制的关键部分,每一端声明自己的窗口大小。检验和是用于整个TCP报文段的,发送端计算好后收端进行检验。可选字段中就包括最长报文段大小MSS这样的字段,而数据字段是可选的,也就是说可以不发送数据。
之前已经铺垫了很多,现在就来看一下建立一个TCP连接的过程:
这种建立连接的方式是比较常见的,即一方主动打开,另一方被动打开的过程:首先请求段发送一个含SYN标志的报文段和自己的初始序号,这被称作第一次握手。服务器收到后返回包含自己序号的SYN报文段,并在这个报文段中置ACK标志表示收到了上一个SYN,确认序号是客户端发送的初始序号加一,这是第二次握手。客户端收到后,再发送一个ACK并将确认序号置为收到的序号加一。
以上是正常情况下的打开连接方式,在少数情况下,还有可能出现同时打开的情况,也就是双方都主动去建立连接,这时打开的握手次数需要四次,如下图:
这种情况下,两端几乎同时直接发送SYN信号,两端在收到对方的SYN信号后都进入SYN_RCVD状态并应答对方已经收到,所以造成四次握手。
对于正常情况下的连接终止,需要四次握手来完成,步骤如下图:
主动执行关闭的一方会发送一个带FIN的数据包表示自己的数据发送终止(只是表示自己这一端终止发送了,对面还是可以发的)。另一端收到后,返回对FIN的ACK并讲过确认序号变为所收到的序号加一。之后,再发送一个自己的FIN表示自己的传输也结束了,以等待发起关闭端进行ACK,一共四次数据交互。当然,既然存在同时打开的情况,那么也存在同时关闭的情况,在同时关闭的情况下双方各自发送FIN并确认对方的FIN,仍然是四次握手的过程。
关于TCP各个状态的变迁,已经早有人总结出下面这个状态变迁图,图上说明得很清楚,有助于进一步理解TCP连接的建立和终止。