TCP(Transmission Control Protoco)
,是一种基于字节流面向连接的传输层协议。数据的传输需要通信双方建立一个连接,TCP协议采用三次握手建立一个连接,采用 4 次挥手来关闭一个连接。每一个TCP连接都有两个端点,叫作套接字(socket),它的定义为IP地址+端口号拼接。
TCP/IP协议概况
- IPv4: 网际协议版本4(Internet Protocol version 4), 使用32位地址
- IPv6: 网际协议版本6(Internet Protocol version 6), 使用128位地址,是IPv4替代品,通常把它两者称为”IP“
- TCP: 传输控制协议(Transmission Control Protocol),TCP是一个面向连接的协议,为用户进程提供可靠的全双工字节流, TCP套接字是一种流套接字(stream sockte), 关心确认, 超时, 重传等细节
- UDP: 用户数据报协议(User Datagram Protocol), UDP是一个无连接协议,UDP套接字是一种数据报套接字(datagram socket)
- SCTP:流控制传输协议(Stream Control Transmission Protocol),SCTP是一个提供可靠全双工关联的面向连接的协议
- ICMP:网际控制消息协议(Internet Control Message Protocol),处理在路由器和主机之间流通的错误和控制消息
- ICMPv6:网际控制消息协议版本6
- IGMP: 网际组管理协议,用于多播
- ARP: 地址解析协议(Address Resolution Protocol),把IPv4地址映射成一个硬件地址(如以太网地址)
- RARP: 反地址解析协议(Reverse..), 将硬件地址映射成IPv4地址
- BPF: BSD分组过滤器
TCP通信三部曲
- 建立:三次握手
- 传输:超时重传、快速重传、流量控制、拥塞控制等
- 断开:四次挥手
TCP服务模型
一个 TCP 连接由一个 4 元组构成,分别是两个 IP 地址和两个端口号。一个 TCP 连接通常分为三个阶段:启动、数据传输、关闭。
当 TCP 接收到另一端的数据时,它会发送一个确认,但这个确认不会立即发送,一般会延迟一会儿。ACK 是累积的,一个确认字节号 N 的 ACK 表示所有直到 N 的字节(不包括 N)已经成功被接收了。这样的好处是如果一个 ACK 丢失,很可能后续的 ACK 就足以确认前面的报文段了。
一个完整的 TCP 连接是双向和对称的,数据可以在两个方向上平等地流动。给上层应用程序提供一种双工服务。一旦建立了一个连接,这个连接的一个方向上的每个 TCP 报文段都包含了相反方向上的报文段的一个 ACK。
序列号的作用是使得一个 TCP 接收端可丢弃重复的报文段,记录以杂乱次序到达的报文段。因为 TCP 使用 IP 来传输报文段,而 IP 不提供重复消除或者保证次序正确的功能。另一方面,TCP 是一个字节流协议,绝不会以杂乱的次序给上层程序发送数据。因此 TCP 接收端会被迫先保持大序列号的数据不交给应用程序,直到缺失的小序列号的报文段被填满。
TCP报文头部
- 源端口和目的端口,各占2个字节;
- 序号,占4个字节,TCP连接中传送的字节流中的每个字节都按顺序编号;
- 确认号,占4个字节,是期望收到对方下一个报文的第一个数据字节的序号,即最后被成功接收的数据字节序列号加 1,这个字段只有在 ACK 位被启用的时候才有效;
- 数据偏移,占4位,它指出TCP报文的数据距离TCP报文段的起始处有多远;
- 保留,占6位,保留今后使用,但目前应都位0;
- 紧急URG,当URG=1,表明紧急指针字段有效。告诉系统此报文段中有紧急数据;
- 确认ACK,仅当ACK=1时,确认号字段才有效。TCP规定,在连接建立后所有报文的传输都必须把ACK置1;
- 推送PSH,当两个应用进程进行交互式通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应,这时候就将PSH=1;
- 复位RST,当RST=1,表明TCP连接中出现严重差错,必须释放连接,然后再重新建立连接;
- 同步SYN,在连接建立时用来同步序号。当SYN=1,ACK=0,表明是连接请求报文,若同意连接,则响应报文中应该使SYN=1,ACK=1;
- 终止FIN,用来释放连接。当FIN=1,表明此报文的发送方的数据已经发送完毕,并且要求释放;
- 窗口,占2字节,指的是通知接收方,发送本报文你需要有多大的空间来接受;
- 检验和,占2字节,校验首部和数据这两部分;
- 紧急指针,占2字节,指出本报文段中的紧急数据的字节数;
TCP状态转换
状 态 | 描 述 |
---|---|
CLOSED | 关闭状态,没有连接活动或正在进行 |
LISTEN | 监听状态,服务器正在等待连接进入 |
SYN_RCVD | 收到一个连接请求,尚未确认 |
SYN_SENT | 已经发出连接请求,等待确认 |
ESTABLISHED | 连接建立,正常数据传输状态 |
FIN_WAIT_1 | (主动关闭)已经发送关闭请求,等待确认 |
FIN_WAIT_2 | (主动关闭)收到对方关闭确认,等待对方关闭请求 |
TIMED_WAIT | 完成双向关闭,等待所有分组死掉 |
CLOSING | 双方同时尝试关闭,等待对方确认 |
CLOSE_WAIT | (被动关闭)收到对方关闭请求,已经确认 |
LAST_ACK | (被动关闭)等待最后一个关闭确认,并等待所有分组死掉 |
那么状态转换为什么要经历三次握手和四次挥手呢?
TCP三次握手
- 服务器进程先创建传输控制块TCB,时刻准备接受客户进程的连接请求,此时服务器就进入了LISTEN(监听)状态;
- 客户端先创建传输控制块TCB,然后向服务器发出连接请求报文,SYN=1,表示请求建立连接,同时选择一个初始序列号 seq=x ,此时,TCP客户端进程进入了 SYN_SENT(同步已发送状态)状态。TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号。
- 服务器收到请求报文后,如果同意连接,则发出确认报文。确认报文中应该 ACK=1,SYN=1,确认号是ack=x+1,同时也要为自己初始化一个序列号 seq=y,此时,TCP服务器进程进入了SYN_RCVD(同步收到)状态。这个报文也不能携带数据,但是同样要消耗一个序号。
- T客户端收到确认后,还要向服务器给出确认。确认报文的ACK=1,ack=y+1,自己的序列号seq=x+1,此时,TCP连接建立,客户端进入ESTABLISHED(已建立连接)状态。TCP规定,ACK报文段可以携带数据,但是如果不携带数据则不消耗序号。
- 当服务器收到客户端的确认后也进入ESTABLISHED状态,三次握手结束,连接建立。
为什么需要第三次客户端ACK?
TCP四次挥手
以客户端主动关闭为例,服务器端也可以主动关闭,方向与下面相反。
- 客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN_WAIT_1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
- 服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE_WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE_WAIT状态持续的时间。
- 客户端收到服务器的确认请求后,此时,客户端就进入FIN_WAIT_2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
- 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST_ACK(最后确认)状态,等待客户端的确认。
- 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME_WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2*MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
- 服务器只要收到了客户端发出的确认,立即进入CLOSED状态,撤销TCB结束TCP连接。
为什么主动关闭连接方最后需要等待2MSL?
为什么关闭连接需要四次挥手,比建立连接多一次呢?
如果通信双方同时请求连接或同时请求释放连接?
TIME_WAIT状态
参考📚:
TCP的三次握手与四次挥手