C#套接字 vs 管道

13

目前我正在Windows上开发一个多进程桌面应用程序。这个应用程序将是一个封装好的应用程序,将部署在世界各地的客户机器上。虽然我们可以对机器有广泛的规格要求——例如Windows XP SP3与.Net 4.0 CF,但我们无法控制它们,并且我们不能过于具体地规定它们的配置——例如我们不能指定机器必须具有cuda 1.4的图形处理器等。

其中一些进程是托管的(.Net 4.0),而其他进程是非托管的(C++ Win32)。这些进程需要共享数据。我到目前为止评估的选项是:

  • Tcp sockets
  • 命名管道

管道似乎表现稍微好一些,但对于我们的需求——两者的性能都是可接受的。而套接字给了我们在未来跨机器(和操作系统——我们希望最终支持非Microsoft的操作系统)的灵活性,因此我们更喜欢使用套接字。

然而——我的主要担忧是——如果我们使用Tcp sockets——我们是否可能会遇到防火墙问题?有没有其他人部署过使用TCP进行IPC的桌面应用程序/程序,并遇到问题?如果有——是什么样的问题?

我知道这是一个相当开放的问题,我很乐意重新表述。但我真的想知道我们可能会遇到什么样的潜在问题。

编辑:为了更好地阐明——我们只传输一些POD(ints,floats和字符串)。我们构建了一个抽象层,提供了两种模式——请求/响应和订阅。传输层已经被抽象化,目前我们有两个实现——基于管道和基于TCP。


请明确一点,这只是在单台机器上进行,所以您不会使用命名管道在桌面之间进行通信?还是也会有一些网络通信? - Kevin Anderson
目前 - 所有的进程都在一台机器上运行。最终 - 在很长的路程中 - 我们将在不同的机器上运行这些进程。 - quixver
你需要确保你的设计是面向未来的 :) 除非“未来”只是理论上的... - bryanmac
@bryanmac - 我同意。这就是为什么我们倾向于使用套接字的原因...除非在实际应用中支持它会带来真正的麻烦。 - quixver
你最终得到了什么? - bryanmac
1
抱歉没有及时更新 - 我们最终选择了TCP/IP。性能相当不错,我们还使用了Google的protobuf(同时使用c++和c#)。到目前为止一切都很顺利:] - quixver
3个回答

8

管道在快速局域网上的性能通常更好,但TCP在较慢的网络或广域网上通常更好。请参见下面的msdn点。

TCP也更可配置。关于防火墙,它们允许您打开/关闭通信端口。如果这不是一个选择或关注的问题,另一种选择将是http(REST / json、Web服务、XML-RPC等),但您必须考虑http开销是否可接受。确保您尝试使用真实世界数据集(在测试中传递琐碎的数据会使开销看起来不合理,而在真实世界数据集中则非常合理)。

来自msdn的其他一些信息:

在快速的本地局域网(LAN)环境中,传输控制协议/互联网协议(TCP/IP)套接字和命名管道客户端在性能方面是可比的。然而,在较慢的网络(如跨广域网(WAN)或拨号网络)中,TCP/IP套接字和命名管道客户端之间的性能差异变得明显。这是因为进程间通信(IPC)机制之间通信方式的不同。
对于命名管道,网络通信通常更加交互式。一个对等体只有在另一个对等体使用读取命令请求时才发送数据。网络读取通常涉及一系列的查看命名管道消息,然后才开始读取数据。这些在慢速网络中可能非常昂贵,并导致过多的网络流量,从而影响其他网络客户端。
还重要的是澄清你是否谈论本地管道或网络管道。如果服务器应用程序在运行Microsoft® SQL Server™ 2000实例的计算机上本地运行,则本地命名管道协议是一个选项。本地命名管道在内核模式下运行,非常快。
对于TCP/IP套接字,数据传输更加流畅,开销更小。数据传输还可以利用TCP/IP套接字性能增强机制,如窗口化、延迟确认等,这在慢速网络中非常有益。根据应用程序的类型,这样的性能差异可能是显著的。
TCP/IP套接字还支持一个积压队列,可以提供与命名管道相比的有限平滑效果,当您尝试连接到SQL Server时可能会导致管道繁忙错误。
总的来说,在慢速LAN、WAN或拨号网络中,套接字更受欢迎,而在网络速度不是问题时,命名管道可以是更好的选择,因为它提供了更多的功能、易用性和配置选项。
有关TCP/IP的更多信息,请参阅Microsoft Windows NT®文档。

感谢 Bryan 指出性能方面的问题!我们并没有真正考虑到网络速度。迄今为止,我们的性能测试主要集中在同一台机器上运行不同进程上。我们并没有为分布式场景投入太多的测试工作。 - quixver

2
如果您需要模拟命名管道客户端的安全凭据,那么真正的选择只有一种 :) 命名管道也有更好的名称(尽管 DNS SRV 记录也可以为 TCP 端口提供这些名称)。
否则,它们之间没有太大区别。两者都将数据视为字节流,因此您需要自己负责查找消息边界。命名管道还有一个额外的选项,可以为您保留消息边界,但请注意,您必须在消息模式下创建管道,并显式设置读取模式。

