#ifndef _TCP_SERVER_HPP_ #define _TCP_SERVER_HPP_ #ifndef ASIO_STANDALONE #define ASIO_STANDALONE #endif #include #include #include "buffers.hpp" using asio::ip::tcp; template using tcp_cb_connected = std::function(tcp::socket&)>; template using tcp_cb_received = std::function; template using tcp_cb_disconnected = std::function; template class TCPSession : public std::enable_shared_from_this> { public: TCPSession(tcp::socket sock) : socket(std::move(sock)) { static_assert(std::is_base_of, SessionType>::value , "SessionType should derive from TCPSession"); } void ReadData() { socket.async_read_some(asio::buffer(read_buffer, 1536), [this, self = this->shared_from_this()](std::error_code ec, std::size_t length) { if (!ec) { if(received) received(static_cast(this), read_buffer, length); ReadData(); } else { if(disconnected) disconnected(static_cast(this)); } }); } void SendData(StaticBuffer&& buffer, size_t length) { auto abuf = asio::buffer(buffer.Data(), length); socket.async_write_some(abuf, [buf = std::move(buffer)](std::error_code ec, size_t length) {}); } void Close() { socket.shutdown(tcp::socket::shutdown_both); socket.close(); } tcp::socket& UnderlyingSocket() { return socket; } SessionType& SetReceivedCallback(const tcp_cb_received& cb) { received = cb; return static_cast(*this);} SessionType& SetDisconnectedCallback(const tcp_cb_disconnected& cb) { disconnected = cb; return static_cast(*this); } private: tcp::socket socket; tcp_cb_received received; tcp_cb_disconnected disconnected; unsigned char read_buffer[1536]; }; template class TCPServer : public std::enable_shared_from_this> { public: TCPServer(asio::io_context& ctx, short port) : acceptor(ctx, tcp::endpoint(tcp::v4(), port)) {} void Start() { if(started) return; started = true; _Accept(); } void Stop() { if(!started) return; started = false; acceptor.close(); } TCPServer& SetConnectedCallback(const tcp_cb_connected& cb) { connected = cb; return *this; } protected: void _Accept() { acceptor.async_accept([this, self = this->shared_from_this()](std::error_code ec, tcp::socket socket) { if (!ec) { if(connected) { auto session = connected(socket); if(session != nullptr) session->ReadData(); } } if(started) _Accept(); }); } bool started = false; tcp_cb_connected connected; tcp::acceptor acceptor; }; using connect_fail_cb = std::function; template class TCPClient : public std::enable_shared_from_this> { public: TCPClient(asio::io_context& ctx) : socket(ctx) {} void Connect(const char* ipaddr, short port, uint32_t timeout_time, connect_fail_cb cb = nullptr) { tcp::endpoint endpoint(asio::ip::make_address_v4(ipaddr), port); auto timeout = std::make_unique(socket.get_io_context(), asio::chrono::milliseconds(timeout_time)); timeout->async_wait([this, cb](std::error_code ec) { if(!ec) { if(cb) cb(ec.message()); socket.cancel(); } }); socket.async_connect(endpoint, [this, cb, timeout = std::move(timeout), self = this->shared_from_this()](std::error_code ec) { if (!ec) { if(connected) { auto session = connected(socket); if(session != nullptr) session->ReadData(); } } else { if(cb) cb(ec.message()); } timeout->cancel(); }); } TCPClient& SetConnectedCallback(tcp_cb_connected cb) { connected = cb; return *this; } protected: tcp::socket socket; tcp_cb_connected connected; }; #endif