Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sockets socket Receive如何在较低级别(例如socket.recv(1024))工作?_Sockets_Tcp - Fatal编程技术网

Sockets socket Receive如何在较低级别(例如socket.recv(1024))工作?

Sockets socket Receive如何在较低级别(例如socket.recv(1024))工作?,sockets,tcp,Sockets,Tcp,我读过很多类似的堆栈溢出问题,但我认为这些答案都不能满足我的好奇心。我有一个下面的例子,我想得到一些澄清 假设客户端在socket.recv(1024)上阻塞: 另外,假设我有一个服务器向客户机发送600字节。让我们假设这600字节被分成4个小数据包(每个150字节),并通过网络发送。现在假设数据包到达客户端的时间不同,相差0.0001秒(例如,一个数据包到达下午12.00.0001pm,另一个数据包到达下午12.00.0002pm,依此类推) socket.recv(1024)如何决定何时将执

我读过很多类似的堆栈溢出问题,但我认为这些答案都不能满足我的好奇心。我有一个下面的例子,我想得到一些澄清

假设客户端在socket.recv(1024)上阻塞:

另外,假设我有一个服务器向客户机发送600字节。让我们假设这600字节被分成4个小数据包(每个150字节),并通过网络发送。现在假设数据包到达客户端的时间不同,相差0.0001秒(例如,一个数据包到达下午12.00.0001pm,另一个数据包到达下午12.00.0002pm,依此类推)


socket.recv(1024)如何决定何时将执行返回到程序并允许print()函数执行?它是否在收到第一个150字节的数据包后立即返回执行?或者它会等待任意的时间量(例如1秒,到那时所有数据包都已经到达)?如果是,这“任意时间”是多长?谁来决定呢?

只要至少有一个字节要返回给调用方,TCP连接上的正常阻塞接收就会返回。如果调用者想要接收更多字节,他们可以简单地再次调用receive函数。

这取决于很多因素,包括操作系统和网络接口的速度。对于100千兆接口,100us是“永远的”,但对于10 mbit接口,您甚至不能以如此快的速度传输数据包。所以我不会太注意你指定的确切时间

早在TCP被设计的那一天,网络速度很慢,CPU很弱。在TCP报头中的标志中有一个“推送”标志,表示应立即将有效负载交付给应用程序。所以如果我们跳进Waybak 机器的答案可能是类似的,这取决于包中是否设置了PSH标志。但是,通常没有用户空间API来控制是否设置标志。通常会发生的情况是,对于被分成多个数据包的单个写入,最终的数据包将设置PSH标志。因此,对于缓慢的网络和脆弱的CPU来说,答案可能是,如果是单次写入,应用程序可能会收到600字节。然后,您可能会认为使用四次单独的写入将导致四次150字节的单独读取,但在引入Nagle算法后,第二次到第四次写入的数据可能会在一个数据包中发送,除非使用TCP_NODELAY套接字选项禁用Nagle算法,因为Nagle的算法将在发送任何小于完整帧的内容之前等待第一个数据包的ACK

如果我们从Waybak机器之旅回到现代,100千兆接口和24个核心机器很常见,那么我们的问题就大不相同了,您将很难找到Linux内核中设置的PSH标志的显式检查。推动接收端设计的是,网络速度越来越快,而数据包大小/MTU在很大程度上是固定的,CPU速度是平坦的,但核心却非常丰富。减少每个数据包的开销(包括硬件中断)并在多个核心之间高效地分发数据包是必要的。同时,必须尽快将数据从100+千兆消防软管传输到应用程序。在这样一个nic上,100微秒的数据是一个相当大的数据量,可以毫无理由地保存

我认为有这么多形式的问题“接收到底做了什么?”的原因之一是很难理解什么是完全异步的过程,其中,发送端有一个更熟悉的控制流,可以更容易地将数据包流跟踪到NIC,并且我们可以完全控制数据包何时发送。在接收端,数据包只在它们想要的时候到达

假设TCP连接已建立且空闲,没有丢失或未确认的数据,读卡器在recv上被阻止,读卡器正在运行Linux内核的新版本。然后一个写入程序将150字节写入套接字,150字节在一个数据包中传输。到达NIC时,数据包将由DMA复制到环形缓冲区中,如果中断被启用,它将引发硬件中断,让驱动程序知道环形缓冲区中有新数据。希望在尽可能少的周期内从硬件中断返回的驱动程序禁用硬件中断,必要时启动软IRQ轮询循环,并从中断返回。来自NIC的传入数据现在将在轮询循环中处理,直到不再有数据要从NIC读取,此时它将重新启用硬件中断。此设计的一般目的是降低高速NIC的硬件中断率

现在这里是事情变得有点奇怪的地方,特别是如果您一直在看OSI模型的干净的图表,其中更高级别的堆栈可以干净地放在彼此的顶部。哦,不,我的朋友,现实世界远比这复杂。例如,您可能一直认为是简单的第2层设备的NIC知道如何将数据包从相同的TCP流定向到相同的CPU/环形缓冲区。它还知道如何将相邻的TCP数据包合并成更大的数据包(尽管Linux不使用此功能,而是在软件中完成)。如果你曾经看过一个网络捕获,看到了一个巨大的帧,然后抓挠了你的头,因为你确实认为MTU是1500,这是因为这个处理处于如此低的水平,它发生在netfilter能够得到数据包之前。这种数据包合并是称为接收卸载的功能的一部分,在pa中也是如此
socket.recv(1024)
print("Received")