1.简介概述
(1)tcp是面向连接的传输层协议
应用程序在使用tcp协议之前,必须先建立连接。在数据传输完成后,必须释放已经建立好的tcp连接。
(2)tcp提供可靠交互的服务
通过tcp连接传送的数据,无差错、不丢失、不重复、并且按序到达。
(3)tcp提供全双工通信
TCP允许通信双方的应用进程在任何时候都能发送数据。TCP连接的两端都设有发送缓存和接收缓存,用来临时存放双向通信的数据。在发送时,应用程序在把数据传送给TCP的缓存后,就可以做自己的事,而TCP在合适的时候把数据发送出去。在接收时,TCP把收到的数据放入缓存,上层的应用进程在合适的时候读取、缓存中的数据。
(4)面向字节流
图1
虽然应用程序和tcp的交互是一次一个数据块,但是tcp把应用程序交下来的数据看成仅仅是一串无结构的字节流。Tcp并不知道所传送的字节流的含义。
Tcp数据被封装在一个ip数据报中:
图2
TCP工作在网络OSI的七层模型中的第四层——传输层。
图3
Tcp连接:
1)TCP 把连接作为最基本的抽象。
2)每一条TCP 连接有两个端点。
3)TCP 连接的端点不是主机,不是主机的IP地址,不是应用进程,也不是运输层的协议端口。TCP 连接的端点叫做套接字(socket)或插口。
4)端口号拼接到(contatenated with) IP 地址即构成了套接字。
5)每一条tcp连接唯一的被通信两端的两个端点确定。
TCP连接: : {socket1,socket2}={(IP1,port1),(IP2,port2)}
2.报文格式
RFC793 tcp报文格式:
图4
图5
源端口和目的端口——分别占用16位(2字节),用于区别主机中的不同进程,而IP地址是用来区分不同的主机的,源端口号和目的端口号配合上IP首部中的源IP地址和目的IP地址就能唯一的确定一个TCP连接;
图6
序号字段——占 4 字节。TCP 连接中传送的数据流中的每一个字节都编上一个序号。序号字段的值则指的是本段报文所发送的数据的第一个字节的序号,主要用来解决网络报乱序的问题。
图7
确认号字段——占 4 字节,是期望收到对方的下一个报文段的数据的第一个字节的序号。因此,确认序号应 当是上次已成功收到数据字节序号加1。不过,只有当标志位中的ACK标志为1时该确认序列号的字 段才有效。主要用来解决不丢包的问题;
图8
头部长度——给出首部中32 bit字的数目,需要这个值是因为任选字段的长度是可变的。这个字段占4bit(最多能 表示15个32bit的的字,即4*15=60个字节的首部长度),因此TCP最多有60字节的首部。然而,没有任选字段, 正常的长度是20字节;
图9
保留字段——占6个字节保留今后使用,目前置为0.
图10
紧急URG —— 当URG = 1 时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据)。
图11
确认ACK —— 只有当ACK = 1 时确认号字段才有效。当ACK = 0 时,确认号无效。
图12
推送PSH (Push) —— 接收TCP 收到PSH = 1 的报文段,就尽快地交付接收应用进程,而不再等到整个缓存都填满了后再向上交付。
图13
复位RST (Reset) —— 当RST = 1 时,表明TCP 连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接。
图14
同步SYN —— 同步SYN = 1 表示这是一个连接请求或连接接受报文。
图15
终止FIN (Finis) —— 用来释放一个连接。FIN = 1 表明此报文段的发送端的数据已发送完毕,并要求释放运输连接。
图16
窗口字段——占 2 字节,用来让对方设置发送窗口的依据,单位为字节。
图17
检验和 —— 占 2 字节。检验和字段检验的范围包括首部和数据这两部分。在计算检验和时,要在 TCP 报文段的前面加上 12 字节的伪首部。
图18
紧急指针字段 —— 占16 位,指出在本报文段中紧急数据共有多少个字节(紧急数据放在本报文段数据的最前面)。
图19
选项字段—— 长度可变。TCP 最初只规定了一种选项,即最大报文段长度MSS。MSS 告诉对方TCP:“我的缓存所能接收的报文段的数据字段的最大长度是MSS 个字节。”
图20
填充字段—— 这是为了使整个首部长度是4字节的整数倍。
TCP伪首部
图21
伪首部并非TCP数据报中实际的有效成分。伪首部是一个虚拟的数据结构,其中的信息是从数据报所在IP分组头的分组头中提取的,既不向下传送也不向上递交,而仅仅是为计算校验和。
这样的校验和,既校验了TCP用户数据的源端口号和目的端口号以及TCP用户数据报的数据部分,又检验了IP数据报的源IP地址和目的地址。伪报头保证TCP数据单元到达正确的目的地址。
因此,伪报头中包含IP地址并且作为计算校验和需要考虑的一部分。最终目的端根据伪报头和数据单元计算校验和以验证通信数据在传输过程中没有改变而且到达了正确的目的地址。
伪首部,更确切的说是校验和包含的一个96位的伪首标,是个理论上的值,只是理论上它位于TCP首标的前面。这个伪首标包含了源地址、目的地址、协议和TCP长度等字段,这使得TCP能够防止出现路由选择错误的数据段。这些信息由网际协议(IP)承载,通过TCP网络接口,在IP上运行的TCP调用参数或者结果中传递。、
TCP 伪头部与校验和计算
typedef struct//tcp 伪头部, 计算校验和时需要
{
unsignedlong saddr; //源IP地址
unsigned long daddr; //目的IP地址
charmbz; // mbz = must bezero,用于填充对齐
charprotocal; //8位协议号
unsigned short tcpl; // TCP包长度
}psdheader_t;
就是把需要进行校验的“字串”加(+)起来,把这相加的结果取反当做“校验和”(Checksum),比如,相加的结果是0101,那么“校验和”就是1010,验证的时候呢,就是0101+1010 = 1111 ,取反后,就是0——如果验证得“零”(0),就是正确的!
MSS (MaximumSegment Size)
是TCP报文段中的数据字段的最大长度。数据字段加上TCP 首部,才等于整个的TCP 报文段。
MSS应尽可能大,只要在IP层不必分片就行。在建立链接时,双方都将自己能够支持的MSS写入这一字段,在以后的数据传送阶段,MSS取双方提出的较小的值。
如果通信双方未填写MSS,默认值为536。因此,因特网上所有的主机都应能接受536+20的报文段
其他选项:
窗口扩大选项一一占3 字节,其中有一个字节表示移位值S。新的窗口值等于TCP 首部中的窗口位数增大到(16 + S),相当于把窗口值向左移动S 位后获得实际的窗口大小。
时间戳选项一一占10 字节,其中最主要的字段时间戳值字段(4 字节) 和时间戳回送回答字段(4 字节)。
选择确认SACK选项一一用来处理未按序号或者缺少序号的一些数据进行重传,而不用重传已经正确到达接收方的数据。
选择确认(SACK)工作原理
TCP的接收方在接收对方发送过来的数据字节流的序号不连续,结果就形成了一些不连续的字节块。如果接收方收到了和前面的字节流不连续的字节块。这些字节的序号都在接收窗口之内,那么接收方就先收下这些数据,但要把这些信息准确地告诉发送方,使发送方不要再重复发送这些已收到的数据。
图22
1.和前后字节不连续的每一个字节块都有两个边界:左边界和右边界。 图中用四个指针标记这些边界。
2.第一个字节块的左边界L1= 1501,但右边界R1= 3001。
3.左边界指出字节块的第一个字节的序号,但右边界减 1 才是字节块中的最后一个序号。
4.第二个字节块的左边界L2= 3501,而右边界R2= 4501
RFC 2018 的规定,如果要使用选择确认,那么在建立TCP 连接时,就要在TCP 首部的选项中加上“允许SACK”的选项,而双方必须都事先商定好。
如果使用选择确认,那么原来首部中的“确认号字段”的用法仍然不变。只是以后在TCP 报文段的首部中都增加了SACK 选项,以便报告收到的不连续的字节块的边界。
由于首部选项的长度最多只有40 字节,而指明一个边界就要用掉4 字节,因此在选项中最多只能指明4 个字节块的边界信息。
3.连接管理
Tcp三次握手
图23
A 的TCP 向B 发出连接请求报文段,其首部中的同步位SYN = 1,并选择序号seq = x,表明传送数据时的第一个数据字节的序号是x。
B 的TCP 收到连接请求报文段后,如同意,则发回确认。
B 在确认报文段中应使SYN= 1,使ACK = 1,其确认号ack = x + 1,自己选择的序号seq = y。
A 收到此报文段后向B 给出确认,其ACK =1,确认号ack = y + 1。通知上层,连接已建立。
B的TCP 收到主机A 的确认后,也通知其上层应用进程:TCP 连接已经建立。
四次挥手
图24
数据传输结束后,通信的双方都可释放连接。现在 A 的应用进程先向其TCP 发出连接释放报文段,并停止再发送数据,主动关闭 TCP 连接。
A 把连接释放报文段首部的FIN= 1,其序号seq = u,等待B 的确认。
B 发出确认,确认号ack= u + 1,而这个报文段自己的序号 seq = v。
TCP 服务器进程通知高层应用进程。
从 A 到B 这个方向的连接就释放了,TCP 连接处于半关闭状态。B 若发送数据,A 仍要接收。
若B 已经没有要向A 发送的数据,其应用进程就通知 TCP 释放连接。
A 收到连接释放报文段后,必须发出确认。
在确认报文段中ACK = 1,确认号ack = w + 1,自己的序号 seq = u + 1。
本方启动关闭:
收到本方应用进程的关闭命令(CLOSE)后, TCP 在发送完尚未处理的报文段后,发 FIN = 1 的报文段给对方,且 TCP 不再受理本方应用进程的数据发送。在 FIN 以前发送的数据字节,包括 FIN ,都需要对方确认,否则要重传。注意 FIN 也占一个顺序号。一旦收到对方对 FIN 的确认以及对方的 FIN 报文段,本方 TCP 就对该 FIN 进行确认,在等待一段时间,然后关闭连接。等待是为了防止本方的确认报文丢失,避免对方的重传报文干扰新的连接。
对方启动关闭:
当 TCP 收到对方发来的 FIN 报文时,发 ACK 确认此 FIN 报文,并通知应用进程连接正在关闭。应用进程将以关闭命令响应。 TCP 在发送完尚未处理的报文段后,发一个 FIN 报文给对方 TCP ,然后等待对方对 FIN 的确认,收到确认后关闭连接。若对方的确认未及时到达,在等待一段时间后也关闭连接。
双方同时启动关闭:
连接双方的应用进程同时发关闭命令,则双方 TCP 在发送完尚未处理的报文段后,发送 FIN 报文。各方 TCP 在 FIN 前所发报文都得到确认后,发 ACK 确认它收到的 FIN 。各方在收到对方对 FIN 的确认后,同样等待一段时间再关闭连接。这称之为同时关闭( simultaneous close )。
Tcp的有限状态机:
图25
粗实线箭头:对客户进程的正常变迁
虚线箭头:对服务器进程的正常变迁
细线箭头:异常变迁
CLOSED:关闭状态,没有连接活动或正在进行
LISTEN:监听状态,服务器正在等待连接进入
SYN RCVD:收到一个连接请求,尚未确认
SYN SENT:已经发出连接请求,等待确认
ESTABLISHED:连接建立,正常数据传输状态
FIN WAIT 1:(主动关闭)已经发送关闭请求,等待确认
FIN WAIT 2:(主动关闭)收到对方关闭确认,等待对方关闭请求
TIMED WAIT:完成双向关闭,等待所有分组死掉
CLOSING:双方同时尝试关闭,等待对方确认
CLOSE WAIT:(被动关闭)收到对方关闭请求,已经确认
LAST ACK:(被动关闭)等待最后一个关闭确认,并等待所有分组死掉
三次握手的正常状态转换:
(1)服务器端首先执行LISTEN 原语进入被动打开状态( LISTEN ),等待客户端连接;
(2)当客户端的一个应用程序发出 CONNECT 命令后,本地的 TCP 实体为其创建一个连接记录并标记为 SYN SENT 状态,然后给服务器发送一个 SYN 报文段;
(3)服务器收到一个 SYN 报文段,其 TCP 实体给客户端发送确认ACK 报文段同时发送一个 SYN 信号,进入 SYN RCVD 状态;
(4)客户端收到 SYN +ACK 报文段,其 TCP 实体给服务器端发送出三次握手的最后一个 ACK 报文段,并转换为 ESTABLISHED 状态;
(5)服务器端收到确认的ACK 报文段,完成了三次握手,于是也进入 ESTABLISHED 状态。
四次挥手的正常状态转换:
(1)客户端执行 CLOSE 原语,本地的 TCP 实体发送一个 FIN 报文段并等待响应的确认(进入状态 FIN WAIT 1 );
(2)服务器收到一个 FIN 报文段,它确认客户端的请求发回一个 ACK 报文段,进入 CLOSE WAIT 状态;
(3)客户端收到确认 ACK 报文段,就转移到 FINWAIT 2 状态,此时连接在一个方向上就断开了;
(4)服务器端应用得到通告后,也执行 CLOSE 原语关闭另一个方向的连接,其本地 TCP 实体向客户端发送一个 FIN 报文段,并进入 LAST ACK 状态,等待最后一个 ACK 确认报文段;
(5)客户端收到 FIN 报文段并确认,进入TIMED WAIT 状态,此时双方连接均已经断开,但 TCP 要等待一个 2 倍报文段最大生存时间 MSL ( Maximum Segment Lifetime ),确保该连接的所有分组全部消失,以防止出现确认丢失的情况。当定时器超时后, TCP 删除该连接记录,返回到初始状态( CLOSED )。
(6)服务器收到最后一个确认 ACK 报文段,其 TCP 实体便释放该连接,并删除连接记录,返回到初始状态( CLOSED )。
4.抓包分析
在wireshark中打开一个完整的tcp会话。
图26
图27
图28
图29
图30