被动套接字和主动套接字

11

引用自此套接字教程:

套接字有两种主要类型。 主动套接字通过一个开放的数据连接与远程活动套接字相连 ... 被动套接字未连接, 而是等待传入的连接,一旦建立连接就会产生一个新的活动套接字 ...

每个端口可以有一个单独的被动套接字绑定到它上面,等待传入的连接, 还可以有多个活动套接字,每个套接字对应端口上的一个开放连接。 就好像工厂工人在等待新消息到达(他代表被动套接字), 当新的发送者发送一条消息时,他通过委派其他人(活动套接字)来实际读取数据包并在必要时回复发件人与他们进行通信。 这使得工厂工人可以自由接收新的数据包。 ...

然后该教程解释说,在建立连接之后,活动套接字会继续接收数据,直到没有剩余字节,然后关闭连接。

我不理解的是:假设有一个传入连接到端口,并且发送者想每20分钟发送一些小数据。 如果当没有剩余字节时,活动套接字关闭连接,那么发送者是否必须在每次想要发送数据时重新连接到端口? 我们如何保持一次已建立的连接更长时间? 你能告诉我我漏掉了什么吗?

我的第二个问题是,谁确定了同时工作的活动套接字的限制?


1
你在转述那篇文章,并从不同的部分摘取片段。上下文是不同的。在最后一节中,作者正在解释他的程序。套接字默认情况下不会像那样工作,事实上,如果你忘记关闭套接字,坏事情就会发生。当套接字接收到最后一个字节时,它不会自动关闭。 - SRM
好的,我以为那是惯例,所以问一下我在这里缺少什么。我对这些概念还很新,所以我想质疑所有我难以理解的东西。 - aslı
没问题,我只是想确保你明白必须显式关闭套接字。当你挠头试图弄清楚为什么套接字没有关闭时,这可能会节省一些麻烦:)。 - SRM
3个回答

9
发送方应该定期发送KEEPALIVE数据包以保持连接活跃。KEEPALIVE的格式取决于协议。在TCP数据段中,它可以只是一个单独的NULL。
至于第二个问题...这取决于I/O。如果是阻塞I/O,则您只需要在计算机上运行一定数量的线程,因此您将无法拥有许多客户端。如果是非阻塞的,则可以有更多的客户端。编程语言应该支持阻塞和非阻塞I/O。(我知道Java确实支持。)
这还取决于带宽、每个客户端的数据传输、内存、时钟速度等等。但是非阻塞与阻塞可以对您可以接受的客户端数量产生巨大影响。如果您阻塞了5-10个客户端,您的服务器可能会崩溃...但如果您不阻塞,则可以拥有数千个客户端。

1
实际上,如果您有很多客户端连接,并且每个请求都很快(就像您所说的20秒),那么最好使用请求/响应类型模式。您只有64k可用端口,这意味着在IP上仅能接受64k个套接字,除非这些套接字被关闭,否则会出现端口耗尽的情况。但这真的取决于您的应用程序。例如,如果您正在编写MMO游戏,则需要持久连接。但是,如果您正在编写Web服务器,则不需要(并且将尝试避免)持久连接(除非您正在使用HTML5,但那是另一回事)。 - SRM
2
从客户端的角度来看,对单个服务器进行简单的保持活动并不需要太多工作。当您连接到互联网时,您可能已经定期发送了半打保持活动。从服务器的角度来看,将套接字列表中的套接字数量保持最少可能会有利于提高I/O性能。如果数据传输之间等待时间长达20分钟,我会每次创建一个新连接。这对客户端来说微不足道,但对服务器来说却不是这样。 - ktm5124
1
你只能同时接受最多64k个连接,只要一些客户端断开连接,就不会达到端口耗尽的情况。此外,只有被动套接字可以接受连接,每个IP/端口对只能关联一个被动套接字,因此你只能使用一个套接字进行监听,但是可以使用多个套接字进行连接。 - SRM
哦,那我表达得不对。我的意思是,假设我要启动一个线程,在每个被动套接字接受连接时执行foo()函数。由于每个端口都有一个被动套接字,所以我最多可以同时拥有64k个套接字,但可能会有超过64k个线程执行foo()函数,这取决于foo()函数何时结束。现在更清楚了,谢谢大家:] - aslı
1
啊,是的,你说的完全正确。这就是队列机制和线程池派上用场的地方。你还可以使用命令模式的变体,其中你的命令处理程序是响应处理程序,而你的命令模式是使用线程池实现的。 - SRM
显示剩余5条评论

