从阻塞套接字更改为非阻塞套接字有啥好处?

Posted

技术标签:

【中文标题】从阻塞套接字更改为非阻塞套接字有啥好处?【英文标题】:What do a benefit from changing from blocking to non-blocking sockets?从阻塞套接字更改为非阻塞套接字有什么好处? 【发布时间】:2011-08-19 20:31:40 【问题描述】:

我们有一个使用 Delphi 2010 和 Indy 10 开发的应用服务器。该服务器每秒接收超过 50 个请求,并且运行良好。但在某些情况下,在我看来,Indy 非常晦涩难懂。他们的组件很好,但有时我发现自己深入研究源代码只是为了理解一件简单的事情。 Indy 缺乏良好的文档和良好的支持。 我遇到的最后一件事对我来说是一个大问题:我必须检测客户端何时非正常断开连接(例如,当客户端崩溃或关闭时。没有告诉服务器它将断开连接)并且 indy 无法去做。如果我愿意,我将不得不开发一种算法,如心跳、池或 TCP 保持活动。我不想花更多的时间做一个,至少我认为,组件工作。经过一番研究,我发现这不是 Indy 的错,而是所有阻塞套接字组件的问题。

现在我真的在考虑将服务器的核心更改为另一个好的套件。我必须承认我倾向于使用非阻塞套接字。基于此,我有一些问题:

从阻塞套接字更改为非阻塞套接字有什么好处? 我能否检测到客户端断开连接(非正常)? 哪个组件套件的产品最好?我所说的最佳产品是指:快速、良好的支持、良好的工具和易于实施。

我知道这一定是一个主观问题,但我真的很想听听您的意见。我的第一个问题是我最关心的问题。我不在乎我是否必须支付 100、500、1000、10000 美元,但我想要一个完整的解决方案。目前,我正在考虑Ip*works。

编辑

我认为有些人不明白我想要什么。我不想创建自己的套接字。我使用套接字已经很长时间了,我已经厌倦了。真的。

非阻塞套接字可以检测客户端断开连接。这是事实,它在整个互联网上都有很好的文档。非阻塞套接字始终检查套接字状态是否有新的传入数据,并且可以检测到套接字无效。这不是心跳算法。客户端使用心跳算法,它会定期向服务器发送数据包(也称为保持活动),以告知服务器仍然处于活动状态。

编辑

我没有说清楚。也许是因为英语不是我的主要语言。 我并不是说无需尝试从套接字发送或接收数据就可以检测到断开的连接。我的意思是,每个非阻塞套接字都能够做到这一点,因为它们不断尝试从套接字读取新的传入数据。 为什么这么难理解?如果你们下载并运行 ip*works 演示,特别是 echoserver 和 echoclient(都使用 TCP),您可以自己测试。我已经对其进行了测试,它的工作方式与我预期的一样。即使您在非阻塞模式下使用旧的 TCPSocketServer 和 TCPSocketClient,您也会明白我的意思。

【问题讨论】:

不优雅地断开连接到底是什么意思? 非阻塞套接字不能比阻塞套接字检测到丢失的连接 - 请参阅下面的答案。 如果由于客户端正常关闭而导致底层连接确实断开,PS Indy 可以很好地断开服务器连接。我有一些非常强大的 TCP 服务器,它们已经运行了多年,没有出现“掉线”连接的问题。最后,我认为在服务器上使用阻塞套接字有更多的优势——它使架构更清晰、更具可扩展性,所以我给你的建议是不要改变。 您可以在 Indy 中为客户端和服务器执行相同的操作。只需使用 IoHandler.CheckForDataOnSource 然后从套接字读取 IoHandler.InputBuffer.Size 字节。对于每个连接,我都在 10 毫秒的循环中运行。那么你不能用 Indy 做什么呢? @Rafael,无论其他点如何,您都无法通过从套接字读取来检测死客户端 - 为此,您必须写入它。读取是一种被动操作,无法检测到断开的连接。 【参考方案1】:

从阻塞套接字更改为非阻塞套接字有什么好处?

提高了速度、可用性和吞吐量(根据我的经验)。我有一个 IndySockets 客户端每秒收到大约 15 个请求,当我直接访问异步套接字时,吞吐量增加到每秒大约 90 个请求(在同一台机器上)。在具有 30 Mbit 连接的数据中心的服务器上进行的单独基准测试中,我每秒能够获得超过 300 个请求。

我能否检测到客户端断开连接(非正常)?

这是我还没有尝试过的一件事,因为我的所有代码都在客户端。

哪个组件套件的产品最好?我所说的最佳产品是指:快速、良好的支持、良好的工具和易于实施。

您可以在几天内构建自己的套接字客户端,它可以非常强大和快速......比我见过的大多数“现成”的东西都要快得多。随意看看我的异步套接字客户端:http://codesprout.blogspot.com/2011/04/asynchronous-http-client.html

