本文的重点是详细介绍TCP三次握手传输的数据流程图。原文根据示例代码非常详细,对大家的学习培训或者工作都有一定的参考价值,有需要的朋友可以参考一下。
RFC793文本文档中带有SYN标记的进程包不能携带数据,换句话说,三次握手的前两次不能携带数据(逻辑上连接还没有创建,携带数据似乎有点不合理)。关键是三次握手能否承载数据。
先说结果:TCP协议创建连通三次握手过程中的三次握手是允许携带数据的。
对比上面TCP状态转移图的连接创建部分,我们来看看RFC793文本文档的名字。RFC793文本文档给出了以下术语(省略不重要的部分):
关键是这句话“可以包括排队等待传输的数据或控制”。换句话说,规范表明三次握手的ACK包可以携带数据。
首先,三次握手的数据包是由连接发起方(以下简称手机客户端)发送给端口号监控器(以下简称服务器)的,所以只需要在一个连接处于SYN-RECV(图中的SYN_RECEIVED)状态时,找到接收到数据包后核心tcp协议的求解过程即可。经过一番查找,发现位于net\ipv4文件目录下的tcp_input.c文件中的tcp_rcv_state_process函数解决了这个过程。如图所示:
这个号其实就是一个TCP状态机,用来解决TCP连接在各个状态下接收数据包的问题。下面是几个并列开关句,由于其长涵数,容易误读层次关联。下图是简化不必要的编码后SYN-RECV状态的求解过程:
一定要注意,这两个开关句是并排的。因此,当TCP_SYN_RECV状态接收到合理合法的标准第二次握手包时,它会立即将套接字状态设置为TCP_ESTABLISHED状态,当实现下面的TCP_ESTABLISHED状态用例时,其中包含的数据(如果有)会被再次求解。
如上所述,当手机客户端发送的三次握手的ACK中包含数据时,服务器可以正常解决一切。手机客户端呢?我们来看看手机客户端在SYN-SEND状态下,如何推送第三个ACK包。如图所示:
tcp_rcv_synsent_state_process函数的完成比较长,最后的关键链接马上贴在这里:
是不是一目了然?如果标准不满足立即响应独立ACK包的要求,如果考虑任何标准,则应使用inet_csk_reset_xmit_timer函数来设置定时器等待瞬变的时间。在此期间,如果有数据,会伴随有数据pushACK,但不会有数据responseACK。
之前的疑惑算解决了。
标准1:SK-->:SK_write_pending!=0
这个值的默认设置是0,那么是什么原因导致它不是0呢?答:当tcp协议推送数据的函数遇到socket状态不成立时,实际上会 *** 作这个自变量,等一小会儿再尝试推送数据。看图:
net/core/stream.c中的sk_stream_wait_connect函数做了以下实际 *** 作:
sk->;Sk_write_pending增长,套接字连接达到建立状态后传输数据。这一点很清楚。
Linuxsocket的默认设置以阻塞的方式工作。也就是说,手机客户端的连接激活在默认设置条件下是可以被阻止的,直到三次握手过程完成或者不正确才会返回。那么nc这个完全通过阻塞socket来完成,并且没有改变默认设置socket的主要参数的cmd微信小程序,就会老老实实的等待connect成功或者不成功再推送数据,这也是我们无法捕捉到三次握手所包含的数据的原因。
然后,设置非阻塞套接字,连接后立即发送数据。如果连接过程不是瞬间成功,可能有机会看到第三次握手有数据。但是,即使开源系统的互联网库没有阻塞套接字,它也会监控应该写入套接字的恶性事件,只有再次连接成功才会写入数据。为了挽救这个基本可以忽略的特性,真的比可靠编码更有价值。
标准2:ICSK-->:icsk_accept_queue.rskq_defer_accept!=0
这个标准很奇怪。defer_accept是一个套接字选项,用于延迟接受。事实上,只有在接收到第一个数据后,才会建立连接。Tcp_defer_ACCEPT一般用在服务器端,会危及socket的SYN和ACCEPT序列。如果没有设置默认设置,当进行三次握手时,套接字将进入接受序列,网络层将识别与接受相关的连接。当tcp_defer_ACCEPT置位时,三次握手完成,socket不进入ACCEPT序列,而是立即停留在SYN序列中(有长度限制,超过核心会拒绝新的连接),直到数据实际发送后再放入ACCEPT序列。设置了这个主参数的服务器在接受后可以立即读取,所以它必须有数据并保存一个系统进程。
SYN序列存储SYN_RECV状态的socket,其长度由net.ipv4.tcp_max_syn_backlog的主要参数控制。accept序列使能时,backlog的基本参数和核心硬限制由net.core.somaxconn限定,即具体值由min(backlog,somaxconn)决定。
有趣的是,如果手机客户端先绑定一个端口号和IP,然后setsockopt(TCP_DEFER_ACCEPT)再连接网络服务器,此时就会出现rskq_defer_accept=1的情况。这时内核会设置一个定时器,等待数据一起响应ACK包。我以前从来没做过。难道只是为了更好的减少ACK的快递空包推送来改善特性?谁知道呢?请告诉我。谢谢你。
标准三:ICSK-->:icsk_ack.pingpong!=0
其实pingpong也是一个socket选项,用来表示当前连接是否是交互数据流。如果它的值为1,则它是交互数据流,并且将应用延迟时间确定系统。
文章里的内容就这些了。期待对大家的学习和培训有所帮助,也期待大家的应用。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)