目录
简介
TCP的服务与缓存
服务
缓存
TCP头部
TCP的建立与终止
三次握手
四次挥手
例题
有限状态机
可靠传输
校验
滑动窗口
确认与重传
累计确认与超时重传
冗余确认与快速重传
流量控制
例题
拥塞控制
慢开始与拥塞避免
快重传与快恢复
例题
TCP和UDP的区别
实战
抓包
三次握手
参考
TCP协议又叫传输控制协议(Transport Control Protocal),是面向连接的,可靠的字节流服务。它的连接是虚连接,连接的端点是套接字(SOCKET)。它的可靠性体现在:3次握手建立连接,滑动窗口机制,一定的拥塞避免算法,流量控制,以及一定的超时重传机制。基于TCP的协议有HTTP、FTP、SMTP、TELNET、SSH、MQTT等。
谈TCP服务之前先说一下计时器,很多服务需要计时器的支持。
接下来是TCP提供的服务:
数据块分割:应用数据被分割成TCP认为最适合发送的数据块,由TCP传递给IP的信息单位称为报文段或段(segment)。
重新排序:既然TCP报文段作为IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段的到达也可能会失序。如果必要,TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层。
全双工(Full Duplex)通信:又称为双向同时通信,即通信的双方可以同时发送和接收信息的信息交互方式。
可靠传输:通过校验和、序号、确认与重传进行可靠的传输。
流量控制:通过滑动窗口算法,设置接收窗口大小来进行流量控制。
拥塞控制:通过慢开始&拥塞避免、快重传&快恢复算法进行拥塞控制。
不提供广播或多播的服务。
发送缓存:准备发送的数据和已发送但未收到确认的数据
图中红色部分为已发送但未收到确认的数据,发送缓存中的其余部分为准备发送的数据。
接收缓存:按序到达但未接受应用程序读取的数据和不按序到达的数据
图中接收缓存中标识“已收到”的表示按序到达但未接受应用程序读取的数据,接收窗口中的红色部分是不按序到达的数据。
窗口扩大:占 3 字节,其中有一个字节表示移位值 S.新的窗口值等于TCP 首部中的窗口位数增大到(16 + S),相当于把窗口值向左移动 S 位后获得实际的窗口大小
时间戳:占10 字节,其中最主要的字段时间戳值字段(4字节)和时间戳回送回答字段(4字节)
选择确认:接收方收到了和前面的字节流不连续的两字节。如果这些字节的序号都在接收窗口之内,那么接收方就先收下这些数据,但要把这些信息准确地告诉发送方,使发送方不要再重复发送这些已收到的数据。
TCP连接的建立采用客户/服务器方式,主动发起连接建立的应用进程叫做客户,而被动等待连接建立的应用进程叫服务器。
TCP连接四元组(sip,sport,dip,dport),源ip、源端口号、目的ip、目的端口号。
客户端发送连接请求报文段,无应用层数据。同步位SYN=1,表示请求连接。序号seq=x(随机)。确认序号无效,ACK=0,因为客户端未收到服务器端的报文段。
CLOSED: 表示初始关闭状态。
LISTEN(服务器): 这个也是非常容易理解的一个状态,表示服务器端的某个SOCKET处于监听状态,可以接受连接了。
SYN-SENT: 这个状态与SYN-RCVD呼应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即它会进入到了SYN-SENT状态,并等待服务端的发送三次握手中的第2个报文。SYN-SENT状态表示客户端已发送SYN报文。
SYN-RCVD(服务器): 这个状态表示接受到了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂,基本上很难看到这种状态的,除非你特意写了一个客户端测试程序,故意将三次TCP握手过程中最后一个ACK报文不予发送。当收到客户端的ACK报文后,它会进入到ESTABLISHED状态。
ESTABLISHED:这个容易理解了,表示连接已经建立了,可以发送数据。
SYN洪泛攻击:攻击者向服务器发送大量TCP连接请求,服务器确认后,攻击者不确认,造成服务器处于半连接状态,消耗了过多的资源(CPU、内存等),造成死机等问题,无法为正常用户提供服务。可使用SYN Cookie进行防御。
客户端和服务器端都可以终止该连接,连接结束后,主机中的资源被释放。
以客户端主动终止连接为例。
ESTABLISHED:建立连接成功,通信中。
CLOSE-WAIT: 表示被动关闭一方在等待关闭。当主动关闭连接的一方关闭SOCKET后发送FIN报文给被动关闭一方,被动关闭一方回应一个ACK报文给对方,此时被动关闭一方则进入到CLOSE-WAIT状态
FIN-WAIT-1:是当Socket在已经连接的状态时主动关闭连接,向对方发送了FIN报文,此时该Socket进入到FIN-WAIT-1状态。而当对方回应ACK报文后,则进入到FIN-WAIT-2状态
FIN-WAIT-2:表示半连接,挥了两次手的状态等待对方的Fin报文
TIM-WAIT:TCP协议中主动关闭连接的一方要处于TIME-WAIT状态,等待两个MSL(maximum segment lifetime)的时间后才能回到CLOSED状态,在TIME-WAIT期间仍然不能再次监听同样的server端口。
LAST-ACK: 被动关闭一方在发送FIN报 文后,最后等待对方的ACK报文。当收到ACK报文后进入CLOSED状态。
CLOSED:已经完全关闭.
服务器端收到确认报文段后关闭TCP连接。客户端等待两倍的MSL(Maximum Segment Lifetime,报文段最长寿命)时间后关闭连接,因为如果服务器端没有收到客户端回复的确认报文段会再次发出连接释放报文段,2MSL后没有收到服务器端再次发出的连接释放报文段,说明服务器端已关闭。
[例题]主机甲与主机乙之间已建立一个TCP连接,主机甲向主机乙发送了两个连续的TCP段,分别包含300字节和500字节的有效载荷,第一个段的序列号为200,主机乙正确接收到两个段后,发送给主机甲的确认序列号是
A.500 B.700 C.800 D.1000
[解析]确认序号即期待的下一个报文段的开始字节,由于主机乙正确接收了两个段,所以乙收到了200~999(200+800-1)字节,期待第1000字节,所以选择D选项。
粗实线为客户端三次握手与四次挥手的状态转移,粗虚线为服务器端三次握手与四次挥手的状态转移。
增加伪首部
以字节为单位的滑动窗口,大小可变。
接收窗口rwnd(recevier window):接收方根据接收缓存设置的值通知发送方,反映接收方容量。
拥塞窗口cwnd(congestion window):发送方根据自己估算的网络拥塞程度而设置的窗口值,反应网络当前容量。初始值IW根据SMSS(SENDER MAXIMUM SEGMENT SIZE,发送方最大报文段大小,不包含TCP头部)来设定。
发送窗口 = Min{接收窗口rwnd,拥塞窗口cwnd}
超时重传:TCP发送方在规定的时间(重传时间,使用重传计时器)内没有收到确认就要重传已发送的报文段。【重传时间:采用自适应算法,动态改变重传时间RTTs(加权平均往返时间)。RTT(round-trip time)为数据完全发送完(交给发送方IP层)到收到确认信号的时间。】
累计确认:收到多个确认一次。接收方“痴心不改”,例如,收到序号1、2、3、6的报文段,到了时间会发送确认序号ack=4的报文段,即使后续再收到了7、5,也依然会通知发送方“小老弟,我还是想要序号为4的报文段”。
超时重传解决的是传输可靠性问题,累计确认是为了更快速的传输。
冗余确认:TCP接收方已经发送过的确认报文段。由于收到了比确认报文段所期待的报文段序号更大的报文段,而未收到确认报文段所期待的报文段,则再次发送确认报文段。如前面的序号4。【冗余确认是原因可能是乱序到达和丢包,两次的话可能是乱序到达造成的,三次的话绝大部分是丢包了,需要快速重传。】
快速重传:TCP发送方在未达到重传时间时收到接收方的冗余确认(三个重复的)就要重传已发送的报文段。
目的:接收方希望发送方发慢一点,好来得及接收。
方法:利用滑动窗口机制实现流量控制。
在通信过程中,接收方根据自己接收缓存的大小,动态地调整发送方的发送窗口大小,即接收窗口rwnd(接收方设置确认报文段的窗口字段来将rwnd通知给发送方),发送方的发送窗口取接收窗口rwnd和拥塞窗口cwnd的最小值。
举例:
开始时,B通知A“老弟,我的接收窗口是400”。
注:填充黄色的为发送方A的发送窗口,绿色字体代表已发送的数据(接收方不一定接收,看ack)。
最后,B通知A接收窗口为0,这时,B可能在忙着给应用层传输数据。那么,什么时候A可以再给B发送数据呢?需要B再次给A发送一个报文段,通知A“老弟,你可以发送数据了,rwnd=xxx”。
但是,假如B发送的这个报文,A没有收到,这时A等待B通知,B等待A发送数据,就会死锁。为了解决这个问题,需要使用坚持计时器。
在发送方收到接收方的rwnd=0时,坚持计时器启动。坚持计时器到期,发送方发送零窗口探测报文段(仅1字节),询问接收方“老哥,你还不接收数据吗?”,如果B给A发送过了通知,这时接收到发送方的探测报文段,就知道它的通知丢失了,A没有收到,就会再发送通知,A接收到后继续发送数据,双方正常通信。
注:TCP规定,即使设置为零窗口,也必须接收以下几种报文段:零窗口探测报文段、确认报文段和携带紧急数据的报文段。
[例题]主机甲和主机乙之间已建立了一个TCP连接,TCP最大段长度为1000字节。若主机甲的当前拥塞窗口为4000字节,在主机甲向主机乙连续发送两个最大段后,成功收到主机乙发送的第一个段的确认段,确认段中通告的接收窗口大小为2000字节,则此时主机甲还可以向主机乙发送的最大字节数是
A.1000 B.2000 C.3000 D.4000
[解析]发送窗口 = Min{rwnd,cwnd} = Min{2000,4000}=2000。第一个1000字节已确认,窗口滑动,第一个1000字节不在发送窗口中。由于第二个1000字节没有被主机乙确认,可能丢失,存在需要重传的可能性,这1000字节还在发送窗口中,因此,还可以发送的最大字节数是2000-1000=1000字节。之后主机甲等待主机乙确认或超时重传。
出现条件:资源需求总和>可用资源。
目的:防止过多的数据注入到网络中。
假设:数据单方向传送,另一方向只传送确认,发送方接到确认后增加拥塞窗口大小。接收方有足够大的缓存空间,发送窗口取决于拥塞程度,即发送窗口=拥塞窗口。
为了讨论方便,纵坐标的单位为最大报文段,长度为MSS。横纵标单位为发送了一批报文段并收到他们确认的时间,即往返时延RTT。
下面的算法,可查看RFC 5681
ssthresh(slow start thresh):慢开始阈值,达到后“加法增大”。
乘法减小:新的ssthresh为上次拥塞状态下拥塞窗口的一半,RFC 5681中写的是ssthresh = max (FlightSize / 2, 2*SMSS),我们这里就直接采用FlightSize / 2了。
慢开始:起始值特别低,例如1,倍增,超时后回到起始值。
拥塞避免:达到ssthresh后,加法增大。达到网络拥塞状态后,拥塞窗口回归到慢开始状态即初始值。
开始值为1,未达到ssthresh前,执行慢开始算法,倍增,1、2、4、8、16;达到ssthresh,执行拥塞避免算法,加法增大,17、18、19、...、24,超时,新的ssthresh=24/2=12,回到1,后续类似。
快速重传:TCP发送方在未达到重传时间时收到接收方的冗余确认(三个重复的,加上之前收到的,共4个一样的ack)就要重传已发送的报文段。
快恢复:达到网络拥塞状态后,拥塞窗口不是回归到初始状态,而是从新的ssthresh(原来的减半)开始,加法增大。
开始值为1,未达到ssthresh前,执行慢开始算法,倍增,1、2、4、8、16;达到ssthresh,执行拥塞避免算法,加法增大,17、18、19、...、24,收到3个重复的确认,新的ssthresh=24/2=12,回到新的ssthresh即12,后续类似。
[例题] 一个TCP连接总是以1KB的最大段长发送TCP段,发送方有足够多的数据要发送。当拥塞窗口为16KB时发生了超时,如果接下来的4个RTT时间内的TCP段的传输都是成功的,那么当第四个RTT时间内发送的所有TCP段都得到肯定应答时,拥塞窗口大小是
A. 7KB B.8KB C.9KB D.16KB
[解析]由题可知是超时而不是收到3个重复确认,所以采用的是慢开始与拥塞避免算法。超时后进行下一轮,新的ssthresh=16/2=8。4个RTT拥塞窗口分别为1、2、4、8,此时达到新的ssthresh,所以拥塞窗口采用加法增大,应该是9KB,选择C选项。
[例题]主机甲和主机乙已建立了TCP连接,甲始终以MSS= 1KB大小的段发送数据,并一直有数据发送;乙每收到一个数据段都会发出一个接收窗口为10KB的确认段。若甲在t时刻发生超时时拥塞窗口为8KB,则从t时刻起,不再发生超时的情况下,经过10个RTT后,甲的发送窗口是
A.10KB B.12KB C.14KB D.15KB
[解析]超时后,新的ssthresh=8/2=4。拥塞窗口慢开始阶段变化为1、2、4,之后进入拥塞避免阶段,5、6、7、8、9、10、11,之后拥塞窗口是12。注意,题目问的是发送窗口,发送窗口大小为Min{10,12}=10KB,选择选项A。
比较项 | TCP | UDP |
面向连接 | 是 | 否 |
可靠/安全 | 是 | 否 |
速度 | 慢 | 快 |
面向 | 字节流 | 报文 |
应用范围 | 大量数据 | 少量数据 |
自己抓包,分析一下各个字段。以百度为例。
cmd下,ping命令,获取百度ip
打开wireshark,设置过滤器tcp&&ip,.addr==61.135.169.121,运行wireshark,浏览器百度一下lady_killer。
窗口扩大:03 03 08,占3个字节。
选择确认:04 02,占2个字节。
窗口扩大:03 03 05,占3个字节。
选择确认:04 02,占2个字节。
之后的数据传输涉及到TLS协议,不再本篇文章探讨内了。
自己实现TCP客户端和服务器端可以查看文章:python-网络编程之socket
《TCP/IP详解卷1》第17-24章
《计算机网络(谢希仁)第七版》5.3-5.9
RFC 5681
RFC 2581
(传输层)TCP协议 - kzangv - 博客园