更新: (根据 Mikey 的 cmets)

我要求您提供一个通用的技术解释,说明 NBS 如何提高吞吐量,而不是设计合理的 BS 服务器。

让我们以高负载服务器为例:假设您的服务器应该在任何给定时间处理 1000 个连接,使用阻塞套接字您将不得不创建 1000 个线程,即使它们大部分处于空闲状态,CPU 仍然会花费大量时间上下文切换。随着客户端数量的增加,您将不得不增加线程数量以跟上,CPU 将不可避免地增加上下文切换。对于您使用阻塞套接字建立的每个连接,您将产生产生新线程的开销,最终您将产生线程后清理的开销。当然,首先想到的是:为什么不使用 ThreadPool,您可以重用线程并减少创建/清理线程的开销。

这是在 Windows 上的处理方式(因此是 .NET 连接):当然可以,但是您会注意到 .NET ThreadPool 的第一件事是它有两种类型的线程,这不是巧合:用户线程和 I/O 完成端口线程。异步套接字使用 IO 完成端口,“允许单个线程在不同的句柄上同时执行 I/O 操作,甚至在同一个句柄上同时执行读写操作。”(1) I/O 完成端口线程是专门设计用于以比您在 ThreadPool 中使用用户线程时更有效的方式处理 I/O,除非您 wrote your own kernel-mode driver。

“完成端口使用一些特殊的巫术来确保一次只能运行特定数量的线程——如果一个线程在内核模式下阻塞,它将自动启动另一个线程。”(2)

还有其他优点:“除了重叠套接字 I/O 的非阻塞优势之外,另一个优势是更好的性能,因为您在 TCP 堆栈缓冲区和每个 I/O 的用户缓冲区之间保存了一个缓冲区副本称呼。” (3)

【讨论】:

感谢分享,但您的回答是基于客户端的观点。我确信我可以构建自己的非阻塞套接字,但我没有时间去做,而且我工作的公司并不关心我是否想购买套件,只要我证明我为什么需要这样的东西。 @Rafael,我了解您的情况,但我认为您会发现大多数套件都基于 .NET 套接字以及您必须编写的代码量为了在 .NET 套接字周围使用他们的包装器,与仅使用 .NET 套接字大致相同。如果我的答案不是 100% 您正在寻找的答案,那么如果您遇到与我在第 3 方解决方案方面遇到的相同问题,它可能仍然有用。 @lirik,感谢您的回答,我真的很喜欢。它帮助了我。但我必须不同意。那边有不错的delphi套件。 ipworks 就是其中之一。我真的不想把时间浪费在做别人可能已经做过的事情上。 @Rafael 没问题,我通常也不鼓励重新发明***,但这完全取决于您需要从第 3 方解决方案中挤出多少性能。祝你好运! @Lirik - 非阻塞如何提高速度?我的理解是一般使用线程阻塞套接字更适合服务器,非阻塞更适合前端客户端应用程序。【参考方案2】:

这篇文章解释了阻塞和非阻塞的主要区别:

Introduction to Indy, by Chad Z. Hower

屏蔽的优点

易于编程 - 阻塞非常易于编程。所有用户代码都可以 存在于一个地方,并且在一个 顺序。 易于移植到 Unix - 由于 Unix 使用阻塞套接字,因此可移植代码 可以很容易地写出来。印地使用这个 事实上实现其单一来源 解决方案。 在线程中运行良好 - 由于阻塞套接字是顺序的,它们 本质上是封装和 因此非常容易在线程中使用。

屏蔽的缺点

与客户端“冻结”的用户界面 - 阻止套接字调用不会 返回,直到他们完成 他们的任务。拨打此类电话时 在应用程序的主线程中, 应用程序无法处理 用户界面消息。这引起 用户界面“冻结”,因为 更新、重绘和其他消息 在阻塞之前无法处理 套接字调用将控制权返回给 应用程序消息处理循环。

他还写道:

阻止不是邪恶的

阻塞套接字已被重复 无证袭击。与之相反 普遍认为,阻塞套接字是 不邪恶。

不是an issue of all blocking sockets components 他们无法检测到客户端断开连接。在这方面,非阻塞组件方面没有技术优势。

【讨论】:

【参考方案3】:

Aahhrrgghh - 能够始终检测“断开”连接的神话。如果您在具有客户端连接的机器上拉下电源,则服务器无法在不发送数据的情况下判断连接“已死”。这是通过TCP协议的设计。不要相信我的话 - 阅读这篇文章 (Detection of Half-Open (Dropped) TCP/IP Socket Connections)。

【讨论】:

我并不是说可以在不发送或检查套接字的情况下检测断开的连接。我是说每个非阻塞套接字都通过不断地从新传入的数据中读取套接字来做到这一点。为什么这么难理解? @Rafael,首先您只能通过发送数据来检测断开的连接。其次,使用 Indy 可以实现相同的功能。这段代码我已经运行多年了,你可以在网上找到它。 有人能告诉我为什么我因为正确而被否决! 对于阻塞和非阻塞之间的区别仍然存在混淆。我已经阅读了 Indy 的介绍,所以我知道阻塞不是邪恶的 :) - swissdelphicenter.ch/en/showarticle.php?id=4 - 没有理由说明非阻塞套接字在检测连接状态时“更智能”【参考方案4】:

在 Windows 中,还有第三个选项是重叠 I/O。非阻塞套接字是使用 Windows 消息开发的模型的必要条件,该模型旨在避免单线程 GUI 应用程序在等待数据时被“阻塞”。恕我直言,现代应用程序最好使用线程和重叠 I/O 进行设计。

参见例如http://support.microsoft.com/kb/181611

【讨论】:

【参考方案5】:

我多年来一直在使用 Indy 和 Synapse TCP 库并取得了不错的效果,但在其中没有发现任何阻碍。我在线程中使用库 - 客户端和服务器端,稳定性和性能不是问题。 (服务器运行在同一系统上时,每秒有 6000 条请求和响应消息是典型的。)

如果协议比简单的“发送字符串/接收字符串”更高级,阻塞套接字非常有用。非阻塞套接字导致消息协议处理程序与套接字读/写逻辑的耦合度更高,因此我很快就放弃了非阻塞代码。

没有库可以克服 TCP/IP 协议在检测连接丢失方面的限制。只有尝试读取或发送数据才能判断连接是否仍然存在。

【讨论】:

正如我所说,非阻塞套接字可以做到这一点。他们不时检查套接字状态。 @Rafael,在检测阻塞连接方面,非阻塞套接字与阻塞套接字没有什么不同。 TCP 协议是专门设计的,因此连接不会在未插入的网络电缆和路由器崩溃时断开。这样可以在重新插入电缆或重新启动路由器时重新使用原始连接。请阅读我在答案中添加的链接。【参考方案6】:

“从阻塞套接字更改为非阻塞套接字有什么好处?我是否能够检测到客户端断开连接(非正常)?

只需我的两分钱就可以解决这个问题 - 我不是插座专家,但我确实对他们有很多经验。如果我弄错了,我相信有人会纠正我... :-)

我假设由于您使用每秒 50 个连接的阻塞套接字运行服务器,因此您有一个线程机制来处理客户端请求。如果是这样,你真的不能从非阻塞套接字中获得任何东西。相反 - 您将不得不将您的服务器逻辑更改为事件驱动 - 基于在您的主线程中从非阻塞套接字触发的事件,或者使用持续轮询来了解您的套接字在做什么。

非阻塞套接字无法检测到客户端在没有通知的情况下断开连接,就像阻塞套接字一样——它们没有心灵感应能力……客户端和服务器之间的 TCP/IP“对话”的性质是相同的——阻塞和非阻塞仅与您的应用程序与进行“对话”的套接字连接的交互有关。

如果您需要清除死连接,则需要在套接字上实现心跳或超时机制(我从未见过不支持超时的现代套接字实现)。

【讨论】:

没有。我根本不使用线程。 Indy 主读取事件是从线程调用的,这意味着我不必关心它。我也不关心是否必须更改服务器逻辑才能使用非阻塞套接字,只要我从中受益。我很确定非阻塞套接字可以检测到客户端断开连接,因为它们可以不时检查套接字状态,向它们发送小包。 -1,看看这个:forums.embarcadero.com/thread.jspa?threadID=49732。它证明了非阻塞套接字可以检测客户端断开连接。 我认为你的 -1 是没有根据的——“对于套接字状态不时向它们发送小包”——这称为心跳,阻塞套接字也可以这样做。我建议你再读一遍那个帖子。我也从免责声明开始。谢谢。 服务器端没有线程?这意味着所有请求都按顺序处理,客户端必须等待服务器套接字可用。还是我误会了? @mjn。 Indy 有自己的线程池。每个请求都在其自己的线程上下文中处理。他的意思是他没有实现自己的线程,而是使用内置于 Indy 中的 Indy 线程池。是的,应用程序仍然是多线程的 :)

以上是关于从阻塞套接字更改为非阻塞套接字有啥好处?的主要内容,如果未能解决你的问题,请参考以下文章

unix套接字中的异步和非阻塞有啥区别?

在 C 中使用非阻塞套接字连接

非阻塞套接字与IO多路复用

阻塞和非阻塞同步和异步

为啥非阻塞套接字在connect() 或accept() 之前是可写的?

为啥套接字不设置为非阻塞模式?