Основные классы
QexMethodTranslator – класс для вызова методов.
QexPropertyTranslator – класс для передачи свойств.
QexSignalSlotTranslator – класс для передачи сигналов.
QexMetaTranslator – агрегатор основных классов.
QexRemintReader – вспомогательный класс для ожидания прихода пакета целиком.
Передаваемые по сети данные
Во время вызова, на вызываемую сторону предается:
— идентификатор ответа;
— имя вызываемого объекта;
— сигнатура вызываемого метода;
— тип возвращаемого значения;
— список типов вызываемых аргументов;
— список значений вызываемых аргументов.
Аргументы сохраняются и восстанавливаются в QByteArray, используя QMetaType::save/ QMetaType::load.
При возвращении ответа передается:
— идентификатор ответа;
— имя объекта, который был вызван;
— имя метода, который был вызван;
— тип ответа;
— значение ответа.
Методы у объектов вызываются через QMetaObject::invokeMethod, поэтому они выполняются в потоке, в котором он обрабатывает слоты.
Пример
Для примера был реализован простенький чат. Что бы выделить важный код, были удалены почти все проверки и очистки. Этот код является очень урезанной версией того, что используется в боевом режиме. Как уже было сказано выше, сетевое взаимодействие не входит в саму библиотеку, поэтому, в начале, устанавливается связь.
Взаимодействие на стороне сервера
Сервер начинает слушать входящие соединения:
m_tcp_server.listen( QHostAddress::Any, 43567 );
При подключении клиента настраиваем его:
// получаем ожидающего клиента QTcpSocket *client = m_tcp_server.nextPendingConnection( ); // мониторим отключение клиента connect( client, SIGNAL( disconnected( ) ), SLOT( disconnect_client( ) ) ); // мониторим когда поступят данные от клиента connect( client, SIGNAL( readyRead( ) ), SLOT( ready_read( ) ) ); // пара отвечает за обработку сигналов/слотов от одного клиента typedef QPair< QexRemintReader*, QexMetaTranslator* > reader_translator; // создаем классы, для обработки данных именного этого клиента reader_translator rt = qMakePair( new QexRemintReader( client ), new QexMetaTranslator( client ) ); // подключаем обработчик данных, которые приходят из сети (у нас это принятые вызовы методов) connect( rt.first, SIGNAL( dataFormed( const QByteArray& ) ), rt.second, SLOT( inputData( const QByteArray& ) ) ); // подключаем обработчик данных, который надо послать в сеть (у нас это сгенеренные сигналы) connect( rt.second, SIGNAL( dataOutputted ( const QByteArray& ) ), SLOT( send_data( const QByteArray& ) ) ); // подключаем список методов, которые можно вызвать rt.second->methodTranslator().connectMethod( "chat_server", this, SLOT( send_message( const QString&, const QString& ) ) ); // подключаем список сигналов, которые будут передаваться клиентам rt.second->signalSlotTranslator().connectSignal( "chat_server", this, SIGNAL( message_received( const QString&, const QString& ) ) );
Когда мы делаем какой-то вызов или генерируем сигнал, нам необходимо отправить данные об этом клиенту. Обработчик всех подобных сигналов реализован один в одном объекте, поэтому необходимо найти какому клиенту предназначены данные. Целевого клиента ищем по отправителю сигнала, т.к. данные отправляет QexMetaTranslator, который отвечает за данного клиента:
QexMetaTranslator * mt = qobject_cast< QexMetaTranslator* >( sender() ); for ( QMap< QTcpSocket*, reader_translator >::iterator it = m_clients.begin(); it != m_clients.end(); ++it ) { if ( it.value().second == mt ) { QDataStream stream( it.key() ); stream << data; } }
Необходимо обернуть отправляемые данные в QDataStream, т.к. при чтении данных, в начале вычитывается размер данных, который записывает QDataStream. При приеме нового сообщения нужно только разослать его всем остальным пользователям:
Q_EMIT message_received( nickname, text );
Взаимодействие на стороне клиента
При создание клиента необходимо настроить прием/передачу сигналов и слотов по аналогии с серверной частью:
connect( m_reader, SIGNAL( dataFormed( const QByteArray& ) ), m_translator, SLOT( inputData( const QByteArray& ) ) ); connect( m_translator, SIGNAL( dataOutputted( const QByteArray& ) ), SLOT( send_data( const QByteArray& ) ) );
Далее настраиваем принимаемые сигналы:
m_translator->signalSlotTranslator().connectSlot( "chat_server", SIGNAL( message_received( const QString&, const QString& ) ), this, SLOT( message_received( const QString&, const QString& ) ) );
При получении сигнала, просто добавляем полученный текст сообщения в окно с сообщениями. После создания объекта клиента, необходимо установить связь по сети. Далее, при получении данных из сокета, отправляем их на обработку в парсер:
m_reader->addData( m_socket->readAll() );
Отправка данных серверу осуществляется просто записью в сокет через QDataStream:
QDataStream stream( m_socket ); stream << data;
Отправка сообщение (вызов метода):
m_translator->methodTranslator().callMethod( "chat_server" , METHOD( send_message( QString, QString ) ) , Q_ARG( QString, m_ui.nick->text() ) , Q_ARG( QString, m_ui.text->toPlainText() ) );
При отправке сообщения задается название вызываемого объекта, сигнатура вызываемой функции и список параметров. Если вызываемый метод что-то возвращает, то можно задать функцию, которая будет вызываться для получения результата.
ссылка на оригинал статьи http://habrahabr.ru/post/171553/
Добавить комментарий