feat(network): add NetworkServer for managing player sessions and communication

- Implemented `NetworkServer` and `Session` classes for handling TCP communication with players.
- Added `NetworkServer.cpp` and `NetworkServer.h` to the project.
- Integrated `asio` library into the build configuration in `CMakeLists.txt`.
This commit is contained in:
Kieran Kihn
2025-11-28 20:40:14 +08:00
parent def62a18c6
commit 84c470859b
3 changed files with 171 additions and 0 deletions

View File

@@ -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

View File

@@ -0,0 +1,89 @@
/**
* @file NetworkServer.cpp
*
* @author Yuzhe Guo
* @date 2025.11.25
*/
#include "NetworkServer.h"
#include <utility>
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<void(size_t, std::string)> callback)
{
this->callback_ = std::move(callback);
read();
}
void Session::send(const std::string &message)
{
auto buffer = std::make_shared<std::string>(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<std::vector<char>>(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<void(size_t, std::string)> 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<std::mutex> lock(this->mutex_);
this->sessions_[this->playerCount] = std::make_shared<Session>(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<std::mutex> 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

View File

@@ -0,0 +1,77 @@
/**
* @file NetworkServer.h
*
* @author Yuzhe Guo
* @date 2025.11.25
*/
#pragma once
#include "Message.h"
#include <asio.hpp>
#include <map>
#include <memory>
namespace UNO::NETWORK {
class Session : public std::enable_shared_from_this<Session> {
private:
size_t player_id_;
asio::ip::tcp::socket socket_;
std::function<void(size_t, std::string)> callback_;
public:
Session(size_t player_id, asio::ip::tcp::socket socket);
/**
* 开始从网络读取消息
* @param callback 回调函数
*/
void start(std::function<void(size_t, std::string)> 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<size_t, std::shared_ptr<Session>> sessions_;
std::function<void(size_t, std::string)> callback_;
std::mutex mutex_;
public:
explicit NetworkServer(uint16_t port, std::function<void(size_t, std::string)> 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