Я пишу многопоточный сервер, используя boost :: asio (для сокетов), boost :: thread (для потоков), libconfig ++ (для чтения файлов конфигурации) и буферы протокола (для реализации протокола).
Сервер более или менее следует по этому маршруту: main () -> создает объект Application -> запускает приложениеобъект.Приложение загружает файл конфигурации, затем создает объект сервера (которому передается класс конфигурации как const).Объект сервера настраивает себя и связывает порт, начинает принимать, бла.Каждый раз, когда обнаруживается новый клиент, сервер создает новый объект Client, а затем создает поток, выполняющий обработчик соединения клиента.
Все это объясняет, что файл конфигурации загружен из моего класса Application, изатем прошел весь путь до моего класса Client.Это не должно создавать никаких проблем, если объект libconfig был передан непосредственно клиенту, но, как мы все знаем, многопоточность подразумевает, что память повреждается при одновременном доступе двух или более потоков.
Способ решения этой проблемы обсуждался в другом посте , и в итоге была реализована оболочка, которая автоматически решает проблему мьютекса.
Магический класс
app_config.h
#ifndef _APP_CONFIG_H_
#define _APP_CONFIG_H_ 1
#include <boost/shared_ptr.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/locks.hpp>
#include <boost/noncopyable.hpp>
#include <libconfig.h++>
#include <string>
namespace BBCP {
namespace App {
class ConfigLock;
class Config {
public:
friend class BBCP::App::ConfigLock;
Config(std::string const &file) :
cfg(new libconfig::Config()),
mutex(new boost::mutex())
{
cfg->readFile(file.c_str());
}
private:
boost::shared_ptr<libconfig::Config> cfg;
boost::shared_ptr<boost::mutex> mutex;
};
class Server;
class Client;
class ConfigLock : boost::noncopyable {
public:
ConfigLock(BBCP::App::Config const &wrapper) :
cfg(wrapper.cfg),
mutex(wrapper.mutex),
lock(new LockType(*mutex))
{ }
libconfig::Config &get() throw() { return *cfg; };
private:
boost::shared_ptr<libconfig::Config> cfg;
boost::shared_ptr<boost::mutex> mutex;
typedef boost::lock_guard<boost::mutex> LockType;
boost::shared_ptr<LockType> lock;
};
}
}
#endif
Для ленивых этот класс состоит из ... ну, два класса (ирония?): BBCP::App::Config
и BBCP::App::ConfigLock
.BBCP::App::Config
просто загружает файл, в то время как BBCP::App::ConfigLock
принимает BBCP::App::Config
в качестве аргумента, а затем блокирует мьютекс BBCP::App::Config
.После создания блокировки вызывается BBCP::App::ConfigLock::get
, который возвращает ссылку на объект libconfig::Config
!.
Проблема
Well:
server.cpp:
void BBCP::App::Server::startAccept() {
newClient.reset(new BBCP::App::Client(io_service, config_wrapper));
acceptor.async_accept(newClient->getSocket(), boost::bind(&BBCP::App::Server::acceptHandler, this, boost::asio::placeholders::error));
}
Эта функция создает новый клиентский объект, загруженный объектом boost::asio::io_service
и объектом BBCP::App::Config
.
server.cpp
void BBCP::App::Server::acceptHandler(boost::system::error_code const &e) {
if (!acceptor.is_open()) {
// ARR ERROR!
return;
}
if (!e) {
client_pool.create_thread(*newClient);
}
else {
// HANDLE ME ERROR
throw;
}
startAccept();
}
ThisФункция создает новый поток или (еще не реализовано) ошибки в случае ... ну, ошибок, а затем снова запускает цикл принятия.
Код клиента в основном не имеет значения до этой части:
client.cpp:
void BBCP::App::Client::parseBody() {
BBCP::Protocol::Header header;
BBCP::Protocol::Hello hello;
boost::scoped_ptr<BBCP::App::ConfigLock> lock;
libconfig::Config *cfg;
(...)
switch ((enum BBCP::Protocol::PacketType)header.type()) {
case BBCP::Protocol::HELLO:
(...)
// config_wrapper is a private variable in the client class!
lock.reset(new BBCP::App::ConfigLock(config_wrapper));
// ARRRRRRR HERE BE DRAGOONS!!
*cfg = lock->get();
(...)
lock.reset();
break;
(...)
}
(...)
}
Ну, по правде говоря, я не ожидал такого рода ошибки:
/usr/include/libconfig.h++: In member function ‘void BBCP::App::Client::parseBody()’:
/usr/include/libconfig.h++:338:13: error: ‘libconfig::Config& libconfig::Config::operator=(const libconfig::Config&)’ is private
client.cpp:64:30: error: within this context
client.cpp:71:21: error: request for member ‘exists’ in ‘cfg’, which is of non-class type ‘libconfig::Config*’
client.cpp:77:51: error: request for member ‘lookup’ in ‘cfg’, which is of non-class type ‘libconfig::Config*’
Но вот она, и мне нужен какой-то способчтобы решить эту проблему :(. Я пытался сделать BBCP::App::Client
классом друга BBCP::App::ConfigLock
, но потом он прошел как:
In file included from ../include/app_config.h:4:0,
from ../include/app_main.h:6,
from main.cpp:18:
../include/app_client.h:15:53: error: ‘BBCP::App::Config’ has not been declared
In file included from ../include/app_config.h:4:0,
from ../include/app_main.h:6,
from main.cpp:18:
../include/app_client.h:32:5: error: ‘Config’ in namespace ‘BBCP::App’ does not name a type
In file included from ../include/app_config.h:4:0,
from ../include/app_main.h:6,
from main.cpp:18:
../include/app_client.h: In constructor ‘BBCP::App::Client::Client(boost::asio::io_service&, const int&)’:
../include/app_client.h:15:120: error: class ‘BBCP::App::Client’ does not have any field named ‘config_wrapper’
А потом я пошел как O_O, так что я просто сдался ипришел сюда, еще раз в поисках помощи какого-нибудь гуру из C ++ гуру über и ругает за совершение такого проступка, как попытка получить доступ к закрытым членам другого класса.