From 17736d56e5456b5af4b1e89221b5ac0e6e4404a3 Mon Sep 17 00:00:00 2001 From: Alexander Zhirov Date: Wed, 21 Sep 2022 16:04:06 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B8=D1=82=D0=BE=D0=B3=D0=BE=D0=B2=D1=8B?= =?UTF-8?q?=D0=B9=20=D1=81=D0=B5=D1=80=D0=B2=D0=B5=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lesson_04/client/tcp_client.cpp | 16 ++++ lesson_04/client/tcp_client.hpp | 19 +++++ lesson_04/connection/connection.cpp | 50 ------------ lesson_04/connection/connection.hpp | 33 -------- lesson_04/connection/tcp_connection.cpp | 103 ++++++++++++++++++++++++ lesson_04/connection/tcp_connection.hpp | 57 +++++++++++++ lesson_04/main.cpp | 17 ++++ lesson_04/server/tcp_server.cpp | 28 +++++-- lesson_04/server/tcp_server.hpp | 14 +++- 9 files changed, 245 insertions(+), 92 deletions(-) create mode 100644 lesson_04/client/tcp_client.cpp create mode 100644 lesson_04/client/tcp_client.hpp delete mode 100644 lesson_04/connection/connection.cpp delete mode 100644 lesson_04/connection/connection.hpp create mode 100644 lesson_04/connection/tcp_connection.cpp create mode 100644 lesson_04/connection/tcp_connection.hpp diff --git a/lesson_04/client/tcp_client.cpp b/lesson_04/client/tcp_client.cpp new file mode 100644 index 0000000..f7462ba --- /dev/null +++ b/lesson_04/client/tcp_client.cpp @@ -0,0 +1,16 @@ +/* + * tcp_client.cpp + * + * Created on: 21 сент. 2022 г. + * Author: alexander + */ + +#include + +namespace azh +{ + TCPClient::TCPClient() + { + + } +} diff --git a/lesson_04/client/tcp_client.hpp b/lesson_04/client/tcp_client.hpp new file mode 100644 index 0000000..1d3e7e4 --- /dev/null +++ b/lesson_04/client/tcp_client.hpp @@ -0,0 +1,19 @@ +/* + * tcp_client.hpp + * + * Created on: 21 сент. 2022 г. + * Author: alexander + */ + +#pragma once + +namespace azh +{ + + class TCPClient + { + public: + TCPClient(); + }; + +} diff --git a/lesson_04/connection/connection.cpp b/lesson_04/connection/connection.cpp deleted file mode 100644 index f00c4fb..0000000 --- a/lesson_04/connection/connection.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * connection.cpp - * - * Created on: 20 сент. 2022 г. - * Author: alexander - */ - -#include -#include - -namespace azh -{ - Connection::Connection(io::ip::tcp::socket&& socket) : _socket(std::move(socket)) - { - } - - tcp::socket& Connection::socket() - { - return _socket; - } - - void Connection::start() - { - auto strongThis = shared_from_this(); - - boost::asio::async_write(_socket, boost::asio::buffer(_message), - [strongThis](const boost::system::error_code &error, size_t bytesTransferred) - { - if (error) - std::cerr << "Failed to send message!" << std::endl; - else - std::cout << "Sent " << bytesTransferred << " bytes" << std::endl; - }); - - boost::asio::streambuf buffer; - - _socket.async_receive(buffer.prepare(512), [this](const boost::system::error_code &error, size_t bytesTransferred) - { - if (error == boost::asio::error::eof) - std::cout << "Client disconnected properly!" << std::endl; - else if (error) - std::cerr << "Client disconnected in bad way!" << std::endl; - }); - } - - Connection::pointer Connection::create(io::ip::tcp::socket&& socket) - { - return pointer(new Connection(std::move(socket))); - } -} diff --git a/lesson_04/connection/connection.hpp b/lesson_04/connection/connection.hpp deleted file mode 100644 index ea5a9dc..0000000 --- a/lesson_04/connection/connection.hpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * connection.hpp - * - * Created on: 20 сент. 2022 г. - * Author: alexander - */ - -#pragma once - -#include -#include - -namespace azh -{ - using boost::asio::ip::tcp; - namespace io = boost::asio; - - class Connection : public std::enable_shared_from_this - { - public: - using pointer = std::shared_ptr; - - static pointer create(io::ip::tcp::socket&& socket); - tcp::socket& socket(); - void start(); - private: - tcp::socket _socket; - std::string _message = "Hello, client!\n"; - private: - explicit Connection(io::ip::tcp::socket&& socket); - }; - -} diff --git a/lesson_04/connection/tcp_connection.cpp b/lesson_04/connection/tcp_connection.cpp new file mode 100644 index 0000000..6fc6af4 --- /dev/null +++ b/lesson_04/connection/tcp_connection.cpp @@ -0,0 +1,103 @@ +/* + * tcp_connection.cpp + * + * Created on: 20 сент. 2022 г. + * Author: alexander + */ + +#include +#include + +namespace azh +{ + TCPConnection::TCPConnection(io::ip::tcp::socket&& socket) : _socket(std::move(socket)) + { + boost::system::error_code ec; + + std::stringstream name; + name << _socket.remote_endpoint(); + + _username = name.str(); + } + + tcp::socket& TCPConnection::socket() + { + return _socket; + } + + void TCPConnection::start(MessageHandler&& messageHandler, ErrorHandler&& errorHandler) + { + _messageHandler = std::move(messageHandler); + _errorHandler = std::move(errorHandler); + + asyncRead(); + } + + void TCPConnection::post(const std::string& message) + { + bool queueIdle = _outgoingMessages.empty(); + _outgoingMessages.push(message); + + if (queueIdle) + asyncWrite(); + } + + void TCPConnection::asyncRead() + { + io::async_read_until(_socket, _streamBuf, '\n', [self = shared_from_this()] + (boost::system::error_code ec, size_t bytesTransferred) { + self->onRead(ec, bytesTransferred); + }); + } + + void TCPConnection::onRead(boost::system::error_code &ec, size_t bytesTransferred) + { + if (ec) + { + _socket.close(ec); + + _errorHandler(); + + return; + } + + std::stringstream message; + message << _username << ": " << std::istream(&_streamBuf).rdbuf(); + + _streamBuf.consume(bytesTransferred); + + _messageHandler(message.str()); + + asyncRead(); + } + + void TCPConnection::asyncWrite() + { + io::async_write(_socket, io::buffer(_outgoingMessages.front()), [self = shared_from_this()] + (boost::system::error_code ec, size_t bytesTransferred) { + self->onWrite(ec, bytesTransferred); + }); + } + + void TCPConnection::onWrite(boost::system::error_code &ec, size_t bytesTransferred) + { + if (ec) + { + _socket.close(ec); + + _errorHandler(); + + return; + } + + _outgoingMessages.pop(); + + if (!_outgoingMessages.empty()) + asyncWrite(); + } + + TCPConnection::Pointer TCPConnection::create(io::ip::tcp::socket&& socket) + { + return Pointer(new TCPConnection(std::move(socket))); + } +} diff --git a/lesson_04/connection/tcp_connection.hpp b/lesson_04/connection/tcp_connection.hpp new file mode 100644 index 0000000..5c18ffd --- /dev/null +++ b/lesson_04/connection/tcp_connection.hpp @@ -0,0 +1,57 @@ +/* + * tcp_connection.hpp + * + * Created on: 20 сент. 2022 г. + * Author: alexander + */ + +#pragma once + +#include +#include +#include + +namespace azh +{ + using boost::asio::ip::tcp; + namespace io = boost::asio; + + using MessageHandler = std::function; + using ErrorHandler = std::function; + + class TCPConnection : public std::enable_shared_from_this + { + public: + using Pointer = std::shared_ptr; + + static Pointer create(io::ip::tcp::socket&& socket); + + inline const std::string& getUsername() const + { + return _username; + } + + tcp::socket& socket(); + void start(MessageHandler&& messageHandler, ErrorHandler&& errorHandler); + + void post(const std::string& message); + private: + tcp::socket _socket; + std::string _username; + + std::queue _outgoingMessages; + io::streambuf _streamBuf {65536}; + + MessageHandler _messageHandler; + ErrorHandler _errorHandler; + private: + explicit TCPConnection(io::ip::tcp::socket&& socket); + + void asyncRead(); + void onRead(boost::system::error_code &ec, size_t bytesTransferred); + + void asyncWrite(); + void onWrite(boost::system::error_code &ec, size_t bytesTransferred); + }; + +} diff --git a/lesson_04/main.cpp b/lesson_04/main.cpp index 818e931..f116408 100644 --- a/lesson_04/main.cpp +++ b/lesson_04/main.cpp @@ -1,5 +1,6 @@ #include #include +#include #include void server(int port) @@ -7,6 +8,22 @@ void server(int port) using namespace azh; TCPServer server(IPV::V4, port); + + server.onJoin = [](TCPConnection::Pointer server) + { + std::cout << "User has joined the server " << server->getUsername() << std::endl; + }; + + server.onLeave = [](TCPConnection::Pointer server) + { + std::cout << "User has left the server " << server->getUsername() << std::endl; + }; + + server.onClientMessage = [&server](const std::string& message) + { + server.broadcast(message); + }; + server.run(); } diff --git a/lesson_04/server/tcp_server.cpp b/lesson_04/server/tcp_server.cpp index 133d5ef..d7bc9c7 100644 --- a/lesson_04/server/tcp_server.cpp +++ b/lesson_04/server/tcp_server.cpp @@ -1,12 +1,12 @@ /* - * server.cpp + * tcp_server.cpp * * Created on: 20 сент. 2022 г. * Author: alexander */ +#include #include -#include #include namespace azh @@ -33,9 +33,12 @@ namespace azh return 0; } - void TCPServer::broadcast(const std::string& message) + void TCPServer::broadcast(const std::string &message) { - + for (auto &connection : _connections) + { + connection->post(message); + } } void TCPServer::startAccept() @@ -44,12 +47,25 @@ namespace azh _acceptor.async_accept(*_socket, [this](const boost::system::error_code &error) { - auto connection = Connection::create(std::move(*_socket)); + auto connection = TCPConnection::create(std::move(*_socket)); + + if (onJoin) + onJoin(connection); _connections.insert(connection); if (!error) - connection->start(); + connection->start( + [this](const std::string& message) + { + if (onClientMessage) onClientMessage(message); + }, + [&, weak = std::weak_ptr(connection)] + { + if (auto shared = weak.lock(); shared && _connections.erase(shared)) + if (onLeave) onLeave(shared); + } + ); startAccept(); }); diff --git a/lesson_04/server/tcp_server.hpp b/lesson_04/server/tcp_server.hpp index ec8fdbd..1af9510 100644 --- a/lesson_04/server/tcp_server.hpp +++ b/lesson_04/server/tcp_server.hpp @@ -1,5 +1,5 @@ /* - * server.hpp + * tcp_server.hpp * * Created on: 20 сент. 2022 г. * Author: alexander @@ -8,7 +8,7 @@ #pragma once #include -#include +#include #include #include #include @@ -24,12 +24,20 @@ namespace azh class TCPServer { + using OnJoinHandler = std::function; + using OnLeaveHandler = std::function; + using OnClientMessageHandler = std::function; + public: TCPServer(IPV ipv, int port); int run(); void broadcast(const std::string& message); + public: + OnJoinHandler onJoin; + OnLeaveHandler onLeave; + OnClientMessageHandler onClientMessage; private: IPV _ipVersion; int _port; @@ -37,7 +45,7 @@ namespace azh io::io_context _ioContext; io::ip::tcp::acceptor _acceptor; std::optional _socket; - std::unordered_set _connections; + std::unordered_set _connections; private: void startAccept(); };