TCP协议——精选推荐

TCP协议——精选推荐

2023年7月23日发(作者:)

TCP协议1、TCP服务特点TCP协议特点:⾯向连接、字节流和可靠传输。通信双⽅必须先建⽴连接,才能开始数据读写,双⽅必须为该连接分配必要的内核资源,以管理连接的状态和连接上数据的传输。TCP连接时全双⼯的,即双⽅的数据可以通过⼀个连接进⾏。完成数据交换之后吗,通信双必须断开连接以释放系统资源。TCP协议连接为⼀对⼀⽅式,因此基于⼴播和多播(⽬标是多个主机地址)的应⽤程序不能使⽤TCP服务。⽽⽆连接协议的UDP则⾮常适合于⼴播和多播。字节流服务和数据报服务的区别:这种区别对应到实际变成中,则体现为通信双⽅释放必须执⾏相同次数的读、写操作(注意,只是表现形式)。当发送端应⽤程序连续执⾏多次写操作时,TCP模块先将这些数据放⼊TCP发送缓冲区中。当TCP模块真正开始发送数据时,发送缓冲区中的这些等待发送的数据被封装为⼀个或多个TCP报⽂发出。所以,TCP模块发送的TCP报⽂的个数和应⽤程序执⾏的写操作次数之间没有固定的数量关系。接收端收到⼀个或多个TCP报⽂段后吗,TCP模块将它们携带的应⽤程序数据安装TCP报⽂端的序号依次放⼊TCP接收缓冲区,滨⽥通知应⽤程序读取数据。接收端应⽤程序可以⼀次性将TCP接收缓冲区的数据全部读出,也可以分多次读取(取决于拥有指定的应⽤程序缓冲区⼤⼩)。因此,应⽤程序执⾏的读操作次数和TCP模块接收到的TCP报⽂段个数之间也没有固定的数量关系。综上所述,得出结论:相对TCP⽽⾔,发送端执⾏的写操作次数和接收端执⾏的读操作次数之间没有任何数量关系,这就是字节流的概念,即应⽤程序对数据的发送和接收时没有边界限制的。相对UDP⽽⾔,发送端应⽤程序每执⾏⼀个UDP数据报执⾏⼀次写操作,UDP模块就将其封装成⼀个UDP数据报并外对端发送。接收端必须及时针对每⼀个UDP数据报执⾏读操作,否则就会丢包(经常发⽣在较慢的服务器上)。并且,如果⽤户没有指定⾜够的应⽤程序缓存区来读取UDP数据,则UDP数据将被截断。这就是数据报的概念。下图显⽰了TCP字节流服务和UDP数据报服务的区别:1)TCP字节流服务2)UDP数据报服务TCP协议采⽤发送应答机制,即发送端发送的每个TCP报⽂段读必须得到接收⽅的应答,才认为这个TCP报⽂段传输成功。其次,TCP协议采⽤超时重传机制,发送端在发送出⼀个TCP报⽂段之后启动定时器,如果在规定时间内未收到应答,则重发该报⽂段。最后,因为TCP报⽂段最终还是⽤IP数据报发送的,⽽IP数据报到达接收端可能乱序、重复,所以TCP协议还想对接收的TCP报⽂重排、整理,再交付给应⽤层。从⽽保证TCP传输的可靠性。UDP协议与IP协议类似,提供不可靠服务。它们都需要上层协议来处理数据确认和超时重传。2、TCP头部结构2.1 TCP固定头部结构TCP头部信息出现在每个TCP报⽂段中,⽤于指定通信的源端⼝和⽬的端⼝,管理TCP连接等。16位端⼝号(port number):告知主机该报⽂来⾃哪⾥,以及传给哪个上层协议或应⽤程序(⽬的端⼝号)。进⾏TCP通信时,客户端通常使⽤系统⾃动选择的临时端⼝号,⽽服务器则使⽤指明的服务端⼝号。所以知名服务端⼝号定义在/etc/services⽂件中。32位序号(sequence number):⼀次TCP通信(从TCP连接建⽴到断开)过程中某个传输⽅向上的字节流的每个字节的编号(即该次传输报⽂段所携带数据的第⼀个字节在整个字节流中的偏移),另⼀个传输⽅向上的TCP报⽂段的序号值也具有相同的含义。32位确认号(acknowledgement number):⽤作对另⼀端发送来的TCP报⽂段的响应。其值是收到的报⽂段的序号值加1。4位头部长度(header length):标志TCP头部有多少个32bit字(4字节),因为4位最⼤能表⽰15,所以TCP头部最长是60字节。6位标志位:URG:表⽰紧急指针(urgent pointer)是否有效。ACK:表⽰确认号是否有效。我们称携带ACK标志的TCP报⽂段位确认报⽂段。PSH:提⽰接收端应⽤程序应该⽴即从TCP接收缓冲区中取⾛数据,为接收后续数据腾出空间。RST:表⽰要求对⽅重新建⽴连接。我们称携带RST标志的TCP报⽂为复位报⽂段。SYN:表⽰请求建⽴⼀个连接。我们称携带SYN标志的TCP报⽂段为同步报⽂段。FIN:表⽰通知对⽅本端要关闭连接。我们称携带FIN标志的TCP报⽂段为结束报⽂段。16为窗⼝⼤⼩(windows size):TCP流量控制的⼀个⼿段。这⾥的窗⼝指接收通过窗⼝(Receive Window,RWND)。它告知对⽅本端的TCP接收缓冲区还能容纳多少字节的数据,这样对⽅就可以控制发送数据的速度。16位校验和(TCP cheksum):由发送端填充,接收端对TCP报⽂执⾏CRC算法检验TCP报⽂段在传输过程中是否损坏。这个校验包括TCP头部和数据部分。因此,这也是TCP可靠传输的⼀个重要保障。16位紧急指针(urgent pointer):⼀个正的偏移量。它和序号段的值相加表⽰最后⼀个紧急数的的下⼀个字节的序号。这个字段是紧急指针相对当前序号的偏移。TCP的紧急指针是发送端向接收端发送紧急数据的⽅法。2.2 TCP 头部选项TCP头部的最后⼀个选项字段(options)是可变长的可选信息。这部分最多包含40字节,因为TCP头部最长是60字节(其中还包含前⾯讨论的20字节的固定部分),典型的TCP头部选项结构如下图:第⼀个字段kind说明选项类型。有的TCP选项没有后⾯两个字段,仅包含1字节kind字段。第⼆个字段length(如果有的话)指定该选项总长度,该长度包括kind字段和length字段占据的2个字节。第三个字段info(如果有的话)是选项的具体信息。常见的TCP选项有7种:kind=0 选项表结束选项。kind=1空操作(nop)选项,没有特殊含义,⼀般⽤于将TCP选项的总长度填充位4字节的整数倍。kind=2最⼤报⽂段长度选项。TCP连接初始化时,通信双⽅使⽤该选项来协商最⼤报⽂段长度(Max Segment Size,MSS)。TCP模块通常将MSS设置为(MTU-40)字节(即减掉20字节TCP头部和20字节IP头部)。此时携带TCP报⽂段的IP数据报长度就不会超过MTU(假设TCP头部和IP头部都不包含选项字段,并且这也是⼀般情况),从⽽避免本机发⽣IP分⽚。对以太⽹⽽⾔,MSS值是1460(1500-40)字节。kind=3为窗⼝扩⼤因⼦选项。TCP连接初始化时,通信双⽅使⽤该选项来协商接收通告窗⼝的扩⼤因⼦。在TCP头部种,接收通告窗⼝⼤⼩是16位表⽰的,故最⼤为65535字节,但实际上TCP模块允许的接收通告窗⼝⼤⼩运不知这个数(为了提⾼TCP通信的吞吐量)。窗⼝扩⼤因⼦解决了这个问题。假设TCP头部中接收通告窗⼝⼤⼩是N,窗⼝扩⼤因⼦(移位数)是M,那么TCP报⽂段的实际接收通告⼤⼩是N乘2的M次⽅,或者说N左移M位。注意,M的取值范围是0~14。可以通过修改/proc/sys/net/ipv4/tcp_window_scaling内核变量来启⽤或者关闭窗⼝扩⼤因⼦选项。与MSS选项⼀样,窗⼝扩⼤因⼦选项只能出现在同步报⽂段中,否则将被忽略。但同步报⽂段本⾝不执⾏窗⼝扩⼤操作,即同步报⽂段头部的接收通告窗⼝是该TCP报⽂段的实际接收通告窗⼝的⼤⼩。当连接建⽴好之后,每个数据传输⽅向的窗⼝扩⼤因⼦就固定不变了。kind=4为选择性确认(Selectvie Acknowledgment,SACK)选项。TCP通信时,如果某个IP报⽂段丢失,则TCP模块会重传最后被确认的TCP报⽂段后续所有的报⽂段,这样原先以及正确传输的TCP报⽂段也可能重复发送,从⽽降低TCP性能。SACK技术正好为改善这种情况⽽产⽣的,它使TCP模块值重发丢失的TCP报⽂段,不⽤发送所有未被确认的TCP报⽂段。选择性确认选项⽤在连接初始化时,表⽰是否⽀持SACK技术。可以通过修改/proc/sys/net/ipv4/tcp_sack内核变量来启动或关闭选择性确认选项。kind=5为SACK实际⼯作的选项。该选项的参数告诉发送⽅本端已经收到并缓存的不连续的数据块,从⽽让发送端可以据此检测并充分丢失的数据库。每个块边沿(edge of block)参数包含⼀个4字节的序号。其中块左边沿表⽰不连续的垮的第⼀个数据的序号,⽽块右边沿不连续块的最后⼀个数据的序号的下⼀个序号。这样对参数之间的数据时没有收到的。因为每个垮信息占⽤8字节,所有TCP头部选项中实际上最多可包含4个这样的不连续数据块(考虑选项类型和长度占⽤的2字节)。kind=8是时间戳选项。该选项提供了较为准确的计算通信双⽅之间的回路时间(Round Trip Time,RTT)的⽅法,从⽽为TCP流量控制提供重要信息。可以通过修改/proc/sys/net/ipv4/tcp_timestamps内核变量来启动或关闭时间戳选项。3、TCP连接的建⽴与关闭 3.1 TCP连接的建⽴与关闭在192.168.6.16抓取TCP连接过程中客户端和服务器交换的TCP报⽂。命令如下: sudo tcpdump -i enp2s0 -nt '(src 192.168.6.16 and dst 192.168.6.180) or (src 192.168.6.180 and dst 192.168.6.16)'在192.168.6.180 上进⾏远程登录,操作过程如下:执⾏telnet命令并在两台通信主机之间建⽴TCP连接后,输⼊quit退出telnet客户端程序,从⽽结束TCP连接。整个过程中(从连接建⽴到结束)tcpdump输出内容如下所⽰:IP 192.168.6.180.46850 > 192.168.6.16.80: Flags [S], seq 689215479, win 64240, options [mss 1460,sackOK,TS val 3022028385 ecr 0,nop,wscale 7], length 0IP 192.168.6.16.80 > 192.168.6.180.46850: Flags [S.], seq 1208482781, ack 689215480, win 65160, options [mss 1460,sackOK,TS val 1179665075 ecr 3022028385,nop,wscale 7], length 0IP 192.168.6.180.46850 > 192.168.6.16.80: Flags [.], ack 1, win 502, options [nop,nop,TS val 3022028386 ecr 1179665075], length 0IP 192.168.6.180.46850 > 192.168.6.16.80: Flags [F.], seq 1, ack 1, win 502, options [nop,nop,TS val 3022040881 ecr 1179665075], length 0IP 192.168.6.16.80 > 192.168.6.180.46850: Flags [F.], seq 1, ack 2, win 510, options [nop,nop,TS val 1179677566 ecr 3022040881], length 0IP 192.168.6.180.46850 > 192.168.6.16.80: Flags [.], ack 2, win 502, options [nop,nop,TS val 3022040882 ecr 1179677566], length 0因为整个过程并没有发送应⽤层数据的交换,所以TCP报⽂的数据部分长度(length)总是0。TCP连接建⽴:第1个TCP报⽂段包含SYN标志,因此它是⼀个同步报⽂段,即180向16发起连接请求。同时该同步报⽂包含⼀个ISN值为689215479的序号。第2个TCP报⽂也是同步报⽂段,表⽰16同意与180建⽴连接,同时发送⾃⼰的ISN值为1208482781的序号,并对第1个报⽂段进⾏确认。确认值是689215480,即第1个报⽂段的序号值加1。前⾯说过,序号之是⽤来标志TCP数据流中每个字节的,但同步报⽂段⽐较特殊,即使它并没有携带任何应⽤程序数据,它要要占⽤1个序号值。第3个TCP报⽂是对180对第2个同步报⽂段的确认。⾄此,TCP连接就建⽴起来了。建⽴TCP连接的这三个步骤称为TCP三次握⼿。TCP连接关闭:第4个报⽂段包含FIN标志,因此是⼀个结束报⽂,即180请求关闭报⽂段。结束报⽂和同步报⽂⼀样,也要占⽤⼀个序号值。第5个报⽂包含FIN标志,也是⼀个结束报⽂,即16发给180的结束报⽂段。(如果有TCP的延迟确认特性,在第5个报⽂发送前,16会先发⼀个确认结束的报⽂)第6个报⽂是180确认确认报⽂段。在连接的关闭过程中,因为180先发送结束报⽂段(telnet客户端主动退出),故称180执⾏主动关闭,⽽称16执⾏被动关闭。整个过程如下图所⽰:3.2 半关闭状态TCP连接是全双⼯的,所以它允许两个⽅向的数据传输被独⽴关闭,即通信的⼀端可以发送结束报⽂段给对端,告诉它本端已经完成了数据的发送,但允许继续接收来⾃对端的数据,直到对端也发送结束报⽂段以关闭连接。TCP连接的这种状态称为半关闭(half close)状态。服务器和客户端应⽤程序判断对⽅是否已经关闭连接的⽅法是:read系统调⽤返回0(收到结束报⽂段)。同时,Linux还提供其他检测连接是否被对⽅关闭的⽅法。3.3 连接超时上⼀节中讨论的是很快建⽴连接的情况,如果客户端访问⼀个距离很远的服务器,或者由于⽹络繁忙,导致服务器对于客户端发送出的同步报⽂没有应答,此时客户端程序必须进⾏重连,如果重连失败,则通知应⽤程序连接超时。重连次数由/proc/sys/net/ipv4/tcp_syn_retries内核变量所定义。每次重连的超时时间都增加⼀倍。抓包指令:sudo tcpdump -n -i enp2s0 port 23登录指令:date;telnet 192.168.6.16;date4、 TCP状态转移TCP连接的任意⼀端在任何时刻都处于某种状态,当前状态可以通过netstat命令查看。本节讨论TCP连接从建⽴到关闭的整个过程中通信两端状态的变化,虚线表⽰典型的服务器端的连接状态转移;粗线线表⽰典型客户端连接状态转移。CLOSED是⼀个假想的起始点,并不是⼀个实际的状态。 4.1 TCP状态转移总图服务器端状态转移过程

