使用C++编写网络守护进程有哪些不足之处?

4
在过去的几年里,我写了许多不同语言的网络守护进程,现在我即将开始一个新项目,需要自定义实现一种专有的网络协议。
所述协议非常简单 - 一些基本的JSON格式消息,这些消息在一些基本的帧包装中传输,以便客户端知道消息完整到达并准备好解析。
该守护进程将需要处理许多连接(大约200个同时)并对它们进行一些管理,并传递消息,就像在聊天室中一样。
在过去,我主要使用C ++编写我的守护进程。通常使用Qt4框架(网络部分,而不是GUI部分!),因为这也是我用于其他项目的工具,而且很容易做到并且非常可移植。这通常运行良好,没有太多麻烦。
作为一名Linux管理员已经有一段时间了,我注意到野外的大多数网络守护进程都是用纯C编写的(当然还有其他语言编写的,但我感觉>80%的守护进程都是用纯C编写的)。
现在我想知道为什么会这样。这是由于纯历史UNIX背景(如KISS)还是为了纯可移植性或减少膨胀?为什么不使用C ++或任何“更高级”的语言来编写守护进程?
提前致谢!
更新1:
对我来说,通常使用C ++更方便,因为我有getter和setter方法等“对象”。纯C的“上下文”对象在某些情况下可能会非常麻烦 - 特别是当您习惯面向对象编程时。
是的,我知道C ++是C的超集,并且可以用C ++编译器编译任何C代码。但这不是重点。;)
更新2:
我知道现在使用高级(脚本)语言如Python、node.js或类似语言可能更有意义。我过去曾这样做过,并且我知道这样做的好处(至少我希望我知道;) - 但这个问题只涉及C和C ++。

我宁愿使用更高级的东西,因为在具有更安全内存模型的语言中,代码执行漏洞比C/C++更不可能发生。缺点是这些通常具有更高的RAM消耗。 - CodesInChaos
1
我浏览了一下 node.js、Python 和其他一些编程语言。但是大多数要么“太多”,要么存在其他缺点,比如缺乏可移植性、缺乏可扩展性等等… - BastiBen
1
那个断言是错误的。问题在于那些在 C++ 中编写 C 代码的人。如果恰当地使用 C++ 中的框架,可以开发极其无泄漏的代码。 - Chris Becke
8个回答

6

我个人认为,在技术上没有任何理由选择C而不是C++。至少,我可以立刻想到反驳的观点。

回答编辑:我强烈反对你考虑“……C代码基本上就是C ++”。尽管你可以使用C ++编译器技术上编译任何C程序(只要不使用C中比C++采用的更新功能),但我真的会劝阻任何人写类似C的代码在C ++中,或者认为C ++是带有对象的C。

关于C在Linux中的标准性,仅在C开发人员继续说它是标准的情况下才是如此:p C ++与C一样成为Linux中任何标准的一部分,并且有大量在Linux上制作的C ++程序。如果您正在编写Linux驱动程序,则需要使用C进行编写。除此之外……我知道RMS喜欢说你更有可能找到C编译器而不是C ++编译器,但这实际上已经很长时间不是真的了。几乎所有安装都会同时提供两者或两者都不提供。

关于可维护性的回应——当然,我持不同意见。

就像我说的,我无法想出任何一个不能立即被驳斥的观点。反之亦然。


好的。我一直在想它是否有技术背景。但从我的感觉来看,我会说C++编译器(特别是g++)在大多数平台上都是可用和稳定的。 - BastiBen
2
但是如果你用C语言编写你的守护进程,你可以使用单词“new”和“delete”作为变量名。你真的无法反驳这种绝妙的做法,不是吗? - par
哈!这正是我等待的论点。我一直想使用 newdelete 作为变量名。 ;) - BastiBen
哈哈哈,实际上有几次我就是这么做的,特别是在参数名称方面。尤其有一次,我有一个信号表示从一个值切换到另一个值。希望接收者可以访问两个值:void value_changed(id old, id new) - Edward Strange

