一、项目整体结构
1.1 文件构成
- Common.hpp // 通用定义和基类
- InetAddr.hpp // 地址处理类
- Mutex.hpp // 互斥锁模块
- Log.hpp // 日志系统
- Socket.hpp // Socket抽象和实现
1.2 命名空间组织
namespace MutexModule // 互斥锁相关
namespace LogModule // 日志系统
namespace SocketModule // Socket封装
二、Common.hpp - 基础定义
2.1 错误码枚举
enum ExitCode {
OK = 0, // 正常
USAGE_ERR, // 使用错误
SOCKET_ERR, // socket创建失败
BIND_ERR, // bind失败
LISTEN_ERR, // listen失败
CONNECT_ERR, // connect失败
FORK_ERR // fork失败
};
2.2 禁止拷贝基类
class NoCopy {
public:
NoCopy() {}
~NoCopy() {}
NoCopy(const NoCopy &) = delete; // 禁止拷贝构造
const NoCopy &operator=(const NoCopy&) = delete; // 禁止赋值
};
2.3 宏定义
#define CONV(addr) ((struct sockaddr*)&addr) // sockaddr_in转sockaddr*
三、InetAddr.hpp - 网络地址封装
3.1 类定义
class InetAddr {
private:
struct sockaddr_in _addr; // 网络地址结构
std::string _ip; // 字符串格式IP
uint16_t _port; // 端口号
};
3.2 构造函数
// 1. 默认构造
InetAddr() {}
// 2. 从sockaddr_in构造
InetAddr(struct sockaddr_in &addr) {
SetAddr(addr);
}
// 3. 指定IP和端口
InetAddr(const std::string &ip, uint16_t port)
: _ip(ip), _port(port) {
// 主机字节序 -> 网络字节序
memset(&_addr, 0, sizeof(_addr));
_addr.sin_family = AF_INET;
inet_pton(AF_INET, _ip.c_str(), &_addr.sin_addr);
_addr.sin_port = htons(_port);
}
// 4. 仅指定端口(监听所有IP)
InetAddr(uint16_t port) : _port(port), _ip() {
memset(&_addr, 0, sizeof(_addr));
_addr.sin_family = AF_INET;
_addr.sin_addr.s_addr = INADDR_ANY; // 0.0.0.0
_addr.sin_port = htons(_port);
}
3.3 核心方法
// 设置地址(从accept接收)
void SetAddr(struct sockaddr_in &addr) {
_addr = addr;
_port = ntohs(_addr.sin_port); // 网络->主机
char ipbuffer[64];
inet_ntop(AF_INET, &_addr.sin_addr, ipbuffer, sizeof(_addr));
_ip = ipbuffer; // 网络IP->字符串IP
}
// 获取各种格式的地址
uint16_t Port() { return _port; }
std::string Ip() { return _ip; }
const struct sockaddr_in &NetAddr() { return _addr; }
const struct sockaddr *NetAddrPtr() { return CONV(_addr); }
socklen_t NetAddrLen() { return sizeof(_addr); }
// 格式化输出
std::string StringAddr() {
return _ip + ":" + std::to_string(_port);
}
四、Mutex.hpp - 线程同步
4.1 互斥锁类
class Mutex {
private:
pthread_mutex_t _mutex;
public:
Mutex() { pthread_mutex_init(&_mutex, nullptr); }
~Mutex() { pthread_mutex_destroy(&_mutex); }
void Lock() { pthread_mutex_lock(&_mutex); }
void Unlock() { pthread_mutex_unlock(&_mutex); }
pthread_mutex_t *Get() { return &_mutex; }
};
4.2 自动锁守卫(RAII)
class LockGuard {
private:
Mutex &_mutex;
public:
LockGuard(Mutex &mutex) : _mutex(mutex) {
_mutex.Lock();
}
~LockGuard() {
_mutex.Unlock();
}
};
五、Log.hpp - 日志系统
5.1 策略模式设计
LogStrategy(抽象基类)
├── ConsoleLogStrategy(控制台输出)
└── FileLogStrategy(文件输出)
5.2 日志等级
enum class LogLevel {
DEBUG, // 调试信息
INFO, // 一般信息
WARNING, // 警告
ERROR, // 错误
FATAL // 致命错误
};
5.3 日志策略实现
5.3.1 控制台策略
class ConsoleLogStrategy : public LogStrategy {
public:
void SyncLog(const std::string &message) override {
LockGuard lockguard(_mutex);
std::cout << message << "\r\n";
}
private:
Mutex _mutex; // 线程安全
};
5.3.2 文件策略
class FileLogStrategy : public LogStrategy {
public:
FileLogStrategy(const std::string &path = "./log",
const std::string &file = "my.log")
: _path(path), _file(file) {
// 自动创建目录
if (!std::filesystem::exists(_path)) {
std::filesystem::create_directories(_path);
}
}
void SyncLog(const std::string &message) override {
LockGuard lockguard(_mutex);
std::string filename = _path + (_path.back() == '/' ? "" : "/") + _file;
std::ofstream out(filename, std::ios::app); // 追加模式
out << message << "\r\n";
out.close();
}
};
5.4 日志格式
[时间戳] [日志级别] [进程ID] [源文件名] [行号] - 日志内容
示例:
[2024-01-01 12:00:00] [INFO] [1234] [server.cpp] [45] - Socket created successfully
5.5 日志生成器
class Logger {
private:
std::unique_ptr<LogStrategy> _fflush_strategy;
// 内部消息类(用于<<操作符)
class LogMessage {
public:
template <typename T>
LogMessage &operator<<(const T &info) {
std::stringstream ss;
ss << info;
_loginfo += ss.str();
return *this;
}
~LogMessage() {
// 析构时自动刷新
_logger._fflush_strategy->SyncLog(_loginfo);
}
};
};
5.6 使用宏
#define LOG(level) logger(level, __FILE__, __LINE__)
#define Enable_Console_Log_Strategy() logger.EnableConsoleLogStrategy()
#define Enable_File_Log_Strategy() logger.EnableFileLogStrategy()
// 使用示例
LOG(LogLevel::INFO) << "Server started on port " << port;
六、Socket.hpp - Socket封装
6.1 模版方法模式
class Socket { // 抽象基类
public:
virtual ~Socket() {}
virtual void SocketOrDie() = 0;
virtual void BindOrDie(uint16_t port) = 0;
virtual void ListenOrDie(int backlog) = 0;
virtual std::shared_ptr<Socket> Accept(InetAddr *client) = 0;
virtual void Close() = 0;
// 模版方法:构建TCP Socket的标准流程
void BuildTcpSocketMethod(uint16_t port, int backlog = 16) {
SocketOrDie(); // 1. 创建socket
BindOrDie(port); // 2. 绑定地址
ListenOrDie(backlog); // 3. 开始监听
}
};
6.2 TCP Socket实现
class TcpSocket : public Socket {
private:
int _sockfd; // 套接字描述符
public:
TcpSocket() : _sockfd(-1) {}
TcpSocket(int fd) : _sockfd(fd) {} // 用于accept返回的已连接socket
void SocketOrDie() override {
_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
if (_sockfd < 0) {
LOG(LogLevel::FATAL) << "socket error";
exit(SOCKET_ERR);
}
}
void BindOrDie(uint16_t port) override {
InetAddr localaddr(port);
int n = ::bind(_sockfd, localaddr.NetAddrPtr(), localaddr.NetAddrLen());
if (n < 0) {
LOG(LogLevel::FATAL) << "bind error";
exit(BIND_ERR);
}
}
void ListenOrDie(int backlog) override {
int n = ::listen(_sockfd, backlog);
if (n < 0) {
LOG(LogLevel::FATAL) << "listen error";
exit(LISTEN_ERR);
}
}
std::shared_ptr<Socket> Accept(InetAddr *client) override {
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int fd = ::accept(_sockfd, CONV(peer), &len);
if (fd < 0) {
LOG(LogLevel::WARNING) << "accept warning ...";
return nullptr;
}
client->SetAddr(peer); // 设置客户端地址
return std::make_shared<TcpSocket>(fd); // 返回已连接的socket
}
void Close() {
if (_sockfd >= 0)
::close(_sockfd);
}
};
七、设计模式总结
7.1 策略模式(日志系统)
-
抽象策略 :
LogStrategy定义日志输出接口 -
具体策略 :
ConsoleLogStrategy和FileLogStrategy实现不同输出方式 -
上下文 :
Logger类使用策略对象
7.2 模版方法模式(Socket)
-
抽象类 :
Socket定义算法骨架 -
具体实现 :
TcpSocket实现具体步骤 -
固定流程 :
BuildTcpSocketMethod()封装标准TCP服务端创建流程
7.3 RAII模式(资源管理)
-
互斥锁 :
LockGuard在构造时加锁,析构时自动解锁 -
日志消息 :
LogMessage在析构时自动刷新日志
八、使用示例
8.1 创建TCP服务器
#include "Socket.hpp"
#include "Log.hpp"
int main() {
using namespace SocketModule;
using namespace LogModule;
Enable_Console_Log_Strategy();
TcpSocket server;
server.BuildTcpSocketMethod(8080, 10);
while (true) {
InetAddr clientAddr;
auto clientSocket = server.Accept(&clientAddr);
if (clientSocket) {
LOG(LogLevel::INFO) << "New connection from: "
<< clientAddr.StringAddr();
// 处理连接...
}
}
return 0;
}
8.2 日志使用示例
LOG(LogLevel::INFO) << "Server started";
LOG(LogLevel::DEBUG) << "Processing request: " << request_id;
LOG(LogLevel::ERROR) << "Failed to process: " << error_msg;
九、关键特性
-
类型安全: 使用C++类和模板,避免原始指针
-
异常安全: RAII管理资源,防止泄露
-
线程安全: 日志系统内部使用互斥锁
-
可扩展性: 策略模式便于添加新的日志输出方式
-
易用性: 宏定义简化日志记录
-
错误处理: 统一错误码和日志记录
十、注意事项
-
跨平台限制: 使用了POSIX API,主要适用于Linux/Unix
-
内存管理: 使用智能指针自动管理资源
-
性能考虑: 日志输出在析构时进行,注意作用域
-
配置灵活性: 日志策略可在运行时切换
-
错误处理: 致命错误直接退出,适合简单应用