文章目录
- 一、函数介绍
-
- [1、TCP 收发数据](#1、TCP 收发数据)
-
- [1)send 函数](#1)send 函数)
- [2)recv 函数](#2)recv 函数)
- 2、进程管道操作
-
- [1)popen 函数](#1)popen 函数)
- [2)pclose 函数](#2)pclose 函数)
- 二、EchoServer
- 三、DictServer
- 四、ChatServer
- 五、CommandServer(远程命令服务)
一、函数介绍
1、TCP 收发数据
1)send 函数

2)recv 函数

2、进程管道操作
1)popen 函数

2)pclose 函数

二、EchoServer
1、多进程
代码结构:
c++
Mutex.hpp
Cond.hpp
Log.hpp
Thread.hpp
ThreadPool.hpp
Common.hpp
InetAddr.hpp
TcpServer.hpp
TcpServer.cc
TcpClient.cc
1)Mutex.hpp
cpp
#pragma once
#include <iostream>
#include <pthread.h>
namespace MutexModule
{
// 互斥锁封装类
class Mutex
{
public:
// 初始化锁
Mutex()
{
pthread_mutex_init(&_mutex, nullptr);
}
// 加锁
void Lock()
{
int n = pthread_mutex_lock(&_mutex);
}
// 解锁
void Unlock()
{
int n = pthread_mutex_unlock(&_mutex);
}
// 获取锁指针
pthread_mutex_t *Get()
{
return &_mutex;
}
// 销毁锁
~Mutex()
{
pthread_mutex_destroy(&_mutex);
}
private:
pthread_mutex_t _mutex; // 原生互斥锁对象
};
// RAII 自动锁
class LockGuard
{
public:
LockGuard(Mutex &mutex)
: _mutex(mutex)
{
_mutex.Lock();
}
~LockGuard()
{
_mutex.Unlock();
}
private:
Mutex &_mutex;
};
}
2)Cond.hpp
cpp
#pragma once
#include <iostream>
#include <pthread.h>
#include "Mutex.hpp"
using namespace MutexModule;
namespace CondModule
{
// 条件变量封装
class Cond
{
public:
Cond()
{
pthread_cond_init(&_cond, nullptr);
}
// 等待: 阻塞并释放锁
void Wait(Mutex &mutex)
{
int n = pthread_cond_wait(&_cond, mutex.Get());
}
// 唤醒一个等待线程
void Signal()
{
int n = pthread_cond_signal(&_cond);
}
// 唤醒所有等待线程
void Broadcast()
{
int n = pthread_cond_broadcast(&_cond);
}
~Cond()
{
pthread_cond_destroy(&_cond);
}
private:
pthread_cond_t _cond; // 底层条件变量
};
}
3)Log.hpp
cpp
#pragma once
#include <iostream>
#include <cstdio>
#include <string>
#include <filesystem>
#include <fstream>
#include <sstream>
#include <memory>
#include <ctime>
#include <sys/types.h>
#include <unistd.h>
#include "Mutex.hpp"
namespace LogModule
{
using namespace MutexModule;
const std::string gsep = "\r\n";
// 日志策略基类(接口)
class LogStrategy
{
public:
~LogStrategy() = default;
virtual void SyncLog(const std::string &message) = 0;
};
// 控制台日志输出(线程安全)
class ConsoleLogStrategy : public LogStrategy
{
public:
void SyncLog(const std::string &message) override
{
LockGuard lockguard(_mutex);
std::cout << message << gsep;
}
~ConsoleLogStrategy() {}
private:
Mutex _mutex;
};
const std::string defaultpath = "./log/"; // /var/log
const std::string defaultfile = "my.log";
// 文件日志输出(自动建目录、线程安全)
class FileLogStrategy : public LogStrategy
{
public:
FileLogStrategy(const std::string &path = defaultpath, const std::string &file = defaultfile)
: _path(path), _file(file)
{
LockGuard lockguard(_mutex);
if (std::filesystem::exists(_path))
return;
try
{
std::filesystem::create_directories(_path);
}
catch (const std::filesystem::filesystem_error &e)
{
std::cerr << e.what() << std::endl;
}
}
// 追加写入日志文件
void SyncLog(const std::string &message) override
{
LockGuard lockguard(_mutex);
std::string filename = _path + (_path.back() == '/' ? "" : "/") + _file;
std::ofstream out(filename, std::ios::app);
if (!out.is_open())
return;
out << message << gsep;
out.close();
}
private:
std::string _path;
std::string _file;
Mutex _mutex;
};
// 日志等级类
enum class LogLevel
{
DEBUG,
INFO,
WARNING,
ERROR,
FATAL
};
// 日志等级转字符串
std::string LevelStr(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";
}
}
// 获取格式化时间字符串(线程安全)
std::string GetTimeStamp()
{
time_t curr = time(nullptr);
struct tm curr_tm; // 出参
localtime_r(&curr, &curr_tm);
char buf[128]; // 出参
snprintf(buf, sizeof(buf), "%4d-%02d-%02d %02d:%02d:%02d",
curr_tm.tm_year + 1900,
curr_tm.tm_mon + 1,
curr_tm.tm_mday,
curr_tm.tm_hour,
curr_tm.tm_min,
curr_tm.tm_sec);
return buf;
}
// 日志核心管理类
class Logger
{
public:
Logger()
{
EnableConsoleLogStrategy();
}
// 切换为文件输出
void EnableFileLogStrategy()
{
_fflush_strategy = std::make_unique<FileLogStrategy>();
}
// 切换为控制台输出
void EnableConsoleLogStrategy()
{
_fflush_strategy = std::make_unique<ConsoleLogStrategy>();
}
// 日志消息构造: 负责拼接内容, 析构时自动输出
class LogMessage
{
public:
// 构造日志头部(时间、等级、进程ID、文件名、行号)
LogMessage(LogLevel level, std::string src_name, int line_number, Logger &logger)
: _logger(logger)
{
std::stringstream ss;
ss << "[" << GetTimeStamp() << "] "
<< "[" << LevelStr(level) << "] "
<< "[" << getpid() << "] "
<< "[" << src_name << "] "
<< "[" << line_number << "] - ";
_loginfo = ss.str();
}
// 流方式拼接日志内容
template <typename T>
LogMessage &operator<<(const T &info)
{
std::stringstream ss;
ss << info;
_loginfo += ss.str();
return *this;
}
// 析构自动输出日志
~LogMessage()
{
if (_logger._fflush_strategy)
{
_logger._fflush_strategy->SyncLog(_loginfo);
}
}
private:
std::string _loginfo;
Logger &_logger;
};
// 仿函数接口, 创建日志消息
LogMessage operator()(LogLevel level, std::string name, int line)
{
return LogMessage(level, name, line, *this);
}
private:
std::unique_ptr<LogStrategy> _fflush_strategy;
};
Logger logger;
// 简化调用宏
#define LOG(level) logger(level, __FILE__, __LINE__)
#define Enable_Console_Log_Strategy() logger.EnableConsoleLogStrategy()
#define Enable_File_Log_Strategy() logger.EnableFileLogStrategy()
}
4)Thread.hpp
cpp
#pragma once
#include <iostream>
#include <string.h>
#include <functional>
#include <pthread.h>
namespace ThreadModule
{
// 全局线程编号(自动命名)
static uint32_t number = 1;
class Thread
{
using func_t = std::function<void()>; // 线程执行函数类型
private:
// 标记线程已分离
void EnableDetach()
{
_isdetach = true;
}
// 标记线程运行中
void EnableRunning()
{
_isrunning = true;
}
// 线程入口(必须static)
static void *Routine(void *args)
{
Thread *self = static_cast<Thread *>(args);
self->EnableRunning();
// 分离线程
if (self->_isdetach)
self->Detach();
// 设置线程名
pthread_setname_np(self->_tid, self->_name.c_str());
// 执行用户任务
self->_func();
return nullptr;
}
public:
// 构造:传入回调,自动生成线程名
Thread(func_t func)
: _tid(0),
_isdetach(false),
_isrunning(false),
res(nullptr),
_func(func)
{
_name = "thread-" + std::to_string(number++);
}
// 分离线程(自动回收)
void Detach()
{
if (_isdetach || !_isrunning) return;
pthread_detach(_tid);
_isdetach = true;
}
// 创建并启动线程
bool Start()
{
if (_isrunning) return false;
int n = pthread_create(&_tid, nullptr, Routine, this);
if (n == 0)
{
std::cout << _name << " create success\n";
return true;
}
std::cerr << "create error: " << strerror(n) << "\n";
return false;
}
// 取消线程
bool Stop()
{
if (!_isrunning) return false;
int n = pthread_cancel(_tid);
if (n == 0)
{
_isrunning = false;
std::cout << _name << " stop\n";
return true;
}
std::cerr << "cancel error: " << strerror(n) << "\n";
return false;
}
// 等待线程结束
void Join()
{
if (_isdetach)
{
std::cout << "线程已分离,无法join\n";
return;
}
pthread_join(_tid, &res);
std::cout << "join success\n";
}
// 获取线程名
std::string Name()
{
return _name;
}
~Thread() {}
private:
pthread_t _tid; // 线程ID
std::string _name; // 线程名
bool _isdetach; // 是否分离
bool _isrunning; // 是否运行
void *res; // 线程返回值
func_t _func; // 线程执行函数
};
}
5)ThreadPool.hpp
cpp
#pragma once
#include <iostream>
#include <vector>
#include <queue>
#include "Mutex.hpp"
#include "Cond.hpp"
#include "Log.hpp"
#include "Thread.hpp"
namespace ThreadPoolModule
{
using namespace MutexModule;
using namespace LogModule;
using namespace CondModule;
using namespace ThreadModule;
const int gnum = 5;
template <typename T>
class ThreadPool
{
private:
// 唤醒所有休眠线程
void WakeUpAllThread()
{
{
LockGuard lockguard(_mutex);
if (_sleepernum)
_cond.Broadcast();
LOG(LogLevel::INFO) << "唤醒所有休眠线程";
}
}
// 唤醒一个休眠线程
void WakeUpOne()
{
_cond.Signal();
LOG(LogLevel::INFO) << "唤醒一个休眠线程";
}
// 构造: 创建指定数量的线程
ThreadPool(int num = gnum)
: _num(num),
_isrunning(false),
_sleepernum(0)
{
for (int i = 0; i < num; i++)
{
_threads.emplace_back([this]()
{ HandlerTask(); });
}
}
// 启动线程池
void Start()
{
if (_isrunning)
return;
_isrunning = true;
for (auto &thread : _threads)
{
thread.Start();
LOG(LogLevel::INFO) << "start new thread success: " << thread.Name();
}
}
public:
// 禁用拷贝与赋值
ThreadPool(const ThreadPool<T> &) = delete;
ThreadPool<T> &operator=(const ThreadPool<T> &) = delete;
// 获取单例实例
static ThreadPool<T> *GetInstance()
{
if (inc == nullptr)
{
LockGuard lockguard(_lock);
LOG(LogLevel::DEBUG) << "获取单例...";
if (inc == nullptr)
{
LOG(LogLevel::DEBUG) << "首次使用单例,创建之...";
inc = new ThreadPool<T>();
inc->Start();
}
}
return inc;
}
// 停止线程池
void Stop()
{
if (!_isrunning)
return;
_isrunning = false;
WakeUpAllThread();
}
// 等待所有线程退出
void Join()
{
for (auto &thread : _threads)
{
thread.Join();
}
}
// 线程处理任务
void HandlerTask()
{
char name[128]; // 出参
pthread_getname_np(pthread_self(), name, sizeof(name));
while (true)
{
T t;
{
LockGuard lockguard(_mutex);
// 任务队列为空, 等待任务
while (_taskq.empty() && _isrunning)
{
_sleepernum++;
_cond.Wait(_mutex); // 释放锁并休眠,被唤醒后重新获取锁
_sleepernum--;
}
// 线程退出条件
if (!_isrunning && _taskq.empty())
{
LOG(LogLevel::INFO) << name << " 退出了, 线程池退出&&任务队列为空";
break;
}
// 取出任务
t = _taskq.front();
_taskq.pop();
}
// 执行任务
t();
}
}
// 任务入队
bool Enqueue(const T &in)
{
if (_isrunning)
{
{
LockGuard lockguard(_mutex);
_taskq.push(in);
}
// 所有线程都在休眠, 唤醒一个
if (_threads.size() == _sleepernum)
WakeUpOne();
return true;
}
return false;
}
~ThreadPool() = default;
private:
std::vector<Thread> _threads; // 线程数组
int _num; // 线程数量
std::queue<T> _taskq; // 任务队列
Cond _cond; // 条件变量
Mutex _mutex; // 互斥锁
bool _isrunning; // 运行状态
int _sleepernum; // 休眠线程数
static ThreadPool<T> *inc; // 单例指针
static Mutex _lock; // 单例创建锁
};
// 静态成员初始化
template <typename T>
ThreadPool<T> *ThreadPool<T>::inc = nullptr;
template <typename T>
Mutex ThreadPool<T>::_lock;
}
6)Common.hpp
cpp
#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/wait.h>
// 程序退出状态码枚举
enum ExitCode
{
OK = 0, // 正常退出
USAGE_ERR, // 参数错误
SOCKET_ERR, // 创建socket失败
BIND_ERR, // 绑定失败
LISTEN_ERR, // 监听失败
CONNECT_ERR, // 连接服务器失败
FORK_ERR, // 创建进程失败
OPEN_ERR // 打开文件失败
};
// 禁止拷贝基类: 继承后无法拷贝/赋值
class NoCopy
{
public:
NoCopy() {}
~NoCopy() {}
NoCopy(const NoCopy &) = delete;
NoCopy &operator=(const NoCopy &) = delete;
};
// 地址类型转换宏: sockaddr_in → sockaddr*
#define CONV(addr) ((struct sockaddr *)&addr)
7)InetAddr.hpp
cpp
#pragma once
#include "Common.hpp"
// IPv4地址封装: 主机格式 <-> 网络格式
class InetAddr
{
public:
// 构造: 网络地址 -> 主机格式
InetAddr(struct sockaddr_in &addr)
: _addr(addr)
{
_port = ntohs(_addr.sin_port); // port
char ipbuffer[64]; // ip
inet_ntop(AF_INET, &_addr.sin_addr, ipbuffer, sizeof(ipbuffer));
_ip = ipbuffer;
}
// 构造:指定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); // ip
_addr.sin_port = htons(_port); // port
}
// 构造:仅端口, 绑定本机所有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; // ip
_addr.sin_port = htons(_port); // port
}
// 设置网络地址信息
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(ipbuffer));
_ip = ipbuffer;
}
// 获取点分十进制IP
std::string Ip() { return _ip; }
// 获取主机字节序端口
uint16_t Port() { return _port; }
// 获取原生网络地址结构体
const struct sockaddr_in &NetAddr() { return _addr; }
// 获取通用地址指针(用于系统调用)
const struct sockaddr *NetAddrPtr() { return CONV(_addr); }
// 获取地址长度
socklen_t NetAddrLen() { return sizeof(_addr); }
// 比较两个地址是否相同
bool operator==(const InetAddr &addr)
{
return addr._ip == _ip && addr._port == _port;
}
// 转为 ip:port 格式字符串
std::string StringAddr()
{
return _ip + ":" + std::to_string(_port);
}
~InetAddr() {}
private:
struct sockaddr_in _addr; // 网络字节序地址
std::string _ip; // 点分十进制IP
uint16_t _port; // 主机字节序端口
};
8)TcpServer.hpp(多进程版)
cpp
#pragma once
#include "Common.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"
using namespace LogModule;
using namespace ThreadPoolModule;
// 业务回调函数: 处理客户端信息
using func_t = std::function<std::string(const std::string &, InetAddr &)>;
const static int defaultsockfd = -1;
const static int backlog = 8;
// TCP服务器(禁止拷贝)
class TcpServer : public NoCopy
{
public:
// 构造: 初始化端口 + 业务回调
TcpServer(uint16_t port, func_t func)
: _port(port),
_listensockfd(defaultsockfd),
_isrunning(false),
_func(func)
{
}
// 初始化服务器: 创建socket + 绑定 + 监听
void Init()
{
// 1. 创建TCP套接字
_listensockfd = socket(AF_INET, SOCK_STREAM, 0);
if (_listensockfd < 0)
{
LOG(LogLevel::FATAL) << "socket error";
exit(SOCKET_ERR);
}
LOG(LogLevel::INFO) << "socket success: " << _listensockfd;
// 2. 绑定端口
InetAddr local(_port);
int n = bind(_listensockfd, local.NetAddrPtr(), local.NetAddrLen());
if (n < 0)
{
LOG(LogLevel::FATAL) << "bind error";
exit(BIND_ERR);
}
LOG(LogLevel::INFO) << "bind success: " << _listensockfd;
// 3. 监听端口
n = listen(_listensockfd, backlog);
if (n < 0)
{
LOG(LogLevel::FATAL) << "listen error";
exit(LISTEN_ERR);
}
LOG(LogLevel::INFO) << "listen success: " << _listensockfd;
}
// 服务处理:读取消息 -> 回调处理 -> 回发响应
void Service(int sockfd, InetAddr &peer)
{
char buffer[1024];
while (true)
{
// 1. 读取客户端数据
ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);
if (n > 0)
{
buffer[n] = 0;
LOG(LogLevel::DEBUG) << peer.StringAddr() << " #" << buffer;
// 2. 执行业务回调
std::string echo_str = _func(buffer, peer);
// 3. 回写响应
write(sockfd, echo_str.c_str(), echo_str.size());
}
else if (n == 0) // 客户端断开
{
LOG(LogLevel::DEBUG) << peer.StringAddr() << " 退出了...";
close(sockfd);
break;
}
else // 读取异常
{
LOG(LogLevel::DEBUG) << peer.StringAddr() << " 异常...";
close(sockfd);
break;
}
}
}
// 运行服务器: 循环accept -> 创建线程处理
void Run()
{
_isrunning = true;
while (_isrunning)
{
// 1. 阻塞等待客户端连接
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int sockfd = accept(_listensockfd, CONV(peer), &len);
if (sockfd < 0)
{
LOG(LogLevel::WARNING) << "accept error";
continue;
}
InetAddr addr(peer); // 客户端地址
LOG(LogLevel::INFO) << "accept success, peer addr: " << addr.StringAddr();
// 多进程版本(双fork创建孤儿进程,避免僵尸进程)
pid_t id = fork();
if (id < 0)
{
LOG(LogLevel::FATAL) << "fork error";
exit(FORK_ERR);
}
else if (id == 0)
{
// 子进程
close(_listensockfd);
// 再次fork, 生成孙子进程
if (fork() > 0)
exit(OK); // 子进程退出,孙子进程变为孤儿进程(由init接管)
// 孙子进程: 真正处理客户端请求
Service(sockfd, addr);
exit(OK);
}
else
{
// 父进程: 只负责监听+accept,不处理业务
close(sockfd); // 关闭通信fd
pid_t rid = waitpid(id, nullptr, 0); // 等待子进程退出,防止僵尸进程
}
}
_isrunning = false;
}
~TcpServer()
{
}
private:
uint16_t _port; // 服务器端口
int _listensockfd; // 监听套接字
bool _isrunning; // 运行状态
func_t _func; // 业务处理回调
};
9)TcpServer.cc
cpp
#include "TcpServer.hpp"
#include "Dict.hpp"
// 默认回调: 回复 haha + 消息
std::string defaulthandler(const std::string &msg, InetAddr &)
{
return "haha, " + msg;
}
// 打印帮助提示
void Usage(std::string proc)
{
std::cerr << "Usage: " << proc << " port" << std::endl;
}
// ./tcpserver port
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
exit(USAGE_ERR);
}
uint16_t port = std::stoi(argv[1]);
Enable_Console_Log_Strategy();
// 1. 创建TCP服务器, 绑定默认回调
std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(port, defaulthandler);
// 2. 初始化服务器
tsvr->Init();
// 3. 启动服务器
tsvr->Run();
return 0;
}
10)TcpClient.cc
cpp
#include <iostream>
#include "Common.hpp"
#include "InetAddr.hpp"
// 打印帮助提示
void Usage(std::string proc)
{
std::cerr << "Usage: " << proc << " server_ip server_port" << std::endl;
}
// ./tcpclient server_ip server_port
int main(int argc, char *argv[])
{
if (argc != 3)
{
Usage(argv[0]);
exit(USAGE_ERR);
}
std::string serverip = argv[1];
uint16_t serverport = std::stoi(argv[2]);
// 1. 创建TCP客户端套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
std::cerr << "socket error" << std::endl;
exit(SOCKET_ERR);
}
// 2. 封装服务器地址并发起连接
InetAddr serveraddr(serverip, serverport);
int n = connect(sockfd, serveraddr.NetAddrPtr(), serveraddr.NetAddrLen());
if (n < 0)
{
std::cerr << "connect error" << std::endl;
exit(CONNECT_ERR);
}
// 3. 循环: 发送消息 + 接收回显
while (true)
{
// 输入消息
std::string line;
std::cout << "Please Enter# ";
std::getline(std::cin, line);
// 发送给服务端
write(sockfd, line.c_str(), line.size());
// 接收服务端响应
char buffer[1024];
ssize_t size = read(sockfd, buffer, sizeof(buffer) - 1);
if (size > 0)
{
buffer[size] = 0;
std::cout << "server echo# " << buffer << std::endl;
}
}
// 关闭套接字
close(sockfd);
return 0;
}
客户端:

服务端:

2、多线程
1)TcpServer.hpp(多线程版)
cpp
#pragma once
#include "Common.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"
using namespace LogModule;
using namespace ThreadPoolModule;
// 业务回调函数: 处理客户端信息
using func_t = std::function<std::string(const std::string &, InetAddr &)>;
const static int defaultsockfd = -1;
const static int backlog = 8;
// TCP服务器(禁止拷贝)
class TcpServer : public NoCopy
{
public:
// 构造: 初始化端口 + 业务回调
TcpServer(uint16_t port, func_t func)
: _port(port),
_listensockfd(defaultsockfd),
_isrunning(false),
_func(func)
{
}
// 初始化服务器: 创建socket + 绑定 + 监听
void Init()
{
// 1. 创建TCP套接字
_listensockfd = socket(AF_INET, SOCK_STREAM, 0);
if (_listensockfd < 0)
{
LOG(LogLevel::FATAL) << "socket error";
exit(SOCKET_ERR);
}
LOG(LogLevel::INFO) << "socket success: " << _listensockfd;
// 2. 绑定端口
InetAddr local(_port);
int n = bind(_listensockfd, local.NetAddrPtr(), local.NetAddrLen());
if (n < 0)
{
LOG(LogLevel::FATAL) << "bind error";
exit(BIND_ERR);
}
LOG(LogLevel::INFO) << "bind success: " << _listensockfd;
// 3. 监听端口
n = listen(_listensockfd, backlog);
if (n < 0)
{
LOG(LogLevel::FATAL) << "listen error";
exit(LISTEN_ERR);
}
LOG(LogLevel::INFO) << "listen success: " << _listensockfd;
}
// 线程数据封装:传递给工作线程
class ThreadData
{
public:
ThreadData(int fd, InetAddr &ar, TcpServer *s)
: sockfd(fd),
addr(ar),
tsvr(s)
{
}
public:
int sockfd; // 通信套接字
InetAddr addr; // 客户端地址
TcpServer *tsvr; // 服务器对象指针
};
// 服务处理:读取消息 -> 回调处理 -> 回发响应
void Service(int sockfd, InetAddr &peer)
{
char buffer[1024];
while (true)
{
// 1. 读取客户端数据
ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);
if (n > 0)
{
buffer[n] = 0;
LOG(LogLevel::DEBUG) << peer.StringAddr() << " #" << buffer;
// 2. 执行业务回调
std::string echo_str = _func(buffer, peer);
// 3. 回写响应
write(sockfd, echo_str.c_str(), echo_str.size());
}
else if (n == 0) // 客户端断开
{
LOG(LogLevel::DEBUG) << peer.StringAddr() << " 退出了...";
close(sockfd);
break;
}
else // 读取异常
{
LOG(LogLevel::DEBUG) << peer.StringAddr() << " 异常...";
close(sockfd);
break;
}
}
}
// 线程入口函数(分离线程)
static void *Routine(void *args)
{
pthread_detach(pthread_self());
ThreadData *td = static_cast<ThreadData *>(args);
td->tsvr->Service(td->sockfd, td->addr);
delete td;
return nullptr;
}
// 运行服务器: 循环accept -> 创建线程处理
void Run()
{
_isrunning = true;
while (_isrunning)
{
// 阻塞等待客户端连接
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int sockfd = accept(_listensockfd, CONV(peer), &len);
if (sockfd < 0)
{
LOG(LogLevel::WARNING) << "accept error";
continue;
}
InetAddr addr(peer); // 客户端地址
LOG(LogLevel::INFO) << "accept success, peer addr: " << addr.StringAddr();
// 多线程版本
// 创建线程处理新连接
ThreadData *td = new ThreadData(sockfd, addr, this);
pthread_t tid;
pthread_create(&tid, nullptr, Routine, td);
}
_isrunning = false;
}
~TcpServer()
{
}
private:
uint16_t _port; // 服务器端口
int _listensockfd; // 监听套接字
bool _isrunning; // 运行状态
func_t _func; // 业务处理回调
};
客户端:

服务端:

3、线程池
1)TcpServer.hpp(线程池版)
cpp
#pragma once
#include "Common.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"
using namespace LogModule;
using namespace ThreadPoolModule;
// 业务回调函数: 处理客户端信息
using func_t = std::function<std::string(const std::string &, InetAddr &)>;
// 线程池任务类型: 无参无返回值
using task_t = std::function<void()>;
const static int defaultsockfd = -1;
const static int backlog = 8;
// TCP服务器(禁止拷贝)
class TcpServer : public NoCopy
{
public:
// 构造: 初始化端口 + 业务回调
TcpServer(uint16_t port, func_t func)
: _port(port),
_listensockfd(defaultsockfd),
_isrunning(false),
_func(func)
{
}
// 初始化服务器: 创建socket + 绑定 + 监听
void Init()
{
// 1. 创建TCP套接字
_listensockfd = socket(AF_INET, SOCK_STREAM, 0);
if (_listensockfd < 0)
{
LOG(LogLevel::FATAL) << "socket error";
exit(SOCKET_ERR);
}
LOG(LogLevel::INFO) << "socket success: " << _listensockfd;
// 2. 绑定端口
InetAddr local(_port);
int n = bind(_listensockfd, local.NetAddrPtr(), local.NetAddrLen());
if (n < 0)
{
LOG(LogLevel::FATAL) << "bind error";
exit(BIND_ERR);
}
LOG(LogLevel::INFO) << "bind success: " << _listensockfd;
// 3. 监听端口
n = listen(_listensockfd, backlog);
if (n < 0)
{
LOG(LogLevel::FATAL) << "listen error";
exit(LISTEN_ERR);
}
LOG(LogLevel::INFO) << "listen success: " << _listensockfd;
}
// 服务处理:读取消息 -> 回调处理 -> 回发响应
void Service(int sockfd, InetAddr &peer)
{
char buffer[1024];
while (true)
{
// 1. 读取客户端数据
ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);
if (n > 0)
{
buffer[n] = 0;
LOG(LogLevel::DEBUG) << peer.StringAddr() << " #" << buffer;
// 2. 执行业务回调
std::string echo_str = _func(buffer, peer);
// 3. 回写响应
write(sockfd, echo_str.c_str(), echo_str.size());
}
else if (n == 0) // 客户端断开
{
LOG(LogLevel::DEBUG) << peer.StringAddr() << " 退出了...";
close(sockfd);
break;
}
else // 读取异常
{
LOG(LogLevel::DEBUG) << peer.StringAddr() << " 异常...";
close(sockfd);
break;
}
}
}
// 运行服务器: 循环accept -> 创建线程处理
void Run()
{
_isrunning = true;
while (_isrunning)
{
// 阻塞等待客户端连接
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int sockfd = accept(_listensockfd, CONV(peer), &len);
if (sockfd < 0)
{
LOG(LogLevel::WARNING) << "accept error";
continue;
}
InetAddr addr(peer); // 客户端地址
LOG(LogLevel::INFO) << "accept success, peer addr: " << addr.StringAddr();
// 线程池版本(适合短服务/高并发)
ThreadPool<task_t>::GetInstance()->Enqueue([this, sockfd, &addr]()
{
// 让线程池处理客户端请求
this->Service(sockfd, addr);
// 任务处理完毕, 关闭文件描述符
close(sockfd);
});
}
_isrunning = false;
}
~TcpServer()
{
}
private:
uint16_t _port; // 服务器端口
int _listensockfd; // 监听套接字
bool _isrunning; // 运行状态
func_t _func; // 业务处理回调
};
客户端:

服务端:

三、DictServer
1、线程池
代码结构:
c++
Mutex.hpp
Cond.hpp
Log.hpp
Thread.hpp
ThreadPool.hpp
Common.hpp
InetAddr.hpp
TcpServer.hpp // 线程池版
TcpServer.cc // 改动
TcpClient.cc
dictionary.txt
Dict.hpp
1)dictionary.txt
cpp
apple: 苹果
banana: 香蕉
cat: 猫
dog: 狗
book: 书
pen: 笔
happy: 快乐的
sad: 悲伤的
hello:
: 你好
run: 跑
jump: 跳
teacher: 老师
student: 学生
car: 汽车
bus: 公交车
love: 爱
hate: 恨
hello: 你好
goodbye: 再见
summer: 夏天
winter: 冬天
2)Dict.hpp
cpp
#pragma once
#include<iostream>
#include<string>
#include<fstream>
#include<unordered_map>
#include"Log.hpp"
#include"InetAddr.hpp"
const std::string defaultdict = "./dictionary.txt";
const std::string sep = ": ";
using namespace LogModule;
// 翻译字典类: 加载文件+单词翻译
class Dict
{
public:
Dict(const std::string& path=defaultdict)
:_dict_path(path)
{}
// 加载字典文件
bool LoadDict()
{
std::ifstream in(_dict_path);
if(!in.is_open())
{
LOG(LogLevel::DEBUG) << "打开字典失败: " << _dict_path;
return false;
}
std::string line; // 出参
while(std::getline(in,line)) // 一行一行读取
{
auto pos = line.find(sep);
if(pos==std::string::npos)
{
LOG(LogLevel::WARNING) << "解析失败: " << line;
continue;
}
std::string english = line.substr(0, pos);
std::string chinese = line.substr(pos + sep.size());
if(english.empty()||chinese.empty())
{
LOG(LogLevel::WARNING) << "无效内容: " << line;
continue;
}
_dict[english] = chinese;
LOG(LogLevel::DEBUG) << "加载: " << line;
}
in.close();
return true;
}
// 翻译单词
std::string Translate(const std::string& word,InetAddr& client)
{
auto it = _dict.find(word);
if(it==_dict.end())
{
LOG(LogLevel::DEBUG) << "进入到了翻译模块, [" << client.Ip() << ":" << client.Port() << "]# " << word << "->None";
return "None";
}
LOG(LogLevel::DEBUG) << "进入到了翻译模块, [" << client.Ip() << ":" << client.Port() << "]# " << word << "->" << it->second;
return it->second;
}
~Dict()
{}
private:
std::string _dict_path; // 字典文件路径
std::unordered_map<std::string, std::string> _dict; // 英汉对照词典
};
3)TcpServer.cc
cpp
#include "TcpServer.hpp"
#include "Dict.hpp"
// 打印帮助提示
void Usage(std::string proc)
{
std::cerr << "Usage: " << proc << " port" << std::endl;
}
// ./tcpserver port
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
exit(USAGE_ERR);
}
uint16_t port = std::stoi(argv[1]);
Enable_Console_Log_Strategy();
// 1. 创建字典对象
Dict d;
// 2. 加载字典文件
d.LoadDict();
// 3. 创建TCP服务器, 绑定翻译回调
std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(port,
[&d](const std::string &word, InetAddr &client)
{
// 收到单词->翻译并返回结果
return d.Translate(word, client);
});
// 4. 初始化服务器
tsvr->Init();
// 5. 启动服务器
tsvr->Run();
return 0;
}
客户端:

服务端:

四、ChatServer
1、线程池
代码结构:
c++
Mutex.hpp
Cond.hpp
Log.hpp
Thread.hpp
ThreadPool.hpp
Common.hpp
InetAddr.hpp
TcpServer.hpp // 线程池版
TcpServer.cc // 改动
TcpClient.cc // 改动
Route.hpp
1)Route.hpp
cpp
#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <vector>
#include "Log.hpp"
#include "Mutex.hpp"
#include "InetAddr.hpp"
using namespace LogModule;
using namespace MutexModule;
// 路由模块: UDP群聊转发
class Route
{
private:
// 判断用户是否在线
bool IsExist(InetAddr &peer)
{
for (auto &user : _online_user)
{
if (user == peer)
{
return true;
}
}
return false;
}
// 添加在线用户
void AddUser(InetAddr &peer)
{
LOG(LogLevel::INFO) << "新增在线用户: " << peer.StringAddr();
_online_user.push_back(peer);
}
// 删除离线用户
void DeleteUser(InetAddr &peer)
{
for (auto iter = _online_user.begin(); iter != _online_user.end(); iter++)
{
if (*iter == peer)
{
LOG(LogLevel::INFO) << "删除用户: " << peer.StringAddr();
_online_user.erase(iter);
break;
}
}
}
public:
Route()
{
}
// 消息路由: 群发消息+上下线管理
void MessageRoute(int sockfd, const std::string &message, InetAddr &peer)
{
LockGuard lockguard(_mutex);
// 未入线则加入列表
if (!IsExist(peer))
AddUser(peer);
// 构造转发消息
std::string send_message = peer.StringAddr() + "# " + message;
// 转发给所有在线用户
for (auto &user : _online_user)
{
sendto(sockfd, send_message.c_str(), send_message.size(), 0,
(const struct sockaddr *)&(user.NetAddr()), sizeof(user.NetAddr()));
}
// 收到QUIT则下线
if (message == "QUIT")
{
DeleteUser(peer);
}
}
~Route()
{
}
private:
std::vector<InetAddr> _online_user; // 在线用户列表
Mutex _mutex; // 互斥锁
};
2)TcpServer.cc
cpp
#include "Command.hpp"
#include "TcpServer.hpp"
#include "Dict.hpp"
#include "Route.hpp"
// 默认回调: 回复 haha + 消息
std::string defaulthandler(const std::string &msg, InetAddr &)
{
return "haha, " + msg;
}
// 打印帮助提示
void Usage(std::string proc)
{
std::cerr << "Usage: " << proc << " port" << std::endl;
}
// ./tcpserver port
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
exit(USAGE_ERR);
}
uint16_t port = std::stoi(argv[1]);
Enable_Console_Log_Strategy();
// 1. 创建路由对象
Route r;
// 2. 创建TCP服务器,绑定路由回调
std::unique_ptr<TcpServer> tsvr= std::make_unique<TcpServer>(port,
[&r](const std::string& message,InetAddr& client) -> std::string
{
// 收到消息 -> 路由处理,返回消息
return r.MessageRoute(message,client);
});
// 3. 初始化服务器
tsvr->Init();
// 4. 启动服务器
tsvr->Run();
return 0;
}
3)TcpClient.cc
cpp
#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "Thread.hpp"
#include "InetAddr.hpp"
using namespace ThreadModule;
// 全局变量
int sockfd = 0;
pthread_t recv_tid;
// 接收线程: 循环读取服务器消息并打印
void Recv()
{
while (true)
{
char buffer[1024];
ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
if (n > 0)
{
buffer[n] = 0;
std::cerr << buffer << std::endl;
std::cout << "Please Enter# ";
}
else if (n == 0)
{
std::cerr << "服务器断开连接" << std::endl;
break;
}
else
{
std::cerr << "接收异常" << std::endl;
break;
}
}
close(sockfd);
}
// 发送线程: 读取输入发送给服务端
void Send()
{
while (true)
{
std::string input;
std::cout << "Please Enter# ";
std::getline(std::cin, input);
send(sockfd, input.c_str(), input.size(), 0);
}
}
// ./udpclient server_ip server_port
int main(int argc, char *argv[])
{
if (argc != 3)
{
std::cerr << "Usage: " << argv[0] << " server_ip server_port" << std::endl;
return 1;
}
std::string server_ip = argv[1];
uint16_t server_port = std::stoi(argv[2]);
// 1. 创建TCP套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
std::cerr << "socket error" << std::endl;
return 2;
}
// 2. 连接服务器
InetAddr server(server_ip, server_port);
if(connect(sockfd,server.NetAddrPtr(),server.NetAddrLen())<0)
{
std::cerr << "connect error" << std::endl;
return 3;
}
// 3. 创建收发线程
Thread recver(Recv);
Thread sender(Send);
// 4. 启动线程
recver.Start();
sender.Start();
recv_tid = recver.Id();
// 5. 等待线程结束
recver.Join();
sender.Join();
close(sockfd);
return 0;
}
客户端:

服务端:

五、CommandServer(远程命令服务)
1、线程池
代码结构:
c++
Mutex.hpp
Cond.hpp
Log.hpp
Thread.hpp
ThreadPool.hpp
Common.hpp
InetAddr.hpp
TcpServer.hpp // 线程池版
TcpServer.cc // 改动
TcpClient.cc
Command.hpp
1)Command.hpp
cpp
#pragma once
#include <iostream>
#include <string>
#include <cstdio>
#include <set>
#include "InetAddr.hpp"
#include "Log.hpp"
using namespace LogModule;
// 命令执行类(带白名单校验)
class Command
{
public:
// 构造: 初始化命令白名单
Command()
{
_WhiteListCommands.insert("ls");
_WhiteListCommands.insert("pwd");
_WhiteListCommands.insert("ls -l");
_WhiteListCommands.insert("touch haha.txt");
_WhiteListCommands.insert("who");
_WhiteListCommands.insert("whoami");
}
// 校验命令是否在白名单内
bool IsSafeCommand(const std::string &cmd)
{
auto iter = _WhiteListCommands.find(cmd);
return iter != _WhiteListCommands.end();
}
// 执行安全命令, 返回结果字符串
std::string Execute(const std::string &cmd, InetAddr &addr)
{
// 1. 安全校验
if (!IsSafeCommand(cmd))
{
return "非法命令! ";
}
std::string client = addr.StringAddr(); // 客户端地址
// 2. 执行命令
FILE *fp = popen(cmd.c_str(), "r");
if (!fp)
{
return std::string("命令执行失败: ") + cmd;
}
std::string res;
char line[1024];
while (fgets(line, sizeof(line), fp))
{
res += line;
}
pclose(fp);
std::string result = client + "execute done, result is: \n" + res;
LOG(LogLevel::DEBUG) << result;
return result;
}
~Command()
{
}
private:
std::set<std::string> _WhiteListCommands; // 命令白名单
};
2)TcpServer.cc
cpp
#include "Command.hpp"
#include "TcpServer.hpp"
#include "Dict.hpp"
// 打印帮助提示
void Usage(std::string proc)
{
std::cerr << "Usage: " << proc << " port" << std::endl;
}
// ./tcpserver port
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
exit(USAGE_ERR);
}
uint16_t port = std::stoi(argv[1]);
Enable_Console_Log_Strategy();
// 1. 创建命令执行对象
Command cmd;
// 2. 创建TCP服务器, 绑定命令执行回调
std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(port,
[&cmd](const std::string &command, InetAddr &addr)
{
// 收到命令 -> 执行并返回结果
return cmd.Execute(command, addr);
});
// 3. 初始化服务器
tsvr->Init();
// 4. 启动服务器
tsvr->Run();
return 0;
}
客户端:

服务端:
