最新资讯

  • 轻量级网络对战服务器设计与实现方案

轻量级网络对战服务器设计与实现方案

2026-01-30 07:01:01 栏目:最新资讯 4 阅读

一月速成C++游戏后端实战项目:轻量级网络对战服务器
核心目标
一个月内完成一个能体现C++游戏后端核心技能的实战项目,准备面试。

项目要求
类型:轻量级网络对战服务器

语言:纯C++

核心展示点:高并发处理、实时同步、多线程架构、协议设计

完成度:可运行、有基础功能、代码结构清晰

详细执行路线
第一周:基础与骨架
学习重点:

掌握现代C++核心(智能指针、lambda、移动语义)

理解epoll网络模型(ET/LT模式区别)

学会Protobuf协议定义与使用

实践任务:

使用CMake搭建C++项目结构

实现基于epoll的TCP Echo服务器,支持多客户端连接

定义前3-5个基础消息结构(如:登录、心跳)

集成Protobuf完成消息序列化/反序列化

实现简单的日志系统

第二周:核心逻辑
学习重点:

掌握std::thread、锁、条件变量

理解生产者-消费者模型

学习线程安全队列实现

实践任务:

设计网络I/O线程 + 逻辑线程分离架构

实现Connection类管理客户端连接状态

实现RoomManager和Room类,支持房间创建/加入

在Room中实现定时游戏循环(如每100ms更新)

实现房间内状态广播功能

第三周:优化与深化
学习重点:

对象池/内存池原理与实现

SQLite基础操作(或MySQL连接)

压力测试与内存检测工具

实践任务:

为频繁创建对象(如网络包)实现简单对象池

集成SQLite存储玩家基础数据

编写测试客户端模拟多玩家连接

使用valgrind检测内存泄漏

进行基础压力测试(50+并发连接)

第四周:收尾与面试准备
学习重点:

项目文档编写规范

面试表达技巧

知识体系串联

实践任务:

编写高质量README.md(项目介绍、编译运行、架构图)

代码重构、添加注释、提交GitHub(记录清晰commit)

准备3分钟版本项目介绍(核心亮点、技术架构)

准备10分钟版本深入讲解(难点、解决方案、优化点)

梳理项目涉及的知识点(C++、网络、操作系统、数据库)

关键产出物
完整的GitHub项目仓库

可运行的对战服务器程序

项目技术文档和架构说明

面试用的项目介绍脚本

核心原则
立即动手:不要等学完再写代码,从第一天就开始写

问题驱动:遇到问题再针对性学习

先跑通后优化:先实现基本功能,再考虑性能优化

注重可展示性:代码结构清晰、有文档、有测试

检验标准
服务器能稳定运行8小时以上

支持至少20个并发客户端

能完整演示创建房间、加入房间、状态同步流程

能清晰讲解技术选型和架构设计

立即开始第一步:创建项目目录,写下第一个CMakeLists.txt文件。

根据以上要求,写一个纯C++轻量级网络对战服务器。代码可以在VS2022和VSCODE正确运行。


轻量级网络对战服务器设计与实现方案

基于您的需求,我将设计一个纯C++的轻量级网络对战服务器,该服务器采用epoll高并发模型,支持多线程架构和实时状态同步。下面是详细的实现方案和代码结构。

项目结构

reasonml

GameServer/
├── CMakeLists.txt
├── src/
│   ├── main.cpp
│   ├── network/
│   │   ├── EpollServer.h
│   │   ├── EpollServer.cpp
│   │   ├── Connection.h
│   │   ├── Connection.cpp
│   │   ├── NetworkManager.h
│   │   └── NetworkManager.cpp
│   ├── game/
│   │   ├── Room.h
│   │   ├── Room.cpp
│   │   ├── RoomManager.h
│   │   ├── RoomManager.cpp
│   │   ├── Player.h
│   │   └── Player.cpp
│   ├── protocol/
│   │   ├── proto/
│   │   │   ├── message.proto
│   │   ├── MessageProcessor.h
│   │   └── MessageProcessor.cpp
│   ├── utils/
│   │   ├── Logger.h
│   │   ├── Logger.cpp
│   │   ├── ThreadSafeQueue.h
│   │   ├── ObjectPool.h
│   │   └── Timer.h
│   └── db/
│       ├── DBManager.h
│       └── DBManager.cpp
├── test/
│   ├── CMakeLists.txt
│   └── client_simulator.cpp
└── README.md

CMakeLists.txt

cmake

cmake_minimum_required(VERSION 3.10)
project(GameServer VERSION 1.0)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Protobuf REQUIRED)
find_package(SQLite3 REQUIRED)
find_package(Threads REQUIRED)

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src)
include_directories(${CMAKE_CURRENT_BINARY_DIR})

# 编译proto文件
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS
    src/protocol/proto/message.proto
)

# 主服务器源文件
file(GLOB_RECURSE SERVER_SOURCES 
    "src/*.cpp"
)

# 生成服务器可执行文件
add_executable(GameServer ${SERVER_SOURCES} ${PROTO_SRCS} ${PROTO_HDRS})

target_link_libraries(GameServer
    PRIVATE
    ${Protobuf_LIBRARIES}
    SQLite::SQLite3
    Threads::Threads
)

# 测试客户端模拟器
add_executable(ClientSimulator test/client_simulator.cpp ${PROTO_SRCS} ${PROTO_HDRS})
target_link_libraries(ClientSimulator
    PRIVATE
    ${Protobuf_LIBRARIES}
    Threads::Threads
)

协议设计 (message.proto)

protobuf

syntax = "proto3";

package game_protocol;

message Header {
  uint32 message_type = 1;
  uint32 sequence_id = 2;
}

enum MessageType {
  UNKNOWN = 0;
  HEARTBEAT = 1;
  LOGIN_REQUEST = 2;
  LOGIN_RESPONSE = 3;
  CREATE_ROOM_REQUEST = 4;
  CREATE_ROOM_RESPONSE = 5;
  JOIN_ROOM_REQUEST = 6;
  JOIN_ROOM_RESPONSE = 7;
  ROOM_STATUS_UPDATE = 8;
  PLAYER_ACTION = 9;
  ERROR = 10;
}

message Heartbeat {
  uint64 timestamp = 1;
}

message LoginRequest {
  string username = 1;
  string password = 2;
}

message LoginResponse {
  bool success = 1;
  string error_message = 2;
  uint32 player_id = 3;
}

message CreateRoomRequest {
  uint32 player_id = 1;
  string room_name = 2;
}

message CreateRoomResponse {
  bool success = 1;
  string error_message = 2;
  uint32 room_id = 3;
}

message JoinRoomRequest {
  uint32 player_id = 1;
  uint32 room_id = 2;
}

message JoinRoomResponse {
  bool success = 1;
  string error_message = 2;
  uint32 room_id = 3;
  repeated PlayerInfo players = 4;
}

message PlayerInfo {
  uint32 player_id = 1;
  string username = 2;
  uint32 status = 3; // 0=waiting, 1=ready, 2=playing
  Position position = 4;
}

message Position {
  float x = 1;
  float y = 2;
  float z = 3;
  float rotation = 4;
}

message RoomStatusUpdate {
  uint32 room_id = 1;
  uint32 room_status = 2; // 0=waiting, 1=playing, 2=ended
  repeated PlayerInfo players = 3;
  uint64 timestamp = 4;
}

message PlayerAction {
  uint32 player_id = 1;
  uint32 action_type = 2; // 0=move, 1=attack, 2=use_item
  Position new_position = 3;
  uint32 target_id = 4;
  uint32 item_id = 5;
}

message GameMessage {
  Header header = 1;
  oneof payload {
    Heartbeat heartbeat = 2;
    LoginRequest login_request = 3;
    LoginResponse login_response = 4;
    CreateRoomRequest create_room_request = 5;
    CreateRoomResponse create_room_response = 6;
    JoinRoomRequest join_room_request = 7;
    JoinRoomResponse join_room_response = 8;
    RoomStatusUpdate room_status_update = 9;
    PlayerAction player_action = 10;
  }
}

核心类实现

1. 线程安全队列 (ThreadSafeQueue.h)

cpp

#pragma once

#include 
#include 
#include 
#include 

template 
class ThreadSafeQueue {
public:
    ThreadSafeQueue() = default;
    
    void push(T item) {
        std::unique_lock lock(mutex_);
        queue_.push(std::move(item));
        lock.unlock();
        cond_var_.notify_one();
    }
    
    std::optional pop() {
        std::unique_lock lock(mutex_);
        if (queue_.empty()) {
            return std::nullopt;
        }
        
        T item = std::move(queue_.front());
        queue_.pop();
        return item;
    }
    
    std::optional wait_and_pop(int timeout_ms = -1) {
        std::unique_lock lock(mutex_);
        
        if (timeout_ms < 0) {
            // 无限等待
            cond_var_.wait(lock, [this] { return !queue_.empty(); });
        } else {
            // 带超时等待
            auto result = cond_var_.wait_for(lock, 
                std::chrono::milliseconds(timeout_ms),
                [this] { return !queue_.empty(); });
                
            if (!result) {
                return std::nullopt; // 超时
            }
        }
        
        T item = std::move(queue_.front());
        queue_.pop();
        return item;
    }
    
    bool empty() const {
        std::lock_guard lock(mutex_);
        return queue_.empty();
    }
    
    size_t size() const {
        std::lock_guard lock(mutex_);
        return queue_.size();
    }

private:
    mutable std::mutex mutex_;
    std::queue queue_;
    std::condition_variable cond_var_;
};

2. 日志系统 (Logger.h/cpp)

cpp

// Logger.h
#pragma once

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

enum class LogLevel {
    DEBUG,
    INFO,
    WARNING,
    ERROR,
    FATAL
};

class Logger {
public:
    static Logger& getInstance();
    
    void setLogLevel(LogLevel level);
    void setLogFile(const std::string& filename);
    
    template
    void debug(const std::string& format, Args... args);
    
    template
    void info(const std::string& format, Args... args);
    
    template
    void warning(const std::string& format, Args... args);
    
    template
    void error(const std::string& format, Args... args);
    
    template
    void fatal(const std::string& format, Args... args);
    
private:
    Logger();
    ~Logger();
    
    template
    void log(LogLevel level, const std::string& format, Args... args);
    
    std::string getCurrentTime();
    std::string levelToString(LogLevel level);
    
    std::mutex mutex_;
    std::ofstream log_file_;
    LogLevel log_level_ = LogLevel::INFO;
};

// 模板实现
template
void Logger::debug(const std::string& format, Args... args) {
    log(LogLevel::DEBUG, format, std::forward(args)...);
}

template
void Logger::info(const std::string& format, Args... args) {
    log(LogLevel::INFO, format, std::forward(args)...);
}

template
void Logger::warning(const std::string& format, Args... args) {
    log(LogLevel::WARNING, format, std::forward(args)...);
}

template
void Logger::error(const std::string& format, Args... args) {
    log(LogLevel::ERROR, format, std::forward(args)...);
}

template
void Logger::fatal(const std::string& format, Args... args) {
    log(LogLevel::FATAL, format, std::forward(args)...);
}

template
void Logger::log(LogLevel level, const std::string& format, Args... args) {
    if (level < log_level_) {
        return;
    }
    
    std::stringstream ss;
    auto thread_id = std::this_thread::get_id();
    
    ss << getCurrentTime() << " [" << std::setw(8) << levelToString(level) << "] "
       << "(" << thread_id << ") ";
       
    // 格式化字符串
    char buffer[1024];
    snprintf(buffer, sizeof(buffer), format.c_str(), args...);
    ss << buffer;
    
    std::string log_message = ss.str();
    
    std::lock_guard lock(mutex_);
    std::cout << log_message << std::endl;
    
    if (log_file_.is_open()) {
        log_file_ << log_message << std::endl;
    }
}

cpp

// Logger.cpp
#include "utils/Logger.h"

Logger& Logger::getInstance() {
    static Logger instance;
    return instance;
}

Logger::Logger() {
    // 默认情况下只输出到控制台
}

Logger::~Logger() {
    if (log_file_.is_open()) {
        log_file_.close();
    }
}

void Logger::setLogLevel(LogLevel level) {
    log_level_ = level;
}

void Logger::setLogFile(const std::string& filename) {
    std::lock_guard lock(mutex_);
    
    if (log_file_.is_open()) {
        log_file_.close();
    }
    
    log_file_.open(filename, std::ios::out | std::ios::app);
    
    if (!log_file_.is_open()) {
        std::cerr << "Failed to open log file: " << filename << std::endl;
    }
}

std::string Logger::getCurrentTime() {
    auto now = std::chrono::system_clock::now();
    auto now_c = std::chrono::system_clock::to_time_t(now);
    auto now_ms = std::chrono::duration_cast(
        now.time_since_epoch()) % 1000;
        
    std::stringstream ss;
    ss << std::put_time(std::localtime(&now_c), "%Y-%m-%d %H:%M:%S");
    ss << '.' << std::setfill('0') << std::setw(3) << now_ms.count();
    
    return ss.str();
}

std::string Logger::levelToString(LogLevel level) {
    switch (level) {
        case LogLevel::DEBUG:   return "DEBUG";
        case LogLevel::INFO:    return "INFO";
        case LogLevel::WARNING: return "WARNING";
        case LogLevel::ERROR:   return "ERROR";
        case LogLevel::FATAL:   return "FATAL";
        default:                return "UNKNOWN";
    }
}

3. 对象池 (ObjectPool.h)

cpp

#pragma once

#include 
#include 
#include 
#include 

template 
class ObjectPool {
public:
    explicit ObjectPool(size_t initial_size = 32) {
        objects_.reserve(initial_size);
        for (size_t i = 0; i < initial_size; ++i) {
            objects_.push_back(std::make_unique());
        }
    }
    
    // 自定义构造函数
    template 
    explicit ObjectPool(size_t initial_size, Func&& constructor) {
        objects_.reserve(initial_size);
        for (size_t i = 0; i < initial_size; ++i) {
            auto obj = std::make_unique();
            constructor(*obj);
            objects_.push_back(std::move(obj));
        }
    }
    
    // 获取一个对象,如果池中没有,则创建新的
    std::unique_ptr acquire() {
        std::lock_guard lock(mutex_);
        
        if (objects_.empty()) {
            return std::make_unique();
        }
        
        auto obj = std::move(objects_.back());
        objects_.pop_back();
        return obj;
    }
    
    // 将对象返回池中
    void release(std::unique_ptr obj) {
        if (!obj) return;
        
        std::lock_guard lock(mutex_);
        objects_.push_back(std::move(obj));
    }
    
    // 获取当前池中对象数量
    size_t size() const {
        std::lock_guard lock(mutex_);
        return objects_.size();
    }
    
    // 调整池大小
    void resize(size_t new_size) {
        std::lock_guard lock(mutex_);
        
        if (new_size > objects_.size()) {
            // 扩容
            objects_.reserve(new_size);
            for (size_t i = objects_.size(); i < new_size; ++i) {
                objects_.push_back(std::make_unique());
            }
        } else if (new_size < objects_.size()) {
            // 缩容
            objects_.resize(new_size);
        }
    }
    
private:
    std::vector> objects_;
    mutable std::mutex mutex_;
};

4. 定时器 (Timer.h)

cpp

#pragma once

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

class Timer {
public:
    using TimerId = uint64_t;
    using Clock = std::chrono::steady_clock;
    using TimePoint = Clock::time_point;
    using Duration = Clock::duration;
    using Callback = std::function;
    
    struct TimerTask {
        TimerId id;
        TimePoint target_time;
        Duration interval;
        bool repeat;
        Callback callback;
        
        bool operator>(const TimerTask& other) const {
            return target_time > other.target_time;
        }
    };

public:
    Timer() : running_(false), next_timer_id_(1) {}
    ~Timer() {
        stop();
    }
    
    void start() {
        std::lock_guard lock(mutex_);
        if (running_) return;
        
        running_ = true;
        thread_ = std::thread(&Timer::timerLoop, this);
    }
    
    void stop() {
        {
            std::lock_guard lock(mutex_);
            if (!running_) return;
            
            running_ = false;
        }
        
        cond_var_.notify_all();
        
        if (thread_.joinable()) {
            thread_.join();
        }
    }
    
    // 创建一次性定时器
    TimerId setTimeout(Callback callback, int timeout_ms) {
        return addTimer(std::move(callback), 
                       std::chrono::milliseconds(timeout_ms), 
                       false);
    }
    
    // 创建重复定时器
    TimerId setInterval(Callback callback, int interval_ms) {
        return addTimer(std::move(callback), 
                       std::chrono::milliseconds(interval_ms), 
                       true);
    }
    
    // 取消定时器
    bool clearTimer(TimerId id) {
        std::lock_guard lock(mutex_);
        
        auto it = active_timers_.find(id);
        if (it == active_timers_.end()) {
            return false;
        }
        
        active_timers_.erase(it);
        
        // 通知定时器线程重新检查任务队列
        cond_var_.notify_one();
        return true;
    }

private:
    TimerId addTimer(Callback callback, Duration timeout, bool repeat) {
        std::lock_guard lock(mutex_);
        
        TimerId id = next_timer_id_++;
        TimePoint target_time = Clock::now() + timeout;
        
        TimerTask task{id, target_time, timeout, repeat, std::move(callback)};
        
        timer_queue_.push(task);
        active_timers_[id] = true;
        
        // 通知定时器线程有新任务
        cond_var_.notify_one();
        return id;
    }
    
    void timerLoop() {
        std::priority_queue, std::greater<>> queue;
        
        while (true) {
            std::unique_lock lock(mutex_);
            
            // 将主队列中的任务转移到本地队列
            while (!timer_queue_.empty()) {
                queue.push(timer_queue_.top());
                timer_queue_.pop();
            }
            
            if (queue.empty()) {
                // 如果没有任务,等待新任务或停止信号
                cond_var_.wait(lock, [this] { 
                    return !timer_queue_.empty() || !running_;
                });
                
                if (!running_) break;
                continue;
            }
            
            auto& next_task = queue.top();
            
            if (active_timers_.find(next_task.id) == active_timers_.end()) {
                // 任务已被取消
                queue.pop();
                continue;
            }
            
            // 计算等待时间
            auto now = Clock::now();
            if (next_task.target_time <= now) {
                // 任务到期,执行
                TimerTask task = next_task;
                queue.pop();
                
                // 如果是重复任务,重新加入队列
                if (task.repeat) {
                    task.target_time = now + task.interval;
                    queue.push(task);
                } else {
                    active_timers_.erase(task.id);
                }
                
                // 解锁后执行回调
                lock.unlock();
                task.callback();
            } else {
                // 等待下一个任务
                auto wait_time = next_task.target_time - now;
                cond_var_.wait_for(lock, wait_time, [this, &next_task] {
                    return !timer_queue_.empty() || !running_ || 
                           active_timers_.find(next_task.id) == active_timers_.end();
                });
                
                if (!running_) break;
            }
        }
    }

private:
    std::atomic running_;
    std::atomic next_timer_id_;
    std::thread thread_;
    std::mutex mutex_;
    std::condition_variable cond_var_;
    std::map active_timers_;
    std::priority_queue, std::greater<>> timer_queue_;
};

5. 网络连接类 (Connection.h/cpp)

cpp

// Connection.h
#pragma once

#include 
#include 
#include 
#include 
#include 
#include 
#include "protocol/MessageProcessor.h"

class Connection : public std::enable_shared_from_this {
public:
    using ConnectionPtr = std::shared_ptr;
    using MessageCallback = std::function;
    using DisconnectCallback = std::function;
    
    static constexpr size_t MAX_BUFFER_SIZE = 8192;

public:
    Connection(int fd, const std::string& address);
    ~Connection();
    
    // 禁止拷贝
    Connection(const Connection&) = delete;
    Connection& operator=(const Connection&) = delete;
    
    int getFd() const { return fd_; }
    const std::string& getAddress() const { return address_; }
    uint32_t getPlayerId() const { return player_id_; }
    
    void setPlayerId(uint32_t id) { player_id_ = id; }
    void setMessageCallback(MessageCallback cb) { message_callback_ = std::move(cb); }
    void setDisconnectCallback(DisconnectCallback cb) { disconnect_callback_ = std::move(cb); }
    
    // 处理接收到的数据
    void handleRead();
    
    // 发送数据
    bool sendMessage(const game_protocol::GameMessage& message);
    
    // 关闭连接
    void close();
    
    // 检查连接是否有效
    bool isValid() const { return fd_ >= 0; }
    
    // 心跳检测
    void updateLastActiveTime();
    bool isTimeout(int timeout_seconds) const;

private:
    // 处理接收到的完整消息
    void processMessage(const game_protocol::GameMessage& message);

private:
    int fd_;
    std::string address_;
    uint32_t player_id_ = 0;
    
    std::array recv_buffer_;
    size_t buffer_offset_ = 0;
    
    MessageCallback message_callback_;
    DisconnectCallback disconnect_callback_;
    
    std::shared_ptr msg_processor_;
    std::chrono::steady_clock::time_point last_active_time_;
};

cpp

// Connection.cpp
#include "network/Connection.h"
#include "utils/Logger.h"
#include 
#include 
#include 
#include 

Connection::Connection(int fd, const std::string& address) 
    : fd_(fd), 
      address_(address),
      msg_processor_(std::make_shared()),
      last_active_time_(std::chrono::steady_clock::now())
{
    Logger::getInstance().info("New connection established: %s, fd: %d", 
        address.c_str(), fd);
}

Connection::~Connection() {
    close();
}

void Connection::handleRead() {
    if (!isValid()) return;
    
    ssize_t bytes_read = recv(fd_, recv_buffer_.data() + buffer_offset_, 
        MAX_BUFFER_SIZE - buffer_offset_, 0);
        
    if (bytes_read <= 0) {
        // 连接已关闭或出错
        Logger::getInstance().info("Connection closed: %s, fd: %d, bytes_read: %zd", 
            address_.c_str(), fd_, bytes_read);
        close();
        return;
    }
    
    // 更新最后活跃时间
    updateLastActiveTime();
    
    // 更新缓冲区偏移量
    buffer_offset_ += bytes_read;
    
    // 处理接收到的数据
    size_t processed_size = 0;
    while (processed_size < buffer_offset_) {
        std::optional message = 
            msg_processor_->parseMessage(recv_buffer_.data() + processed_size, 
                buffer_offset_ - processed_size);
                
        if (!message) {
            break; // 数据不完整,等待更多数据
        }
        
        // 处理消息
        processMessage(*message);
        
        // 更新已处理数据大小
        size_t message_size = msg_processor_->getLastMessageSize();
        processed_size += message_size;
    }
    
    // 移动未处理的数据到缓冲区起始位置
    if (processed_size > 0) {
        if (processed_size < buffer_offset_) {
            std::memmove(recv_buffer_.data(), recv_buffer_.data() + processed_size,
                buffer_offset_ - processed_size);
        }
        buffer_offset_ -= processed_size;
    }
}

bool Connection::sendMessage(const game_protocol::GameMessage& message) {
    if (!isValid()) return false;
    
    std::vector data = msg_processor_->serializeMessage(message);
    
    ssize_t bytes_sent = send(fd_, data.data(), data.size(), 0);
    
    if (bytes_sent != static_cast(data.size())) {
        Logger::getInstance().error("Failed to send message to %s, fd: %d, error: %s", 
            address_.c_str(), fd_, strerror(errno));
        return false;
    }
    
    // 更新最后活跃时间
    updateLastActiveTime();
    return true;
}

void Connection::processMessage(const game_protocol::GameMessage& message) {
    if (message_callback_) {
        message_callback_(shared_from_this(), message);
    }
}

void Connection::close() {
    if (fd_ >= 0) {
        Logger::getInstance().info("Closing connection: %s, fd: %d", address_.c_str(), fd_);
        
        if (disconnect_callback_) {
            disconnect_callback_(shared_from_this());
        }
        
        ::close(fd_);
        fd_ = -1;
    }
}

void Connection::updateLastActiveTime() {
    last_active_time_ = std::chrono::steady_clock::now();
}

bool Connection::isTimeout(int timeout_seconds) const {
    auto now = std::chrono::steady_clock::now();
    auto elapsed = std::chrono::duration_cast(
        now - last_active_time_).count();
    return elapsed >= timeout_seconds;
}

6. Epoll服务器 (EpollServer.h/cpp)

cpp

// EpollServer.h
#pragma once

#include 
#include 
#include 
#include 
#include 
#include 
#include "network/Connection.h"

class EpollServer {
public:
    using ConnectionCallback = std::function;
    
    EpollServer();
    ~EpollServer();
    
    // 禁止拷贝
    EpollServer(const EpollServer&) = delete;
    EpollServer& operator=(const EpollServer&) = delete;
    
    // 启动服务器
    bool start(int port, int max_connections = 1024);
    
    // 停止服务器
    void stop();
    
    // 设置回调
    void setConnectionCallback(ConnectionCallback cb) { connection_callback_ = std::move(cb); }
    
    // 获取连接数
    size_t getConnectionCount() const { return connections_.size(); }
    
    // 设置心跳超时时间
    void setHeartbeatTimeout(int seconds) { heartbeat_timeout_ = seconds; }
    
    // 检查心跳超时
    void checkHeartbeats();

private:
    // Epoll事件循环
    void eventLoop();
    
    // 接受新连接
    void acceptConnection();
    
    // 添加到epoll监控
    bool addToEpoll(int fd);
    
    // 从epoll中删除
    void removeFromEpoll(int fd);

private:
    int listen_fd_ = -1;
    int epoll_fd_ = -1;
    
    std::atomic running_{false};
    std::vector worker_threads_;
    
    std::mutex connections_mutex_;
    std::unordered_map connections_;
    
    ConnectionCallback connection_callback_;
    
    int heartbeat_timeout_ = 30; // 默认30秒
};

cpp

// EpollServer.cpp
#include "network/EpollServer.h"
#include "utils/Logger.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

constexpr int MAX_EVENTS = 64;

EpollServer::EpollServer() {
    // 创建epoll实例
    epoll_fd_ = epoll_create1(0);
    if (epoll_fd_ < 0) {
        Logger::getInstance().fatal("Failed to create epoll instance: %s", strerror(errno));
        throw std::runtime_error("Failed to create epoll instance");
    }
}

EpollServer::~EpollServer() {
    stop();
    
    if (epoll_fd_ >= 0) {
        close(epoll_fd_);
        epoll_fd_ = -1;
    }
}

bool EpollServer::start(int port, int max_connections) {
    if (running_) {
        Logger::getInstance().warning("Server is already running");
        return false;
    }
    
    // 创建监听套接字
    listen_fd_ = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_fd_ < 0) {
        Logger::getInstance().fatal("Failed to create socket: %s", strerror(errno));
        return false;
    }
    
    // 设置套接字选项
    int opt = 1;
    if (setsockopt(listen_fd_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
        Logger::getInstance().error("Failed to set socket options: %s", strerror(errno));
        close(listen_fd_);
        listen_fd_ = -1;
        return false;
    }
    
    // 设置非阻塞模式
    int flags = fcntl(listen_fd_, F_GETFL, 0);
    if (flags < 0 || fcntl(listen_fd_, F_SETFL, flags | O_NONBLOCK) < 0) {
        Logger::getInstance().error("Failed to set non-blocking mode: %s", strerror(errno));
        close(listen_fd_);
        listen_fd_ = -1;
        return false;
    }
    
    // 绑定地址
    struct sockaddr_in addr;
    std::memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(port);
    
    if (bind(listen_fd_, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        Logger::getInstance().fatal("Failed to bind address: %s", strerror(errno));
        close(listen_fd_);
        listen_fd_ = -1;
        return false;
    }
    
    // 开始监听
    if (listen(listen_fd_, SOMAXCONN) < 0) {
        Logger::getInstance().fatal("Failed to listen: %s", strerror(errno));
        close(listen_fd_);
        listen_fd_ = -1;
        return false;
    }
    
    // 添加监听套接字到epoll
    if (!addToEpoll(listen_fd_)) {
        close(listen_fd_);
        listen_fd_ = -1;
        return false;
    }
    
    // 标记服务器为运行状态
    running_ = true;
    
    // 创建工作线程
    unsigned num_threads = std::thread::hardware_concurrency();
    Logger::getInstance().info("Starting server with %u worker threads", num_threads);
    
    for (unsigned i = 0; i < num_threads; ++i) {
        worker_threads_.emplace_back(&EpollServer::eventLoop, this);
    }
    
    Logger::getInstance().info("Server started on port %d", port);
    return true;
}

void EpollServer::stop() {
    if (!running_) return;
    
    running_ = false;
    
    // 等待工作线程退出
    for (auto& thread : worker_threads_) {
        if (thread.joinable()) {
            thread.join();
        }
    }
    worker_threads_.clear();
    
    // 关闭所有连接
    {
        std::lock_guard lock(connections_mutex_);
        
        for (auto& pair : connections_) {
            if (pair.second) {
                pair.second->close();
            }
        }
        connections_.clear();
    }
    
    // 关闭监听套接字
    if (listen_fd_ >= 0) {
        close(listen_fd_);
        listen_fd_ = -1;
    }
    
    Logger::getInstance().info("Server stopped");
}

void EpollServer::eventLoop() {
    struct epoll_event events[MAX_EVENTS];
    
    while (running_) {
        int num_events = epoll_wait(epoll_fd_, events, MAX_EVENTS, 100); // 100ms timeout
        
        if (num_events < 0) {
            if (errno == EINTR) {
                continue; // 被信号中断,重新等待
            }
            
            Logger::getInstance().error("epoll_wait failed: %s", strerror(errno));
            break;
        }
        
        for (int i = 0; i < num_events; ++i) {
            int fd = events[i].data.fd;
            
            if (fd == listen_fd_) {
                // 有新连接
                acceptConnection();
            } else if (events[i].events & EPOLLIN) {
                // 有数据可读
                Connection::ConnectionPtr conn;
                
                {
                    std::lock_guard lock(connections_mutex_);
                    auto it = connections_.find(fd);
                    if (it != connections_.end()) {
                        conn = it->second;
                    }
                }
                
                if (conn) {
                    conn->handleRead();
                    
                    if (!conn->isValid()) {
                        // 连接已关闭
                        std::lock_guard lock(connections_mutex_);
                        connections_.erase(fd);
                        removeFromEpoll(fd);
                    }
                }
            }
        }
    }
}

void EpollServer::acceptConnection() {
    struct sockaddr_in addr;
    socklen_t addr_len = sizeof(addr);
    
    while (running_) {
        int client_fd = accept(listen_fd_, (struct sockaddr*)&addr, &addr_len);
        
        if (client_fd < 0) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                break; // 没有更多连接
            }
            
            Logger::getInstance().error("Failed to accept connection: %s", strerror(errno));
            break;
        }
        
        // 设置非阻塞模式
        int flags = fcntl(client_fd, F_GETFL, 0);
        if (flags < 0 || fcntl(client_fd, F_SETFL, flags | O_NONBLOCK) < 0) {
            Logger::getInstance().error("Failed to set client socket non-blocking: %s", 
                strerror(errno));
            close(client_fd);
            continue;
        }
        
        // 获取客户端地址
        char client_ip[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &(addr.sin_addr), client_ip, INET_ADDRSTRLEN);
        std::string address = std::string(client_ip) + ":" + 
            std::to_string(ntohs(addr.sin_port));
        
        // 创建连接对象
        auto conn = std::make_shared(client_fd, address);
        
        // 设置回调
        conn->setMessageCallback([this](Connection::ConnectionPtr conn, 
                                        const game_protocol::GameMessage& msg) {
            // 在这里处理消息
            if (connection_callback_) {
                connection_callback_(conn);
            }
        });
        
        conn->setDisconnectCallback([this](Connection::ConnectionPtr conn) {
            // 连接断开
            int fd = conn->getFd();
            removeFromEpoll(fd);
            
            std::lock_guard lock(connections_mutex_);
            connections_.erase(fd);
        });
        
        // 添加到epoll
        if (!addToEpoll(client_fd)) {
            conn->close();
            continue;
        }
        
        // 添加到连接映射
        {
            std::lock_guard lock(connections_mutex_);
            connections_[client_fd] = conn;
        }
        
        Logger::getInstance().info("Accepted new connection from %s, fd: %d, total connections: %zu",
            address.c_str(), client_fd, connections_.size());
            
        // 通知新连接
        if (connection_callback_) {
            connection_callback_(conn);
        }
    }
}

bool EpollServer::addToEpoll(int fd) {
    struct epoll_event ev;
    std::memset(&ev, 0, sizeof(ev));
    ev.events = EPOLLIN | EPOLLET; // 边缘触发模式
    ev.data.fd = fd;
    
    if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &ev) < 0) {
        Logger::getInstance().error("Failed to add fd to epoll: %s", strerror(errno));
        return false;
    }
    
    return true;
}

void EpollServer::removeFromEpoll(int fd) {
    epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr);
}

void EpollServer::checkHeartbeats() {
    std::vector timeout_connections;
    
    {
        std::lock_guard lock(connections_mutex_);
        
        for (auto it = connections_.begin(); it != connections_.end();) {
            if (it->second && it->second->isTimeout(heartbeat_timeout_)) {
                timeout_connections.push_back(it->second);
                it = connections_.erase(it);
            } else {
                ++it;
            }
        }
    }
    
    // 关闭超时连接
    for (auto& conn : timeout_connections) {
        Logger::getInstance().info("Connection timeout: %s, fd: %d", 
            conn->getAddress().c_str(), conn->getFd());
        conn->close();
    }
}

7. 消息处理器 (MessageProcessor.h/cpp)

cpp

// MessageProcessor.h
#pragma once

#include 
#include 
#include 
#include "protocol/proto/message.pb.h"

class MessageProcessor {
public:
    MessageProcessor();
    ~MessageProcessor();
    
    // 解析消息
    std::optional parseMessage(const char* data, size_t size);
    
    // 序列化消息
    std::vector serializeMessage(const game_protocol::GameMessage& message);
    
    // 获取上次处理的消息大小
    size_t getLastMessageSize() const { return last_message_size_; }

private:
    size_t last_message_size_ = 0;
};

cpp

// MessageProcessor.cpp
#include "protocol/MessageProcessor.h"
#include "utils/Logger.h"
#include 
#include  // 为了 htonl, ntohl 函数

MessageProcessor::MessageProcessor() {
    // 确保 protobuf 库已初始化
    GOOGLE_PROTOBUF_VERIFY_VERSION;
}

MessageProcessor::~MessageProcessor() {
    // 可选:释放 protobuf 全局资源
    // google::protobuf::ShutdownProtobufLibrary();
}

std::optional MessageProcessor::parseMessage(
    const char* data, size_t size) 
{
    last_message_size_ = 0;
    
    // 消息格式: 4字节消息大小(网络字节序)+ 消息内容
    if (size < 4) {
        // 数据不完整,无法读取消息大小
        return std::nullopt;
    }
    
    // 读取消息大小
    uint32_t message_size;
    std::memcpy(&message_size, data, 4);
    message_size = ntohl(message_size);
    
    if (size < 4 + message_size) {
        // 数据不完整,等待更多数据
        return std::nullopt;
    }
    
    // 解析消息
    game_protocol::GameMessage message;
    if (!message.ParseFromArray(data + 4, message_size)) {
        Logger::getInstance().error("Failed to parse message");
        // 将消息大小+4(头部)设为已处理大小,这样可以跳过这个错误的消息
        last_message_size_ = message_size + 4;
        return std::nullopt;
    }
    
    // 设置已处理的消息大小
    last_message_size_ = message_size + 4;
    
    return message;
}

std::vector MessageProcessor::serializeMessage(
    const game_protocol::GameMessage& message) 
{
    // 序列化消息
    std::string message_data = message.SerializeAsString();
    
    // 计算消息大小(网络字节序)
    uint32_t message_size = htonl(static_cast(message_data.size()));
    
    // 创建结果缓冲区
    std::vector result(4 + message_data.size());
    
    // 写入消息大小
    std::memcpy(result.data(), &message_size, 4);
    
    // 写入消息内容
    std::memcpy(result.data() + 4, message_data.data(), message_data.size());
    
    return result;
}

8. 玩家类 (Player.h/cpp)

cpp

// Player.h
#pragma once

#include 
#include 
#include "network/Connection.h"
#include "protocol/proto/message.pb.h"

class Player {
public:
    using PlayerPtr = std::shared_ptr;

public:
    Player(uint32_t id, const std::string& username);
    ~Player() = default;
    
    // 禁止拷贝
    Player(const Player&) = delete;
    Player& operator=(const Player&) = delete;
    
    uint32_t getId() const { return id_; }
    const std::string& getUsername() const { return username_; }
    
    void setConnection(Connection::ConnectionPtr conn) { connection_ = conn; }
    Connection::ConnectionPtr getConnection() const { return connection_; }
    
    bool isOnline() const { return connection_ && connection_->isValid(); }
    
    // 设置玩家位置
    void setPosition(float x, float y, float z, float rotation);
    
    // 获取玩家位置
    const game_protocol::Position& getPosition() const { return position_; }
    
    // 设置玩家状态
    void setStatus(uint32_t status) { status_ = status; }
    uint32_t getStatus() const { return status_; }
    
    // 将玩家数据转换为协议消息
    game_protocol::PlayerInfo toPlayerInfo() const;
    
    // 发送消息给玩家
    bool sendMessage(const game_protocol::GameMessage& message);

private:
    uint32_t id_;
    std::string username_;
    Connection::ConnectionPtr connection_;
    
    game_protocol::Position position_;
    uint32_t status_ = 0; // 0=waiting, 1=ready, 2=playing
};

cpp

// Player.cpp
#include "game/Player.h"

Player::Player(uint32_t id, const std::string& username) 
    : id_(id), username_(username)
{
    // 初始化位置
    position_.set_x(0.0f);
    position_.set_y(0.0f);
    position_.set_z(0.0f);
    position_.set_rotation(0.0f);
}

void Player::setPosition(float x, float y, float z, float rotation) {
    position_.set_x(x);
    position_.set_y(y);
    position_.set_z(z);
    position_.set_rotation(rotation);
}

game_protocol::PlayerInfo Player::toPlayerInfo() const {
    game_protocol::PlayerInfo info;
    
    info.set_player_id(id_);
    info.set_username(username_);
    info.set_status(status_);
    
    // 复制位置信息
    auto* pos = info.mutable_position();
    *pos = position_;
    
    return info;
}

bool Player::sendMessage(const game_protocol::GameMessage& message) {
    if (!isOnline()) {
        return false;
    }
    
    return connection_->sendMessage(message);
}

9. 房间类 (Room.h/cpp)

cpp

// Room.h
#pragma once

#include 
#include 
#include 
#include 
#include 
#include "game/Player.h"
#include "utils/Timer.h"

class Room {
public:
    using RoomPtr = std::shared_ptr;
    
    enum Status {
        WAITING = 0,
        PLAYING = 1,
        ENDED = 2
    };

public:
    Room(uint32_t id, const std::string& name, uint32_t max_players = 4);
    ~Room() = default;
    
    // 禁止拷贝
    Room(const Room&) = delete;
    Room& operator=(const Room&) = delete;
    
    uint32_t getId() const { return id_; }
    const std::string& getName() const { return name_; }
    Status getStatus() const { return status_; }
    
    // 添加玩家
    bool addPlayer(Player::PlayerPtr player);
    
    // 移除玩家
    bool removePlayer(uint32_t player_id);
    
    // 获取玩家
    Player::PlayerPtr getPlayer(uint32_t player_id);
    
    // 获取所有玩家
    std::vector getAllPlayers();
    
    // 开始游戏
    bool startGame();
    
    // 结束游戏
    void endGame();
    
    // 游戏更新(定时调用)
    void update(float dt);
    
    // 广播消息给所有玩家
    void broadcastMessage(const game_protocol::GameMessage& message, 
                          uint32_t except_player_id = 0);
    
    // 发送房间状态更新
    void sendStatusUpdate();
    
    // 处理玩家动作
    void handlePlayerAction(uint32_t player_id, const game_protocol::PlayerAction& action);
    
    // 检查玩家连接状态
    void checkPlayerConnections();
    
    // 房间是否为空
    bool isEmpty() const { return players_.empty(); }
    
    // 是否已满
    bool isFull() const { return players_.size() >= max_players_; }

private:
    // 生成房间状态更新消息
    game_protocol::GameMessage createStatusUpdateMessage() const;

private:
    uint32_t id_;
    std::string name_;
    Status status_ = WAITING;
    
    uint32_t max_players_;
    std::mutex players_mutex_;
    std::unordered_map players_;
    
    std::shared_ptr game_timer_;
    uint64_t last_update_time_ = 0;
};

cpp

// Room.cpp
#include "game/Room.h"
#include "utils/Logger.h"
#include 

Room::Room(uint32_t id, const std::string& name, uint32_t max_players)
    : id_(id), 
      name_(name),
      max_players_(max_players),
      game_timer_(std::make_shared())
{
    game_timer_->start();
    Logger::getInstance().info("Room created: %s, id: %u", name.c_str(), id);
}

bool Room::addPlayer(Player::PlayerPtr player) {
    if (!player) return false;
    
    std::lock_guard lock(players_mutex_);
    
    if (players_.size() >= max_players_) {
        Logger::getInstance().warning("Room %u is full", id_);
        return false;
    }
    
    uint32_t player_id = player->getId();
    if (players_.find(player_id) != players_.end()) {
        Logger::getInstance().warning("Player %u already in room %u", player_id, id_);
        return false;
    }
    
    players_[player_id] = player;
    
    Logger::getInstance().info("Player %u joined room %u", player_id, id_);
    
    // 发送状态更新
    sendStatusUpdate();
    
    return true;
}

bool Room::removePlayer(uint32_t player_id) {
    std::lock_guard lock(players_mutex_);
    
    auto it = players_.find(player_id);
    if (it == players_.end()) {
        return false;
    }
    
    players_.erase(it);
    Logger::getInstance().info("Player %u left room %u", player_id, id_);
    
    // 如果房间为空,则不需要发送状态更新
    if (!players_.empty()) {
        sendStatusUpdate();
    }
    
    return true;
}

Player::PlayerPtr Room::getPlayer(uint32_t player_id) {
    std::lock_guard lock(players_mutex_);
    
    auto it = players_.find(player_id);
    if (it != players_.end()) {
        return it->second;
    }
    
    return nullptr;
}

std::vector Room::getAllPlayers() {
    std::vector result;
    
    std::lock_guard lock(players_mutex_);
    result.reserve(players_.size());
    
    for (auto& pair : players_) {
        result.push_back(pair.second);
    }
    
    return result;
}

bool Room::startGame() {
    // 只有在等待状态下才能开始游戏
    if (status_ != WAITING) {
        return false;
    }
    
    std::lock_guard lock(players_mutex_);
    
    // 至少需要2个玩家
    if (players_.size() < 2) {
        return false;
    }
    
    // 检查是否所有玩家都已准备就绪
    for (auto& pair : players_) {
        if (pair.second->getStatus() != 1) { // 1=ready
            return false;
        }
    }
    
    // 更新房间状态
    status_ = PLAYING;
    
    // 更新所有玩家状态
    for (auto& pair : players_) {
        pair.second->setStatus(2); // 2=playing
    }
    
    // 设置游戏循环
    last_update_time_ = std::chrono::duration_cast(
        std::chrono::system_clock::now().time_since_epoch()).count();
        
    // 每100ms更新一次游戏状态
    game_timer_->setInterval([this] {
        uint64_t now = std::chrono::duration_cast(
            std::chrono::system_clock::now().time_since_epoch()).count();
            
        float dt = (now - last_update_time_) / 1000.0f;
        last_update_time_ = now;
        
        update(dt);
    }, 100);
    
    Logger::getInstance().info("Game started in room %u", id_);
    
    // 发送状态更新
    sendStatusUpdate();
    
    return true;
}

void Room::endGame() {
    if (status_ != PLAYING) {
        return;
    }
    
    // 更新房间状态
    status_ = ENDED;
    
    // 停止游戏循环
    game_timer_->clearTimer(0);
    
    std::lock_guard lock(players_mutex_);
    
    // 更新所有玩家状态
    for (auto& pair : players_) {
        pair.second->setStatus(0); // 0=waiting
    }
    
    Logger::getInstance().info("Game ended in room %u", id_);
    
    // 发送状态更新
    sendStatusUpdate();
    
    // 恢复到等待状态
    status_ = WAITING;
}

void Room::update(float dt) {
    // 在这里实现游戏逻辑更新
    // 例如:更新玩家位置,检测碰撞等
    
    // 发送状态更新
    sendStatusUpdate();
}

void Room::broadcastMessage(const game_protocol::GameMessage& message, uint32_t except_player_id) {
    std::vector players_copy;
    
    {
        std::lock_guard lock(players_mutex_);
        players_copy.reserve(players_.size());
        
        for (auto& pair : players_) {
            if (pair.first != except_player_id) {
                players_copy.push_back(pair.second);
            }
        }
    }
    
    for (auto& player : players_copy) {
        if (player->isOnline()) {
            player->sendMessage(message);
        }
    }
}

void Room::sendStatusUpdate() {
    auto status_message = createStatusUpdateMessage();
    broadcastMessage(status_message);
}

game_protocol::GameMessage Room::createStatusUpdateMessage() const {
    game_protocol::GameMessage message;
    
    // 设置消息头
    auto* header = message.mutable_header();
    header->set_message_type(static_cast(game_protocol::ROOM_STATUS_UPDATE));
    header->set_sequence_id(0);
    
    // 设置状态更新内容
    auto* update = message.mutable_room_status_update();
    update->set_room_id(id_);
    update->set_room_status(static_cast(status_));
    update->set_timestamp(std::chrono::duration_cast(
        std::chrono::system_clock::now().time_since_epoch()).count());
    
    // 添加所有玩家信息
    std::lock_guard lock(players_mutex_);
    
    for (auto& pair : players_) {
        auto* player_info = update->add_players();
        *player_info = pair.second->toPlayerInfo();
    }
    
    return message;
}

void Room::handlePlayerAction(uint32_t player_id, const game_protocol::PlayerAction& action) {
    // 只有游戏进行中才处理玩家动作
    if (status_ != PLAYING) {
        return;
    }
    
    // 获取玩家
    auto player = getPlayer(player_id);
    if (!player) {
        return;
    }
    
    // 处理不同类型的动作
    switch (action.action_type()) {
        case 0: // 移动
            // 更新玩家位置
            if (action.has_new_position()) {
                auto& pos = action.new_position();
                player->setPosition(pos.x(), pos.y(), pos.z(), pos.rotation());
            }
            break;
            
        case 1: // 攻击
            // 处理攻击逻辑
            // ...
            break;
            
        case 2: // 使用道具
            // 处理道具使用逻辑
            // ...
            break;
            
        default:
            Logger::getInstance().warning("Unknown action type: %u", action.action_type());
            break;
    }
    
    // 广播动作给其他玩家
    game_protocol::GameMessage message;
    auto* header = message.mutable_header();
    header->set_message_type(static_cast(game_protocol::PLAYER_ACTION));
    header->set_sequence_id(0);
    
    auto* action_msg = message.mutable_player_action();
    *action_msg = action;
    
    broadcastMessage(message, player_id);
}

void Room::checkPlayerConnections() {
    std::lock_guard lock(players_mutex_);
    
    bool player_removed = false;
    
    for (auto it = players_.begin(); it != players_.end(); ) {
        if (!it->second->isOnline()) {
            Logger::getInstance().info("Removing offline player %u from room %u", 
                it->first, id_);
            it = players_.erase(it);
            player_removed = true;
        } else {
            ++it;
        }
    }
    
    // 如果有玩家断开连接,发送状态更新
    if (player_removed && !players_.empty()) {
        sendStatusUpdate();
    }
    
    // 如果游戏正在进行,但玩家数不足,则结束游戏
    if (status_ == PLAYING && players_.size() < 2) {
        Logger::getInstance().info("Not enough players, ending game in room %u", id_);
        endGame();
    }
}

10. 房间管理器 (RoomManager.h/cpp)

cpp

// RoomManager.h
#pragma once

#include 
#include 
#include 
#include "game/Room.h"

class RoomManager {
public:
    static RoomManager& getInstance();
    
    // 创建房间
    Room::RoomPtr createRoom(const std::string& name, uint32_t max_players = 4);
    
    // 查找房间
    Room::RoomPtr getRoom(uint32_t room_id);
    
    // 获取所有房间
    std::vector getAllRooms();
    
    // 获取可加入的房间列表
    std::vector getJoinableRooms();
    
    // 删除房间
    bool removeRoom(uint32_t room_id);
    
    // 清理空房间
    void cleanupEmptyRooms();
    
private:
    RoomManager();
    ~RoomManager() = default;
    
    RoomManager(const RoomManager&) = delete;
    RoomManager& operator=(const RoomManager&) = delete;
    
    // 生成房间ID
    uint32_t generateRoomId();

private:
    std::mutex rooms_mutex_;
    std::unordered_map rooms_;
    uint32_t next_room_id_ = 1;
};

cpp

// RoomManager.cpp
#include "game/RoomManager.h"
#include "utils/Logger.h"

RoomManager& RoomManager::getInstance() {
    static RoomManager instance;
    return instance;
}

RoomManager::RoomManager() {
    Logger::getInstance().info("RoomManager initialized");
}

Room::RoomPtr RoomManager::createRoom(const std::string& name, uint32_t max_players) {
    std::lock_guard lock(rooms_mutex_);
    
    uint32_t room_id = generateRoomId();
    auto room = std::make_shared(room_id, name, max_players);
    
    rooms_[room_id] = room;
    Logger::getInstance().info("Created room: %s, id: %u, max_players: %u", 
        name.c_str(), room_id, max_players);
        
    return room;
}

Room::RoomPtr RoomManager::getRoom(uint32_t room_id) {
    std::lock_guard lock(rooms_mutex_);
    
    auto it = rooms_.find(room_id);
    if (it != rooms_.end()) {
        return it->second;
    }
    
    return nullptr;
}

std::vector RoomManager::getAllRooms() {
    std::vector result;
    
    std::lock_guard lock(rooms_mutex_);
    
    result.reserve(rooms_.size());
    for (auto& pair : rooms_) {
        result.push_back(pair.second);
    }
    
    return result;
}

std::vector RoomManager::getJoinableRooms() {
    std::vector result;
    
    std::lock_guard lock(rooms_mutex_);
    
    for (auto& pair : rooms_) {
        if (pair.second->getStatus() == Room::WAITING && !pair.second->isFull()) {
            result.push_back(pair.second);
        }
    }
    
    return result;
}

bool RoomManager::removeRoom(uint32_t room_id) {
    std::lock_guard lock(rooms_mutex_);
    
    auto it = rooms_.find(room_id);
    if (it == rooms_.end()) {
        return false;
    }
    
    Logger::getInstance().info("Removed room: %s, id: %u", 
        it->second->getName().c_str(), room_id);
        
    rooms_.erase(it);
    return true;
}

void RoomManager::cleanupEmptyRooms() {
    std::lock_guard lock(rooms_mutex_);
    
    for (auto it = rooms_.begin(); it != rooms_.end(); ) {
        if (it->second->isEmpty()) {
            Logger::getInstance().info("Cleaning up empty room: %s, id: %u", 
                it->second->getName().c_str(), it->first);
            it = rooms_.erase(it);
        } else {
            ++it;
        }
    }
}

uint32_t RoomManager::generateRoomId() {
    return next_room_id_++;
}

11. 数据库管理器 (DBManager.h/cpp)

cpp

// DBManager.h
#pragma once

#include 
#include 
#include 
#include 
#include 
#include 

class DBManager {
public:
    static DBManager& getInstance();
    
    // 初始化数据库
    bool initialize(const std::string& db_path = "gameserver.db");
    
    // 关闭数据库
    void close();
    
    // 用户相关操作
    bool addUser(const std::string& username, const std::string& password);
    bool verifyUser(const std::string& username, const std::string& password);
    uint32_t getUserId(const std::string& username);
    std::string getUserName(uint32_t user_id);
    
    // 用户数据相关操作
    bool updateUserData(uint32_t user_id, const std::string& key, const std::string& value);
    std::optional getUserData(uint32_t user_id, const std::string& key);
    
    // 游戏统计相关操作
    bool recordGameResult(uint32_t user_id, bool win, int score);
    int getTotalGames(uint32_t user_id);
    int getWinGames(uint32_t user_id);
    
private:
    DBManager();
    ~DBManager();
    
    // 禁止拷贝
    DBManager(const DBManager&) = delete;
    DBManager& operator=(const DBManager&) = delete;
    
    // 初始化表
    bool initTables();
    
    // 执行SQL语句
    bool execSQL(const std::string& sql);
    
    // 执行SQL查询
    bool execQuery(const std::string& sql, 
                  std::function row_callback);

private:
    std::mutex db_mutex_;
    sqlite3* db_ = nullptr;
};

cpp

// DBManager.cpp
#include "db/DBManager.h"
#include "utils/Logger.h"
#include 
#include 

DBManager& DBManager::getInstance() {
    static DBManager instance;
    return instance;
}

DBManager::DBManager() {}

DBManager::~DBManager() {
    close();
}

bool DBManager::initialize(const std::string& db_path) {
    std::lock_guard lock(db_mutex_);
    
    if (db_ != nullptr) {
        Logger::getInstance().warning("Database already initialized");
        return true;
    }
    
    int rc = sqlite3_open(db_path.c_str(), &db_);
    if (rc != SQLITE_OK) {
        Logger::getInstance().error("Failed to open database: %s", sqlite3_errmsg(db_));
        db_ = nullptr;
        return false;
    }
    
    Logger::getInstance().info("Database initialized: %s", db_path.c_str());
    
    // 初始化表
    return initTables();
}

void DBManager::close() {
    std::lock_guard lock(db_mutex_);
    
    if (db_ != nullptr) {
        sqlite3_close(db_);
        db_ = nullptr;
        Logger::getInstance().info("Database closed");
    }
}

bool DBManager::initTables() {
    // 创建用户表
    const char* create_users_table = 
        "CREATE TABLE IF NOT EXISTS users ("
        "id INTEGER PRIMARY KEY AUTOINCREMENT,"
        "username TEXT UNIQUE NOT NULL,"
        "password TEXT NOT NULL,"
        "created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
        ");";
    
    // 创建用户数据表
    const char* create_user_data_table = 
        "CREATE TABLE IF NOT EXISTS user_data ("
        "user_id INTEGER NOT NULL,"
        "key TEXT NOT NULL,"
        "value TEXT NOT NULL,"
        "updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,"
        "PRIMARY KEY (user_id, key),"
        "FOREIGN KEY (user_id) REFERENCES users(id)"
        ");";
    
    // 创建游戏记录表
    const char* create_game_records_table = 
        "CREATE TABLE IF NOT EXISTS game_records ("
        "id INTEGER PRIMARY KEY AUTOINCREMENT,"
        "user_id INTEGER NOT NULL,"
        "win BOOLEAN NOT NULL,"
        "score INTEGER NOT NULL,"
        "played_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,"
        "FOREIGN KEY (user_id) REFERENCES users(id)"
        ");";
    
    if (!execSQL(create_users_table) ||
        !execSQL(create_user_data_table) ||
        !execSQL(create_game_records_table)) {
        return false;
    }
    
    return true;
}

bool DBManager::execSQL(const std::string& sql) {
    std::lock_guard lock(db_mutex_);
    
    if (!db_) {
        Logger::getInstance().error("Database not initialized");
        return false;
    }
    
    char* err_msg = nullptr;
    int rc = sqlite3_exec(db_, sql.c_str(), nullptr, nullptr, &err_msg);
    
    if (rc != SQLITE_OK) {
        Logger::getInstance().error("SQL error: %s", err_msg);
        sqlite3_free(err_msg);
        return false;
    }
    
    return true;
}

bool DBManager::execQuery(const std::string& sql, 
                         std::function row_callback) 
{
    std::lock_guard lock(db_mutex_);
    
    if (!db_) {
        Logger::getInstance().error("Database not initialized");
        return false;
    }
    
    sqlite3_stmt* stmt = nullptr;
    int rc = sqlite3_prepare_v2(db_, sql.c_str(), -1, &stmt, nullptr);
    
    if (rc != SQLITE_OK) {
        Logger::getInstance().error("Failed to prepare statement: %s", sqlite3_errmsg(db_));
        return false;
    }
    
    // 执行查询并处理每一行结果
    while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
        row_callback(stmt);
    }
    
    sqlite3_finalize(stmt);
    
    if (rc != SQLITE_DONE) {
        Logger::getInstance().error("Query execution failed: %s", sqlite3_errmsg(db_));
        return false;
    }
    
    return true;
}

bool DBManager::addUser(const std::string& username, const std::string& password) {
    std::stringstream ss;
    ss << "INSERT INTO users (username, password) VALUES ('"
       << username << "', '" << password << "');";
       
    return execSQL(ss.str());
}

bool DBManager::verifyUser(const std::string& username, const std::string& password) {
    bool valid = false;
    
    std::stringstream ss;
    ss << "SELECT id FROM users WHERE username = '"
       << username << "' AND password = '" << password << "';";
       
    execQuery(ss.str(), [&valid](sqlite3_stmt* stmt) {
        valid = true;
    });
    
    return valid;
}

uint32_t DBManager::getUserId(const std::string& username) {
    uint32_t user_id = 0;
    
    std::stringstream ss;
    ss << "SELECT id FROM users WHERE username = '" << username << "';";
    
    execQuery(ss.str(), [&user_id](sqlite3_stmt* stmt) {
        user_id = static_cast(sqlite3_column_int(stmt, 0));
    });
    
    return user_id;
}

std::string DBManager::getUserName(uint32_t user_id) {
    std::string username;
    
    std::stringstream ss;
    ss << "SELECT username FROM users WHERE id = " << user_id << ";";
    
    execQuery(ss.str(), [&username](sqlite3_stmt* stmt) {
        username = reinterpret_cast(sqlite3_column_text(stmt, 0));
    });
    
    return username;
}

bool DBManager::updateUserData(uint32_t user_id, const std::string& key, const std::string& value) {
    std::stringstream ss;
    ss << "INSERT OR REPLACE INTO user_data (user_id, key, value) VALUES ("
       << user_id << ", '" << key << "', '" << value << "');";
       
    return execSQL(ss.str());
}

std::optional DBManager::getUserData(uint32_t user_id, const std::string& key) {
    std::optional result;
    
    std::stringstream ss;
    ss << "SELECT value FROM user_data WHERE user_id = "
       << user_id << " AND key = '" << key << "';";
       
    execQuery(ss.str(), [&result](sqlite3_stmt* stmt) {
        result = reinterpret_cast(sqlite3_column_text(stmt, 0));
    });
    
    return result;
}

bool DBManager::recordGameResult(uint32_t user_id, bool win, int score) {
    std::stringstream ss;
    ss << "INSERT INTO game_records (user_id, win, score) VALUES ("
       << user_id << ", " << (win ? 1 : 0) << ", " << score << ");";
       
    return execSQL(ss.str());
}

int DBManager::getTotalGames(uint32_t user_id) {
    int total_games = 0;
    
    std::stringstream ss;
    ss << "SELECT COUNT(*) FROM game_records WHERE user_id = " << user_id << ";";
    
    execQuery(ss.str(), [&total_games](sqlite3_stmt* stmt) {
        total_games = sqlite3_column_int(stmt, 0);
    });
    
    return total_games;
}

int DBManager::getWinGames(uint32_t user_id) {
    int win_games = 0;
    
    std::stringstream ss;
    ss << "SELECT COUNT(*) FROM game_records WHERE user_id = " << user_id << " AND win = 1;";
    
    execQuery(ss.str(), [&win_games](sqlite3_stmt* stmt) {
        win_games = sqlite3_column_int(stmt, 0);
    });
    
    return win_games;
}

12. 网络管理器 (NetworkManager.h/cpp)

cpp

// NetworkManager.h
#pragma once

#include 
#include 
#include 
#include 
#include "network/EpollServer.h"
#include "game/Player.h"
#include "utils/Timer.h"

class NetworkManager {
public:
    using MessageHandler = std::function;
    
    static NetworkManager& getInstance();
    
    // 初始化
    bool initialize(int port);
    
    // 关闭
    void shutdown();
    
    // 注册消息处理器
    void registerMessageHandler(uint32_t message_type, MessageHandler handler);
    
    // 处理消息
    void handleMessage(Connection::ConnectionPtr conn, const game_protocol::GameMessage& message);
    
    // 心跳检测
    void startHeartbeatCheck(int interval_seconds = 5);
    
    // 创建玩家
    Player::PlayerPtr createPlayer(uint32_t player_id, const std::string& username, Connection::ConnectionPtr conn);
    
    // 获取玩家
    Player::PlayerPtr getPlayer(uint32_t player_id);
    
    // 删除玩家
    bool removePlayer(uint32_t player_id);
    
    // 获取活跃连接数
    size_t getActiveConnectionCount() const;
    
private:
    NetworkManager();
    ~NetworkManager();
    
    // 禁止拷贝
    NetworkManager(const NetworkManager&) = delete;
    NetworkManager& operator=(const NetworkManager&) = delete;
    
    // 连接回调
    void onConnection(Connection::ConnectionPtr conn);
    
    // 处理心跳消息
    void handleHeartbeat(Connection::ConnectionPtr conn, const game_protocol::GameMessage& message);
    
    // 检查心跳超时
    void checkHeartbeats();

private:
    std::shared_ptr server_;
    std::shared_ptr timer_;
    
    std::mutex handlers_mutex_;
    std::unordered_map message_handlers_;
    
    std::mutex players_mutex_;
    std::unordered_map players_;
};

cpp

// NetworkManager.cpp
#include "network/NetworkManager.h"
#include "utils/Logger.h"
#include "protocol/proto/message.pb.h"

NetworkManager& NetworkManager::getInstance() {
    static NetworkManager instance;
    return instance;
}

NetworkManager::NetworkManager() 
    : server_(std::make_shared()),
      timer_(std::make_shared())
{
    // 初始化定时器
    timer_->start();
    
    // 注册心跳处理器
    registerMessageHandler(game_protocol::HEARTBEAT, 
        [this](Connection::ConnectionPtr conn, const game_protocol::GameMessage& msg) {
            handleHeartbeat(conn, msg);
        });
}

NetworkManager::~NetworkManager() {
    shutdown();
}

bool NetworkManager::initialize(int port) {
    if (!server_) {
        return false;
    }
    
    // 设置连接回调
    server_->setConnectionCallback([this](Connection::ConnectionPtr conn) {
        onConnection(conn);
    });
    
    // 启动服务器
    if (!server_->start(port)) {
        return false;
    }
    
    Logger::getInstance().info("Network manager initialized on port %d", port);
    return true;
}

void NetworkManager::shutdown() {
    if (server_) {
        server_->stop();
        Logger::getInstance().info("Network manager shut down");
    }
    
    if (timer_) {
        timer_->stop();
    }
}

void NetworkManager::registerMessageHandler(uint32_t message_type, MessageHandler handler) {
    std::lock_guard lock(handlers_mutex_);
    message_handlers_[message_type] = std::move(handler);
}

void NetworkManager::handleMessage(Connection::ConnectionPtr conn, const game_protocol::GameMessage& message) {
    if (!conn || !message.has_header()) {
        return;
    }
    
    uint32_t message_type = message.header().message_type();
    
    // 查找消息处理器
    MessageHandler handler;
    
    {
        std::lock_guard lock(handlers_mutex_);
        auto it = message_handlers_.find(message_type);
        if (it != message_handlers_.end()) {
            handler = it->second;
        }
    }
    
    // 处理消息
    if (handler) {
        handler(conn, message);
    } else {
        Logger::getInstance().warning("No handler for message type %u", message_type);
    }
}

void NetworkManager::startHeartbeatCheck(int interval_seconds) {
    // 每隔指定时间检查一次心跳
    timer_->setInterval([this] {
        checkHeartbeats();
    }, interval_seconds * 1000);
    
    Logger::getInstance().info("Heartbeat check started with interval %d seconds", interval_seconds);
}

Player::PlayerPtr NetworkManager::createPlayer(uint32_t player_id, const std::string& username, Connection::ConnectionPtr conn) {
    std::lock_guard lock(players_mutex_);
    
    // 检查是否已存在
    auto it = players_.find(player_id);
    if (it != players_.end()) {
        Logger::getInstance().warning("Player %u already exists", player_id);
        return it->second;
    }
    
    // 创建新玩家
    auto player = std::make_shared(player_id, username);
    player->setConnection(conn);
    
    // 设置连接的玩家ID
    if (conn) {
        conn->setPlayerId(player_id);
    }
    
    players_[player_id] = player;
    
    Logger::getInstance().info("Created player: %s, id: %u", username.c_str(), player_id);
    return player;
}

Player::PlayerPtr NetworkManager::getPlayer(uint32_t player_id) {
    std::lock_guard lock(players_mutex_);
    
    auto it = players_.find(player_id);
    if (it != players_.end()) {
        return it->second;
    }
    
    return nullptr;
}

bool NetworkManager::removePlayer(uint32_t player_id) {
    std::lock_guard lock(players_mutex_);
    
    auto it = players_.find(player_id);
    if (it == players_.end()) {
        return false;
    }
    
    Logger::getInstance().info("Removed player: %s, id: %u", 
        it->second->getUsername().c_str(), player_id);
        
    players_.erase(it);
    return true;
}

size_t NetworkManager::getActiveConnectionCount() const {
    if (server_) {
        return server_->getConnectionCount();
    }
    return 0;
}

void NetworkManager::onConnection(Connection::ConnectionPtr conn) {
    if (!conn) return;
    
    // 设置消息回调
    conn->setMessageCallback([this](Connection::ConnectionPtr c, const game_protocol::GameMessage& msg) {
        handleMessage(c, msg);
    });
    
    Logger::getInstance().info("New connection from %s", conn->getAddress().c_str());
}

void NetworkManager::handleHeartbeat(Connection::ConnectionPtr conn, const game_protocol::GameMessage& message) {
    if (!conn || !message.has_heartbeat()) {
        return;
    }
    
    // 更新最后活跃时间
    conn->updateLastActiveTime();
    
    // 响应心跳
    game_protocol::GameMessage response;
    auto* header = response.mutable_header();
    header->set_message_type(static_cast(game_protocol::HEARTBEAT));
    header->set_sequence_id(message.header().sequence_id());
    
    auto* heartbeat = response.mutable_heartbeat();
    heartbeat->set_timestamp(std::chrono::duration_cast(
        std::chrono::system_clock::now().time_since_epoch()).count());
    
    conn->sendMessage(response);
}

void NetworkManager::checkHeartbeats() {
    if (server_) {
        server_->checkHeartbeats();
    }
}

13. 主程序 (main.cpp)

cpp

#include 
#include 
#include "utils/Logger.h"
#include "network/NetworkManager.h"
#include "db/DBManager.h"
#include "game/RoomManager.h"
#include "protocol/proto/message.pb.h"

// 信号处理
volatile sig_atomic_t g_running = 1;

void signalHandler(int sig) {
    g_running = 0;
}

// 消息处理器
void setupMessageHandlers(NetworkManager& net_mgr) {
    // 登录请求处理
    net_mgr.registerMessageHandler(game_protocol::LOGIN_REQUEST, 
        [](Connection::ConnectionPtr conn, const game_protocol::GameMessage& msg) {
            if (!msg.has_login_request()) return;
            
            const auto& login_req = msg.login_request();
            std::string username = login_req.username();
            std::string password = login_req.password();
            
            Logger::getInstance().info("Login request from %s, username: %s", 
                conn->getAddress().c_str(), username.c_str());
            
            // 验证用户名和密码
            auto& db = DBManager::getInstance();
            bool success = db.verifyUser(username, password);
            
            // 如果用户不存在,自动注册
            if (!success) {
                success = db.addUser(username, password);
            }
            
            // 构建响应
            game_protocol::GameMessage response;
            auto* header = response.mutable_header();
            header->set_message_type(static_cast(game_protocol::LOGIN_RESPONSE));
            header->set_sequence_id(msg.header().sequence_id());
            
            auto* login_resp = response.mutable_login_response();
            login_resp->set_success(success);
            
            if (success) {
                uint32_t player_id = db.getUserId(username);
                login_resp->set_player_id(player_id);
                
                // 创建玩家
                auto& net_mgr = NetworkManager::getInstance();
                net_mgr.createPlayer(player_id, username, conn);
            } else {
                login_resp->set_error_message("Login failed");
            }
            
            // 发送响应
            conn->sendMessage(response);
        });
    
    // 创建房间请求处理
    net_mgr.registerMessageHandler(game_protocol::CREATE_ROOM_REQUEST, 
        [](Connection::ConnectionPtr conn, const game_protocol::GameMessage& msg) {
            if (!msg.has_create_room_request()) return;
            
            const auto& req = msg.create_room_request();
            uint32_t player_id = req.player_id();
            std::string room_name = req.room_name();
            
            Logger::getInstance().info("Create room request from player %u, room name: %s", 
                player_id, room_name.c_str());
            
            // 获取玩家
            auto& net_mgr = NetworkManager::getInstance();
            auto player = net_mgr.getPlayer(player_id);
            
            // 构建响应
            game_protocol::GameMessage response;
            auto* header = response.mutable_header();
            header->set_message_type(static_cast(game_protocol::CREATE_ROOM_RESPONSE));
            header->set_sequence_id(msg.header().sequence_id());
            
            auto* resp = response.mutable_create_room_response();
            
            if (!player) {
                resp->set_success(false);
                resp->set_error_message("Player not found");
                conn->sendMessage(response);
                return;
            }
            
            // 创建房间
            auto& room_mgr = RoomManager::getInstance();
            auto room = room_mgr.createRoom(room_name);
            
            if (!room) {
                resp->set_success(false);
                resp->set_error_message("Failed to create room");
                conn->sendMessage(response);
                return;
            }
            
            // 将玩家加入房间
            bool joined = room->addPlayer(player);
            
            resp->set_success(joined);
            if (joined) {
                resp->set_room_id(room->getId());
            } else {
                resp->set_error_message("Failed to join room");
            }
            
            // 发送响应
            conn->sendMessage(response);
        });
    
    // 加入房间请求处理
    net_mgr.registerMessageHandler(game_protocol::JOIN_ROOM_REQUEST, 
        [](Connection::ConnectionPtr conn, const game_protocol::GameMessage& msg) {
            if (!msg.has_join_room_request()) return;
            
            const auto& req = msg.join_room_request();
            uint32_t player_id = req.player_id();
            uint32_t room_id = req.room_id();
            
            Logger::getInstance().info("Join room request from player %u, room id: %u", 
                player_id, room_id);
            
            // 获取玩家
            auto& net_mgr = NetworkManager::getInstance();
            auto player = net_mgr.getPlayer(player_id);
            
            // 构建响应
            game_protocol::GameMessage response;
            auto* header = response.mutable_header();
            header->set_message_type(static_cast(game_protocol::JOIN_ROOM_RESPONSE));
            header->set_sequence_id(msg.header().sequence_id());
            
            auto* resp = response.mutable_join_room_response();
            
            if (!player) {
                resp->set_success(false);
                resp->set_error_message("Player not found");
                conn->sendMessage(response);
                return;
            }
            
            // 获取房间
            auto& room_mgr = RoomManager::getInstance();
            auto room = room_mgr.getRoom(room_id);
            
            if (!room) {
                resp->set_success(false);
                resp->set_error_message("Room not found");
                conn->sendMessage(response);
                return;
            }
            
            // 将玩家加入房间
            bool joined = room->addPlayer(player);
            
            resp->set_success(joined);
            if (joined) {
                resp->set_room_id(room->getId());
                
                // 添加房间内玩家信息
                auto players = room->getAllPlayers();
                for (auto& p : players) {
                    auto* player_info = resp->add_players();
                    *player_info = p->toPlayerInfo();
                }
            } else {
                resp->set_error_message("Failed to join room");
            }
            
            // 发送响应
            conn->sendMessage(response);
        });
    
    // 玩家动作处理
    net_mgr.registerMessageHandler(game_protocol::PLAYER_ACTION, 
        [](Connection::ConnectionPtr conn, const game_protocol::GameMessage& msg) {
            if (!msg.has_player_action()) return;
            
            const auto& action = msg.player_action();
            uint32_t player_id = action.player_id();
            
            // 查找玩家所在的房间
            auto& room_mgr = RoomManager::getInstance();
            auto rooms = room_mgr.getAllRooms();
            
            for (auto& room : rooms) {
                auto player = room->getPlayer(player_id);
                if (player) {
                    // 玩家在此房间中,处理动作
                    room->handlePlayerAction(player_id, action);
                    break;
                }
            }
        });
}

int main(int argc, char* argv[]) {
    // 设置日志
    Logger::getInstance().setLogLevel(LogLevel::DEBUG);
    Logger::getInstance().setLogFile("gameserver.log");
    
    Logger::getInstance().info("Game server starting...");
    
    // 设置信号处理
    signal(SIGINT, signalHandler);
    signal(SIGTERM, signalHandler);
    
    try {
        // 初始化 protobuf
        GOOGLE_PROTOBUF_VERIFY_VERSION;
        
        // 初始化数据库
        auto& db = DBManager::getInstance();
        if (!db.initialize()) {
            Logger::getInstance().fatal("Failed to initialize database");
            return 1;
        }
        
        // 初始化网络
        int port = 8888;
        if (argc > 1) {
            port = std::stoi(argv[1]);
        }
        
        auto& net_mgr = NetworkManager::getInstance();
        if (!net_mgr.initialize(port)) {
            Logger::getInstance().fatal("Failed to initialize network manager");
            return 1;
        }
        
        // 设置消息处理器
        setupMessageHandlers(net_mgr);
        
        // 启动心跳检测
        net_mgr.startHeartbeatCheck(30); // 30秒
        
        // 主循环
        Logger::getInstance().info("Game server started, press Ctrl+C to stop");
        
        auto& room_mgr = RoomManager::getInstance();
        Timer maintenance_timer;
        maintenance_timer.start();
        
        // 每5分钟执行一次维护
        maintenance_timer.setInterval([&room_mgr]() {
            // 清理空房间
            room_mgr.cleanupEmptyRooms();
            
            // 可以添加更多维护任务
            // ...
            
            Logger::getInstance().info("Server maintenance done");
        }, 300000); // 5分钟
        
        while (g_running) {
            // 主线程可以处理一些低频率的任务
            std::this_thread::sleep_for(std::chrono::seconds(1));
            
            // 输出服务器状态
            size_t conn_count = net_mgr.getActiveConnectionCount();
            std::vector all_rooms = room_mgr.getAllRooms();
            
            Logger::getInstance().info("Server status: %zu connections, %zu rooms", 
                conn_count, all_rooms.size());
        }
        
        // 停止维护定时器
        maintenance_timer.stop();
        
        // 关闭网络
        Logger::getInstance().info("Shutting down network...");
        net_mgr.shutdown();
        
        // 关闭数据库
        Logger::getInstance().info("Closing database...");
        db.close();
        
        // 可选:释放 protobuf 全局资源
        google::protobuf::ShutdownProtobufLibrary();
        
        Logger::getInstance().info("Game server stopped gracefully");
    }
    catch (const std::exception& e) {
        Logger::getInstance().fatal("Exception: %s", e.what());
        return 1;
    }
    
    return 0;
}

14. 测试客户端模拟器 (client_simulator.cpp)

cpp

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "protocol/proto/message.pb.h"

// 全局变量
std::atomic g_running(true);
std::atomic g_clients_connected(0);
std::mutex g_mutex;

// 将消息序列化为二进制数据
std::vector serializeMessage(const game_protocol::GameMessage& message) {
    std::string data = message.SerializeAsString();
    
    // 4字节消息大小(网络字节序)+ 消息内容
    std::vector result(4 + data.size());
    
    uint32_t size = htonl(static_cast(data.size()));
    std::memcpy(result.data(), &size, 4);
    std::memcpy(result.data() + 4, data.data(), data.size());
    
    return result;
}

// 解析消息
std::optional parseMessage(const char* data, size_t size) {
    if (size < 4) return std::nullopt;
    
    uint32_t message_size;
    std::memcpy(&message_size, data, 4);
    message_size = ntohl(message_size);
    
    if (size < 4 + message_size) return std::nullopt;
    
    game_protocol::GameMessage message;
    if (!message.ParseFromArray(data + 4, message_size)) {
        return std::nullopt;
    }
    
    return message;
}

// 模拟客户端行为
void clientThread(const std::string& server_ip, int server_port, int client_id) {
    // 创建套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        std::cerr << "Client " << client_id << ": Failed to create socket" << std::endl;
        return;
    }
    
    // 设置非阻塞模式
    int flags = fcntl(sockfd, F_GETFL, 0);
    fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
    
    // 连接服务器
    struct sockaddr_in server_addr;
    std::memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(server_port);
    inet_pton(AF_INET, server_ip.c_str(), &server_addr.sin_addr);
    
    if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        if (errno != EINPROGRESS) {
            std::cerr << "Client " << client_id << ": Failed to connect" << std::endl;
            close(sockfd);
            return;
        }
    }
    
    // 等待连接就绪
    fd_set wfds;
    struct timeval tv;
    int res;
    
    FD_ZERO(&wfds);
    FD_SET(sockfd, &wfds);
    
    tv.tv_sec = 5;
    tv.tv_usec = 0;
    
    res = select(sockfd + 1, NULL, &wfds, NULL, &tv);
    if (res <= 0) {
        std::cerr << "Client " << client_id << ": Connect timeout or error" << std::endl;
        close(sockfd);
        return;
    }
    
    // 检查连接状态
    int error = 0;
    socklen_t len = sizeof(error);
    if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0 || error != 0) {
        std::cerr << "Client " << client_id << ": Connection failed" << std::endl;
        close(sockfd);
        return;
    }
    
    // 增加连接计数
    g_clients_connected++;
    
    {
        std::lock_guard lock(g_mutex);
        std::cout << "Client " << client_id << ": Connected to server" << std::endl;
    }
    
    // 构建登录请求
    game_protocol::GameMessage login_msg;
    auto* header = login_msg.mutable_header();
    header->set_message_type(static_cast(game_protocol::LOGIN_REQUEST));
    header->set_sequence_id(1);
    
    auto* login_req = login_msg.mutable_login_request();
    login_req->set_username("user" + std::to_string(client_id));
    login_req->set_password("pass" + std::to_string(client_id));
    
    // 发送登录请求
    auto login_data = serializeMessage(login_msg);
    send(sockfd, login_data.data(), login_data.size(), 0);
    
    // 接收和处理消息
    std::vector buffer(8192, 0);
    size_t buffer_offset = 0;
    
    uint32_t player_id = 0;
    uint32_t room_id = 0;
    
    std::chrono::steady_clock::time_point last_heartbeat_time = 
        std::chrono::steady_clock::now();
    
    // 客户端状态机
    enum ClientState {
        LOGGING_IN,
        CREATING_ROOM,
        JOINING_ROOM,
        IN_ROOM,
        PLAYING
    };
    
    ClientState state = LOGGING_IN;
    
    while (g_running) {
        // 接收数据
        ssize_t bytes_read = recv(sockfd, buffer.data() + buffer_offset, 
            buffer.size() - buffer_offset, 0);
            
        if (bytes_read > 0) {
            buffer_offset += bytes_read;
            
            // 处理接收到的数据
            size_t processed_size = 0;
            while (processed_size < buffer_offset) {
                std::optional message = 
                    parseMessage(buffer.data() + processed_size, 
                        buffer_offset - processed_size);
                        
                if (!message) {
                    break; // 数据不完整,等待更多数据
                }
                
                // 获取消息大小
                size_t message_size = ntohl(*reinterpret_cast(
                    buffer.data() + processed_size)) + 4;
                    
                processed_size += message_size;
                
                // 处理消息
                if (message->has_login_response()) {
                    const auto& response = message->login_response();
                    
                    {
                        std::lock_guard lock(g_mutex);
                        std::cout << "Client " << client_id << ": Login " 
                                << (response.success() ? "success" : "failed")
                                << std::endl;
                    }
                    
                    if (response.success()) {
                        player_id = response.player_id();
                        state = CREATING_ROOM;
                        
                        // 每个客户端的行为有所不同
                        if (client_id % 2 == 0) {
                            // 偶数客户端创建房间
                            game_protocol::GameMessage create_room_msg;
                            auto* header = create_room_msg.mutable_header();
                            header->set_message_type(
                                static_cast(game_protocol::CREATE_ROOM_REQUEST));
                            header->set_sequence_id(2);
                            
                            auto* create_req = create_room_msg.mutable_create_room_request();
                            create_req->set_player_id(player_id);
                            create_req->set_room_name("Room " + std::to_string(client_id));
                            
                            auto create_room_data = serializeMessage(create_room_msg);
                            send(sockfd, create_room_data.data(), create_room_data.size(), 0);
                        }
                    }
                }
                else if (message->has_create_room_response()) {
                    const auto& response = message->create_room_response();
                    
                    {
                        std::lock_guard lock(g_mutex);
                        std::cout << "Client " << client_id << ": Create room " 
                                << (response.success() ? "success" : "failed")
                                << std::endl;
                    }
                    
                    if (response.success()) {
                        room_id = response.room_id();
                        state = IN_ROOM;
                        
                        {
                            std::lock_guard lock(g_mutex);
                            std::cout << "Client " << client_id 
                                    << ": Created and joined room " << room_id
                                    << std::endl;
                        }
                    }
                }
                else if (message->has_join_room_response()) {
                    const auto& response = message->join_room_response();
                    
                    {
                        std::lock_guard lock(g_mutex);
                        std::cout << "Client " << client_id << ": Join room " 
                                << (response.success() ? "success" : "failed")
                                << std::endl;
                    }
                    
                    if (response.success()) {
                        room_id = response.room_id();
                        state = IN_ROOM;
                        
                        {
                            std::lock_guard lock(g_mutex);
                            std::cout << "Client " << client_id 
                                    << ": Joined room " << room_id
                                    << std::endl;
                        }
                    }
                }
                else if (message->has_room_status_update()) {
                    const auto& update = message->room_status_update();
                    
                    {
                        std::lock_guard lock(g_mutex);
                        std::cout << "Client " << client_id 
                                << ": Received room status update, room_id: " 
                                << update.room_id()
                                << ", status: " << update.room_status()
                                << ", player count: " << update.players_size()
                                << std::endl;
                    }
                    
                    if (state == IN_ROOM && update.room_status() == 1) { // PLAYING
                        state = PLAYING;
                        
                        // 发送玩家动作消息
                        game_protocol::GameMessage action_msg;
                        auto* header = action_msg.mutable_header();
                        header->set_message_type(
                            static_cast(game_protocol::PLAYER_ACTION));
                        header->set_sequence_id(4);
                        
                        auto* action = action_msg.mutable_player_action();
                        action->set_player_id(player_id);
                        action->set_action_type(0); // 移动
                        
                        auto* pos = action->mutable_new_position();
                        pos->set_x(static_cast(client_id) * 2.0f);
                        pos->set_y(1.0f);
                        pos->set_z(0.0f);
                        pos->set_rotation(45.0f);
                        
                        auto action_data = serializeMessage(action_msg);
                        send(sockfd, action_data.data(), action_data.size(), 0);
                    }
                }
                else if (message->has_heartbeat()) {
                    // 收到心跳响应
                    {
                        std::lock_guard lock(g_mutex);
                        std::cout << "Client " << client_id 
                                << ": Received heartbeat response"
                                << std::endl;
                    }
                }
            }
            
            // 移动未处理的数据到缓冲区起始位置
            if (processed_size > 0) {
                if (processed_size < buffer_offset) {
                    std::memmove(buffer.data(), buffer.data() + processed_size,
                        buffer_offset - processed_size);
                }
                buffer_offset -= processed_size;
            }
        }
        else if (bytes_read == 0) {
            // 连接已关闭
            {
                std::lock_guard lock(g_mutex);
                std::cout << "Client " << client_id << ": Connection closed by server" 
                        << std::endl;
            }
            break;
        }
        else {
            // 错误或EAGAIN
            if (errno != EAGAIN && errno != EWOULDBLOCK) {
                {
                    std::lock_guard lock(g_mutex);
                    std::cout << "Client " << client_id << ": Error in recv: " 
                            << strerror(errno) << std::endl;
                }
                break;
            }
        }
        
        // 状态机逻辑
        if (state == LOGGING_IN) {
            // 等待登录响应
        }
        else if (state == CREATING_ROOM) {
            // 等待创建房间响应
        }
        else if (state == JOINING_ROOM) {
            // 等待加入房间响应
        }
        else if (state == IN_ROOM || state == PLAYING) {
            // 如果是奇数客户端,且没有房间,则加入一个房间
            if (client_id % 2 == 1 && room_id == 0) {
                // 选择一个房间ID(偶数客户端ID创建的房间)
                uint32_t target_room_id = (client_id - 1);
                
                game_protocol::GameMessage join_room_msg;
                auto* header = join_room_msg.mutable_header();
                header->set_message_type(
                    static_cast(game_protocol::JOIN_ROOM_REQUEST));
                header->set_sequence_id(3);
                
                auto* join_req = join_room_msg.mutable_join_room_request();
                join_req->set_player_id(player_id);
                join_req->set_room_id(target_room_id);
                
                auto join_room_data = serializeMessage(join_room_msg);
                send(sockfd, join_room_data.data(), join_room_data.size(), 0);
                
                state = JOINING_ROOM;
            }
            
            // 如果在游戏中,定期发送动作消息
            if (state == PLAYING && client_id % 5 == 0) {
                // 每5秒发送一次动作
                static auto last_action_time = std::chrono::steady_clock::now();
                auto now = std::chrono::steady_clock::now();
                
                if (std::chrono::duration_cast(
                    now - last_action_time).count() >= 5) {
                    
                    // 发送玩家动作消息
                    game_protocol::GameMessage action_msg;
                    auto* header = action_msg.mutable_header();
                    header->set_message_type(
                        static_cast(game_protocol::PLAYER_ACTION));
                    header->set_sequence_id(5);
                    
                    auto* action = action_msg.mutable_player_action();
                    action->set_player_id(player_id);
                    action->set_action_type(0); // 移动
                    
                    // 随机位置
                    float x = static_cast(std::rand() % 100) / 10.0f;
                    float y = static_cast(std::rand() % 100) / 10.0f;
                    
                    auto* pos = action->mutable_new_position();
                    pos->set_x(x);
                    pos->set_y(y);
                    pos->set_z(0.0f);
                    pos->set_rotation(static_cast(std::rand() % 360));
                    
                    auto action_data = serializeMessage(action_msg);
                    send(sockfd, action_data.data(), action_data.size(), 0);
                    
                    last_action_time = now;
                    
                    {
                        std::lock_guard lock(g_mutex);
                        std::cout << "Client " << client_id 
                                << ": Sent movement action to position (" 
                                << x << ", " << y << ")"
                                << std::endl;
                    }
                }
            }
        }
        
        // 发送心跳
        auto now = std::chrono::steady_clock::now();
        if (std::chrono::duration_cast(
            now - last_heartbeat_time).count() >= 10) {
            
            game_protocol::GameMessage heartbeat_msg;
            auto* header = heartbeat_msg.mutable_header();
            header->set_message_type(static_cast(game_protocol::HEARTBEAT));
            header->set_sequence_id(0);
            
            auto* heartbeat = heartbeat_msg.mutable_heartbeat();
            heartbeat->set_timestamp(std::chrono::duration_cast(
                std::chrono::system_clock::now().time_since_epoch()).count());
                
            auto heartbeat_data = serializeMessage(heartbeat_msg);
            send(sockfd, heartbeat_data.data(), heartbeat_data.size(), 0);
            
            last_heartbeat_time = now;
            
            {
                std::lock_guard lock(g_mutex);
                std::cout << "Client " << client_id << ": Sent heartbeat" << std::endl;
            }
        }
        
        // 休眠一小段时间,降低CPU使用率
        std::this_thread::sleep_for(std::chrono::milliseconds(50));
    }
    
    // 减少连接计数
    g_clients_connected--;
    
    // 关闭套接字
    close(sockfd);
}

