2012-06-12 41 views
6

在工作中,我负责将TCP服务器作为Modbus从站设备的一部分来实现。我在堆栈交换和一般的互联网上都做了大量的阅读(包括优秀的http://beej.us/guide/bgnet/),但我在设计问题上挣扎。总之,我的设备只能接受2个连接,并且每个连接都会有输入的modbus请求,我必须在主控制器循环中处理这些请求,然后回复成功或失败状态。我有如何实现这一点的以下想法。在Linux上编写多线程TCP服务器

  1. 有一个创建,结合,监听并接受连接,侦听器线程,然后产生一个新的并行线程监听传入数据和空闲超时时间后关闭连接的连接上。如果活动线程数目前为2,则立即关闭新连接以确保只允许2个连接。

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

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

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

回答

3

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

我可能会使用你的第二个选择,除非你需要做很多处理,在这种情况下,第一个到替代的组合可能是有用的。

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

+0

我喜欢这样的声音 - 唯一的问题是我的主循环严格禁止。它必须执行处理并定期处理来自监听器线程的请求。考虑到这一点,你说选项2.会是最好的? – mathematician1975

+0

@ mathematician1975你仍然可以使用我的方法,但不是在'accept'上使用short或no-timeout'select'来阻塞(或者让侦听套接字非阻塞,并使用'accept'并检查'EAGAIN' /'EWOULDBLOCK ')知道何时可以接受连接。 –

+0

我认为考虑到我的时间限制,这是我在短期内追求的最佳解决方案。谢谢你的建议。 – mathematician1975

2

由于您只处理2个连接,因此每个连接的线程对于此类应用程序来说都是完美的。如果您需要扩展到数千个连接,则使用非阻塞或异步I/O的面向对象的方法会更好。 2个监听线程很有意义,您不需要关闭接受fd。连接完成后再回来接受。事实上,一个变体是有三个线程被阻止接受。如果其中两个线程正在主动处理连接,则第三个将重置新创建的连接(或返回忙碌响应,无论适用于您的设备)。

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

man page for pthreads on Linux指示接受是线程安全的。 (线程安全函数中的部分列出了不是线程安全的函数,请参见图)

+0

你的意思是选项3在我的问题? – mathematician1975

+0

@ mathematician1975:是的,我想到选项3的变体,但有三个线程接受。 – jxh

+0

但这里的其他答案说我只能绑定一次,这就排除了选项3。 – mathematician1975

2

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

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

这将最接近你的第一个设计选择,提升可以在1线程中做到这一点,并在后台做类似于select()(通常为epoll())。但这是“C++方式”,在我看来,使用select()和原始pthread s是C方式。

+0

感谢您的建议。我完全同意你关于它的C++和OO方面的内容。然而,考虑到我目前的时间限制,我认为我将不得不寻求原始的linux API方法,因为它让我花了时间去适应它并需要快速获得原型。但是,一旦项目被接受,我想我肯定会赞成这种方法 – mathematician1975