简体   繁体   English

在C / C ++中实现跨平台,多线程服务器的最佳方法是什么?

[英]What is the best way to implement a cross-platform, multi-threaded server in C/C++?

Part of the development team I work with has been given the challenge of writing a server for integration with our product. 我与之合作的开发团队的一部分面临着编写服务器以与我们的产品集成的挑战。 We have some low-level sensor devices that provide a C SDK, and we want to share them over a network for use by people collecting data. 我们有一些提供C SDK的低级传感器设备,我们希望通过网络共享它们以供收集数据的人使用。 Sounds simple, right? 听起来很简单吧? Someone would connect a sensor device to their machine in one part of the building and run our server, thus sharing the device(s) with the rest of the network. 有人会将传感器设备连接到建筑物的一部分中的机器并运行我们的服务器,从而与网络的其余部分共享设备。 Then a client would connect to that server via our application and collect sensor readings from the device. 然后,客户端将通过我们的应用程序连接到该服务器,并从设备收集传感器读数。

I created a simple, language-agnostic network protocol, and a reference implementation in Java. 我创建了一个简单的,与语言无关的网络协议,以及Java中的参考实现。 The problem is creating an implementation that will work with our devices that only provide an SDK written in C. We were thinking of doing the following: 问题是创建一个实现,它将与我们的设备一起使用,这些设备仅提供用C编写的SDK。我们正在考虑执行以下操作:

  1. Create polling threads that collect and store the most recent readings from each connected device. 创建轮询线程,收集并存储每个连接设备的最新读数。
  2. Use a multi-threaded server to spin off each incoming connection to a worker thread. 使用多线程服务器将每个传入连接分离到工作线程。
  3. When a worker thread receives a request for a sensor reading, the most recent value collected by the polling thread is sent back to the client. 当工作线程收到传感器读取请求时,轮询线程收集的最新值将被发送回客户端。

