IPV4中TCP内核参数

      参照debian系统,查看的tcp相关配置 # 全连接队列满了,新来的连接发送rst, 1表示开启 net.ipv4.tcp_abort_on_overflow = 0 # 接受缓冲区1/2^n用来接受缓冲,换句话来说,接收窗口不表示接受buffer的大小 net.ipv4.tcp_adv_win_scale = 1 # 拥塞算法 net.ipv4.tcp_allowed_congestion_control = reno cubic net.ipv4.tcp_app_win = 31 # 自动开启crok选项 net.ipv4.tcp_autocorking = 1 net.ipv4.tcp_available_congestion_control = reno cubic net.ipv4.tcp_available_ulp = net.ipv4.tcp_base_mss = 1024 net.ipv4.tcp_challenge_ack_limit = 1000 net.ipv4.tcp_comp_sack_delay_ns = 1000000 net.ipv4.tcp_comp_sack_nr = 44 net.ipv4.tcp_congestion_control = cubic net.ipv4.tcp_dsack = 1 net.ipv4.tcp_early_demux =… Continue reading IPV4中TCP内核参数

Published
Categorized as 网络 Tagged

HTTP 队头阻塞(Head-of-Line blocking)

(译)Robin Marx: QUIC 和 HTTP/3 队头阻塞的细节 作者:Robin Marx,原文:Head-of-Line Blocking in QUIC and HTTP/3: The Details,GitHub:rmarx/holblocking-blogpost 您可能已经听说,经过4年的工作,新的 HTTP/3 和 QUIC 协议终于接近正式标准化。预览版现在可以在服务器和浏览器中进行测试。 与 HTTP/2 相比,HTTP/3 有很大的性能改进,这主要是因为它将底层传输协议从 TCP 改为基于 UDP 的 QUIC。在这篇文章中,我们将深入了解其中的一项改进,即消除“队头阻塞”(Head-of-Line blocking, 简写:HOL blocking)问题。这很有用,因为我读过很多关于这实际上意味着什么以及它在现实中有多大帮助的误解。解决队头阻塞也是 HTTP/3 和 QUIC 以及 HTTP/2 背后的主要动机之一,因此它也为协议演进的原因提供了一个极好的视角。 我将首先介绍队头阻塞问题,然后在整个 HTTP 历史中跟踪它的不同形式。我们还将研究它如何与其他系统交互,如优先级和拥塞控制。我们的目标是帮助人们对 HTTP/3 的性能改进做出正确的判断,而这(剧透)可能不像营销材料中所说的那样令人惊讶。 目录: 什么是队头阻塞? HTTP/1.1 的队头阻塞 HTTP/2(基于 TCP)的队头阻塞 HTTP/3(基于 QUIC)的队头阻塞 总结与结论 彩蛋内容: 彩蛋:HTTP/1.1 管道… Continue reading HTTP 队头阻塞(Head-of-Line blocking)

Nagle算法的一些理解

背景 Nagle算法是以他的发明人John Nagle的名字命名的,它用于自动连接许多的小缓冲器消息;这一过程(称为nagling)通过减少必须发送包的个数来增加网络软件系统的效率。 Nagle算法于1984年定义为福特航空和通信公司IP/TCP拥塞控制方法,这使福特经营的最早的专用TCP/IP网络减少拥塞控制,从那以后这一方法得到了广泛应用。Nagle的文档里定义了处理他所谓的小包问题的方法,这种问题指的是应用程序一次产生一字节数据,这样会导致网络由于太多的包而过载(一个常见的情况是发送端的”糊涂窗口综合症(Silly Window Syndrome)”)。从键盘输入的一个字符,占用一个字节,可能在传输上造成41字节的包,其中包括1字节的有用信息和40字节的首部数据。这种情况转变成了4000%的消耗,这样的情况对于轻负载的网络来说还是可以接受的,但是重负载的福特网络就受不了了,它没有必要在经过节点和网关的时候重发,导致包丢失和妨碍传输速度。吞吐量可能会妨碍甚至在一定程度上会导致连接失败。Nagle的算法通常会在TCP程序里添加两行代码,在未确认数据发送的时候让发送器把数据送到缓存里。任何数据随后继续直到得到明显的数据确认或者直到攒到了一定数量的数据了再发包。尽管Nagle的算法解决的问题只是局限于福特网络,然而同样的问题也可能出现在ARPANet。这种方法在包括因特网在内的整个网络里得到了推广,成为了默认的执行方式,尽管在高互动环境下有些时候是不必要的,例如在客户/服务器情形下。在这种情况下,nagling可以通过使用TCP_NODELAY 套接字选项关闭。 算法规则 如果包长度达到MSS,则允许发送 如果包含FIN,则允许发送 如果设置了TCP_NODELAY,则允许发送 未设置TCP_CORK选项时,若所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送 上述条件都未满足,但发生了超时(一般为200ms),则立即发送。 Nagle算法只允许一个未被ACK的包存在于网络,它并不管包的大小,因此它事实上就是一个扩展的停-等协议,只不过它是基于包停-等的,而不是基于字节停-等的。Nagle算法完全由TCP协议的ACK机制决定,这会带来一些问题,比如如果对端ACK回复很快的话,Nagle事实上不会拼接太多的数据包,虽然避免了网络拥塞,网络总体的利用率依然很低。 也就是说,Nagle的算法是控制发送的,发送端在有未确认数据包并且本次发送包较小时,会等待后续数据包,直至超过mss才发送 举个例子,client端调用socket的write操作将一个int型数据(称为A块)写入到网络中,由于此时连接是空闲的(也就是说还没有未被确认的小段),因此这个int型数据会被马上发送到server端,接着,client端又调用write操作写入‘rn’(简称B块),这个时候,A块的ACK没有返回,所以可以认为已经存在了一个未被确认的小段,所以B块没有立即被发送,一直等待A块的ACK收到(大概40ms之后),B块才被发送。 这里还隐藏了一个问题,就是A块数据的ACK为什么40ms之后才收到?这是因为TCP/IP中不仅仅有nagle算法,还有一个TCP确认延迟机制 。当Server端收到数据之后,它并不会马上向client端发送ACK,而是会将ACK的发送延迟一段时间(假设为t),它希望在t时间内server端会向client端发送应答数据,这样ACK就能够和应答数据一起发送,就像是应答数据捎带着ACK过去。在我之前的时间中,t大概就是40ms。这就解释了为什么’rn’(B块)总是在A块之后40ms才发出。 抓包时经常看到PSH,ACK的包,正是这个机制的体现,将ack和数据一并送出 其实就算没有延迟确认,Nagle这种只允许一个未确认包存在的机制,也是利用率很低的,因为完全串行化了,当网络延迟较高时,根本无法最大化利用物理带宽(有空试试Nagle在大带宽,高延迟的网络下的表现) 这两个机制配合在一起也是绝配,一个不回,一个不发,接收端不ack,发送端就不发送下一个小报文,也就造成了这个40ms(tcp delay ack)的延迟问题 tcp_nodelay 禁用的是只是nagle算法,并没有禁用延迟确认 禁用了nagle算法之后,相当于拆散了这对CP,发送端发送时也就不会再等待所有包都ack了;接收端的延迟ack影响并不是很大,只是会一定程度降低tcp 窗口滑动的速度而已

