为了工作,我需要编写一个 tcp 守护进程来响应我们的客户端软件,并且想知道是否有人对解决此问题的最佳方法有任何提示。
我应该像通常使用线程一样为每个新连接分叉吗?
这取决于您的应用程序。线程和分叉都可以是完全有效的方法,也是单线程事件驱动模型的第三种选择。如果您可以详细解释您正在写的内容,那么在提供建议时会有所帮助。
对于它的价值,这里有一些一般准则:
通常分叉将是最容易实现的,因为一旦分叉,您基本上可以忽略所有其他连接;由于额外的同步要求,线程次之;由于需要将您的处理转换为状态机,因此事件循环更加困难;并且多个线程运行事件循环是其中最困难的(由于结合了其他因素)。
我建议任何一天都通过线程分叉连接。线程的问题在于共享内存空间,以及操作另一个线程的内存是多么容易。对于分叉的进程,进程之间的任何通信都必须由您有意完成。
刚刚搜索并找到了这个答案:fork 的目的是什么?. 您显然知道答案,但该线程中的#1 答案对 fork() 的优点有很好的说明。
除了@hobodave 的好答案之外,“每个连接分叉”的另一个好处是您可以通过使用inetd
ortcpserver
或类似的方法非常简单地实现您的服务器:然后您可以使用标准输入和标准输出与套接字进行通信,并且不要不必进行任何侦听套接字管理(侦听连接等)等。
当然,另一种选择是预先分叉多个守护进程副本,并让每个副本保持活动状态并继续响应请求。这完全取决于您的应用程序、预期负载和性能要求等。
最简单最简单的方法是编写一个基于inetd的守护进程;您的软件可以忽略它通过 TCP 连接运行的事实,而只需通过标准输入/标准输出处理输入/输出。这在绝大多数情况下都很有效。
如果您不打算每秒处理许多新连接,请考虑从 inetd 运行。除此以外...
下载 OpenSSH 源代码。他们在权限分离方面做了很多工作,恰到好处,它是可移植的,而且它的安全性受到了比其他任何东西都多的审查。
根据您的需要调整它,您可能可以扔掉大部分。当然要遵守许可协议。遵循具有良好 SCC 的未来补丁。
在你有充分的证据证明这是一个真正的问题之前,不要担心分叉进程与线程的性能。Apache 多年来一直使用简单的每客户端进程模型来运行最繁忙的站点。
如果你真的有野心,你可以使用某种非阻塞异步 IO 模型。我喜欢 Boost.Asio,但我对 C++ 很感兴趣。
确保您的代码正确处理信号。HUP 重新加载配置。TERM 正常关闭。
不要尝试编写自己的日志文件。仅使用 syslog,或者只写入可以重定向到 syslog 的 stderr。尝试在所有日志略有不同的家用服务器上设置 logrotate 真的很痛苦。
如果您想避免一起使用线程/分叉,我建议将所有非阻塞 I/O 与libevent一起使用。Libevent 是众所周知的事件驱动编程的高性能解决方案。
查看ACE (C++/Java)。它有许多线程、事件和分叉 TCP 反应器,可满足您的通信需求。您还可以查看Boost ASIO做类似的事情