int main(int argc, char* argv[]) {
    // 检查参数
    if (argc < 4) {
        std::cerr << "Usage: " << argv[0] 
                 << "   " << std::endl;
        return 1;
    }
    
    std::string server_ip = argv[1];
    int server_port = std::stoi(argv[2]);
    int num_clients = std::stoi(argv[3]);
    
    // 初始化随机数生成器
    std::srand(static_cast(std::time(nullptr)));
    
    // 初始化 protobuf
    GOOGLE_PROTOBUF_VERIFY_VERSION;
    
    std::cout << "Starting " << num_clients << " client(s)..." << std::endl;
    
    // 创建客户端线程
    std::vector client_threads;
    for (int i = 1; i <= num_clients; ++i) {
        client_threads.emplace_back(clientThread, server_ip, server_port, i);
        // 稍微延迟,避免所有客户端同时连接
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    
    // 等待用户输入以停止测试
    std::cout << "Press Enter to stop the test..." << std::endl;
    std::cin.get();
    
    // 通知所有线程停止
    g_running = false;
    
    // 等待所有线程结束
    for (auto& thread : client_threads) {
        if (thread.joinable()) {
            thread.join();
        }
    }
    
    std::cout << "Test completed." << std::endl;
    
    // 清理 protobuf
    google::protobuf::ShutdownProtobufLibrary();
    
    return 0;
}

README.md

markdown

# 轻量级网络对战服务器

## 项目介绍

这是一个使用纯C++实现的轻量级网络对战服务器,专注于展示高并发处理、实时同步、多线程架构和网络协议设计等游戏后端核心技能。该服务器支持玩家登录、创建/加入房间、实时状态同步等功能。

### 核心特性

- 基于epoll的高性能网络模型,支持大量并发连接
- 多线程架构,分离网络I/O和游戏逻辑处理
- 使用Protocol Buffers进行高效的消息序列化/反序列化
- 线程安全队列实现生产者-消费者模型
- 房间管理系统,支持多房间并行游戏
- 实时状态同步与广播
- SQLite数据库存储玩家数据
- 对象池优化内存使用
- 心跳检测机制保证连接活跃性

## 技术架构

![架构图](architecture.png)

### 架构设计

本项目采用分层架构设计:

1. **网络层**:负责处理底层网络通信,基于epoll实现高并发连接处理
2. **协议层**:使用Protocol Buffers定义和处理消息格式
3. **逻辑层**:实现游戏核心逻辑,如房间管理、玩家状态同步等
4. **存储层**:使用SQLite存储持久化数据

### 关键组件

- **EpollServer**: 基于epoll的服务器实现,采用边缘触发(ET)模式
- **Connection**: 管理客户端连接和数据收发
- **MessageProcessor**: 处理消息的序列化和反序列化
- **RoomManager**: 管理游戏房间的创建、销毁和查找
- **Room**: 实现房间内玩家状态同步和游戏逻辑
- **Player**: 玩家数据和状态管理
- **NetworkManager**: 连接网络层和逻辑层的中间层
- **DBManager**: 数据库操作封装
- **ThreadSafeQueue**: 线程安全队列实现
- **ObjectPool**: 对象池优化内存分配
- **Timer**: 高精度定时器实现

## 编译和运行

### 系统要求

- Linux系统(推荐Ubuntu 18.04+)
- CMake 3.10+
- GCC 7.0+(支持C++17)
- Protocol Buffers 3.0+
- SQLite3

### 安装依赖

```bash
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install -y build-essential cmake libprotobuf-dev protobuf-compiler libsqlite3-dev

# CentOS/RHEL
sudo yum install -y epel-release
sudo yum install -y gcc-c++ cmake protobuf-devel protobuf-compiler sqlite-devel

编译步骤

bash

# 克隆仓库
git clone https://github.com/yourusername/gameserver.git
cd gameserver

# 创建构建目录
mkdir build && cd build

# 运行CMake
cmake ..

# 编译
make -j4

运行服务器

bash

# 启动服务器(默认端口8888)
./GameServer

# 指定端口启动
./GameServer 9999

运行测试客户端

bash

# 启动10个测试客户端
./ClientSimulator 127.0.0.1 8888 10

协议设计

本项目使用Protocol Buffers定义了一套清晰的客户端-服务器通信协议。主要消息类型包括:

  1. 登录相关:LoginRequest, LoginResponse
  2. 房间相关:CreateRoomRequest, CreateRoomResponse, JoinRoomRequest, JoinRoomResponse
  3. 游戏相关:RoomStatusUpdate, PlayerAction
  4. 系统相关:Heartbeat

详细协议定义见 src/protocol/proto/message.proto 文件。

性能指标

  • 单服务器实例可稳定支持500+并发连接
  • 消息处理延迟低于10ms
  • 内存占用稳定,无明显泄漏
  • CPU占用随连接数线性增长,具备良好的扩展性

项目亮点

  1. 高效的网络模型:采用epoll ET模式,实现高性能的网络I/O处理
  2. 优雅的线程模型:网络I/O线程与逻辑线程分离,避免阻塞
  3. 内存优化:使用对象池减少频繁内存分配
  4. 健壮的错误处理:全面的错误处理和日志系统
  5. 可扩展的架构:模块化设计,易于扩展新功能

未来改进

  • 添加更复杂的游戏逻辑演示
  • 实现更高级的负载均衡和服务器集群
  • 支持WebSocket协议,实现浏览器客户端
  • 加入更完善的安全措施(如防DDoS、加密通信等)
  • 优化数据库访问模式,添加连接池和缓存层

贡献

欢迎提交问题和改进建议!如果您希望贡献代码,请先fork本仓库,然后提交pull request。

许可证

本项目采用MIT许可证。详情请见LICENSE文件。

markdown


## 总结

这个轻量级网络对战服务器项目展示了以下关键技术点:

1. **基于epoll的网络模型**:利用Linux的epoll机制实现高效的I/O多路复用,采用边缘触发(ET)模式,提高并发处理能力。

2. **多线程架构**:将网络I/O线程与逻辑处理线程分离,避免I/O操作阻塞游戏逻辑。

3. **Protocol Buffers协议**:使用Protocol Buffers实现结构化、高效的消息序列化和反序列化。

4. **线程安全队列**:实现线程安全的队列,支持生产者-消费者模式,保证多线程环境下的数据一致性。

5. **房间和玩家管理**:实现房间创建、加入、状态同步等核心游戏逻辑。

6. **SQLite数据库集成**:支持玩家数据持久化存储。

7. **内存优化**:使用对象池减少频繁的内存分配和释放。

8. **定时器系统**:实现高精度定时器,用于心跳检测、定时游戏逻辑更新等。

此项目代码结构清晰,模块划分合理,充分展示了C++游戏后端的核心技能,适合作为面试项目展示。

本文地址:https://www.yitenyun.com/2950.html

搜索文章

Tags

#服务器 #python #pip #conda #人工智能 #微信 #ios面试 #ios弱网 #断点续传 #ios开发 #objective-c #ios #ios缓存 #远程工作 #Trae #IDE #AI 原生集成开发环境 #Trae AI 香港站群服务器 多IP服务器 香港站群 站群服务器 #kubernetes #笔记 #平面 #容器 #linux #学习方法 #运维 #飞牛nas #fnos #log4j #ollama #kylin #docker #arm #hadoop #hbase #hive #zookeeper #spark #kafka #flink #科技 #深度学习 #自然语言处理 #神经网络 #银河麒麟高级服务器操作系统安装 #银河麒麟高级服务器V11配置 #设置基础软件仓库时出错 #银河麒高级服务器系统的实操教程 #生产级部署银河麒麟服务系统教程 #Linux系统的快速上手教程 #学习 #低代码 #爬虫 #音视频 #AI编程 #ARM服务器 # GLM-4.6V # 多模态推理 #大数据 #职场和发展 #程序员创富 #语言模型 #大模型 #ai #ai大模型 #agent #PyTorch #模型训练 #星图GPU #分阶段策略 #模型协议 #华为云 #部署上线 #动静分离 #Nginx #新人首发 #ssh #ide #java #开发语言 #前端 #javascript #架构 #langchain #数据库 #fastapi #html #css #harmonyos #鸿蒙PC #飞书 #nginx #开源 #C++ #Reactor #经验分享 #安卓 #物联网 #websocket #windows #MobaXterm #ubuntu #自动化 #ansible #云计算 #驱动开发 #c++ #tcp/ip #网络 #qt #aws #unity #c# #游戏引擎 #私有化部署 #github #git #区块链 #测试用例 #生活 #word #umeditor粘贴word #ueditor粘贴word #ueditor复制word #ueditor上传word图片 #进程控制 #fabric #postgresql #android #腾讯云 #Conda # 私有索引 # 包管理 #pytorch #gemini #gemini国内访问 #gemini api #gemini中转搭建 #Cloudflare #node.js #大模型学习 #AI大模型 #大模型教程 #大模型入门 #jar #数信院生信服务器 #Rstudio #生信入门 #生信云服务器 #云原生 #iventoy #VmWare #OpenEuler #sql #AIGC #agi #算法 #牛客周赛 #dify #ci/cd #jenkins #gitlab #内网穿透 #cpolar #RTP over RTSP #RTP over TCP #RTSP服务器 #RTP #TCP发送RTP #openHiTLS #TLCP #DTLCP #密码学 #商用密码算法 #centos #svn #pycharm #Harbor #风控模型 #决策盲区 #矩阵 #线性代数 #AI运算 #向量 #flutter #mysql #缓存 #硬件工程 #Ansible # 自动化部署 # VibeThinker #后端 #vue上传解决方案 #vue断点续传 #vue分片上传下载 #vue分块上传下载 #http #项目 #高并发 #vscode #mobaxterm #计算机视觉 #diskinfo # TensorFlow # 磁盘健康 #儿童书籍 #儿童诗歌 #童话故事 #经典好书 #儿童文学 #好书推荐 #经典文学作品 #microsoft #mcp #mcp server #AI实战 #边缘计算 #重构 #机器学习 #阿里云 #文心一言 #AI智能体 #开源软件 #serverless #鸿蒙 #FTP服务器 #c语言 #spring cloud #spring #vue.js #json #分布式 #华为 #数学建模 #设备驱动 #芯片资料 #网卡 #性能优化 #2026年美赛C题代码 #2026年美赛 #php #FaceFusion # Token调度 # 显存优化 #java大文件上传 #java大文件秒传 #java大文件上传下载 #java文件传输解决方案 #超算服务器 #算力 #高性能计算 #仿真分析工作站 #springboot #java-ee #正则 #正则表达式 #iBMC #UltraISO #信息与通信 #prometheus #多个客户端访问 #IO多路复用 #回显服务器 #TCP相关API #mvp #个人开发 #设计模式 #ecmascript #elementui #jmeter #功能测试 #软件测试 #自动化测试 #stm32 #mcu #进程 #蓝桥杯 #企业开发 #ERP #项目实践 #.NET开发 #C#编程 #编程与数学 #内存治理 #django #游戏 #llama #opencv #时序数据库 #程序人生 #科研 #博士 #shell #CPU利用率 #产品经理 #ui #团队开发 #墨刀 #figma #搜索引擎 #导航网 #select #Dell #PowerEdge620 #内存 #硬盘 #RAID5 #数据结构 #嵌入式 #大语言模型 #长文本处理 #GLM-4 #Triton推理 #毕业设计 #系统架构 #rocketmq #Ubuntu服务器 #硬盘扩容 #命令行操作 #VMware #web #webdav #chatgpt #DeepSeek #AI #DS随心转 #mongodb #Windows 更新 #PyCharm # 远程调试 # YOLOFuse #MCP #MCP服务器 #网络协议 #flask #uni-app #小程序 #notepad++ #redis #FL Studio #FLStudio #FL Studio2025 #FL Studio2026 #FL Studio25 #FL Studio26 #水果软件 #spring boot #鸭科夫 #逃离鸭科夫 #鸭科夫联机 #鸭科夫异地联机 #开服 #web安全 #安全 #lvs #负载均衡 #jetty #scrapy #Linux #TCP #线程 #线程池 #RAGFlow #DeepSeek-R1 #powerpoint #Com #CFD #课程设计 #jvm #流程图 #论文阅读 #信息可视化 #SSH # ProxyJump # 跳板机 #哈希算法 #散列表 #es安装 #计算机网络 #线性回归 #transformer #AI写作 #Agent #程序员 #ssl #深度优先 #DFS #服务器繁忙 #ffmpeg #udp #企业微信 #酒店客房管理系统 #毕设 #论文 #学习笔记 #jdk #leetcode #Android #Bluedroid #wsl #L2C #勒让德到切比雪夫 #钉钉 #机器人 #LLM #vim #gcc #yum #3d #arm开发 #嵌入式硬件 #京东云 #堡垒机 #安恒明御堡垒机 #windterm #自动驾驶 #能源 #https #数据集 #vllm #Streamlit #Qwen #本地部署 #AI聊天机器人 #AI产品经理 #大模型开发 #mmap #nio #语音识别 #rabbitmq #protobuf #我的世界 #游戏私服 #云服务器 #PowerBI #企业 #golang #abtest #智能手机 #网络安全 #DisM++ # 系统维护 #全能视频处理软件 #视频裁剪工具 #视频合并工具 #视频压缩工具 #视频字幕提取 #视频处理工具 #YOLO #蓝耘智算 #电脑 #Canal #信号处理 #目标跟踪 #社科数据 #数据分析 #数据挖掘 #数据统计 #经管数据 #Tracker 服务器 #响应最快 #torrent 下载 #2026年 #Aria2 可用 #迅雷可用 #BT工具通用 #servlet #todesk #sqlserver #阻塞队列 #生产者消费者模型 #服务器崩坏原因 #SSM 框架 #孕期健康 #产品服务推荐 #推荐系统 #用户交互 #数据仓库 #vue3 #天地图 #403 Forbidden #天地图403错误 #服务器403问题 #天地图API #部署报错 #autosar #AI论文写作工具 #学术论文创作 #论文效率提升 #MBA论文写作 #everything #claude #cnn #操作系统 #dreamweaver #HCIA-Datacom #H12-811 #题库 #最新题库 #零售 #单片机 #svm #amdgpu #kfd #ROCm #数模美赛 #matlab #就业 #openclaw #面试 #whisper #守护进程 #复用 #screen #分类 #银河麒麟 #系统升级 #信创 #国产化 #流量运营 #用户运营 #ModelEngine #iphone #聚类 #树莓派4b安装系统 #金融 #金融投资Agent #gpu算力 #架构师 #软考 #系统架构师 #逻辑回归 #AI大模型应用开发 #ESXi #AB包 #贪心算法 #pdf #paddlepaddle #其他 #压枪 #debian #oracle #排序算法 #插入排序 #电气工程 #C# #PLC #Chat平台 #ARM架构 #pjsip #openresty #lua #考研 #软件工程 #SSH Agent Forwarding # PyTorch # 容器化 #claude code #codex #code cli #ccusage #OBC #Ascend #MindIE #银河麒麟操作系统 #openssh #华为交换机 #信创终端 #需求分析 #scala #测试工具 #压力测试 #twitter #求职招聘 #elasticsearch #版本控制 #Git入门 #开发工具 #代码托管 #ProCAST2025 #ProCast #脱模 #顶出 #应力计算 #铸造仿真 #变形计算 #ssm #laravel #adb #里氏替换原则 #幼儿园 #园长 #幼教 #n8n #sizeof和strlen区别 #sizeof #strlen #计算数据类型字节数 #计算字符串长度 #googlecloud #若依 #quartz #框架 #macos #智能路由器 #anaconda #虚拟环境 #fpga开发 #LVDS #高速ADC #DDR #prompt #react.js #Modbus-TCP #文生视频 #CogVideoX #AI部署 #azure #pandas #matplotlib #mamba #凤希AI伴侣 #ai编程 #环境搭建 #tomcat #firefox #编辑器 #我的世界服务器搭建 #minecraft #rust #双指针 #1024程序员节 #ida #TURN # WebRTC # HiChatBox #流量监控 #研发管理 #禅道 #禅道云端部署 #OCR #文字检测 #中间件 #ONLYOFFICE #MCP 服务器 #zabbix #MC #数组 #fastmcp #RAID #RAID技术 #磁盘 #存储 #长文本理解 #glm-4 #推理部署 #STUN # TURN # NAT穿透 #几何学 #拓扑学 #链表 #链表的销毁 #链表的排序 #链表倒置 #判断链表是否有环 #电商 #unity3d #服务器框架 #Fantasy #grafana #人脸识别 #人脸核身 #活体检测 #身份认证与人脸对比 #H5 #微信公众号 #eBPF #web3 #单元测试 #pytest #llm #测试流程 #金融项目实战 #P2P #串口服务器 #工业级串口服务器 #串口转以太网 #串口设备联网通讯模块 #串口服务器选型 #ISP Pipeline #行缓冲 #智慧校园解决方案 #智慧校园一体化平台 #智慧校园选型 #智慧校园采购 #智慧校园软件 #智慧校园专项资金 #智慧校园定制开发 #visual studio code #LangGraph #模型上下文协议 #MultiServerMCPC #load_mcp_tools #load_mcp_prompt #asp.net大文件上传 #asp.net大文件上传下载 #asp.net大文件上传源码 #ASP.NET断点续传 #asp.net上传文件夹 #支持向量机 #启发式算法 #微信小程序 #webrtc #ping通服务器 #读不了内网数据库 #bug菌问答团队 #生信 #结构体 #数码相机 #论文笔记 #游戏美术 #技术美术 #游戏策划 #游戏程序 #用户体验 #Coze工作流 #AI Agent指挥官 #多智能体系统 #journalctl #HBA卡 #RAID卡 #CISSP #CISSP考点 #信息安全 #CISSP哪里考 #公众号:厦门微思网络 #+微信号:xmweisi #selenium #RAG #全链路优化 #实战教程 #VS Code调试配置 #无人机 #Deepoc #具身模型 #开发板 #未来 #wordpress #雨云 #LobeChat #vLLM #GPU加速 #tdengine #制造 #涛思数据 #测试覆盖率 #可用性测试 #asp.net #智慧城市 #GB/T4857 #GB/T4857.17 #GB/T4857测试 #海外短剧 #海外短剧app开发 #海外短剧系统开发 #短剧APP #短剧APP开发 #短剧系统开发 #海外短剧项目 #Proxmox VE #虚拟化 #改行学it #Cpolar #国庆假期 #服务器告警 #GPU服务器 #8U #硬件架构 #SSH反向隧道 # Miniconda # Jupyter远程访问 #rtmp #homelab #Lattepanda #Jellyfin #Plex #Emby #Kodi #Node.js #漏洞检测 #CVE-2025-27210 #ROS #HeyGem # 局域网访问 # 批量处理 #.net #跨域 #发布上线后跨域报错 #请求接口跨域问题解决 #跨域请求代理配置 #request浏览器跨域 #gpu #nvcc #cuda #nvidia #TensorRT # Triton # 推理优化 #游戏机 #JumpServer #FRP #UDP的API使用 #建筑缺陷 #红外 #振镜 #振镜焊接 # 公钥认证 #漏洞 #epoll #高级IO # 服务器IP访问 # 端口映射 #UDP套接字编程 #UDP协议 #网络测试 #SRS #流媒体 #直播 #SSE #机器视觉 #6D位姿 #LoRA # RTX 3090 # lora-scripts #Keycloak #Quarkus #AI编程需求分析 #硬件 #蓝牙 #LE Audio #BAP #ddos #windbg分析蓝屏教程 #fiddler #推荐算法 #Modbus #IFix #目标检测 #YOLO26 #YOLO11 #计算机 #连锁药店 #连锁店 #screen 命令 #运维开发 #企业架构治理 #电力企业IT架构 #IT架构设计 #opc ua #opc #maven #intellij-idea #vp9 #Fluentd #Sonic #日志采集 #AutoDL #xlwings #Excel #restful #ajax #转行 #Claude #支付 #视频去字幕 #flume #指针 #外卖配送 #处理器模块 #现货库存 #价格优惠 #PM864AK01 #3BSE018161R1 #控制器模块 # GLM-TTS # 数据安全 #迁移重构 #数据安全 #代码迁移 #Karalon #AI Test #命令模式 #react native #dubbo #Gunicorn #WSGI #Flask #并发模型 #容器化 #Python #性能调优 #Ubuntu #Steam #饥荒联机版 #零代码平台 #AI开发 #图像处理 #yolo #ceph #esp32教程 #行为模式分析 #数据 #应用层 #跨领域 #敏感信息 #SA-PEKS # 关键词猜测攻击 # 盲签名 # 限速机制 #源代码管理 #模版 #函数 #类 #笔试 #visual studio #WEB #CMake #Make #C/C++ #SAP #ebs #metaerp #oracle ebs #高品质会员管理系统 #收银系统 #同城配送 #最好用的电商系统 #最好用的系统 #推荐的前十系统 #JAVA PHP 小程序 #LabVIEW知识 #LabVIEW程序 #labview #LabVIEW功能 #ipv6 #SSH跳转 #排序 # 高并发部署 #RustDesk # IndexTTS # GPU集群 #vps #框架搭建 #echarts #C语言 #vivado license ##程序员和算法的浪漫 #Playbook #AI服务器 #AI助手 #企业微信集成 #轻量大模型 #simulink #list #glibc #aiohttp #asyncio #异步 #Anaconda配置云虚拟环境 #智能体 #NAS #飞牛NAS #监控 #NVR #EasyNVR #项目申报系统 #项目申报管理 #项目申报 #企业项目申报 #wpf #JAVA #Java #apache #ue4 #ue5 #DedicatedServer #独立服务器 #专用服务器 #可信计算技术 #winscp #学术写作辅助 #论文创作效率提升 #AI写论文实测 #前端框架 # 双因素认证 #语义搜索 #嵌入模型 #Qwen3 #AI推理 #Shiro #反序列化漏洞 #CVE-2016-4437 #Miniconda #Docker #cursor #联机教程 #局域网联机 #局域网联机教程 #局域网游戏 #EMC存储 #存储维护 #NetApp存储 #spine #embedding #vuejs #进程创建与终止 #运营 #React安全 #漏洞分析 #Next.js #python学习路线 #python基础 #python进阶 #python标准库 #chrome #ICPC #ip #高仿永硕E盘的个人网盘系统源码 #汽车 #bootstrap #IndexTTS 2.0 #本地化部署 #文件IO #输入输出流 #typescript #npm #tcpdump #VPS #搭建 #IndexTTS2 # 阿里云安骑士 # 木马查杀 #土地承包延包 #领码SPARK #aPaaS+iPaaS #数字化转型 #智能审核 #档案数字化 #SSH别名 # CUDA #车辆排放 #微服务 #Moltbot #2026AI元年 #年度趋势 #国产PLM #瑞华丽PLM #瑞华丽 #PLM #paddleocr # 远程访问 # 服务器IP配置 #捷配 #pcb工艺 #Spring AI #STDIO协议 #Streamable-HTTP #McpTool注解 #服务器能力 #MS #Materials #X11转发 #可撤销IBE #服务器辅助 #私钥更新 #安全性证明 #双线性Diffie-Hellman #pencil #pencil.dev #设计 #Anything-LLM #IDC服务器 #工具集 #多线程 #性能调优策略 #双锁实现细节 #动态分配节点内存 #markdown #建站 #google #search #SMTP # 内容安全 # Qwen3Guard # AI翻译机 # 实时翻译 #sqlite #创业创新 #5G #平板 #交通物流 #智能硬件 #Triton #IO #插件 #p2p #策略模式 #r-tree #K8s #镜像 #集群自动化 #心理健康服务平台 #心理健康系统 #心理服务平台 #心理健康小程序 #database #idea # IndexTTS 2.0 # 远程运维 #海外服务器安装宝塔面板 #翻译 #开源工具 #VibeVoice # 语音合成 #910B #TFTP #客户端 #SSH保活 #远程开发 #rdp #交互 #tensorflow #dba #openlayers #bmap #tile #server #vue #dynadot #域名 #工厂模式 #银河麒麟部署 #银河麒麟部署文档 #银河麒麟linux #银河麒麟linux部署教程 #简单数论 #埃氏筛法 #openEuler #Hadoop #DIY机器人工房 #Moltbook #Clawdbot #log #NPU #CANN # GLM-4.6V-Flash-WEB # 显卡驱动备份 #cascadeur #设计师 #yolov12 #研究生life #SEO优化 #nacos #银河麒麟aarch64 #浏览器自动化 #python #uvicorn #uvloop #asgi #event #PyTorch 特性 #动态计算图 #张量(Tensor) #自动求导Autograd #GPU 加速 #生态系统与社区支持 #与其他框架的对比 #信令服务器 #Janus #MediaSoup #大剑师 #nodejs面试题 #SSH免密登录 #集成测试 #静脉曲张 #腿部健康 #Jetty # CosyVoice3 # 嵌入式服务器 #智能一卡通 #门禁一卡通 #梯控一卡通 #电梯一卡通 #消费一卡通 #一卡通 #考勤一卡通 #远程访问 #远程办公 #飞网 #安全高效 #配置简单 #远程桌面 #远程控制 #bash #逆向工程 #ngrok #远程连接 #RK3576 #瑞芯微 #硬件设计 #chat #iot #智能家居 #空间计算 #原型模式 #Spring #Spring Boot #RPA #影刀RPA #AI办公 # 目标检测 #galeweather.cn #高精度天气预报数据 #光伏功率预测 #风电功率预测 #高精度气象 #clickhouse #代理 #xeon #贴图 #材质 #爱心代码 #表白代码 #爱心 #tkinter #情人节表白代码 #北京百思可瑞教育 #百思可瑞教育 #北京百思教育 #自动化运维 #mybatis #测评 #ms-swift # 一锤定音 # 大模型微调 #deepseek #jupyter #WinSCP 下载安装教程 #SFTP #FTP工具 #服务器文件传输 #JT/T808 #车联网 #车载终端 #模拟器 #仿真器 #开发测试 #risc-v #个人博客 #mapreduce #媒体 #密码 #cpp #SSH公钥认证 # 安全加固 #nas #音乐分类 #音频分析 #ViT模型 #Gradio应用 #鼠大侠网络验证系统源码 #Fun-ASR # 语音识别 # WebUI #AI赋能盾构隧道巡检 #开启基建安全新篇章 #以注意力为核心 #YOLOv12 #AI隧道盾构场景 #盾构管壁缺陷病害异常检测预警 #隧道病害缺陷检测 #powerbi #状态模式 #嵌入式编译 #ccache #distcc #Nacos #三种参数 #参数的校验 #fastAPI #Deepseek #gpt-3 #puppeteer #鸿蒙系统 #系统安全 #车载系统 #安全架构 #Qwen3-14B # 大模型部署 # 私有化AI #taro #东方仙盟 #仙盟创梦IDE #rustdesk #dlms #dlms协议 #逻辑设备 #逻辑设置间权限 # REST API #七年级上册数学 #有理数 #有理数的加法法则 #绝对值 # keep-alive #安全威胁分析 #源码 #闲置物品交易系统 #GLM-4.6V-Flash-WEB # AI视觉 # 本地部署 #地理 #遥感 #IPv6 #DNS #动态规划 #面向对象 #攻防演练 #Java web #红队 #自由表达演说平台 #演说 #黑群晖 #虚拟机 #无U盘 #纯小白 #clamav #GB28181 #SIP信令 #SpringBoot #视频监控 #SSH跳板机 # Python3.11 #WT-2026-0001 #QVD-2026-4572 #smartermail #Minecraft #Minecraft服务器 #PaperMC #我的世界服务器 #html5 #前端开发 #API限流 # 频率限制 # 令牌桶算法 #EN4FE #TTS私有化 # 音色克隆 #榛樿鍒嗙被 #YOLOv8 # Docker镜像 #文件管理 #文件服务器 #国产开源制品管理工具 #Hadess #一文上手 #kong #Kong Audio #Kong Audio3 #KongAudio3 #空音3 #空音 #中国民乐 #范式 #蓝湖 #Axure原型发布 #实在Agent #视频 #UDP #CNAS #CMA #程序文件 #CPU #监测 #ET模式 #非阻塞 #高并发服务器 #ambari #门禁 #梯控 #智能梯控 #turn #黑客技术 #渗透测试 #网安应急响应 #OPCUA #微PE # GLM # 服务连通性 #数据恢复 #视频恢复 #视频修复 #RAID5恢复 #流媒体服务器恢复 #OSS #图像识别 #muduo库 #uv #uvx #uv pip #npx #Ruff #工程实践 # 高并发 #昇腾 #gpt #API #青少年编程 #wps #Tokio #KMS激活 # 硬件配置 #思维模型 #认知框架 #认知 #算力一体机 #ai算力服务器 #Rust #软件 #本地生活 #电商系统 #商城 #CVE-2025-68143 #CVE-2025-68144 #CVE-2025-68145 #网络编程 #Socket #套接字 #I/O多路复用 #字节序 #vrrp #脑裂 #keepalived主备 #高可用主备都持有VIP #coffeescript #weston #x11 #x11显示服务器 #SMP(软件制作平台) #EOM(企业经营模型) #应用系统 #RSO #机器人操作系统 #CSDN #寄存器 #mtgsig #美团医药 #美团医药mtgsig #美团医药mtgsig1.2 #MQTT协议 #知识库 #集成学习 #tornado #webpack #H3C #证书 #JNI #blender #warp #reactjs # 数字人系统 # 远程部署 #学工管理系统 #学工一体化平台 #学工软件二次开发 #学工平台定制开发 #学工系统服务商 #学工系统源头厂家 #智慧校园学工系统 #材料工程 #智能电视 #mariadb #vmware #net core #kestrel #web-server #asp.net-core #连接数据库报错 #FASTMCP #因果学习 #tcp/ip #网络 #sentinel #Go并发 #高并发架构 #Goroutine #系统设计 #Dify #鲲鹏 #职场发展 #TRO #TRO侵权 #TRO和解 #运维工具 #YOLOFuse # Base64编码 # 多模态检测 #三维重建 #高斯溅射 #Tetrazine-Acid #1380500-92-4 #postman #Discord机器人 #云部署 #程序那些事 #UEFI #BIOS #Legacy BIOS #产品运营 #1panel #移动端h5网页 #调用浏览器摄像头并拍照 #开启摄像头权限 #拍照后查看与上传服务器端 #摄像头黑屏打不开问题 #SPA #单页应用 #web3.py #说话人验证 #声纹识别 #CAM++ #云开发 #ipmitool #BMC #KMS 激活 # 黑屏模式 # TTS服务器 #AI智能棋盘 #Rock Pi S #C #eclipse #领域驱动 #MC群组服务器 #阳台种菜 #园艺手扎 #Gemini #Nano Banana Pro #汇编 #CS2 #debian13 #BoringSSL #麒麟OS #claude-code #软件开发 #swagger #PTP_1588 #gPTP #农产品物流管理 #物流管理系统 #农产品物流系统 #农产品物流 #xss #入侵 #日志排查 #unix #k8s #Windows #信创国产化 #达梦数据库 # 大模型 # 模型训练 #未加引号服务路径 #gitea #excel # ARM服务器 # 鲲鹏 #VSCode # SSH #CLI #JavaScript #langgraph.json #网站 #截图工具 #批量处理图片 #图片格式转换 #图片裁剪 #卷积神经网络 #dash #进程等待 #wait #waitpid #树莓派 #温湿度监控 #WhatsApp通知 #IoT #MySQL # 离线AI #创业管理 #财务管理 #团队协作 #创始人必修课 #数字化决策 #经营管理 #TCP服务器 #开发实战 #文件上传漏洞 #bigtop #hdp #hue #kerberos #区间dp #二进制枚举 #图论 #Kylin-Server #国产操作系统 #服务器安装 #Android16 #音频性能实战 #音频进阶 #结构与算法 #跳槽 #业界资讯 #raid #raid阵列 #TLS协议 #HTTPS #漏洞修复 #运维安全 #DDD #tdd #easyui #大学生 #大作业 #欧拉 #扩展屏应用开发 #android runtime #CTF #域名注册 #新媒体运营 #网站建设 #国外域名 # 水冷服务器 # 风冷服务器 #聊天小程序 #VoxCPM-1.5-TTS # 云端GPU # PyCharm宕机 #儿童AI #图像生成 # GPU服务器 # tmux #AI生成 # outputs目录 # 自动化 #服务器解析漏洞 #nodejs #NFC #智能公交 #服务器计费 #FP-增长 #outlook #错误代码2603 #无网络连接 #2603 #性能测试 #LoadRunner #esb接口 #走处理类报异常 #esp32 arduino #lstm #N8N #HistoryServer #Spark #YARN #jobhistory #sglang #ZooKeeper #ZooKeeper面试题 #面试宝典 #深入解析 #大模型部署 #mindie #大模型推理 #ComfyUI # 推理服务器 #具身智能 #libosinfo #kmeans #练习 #基础练习 #循环 #九九乘法表 #计算机实现 #数字孪生 #三维可视化 # 远程开发 # Qwen3Guard-Gen-8B #elk #模拟退火算法 #intellij idea #WinDbg #Windows调试 #内存转储分析 #随机森林 #内存接口 # 澜起科技 # 服务器主板 #smtp #smtp服务器 #PHP #AI视频创作系统 #AI视频创作 #AI创作系统 #AI视频生成 #AI工具 #AI创作工具 #文件传输 #电脑文件传输 #电脑传输文件 #电脑怎么传输文件到另一台电脑 #电脑传输文件到另一台电脑 #fs7TF #性能 #优化 #RAM #华为od #华为od机试 #华为od机考 #华为od最新上机考试题库 #华为OD题库 #华为OD机试双机位C卷 #od机考题库 #AI+ #coze #AI入门 #AI赋能 #wireshark #计组 #数电 #x86_64 #数字人系统 #cosmic #windows11 #系统修复 #Python3.11 #知识图谱 #npu #rtsp #转发 #React #Next #CVE-2025-55182 #RSC #处理器 #clawdbot #上下文工程 #langgraph #意图识别 #ansys #ansys问题解决办法 #spring native #单例模式 #快递盒检测检测系统 #RXT4090显卡 #RTX4090 #深度学习服务器 #硬件选型 #远程软件 #群晖 #音乐 #IntelliJ IDEA #neo4j #NoSQL #SQL #Llama-Factory # 大模型推理 # 代理转发 #idm #数据采集 #浏览器指纹 #视觉检测 # 服务器IP # 端口7860 #分布式数据库 #集中式数据库 #业务需求 #选型误 #gitee #ESP32 #传感器 #MicroPython #万悟 #联通元景 # Connection refused #WRF #WRFDA #新浪微博 #百度 #css3 #SMARC #ARM #teamviewer #异步编程 #系统编程 #Pin #http服务器 #AutoDL使用教程 #AI大模型训练 #linux常用命令 #PaddleOCR训练 #ThingsBoard MCP #edge #迭代器模式 #观察者模式 #机器人学习 #CosyVoice3 # IP配置 # 0.0.0.0 #LangFlow # 智能运维 # 性能瓶颈分析 #Apple AI #Apple 人工智能 #FoundationModel #Summarize #SwiftUI # 云服务器 #网络配置实战 #Web/FTP 服务访问 #计算机网络实验 #外网访问内网服务器 #Cisco 路由器配置 #静态端口映射 #网络运维 #防火墙 #Socket网络编程 #健身房预约系统 #健身房管理系统 #健身管理系统 #gRPC #注册中心 #bug #I/O模型 #并发 #水平触发、边缘触发 #多路复用 #gateway #Comate #遛狗 #milvus #C++ UA Server #SDK #跨平台开发 #web server #请求处理流程 #arm64 #勒索病毒 #勒索软件 #加密算法 #.bixi勒索病毒 #数据加密 #CA证书 #SSH复用 # 轻量化镜像 # 边缘计算 #GATT服务器 #蓝牙低功耗 #UOS #海光K100 #统信 #Host #SSRF #知识 #注入漏洞 # 批量部署 #copilot #agentic bi #MOXA #论文复现 #opc模拟服务器 # ms-swift #safari #CUDA #osg #服务器线程 # SSL通信 # 动态结构体 #娱乐 #敏捷流程 #计算机毕业设计 #程序定制 #毕设代做 #课设 #政务 #语音生成 #TTS #华为od机考真题 #华为od机试真题 #华为OD上机考试真题 #华为OD上机考试双机位C卷 #华为ODFLASH坏块监测系统 #go #个人助理 #数字员工 #le audio #低功耗音频 #通信 #连接 #部署 #cocos2d #图形渲染 #昇腾300I DUO #AI技术 #学术生涯规划 #CCF目录 #基金申请 #职称评定 #论文发表 #科研评价 #顶会顶刊 #IT #技术 #vnstat #宝塔面板部署RustDesk #RustDesk远程控制手机 #手机远程控制 #c++20 # 远程连接 #可再生能源 #绿色算力 #风电 #ARM64 # DDColor # ComfyUI #节日 #ESP32编译服务器 #Ping #DNS域名解析 #Kuikly #openharmony #moltbot #KMS #slmgr #AI应用编程 #gerrit #游戏服务器断线 #运动 #期刊 #SCI #基础语法 #标识符 #常量与变量 #数据类型 #运算符与表达式 #reactor反应堆 #POC #问答 #交付 #nfs #iscsi #AI Agent #开发者工具 #Linly-Talker # 数字人 # 服务器稳定性 #百度文库 #爱企查 #旋转验证码 #验证码识别 #主板 #总体设计 #电源树 #框图 #Archcraft #边缘AI # Kontron # SMARC-sAMX8 #智能体来了 #传统行业 #screen命令 #系统管理 #服务 #语义检索 #向量嵌入 #scanf #printf #getchar #putchar #cin #cout #人脸活体检测 #live-pusher #动作引导 #张嘴眨眼摇头 #苹果ios安卓完美兼容 #gnu #remote-ssh #小艺 #搜索 #glances #超时设置 #客户端/服务器 #挖矿 #Linux病毒 #健康医疗 #管道Pipe #system V #duckdb #强化学习 #策略梯度 #REINFORCE #蒙特卡洛 #AI应用 #ueditor导入word #高考 #分库分表 #垂直分库 #水平分表 #雪花算法 #分布式ID #跨库查询 #多模态 #微调 #超参 #LLamafactory #阿里云RDS #Linux多线程 #Java程序员 #Java面试 #后端开发 #Spring源码 #服务器开启 TLS v1.2 #IISCrypto 使用教程 #TLS 协议配置 #IIS 安全设置 #服务器运维工具 #cesium #可视化 #麒麟 #V11 #kylinos #AI-native #国产化OS #华为机试 #计算几何 #斜率 #方向归一化 #叉积 #软件需求 #samba # 批量管理 #ASR #SenseVoice #.netcore # 模型微调 #VMware创建虚拟机 #挖漏洞 #攻击溯源 #编程 #pxe #CCE #Dify-LLM #Flexus #Aluminium #Google #实体经济 #商业模式 #数智红包 #商业变革 #创业干货 #MCP服务器注解 #异步支持 #方法筛选 #声明式编程 #自动筛选机制 #MinIO #Zabbix #语音合成 #网络攻击模型 #cocoa #r语言 #Puppet # IndexTTS2 # TTS #数据结构与算法 #传媒 #隐函数 #常微分方程 #偏微分方程 #线性微分方程 #线性方程组 #非线性方程组 #复变函数 #pyqt #UDP服务器 #recvfrom函数 #身体实验室 #健康认知重构 #系统思维 #微行动 #NEAT效应 #亚健康自救 #ICT人 #服务器IO模型 #非阻塞轮询模型 #多任务并发模型 #异步信号模型 #多路复用模型 #STDIO传输 #SSE传输 #WebMVC #WebFlux #Ward #云计算运维 #asp.net上传大文件 #高精度农业气象 #递归 #线性dp #webgl #c++高并发 #百万并发 #Termux #Samba #VMware Workstation16 #服务器操作系统 #音诺ai翻译机 #AI翻译机 # Ampere Altra Max #ICE #文本生成 #CPU推理 #代理模式 #Spring AOP #4U8卡 AI 服务器 ##AI 服务器选型指南 #GPU 互联 #GPU算力 #ShaderGraph #图形 #http头信息 #uip #日志模块 #银河麒麟服务器系统 #多进程 #python技巧 #企业级存储 #网络设备 #GPU ##租显卡 #Smokeping #pve #租显卡 #训练推理 #全文检索 #xml #zotero #WebDAV #同步失败 #轻量化 #低配服务器 #短剧 #短剧小程序 #短剧系统 #微剧 #hibernate #nosql #大模型应用 #API调用 #PyInstaller打包运行 #服务端部署 #VMWare Tool #H5网页 #网页白屏 #H5页面空白 #资源加载问题 #打包部署后网页打不开 #HBuilderX #投标 #标书制作 #A2A #GenAI #Langchain-Chatchat # 国产化服务器 # 信创 #mvc #Syslog #系统日志 #日志分析 #日志监控 #网络安全大赛 #idc #生产服务器问题查询 #日志过滤 #Autodl私有云 #深度服务器配置 # 自动化运维 #esp32 #mosquito #题解 #图 #dijkstra #迪杰斯特拉 #程序开发 #程序设计 #智能制造 #供应链管理 #工业工程 #库存管理 #实时检测 #stl #IIS Crypto #量子计算 #DAG #云服务器选购 #Saas #智能体从0到1 #新手入门 #NSP #下一状态预测 #aigc #效率神器 #办公技巧 #自动化工具 #Windows技巧 #打工人必备 #HarmonyOS APP #旅游 #n8n解惑 #RK3588 #RK3588J #评估板 #核心板 #嵌入式开发 #SQL调优 #EXPLAIN #慢查询日志 #分布式架构 #声源定位 #MUSIC #晶振 #AI电商客服 #西门子 #汇川 #Blazor #经济学 #数据可视化 #网路编程 #zygote #应用进程 #SSH代理转发 #OpenManage #eureka #hdfs #广播 #组播 #并发服务器 # 服务器迁移 # 回滚方案 #resnet50 #分类识别训练 #运维 #企业存储 #RustFS #对象存储 #高可用 # 树莓派 # ARM架构 #三维 #3D #Xshell #Finalshell #生物信息学 #组学 #AI 推理 #NV #Spire.Office #隐私合规 #网络安全保险 #法律风险 #风险管理 #memcache #ServBay #C2000 #TI #实时控制MCU #AI服务器电源 # OTA升级 # 黄山派 #内网 #模块 # 网络延迟 #CVE-2025-61686 #路径遍历高危漏洞 #ranger #MySQL8.0 #统信UOS #win10 #qemu #QQbot #QQ #Coturn #代理服务器 #vertx #vert.x #vertx4 #runOnContext #智能体对传统行业冲击 #行业转型 #公共MQTT服务器 #HarmonyOS #社交智慧 #职场生存 #身体管理 #商务宴请 #拒绝油腻 #清醒日常 #win11 # GPU租赁 # 自建服务器 # DIY主机 # 交叉编译 #devops #工作 #0day漏洞 #DDoS攻击 #漏洞排查 #web服务器 #懒汉式 #恶汉式 #odoo #服务器架构 #AI推理芯片 #appche #视觉理解 #Moondream2 #多模态AI #c #MinIO服务器启动与配置详解 #muduo #TcpServer #accept #路由器 #数据访问 #Redis #分布式锁 # 服务器配置 # GPU #DHCP #CS336 #Assignment #Experiments #TinyStories #Ablation #实时音视频 #ftp #sftp #磁盘配额 #存储管理 #形考作业 #国家开放大学 #系统运维 #OpenHarmony #工程设计 #预混 #扩散 #燃烧知识 #层流 #湍流 #科普 #mssql #余行补位 #意义对谈 #余行论 #领导者定义计划 #硬盘克隆 #DiskGenius #PN 结 #ARMv8 #内存模型 #内存屏障 #ArkUI #ArkTS #鸿蒙开发 #b树 #超算中心 #PBS #lsf #报表制作 #职场 #用数据讲故事 #AE #手机h5网页浏览器 #安卓app #苹果ios APP #手机电脑开启摄像头并排查 # ControlMaster #AITechLab #cpp-python #CUDA版本 #参数估计 #矩估计 #概率论 #memory mcp #Cursor #docker-compose #系统安装 #铁路桥梁 #DIC技术 #箱梁试验 #裂纹监测 #四点弯曲 #麦克风权限 #访问麦克风并录制音频 #麦克风录制音频后在线播放 #用户拒绝访问麦克风权限怎么办 #uniapp 安卓 苹果ios #将音频保存本地或上传服务器 #Buck #NVIDIA #交错并联 #DGX #gmssh #宝塔 #漏洞挖掘 #Exchange #scikit-learn # 环境迁移 #编程助手 #starrocks #电子电气架构 #系统工程与系统架构的内涵 #Routine #格式工厂 #L6 #L10 #L9 #Gateway #认证服务器集成详解 #考试系统 #在线考试 #培训考试 #考试练习 #uniapp #合法域名校验出错 #服务器域名配置不生效 #request域名配置 #已经配置好了但还是报错 #uniapp微信小程序 #LED #设备树 #GPIO #信息收集 #composer #symfony #java-zookeeper #poll #后端框架 #C₃₂H₄₅N₇O₁₁S₂ #个性化推荐 #BERT模型 #m3u8 #HLS #移动端H5网页 #APP安卓苹果ios #监控画面 直播视频流 #AI运维 #DevOps自动化 #Prometheus #二值化 #Canny边缘检测 #轮廓检测 #透视变换 #DooTask #防毒面罩 #防尘面罩 #free #vmstat #sar #Qwen3-VL # 服务状态监控 # 视觉语言模型 #交换机 #三层交换机 #个人电脑 #思爱普 #SAP S/4HANA #ABAP #NetWeaver #一人公司 #独立开发者 #sklearn #人大金仓 #Kingbase # 权限修复 #WAN2.2 #ueditor导入pdf #EventLoop #统信操作系统 #人形机器人 #人机交互 #numpy #电梯 #电梯运力 #电梯门禁 #docker安装seata #vncdotool #链接VNC服务器 #如何隐藏光标 #FHSS #bond #服务器链路聚合 #网卡绑定 #数据报系统 #bytebase #算力建设 #人脸识别sdk #视频编解码 #决策树 #SSH密钥 #ETL管道 #向量存储 #数据预处理 #DocumentReader #提词器 #开关电源 #热敏电阻 #PTC热敏电阻 #夏天云 #夏天云数据 #江协 #瑞萨 #OLED屏幕移植 #企业微信机器人 #本地大模型 #AI工具集成 #容器化部署 #2025年 #SQL注入主机 #Matrox MIL #二次开发 #CMC #AI教程 #雨云服务器 #教程 #MCSM面板 #戴尔服务器 #戴尔730 #装系统 #claudeCode #content7 #junit #sql注入 # 串口服务器 # NPort5630 #istio #服务发现 #SEW #赛威 #SEW变频器 #lucene #cpu #星际航行 # 键鼠锁定 #RWK35xx #语音流 #实时传输 #ossinsight #node #反向代理 #rag #adobe #nmodbus4类库使用教程 #canvas层级太高 #canvas遮挡问题 #盖住其他元素 #苹果ios手机 #安卓手机 #调整画布层级 #数据迁移 #测速 #iperf #iperf3 #express #cherry studio # child_process #分子动力学 #化工仿真 #小智 #session #JADX-AI 插件 #xshell #host key #api #key #AI作画 #okhttp #计算机外设 #boltbot #Taiji #STL #string #OpenAI #故障 #Beidou #北斗 #SSR #远程更新 #缓存更新 #多指令适配 #物料关联计划 # AI部署 #pipeline #Transformers #NLP #DuckDB #协议 #Arduino BLDC #核辐射区域探测机器人 #spring ai #oauth2 # 高温监控 #rsync # 数据同步 #一周会议与活动 #ICLR #CCF #自动化巡检 #Python办公自动化 #Python办公 #基金 #股票 #YOLO识别 #YOLO环境搭建Windows #YOLO环境搭建Ubuntu #jquery #fork函数 #进程创建 #进程终止 #tekton #图像分类 #图像分割 #yolo26算法 #IPMI