TCP协议简介
tcp/ip协议族中传输层最重要的两种协议是UDP和TCP协议,上一篇文章用很短的篇幅介绍完了UDP协议相关的内容,但相对于UDP而言的TCP协议,是种更复杂,应用更广的协议。在接下来的几篇文章中都会学习TCP协议相关的知识。这里补充一点有用的小知识:之前分析网络包我都用的tcpdump命令,因为写博客时都在用ubuntu系统,所以linux下的tcpdump简单强大,也不用安装什么。现在写文章时换回了windows7系统,因为之前在ubuntu下写一篇文章时浏览器总是莫名的把写了半天的文章搞丢,所以换回windows写文章。在windows下分析网络包用的wireshark,一个你不得不知道的专业分析网络包的工具。
首先还是介绍下TCP协议的rfc定义文档内容。RFC793 定义了TCP协议。
首先我们要清楚TCP在整个TCP/IP协议族中的位置。它基于IP协议之上,服务于更高层的应用层协议,如ftp,http,smtp等等。
TCP的头部格式(Header Format)如下:
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Source Port | Destination Port | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Sequence Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Acknowledgment Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Data | |U|A|P|R|S|F| | | Offset| Reserved |R|C|S|S|Y|I| Window | | | |G|K|H|T|N|N| | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Checksum | Urgent Pointer | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Options | Padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | data | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
(给大家介绍一个画ascii图的网址:http://www.asciiflow.com/#Draw)
- Source Port: 源端口。我们知道ip报文中有源ip地址和目的ip地址,这两个值加上tcp报文中的源端口(source port)和目的端口(destination port),就确定了一个tcp连接。
- Destination Port: 目的端口。如source port介绍一样。 一般对于一个ip地址和一个port端口号,我们也称为一个socket。说起socket我们应该并不陌生,在应用开发时经常用到。socket就是对网络编程接口的封装。
- Sequence Number: 序列号。我们知道tcp是面向流的连接。tcp针对每个传送的字节,都会进行计数。序号是32bit的无符号数。值范围是0~232-1,当达到最大值时会从又从0开始计数。
- Acknowledgment Number: 确认序号。指期望下次接收到的序列号。
- Data offset: TCP报文头部长度。以32位(8byte)为单位。所以最大报议长长度是60字节。如果没有可选字段的话,一般是20字节。
- Reserved: 保留字段
- U,A,P,R,S,F: 六个标志位。URG:紧急指针 ACK:确认序号有效 PSH:接收方应该尽快将该报文交给应用层 RST:重新连接 SYN:同步序号发起一个连接 FIN:发端完成发送任务
- window: TCP流量的窗口大小。以字节为单位。最大是216 - 1 = 65535
- Checksum: 检验和,覆盖了整个TCP的头部和数据。同发送端计算,接收端验证。
- Urgent Pointer: 紧急指针是一个正的偏移量,它和序号字段相加表示紧急数据的最后一个字节序号
- options: 可选字段。最常见的可选字段如MSS(maximum segment size)。
- Padding: 填充字段。因为头部字段必需是32位的整数倍。所以当可选字段非32整数倍长度时,用0来填充。
- data: tcp数据
TCP连接建立
TCP连接的建立,我想很多找工作的时候都喜欢问这个问题,比如说说TCP连接的三次握手(three way handshake)的过程。所以搞清楚tcp连接建立的过程至少对找工作还是挺有帮助的。
- client端发送一个syn初始化同步序列号,假设seq=0x9e0dd824, 我们这里用16进表示。上图中标示seq=0的表示方法是相对表示法,这样以后出现的seq序列号都是相对于syn时的初始序列号的数。
- server端收到客户端的syn报文后,同时也发送一个[syn,ack]的报文。注意这里的seq=0表示server端的初始化序列,也是一个相对值,它的真实值可能是seq=0x59bc0bd5。这时发送的报文中设置了ack标识,并置确认序列号(acknowledge number)为ack=1,即为之前收到的[syn]中seq的初始化值加1,真实值为ack=0x9e0dd825
- client发送一个[ack]报文。seq=1,即为seq=0x9e0dd825, ack=1指对server端上次发送的seq加1 ,即ack=0x59bc0bd6
现在经过这三步(三次握手),已经建立起了一个tcp连接。这里要强调的上图中没有所谓的哪边是客户端,哪边是服务端,都只是相对而言的。
TCP连接终止
tcp的关闭有较连接建立有点复杂,因为我们知道tcp连接是全双工的,即端与端之前可以同时都可以发送,接收数据。那么关闭连接理论上是可以有多种组合的。
下面来看一种正常情况下的关闭流程:
- clinet端主动发起关闭操作,发送一个[fin,ack]的报文。
- server端在收到client端发送的[fin,ack]报文后,也回复一个[fin,ack]报文
- client端最后发送一个[ack],此时连接终止过程就完成了。
还有一种情况,就是一端完成关闭通知后,它还可以接收别一端继续传输过来的数据直到别一端也通知连接在这个方向上也关闭了。这就是所谓的tcp连接半关闭。如图:
- client端发送[fin,ack]报文通知server端它已经结束在这个方向上的数据传输。
- server端发送[ack]确认
- 此时client-->server方向上已经不能再传输任何数据了。但是server-->client方向上还可以继续传输数据。
- 当server完成了这个方向上的数据传输时,它现在要关闭这个方向上的连接,则要发送[fin,ack]报文
- client最后一次发送[ack]确认
TCP状态变迁
上图说明:实线箭头方向是客户端的正常状态变迁,虚线箭头是服务端的正常状态变迁,如果大家要上机实践下,可以用netstat命令查看本机的所有连接状态。结合wireshark截包分析下。
用序列图来表示整个连接建立和终止的过程中状态的变迁过程:
其实这两个图描述的tcp状态变迁过程一样,只是表现形式不同。可以从这两个图中明确的学习到tcp的状态变化。