当 tcp/udp 服务器的发布速度快于客户端的消耗速度时会发生啥?

Posted

技术标签:

【中文标题】当 tcp/udp 服务器的发布速度快于客户端的消耗速度时会发生啥?【英文标题】:what happens when tcp/udp server is publishing faster than client is consuming?当 tcp/udp 服务器的发布速度快于客户端的消耗速度时会发生什么? 【发布时间】:2011-01-01 03:30:32 【问题描述】:

我试图了解当服务器发布(通过 tcp、udp 等)的速度快于客户端使用数据时会发生什么。

在一个程序中,我知道如果生产者和消费者之间有一个队列,它会开始变大。如果没有队列,那么生产者根本无法生产任何新东西,直到消费者可以消费(我知道可能会有更多变化)。

我不清楚当数据离开服务器(可能是不同的进程、机器或数据中心)并发送到客户端时会发生什么。如果客户端根本无法足够快地响应传入的数据,假设服务器和消费者非常松散耦合,那么传输中的数据会发生什么?

我在哪里可以阅读以获取有关此主题的详细信息?我只需要阅读 TCP/UDP 的底层细节吗?

谢谢

【问题讨论】:

RWIN(TCP 接收窗口)是计算机可以在不确认发送者的情况下接受的数据量。如果发送者没有收到它发送的第一个数据包的确认,它将停止并等待,如果这个等待超过一定的限制,它甚至可能重新发送。这就是 TCP 实现可靠数据传输的方式。 UDP 没有类似的功能,因为接收到 UDP 数据包并不重要 【参考方案1】:

服务器不能长时间比客户端快。在它比客户端快了一段时间后,它所在的系统会在它写入套接字时阻塞它(写入可以阻塞满缓冲区,就像读取可以阻塞空缓冲区一样)。

【讨论】:

【参考方案2】:

TCP Wikipedia article 显示 TCP 标头格式,其中保存了窗口大小和确认序列号。其余字段和那里的描述应该很好地概述了传输节流的工作原理。 RFC 793 指定基本操作;第 41 和 42 页详细介绍了流量控制。

【讨论】:

【参考方案3】:

TCP 有一个称为flow control 的功能。

作为 TCP 协议的一部分,客户端告诉服务器在不填满缓冲区的情况下可以发送多少数据。如果缓冲区填满,客户端会告诉服务器它还不能发送更多数据。一旦缓冲区被清空一点,客户端就会告诉服务器它可以再次开始发送数据。 (这也适用于客户端向服务器发送数据时)。

另一方面,UDP 则完全不同。 UDP 本身不会做这样的事情,如果数据进来的速度快于进程可以处理的速度,它将开始丢弃数据。如果它不能丢失数据(即,如果它需要一个“可靠的”数据流),则由应用程序向应用程序协议添加逻辑。

【讨论】:

【参考方案4】:

对于 TCP,有一个 TCP Window 用于流量控制。 TCP 一次只允许一定数量的数据保持未确认状态。如果服务器生成数据的速度快于客户端消耗数据的速度,则未确认的数据量将增加,直到 TCP 窗口“满”,此时发送 TCP 堆栈将等待并且不会再发送任何数据,直到客户端确认一些待处理的数据。

UDP 没有这样的流量控制系统;毕竟这是不可靠的。客户端和服务器上的 UDP 堆栈如果愿意,都可以丢弃数据报,它们之间的所有路由器也是如此。如果您发送的数据报多于链接可以传递给客户端的数据,或者如果链接传递的数据报多于您的客户端代码可以接收的数据报,那么其中一些将被丢弃。除非您在基本 UDP 上构建了某种形式的可靠协议,否则服务器和客户端代码可能永远不会知道。尽管实际上您可能会发现数据报不会被网络堆栈丢弃,并且 NIC 驱动程序只是简单地咀嚼所有可用的非分页池并最终使系统崩溃(请参阅this blog posting for more details)。

回到 TCP,您的服务器代码如何处理 TCP 窗口变满取决于您使用的是阻塞 I/O、非阻塞 I/O 还是异步 I/O。

如果您使用阻塞 I/O,那么您的发送调用将阻塞,您的服务器将变慢;实际上,您的服务器现在与您的客户端同步。在客户端收到待处理的数据之前,它不能发送更多数据。

如果服务器使用非阻塞 I/O,那么您可能会收到一个错误返回,告诉您调用将被阻塞;您可以做其他事情,但您的服务器需要在以后重新发送数据...

如果您使用异步 I/O,那么事情可能会更复杂。例如,对于在 Windows 上使用 I/O 完成端口的异步 I/O,您根本不会注意到任何不同。您的重叠发送仍然会被接受,但您可能会注意到它们需要更长的时间才能完成。重叠的发送正在您的服务器计算机上排队,并且正在为您的重叠缓冲区使用内存,并且可能也使用了“非分页池”。如果您继续发出重叠发送,那么您将面临耗尽非分页池内存或使用可能无限量的内存作为 I/O 缓冲区的风险。因此,使用异步 I/O 和服务器生成数据的速度可能快于其客户端使用数据的速度,您应该编写自己的流控制代码,并使用写入的完成来驱动这些代码。我已经在我的博客here 和here 上写过这个问题,我的服务器框架提供了自动为您处理的代码。

就“传输中”的数据而言,双方的 TCP 堆栈将确保数据按预期到达(即有序且没有丢失任何内容),他们将在需要时通过重新发送数据来做到这一点.

【讨论】:

【参考方案5】:

如果您真的想了解 TCP,您几乎需要结合 RFC 阅读实现;实际的 TCP 实现并不完全符合规定。例如,Linux 有一个“内存压力”概念,它可以防止内核的(相当小的)DMA 内存池用完,并且还可以防止一个套接字运行任何其他套接字的缓冲区空间不足。

【讨论】:

【参考方案6】:

使用 TCP,这不会发生。

在 UDP 的情况下,数据包会丢失。

【讨论】:

以上是关于当 tcp/udp 服务器的发布速度快于客户端的消耗速度时会发生啥?的主要内容,如果未能解决你的问题,请参考以下文章

graylog 客户端的安装配置

TCP/IP中的传输层协议TCPUDP

客户端 SOCKET 编程

python学习之TCP/UDP

socket网络编程:传输层详解(TCP / UDP)

python怎么建立socket服务端