TCP Keepalive机制刨根问底

  TCP协议中有长连接和短连接之分。短连接环境下,数据交互完毕后,主动释放连接; 双方建立交互的连接,但是并不是一直存在数据交互,有些连接会在数据交互完毕后,主动释放连接,而有些不会,那么在长时间无数据交互的时间段内,交互双方都有可能出现掉电、死机、异常重启,还是中间路由网络无故断开、NAT超时等各种意外。 当这些意外发生之后,这些TCP连接并未来得及正常释放,那么,连接的另一方并不知道对端的情况,它会一直维护这个连接,长时间的积累会导致非常多的半打开连接,造成端系统资源的消耗和浪费,为了解决这个问题,在传输层可以利用TCP的保活报文来实现,这就有了TCP的Keepalive(保活探测)机制。   Tcp Keepalive存在的作用   探测连接的对端是否存活 在应用交互的过程中,可能存在以下几种情况: 客户端或服务端意外断电,死机,崩溃,重启。 中间网络已经中断,而客户端与服务器并不知道。 利用保活探测功能,可以探知这种对端的意外情况,从而保证在意外发生时,可以释放半打开的TCP、 连接。   防止中间设备因超时删除连接相关的连接表 中间设备如防火墙等,会为经过它的数据报文建立相关的连接信息表,并未其设置一个超时时间的定时器,如果超出预定时间,某连接无任何报文交互的话,中间设备会将该连接信息从表中删除,在删除后,再有应用报文过来时,中间设备将丢弃该报文,从而导致应用出现异常,这个交互的过程大致如下图所示: 这种情况在有防火墙的应用环境下非常常见,这会给某些长时间无数据交互但是又要长时间维持连接的应用(如数据库)带来很大的影响,为了解决这个问题,应用本身或TCP可以通过保活报文来维持中间设备中该连接的信息,(也可以在中间设备上开启长连接属性或调高连接表的释放时间来解决。 常见应用故障场景: 某财务应用,在客户端需要填写大量的表单数据,在客户端与服务器端建立TCP连接后,客户端终端使用者将花费几分钟甚至几十分钟填写表单相关信息,终端使用者终于填好表单所需信息后,点击“提交”按钮,结果,这个时候由于中间设备早已经将这个TCP连接从连接表中删除了,其将直接丢弃这个报文或者给客户端发送RST报文,应用故障产生,这将导致客户端终端使用者所有的工作将需要重新来过,给使用者带来极大的不便和损失。   TCP保活报文交互过程 TCP保活的交互过程大致如下图所示: TCP保活可能带来的问题: 中间设备因大量保活连接,导致其连接表满,网关设备由于保活问题,导致其连接表满,无法新建连接(XX局网闸故障案例)或性能下降严重 正常连接被释放 当连接一端在发送保活探测报文时,中间网络正好由于各种异常(如链路中断、中间设备重启等)而无法将保活探测报文正确转发至对端时,可能会导致探测的一方释放本来正常的连接,但是这种可能情况发生的概率较小, 另外,一般也可以增加保活探测报文发生的次数来减少这种情况发生的概率和影响。 TCP Keepalive协议解读 下面协议解读,基于 RFC1122#TCP Keep-Alives  (注意这是协议的解读站在协议的角度) TCP Keepalive 虽不是标准规范,但操作系统一旦实现,默认情况下须为关闭,可以被上层应用开启和关闭。 TCP Keepalive必须在 没有任何数据(包括ACK包)接收之后的周期内才会被发送,允许配置,默认值不能够小于2个小时 不包含数据的ACK段在被TCP发送时没有可靠性保证,意即一旦发送,不确保一定发送成功。系统实现不能对任何特定探针包作死连接对待 规范建议keepalive保活包不应该包含数据,但也可以包含1个无意义的字节,比如0x0。 SEG.SEQ = SND.NXT-1,即TCP保活探测报文序列号将前一个TCP报文序列号减1。SND.NXT = RCV.NXT,即下一次发送正常报文序号等于ACK序列号;总之保活报文不在窗口控制范围内 有一张图,可以很容易说明,但请仔细观察Tcp Keepalive部分:   TCP Keepalive 需要注意的点(协议层面) 不太好的TCP堆栈实现,可能会要求保活报文必须携带有1个字节的数据负载 TCP Keepalive应该在服务器端启用,客户端不做任何改动;若单独在客户端启用,若客户端异常崩溃或出现连接故障,存在服务器无限期的为已打开的但已失效的文件描述符消耗资源的严重问题。 但在特殊的NFS文件系统环境下,需要客户端和服务器端都要启用Tcp… Continue reading TCP Keepalive机制刨根问底

浅谈Linux下Socket选项设置

0.前言 TCP/IP协议栈是Linux内核的重要组成部分和网络编程的基石,虽然Linux和BSD有很大的联系,但是对于某些Socket选项和内核操作仍然存在差异,因此文中适用场景均为仅Linux。 《UNIX网络编程》是已故UNIX网络专家W. Richard Stevens博士(1951-1999)、世界著名网络专家Bill Fenner和Andrew M. Rudoff完成的权威著作,该书对网络编程进行全面而深入的阐述,是提高网络编程功力的不二之选。 1.Socket和TCP/IP的关系 “All problems in computer science can be solved by another level of indirection.” 为满足应用层需求,系统对TCP/IP层进行细节屏蔽和抽象,Socket层就相当于TCP/IP和应用层之间的中间层。 常用的socket/bind/accept/connect就是抽象出来的接口,使用它们可以快速进行网络程序开发,可见Socket中间层的重要性。 Socket选项就是为满足用户的定制化需求而生的。我们经常遇到的情况包括地址复用、端口复用、读写超时时间、读写缓冲区大小等。 在Linux的TCP/IP协议栈中包括很多Socket选项,它们会出现在TCP层、IP层、Socket层等,为此在读取和设置socket选项时需要指定level。 如图可以看到Socket层作为中间层以及各层支持的部分Socket选项: 注:可通过man 7 tcp/man 7 ip查看tcp/ip各层Socket选项详细定义和添加内核版本等信息。   2.操作Socket选项的API 读取和设置Socket选项的API包括: getsockopt、setsockopt、fcntl、ioctl等; 其中fcntl和ioctl用来设置socket的阻塞和非阻塞状态。 通过man获得的函数定义: //ioctl函数定义 #include <sys/ioctl.h> int ioctl(int d, int request, …); //fcntl函数定义 #include <unistd.h> #include <fcntl.h> int fcntl(int fd,… Continue reading 浅谈Linux下Socket选项设置

聊聊TCP连接耗时的那些事儿

在互联网后端日常开发接口的时候中,不管你使用的是C、Java、PHP还是Golang,都避免不了需要调用mysql、redis等组件来获取数据,可能还需要执行一些rpc远程调用,或者再调用一些其它restful api。 在这些调用的底层,基本都是在使用TCP协议进行传输。这是因为在传输层协议中,TCP协议具备可靠的连接,错误重传,拥塞控制等优点,所以目前应用比UDP更广泛一些。 相信你也一定听闻过TCP也存在一些缺点,那就是老生常谈的开销要略大。但是各路技术博客里都在单单说开销大、或者开销小,而少见不给出具体的量化分析。不客气一点,这都是营养不大的废话。经过日常工作的思考之后,我更想弄明白的是,开销到底多大。一条TCP连接的建立需要耗时延迟多少,是多少毫秒,还是多少微秒?能不能有一个哪怕是粗略的量化估计?当然影响TCP耗时的因素有很多,比如网络丢包等等。我今天只分享我在工作实践中遇到的比较高发的各种情况。 一 正常TCP连接建立过程 要想搞清楚TCP连接的建立耗时,我们需要详细了解连接的建立过程。在前文《图解Linux网络包接收过程》中我们介绍了数据包在接收端是怎么被接收的。数据包从发送方出来,经过网络到达接收方的网卡。在接收方网卡将数据包DMA到RingBuffer后,内核经过硬中断、软中断等机制来处理(如果发送的是用户数据的话,最后会发送到socket的接收队列中,并唤醒用户进程)。 在软中断中,当一个包被内核从RingBuffer中摘下来的时候,在内核中是用struct sk_buff结构体来表示的(参见内核代码include/linux/skbuff.h)。其中的data成员是接收到的数据,在协议栈逐层被处理的时候,通过修改指针指向data的不同位置,来找到每一层协议关心的数据。 对于TCP协议包来说,它的Header中有一个重要的字段-flags。如下图: 通过设置不同的标记为,将TCP包分成SYNC、FIN、ACK、RST等类型。客户端通过connect系统调用命令内核发出SYNC、ACK等包来实现和服务器TCP连接的建立。在服务器端,可能会接收许许多多的连接请求,内核还需要借助一些辅助数据结构-半连接队列和全连接队列。我们来看一下整个连接过程: 在这个连接过程中,我们来简单分析一下每一步的耗时 客户端发出SYNC包:客户端一般是通过connect系统调用来发出SYN的,这里牵涉到本机的系统调用和软中断的CPU耗时开销 SYN传到服务器:SYN从客户端网卡被发出,开始“跨过山和大海,也穿过人山人海……”,这是一次长途远距离的网络传输 服务器处理SYN包:内核通过软中断来收包,然后放到半连接队列中,然后再发出SYN/ACK响应。又是CPU耗时开销 SYC/ACK传到客户端:SYC/ACK从服务器端被发出后,同样跨过很多山、可能很多大海来到客户端。又一次长途网络跋涉 客户端处理SYN/ACK:客户端内核收包并处理SYN后,经过几us的CPU处理,接着发出ACK。同样是软中断处理开销 ACK传到服务器:和SYN包,一样,再经过几乎同样远的路,传输一遍。 又一次长途网络跋涉 服务端收到ACK:服务器端内核收到并处理ACK,然后把对应的连接从半连接队列中取出来,然后放到全连接队列中。一次软中断CPU开销 服务器端用户进程唤醒:正在被accpet系统调用阻塞的用户进程被唤醒,然后从全连接队列中取出来已经建立好的连接。一次上下文切换的CPU开销 以上几步操作,可以简单划分为两类: 第一类是内核消耗CPU进行接收、发送或者是处理,包括系统调用、软中断和上下文切换。它们的耗时基本都是几个us左右。具体的分析过程可以参见《一次系统调用开销到底有多大?》、《软中断会吃掉你多少CPU?》、《进程/线程切换会用掉你多少CPU?》这三篇文章。 第二类是网络传输,当包被从一台机器上发出以后,中间要经过各式各样的网线、各种交换机路由器。所以网络传输的耗时相比本机的CPU处理,就要高的多了。根据网络远近一般在几ms~到几百ms不等。。 1ms就等于1000us,因此网络传输耗时比双端的CPU开销要高1000倍左右,甚至更高可能还到100000倍。所以,在正常的TCP连接的建立过程中,一般可以考虑网络延时即可。一个RTT指的是包从一台服务器到另外一台服务器的一个来回的延迟时间。所以从全局来看,TCP连接建立的网络耗时大约需要三次传输,再加上少许的双方CPU开销,总共大约比1.5倍RTT大一点点。不过从客户端视角来看,只要ACK包发出了,内核就认为连接是建立成功了。所以如果在客户端打点统计TCP连接建立耗时的话,只需要两次传输耗时-既1个RTT多一点的时间。(对于服务器端视角来看同理,从SYN包收到开始算,到收到ACK,中间也是一次RTT耗时) 二 TCP连接建立时的异常情况 上一节可以看到在客户端视角,在正常情况下一次TCP连接总的耗时也就就大约是一次网络RTT的耗时。如果所有的事情都这么简单,我想我的这次分享也就没有必要了。事情不一定总是这么美好,总会有意外发生。在某些情况下,可能会导致连接时的网络传输耗时上涨、CPU处理开销增加、甚至是连接失败。现在我们说一下我在线上遇到过的各种沟沟坎坎。 1)客户端connect系统调用耗时失控 正常一个系统调用的耗时也就是几个us(微秒)左右。但是在《追踪将服务器CPU耗光的凶手!》一文中笔者的一台服务器当时遇到一个状况,某次运维同学转达过来说该服务CPU不够用了,需要扩容。当时的服务器监控如下图: 该服务之前一直每秒抗2000左右的qps,CPU的idel一直有70%+。怎么突然就CPU一下就不够用了呢。而且更奇怪的是CPU被打到谷底的那一段时间,负载却并不高(服务器为4核机器,负载3-4是比较正常的)。 后来经过排查以后发现当TCP客户端TIME_WAIT有30000左右,导致可用端口不是特别充足的时候,connect系统调用的CPU开销直接上涨了100多倍,每次耗时达到了2500us(微秒),达到了毫秒级别。 当遇到这种问题的时候,虽然TCP连接建立耗时只增加了2ms左右,整体TCP连接耗时看起来还可接受。但是这里的问题在于这2ms多都是在消耗CPU的周期,所以问题不小。 解决起来也非常简单,办法很多:修改内核参数net.ipv4.ip_local_port_range多预留一些端口号、改用长连接都可以。 2)半/全连接队列满 如果连接建立的过程中,任意一个队列满了,那么客户端发送过来的syn或者ack就会被丢弃。客户端等待很长一段时间无果后,然后会发出TCP Retransmission重传。拿半连接队列举例: 要知道的是上面TCP握手超时重传的时间是秒级别的。也就是说一旦server端的连接队列导致连接建立不成功,那么光建立连接就至少需要秒级以上。而正常的在同机房的情况下只是不到1毫秒的事情,整整高了1000倍左右。尤其是对于给用户提供实时服务的程序来说,用户体验将会受到较大影响。如果连重传也没有握手成功的话,很可能等不及二次重试,这个用户访问直接就超时了。 还有另外一个更坏的情况是,它还有可能会影响其它的用户。假如你使用的是进程/线程池这种模型提供服务,比如php-fpm。我们知道fpm进程是阻塞的,当它响应一个用户请求的时候,该进程是没有办法再响应其它请求的。假如你开了100个进程/线程,而某一段时间内有50个进程/线程卡在和redis或者mysql服务器的握手连接上了(注意:这个时候你的服务器是TCP连接的客户端一方)。这一段时间内相当于你可以用的正常工作的进程/线程只有50个了。而这个50个worker可能根本处理不过来,这时候你的服务可能就会产生拥堵。再持续稍微时间长一点的话,可能就产生雪崩了,整个服务都有可能会受影响。 既然后果有可能这么严重,那么我们如何查看我们手头的服务是否有因为半/全连接队列满的情况发生呢?在客户端,可以抓包查看是否有SYN的TCP Retransmission。如果有偶发的TCP Retransmission,那就说明对应的服务端连接队列可能有问题了。 在服务端的话,查看起来就更方便一些了。netstat -s可查看到当前系统半连接队列满导致的丢包统计,但该数字记录的是总丢包数。你需要再借助watch命令动态监控。如果下面的数字在你监控的过程中变了,那说明当前服务器有因为半连接队列满而产生的丢包。你可能需要加大你的半连接队列的长度了。 $ watch ‘netstat -s | grep LISTEN’    8 SYNs to LISTEN sockets ignored 对于全连接队列来说呢,查看方法也类似。… Continue reading 聊聊TCP连接耗时的那些事儿