7
请不要混淆TCP/IP实现在网络上传输的实际数据包和程序与实现TCP/IP的库之间的交互。
套接字只是TCP/IP实现(库或内核操作系统)向您的程序提供的一种抽象。您可以将套接字视为连接到管道(localIP:port-remoteIP:port)。您的程序打开套接字,通过套接字通信数据,并可能在不再需要时关闭套接字以帮助释放资源。这是正常的流程。但是,TCP/IP实现可能出于其自己的有效原因关闭套接字。其中一些原因:网络访问电缆断开,网络路由错误,服务器宕机等。因此,即使程序没有关闭套接字,您的程序也可能发现tcp/ip套接字已关闭。
现在你的第一个问题是,如果我的程序发送小的数据段并且它们之间有很长的暂停时间,我该怎么办?答案是:取决于暂停的时间和其他程序在另一端监听的内容。大多数TCP/IP实现都有连接超时的概念,以为您提供可靠连接的抽象,在真正不可靠的网络上。因此,如果您的程序暂停的时间超过tcp/ip超时时间,您将发现库已关闭套接字,并且您需要重新打开套接字。这也可能导致您重新开始通信,具体取决于在tcp/ip连接管道的另一侧监听您的程序。
有多种方法可以增加tcp/ip超时时间并保持其活动状态。这些可能作为网络配置的一部分,在另一端的服务器软件配置或通过显式要求设置KEEPALIVE参数来保持套接字打开的方式进行。它是否仍然打开取决于具体情况。如何保持tcp/ip套接字打开的全部细节不应使您感到困惑,因为它与您的代码无关。TCP/IP有许多设置和不同的超时时间,以为您的程序提供稳定可靠连接的假象。好处是只要您不滥用它,它就会隐藏在您的程序代码中。将暂停保持在几秒钟以下:)一组超时设置可能适用于可靠的本地网络中的小型应用程序,并且对于高负载应用程序或跨大陆连接将无法工作。每个特定情况都有自己的解决方案,通常不止一个。
在这个特定的问题“每20分钟发送一些小数据”中,我建议您为每个通信关闭并打开套接字连接。打开一个的时间少于一秒钟,不应影响您的通信。作为回报,您在通信协议中获得了更少的复杂性。接收器始终在新的套接字连接上启动,两个系统都可以在您不需要它的所有20分钟内享受tcp/ip通信中的免费资源。

0
第一个问题: 是的,一旦套接字关闭,您必须进行打开以重新启动通信。
第二个问题: 是的。如果您愿意,可以创建64k个连接到您的服务器并遭受端口耗尽(我不建议这样做)。正如ktm5124所说,这完全取决于您的应用程序。有几种不同的方法可以使您的服务器可扩展,包括使用异步I/O和/或线程池来处理客户端请求。

请参考Will在stackoverflow上的回答https://dev59.com/8nE95IYBdhLWcg3wY85l#2332756,也许您对TCP有所误解。 - onmyway133
好的,如果TCP/IP端口耗尽只是客户端的事情(如每个服务器端口每个客户端的64k限制所示),那么为什么服务器上会存在真实的端口耗尽问题呢?您仍然可以使用元组的所有值 - 也许我的64k数字是错误的,但是元组可以容纳的组合数量有一个硬性限制。当您用完组合时,除非您正在重用地址,否则您将用完地址(真正的唯一元组)来分配传入连接,并且连接将被拒绝。 - SRM

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接