除非我误解了你的意思,否则这并不完全正确。命名管道可以在字节模式或消息模式下创建。在消息模式下,每个对管道的WriteFile操作都成为一个单独的消息。 - Carey Gregory
@Carey:我认为他在谈论字节模式下的管道,因为它与TCP而不是UDP进行比较。然而,你说得对,消息模式可用,所以我把它加到了我的答案中。 - Ben Voigt
@BenVoigt:我在主要观点上同意你的看法,但确切地说,你可以使用SSPI(GSSAPI的专有变体)在Socket通信中发送安全令牌。代码示例在这里。该代码对于大多数实际用途来说过于棘手和复杂,但仍然是可能的。 - Oleg

1

如果我正确理解您的要求,您需要在同一台计算机上运行的进程之间进行通信。这些进程可能都在交互式登录的用户的相同安全上下文中运行。

在这种情况下,我应该提到解决方案的不同方面。一个问题只是共享应用程序之间的数据。另一个问题是协议,它定义了如何访问和修改公共数据以及进程之间的通信方式。例如,您可以有一个提供数据的进程,而所有其他进程都订阅数据。另一种情况:您可以有共享数据,所有应用程序都可以读取或修改,您只需要确保没有人在同时修改共享数据或在另一个人修改数据时访问数据。当然,还可以有许多其他不同的通信场景。

在这个角度下,我建议您另外考虑两个选项,这些选项在您的问题中没有包括:

  • 使用内存映射文件(请参见此处此处
  • 使用COM接口

这两种方法都可以在.NET和非托管C++中很好地实现。从性能角度来看,使用内存映射文件是最好的方式。如果您创建的视图不与某个物理文件关联,则会有一个普通的内存块可供进程间使用。您还可以使用互斥体或事件来控制多个应用程序同时使用该内存块的情况。

在最简单的情况下,您甚至可以在C++中使用#pragma data_seg将一些数据放置在DLL的命名部分中,并使用/SECTION选项(如/SECTION:.MYSEC,RWS)使数据共享。您可以在所有.NET应用程序和所有非托管C++应用程序中使用DLL来访问公共数据。这样,您就可以简单地访问公共数据。
如果您需要更复杂的通信场景,则使用C++/.NET中的COM接口可能是最佳选择。在这种情况下,我建议您阅读该文章,该文章逐步描述了如何仅在.NET中实现主互操作程序集与COM接口,并在.NET和C++ COM中使用它进行通信。

嗨,Oleg,感谢你提出内存映射文件的问题。然而,我认为我们不会走这条路,因为管道和套接字(在同一台机器上)内部都使用内存映射文件 - 而且我们可以接受开销。我们更喜欢管道和套接字给我们的灵活性 - 因为我们将来可以将各个进程移动到不同的机器上。至于COM - 不幸的是,我对它有偏见...太多年支持使用DCOM的应用程序了;) - quixver
哦 - 我们也希望最终能够将一些应用程序移植到非微软平台上的选项。 - quixver
@quixver:说“管道和套接字(在同一台机器上)都内部使用内存映射文件”是不正确的。套接字根本不使用内存映射文件,而命名管道则比看起来复杂得多。如果您只是检查CreateNamedPipe的参数,您就会发现它。我只是使用了您在问题中写的信息。您提到了“Windows XP SP3 with .Net 4.0 CF”,但没有提到“非Microsoft平台”和“Windows上的多进程桌面应用程序”。如果您需要在不同计算机之间进行通信,则应编写它。 - Oleg
@quixver: ... 另一方面。在这种情况下,COM 将非常好,因为它最初是在 RPC(远程过程调用)中开发的接口定义语言。我想让您注意问题所在。 - Oleg
@quixver:重要的是了解数据结构(例如复杂性)的更多信息,需要共享。如果存在安全方面,则也很重要。通信协议也完全未知:一个创建数据,发送到另一个进程,另一个读取,修改并发送回等等。这是一对多通信还是多对多通信...如果您将通信实现为多个函数调用,您将如何描述通信和输入、输出和输入/输出参数?可能数据是原始的,通信也是如此,只需写下即可。 - Oleg
显示剩余2条评论

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