diff --git a/CMakeLists.txt b/CMakeLists.txt index 7828068..308d79f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,7 @@ set(CMAKE_CXX_STANDARD 26) find_package(ftxui CONFIG REQUIRED) find_package(nlohmann_json REQUIRED) +find_package(asio REQUIRED) add_library(uno-game-lib src/game/Card.cpp @@ -14,6 +15,7 @@ add_library(uno-game-lib src/common/Utils.cpp src/network/Message.cpp src/network/MessageSerializer.cpp + src/network/NetworkServer.cpp ) target_link_libraries(uno-game-lib PRIVATE ftxui::screen @@ -23,6 +25,9 @@ target_link_libraries(uno-game-lib target_link_libraries(uno-game-lib PRIVATE nlohmann_json::nlohmann_json ) +target_link_libraries(uno-game-lib + PRIVATE asio::asio +) add_executable(uno-game src/main.cpp) target_link_libraries(uno-game diff --git a/src/network/NetworkServer.cpp b/src/network/NetworkServer.cpp new file mode 100644 index 0000000..e85383b --- /dev/null +++ b/src/network/NetworkServer.cpp @@ -0,0 +1,89 @@ +/** + * @file NetworkServer.cpp + * + * @author Yuzhe Guo + * @date 2025.11.25 + */ +#include "NetworkServer.h" + +#include + +namespace UNO::NETWORK { + Session::Session(size_t player_id, asio::ip::tcp::socket socket) : player_id_(player_id), socket_(std::move(socket)) {} + + void Session::start(std::function callback) + { + this->callback_ = std::move(callback); + read(); + } + + void Session::send(const std::string &message) + { + auto buffer = std::make_shared(message); + asio::async_write(socket_, asio::buffer(*buffer), [this, self = shared_from_this(), buffer](const asio::error_code &ec, size_t) { + if (ec) {} + }); + } + + void Session::read() + { + size_t messageLength; + asio::async_read(socket_, + asio::buffer(&messageLength, sizeof(messageLength)), + [this, self = shared_from_this(), messageLength](const asio::error_code &ec, size_t length) { + if (!ec) { + if (messageLength > 0 && messageLength <= 10 * 1024 * 1024) { + this->readBody(messageLength); + } + } + }); + } + + void Session::readBody(size_t length) + { + auto buffer = std::make_shared>(length); + asio::async_read( + socket_, asio::buffer(*buffer), [this, self = shared_from_this(), buffer](const asio::error_code &ec, size_t length) { + if (!ec) { + std::string message = {buffer->begin(), buffer->end()}; + this->callback_(this->player_id_, message); + } + }); + } + + void NetworkServer::accept() + { + this->acceptor_.async_accept([this](const asio::error_code &ec, asio::ip::tcp::socket socket) { + if (!ec) { + this->addPlayer(std::move(socket)); + } + }); + } + + NetworkServer::NetworkServer(uint16_t port, std::function callback) : + acceptor_(io_context_, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)), playerCount(0), callback_(std::move(callback)) + { + } + + void NetworkServer::addPlayer(asio::ip::tcp::socket socket) + { + std::lock_guard lock(this->mutex_); + this->sessions_[this->playerCount] = std::make_shared(playerCount, std::move(socket)); + this->sessions_[this->playerCount]->start(callback_); + this->playerCount++; + } + + void NetworkServer::send(size_t id, const std::string &message) + { + std::lock_guard lock(this->mutex_); + if (this->sessions_.contains(id) == false) { + throw std::invalid_argument("Player session not found"); + } + this->sessions_[id]->send(message); + } + + void NetworkServer::run() + { + this->io_context_.run(); + } +} // namespace UNO::NETWORK \ No newline at end of file diff --git a/src/network/NetworkServer.h b/src/network/NetworkServer.h new file mode 100644 index 0000000..e3202cd --- /dev/null +++ b/src/network/NetworkServer.h @@ -0,0 +1,77 @@ +/** + * @file NetworkServer.h + * + * @author Yuzhe Guo + * @date 2025.11.25 + */ +#pragma once +#include "Message.h" + + +#include +#include +#include + +namespace UNO::NETWORK { + + class Session : public std::enable_shared_from_this { + private: + size_t player_id_; + asio::ip::tcp::socket socket_; + std::function callback_; + + public: + Session(size_t player_id, asio::ip::tcp::socket socket); + + /** + * 开始从网络读取消息 + * @param callback 回调函数 + */ + void start(std::function callback); + + /** + * 发送消息 + * @param message 要发送的消息 + */ + void send(const std::string &message); + + private: + void read(); + void readBody(size_t length); + }; + + class NetworkServer { + private: + asio::io_context io_context_; + asio::ip::tcp::acceptor acceptor_; + size_t playerCount; + std::map> sessions_; + std::function callback_; + std::mutex mutex_; + + public: + explicit NetworkServer(uint16_t port, std::function callback); + + /** + * 添加玩家 + * @param socket 玩家的连接 socket + */ + void addPlayer(asio::ip::tcp::socket socket); + + /** + * 向玩家发送消息 + * @param id 要发送到的玩家 id + * @param message 要发送的消息 + */ + void send(size_t id, const std::string &message); + + /** + * 开始网络进程 + */ + void run(); + + private: + void accept(); + }; + +} // namespace UNO::NETWORK \ No newline at end of file