6

在工作中,我的任务是实现 TCP 服务器作为 Modbus 从设备的一部分。我在堆栈交换和一般互联网上都做了很多阅读(包括优秀的ht​​tp://beej.us/guide/bgnet/),但我正在努力解决设计问题。总之,我的设备只能接受 2 个连接,并且每个连接上都会传入 modbus 请求,我必须在主控制器循环中处理这些请求,然后回复成功或失败状态。我对如何实现这一点有以下想法。

  1. 有一个侦听器线程来创建、绑定、侦听和接受连接,然后生成一个新的 pthread 来侦听连接上的传入数据并在空闲超时时间后关闭连接。如果当前活动线程数为 2,则立即关闭新连接以确保仅允许 2 个。

  2. 不要从侦听器线程中生成新线程,而是使用 select() 来检测传入的连接请求以及活动连接上的传入 modbus 连接(类似于 Beejs 指南中的方法)。

  3. 创建 2 个侦听器线程,每个线程都创建一个套接字(相同的 IP 和端口号),可以阻塞 accept() 调用,然后关闭套接字 fd 并处理连接。在这里,我(也许天真地)假设这将只允许最多 2 个连接,我可以使用阻塞读取来处理这些连接。

我已经使用 C++ 很长时间了,但我对 Linux 开发还很陌生。我真的很欢迎关于上述哪种方法最好(如果有的话)的任何建议,以及我对 Linux 的缺乏经验是否意味着它们中的任何一种都是非常糟糕的想法。我热衷于避免 fork() 并坚持使用 pthread,因为传入的 modbus 请求将被排队并定期读取主控制器循环。提前感谢您的任何建议。

4

3 回答 3

3

第三种方法不行,只能绑定一次本地地址。

我可能会使用您的第二个替代方案,除非您需要进行大量处理,在这种情况下,第一个替代方案的组合可能有用。

我正在考虑的两个第一个替代方案的组合是让主线程(程序启动时始终拥有的那个)创建两个工作线程,然后进行阻塞accept调用以等待新连接。当新连接到达时,告诉其中一个线程开始处理新连接并返回阻塞accept。当第二个连接被接受时,你告诉另一个线程在那个连接上工作。如果两个连接都已打开,则要么在一个连接关闭之前不接受,要么等待新连接但立即关闭它们。

于 2012-06-12T09:22:12.717 回答
2

由于您只处理 2 个连接,因此每个连接的线程非常适合这种应用程序。如果您需要扩展到数千个连接,则使用非阻塞或异步 I/O 的面向对象方法会更好。2 个侦听器线程有意义,您无需关闭接受 fd。连接完成后回来接受就可以了。实际上,一种变体是让三个线程阻塞执行接受。如果其中两个线程正在主动处理连接,则第三个线程会重置新创建的连接(或返回忙碌响应,无论您的设备是否合适)。

要让所有三个线程在接受时阻塞,您需要让主线程在三个线程启动之前创建并绑定您的套接字以进行接受/处理处理。

Linux 上 pthreads的手册页表明 accept 是线程安全的。(线程安全函数下的部分列出了非线程安全的函数,看图。)

于 2012-06-12T09:22:15.493 回答
2

您提出的所有设计选项都不是非常面向对象的,而且它们都更倾向于 C 而不是 C++。如果您的工作允许您使用 boost,那么 Boost.Asio 库非常适合制作简单(和复杂)的套接字服务器。您几乎可以采用他们的任何示例并将其简单地扩展为仅允许 2 个活动连接,并在打开所有其他连接后立即关闭它们。

在我的脑海中,他们的简单 HTTP 服务器可以通过在连接类中保留一个静态计数器(构造函数中的 inc,析构函数中的 dec)进行修改,并在创建新计数器时检查计数并决定是否关闭连接。连接类也可以获得 boost::asio::deadline_timer 来跟踪超时。

这将最类似于您的第一个设计选择,boost 可以在 1 个线程中执行此操作,并且在后台执行类似于select()(通常epoll())的操作。但这是“C++ 方式”,在我看来,使用select()and raw pthreads 是 C 方式。

于 2012-06-12T09:55:59.897 回答