这可能是一个愚蠢的问题,但我和几个伙伴一直在讨论TCP的潜在限制。我们有一个应用程序将监听客户端(例如网关),并将所有连接的客户端数据通过单个连接的kafka发布者路由到一个主题。

我的一个好友说TCP将对于此网关而言,这是一个问题,因为它将为其发送的每条消息建立新的连接(不是kafka,而是底层的传输协议本身就是问题),每次都需要一个新的端口。按照我们将向这些客户端发送消息(千兆字节)的速度,kafka将用光所有端口以供读取???我想对TCP的工作原理有一个较低的了解(我认为我有)。我的理解是,当您建立TCP连接时,该连接将保持打开状态,直到应用程序将其超时或服务器或客户端强行关闭该连接为止。通过此连接发送的数据是流,无论3 V(体积,速度,变化)如何,都不会打开/关闭新连接。

就端口而言,一个端口是用于广播和内部文件描述符端口是应用程序管理的各个客户端的读/写操作。我从不了解TCP为它写入的每个数据包建立新的连接。

如果这个问题不是直接的,或者太含糊,我先向您道歉。我真的很困惑,希望有人能提供更多我同事所说的背景?

评论

我认为您误解了朋友在说什么。 TCP不会做这种事情,但是某个客户端可能会为要传递的每个消息建立一个新的TCP连接。

TCP不可能为每个数据包打开一个新的连接,因为它需要几个数据包才能打开一个新的连接。而且它无法为每条消息打开新的连接,因为TCP没有消息的概念。你的朋友很困惑。关于TCP的最重要的知识是最基本的概念,它是TCP是字节流协议。

您的伙伴的论点不一定是错误的-如果您不通过应用程序级保持活动复用端口,或者客户端太多,则系统可能会耗尽临时端口。有一些方法可以解决该问题:使用SO_REUSEADDR可以更快地关闭套接字,增加临时端口的范围等。此外,可以使用TCP_FASTOPEN和几个OS级别的切换来解决TCP的其他众所周知的限制。无论哪种方式,即使您没有要测试的工作量,也没有必要讨论TCP的局限性。

#1 楼


我的一个伙伴说TCP将是此网关的一个问题,因为它将为它发送的每个消息建立一个新的连接(不是kafka,而是底层的传输协议本身),需要一个新的每次移植。以我们将向这些客户端发送消息(千兆字节)的速度,kafka的端口将用光,无法从中读取数据?


您的朋友很困惑。 TCP是面向流的协议。它没有消息的概念。当然,它确实在IP层使用数据包,但是对于应用程序来说,这是一个实现细节。 TCP会在可能的情况下插入数据包边界,而不必每个write()send()插入一次。同样,如果在两次调用read()recv()之间收到多个分组,它将连续的数据包组合在一起。不用说,如果每个发送都建立一个新的连接,则这种面向流的设计将是完全不可行的。因此,建立新连接的唯一方法是手动关闭并重新打开连接。

(实际上,大多数基于TCP构建的协议都具有类似于消息的内容,例如HTTP请求和响应但是,TCP并不了解或不关心此类事物的结构。)

您的朋友可能想到的是UDP,它确实有消息,但也是无连接的。大多数套接字实现允许您将UDP套接字“连接”到远程主机,但这只是避免重复指定IP地址和端口的便捷方法。它实际上并没有在网络级别上做任何事情。不过,您可以手动跟踪在UDP下与哪些对等对象进行通信。但是,如果这样做,那么确定什么才是“连接”是您的问题,而不是操作系统的问题。如果要在每条消息上重新建立“连接”,则可以这样做。但是,这可能不是一个好主意。

#2 楼


我的理解是,当您建立TCP连接时,该连接会一直保持打开状态,直到该连接被应用程序超时或被服务器或客户端强制关闭为止。


从TCP的角度来看,没有客户端或服务器(客户端/服务器是在这里没有主题的应用程序概念)。 TCP在对等点之间建立连接,并且两个对等点都可以在该连接上进行发送和接收,直到任何一个对等点将其关闭或由于不活动而超时。


通过此连接发送的数据为流,无论3 V(体积,速度,
变化)如何,都不会打开/
关闭新连接。


可能会使情况混乱的原因是一些应用程序,例如浏览器将打开多个连接以同时加载网页元素之类的内容。

TCP不会为发送的每个段打开一个新的连接,但是应用程序可能会打开多个TCP连接。同样,当关闭TCP连接时,将释放连接中使用的TCP端口,并且可以再次使用它。此答案提供了一些信息,它使您指向TCP的RFC。

评论


尽管在TCP中,有一个伙伴发起连接(通常称为“客户端”),而另一个则发起连接(通常称为“服务器”)。当然,建立连接后,这种区别不再重要。

–PaŭloEbermann
18年3月14日在19:11

@PaŭloEbermann,TCP RFC中没有有关客户端或服务器的任何内容。客户端/服务器概念是一个应用程序概念。这里讨论的是OSI 4层或以下的协议,并且这些协议中没有客户端或服务器。实际上,您可能假定为客户端(打开TCP连接的客户端)实际上可能是应用程序服务器。我们有一些服务器会启动与客户端的TCP连接,以执行诸如安全检查和更新之类的操作。

–罗恩·莫潘♦
18-3-14在19:13



#3 楼

否,TCP不需要为每个发送的数据包打开一个新连接。

您可以通过HTTP持久连接发送多个数据包,其中:


... [用于]发送和接收多个HTTP请求/响应的单个TCP连接,而不是为每个请求/响应对打开新连接。


所附的图显示了多个连接(建立了许多连接以每个连接发送一个对象)和持久连接(建立了一个连接并在其中发送多个对象)之间的区别:



来源:https://www.vcloudnine.de/how-to-dramatically-improve-website-load-times/

评论


这个答案似乎令人困惑。 HTTP请求/响应很少是单个数据包。

– Barmar
18年3月13日在20:44

更不用说每个“打开”实际上是3个箭头(syn,synack,ack),每个“关闭”是另外4个箭头(fin,ack 2x服务器和客户端),因此,如果每个数据包实际上有一个连接,那么开销很快就会加起来。

– htmlcoderexe
18 Mar 14 '18 at 16:02

#4 楼

您对TCP的工作方式的解释是正确的。

关于您的朋友所说的,我在这里看到两种可能性:


您误解了所指的朋友应用程序层的一些限制,导致每条消息都通过新的连接发送(这并不一定非同寻常;根据您使用的软件堆栈,可能或不可能决定这种行为);
您的朋友错了。


#5 楼

正如其他人指出的那样,TCP绝对允许连接保持打开状态任何时间,在此期间在任何方向上交换任何数量的“消息”。就是说,最终要由应用程序(客户端和服务器)确定是否利用了该功能。

为了重用现有的TCP连接(套接字),客户端应用程序必须保留该套接字需要写入更多数据时,请打开并使用它。如果客户端不执行此操作,而是丢弃旧的套接字,并在每次需要时打开新的套接字,则确实会强制建立新的连接,如果连接频繁,可能会耗尽客户端或服务器上的资源问题要么,要么是TCP堆栈的连接池。

同样,服务器必须足够聪明,以保持套接字在其侧面打开并等待更多数据。像客户端一样,它可以选择关闭套接字,此时,希望发送更多数据的容错客户端别无选择,只能打开一个新的套接字,从而导致相同的问题。

最后,就像其他人提到的那样,TCP是面向流的。没有任何框架。仅仅因为一个对等方以特定方式写入数据(例如1 1024字节的写入调用,然后是2 256字节的写入调用),这并不能保证另一对等方将以相同大小的块读取数据(例如,它可能会获得全部1536字节)在一次阅读通话中)。因此,如果要通过原始TCP套接字发送多个“消息”,则必须提供自己的成帧协议来描绘不同的消息。尽管当然有简单的方法可以做到这一点,但是通常不建议这样做,因为有许多基于TCP的协议可以解决此问题。有关进一步的讨论,请参阅:https://blog.stephencleary.com/2009/04/message-framing.html

#6 楼

我认为您的朋友是在谈论HTTP,而不是TCP。

HTTP最初是无状态协议:每个HTTP请求都将使用单独的TCP连接。这就是为什么我们需要cookie(或类似的东西)来实现会话的原因。

#7 楼

您已经提到“单一连接,每次都需要一个新端口”,我会解释为您有许多客户端在同一网络环境中使用PAT技术来连接到组织外部的服务器。 PAT的限制为65535(IPv4地址上的TCP会话限制)。如果为true,则有限制。

TCP是否为发送的每个数据包打开一个新连接?
否,只要TCP会话有效,它就不会打开。和...

#8 楼

我喜欢TCP上出色的Wikipedia页面。它清楚地显示了端口号发生的情况。偶然的是,它还包含了有关资源使用情况的有用章节:

资源使用情况
大多数实现在表中分配一个条目,该表将会话映射到正在运行的操作系统进程。由于TCP数据包不包含会话标识符,因此两个端点都使用客户端的地址和端口来标识会话。每当接收到数据包时,TCP实现都必须在此表上执行查找以找到目标进程。该表中的每个条目都称为传输控制块或TCB。它包含有关端点(IP和端口),连接状态,有关正在交换的数据包的运行数据以及用于发送和接收数据的缓冲区的信息。
服务器端的会话数仅受以下限制:内存,并且会随着新连接的到达而增长,但是客户端必须在向服务器发送第一个SYN之前分配一个随机端口。此端口在整个会话期间保持分配状态,并有效地限制了来自每个客户端IP地址的传出连接数。如果某个应用程序无法正确关闭不需要的连接,则客户端可能会耗尽资源,甚至无法从其他应用程序建立新的TCP连接。

简而言之,TCP会占用一个非常有限的资源,这是客户端上的端口数(受TCP标头中port字段的大小限制,为16位)。
因此,如果客户端打开一个端口,则TCP可以用尽端口大量TCP并行连接而无需关闭它们。该问题仅发生在客户端,并且连接是否具有相同或不同的服务器IP地址或服务器端口都没有关系。
在您的设置中,您似乎有一个应用程序可以接收许多客户端请求(这些请求可以是单个TCP请求,因为您的客户端可能会使用该应用程序将一些事件记录到您的应用程序中,而不会保持它们之间打开的TCP通道)向您的Kafka代理创建一个新的内部请求(如果选择以这种方式实现,则很容易成为单个TCP连接)。在这种情况下,瓶颈(就资源而言,而不是性能)将是如果您设法同时从客户端获取大量请求(这对您来说没有问题,因为在服务器端,您只需要一个端口即可)所有这些),并且您向Kafka打开了大量转发请求,而Kafka无法足够快地处理它们,最终导致您同时打开了16位以上的连接。
自己的法官检查您的应用程序,并尝试确定您是否每次都通过单独的请求(可能是通过某些REST API代理)连接到Kafka。如果这样做,并且有大量客户,那肯定有危险。
如果只有少数客户(少于6万5千),和/或与Kafka保持单一连接浏览器,那就没问题了。