From b16bab5fed341a72a815332dc73e4a67cdbd9f67 Mon Sep 17 00:00:00 2001 From: Andrey Astafyev Date: Fri, 24 Apr 2020 17:13:33 +0300 Subject: [PATCH] =?UTF-8?q?=D0=97=D0=B0=D0=BA=D0=BE=D0=BD=D1=87=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BE=D1=81=D0=BD=D0=BE=D0=B2=D0=B0=20=D0=B1?= =?UTF-8?q?=D0=B8=D0=B1=D0=BB=D0=B8=D0=BE=D1=82=D0=B5=D0=BA=D0=B8=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20=D1=81=20?= =?UTF-8?q?Redis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/redis/01_client/CMakeLists.txt | 9 +- examples/redis/01_client/client.hpp | 120 ++++++++++++++---------- src/myx/base/CMakeLists.txt | 1 - src/myx/redis/client-inl.hpp | 10 ++ src/myx/redis/client.hpp | 32 ++++++- src/myx/redis/parser-inl.hpp | 14 +-- src/myx/redis/parser.hpp | 1 + src/myx/redis/request-inl.hpp | 8 ++ src/myx/redis/request.hpp | 6 +- src/myx/redis/request_p-inl.hpp | 46 --------- 10 files changed, 133 insertions(+), 114 deletions(-) delete mode 100644 src/myx/redis/request_p-inl.hpp diff --git a/examples/redis/01_client/CMakeLists.txt b/examples/redis/01_client/CMakeLists.txt index ce03a3e..66c16c9 100644 --- a/examples/redis/01_client/CMakeLists.txt +++ b/examples/redis/01_client/CMakeLists.txt @@ -30,7 +30,7 @@ if(MYXLIB_BUILD_EXAMPLES) add_pvs_check(${TRGT}) # Создание цели для автоматического форматирования кода - add_format_sources(${TRGT} ${TRGT_cpp}) + add_format_sources(${TRGT} ${TRGT_cpp} ${TRGT_moc_hpp}) # Qt5 target_include_directories(${TRGT} PRIVATE ${CMAKE_SOURCE_DIR}/src) @@ -60,11 +60,8 @@ endif() if(MYXLIB_BUILD_EXAMPLES_HO) set(REDIS_LIB_DIR ${CMAKE_SOURCE_DIR}/src/myx/redis) - set(REDIS_moc_hpp - ${REDIS_LIB_DIR}/client.hpp - ${REDIS_LIB_DIR}/lexer.hpp - ${REDIS_LIB_DIR}/parser.hpp - ${REDIS_LIB_DIR}/request.hpp) + set(REDIS_moc_hpp ${REDIS_LIB_DIR}/client.hpp ${REDIS_LIB_DIR}/lexer.hpp ${REDIS_LIB_DIR}/parser.hpp + ${REDIS_LIB_DIR}/request.hpp) qt5_wrap_cpp(REDIS_moc_cpp ${REDIS_moc_hpp}) diff --git a/examples/redis/01_client/client.hpp b/examples/redis/01_client/client.hpp index 0aba388..29cb080 100644 --- a/examples/redis/01_client/client.hpp +++ b/examples/redis/01_client/client.hpp @@ -7,63 +7,83 @@ namespace MR = myx::redis; class RedisClient : public QObject { - Q_OBJECT + Q_OBJECT - MR::Client m_client; - MR::Request* m_request; + MR::Client m_client; + MR::Client m_subscribe; + MR::Request* m_request; + MR::Request* m_channel; public: - RedisClient(QObject* parent = nullptr) : QObject(parent) - { - connect(&m_client, &MR::Client::connected, this, &RedisClient::slotConnected); - m_client.connectToHost("127.0.0.1"); - } - virtual ~RedisClient() {} - - Q_SLOT void slotConnected() - { - m_request = m_client.sendCommand("PING"); - connect(m_request, &MR::Request::reply, this, &RedisClient::slotPong); - } - - Q_SLOT void slotPong( const MR::Reply& reply) - { - qDebug() << static_cast(reply.type()); - qDebug() << const_cast(reply).value().toString(); - m_request->disconnect(); - m_request->deleteLater(); - - m_request = m_client.sendCommand( "SET value 10" ); - connect(m_request, &MR::Request::reply, this, &RedisClient::slotSetValue); - } + RedisClient( QObject* parent = nullptr ) : + QObject( parent ) + { + connect( &m_client, &MR::Client::connected, this, &RedisClient::slotConnected ); + connect( &m_subscribe, &MR::Client::connected, this, &RedisClient::slotStartSubscribe ); + m_client.connectToHost( "127.0.0.1" ); + m_subscribe.connectToHost( "127.0.0.1" ); + } - Q_SLOT void slotSetValue( const MR::Reply& reply) - { - qDebug() << static_cast(reply.type()); - qDebug() << const_cast(reply).value().toString(); - m_request->disconnect(); - m_request->deleteLater(); + virtual ~RedisClient() {} - m_request = m_client.sendCommand( "GET value" ); - connect(m_request, &MR::Request::reply, this, &RedisClient::slotGetValue); - } + Q_SLOT void slotStartSubscribe() + { + m_channel = m_subscribe.subscribeToChannel( "test" ); + connect( m_channel, &MR::Request::reply, this, &RedisClient::slotSubscribeTest ); + } - Q_SLOT void slotGetValue( const MR::Reply& reply) - { - qDebug() << static_cast(reply.type()); - qDebug() << const_cast(reply).value().toByteArray(); - m_request->disconnect(); - m_request->deleteLater(); + Q_SLOT void slotSubscribeTest( MR::Reply reply ) + { + qDebug() << static_cast< int >( reply.type() ); + auto& v = reply.value(); + if ( !v.canConvert< QVariantList >() ) { return; } - m_request = m_client.sendCommand( "SUBSCRIBE test" ); - connect(m_request, &MR::Request::reply, this, &RedisClient::slotSubscribeTest); - } + auto l = v.toList(); + for ( auto& a: l ) + { + qDebug() << a.value< MR::Reply >().value().toByteArray(); + } + } - Q_SLOT void slotSubscribeTest( const MR::Reply& reply) - { - qDebug() << static_cast(reply.type()); - qDebug() << const_cast(reply).value(); - } -}; + + Q_SLOT void slotConnected() + { + m_request = m_client.sendCommand( "PING" ); + connect( m_request, &MR::Request::reply, this, &RedisClient::slotPong ); + } + + + Q_SLOT void slotPong( MR::Reply reply ) + { + qDebug() << static_cast< int >( reply.type() ); + qDebug() << reply.value().toString(); + m_request->disconnect(); + m_request->deleteLater(); + + m_request = m_client.sendCommand( "SET value 10" ); + connect( m_request, &MR::Request::reply, this, &RedisClient::slotSetValue ); + } + + + Q_SLOT void slotSetValue( MR::Reply reply ) + { + qDebug() << static_cast< int >( reply.type() ); + qDebug() << reply.value().toString(); + m_request->disconnect(); + m_request->deleteLater(); + + m_request = m_client.sendCommand( "GET value" ); + connect( m_request, &MR::Request::reply, this, &RedisClient::slotGetValue ); + } + + + Q_SLOT void slotGetValue( MR::Reply reply ) + { + qDebug() << static_cast< int >( reply.type() ); + qDebug() << reply.value().toByteArray(); + m_request->disconnect(); + m_request->deleteLater(); + } +}; // class RedisClient diff --git a/src/myx/base/CMakeLists.txt b/src/myx/base/CMakeLists.txt index e607711..4f4cc75 100644 --- a/src/myx/base/CMakeLists.txt +++ b/src/myx/base/CMakeLists.txt @@ -53,7 +53,6 @@ generate_pkgconfig(myx-${TRGT} COMPONENT headers INSTALL_LIBRARY ${MYXLIB_BUILD_ install(FILES ${TRGT_headers} ${CMAKE_BINARY_DIR}/include/myx/base/compiler_features.hpp COMPONENT headers DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/${TRGT}) - # Цель, используемая только для установки заголовочных файлов без компиляции проекта add_custom_target(${TRGT}-install-headers COMMAND "${CMAKE_COMMAND}" -DCMAKE_INSTALL_COMPONENT=headers -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") diff --git a/src/myx/redis/client-inl.hpp b/src/myx/redis/client-inl.hpp index 474a43a..c00a5b0 100644 --- a/src/myx/redis/client-inl.hpp +++ b/src/myx/redis/client-inl.hpp @@ -46,6 +46,16 @@ MYXLIB_INLINE Request* Client::sendCommand( const QByteArray& command ) } +MYXLIB_INLINE Request* Client::subscribeToChannel( const QByteArray& channel ) +{ + d->m_socket.write( "subscribe " + channel + "\r\n" ); + + auto* request = new Request( channel ); + d->m_list.append( request ); + return( request ); +} + + MYXLIB_INLINE bool Client::waitForConnected( int msecs ) { return( d->m_socket.waitForConnected( msecs ) ); diff --git a/src/myx/redis/client.hpp b/src/myx/redis/client.hpp index 187b6a7..6f12cdb 100644 --- a/src/myx/redis/client.hpp +++ b/src/myx/redis/client.hpp @@ -76,6 +76,8 @@ public: */ Request* sendCommand( const QByteArray& command ); + Request* subscribeToChannel( const QByteArray& command ); + /** * @brief Attempts to set the specified key to the specified value * @param name the name of the key @@ -128,21 +130,45 @@ class ClientPrivate : public QObject connect( &m_socket, &QTcpSocket::connected, client, &Client::connected ); connect( &m_socket, &QTcpSocket::disconnected, client, &Client::disconnected ); connect( &m_parser, &Parser::reply, this, &ClientPrivate::sendReply ); + connect( &m_parser, &Parser::replyMultiBulk, this, &ClientPrivate::sendReplyMultiBulk ); } QTcpSocket m_socket; QQueue< Request* > m_queue; + QList< Request* > m_list; Lexer m_lexer; Parser m_parser; Q_SLOT void sendReply( const myx::redis::Reply& reply ) { - if ( !m_queue.isEmpty() ) + if ( m_queue.isEmpty() ) { return; } + Q_EMIT m_queue.dequeue()->reply( const_cast< myx::redis::Reply& >( reply ) ); + } // sendReply + + + Q_SLOT void sendReplyMultiBulk( const myx::redis::Reply& reply ) + { + if ( m_list.isEmpty() ) { return; } + + QVariant v = const_cast< myx::redis::Reply& >( reply ).value(); + if ( !v.isValid() ) { return; } + + if ( !v.canConvert< QVariantList >() ) { return; } + + auto l = v.toList(); + if ( l.size() >= 2 ) { - Q_EMIT m_queue.dequeue()->reply( reply ); + auto b = l[1].value< Reply >().value().toByteArray(); + for ( auto& request: m_list ) + { + if ( request->channel() == b ) + { + request->reply( const_cast< myx::redis::Reply& >( reply ) ); + } + } } - } + } // sendReplyMultiBulk }; // class ClientPrivate } // namespace redis diff --git a/src/myx/redis/parser-inl.hpp b/src/myx/redis/parser-inl.hpp index df34a70..0ce98f5 100644 --- a/src/myx/redis/parser-inl.hpp +++ b/src/myx/redis/parser-inl.hpp @@ -70,11 +70,6 @@ MYXLIB_INLINE void Parser::descend() while ( true ) { auto& task = tos(); - if ( task.m_reply.value().isNull() ) - { - task.m_reply.value().setValue( QList< QVariant >() ); - } - if ( ( task.m_reply.type() == Reply::kMultiBulk ) && ( task.m_reply.value().toList().count() < task.m_count ) ) { @@ -83,7 +78,14 @@ MYXLIB_INLINE void Parser::descend() if ( m_stack.count() == 1 ) { - Q_EMIT reply( m_stack.takeLast().m_reply ); + if ( task.m_reply.type() == Reply::kMultiBulk ) + { + Q_EMIT replyMultiBulk( m_stack.takeLast().m_reply ); + } + else + { + Q_EMIT reply( m_stack.takeLast().m_reply ); + } return; } diff --git a/src/myx/redis/parser.hpp b/src/myx/redis/parser.hpp index 239cff3..887b95b 100644 --- a/src/myx/redis/parser.hpp +++ b/src/myx/redis/parser.hpp @@ -30,6 +30,7 @@ public: ~Parser() override = default; Q_SIGNAL void reply( const myx::redis::Reply& ); + Q_SIGNAL void replyMultiBulk( const myx::redis::Reply& ); private: Q_SLOT void readCharacter( char ); diff --git a/src/myx/redis/request-inl.hpp b/src/myx/redis/request-inl.hpp index 5c630c5..765f339 100644 --- a/src/myx/redis/request-inl.hpp +++ b/src/myx/redis/request-inl.hpp @@ -23,6 +23,14 @@ MYXLIB_INLINE Request::Request( QObject* parent ) : } +MYXLIB_INLINE Request::Request( const QByteArray& channel, QObject* parent ) : + QObject( parent ), + d ( new Loop ) +{ + m_channel = channel; +} + + MYXLIB_INLINE bool Request::waitForReply( int msecs ) { QTimer timer; diff --git a/src/myx/redis/request.hpp b/src/myx/redis/request.hpp index ad1dd1b..7d82b36 100644 --- a/src/myx/redis/request.hpp +++ b/src/myx/redis/request.hpp @@ -40,6 +40,7 @@ public: * @param parent the parent QObject */ explicit Request( QObject* parent = nullptr ); + explicit Request( const QByteArray& channel, QObject* parent = nullptr ); Request( const Request& ) = delete; Request& operator=( const Request& ) = delete; @@ -62,11 +63,12 @@ public: * @brief Emitted when a reply is received * @param reply the reply received */ - Q_SIGNAL void reply( const myx::redis::Reply& reply ); + Q_SIGNAL void reply( myx::redis::Reply& ); + QByteArray channel() const { return( m_channel ); } private: - + QByteArray m_channel; const QScopedPointer< Loop > d; }; // class Request diff --git a/src/myx/redis/request_p-inl.hpp b/src/myx/redis/request_p-inl.hpp deleted file mode 100644 index ed7676c..0000000 --- a/src/myx/redis/request_p-inl.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef MYX_REDIS_REQUEST_INL_HPP_ -#define MYX_REDIS_REQUEST_INL_HPP_ - -#pragma once - -#include - -#ifndef MYXLIB_HEADER_ONLY -#include -#endif - -#include - -namespace myx { - -namespace redis { - -MYXLIB_INLINE Request::Request( QObject* parent ) : - QObject( parent ), - d ( new Loop( parent ) ) -{ - connect( this, &Request::reply, this, &Request::deleteLater ); -} - - -MYXLIB_INLINE bool Request::waitForReply( int msecs ) -{ - QTimer timer; - timer.setInterval( msecs ); - timer.setSingleShot( true ); - - connect( &timer, &QTimer::timeout, &d->m_loop, &QEventLoop::quit ); - connect( this, &Request::reply, d.data(), &Loop::quitEventLoop ); - - /* - * If the timer fires, the return value will be 0. - * Otherwise, quitEventLoop() will terminate the loop with 1. - */ - return( ( d->m_loop.exec( QEventLoop::ExcludeUserInputEvents ) != 0 ) ); -} - -} // namespace redis - -} // namespace myx - -#endif // ifndef MYX_REDIS_REQUEST_INL_HPP_