TCP快速打开 fast_open

TCP快速打开(英语:TCP Fast Open,简称TFO)是对计算机网络中传输控制协议(TCP)连接的一种简化握手手续的拓展,用于提高两端点间连接的打开速度。 它通过握手开始时的SYN包中的TFO cookie(一个TCP选项)来验证一个之前连接过的客户端。如果验证成功,它可以在三次握手最终的ACK包收到之前就开始发送数据,这样便跳过了一个绕路的行为,更在传输开始时就降低了延迟。这个加密的Cookie被存储在客户端,在一开始的连接时被设定好。然后每当客户端连接时,这个Cookie被重复返回。 此Cookie通常采用一种分组密码,私钥由服务器根据客户端的IP地址保存,生成一个第三方难以仿冒的消息认证码标签,即便第三方可以伪造源IP地址或从其他IP地址制造到同一个服务器的连接。尽管使用了加密技术来生成cookie,但TFO并不着眼于提供比它所替换的三次握手有更多的安全性,并且不对所产生的TCP连接提供任何形式的加密保护或端点身份认证。它的目的不是为了抵挡中间人攻击。 这个协议最早提出于2011年 并在2012年2月时已为一个IETF互联网草案, 这项规范最终在2014年12月作为RFC 7413发布。 过程 请求Fast Open Cookie 客户端发送SYN数据包,该数据包包含Fast Open选项,且该选项的Cookie为空,这表明客户端请求Fast Open Cookie; 支持TCP Fast Open的服务器生成Cookie,并将其置于SYN-ACK数据包中的Fast Open选项以发回客户端; 客户端收到SYN-ACK后,缓存Fast Open选项中的Cookie。 第一次连接抓包数据 captures 实施TCP Fast Open 以下描述假定客户端在此前的TCP连接中已完成请求Fast Open Cookie的过程并存有有效的Fast Open Cookie。 客户端发送SYN数据包,该数据包包含数据(对于非TFO的普通TCP握手过程,SYN数据包中不包含数据)以及此前记录的Cookie,也可以带上请求数据; 支持TCP Fast Open的服务器会对收到Cookie进行校验:如果Cookie有效,服务器将在SYN-ACK数据包中对SYN和数据进行确认(Acknowledgement),服务器随后将数据递送至相应的应用程序;否则,服务器将丢弃SYN数据包中包含的数据,且其随后发出的SYN-ACK数据包将仅确认(Acknowledgement)SYN的对应序列号; 如果服务器接受了SYN数据包中的数据,服务器可在握手完成之前发送数据; 客户端将发送ACK确认服务器发回的SYN以及数据,但如果客户端在初始的SYN数据包中发送的数据未被确认,则客户端将重新发送数据; 此后的TCP连接和非TFO的正常情况一致。 注:客户端在请求并存储了Fast Open Cookie之后,可以不断重复TCP Fast Open直至服务器认为Cookie无效(通常为过期)。 获取fast_open的cookies的快速打开包 captures 实现 TFO的实现包括: TFO的IPv4支持在3.6(客户端)和3.7(服务端)版本中被合并进Linux内核主线。IPv6服务器的TFO支持被合并进入3.16版本。 FreeBSD自10.3版本(支持服务端)和12.0版本(支持客户端)开始支持TFO。 Mozilla Firefox 56支持TFO。… Continue reading TCP快速打开 fast_open