服务器通过listen系统调⽤,进⼊LISTEN状态,被动等待客户端连接。服务器⼀旦监听到某个连接请求(收到同步报⽂段),就将该连接放⼊内核等待队列中,并向客户端发送带SYN标志的确认报⽂段。此时该连接处于SYN_RCVD状态。如果服务器成功接收到客户端发送回的确认报⽂段,则该连接转移到ESTABLISHED状态。ESTABLISHED状态是连接双⽅能够进⾏双向数据传输的状态。当客户端主动关闭连接时(通过close或shutdown系统调⽤向服务器发送结束报⽂段),服务器通过返回确认报⽂段使连接进⼊CLOSE_WAIT状态,表⽰等待服务器应⽤程序关闭连接。通常,服务器检测到客户端关闭连接后,也会⽴即给客户端发送⼀个结束报⽂段来关闭连接。这将使连接转移到LAST_ACK状态,以等待客户端对结束报⽂段的最后⼀次确认。⼀旦确认完成,连接就彻底关闭了。客户端状态转移过程客户端通过connect系统调⽤主动与服务器建⽴连接。connect系统调⽤⾸先给服务器发送⼀个同步报⽂段,使连接转移到SYN_SENT状态。此后,connect系统调⽤可以因为如下两个原因失败返回:connect连接⽬标断开不存在(未被任何进程监听),或者断开仍处于TIME_WAIT状态的连接所占⽤,则服务器将给客户端发送⼀个复位报⽂段,connect调⽤失败。如果⽬标端⼝存在,但connect在超时时间内未收到服务器的确认报⽂段,则connect调⽤失败。connect调⽤失败将使连接⽴即返回到初始的CLOSED状态。如果客户端成功收到服务器的同步报⽂段和确认,则connect调⽤成功,连接转移⾄ESTABLISHED状态。客户端主动关闭时,它向服务器发送⼀个结束报⽂段,同时连接进来FIN_WAIT_1状态。若此时客户端收到服务器专门⽤于确认⽬的的确认报⽂段,则连接转移⾄FIN_WAIT_2状态。当客户端处于FIN_WAIT_2状态时,服务器处于CLOSE_WAIT状态,这⼀对状态是可能发⽣的半关闭的状态。此时如果服务器也关闭连接(发送结束报⽂段),则客户端将给予确认并进⼊TIME_WAIT状态。另外,上图中还给出了客户端从FIN_WAIT_1状态直接进⼊TIME_WAIT状态的⼀条路线(不经过FIN_WAIT_2状态),前提是处于FIN_WAIT_1状态的客户端直接收到带确认的结束报⽂段(⽽不是先收到确认报⽂段,再收到结束报⽂段) 处于FIN_WAIT_2状态的客户端需要等待服务器发送结束报⽂段,才能转移⾄TIME_WAIT状态,否则它将⼀直停留再这个状态。如果不是为了再半关闭状态下继续收数据,连接长时间地停留在FIN_WAIT_2状态并⽆益处。连接停留在FIN_WAIT_2状态的情况可能发⽣在:客户端执⾏半关闭后,未等服务器关闭连接就强⾏退出了。此时客户端连接由内核来接管,可称之为孤⼉连接。Linux为了防⽌孤⼉连接长时间停留在内核中,定义了两个内核变量:/proc/sys/net/ipv4/tcp_max_orphans和/procs/sys/net/ipv4/tcp_fin_timeout。前者指定内核能接管的孤⼉连接数⽬,后者指定孤⼉连接在内核中⽣存的时间。TCP连接建⽴和断开过程中客户端和服务器端状态变化如下图所⽰:、4.2 TIME_WAIT状态客户端连接在收到服务器的结束报⽂段之后,并没有直接进⼊CLOSED状态,⽽是转移到TIME_WAIT状态。在这个状态,客户端连接要等待⼀段长为2MSL(Maximum Segment Life,报⽂段最⼤⽣存时间)的时间,才能完全关闭。MSL是TCP报⽂段在⽹络中的最⼤⽣存时间,标准⽂档RFC1122的建议值是2min。 TIME_WAIT状态存在的原因有两点:可靠的终⽌TCP连接。保持迟来的TCP报⽂段有⾜够的时间被识别并丢弃。第⼀个原因:假设⽤于确认服务器结束报⽂段6的TCP报⽂7丢失,那么服务器将重发结束报⽂段。因此客户端需要停留在某个状态以处理重复收到的结束报⽂段(即向服务器发送确认报⽂段)。否则客户端将以复位报⽂段来回应服务器,服务器则认为这是⼀个错误,因为它期望的是⼀个向TCP报⽂段7那样的确认报⽂段。在Linux系统上,⼀个TCP断开不能被同时打开多次(两次及以上)。当⼀个TCP连接处于TIME_WAIT状态时,我们将⽆法⽴即使⽤该连接占⽤着的端⼝来建⽴⼀个新连接。相反,如果不存在TIME_WAIT状态,则应⽤程序能够理解建⽴⼀个和刚关闭的连接类似的连接(这⾥说的类似,是指他们具有相同的IP地址和端⼝号)。这个新的和原来相似的连接被称为原来的连接的化⾝。新的化⾝可能接收到属于原来的连接的、携带应⽤程序数据的TCP报⽂段(迟到的报⽂段),这显然是不应该发⽣的。这就是TIME_WAIT存在的第⼆个原因。 有时应⽤程序希望避免使⽤TIME_WAIT状态。因为当程序退出以后,我们希望⽴即重启它。但由于处在TIME_WAIT状态的连接还占⽤着端⼝,程序将⽆法启动(直到2MSL超时结束)。对于客户端程序来说,通常不⽤担⼼上⾯描述的重启问题,因为客户端⼀般使⽤系统⾃动分配的临时客户端号码来建⽴连接,⽽由于随机性,临时端⼝号⼀般和程序上⼀次使⽤的端⼝号不同。所以客户端程序⼀般可以⽴即重启。但如果是服务器主动关闭连接后异常终⽌,则因为它总是使⽤⼀个知名的服务端⼝号,连夜连接的TIME_WAIT状态将导致它不能⽴即重启。不过,我们可以通过Socket选项SQ_REUSERADDR来强制进⾏⽴即使⽤处于TIME_WAIT状态的连接占⽤的端⼝。5、复位报⽂段在某些特殊条件下,TCP连接的⼀端会向另⼀端发送携带RST标志的报⽂段,即复位报⽂段,以通知对⽅关闭连接或重新建⽴连接。5.1 访问不存在的端⼝当客户端程序访问⼀个不存在的端⼝是,⽬标主机将给它发送⼀个复位报⽂段。⽐如16上执⾏telent命令登录180上⼀个不存在的端⼝54321,并⽤tcpdump抓取该过程中两台主机的TCP报⽂段。具体操作如下://180上执⾏sudo tcpdump -nt -i enp1s0 port 54321//16上执⾏telnet 192.168.6.180 5432116上的telnet程序输出显⽰连接被拒绝,因为这个端⼝不存在。180上tcpdump抓取到的TCP报⽂段内容如下:由此可见,180针对16的连接请求(同步报⽂段)回应了⼀个复位报⽂段(tcpdump输出R标志)。因为复位报⽂段的接收通告窗⼝⼤⼩为0,所以可以预见:收到复位报⽂段的⼀端应该关闭连接或者重新连接,⽽不能回应这个复位报⽂段。实现上,当客户端程序向服务器的某个端⼝发起连接,⽽该端⼝仍被处于TIME_WAIT状态的连接所占⽤时,客户端程序也将收到复位报⽂段。5.2 异常连接终⽌前⾯讨论的连接终⽌⽅式都是正常终⽌⽅式:数据交换完成之后,⼀⽅给另⼀⽅发送结束报⽂段。⽽TCP提供异常终⽌连接的⽅法,即给对⽅发送⼀个复位报⽂段。⼀旦发送了复位报⽂段,发送端所有排队等待发送的数据都将被丢弃。应⽤程序可以使⽤socket选项SO_LINGER来发送复位报⽂段,以异常终⽌⼀个连接。5.3 处理半打开的连接服务器(或客户端)关闭或者异常终⽌了连接,⽽对⽅没有收到结束报⽂(⽐如⽹络发⽣了故障),此时,客户端(或者服务器)还维持着原来的连接,⽽服务器(或客户端)即使重启,也已经没有该连接的任何信息了。我们称这种状态为半打开的状态,处于这种状态的连接称为半打开连接。如果客户端(服务器)往处于半打开状态的连接写⼊数据,则对⽅将回复⼀个复位报⽂段。//109上运⾏服务器程序nc -l 12345sudo tcpdump -nt -i enp1s0 port 12345//16上执⾏ telnettelnet 192.168.0.109 1234516远程登录后,拔掉109⽹线,并重启服务器。在后在16上向半连接状态的连接写⼊字符a。tcpdump抓取到的TCP报⽂段如下:输出内容中,前3个TCP报⽂是正常的TCP连接的3此握⼿过程。第4个TCP报⽂段由客户端发送给服务器,它携带了3个字节应⽤程序数据,这3个字节依次是字母”a“,回车符”r“,换⾏符”n“。不过因为服务器程序已经被断开,所有109对客户端发送的数据库回应了⼀个复位报⽂段5。6、TCP交互数据流TCP报⽂段所携带的应⽤程序数据按照长度分为两种:交互数据成块数据交互数据包含很少的字节。使⽤交互数据的应⽤程序(或协议)对实时性要求⾼,⽐如telnet、ssh等。成块数据的长度则通常为TCP报⽂段允许的最⼤数据长度。使⽤成块数据的应⽤程序(或协议)对传输效率要求较⾼,⽐如ftp。⽐如在本机执⾏telent登录到本机,然后再shell命令提⽰符后执⾏ls命令。同时使⽤tcpdump抓取这⼀过程中telnet客户端和telnet服务器的TCP报⽂段。具体操作如下:tcpdump -nt -i lo port 23

telnet 127.0.0.1//登录后输⼊ls ls (回车)tcpdump 输出如下:TCP报⽂段1由客户端发给服务器,它携带1个字节的应⽤程序数据,即字母"l"。TCP报⽂段2是服务器对报⽂段1的确认。同时回显字母"l"。TCP报⽂段3是客户端对TCP报⽂段2的确认。第4~6个报⽂段是针对字母"s"的上述过程。TCP报⽂段7传送的2字节数据分别是:客户端键⼊的回车符和流结束符(EOF,本例中是0x00)。TCP报⽂段8携带服务器返回的客户端查询的⽬录的内容(ls命令输出),包括该⽬录下的⽂件的名字及其显⽰控制参数。TCP报⽂段9是客户端对TCP报⽂段8的确认。TCP报⽂段10是携带的也是服务器返回给客户端的数据,包括⼀个回车符、⼀个换⾏符、客户端登录⽤户的PS1环境变量(第⼀级命令提⽰符)。TCP报⽂段11是对客户 端的TCP报⽂段10的确认。在上述过程中,客户端争对服务器返回的数据所发送的确认报⽂段(TCP报⽂段6、9、11)都不携带任何应⽤程序数据(长度为0),⽽服务器每次发送的确认报⽂段(TCP报⽂段2、5、8、10)都包含它需要发送的应⽤程序数据。服务器的这种处理⽅式称为延迟确认,即它不马上确认上次收到的数据,⽽是在⼀端延迟时间后查看本端是否由数据需要发送,如果有,则和确认信息⼀起发出。因为服务器对客户端请求处理的很快,所有发送确认报⽂段的时候总是有数据⼀起发送。延迟确认可以减少发送TCP报⽂段的数量。⽽由于⽤户的输⼊书店明显慢于客户端程序的处理速度,所以客户端的确认报⽂段总是不携带任何应⽤数据。注意:在TCP连接的建⽴和断开过程中,也可能发⽣延迟确认。上述例⼦在本端回路运⾏的结果,在局域⽹中也能得到基本相同的结果,但在⼴域⽹中就未必如此了。⼴域⽹上的交互数据流可能经受很⼤的延迟,并且携带交互数据的微⼩TCP报⽂段数量⼀般很多(⼀个按键输⼊就导致⼀个TCP报⽂段),这些因素都可能导致拥塞发⽣。⽽解决该问题的⼀个简单有效的⽅法是使⽤Nagle算法。Nagle算法要求⼀个TCP连接的通信双⽅在任意时刻都最多只能发送⼀个未确认的TCP报⽂段,在该TCP报⽂段的确认到达之前不能发送其他TCP报⽂段。另⼀⽅⾯,发送⽅在等待确认的同时收集本端需要发送的微量数据,并在确认到来时以⼀个TCP报⽂段将它们全部发出。这样极⼤减少了⽹络上的微⼩TCP报⽂段的数量。该算法的另⼀个有点在于其⾃适应性:确认到达得越快,数据也就发送得越快。7、TCP成块数据流FTP协议传输⼤⽂件时,则采⽤TCP成块数据流进⾏传输。操作过程:tcpdump -nt -i enp1s0 port 20ftp 127.0.0.1ftp>get bigfile(回车) #获取⼤⽂件上述过程,tcpdump输出如下:注意,客户端发送的最后两个TCP报⽂段17和18,它们分别是对TCP报⽂段2和16的确认(从序号值和确认值来判断)。由此可见,当传输⼤量⼤数据块的时候,发送⽅会连续发送多个TCP报⽂段,接收⽅可以⼀次确认所有这些报⽂段。那么发送⽅在收到上⼀次确认后能连续发送多个TCP报⽂段呢?这是由接收通告窗⼝(还需考虑拥塞窗⼝)的⼤⼩决定。TCP报⽂段17说明客户端还能接收30084*64字节(本例中窗⼝扩⼤因⼦为6)。⽽在TCP报⽂段18中,接收通告⼤⼩为27317*64字节,即客户端能接收的数据量变⼩了。这表明客户端的TCP接收缓冲区还有更多的数据未被应⽤程序读取⽽停留在其中,这些数据都来⾃TCP报⽂段3~16中的⼀部分。服务器在收到TCP报⽂段18后,它⾄少还能连续发送尾部确认的报⽂段数量是106个(但⼀般不会连续发这么多i)。其中16384是成块数据的长度(见TCP报⽂段1~16的lengh值),和显然它⼩于但解决MSS规定的16396字节。注意,服务器没发送4个TCP报⽂段就传送⼀个PSH标志给客户端,⽤于通知客户端的应⽤程序尽快读取数据。不过,这对于服务器来说显然不是必需。8、带外数据有些传输层协议具有带外(Out Of Band,OOB)数据的概念,⽤于迅速通告对⽅本端发⽣的重要事件。因此,带外数据⽐普通数据(也称带内数据)有更⾼的优先级,它应该⽴即被发送,⽽不论发送缓冲区 中是否有排队等待发送的普通数据。带外数据的传输可以使⽤⼀条独⽴的传输层连接,也可以映射到传输普通数据的连接中。实际应⽤中,带外数据的使⽤很少见,已知的仅有telent、ftp等远程⾮活跃程序。UDP没有实现带外数据传输,TCP也没有真正的带外数据库。不过TCP利⽤其头部中的紧急指针标志和紧急指针两个字段,给应⽤程序提供⼀种紧急⽅式。TCP紧急⽅式利⽤传输普通数据的连接来传输紧急数据。这种紧急数据的含义和带外数据类似,因此也将TCP 紧急数据称为带外数据。 TCP发送外带数据的过程:假设⼀个进程已经往某个TCP连接的发送缓冲区写⼊了N字节普通数据,并等待发送。在数据被发送钱,该进程⼜向该连接写⼊了3个字节的带外数据“abc”,此时,待发送的TCP报⽂段的头部将被设置URG标志,并且紧急指针被设置为指向最后⼀个带外数据的下⼀字节(进⼀步减去当前TCP报⽂段的序号值得到其头部中的紧急偏移值)。发送端⼀次发送的多个字节的带外数据之哟u最后⼀个字节被当作带外数据(字母c),⽽其他数据(字母a和b)被当成了普通数据。如果TCP模块以多个TCP报⽂段来发送上图所⽰TCP发送缓冲区的内容,则每个TCP报⽂段都将设置URG标志,并且它们的紧急指针指向同⼀个位置(数据流中带外数据的下⼀个位置),但只有⼀个TCP报⽂段真正携带带外数据。TCP接收带外数据的过程:TCP接收端只有在收到紧急指针标志时才检查紧急指针,然后根据紧急指针所指的位置确定带外数据的位置,并将它读⼊⼀个特殊的缓存中,这个缓存只有1字节,称为带外缓存。如果上层应⽤程序没有及时将带外数据从带外缓存中读取出,则后续的带外数据将覆盖它。如果TCP连接设置了SO_OOBINLINE选项,则带外数据将和普通数据⼀样被TCP模块存放在TCP接收缓冲区中。此时应⽤程序需要像读取普通数据⼀样来读取带外数据。那么这种情况下如何区分带外数据和普通数据呢?显然,紧急指针可以⽤来指出带外数据的位置,socket编程接⼝也提供了系统调⽤来识别带外数据。9、TCP超时重传在异常⽹络状况下(开始出现超时或丢包),TCP能控制数据传输以保证可靠服务。TCP服务必须能够重传超时时间内未收到确认的TCP报⽂段。为此,TCP模块未每个TCP报⽂段都维护⼀个重传定时器,该定时器在TCP报⽂段第⼀次被发送时启动。如果超时时间内未收到接收⽅的应答,TCP模块将重传TCP报⽂段并重置定时器。⾄于下次重传的超时时间如何选择,以及最多指向多少次重传,就是TCP的重传策略。实验过程:#109上执⾏sudo tcpdump -n -i eth0 port 5001iperf -s #108上执⾏#109上执⾏telent 192.168.1.108 5001Trying 192.168.1.108 ...connected to character is '^]'.1234 #发送完之后断开服务器⽹线。12Connection closed by foreign hostiperf是⼀个测量⽹络状况的⼯具,-s 选项表⽰其作为服务器运⾏。iperf默认监听5001端⼝,并丢弃该端⼝上收到的所有数据。相当于⼀个discard服务器。上述操作过程tcpdump输出如下:TCP报⽂段1~3是三次握⼿建⽴连接的过程。TCP报⽂段4~5是客户端发送数据“1234”(应⽤程序数据长度为6,包括回车、换⾏两个字符)及服务器确认的过程。TCP报⽂段6是客户端第⼀次发送数据"12"的过程。TCP报⽂段7~11报⽂段是因为服务器的⽹线被断开,所以在客户端⽆法收到TCP报⽂段6的确认报⽂段后,执⾏了5此重传(可以从TCP报⽂段序号得知)。数据包12~23都是ARP模块输出内容。即从109查询108的MAC地址。从tcpdump输出的时间戳,可以看到TCP报⽂段6~11被发送的时间间隔分别为0.2s、0.4s、0.8s、1.6s、3.2s。可见TCP⼀共执⾏5次重传,每次重传超时时间都增加⼀倍。在5次重传均失败的情况下,底层的IP和ARP开始接管,直到telent客户端放弃连接为⽌。 Linux有连个重要内核参数与TCP超时重传相关:/proc/sys/net/ipv4/tcp_retries1和/pro/sys/net/ipv4/tcp_retries2。前者指定在底层IP接管之前TCP最少执⾏的重传次数,默认值是3.后者指定连接放弃前TCP最多可以执⾏重传次数,默认值是15(⼀般对于13~30min)。在上述实例中,超时重传发送了5次,连接坚持时间是15分钟。10、拥塞控制10.1拥塞控制简介TCP模块还有⼀个重要任务,即提⾼⽹络利⽤率,降低丢包率,并保证⽹络资源对每条数据流的公平性。这就是所谓的拥塞控制。TCP拥塞控制的标志⽂档是RFC 5681,其中详细介绍了拥塞控制的四个部分:慢启动(slow start)拥塞避免(congestion avoidance)快速重传(fast retransmit)快速回复(fast recovery)拥塞控制算法在Linux下有多种实现,⽐如reno算法、vegas算法和cubic算法等。它们部分或者全部实现了上述四个部分。/proc/sys/net/ipv4/tcp_congestion_control⽂件指⽰机器当前所使⽤的拥塞算法。拥塞控制的最终受控变量是发送端向⽹络⼀次连续写⼊(收到其中第⼀个数据的确认之前)的数据量,我们称为SWND(send window,发送窗⼝)。不过,发送端最终以TCP报⽂段来发送数据,所以SWND限定了发送端能连续发送的TCP报⽂段数量。这些TCP报⽂段的最⼤长度(仅指数据部分)称为SMSS(Sender Maximun Segment Size,发送者对打段⼤⼩),其指⼀般等于MSS。发送端需要合理地选择SWND的⼤⼩,如果SWND太⼩,会引起明显的⽹络延迟;反之,如果SWND太⼤,则容易导致⽹络拥塞。接收⽅可以通过其接收通告窗⼝(RWND)来控制发送端的SWND。但这显然不够,所以发送端引⼊了⼀个称为拥塞窗⼝(CongestionWindow,CWND)的状态变量。实际的SWND值是RWND和CWND中较⼩者。下图显⽰了拥塞控制的输⼊和输出(可见,它是⼀个闭环反馈控制)。10.2 慢启动和拥塞避免TCP连接建⽴好之后,CWND将被设置成初始值IW(Initial Windows),其⼤⼩为2~4个SMMS。但新的Linux内核提⾼了该初始值,以减⼩传输滞后。此时发送端最多能发送IW字节的数据。此番发送端每收到接收端的⼀个确认,其CWND就按照如下公式增加:CWND +=min(N,SMMS) 其中N是此次确认中包含的之前未被确认的字节数。这样⼀来,CWND按照指数形式扩⼤,这就是所谓的慢启动。慢启动的理由是,TCP模块刚开始发送数据时并不知道⽹络的实际情况,需要⽤⼀种试探的⽅式平滑的增加CWND的⼤⼩。但如果不施加其他⼿段,慢启动必然使得CWND很快膨胀,并最终导致⽹络拥塞。因此TCP拥塞控制中定义了另⼀个重要的状态变量:慢启动门限(slowstart threshold size,ssthresh)。当CWND的⼤⼩超过该值时,TCP拥塞控制将进⼊拥塞避免阶段。拥塞避免算法使得CWND按照线性⽅式增加,从⽽减缓其扩⼤。RFC 5681中提到了如下两种实现⽅式:每个RTT时间内按照上述公式计算新的CWND,⽽不论该RTT时间内发送端收到多少个确认。每收到⼀个对新数据的确认报⽂段,按照如下公式更新CWND。CWND +=SMSS*SMSS/CWND下图粗略地描述了慢启动和拥塞避免发⽣的时机和区别。图中,为例讨论⽅便,以SMSS为单位来显⽰CWND(实际上以字节为单位),以次数为单位来显⽰ RTT。以上讨论了发送端在未检测到拥塞时所采⽤的积极编码拥塞的⽅法。接下来接收拥塞发⽣时(可能发⽣在慢启动阶段或拥塞避免阶段)拥塞控制⾏为。发送端判断拥塞发⽣的依据有如下两个:传输超时,或者说TCP重传定时器益处。接收到重复的确认报⽂段。拥塞控制对这两种情况有不同的处理⽅式。对第⼀种情况仍然使⽤慢启动和拥塞避免。对第⼆种情况则使⽤快速重传和快速恢复(如果是真的发⽣拥塞的话)。注意:第⼆种情况如果发⽣在重传定时器溢出之后,则也被拥塞控制当成第⼀种情况来对待。如果发送端检测到拥塞发⽣是由于传输超时,即上述第⼀种情况,那么它将指向重传并做如下调整:ssthresh=max(FlightSize/2,2*SMSS)CWMD <=SMSS其中,FlightSize是已经发送但未收到确认的字节数。这样调整之后,CWMD将⼩于SMSS,那么也必然⼩于新的慢启动门限值ssthresh,它⼀定不⼩于SMSS的2倍,故⽽拥塞控制再次进⼊慢启动阶段。10.3 快速重传和快速恢复在很多情况下,发送端都可能接收到重复的确认报⽂段,⽐如TCP报⽂段丢失,或者接收端收到乱序TCP报⽂段并重排之等。拥塞控制算法需要判断当前收到重复的确认报⽂段时,⽹络是否真的发送了拥塞,或者说TCP报⽂段是否真的丢失了。具体做法是:发送端如果连续收到3个重复确认报⽂段,就认为是拥塞发⽣了。然后它启动快速重传和快速恢复算法来处理拥塞。过程如下:当收到第3个重复确认报⽂段时,按照如下公式计算ssthresh,然后⽴即重传丢失的报⽂段。ssthresh=max(FlightSize/2,2*SMSS)CWMD <=SMSS并按照如下公司设置CWND。CWND=ssthresh + 3*SMSS每次收到1个重复确认时,设置CWND=CWND+ssthresh。此时发送端可以发送新的TCP报⽂段(如果新的CWND允许的话)。当收到新的数据确认时,设置CWND=ssthresh(ssthresh是新的慢启动门限值,由第⼀步计算得到)快速重传和快速恢复完成之后,拥塞控制将恢复到拥塞避免阶段。注:笔记来源《Linux⾼性能服务器编程》

发布者:admin,转转请注明出处:http://www.yc00.com/news/1690103500a306144.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信