TCP 的优化
整理自 CSDN 公众号1. 客户端TCP 三次握手的开始是客户端发起 SYN,如果服务端没有及时回复,那么会重传,重传的间隔和次数是可控的,默认是五次,第一次间隔 1 秒,第二次 2 秒,第三次 4 秒,第四次 8 秒,第五次16 秒,最终超时时间是 63 秒,因此在优化时可以修改重传次数和间隔,以尽快把错误暴露给应用程序。2. 服务端的半连接队列优化服务端在第一次返回 SYN + ACK 时,就会把这次请求维护进一个半连接队列,这个队列用来维护尚未完成的握手信息(相对于全连接),如果这个队列溢出了,服务端就无法继续接受新的请求了,这也是 SYN Flood 攻击的点。通过一个命令 netstat -s 可以得到累计的、由于半连接队列已满引发的失败次数,隔几秒执行一次就可以知道这个次数是否有上升的趋势以及分析是否正常。这种 SYN Flood 攻击之所以成立,是因为维护这个半连接队列一定要分配一定的内存资源,那么应对的方式之一 syncookies 就是如何不分配资源的前提下,可以确认是一次有效的连接并 establish。syncookies 的工作原理是,服务器使用一种算法,计算出一个哈希值,它包含了客户端发来请求的部分信息,再将这个哈希值和 SYN+ACK 一起返回给客户端,客户端也经过一些运算,再返回给服务端,那么服务端根据这个返回值和之前的计算值比较,如果合法,就可以建立有效连接,从而不会占据半连接队列的内存。应对 SYN 攻击时,只需将 syncookies 的参数值调为 1(半连接队列溢出时启用),即可。相当的,可以增大半连接队列,但是要和 accept 的队列同时增大才有效,(否则会导致 accept 队列溢出同样丢失 TCP 连接)此时,对于客户端来说已经是 established 状态,但是还要再返回给服务端一个 ACK,服务端收到后,服务端才是 established 状态并开始传数据,如果网络不稳定,同样的,服务端会重发 SYN+ACK,当网络不稳定时,应该增加服务端重发 SYN+ACK 的次数。3. 服务端的 accept 队列优化当连接已经建立、应用程序尚未调用时,TCP 连接会被保存在一个 accept 队列中,如果进程未能及时调用,就会导致 accept 队列溢出,溢出部分连接将被默认丢弃。对此可以做的是,选择向客户端发送 RST 报文,告知关闭这个连接,丢弃握手过程。打开这一功能需要将 tcp_abort_on_overflow 参数设置为 1。如果想让客户端了解是由于 accept 队列溢出造成连接失败可以这样做。当 tcp_abort_on_overflow 参数设置为 0 时,则如果 accept 队列溢出,就会丢弃客户端传来的 ACK(用于最后一次握手)。应对高并发流量时,更好的选择是 tcp_abort_on_overflow 参数设置为 0,这样对于客户端它的状态仍然是 established,客户端会定时发送带有 ack 报文的发送数据请求,一旦服务端的 accept 队列有空位,那么连接仍有可能建立成功。所以只有很确定在一段时间内 accept 都是将溢出的状态,才推荐 tcp_abort_on_overflow 参数设置为 1。同样的,可以调整 accept 队列长度,也可以查看累计的由于溢出导致丢失的连接总数,来判断趋势。在 Linux 3.7 内核版本之后,提供了 TCP Fast Open 功能,这个功能如此生效:初次建立 TCP 连接时,客户端在第一个 SYN 包中传入一个请求 cookie,表明打开 fast open 功能,服务端对应生成一个 cookie 给客户端,除此之外,三次握手没有不同,但是,在 cookie 没有过期之前,下一次再连接的时候,客户端发送带有 cookie 的 SYN 包,服务端校验了 cookie 有效以后,就可以开始传输数据了,从而节约了一个往返的时间消耗。TCP Fast Open 功能需要服务端和客户端同时打开才能生效。(备注一个之前看到差点忘了的知识点。当主动方收到被动方的 FIN 报文后,内核会回复 ACK 报文给被动方,同时主动方的连接状态由 FIN_WAIT2 变为 TIME_WAIT,在 Linux 系统下大约等待 1 分钟后,TIME_WAIT 状态的连接才会彻底关闭。1. 主动方的优化关闭的方式有两种 RST 和 FIN,RST 是暴力关闭连接的方式,安全关闭连接则必须四次挥手。FIN 报文关闭则可以使用 close 和 shutdown 两种函数来实现。close 相对来说是“不优雅”的,调用 close 的一方的连接叫做「孤儿连接」,会同时关闭读和写,而 shutdown 可以控制是读还是写。关闭读的时候,会丢弃接收缓冲区里的所有数据,如果后续再接受到数据,也会悄悄丢弃,并发送 ACK,对方不会知道被丢弃了。关闭写的时候,会把发送缓冲区的数据全部发送并发送 FIN。(1)FIN_WAIT1 的优化主动方发送 FIN 以后,进入 FIN_WAIT1 状态,如果迟迟没收到 ACK,会定时重发 FIN,重发次数由 tcp_orphan_retries 参数控制,默认为 8 次,如果处于 FIN_WAIT1 状态的连接过多,应该考虑降低次数,重发次数超过参数时,连接会被直接关闭。如果遇到恶意攻击,可能无法发送出 FIN,因为 TCP 按顺序发送所有包, FIN 也不能绕过,另外如果对方的接收窗口已经满了,发送方也无法再发送数据。此时应该做的是调整 tcp_max_orphans 参数,它定义了「孤儿连接」的最大数量,当系统中的孤儿连接超过参数值,新增的孤儿连接不会再处于 FIN_WAIT1 状态,而是会被 RST 报文直接关闭。(只会影响 CLOSE 函数关闭的连接,不会影响 shutdown 关闭的,不会影响还有读或写的可能)(2)FIN_WAIT2 的优化主动方收到 ACK 后,会处于 FIN_WAIT2,因为被动方还可能有数据发送,如果是 shutdown 关闭,那它也可能还会发送数据,但是对于 close 关闭的连接,无法再发送和接收数据,保持在 FIN_WAIT2 的状态已经没有太大意义,tcp_fin_timeout 控制了这个状态下连接的持续时长,默认值是 60 秒。这个时间和 TIME_WAIT 状态时长是一致的。(3)TIME_WAIT 的优化TIME_WAIT 和 FIN_WAIT2 的时间是一致的,都是 2MSL,1MSL 表示一个报文在网络中存活的最长时间(报文每经过一次路由器的转发,IP 头部的 TTL 字段就会减 1,减到 0 时报文就被丢弃,这就限制了报文的最长存活时间),那么为什么是等待 2MSL 呢,其实就是允许报文至少丢失一次、再发送一次,这样第一个丢失了,等待的时间里第二个 ACK 还会到达,为什么不是 4MSL 以上呢,这是一个概率的问题,如果一个网络丢包率达到 1%,那么连续两次丢包的概率是万分之一,不必为了这种概率增加等待的时长。TIME_WAIT 有存在的意义,但是太多保持在这种状态的连接会占用双方资源,占据客户端的端口资源和服务端的系统资源。Linux 提供了 tcp_max_tw_buckets 参数,当 TIME_WAIT 的连接数量超过该参数时,新关闭的连接就不再经历 TIME_WAIT 而直接关闭。这个参数的设定应该取一个平衡点,即既不会太少导致高并发时产生连接间数据错乱的问题,也不会太多而导致耗尽端口和线程资源。对于用户端来讲,还可以启用 tcp_tw_reuse 参数来复用处于 TIME_WAIT 状态的连接(来节约接口资源。)这个参数有几个前提,一个是只有客户端可以打开,一个是 TIME_WAIT 状态也要保持 1 秒,另一个是要同步打开时间戳功能,报文带上时间戳就可以避免没有了 2MSL 时长以后的混乱情况,时间戳过期的报文就会被丢掉。另外对于 TIME_WAIT,还可以调整 socket 选项,来达到调用 close 关闭连接时跳过四次挥手直接关闭的效果,但不推荐。2. 被动方的优化首先,被动方收到 FIN 时,会自动回复 ACK,接着等待应用程序调用 close/shutdown 来结束连接,再发送 FIN。如果系统中同时查看到多个连接处于 CLOSE_WAIT 状态,则需要排查是否是应用程序出了故障。然后,当被动方也发送了 FIN 以后,还需要等待主动方回复一个 ACK,如果迟迟没收到,也会重发 FIN,重发次数也是 tcp_orphan_retries 参数控制,这点和主动方的优化一致,可以调整次数。(需确认被动方是否有 tcp_max_orphans 参数)3. 如果双方同时关闭?1.ACK 延迟目前在 TCP 中每传输一个报文都要求接收方进行确认,大量短而频繁的确认报文给网络带来了很多开销。因此采取了延迟 ACK 策略来减少 ACK 的数量,就是接收方收到一个报文以后,不会立即发送 ACK,而是等待 1~200ms,这期间若有回送数据报文就捎带确认,但收到两个连续数据报文或者等待超时则发送一个独立确认。有效减少了 ACK 的数量,改善了 TCP 的整体性能。2. 滑动窗口接收方的接收缓冲区不是不变的,接收到新的会变小,应用程序取出后又会变大,因此接收方会把自己当前的接收窗口大小放在 TCP 头告知发送方,如果不考虑拥塞控制,发送方的窗口大小「约等于」接收方的窗口大小。对于这一点,可以把 tcp_window_scaling 配置设为 1(默认打开)来扩大 TCP 通告窗口至 1G 大小。要使用这一选项,需要主动方在 SYN 中先告知,被动方在 SYN 中再反馈。但是缓冲区并非越大越好,还要考虑网络吞吐的能力。如果缓冲区与网络传输能力匹配,那么缓冲区的利用率就达到了最大化。3. 调整缓冲区大小这里需要说一个概念,就是带宽时延积,它决定网络中飞行报文的大小,它的计算方式:(1)发送缓冲区的调整发送缓冲区是自行调节的,当发送方发送的数据被确认后,并且没有新的数据要发送,就会把发送缓冲区的内存释放掉。接收缓冲区要复杂一些:上面三个数字单位都是字节,它们分别表示:(2)接收缓冲区的调整接收缓冲区可以根据系统空闲内存的大小来调节接收窗口:(3)内存的判断那么如何判断内存紧张或充分呢?上面三个数字单位不是字节,而是「页面大小」,1 页表示 4KB,它们分别表示:在实际的场景中,TCP 缓冲区最小值保持默认 4K 即可,来提高并发处理能力;最大值则尽可能靠近带宽时延积,来最大化网络效率。总结以上:为了提高并发能力、提高网络效率,我们要充分利用网络能力和自己的内存。网络这方面就是将缓冲区大小的极值尽可能靠近带宽时延积,而同时对缓冲区的自动调节需要结合内存来判断,这个 TCP 内存的判断是通过系统内存计算出来的几个值来划分的,在不同区间会对分配给缓冲区的内存大小进行调整。以上就是 TCP 在不同阶段的优化策略和思路,有关拥塞控制和流量控制之后再补一篇笔记。

tcp/ip协议如何实现可靠传输
1、建立连接:简单来说每个tcp/ip连接都是在三次握手基础上建立连接,并且实时检查连接状态。数据的传输具规范性。 2、超时重传:是TCP协议保证数据可靠性的另一个重要机制,其原理是在发送某一个数据以后就开启一个计时器,在一定时间内如果没有得到发送的数据报的ACK报文,那么就重新发送数据,直到发送成功为止。
1.通过三次握手 2.设置了窗口 使它的传输有流量控制和差错控制实现的可靠传输 3.它是面向连接的协议不像udp

三次握手四次挥手,重传机制
第一次握手 : 建立连接时, 客户端发送SYN 包(SYN=j)到服务器,并进入 SYN_SEND状态 ,等待服务器确认。(SYN:同步序列编号(Synchronize Sequence Numbers)。)服务器 收到SYN包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即 SYN+ACK包 ,此时服务器进入 SYN_RECV状态 。客户端 收到服务器的SYN+ACK包, 向服务器发送确认包ACK (ack=k+1),此包发送完毕, 客户端和服务器进入ESTABLISHED状态 ,完成3次握手。完成3次握手,客户端与服务器端开始传送数据。涉及的几个重要概念:1.未连接队列在3次握手协议中,服务器维护一个未连接队列,该队列为每个客户端的SYN包(syn=j)开设一个条目,该条目表明服务器已收到SYN包,并向客户发出确认,正在等待客户的确认包。这些条目所标识的连接在服务器处于SYN_RECV状态,当服务器收到客户的确认包时,删除该条目,服务器进入ESTABLISHED状态。2. backlog参数未连接队列的最大容纳数3.SYN-ACK重传次数服务器发送完SYN-ACK包,如果未收到客户端确认包,服务器进入首次重传,等待一段时间仍未收到客户确认包,进行第二次重传,如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。注意,每次重传等待的时间不一定相同。4. 半连接存活时间/ timeout时间/ SYN_RECV存活时间半连接队列的条目存活的最长时间,即服务从收到SYN包到确认这个报文无效的最长时间,该时间值是所有重传请求包的最长等待时间综合。为什么不是两次我们先来将三次握手这个过程捋一遍。(S-服务端,C-客户端)假想一下,如果我们去掉了第三次呢?因为我们不进行第三次握手,所以在S对C的请求进行回应(第二次握手)后,就会理所当然的认为连接已建立,而如果C并没有收到S的回应呢?此时,C仍认为连接未建立,S会对已建立的连接保存必要的资源,如果大量的这种情况,S会崩溃。因此第三次握手是必要的。为什么不是四次首先,如果乐于思考的同学应该会对上面有这样的疑问:既然没法确认第二次的握手,C是否可以收到,那么怎么确定第三次握手S就可以收到呢?不错,这根本没法确定,因为完全可靠的通信协议是根本不存在的,我们任何的通信协议都是在接受这样的现实情况之上进行的。而 三次握手后,C和S至少可以确认之前的通信情况,但无法确认之后的情况。在这个道理上说,无论是四次还是五次或是更多次都是徒劳的 。假设Client端发起中断连接请求,也就是发送FIN报文。 Server端接到FIN报文后,意思是说"我Client端没有数据要发给你了",但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以 你先发送ACK,"告诉Client端,你的请求我收到了,但是我还没准备好,请继续你等我的消息"。这个时候Client端就进入FIN_WAIT状态,继续等待Server端的FIN报文。当Server端确定数据已发送完成,则向Client端发送FIN报文 ,"告诉Client端,好了,我这边数据发完了,准备好关闭连接了"。 Client端收到FIN报文后,"就知道可以关闭连接了,但是他还是不相信网络,怕Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态, 如果Server端没有收到ACK则可以重传。“, Server端收到ACK后,"就知道可以断开连接了"。Client端等待了2MSL( 最大报文段生存时间)后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。Ok,TCP连接就这样关闭了!为什么必须是四次挥手?不能是两次?TCP连接是全双工的, 也就是说接收到FIN只是说没有数据再发过来但是还是可以发送数据的,也就是接受到一个FIN只是关闭了一个方向的数据传输,另一个方向还可以继续发送数据。在四次挥手的时候也是这样 前两次挥手只是确认关闭了一个方向的数据,加上后面两次挥手才真正的关闭了整个全双工连接 。【问题1】为什么连接的时候是三次握手,关闭的时候却是四次握手?答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。【问题2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态? 答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假设网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。

网络 之 三次握手&四次挥手 介绍
要了解三次握手&四次挥手的过程,就需要对TCP的报头以及有限状态机的概念有所了解,本文将介绍TCP报头的字段的含义,以及有限状态机各个状态的意义,最后对三次握手和四次挥手的过程做介绍 TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能,用户数据报协议(UDP)是同一层内另一个重要的传输协议。在因特网协议族(Internet protocol suite)中,TCP层是位于IP层之上,应用层之下的中间层。不同主机的应用层之间经常需要可靠的、像管道一样的连接,但是IP层不提供这样的流机制,而是提供不可靠的包交换。这里将介绍TCP报头的特性以及TCP报头各个字段的含义.工作在传输层面向连接协议.全双工协议.半关闭.错误检查.将数据打包成段,排序.确认机制.数据恢复,重传.流量控制,滑动窗口.拥塞控制,慢启动和拥塞避免算法.源端口、目标端口 :计算机上的进程要和其他进程通信是要通过计算机端口的,而一个计算机端口某个时刻只能被一个进程占用,所以通过指定源端口和目标端口,就可以知道是哪两个进程需要通信。源端口、目标端口是用16位表示的,可推算计算机的端口个数为2^16个. 序列号 :表示本报文段所发送数据的第一个字节的编号。在TCP连接中所传送的字节流的每一个字节都会按顺序编号。由于序列号由32位表示,所以每2^32个字节,就会出现序列号回绕,再次从0 开始. 确认号 :表示接收方期望收到发送方下一个报文段的第一个字节数据的编号。也就是告诉发送发:我希望你(指发送方)下次发送的数据的第一个字节数据的编号是这个确认号. 数据偏移 :表示TCP报文段的首部长度,共4位,由于TCP首部包含一个长度可变的选项部分,需要指定这个TCP报文段到底有多长。它指出TCP 报文段的数据起始处距离TCP 报文段的起始处有多远。该字段的单位是32位(即4个字节为计算单位),4位二进制最大表示15,所以数据偏移也就是TCP首部最大60字节. URG :表示本报文段中发送的数据是否包含紧急数据。后面的紧急指针字段(urgent pointer)只有当URG=1时才有效. ACK :表示是否前面的确认号字段是否有效。ACK=1,表示有效。只有当ACK=1时,前面的确认号字段才有效。TCP规定,连接建立后,ACK必须为1,带ACK标志的TCP报文段称为确认报文段. PSH :提示接收端应用程序应该立即从TCP接收缓冲区中读走数据,为接收后续数据腾出空间。如果为1,则表示对方应当立即把数据提交给上层应用,而不是缓存起来,如果应用程序不将接收到的数据读走,就会一直停留在TCP接收缓冲区中. RST :如果收到一个RST=1的报文,说明与主机的连接出现了严重错误(如主机崩溃),必须释放连接,然后再重新建立连接。或者说明上次发送给主机的数据有问题,主机拒绝响应,带RST标志的TCP报文段称为复位报文段. SYN :在建立连接时使用,用来同步序号。当SYN=1,ACK=0时,表示这是一个请求建立连接的报文段;当SYN=1,ACK=1时,表示对方同意建立连接。SYN=1,说明这是一个请求建立连接或同意建立连接的报文。只有在前两次握手中SYN才置为1,带SYN标志的TCP报文段称为同步报文段. FIN :表示通知对方本端要关闭连接了,标记数据是否发送完毕。如果FIN=1,即告诉对方:“我的数据已经发送完毕,你可以释放连接了”,带FIN标志的TCP报文段称为结束报文段. 窗口大小 :表示现在充许对方发送的数据量,也就是告诉对方,从本报文段的确认号开始允许对方发送的数据量. 校验和 :提供额外的可靠性. 紧急指针 :标记紧急数据在数据字段中的位置. 选项部分 :其最大长度可根据TCP首部长度进行推算。TCP首部长度用4位表示,选项部分最长为:(2^4-1)*4-20=40字节常见选项 :.最大报文段长度:MaxiumSegment Size,MSS.窗口扩大:Windows Scaling.时间戳:Timestamps.a 最大报文段长度指明自己期望对方发送TCP报文段时那个数据字段的长度。默认是536字节。数据字段的长度加上TCP首部的长度才等于整个TCP报文段的长度。MSS不宜设的太大也不宜设的太小。若选择太小,极端情况下,TCP报文段只含有1字节数据,在IP层传输的数据报的开销至少有40字节(包括TCP报文段的首部和IP数据报的首部)。这样,网络的利用率就不会超过1/41。若TCP报文段非常长,那么在IP层传输时就有可能要分解成多个短数据报片。在终点要把收到的各个短数据报片装配成原来的TCP报文段。当传输出错时还要进行重传,这些也都会使开销增大。因此MSS应尽可能大,只要在IP层传输时不需要再分片就行。在连接建立过程中,双方都把自己能够支持的MSS接入这一字段。MSS只出现在SYN报文中。即:MSS出现在SYN=1的报文段中.b 窗口扩大为了扩大窗口,由于TCP首部的窗口大小字段长度是16位,所以其表示的最大数是65535。但是随着时延和带宽比较大的通信产生(如卫星通信),需要更大的窗口来满足性能和吞吐率,所以产生了这个窗口扩大选项.c 时间戳可以用来计算RTT(往返时间),发送方发送TCP报文时,把当前的时间值放入时间戳字段,接收方收到后发送确认报文时,把这个时间戳字段的值复制到确认报文中,当发送方收到确认报文后即可计算出RTT。也可以用来防止回绕序号PAWS,也可以说可以用来区分相同序列号的不同报文。因为序列号用32为表示,每2^32个序列号就会产生回绕,那么使用时间戳字段就很容易区分相同序列号的不同报文2.3 TCP协议PORT.传输层通过port号,确定应用层协议.Port number:. tcp :0-65535,传输控制协议,面向连接的协议;通信前需要建立虚拟链路;结束后拆除链路.. udp :0-65535,User Datagram Protocol,无连接的协议.. IANA :互联网数字分配机构(负责域名,数字资源,协议分配)0-1023:系统端口或特权端口(仅管理员可用) ,众所周知,永久的分配给固定的系统应用使用,22/tcp(ssh), 80/tcp(http), 443/tcp(https)1024-49151:用户端口或注册端口,但要求并不严格,分配给程序注册为某应用使用,1433/tcp(SqlServer),1521/tcp(oracle),3306/tcp(mysql),11211/tcp/udp(memcached)49152-65535:动态端口或私有端口,客户端程序随机使用的端口其范围的定义:/proc/sys/net/ipv4/ip_local_port_range有限状态机,(英语:Finite-state machine, FSM),又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。常见的计算机就是使用有限状态机作为计算模型的:对于内存的不同状态,CPU通过读取内存值进行计算,更新内存中的状态。CPU还通过消息总线接受外部输入设备(如键盘、鼠标)的指令,计算后更改内存中的状态,计算结果输出到外部显示设备(如显示器),以及持久化存储在硬盘。TCP协议也存在有限状态机的概念,TCP 协议的操作可以使用一个具有 11 种状态的有限状态机来表示.CLOSED 没有任何连接状态.LISTEN 侦听状态,等待来自远方TCP端口的连接请求.SYN-SENT 在发送连接请求后,等待对方确认.SYN-RECEIVED 在收到和发送一个连接请求后,等待对方确认.ESTABLISHED 代表传输连接建立,双方进入数据传送状态.FIN-WAIT-1 主动关闭,主机已发送关闭连接请求,等待对方确认.FIN-WAIT-2 主动关闭,主机已收到对方关闭传输连接确认,等待对方发送关闭传输连接请求.TIME-WAIT 完成双向传输连接关闭,等待所有分组消失.CLOSE-WAIT 被动关闭,收到对方发来的关闭连接请求,并已确认.LAST-ACK 被动关闭,等待最后一个关闭传输连接确认,并等待所有分组消失.CLOSING 双方同时尝试关闭传输连接,等待对方确认.客户端通过connect系统调用主动与服务器建立连接connect系统调用首先给服务器发送一个同步报文段,使连接转移到SYN_SENT状态。.此后connect系统调用可能因为如下两个原因失败返回:.1、如果connect连接的目标端口不存在(未被任何进程监听),或者该端口仍被处于TIME_WAIT状态的连接所占用(见后文),则服务器将给客户端发送一个复位报文段,connect调用失败。.2、如果目标端口存在,但connect在超时时间内未收到服务器的确认报文段,则connect调用失败。.connect调用失败将使连接立即返回到初始的CLOSED状态。如果客户端成功收到服务器的同步报文段和确认,则connect调用成功返回,连接转移至ESTABLISHED状态.当客户端执行主动关闭时,它将向服务器发送一个结束报文段FIN,同时连接进入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给服务端,自己进入了FIN_WAIT_1状态,这时等待接收服务端的报文,该报文会有三种可能:a 只有服务端的ACK,只收到服务器的ACK,客户端会进入FIN_WAIT_2状态,后续当收到服务端的FIN时,回应发送一个ACK,会进入到TIME_WAIT状态,这个状态会持续2MSL(TCP报文段在网络中的最大生存时间,RFC 1122标准的建议值是2min).客户端等待2MSL,是为了当最后一个ACK丢失时,可以再发送一次。因为服务端在等待超时后会再发送一个FIN给客户端,进而客户端知道ACK已丢失b 只有服务端的FIN,回应一个ACK给服务端,进入CLOSING状态,然后接收到服务端的ACK时,进入TIME_WAIT状态c 同时收到服务端的ACK和FIN,直接进入TIME_WAIT状态.收到服务器ACK后,客户端处于FIN_WAIT_2状态,此时需要等待服务器发送结束报文段,才能转移至TIME_WAIT状态,否则它将一直停留在这个状态。如果不是为了在半关闭状态下继续接收数据,连接长时间地停留在FIN_WAIT_2状态并无益处。连接停留在FIN_WAIT_2状态的情况可能发生在:客户端执行半关闭后,未等服务器关闭连接就强行退出了。此时客户端连接由内核来接管,可称之为孤儿连接(和孤儿进程类似)。.Linux为了防止孤儿连接长时间存留在内核中,定义了两个内核参数:./proc/sys/net/ipv4/tcp_max_orphans 指定内核能接管的孤儿连接数目./proc/sys/net/ipv4/tcp_fin_timeout指定孤儿连接在内核中生存的时间TCP协议中的三次握手和四次挥手客户机端的三次握手和四次挥手服务器端的三次握手和四次挥手1 client 首先发送一个连接试探,此时ACK=0,表示确认号无效,SYN=1表示这是一个请求连接或连接接受报文,同时表示这个数据包不携带数据,seq=x表示此时client自己数据的初始序号是x,这时候client进入syn_sent状态,表示客户端等等服务器的回复2 server 监听到连接请求报文后,如同意建立连接,则向client发送确认,将TCP报文首部的SYN和ACK都置为1,因为client上一个请求连接的报文中seq=x,所以服务器端这次就发ack=x+1,表示服务器端希望客户端下一个报文段的第一个数据字节序号是x+1,同时表示x为止的所有数据都已经正确收到了,其中,此时服务器端发送seq=y表示server自己的初始序号是y,这时服务器进入了SYN_RCVD状态,表示服务器已经收到了客户端的请求,等待client的确认。3 client收到确认后还要再次给服务器端发送确认,同时携带要发给server的数据。ACK=1表示确认号ack=y+1有效,client这时的序号seq为x+1一旦client确认后,这个TCP连接的client 和 server 都直接进入到established状态,可以发起http请求了4.2 四次挥手详解第一次挥手:client向server,发送FIN报文段,表示关闭数据传送,此时ACK=0,seq=u,表示客户端此时数据的报文序号是u,此时,client进入FIN_WAIT_1状态,表示没有数据要传输了第二次挥手:server收到FIN报文段后进入CLOSE_WAIT状态(被动关闭),然后发送ACK确认,表示同意你关闭请求了,主机到主机的数据链路关闭,同时发送seq=v,表示此时server端的数据包字节序号是v,ack=u+1,表示希望client发送的下一个包的序号是u+1,表示确认了序号u之前的包都已经收到,客户端收到server的ACK报文后,进入FIN_WAIT_2状态第三次挥手:server等待client发送完数据,发送FIN=1,ACK=1到client请求关闭,server进入LAST_ACK状态。此时发送的seq有变化,因为上一个ACK的后server端可能又发送了一些数据,说以数据字节序号发送了变化,为w,但是ack还是保持不变第四次挥手:client收到server发送的FIN后,回复ACK确认到server,client进入TIME_WAIT状态。发送ack=w+1,表示希望服务器下个发送的报文的字节序号是w+1,确认了服务器之前发送的w字节都已经正确收到,发送seq=u+1表示当前client的字节序号是u+1.server收到client的ACK后就关闭连接了,状态为CLOSED。client等待2MSL,仍然没有收到server的回复,说明server已经正常关闭了,client关闭连接。其中,MSL(Maximum Segment Lifetime):报文最大生存时间,是任何报文段被丢弃前在网络内的最长时间。当client回复server的FIN后,等待(2-4分钟),即使两端的应用程序结束。TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态的原因是如果client直接进入CLOSED状态,由于IP协议不可靠性或网络问题,导致client最后发出的ACK报文未被server接收到,那么server在超时后继续向client重新发送FIN,而client已经关闭,那么找不到向client发送FIN的连接,server这时收到RST并把错误报告给高层,不符合TCP协议的可靠性特点。如果client直接进入CLOSED状态,而server还有数据滞留在网络中,当有一个新连接的端口和原来server的相同,那么当原来滞留的数据到达后,client认为这些数据是新连接的。等待2MSL确保本次连接所有数据消失。 当客户端等待2MSL后服务器端没有再次发送确认的报文后,client认为该次断开连接已经正常结束,client进入closed状态。四次挥手正式结束

TCP/IP协议是什么
TCP/IP协议是什么TCP和UDP处在同一层---运输层,但是TCP和UDP最不同的地方是,TCP提供了一种可靠的数据传输服务,TCP是面向连接的,也就是说,利用TCP通信的两台主机首先要经历一个“拨打电话”的过程,等到通信准备结束才开始传输数据,最后结束通话。所以TCP要比UDP可靠的多,UDP是把数据直接发出去,而不管对方是不是在收信,就算是UDP无法送达,也不会产生ICMP差错报文,这一经时重申了很多遍了。把TCP保证可靠性的简单工作原理:应用数据被分割成TCP认为最适合发送的数据块。这和UDP完全不同,应用程序产生的 数据报长度将保持不变。由TCP传递给IP的信息单位称为报文段或段当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能 及时收到一个确认,将重发这个报文段.当TCP收到发自TCP连接另一端的数据,它将发送一个确认。这个确认不是立即发送,通常将推迟几分之一秒.TCP将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输 过程中的任何变化。如果收到段的检验和有差错, T P将丢弃这个报文段和不确认收到此报文段(希望发端超时并重发)。既然TCP报文段作为IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段 的到达也可能会失序。如果必要, TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层。TCP还能提供流量控制。TCP连接的每一方都有固定大小的缓冲空间。TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据。这将防止较快主机致使较慢主机的缓冲区溢出。从这段话中可以看到,TCP中保持可靠性的方式就是超时重发,这是有道理的,虽然TCP也可以用各种各样的ICMP报文来处理这些,但是这也不是可靠的,最可靠的方式就是只要不得到确认,就重新发送数据报,直到得到对方的确认为止。TCP的首部和UDP首部一样,都有发送端口号和接收端口号。但是显然,TCP的首部信息要比UDP的多,可以看到,TCP协议提供了发送和确认所需要的所有必要的信息。可以想象一个TCP数据的发送应该是如下的一个过程。双方建立连接发送方给接受方TCP数据报,然后等待对方的确认TCP数据报,如果没有,就重新发,如果有,就发送下一个数据报。接受方等待发送方的数据报,如果得到数据报并检验无误,就发送ACK(确认)数据报,并等待下一个TCP数据报的到来。直到接收到FIN(发送完成数据报)中止连接可以想见,为了建立一个TCP连接,系统可能会建立一个新的进程(最差也是一个线程),来进行数据的传送--TCP协议TCP是一个面向连接的协议,在发送输送之前 ,双方需要确定连接。而且,发送的数据可以进行TCP层的分片处理。TCP连接的建立过程 ,可以看成是三次握手 。而连接的中断可以看成四次握手 。1.连接的建立在建立连接的时候,客户端首先向服务器申请打开某一个端口(用SYN段等于1的TCP报文),然后服务器端发回一个ACK报文通知客户端请求报文收到,客户端收到确认报文以后再次发出确认报文确认刚才服务器端发出的确认报文(绕口么),至此,连接的建立完成。这就叫做三次握手。如果打算让双方都做好准备的话,一定要发送三次报文,而且只需要三次报文就可以了。可以想见,如果再加上TCP的超时重传机制,那么TCP就完全可以保证一个数据包被送到目的地。2.结束连接TCP有一个特别的概念叫做half-close,这个概念是说,TCP的连接是全双工(可以同时发送和接收)连接,因此在关闭连接的`时候,必须关闭传和送两个方向上的连接。客户机给服务器一个FIN为1的TCP报文,然后服务器返回给客户端一个确认ACK报文,并且发送一个FIN报文,当客户机回复ACK报文后(四次握手),连接就结束了。3.最大报文长度在建立连接的时候,通信的双方要互相确认对方的最大报文长度(MSS),以便通信。一般这个SYN长度是MTU减去固定IP首部和TCP首部长度。对于一个以太网,一般可以达到1460字节。当然如果对于非本地的IP,这个MSS可能就只有536字节,而且,如果中间的传输网络的MSS更加的小的话,这个值还会变得更小。4.客户端应用程序的状态迁移图客户端的状态可以用如下的流程来表示:CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED以上流程是在程序正常的情况下应该有的流程,从书中的图中可以看到,在建立连接时,当客户端收到SYN报文的ACK以后,客户端就打开了数据交互地连接。而结束连接则通常是客户端主动结束的,客户端结束应用程序以后,需要经历FIN_WAIT_1,FIN_WAIT_2等状态,这些状态的迁移就是前面提到的结束连接的四次握手。5.服务器的状态迁移图服务器的状态可以用如下的流程来表示:CLOSED->LISTEN->SYN收到->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED在建立连接的时候,服务器端是在第三次握手之后才进入数据交互状态,而关闭连接则是在关闭连接的第二次握手以后(注意不是第四次)。而关闭以后还要等待客户端给出最后的ACK包才能进入初始的状态。6.TCP服务器设计前面曾经讲述过UDP的服务器设计,可以发现UDP的服务器完全不需要所谓的并发机制,它只要建立一个数据输入队列就可以。但是TCP不同,TCP服务器对于每一个连接都需要建立一个独立的进程(或者是轻量级的,线程),来保证对话的独立性。所以TCP服务器是并发的。而且TCP还需要配备一个呼入连接请求队列(UDP服务器也同样不需要),来为每一个连接请求建立对话进程,这也就是为什么各种TCP服务器都有一个最大连接数的原因。而根据源主机的IP和端口号码,服务器可以很轻松的区别出不同的会话,来进行数据的分发。TCP的交互数据流对于交互性要求比较高的应用,TCP给出两个策略来提高发送效率和减低网络负担:(1)捎带ACK。(2)Nagle算法(一次尽量多的发数据)捎带ACK的发送方式这个策略是说,当主机收到远程主机的TCP数据报之后,通常不马上发送ACK数据报,而是等上一个短暂的时间,如果这段时间里面主机还有发送到远程主机的TCP数据报,那么就把这个ACK数据报“捎带”着发送出去,把本来两个TCP数据报整合成一个发送。一般的,这个时间是200ms。可以明显地看到这个策略可以把TCP数据报的利用率提高很多。Nagle算法上过bbs的人应该都会有感受,就是在网络慢的时候发贴,有时键入一串字符串以后,经过一段时间,客户端“发疯”一样突然回显出很多内容,就好像数据一下子传过来了一样,这就是Nagle算法的作用。Nagle算法是说,当主机A给主机B发送了一个TCP数据报并进入等待主机B的ACK数据报的状态时,TCP的输出缓冲区里面只能有一个TCP数据报,并且,这个数据报不断地收集后来的数据,整合成一个大的数据报,等到B主机的ACK包一到,就把这些数据“一股脑”的发送出去。虽然这样的描述有些不准确,但还算形象和易于理解,我们同样可以体会到这个策略对于低减网络负担的好处。在编写插口程序的时候,可以通过TCP_NODELAY来关闭这个算法。并且,使用这个算法看情况的,比如基于TCP的X窗口协议,如果处理鼠标事件时还是用这个算法,那么“延迟”可就非常大了。 ;

本文由 在线网速测试 整理编辑,转载请注明出处,原文链接:https://www.wangsu123.cn/news/60212.html。