面试必备!TCP协议经典十五连问!

前言 TCP协议是大厂面试必问的知识点。整理了15道非常经典的TCP面试题,希望大家都找到理想的offer呀 公众号:捡田螺的小男孩 1.  讲下TCP三次握手流程 开始客户端和服务器都处于CLOSED状态,然后服务端开始监听某个端口,进入LISTEN状态 第一次握手(SYN=1, seq=x),发送完毕后,客户端进入 SYN_SEND 状态 第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1), 发送完毕后,服务器端进入 SYN_RCVD 状态。 第三次握手(ACK=1,ACKnum=y+1),发送完毕后,客户端进入 ESTABLISHED 状态,当服务器端接收到这个包时,也进入 ESTABLISHED 状态,TCP 握手,即可以开始数据传输。 2.TCP握手为什么是三次,不能是两次?不能是四次? TCP握手为什么是三次呢?为了方便理解,我们以谈恋爱为例子:两个人能走到一起,最重要的事情就是相爱,就是我爱你,并且我知道,你也爱我,接下来我们以此来模拟三次握手的过程: 为什么握手不能是两次呢? 如果只有两次握手,女孩子可能就不知道,她的那句我也爱你,男孩子是否收到,恋爱关系就不能愉快展开。 为什么握手不能是四次呢? 因为握手不能是四次呢?因为三次已经够了,三次已经能让双方都知道:你爱我,我也爱你。而四次就多余了。 3. 讲下TCP四次挥手过程 第一次挥手(FIN=1,seq=u),发送完毕后,客户端进入FIN_WAIT_1 状态 第二次挥手(ACK=1,ack=u+1,seq =v),发送完毕后,服务器端进入CLOSE_WAIT 状态,客户端接收到这个确认包之后,进入 FIN_WAIT_2 状态 第三次挥手(FIN=1,ACK1,seq=w,ack=u+1),发送完毕后,服务器端进入LAST_ACK 状态,等待来自客户端的最后一个ACK。 第四次挥手(ACK=1,seq=u+1,ack=w+1),客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT状态,等待了某个固定时间(两个最大段生命周期,2MSL,2 Maximum Segment Lifetime)之后,没有收到服务器端的 ACK ,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入 CLOSED 状态。服务器端接收到这个确认包之后,关闭连接,进入 CLOSED 状态。 4. TCP挥手为什么需要四次呢? 举个例子吧! ★… Continue reading 面试必备!TCP协议经典十五连问!

Linux 篇: Epoll 实现的多路复用为什么这么快?

坚持思考,就会很酷 在 Linux 系统之中有一个核心武器:epoll 池,在高并发的,高吞吐的 IO 系统中常常见到 epoll 的身影。 IO 多路复用   在 Go 里最核心的是 Goroutine ,也就是所谓的协程,协程最妙的一个实现就是异步的代码长的跟同步代码一样。比如在 Go 中,网络 IO 的 read,write 看似都是同步代码,其实底下都是异步调用,一般流程是: write ( /* IO 参数 */ )     请求入队     等待完成       后台 loop 程序     发送网络请求     唤醒业务方 Go 配合协程在网络 IO 上实现了异步流程的代码同步化。核心就是用 epoll 池来管理网络 fd 。 实现形式上,后台的程序只需要 1 个就可以负责管理多个 fd 句柄,负责应对所有的业务方的 IO 请求。这种一对多的 IO 模式我们就叫做 IO 多路复用。 多路是指?多个业务方(句柄)并发下来的 IO 。 复用是指?复用这一个后台处理程序。 站在 IO… Continue reading Linux 篇: Epoll 实现的多路复用为什么这么快?