6
C++在守护进程开发中遭遇的阻力源自以下几个方面:
  • C++因难以避免内存泄漏而声名狼藉。而内存泄漏在任何长时间运行的软件中都是不可接受的。这在某种程度上是不真实的——问题在于C语言背景下的开发人员倾向于在C++中使用C惯用语法,而这非常容易出现泄漏。使用可用的C++特性,如向量和智能指针,可以生成无泄漏代码。

  • 相反,虽然智能指针模板类将资源分配和释放隐藏在程序员之下,但却在幕后做了很多工作。事实上,由于复制构造函数等原因,C++通常具有很多隐式分配。因此,随着时间的推移,C++堆可能会变得碎片化,守护进程最终会因为内存不足而失败,即使有足够的RAM也无济于事。这可以通过使用更具防止碎片化的现代堆管理器来缓解,但它们会消耗更多的资源。

  • 虽然这并不适用于用户模式的守护进程代码,但内核模式开发人员仍然避免使用C++,这是因为C++生成的隐式代码以及C++库用于处理错误的异常。大多数C++编译器使用硬件异常来实现C++异常,并且许多内核模式代码在不允许抛出异常的环境中执行。此外,由C++生成的所有隐式代码都无法包装在#pragma指令中,以保证其放置在可分页或不可分页内存中。

因此,在任何平台上,C++都不适用于内核开发,并且通常也被守护进程开发人员所回避。即使一个人的代码使用了适当的智能内存管理类并且没有泄漏,但保持对潜在内存碎片问题的控制,使得那些内存分配是显式的语言成为首选。


我点赞了你的答案,因为它在很大程度上是正确且相当不错的,但事实上,在至少一个平台上可以使用C ++进行内核开发。 Mac OS X IOKit完全用C ++编写,并且您“必须”使用C ++编写驱动程序,尽管禁用了异常和RTTI。 - par
我不同意没有异常处理的C++仍然可以被称为C++。不过,感谢您的更新。我仍然想知道他们是如何解决将隐式代码标记为非可分页的困难。 - Chris Becke
1
这是可能的。如果您使用C++的子集,可以将其用于内核代码,并且如果您知道自己在做什么,建议使用C++编写守护程序...但这适用于任何语言。我也喜欢这个答案。它解释了为什么有些人犹豫不决,而不会让C守护进程编写者感觉像完全的文盲。 - Prof. Falken

2

我建议你选择自己更熟悉的语言。如果你更熟悉C++,你的代码将会更干净并且运行效率更高,因为你会更加熟悉它。

同样的道理也适用于比如Python和Perl的讨论。选择你更熟悉的一种语言可能会产生更好的代码,因为你有经验。


1

我认为原因是ANSI C是Linux的标准编程语言。每当人们想要与他人共享代码等时,遵循此标准非常重要。但如果您只想为自己编写一些东西,则不是必需的。

您个人可以使用C或C++,结果将是相同的。如果您熟悉C++并且可以在代码中利用一些特殊的面向对象功能,则应选择C++。不要太关注其他人,在C++方面表现良好,就去用C++编写您的守护程序吧。我个人也会选择用C++编写。


谢谢。这可能是每个人在大多数Linux事务中使用纯C的主要原因之一。 - BastiBen

1

你说得对。不使用C++的原因是KISS,特别是如果你希望将来有其他人维护你的代码。我认识的大多数人都是通过现有的源代码或读Stevens的书籍来学习编写守护进程的。这基本上意味着你的示例将使用C语言编写。C++也可以,我自己也写过守护进程,但我认为如果你希望它能够被维护,并且你不知道未来的维护者可能是谁,那么用C语言编写代码会更具有远见。


我会修改这篇文章,有点羞怯地承认,对于任何预计代码行数超过几千行的程序(无论是否为守护进程),我都会使用C++并将我的代码组织成对象;-) - par

1
Boost让使用asio库编写单线程,多线程和高度可扩展的网络守护进程变得非常容易。

0
我建议使用C++,但对于使用异常处理和动态RTTI要保留一些意见。这些特性可能会对运行时性能产生影响,并且在各平台上的支持也不是很好。
C++更加模块化和可维护,如果您可以避免使用这些功能,那么可以放心地在您的项目中使用它。

我在C++中从未真正使用过RTTI和异常处理。不确定是否只有我这样感觉,但它们让人感到有些棘手。也许我应该花几个小时阅读我的C++书籍来掌握它们... - BastiBen

0

无论是C还是C++都非常适合编写守护进程的任务。

此外,现在你也应该考虑像Perl或Python这样的脚本语言。性能通常足够好,而且您将能够更加稳健地编写应用程序,并且用更少的时间。

顺便说一句,看看ACE,这是一个用于在C++中编写可移植网络应用程序的框架。


谢谢,我一定会看看ACE。就像我上面评论的那样,我之前确实用Python、PHP和node.js编写过一些守护进程,并且我知道这样做的好处。然而,我的原始问题是关于C和C++的。 :) - BastiBen

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