That's a lot of threading, especially in C. So, to review, the general requirements are: 这是很多线程,特别是在C中。因此,要审查,一般要求是:

  • Runs on Windows XP/Vista, Linux, and OS X machines 在Windows XP / Vista,Linux和OS X计算机上运行
  • Written in C or C++, to interact with the C SDK we have 用C或C ++编写,与我们拥有的C SDK进行交互
  • Accepts a variable number of simultaneous connections (worker threads) 接受可变数量的同时连接(工作线程)
  • Must use threads, not forking (don't want to deal with another layer of IPC) 必须使用线程,而不是分叉(不想处理另一层IPC)

Can anyone suggest a library and preferably some example code to get use started? 谁能建议一个库,最好是一些示例代码才能开始使用?

I've used Boost.Thread & Boost.Asio to build a multi-threaded server on Windows & Linux systems. 我已经使用Boost.ThreadBoost.Asio在Windows和Linux系统上构建了一个多线程服务器。 The tutorials made it easy to get started. 这些教程使其易于入门。

The best way to write such a server is not to write one, and to rearchitect your system so it is not necessary, and/or to reuse components that already exist. 编写这样一个服务器的最好方法不是编写一个服务器,而是重新构建系统,这样就没有必要,和/或重用已经存在的组件。 Because: 因为:

Someone would connect a sensor device to their machine in one part of the building and run our server, thus sharing the device(s) with the rest of the network. 有人会将传感器设备连接到建筑物的一部分中的机器并运行我们的服务器,从而与网络的其余部分共享设备。

This also has the potential to share the entire machine with rest of the network, if your code has a vulnerability (which it probably will, as you're writing it in C++ from scratch and inventing a new protocol). 如果您的代码存在漏洞(这可能会在您从头开始用C ++编写并发明新协议),这也有可能与整个网络共享整个机器。

So, do it the other way around. 所以,反过来做。 Install a simple client on the machine that has the sensor hardware, then run it either all the time, or periodically, and have it push (post) results to a central server. 在具有传感器硬件的计算机上安装一个简单的客户端,然后一直或定期运行它,并将结果推送(发布)到中央服务器。 The central server could even be a standard web server. 中央服务器甚至可以是标准的Web服务器。 Or it could be a database. 或者它可以是一个数据库。 (Notice that both of these have been written already - no need to reinvent the wheel ;-) (请注意,这两个都已经写好了 - 不需要重新发明轮子;-)

Your application then works the same way you have in mind now, however it collects data from the database rather than the sensors. 然后,您的应用程序的工作方式与您现在的工作方式相同,但它会从数据库而不是传感器收集数据。 The part running on the machine with the sensor, however, has shrunk from a multi-threaded custom server nightmare, to a nice little single threaded command line client that only makes outgoing connections, and which can be run from cron (or equivalent on windows). 然而,在带有传感器的机器上运行的部件已经从多线程自定义服务器噩梦缩小到一个很好的小型单线程命令行客户端,它只能进行传出连接,并且可以从cron运行(或者在Windows上运行)。

Even if you need real time data collection (and from your description it sounds like you do not) it still may be better for the sensor collector be a client and not a server. 即使您需要实时数据收集(并且从您的描述中听起来就像您没有这样),传感器收集器仍然可能更好是客户端而不是服务器。 Let it open a long lived connection to a central collector (or a group of them) and await instructions to provide its data. 让它打开与中央收集器(或其中一组)的长期连接,并等待提供其数据的指令。

edit: ceretullis and pukku's answers suggest a nice variation on this using multicast - see this answer and the comments 编辑:ceretullis和pukku的答案建议使用多播这是一个很好的变化 - 请参阅此答案和评论

Douglas Schmidt's ACE (Adaptive Communications Environment) is a mature, highly portable open-source framework for building high-performance multithreaded servers. Douglas Schmidt的ACE(自适应通信环境)是一个成熟的, 高度可移植的开源框架,用于构建高性能多线程服务器。 It's mainly aimed at telecommunication applications, but has been used for a variety of projects . 它主要针对电信应用,但已被用于各种项目 It also comes with an object request broker called TAO (if you're into CORBA ) 它还带有一个名为TAO对象请求代理 (如果你进入CORBA

One claim to fame of the framework is that it supports many threading models (thread pool, thread per request, asynchronous + threads etc.), so you can use its thread management in a way that's optimal for your application. 声称该框架的一个主张是它支持许多线程模型(线程池,每个请求的线程,异步+线程等),因此您可以以对您的应用程序最佳的方式使用其线程管理。 This is actually the most interesting feature of the system - the server framework functionality comes out of the box. 这实际上是系统最有趣的功能 - 服务器框架功能开箱即用。 Most of the other libraries I've seen suggested here would still require you to implement much of this functionality yourself. 我在这里看到的大多数其他库仍然需要您自己实现大部分功能。

There is quite a bit of electronic documentation and also several books written about it. 有相当多的电子文档 ,还有几本关于它的书籍 It's not the most warm and fluffy of systems and is really built for speed rather than comfort - but we are using C++. 它不是最温暖和蓬松的系统,它真的是为速度而不是舒适而构建 - 但我们使用的是C ++。 You will probably find that it's much less effort to get your head around ACE than try to rebuild the functionality and debug all the synchronisation. 您可能会发现,与ACE相比,尝试重建功能并调试所有同步的工作要少得多。

Oh, and by the way, it's free as in speech - and as in beer. 哦,顺便说一下,它在演讲中是免费的 - 就像在啤酒中一样。 If you want to a commercial route there is also an ecosystem of consultants that will provide support and mentoring services for it. 如果您想要商业路线,还有一个顾问生态系统 ,将为其提供支持和指导服务。 A tutorial with some code snippets can be found here. 可以在此处找到包含一些代码片段的教程

I would use QT. 我会用QT。 It has a cross-platform threading support. 它具有跨平台线程支持。 Good documentation: 好的文件:

QT Threading Documentation QT线程文档

Their signal/slot messaging mechanism works seamlessly between threads too. 它们的信号/插槽消息传递机制也可以在线程之间无缝协作

Not really an answer to your question, but since it sounds like you are actually more familiar with Java than C/C++, why not continue with your reference implementation, and connect to the SDK using Java Native Interface . 这不是你问题的答案,但是因为听起来你实际上比C / C ++更熟悉Java,为什么不继续你的参考实现,并使用Java Native Interface连接到SDK。 (I never really used it, but I figured it would be useful exactly for these kinds of situations.) (我从来没有真正使用它,但我认为它对于这些情况确实很有用。)

Alternatively, you could easily write a simple C program that employs the SDK, and then sends the data to your Java program, using socket-based streams for example. 或者,您可以轻松编写一个使用SDK的简单C程序,然后使用基于套接字的流将数据发送到Java程序。 This way, you could again handle the more difficult stuff in Java. 这样,你可以再次处理Java中更难的东西。

I'd also like to recommend The Spread Toolkit , which (according to the project's web site) is "an open source toolkit that provides a high performance messaging service that is resilient to faults across local and wide area networks". 我还想推荐The Spread Toolkit ,它(根据项目的网站)是“一个开源工具包,提供高性能的消息服务,可以抵御局域网和广域网的故障”。 I have used it couple of times in situations which sound very similar to yours. 在与你的声音非常相似的情况下,我已经使用了几次。 Essentially, it gives you the server that frankodwyer suggests . 从本质上讲,它为您提供了frankodwyer建议的服务器。

The Spread daemon (ie, the server) ain't multithreaded, but it's really fast and scales well up to at least hundreds or thousands of clients. Spread守护程序(即服务器)不是多线程的,但它确实很快并且可以扩展到至少数百或数千个客户端。 Moreover, the protocol caters for reliable IP multicasting, which (in a multi-client environment) may give you (performance-wise) a definite edge against anything implemented using point-to-point TCP or UDP connections only. 此外,该协议适用于可靠的IP多播,(在多客户端环境中)可以为您(性能方面)提供明确的优势,防止仅使用点对点TCP或UDP连接实现的任何内容。 (But: do not try to implement reliable IP multicasting yourself... apparently, the Spread project has produced a number of PhD/MSc theses as a side-product - or is it the toolkit that is the side-product while the main emphasis was always academic research? I don't really know...). (但是:不要试图自己实现可靠的IP多播......显然,Spread项目已经产生了许多博士/硕士论文作为副产品 - 或者它是作为副产品的工具包,而主要强调总是学术研究?我真的不知道......)。

Since Spread has C and Java client APIs (plus Python), it sounds like a very good match to your problem. 由于Spread具有C和Java客户端API(以及Python),因此它听起来非常适合您的问题。 They have two licensing models; 他们有两种许可模式; the first alternative is close to BSD's. 第一种选择接近BSD。 And it's cross-platform of course (regarding both the client, and the server). 当然它是跨平台的(关于客户端和服务器)。

However, Spread will (of course) not do everything for you. 但是,Spread(当然)不会为你做任何事情。 Most prominently perhaps, it does not do persistence (ie, if your client is offline or otherwise unable to receive messages, Spread will not buffer them, beyond a very small number of msgs at least). 也许最重要的是,它不会持久化(即,如果您的客户端处于脱机状态或者无法接收消息,Spread将不会缓冲它们,至少超过极少数的msg)。 But fortunately it's not really too difficult to roll your own persistence implementation on top of what Spread does guarantee (well I don't even know if such an issue is important for you, or not). 但幸运的是,在Spread确保的基础上推出自己的持久性实现并不是很困难(我甚至不知道这样的问题对你来说是否重要)。 Second, Spread limits your messages to 100 kilobytes each, but that limit is also quite easy to circumvent simply by making the sender chop a big message into a number of smaller ones and then concatenating 'em at the receiver. 其次,Spread将你的消息限制为每个100千字节,但这个限制也很容易规避,只需让发送者将一个大消息切换成一些较小的消息,然后在接收器上连接它们。

I use libevent to multiplex network I/O. 我使用libevent来复用网络I / O. You should consider it as an alternative to Boost.Asio. 您应该将其视为Boost.Asio的替代品。

The libevent API provides a mechanism to execute a callback function when a specific event occurs on a file descriptor or after a timeout has been reached. libevent API提供了一种机制,用于在文件描述符上发生特定事件或达到超时后执行回调函数。 Currently, libevent supports /dev/poll, kqueue, event ports, select, poll and epoll. 目前,libevent支持/ dev / poll,kqueue,event ports,select,poll和epoll。

I agree with frankodwyer, flip the protocol from a pull model to a push model. 我同意frankodwyer,将协议从拉模型转换为推模型。

Have the computer with the connected sensor broadcast the readings over UDP multicast at 100Hz (or whatever makes since for your sensors) whenever the sharing service is running. 在共享服务运行时,使用连接的传感器的计算机以100Hz(或传感器以后的任何方式)通过UDP多播广播读数。 Then write clients that read the multicast data. 然后编写读取多播数据的客户端。

Alternatively you could use broadcast UDP instead of multicast. 或者,您可以使用广播UDP而不是多播。

BTW, this is how many GPS, Lidar, and other sensors do things. 顺便说一句,这是多少GPS,激光雷达和其他传感器做的事情。

Use a Cross-Platform API or Create your own API which you change on each Architecture. 使用跨平台API或创建您自己的API,您可以在每个架构上更改它们。

Also revise this: http://www.goingware.com/tips/getting-started/ 还修改了这个: http//www.goingware.com/tips/getting-started/

如果你想使用C(而不是C ++), NSPR库可能会提供你需要的东西......

I highly recommend you consider the prototype design pattern. 我强烈建议您考虑原型设计模式。

I used this pattern to write a protocol agnostic server in C++ that I have used for everything from HTTP Web Services to custom proprietary binary protocols. 我使用这种模式在C ++中编写一个与协议无关的服务器,我已经将其用于从HTTP Web服务到自定义专有二进制协议的所有内容。

Basically, the idea is this: 基本上,这个想法是这样的:

The Server takes care of accept()ing incoming connections on a particular port and creating threads (or processes) to handle those connections. 服务器负责接受()特定端口上的传入连接并创建线程(或进程)来处理这些连接。

When you're trying to build a generic server you realize that you cannot really read or write any data without making assumptions about the protocol... So, the trick is to use the prototype pattern. 当您尝试构建通用服务器时,您会发现在不对协议进行假设的情况下无法真正读取或写入任何数据......因此,诀窍是使用原型模式。

Create a "ConnectionHandlerBase" class with a pure "HandleConnection() = 0" method. 使用纯“HandleConnection()= 0”方法创建“ConnectionHandlerBase”类。 Make the users of the server class subclass this class with their own implementation. 使服务器类的用户使用自己的实现将此类子类化。 Additionally, this class implements a "Clone()" method that returns a copy of itself... This way the server can create new instances of it without needing to know its type... Then when you get a connection, call "Clone()" on your prototypical instance and have the handling thread call "HandleConnection()" on this object. 另外,这个类实现了一个“Clone()”方法,它返回自身的副本......这样服务器就可以创建它的新实例而无需知道它的类型......然后当你得到一个连接时,调用“Clone” ()“在您的原型实例上,并在此对象上使用处理线程调用”HandleConnection()“。

At application startup, the user of the server class has to call something like this: 在应用程序启动时,服务器类的用户必须调用以下内容:

"Server.AttachConnectionPrototype( &MyConnectionObject );" “Server.AttachConnectionPrototype(&MyConnectionObject);”

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM