线程池模型的服务器通常用于处理并发连接,它通过预先创建一组线程来处理传入的连接请求,从而避免了频繁创建和销毁线程的开销。以下是创建线程池模型服务器的一般步骤:
1.
初始化线程池:
创建一个包含固定数量线程的线程池。这些线程等待从任务队列中获取任务并执行。
2.
初始化任务队列:
创建一个任务队列,用于存储客户端的连接请求或需要处理的任务。
3.
监听端口:
创建一个监听套接字,并将其绑定到服务器的地址和端口。开始监听传入的连接请求。
4.
接受连接请求:
当客户端尝试连接时,服务器接受连接请求,并将客户端套接字添加到任务队列中。
5.
任务处理:
线程池中的线程从任务队列中取出任务,并执行相应的处理,例如处理客户端连接或执行其他任务。
6.
线程池管理:
线程池需要管理线程的生命周期,包括线程的创建、销毁和复用等。
7.
关闭服务器:
当不再需要服务器时,关闭监听套接字,停止接受新的连接请求,并等待线程池中的所有任务执行完成后,释放资源并关闭线程池。
#include <iostream>
#include <cstring>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
#pragma comment(lib, "Ws2_32.lib")
// 任务结构体,包含了客户端连接套接字
struct Task {
SOCKET clientSocket;
Task() : clientSocket() {}
Task(SOCKET socket) : clientSocket(socket) {}
};
// 线程池类
class ThreadPool {
public:
// 构造函数,初始化线程池
ThreadPool(size_t numThreads) : stop(false) {
// 创建指定数量的线程,并启动线程执行任务
for (size_t i = 0; i < numThreads; ++i) {
threads.emplace_back([this] {
while (true) {
Task task;
{
// 锁定任务队列
std::unique_lock<std::mutex> lock(queueMutex);
// 等待任务队列不为空的条件变量
condition.wait(lock, [this] { return stop || !tasks.empty(); });
// 如果线程池停止并且任务队列为空,则退出线程
if (stop && tasks.empty()) return;
// 从队列中取出任务
task = std::move(tasks.front());
tasks.pop();
}
// 处理任务
handleTask(task);
}
});
}
}
// 析构函数,停止线程池并等待所有线程结束
~ThreadPool() {
{
// 锁定任务队列
std::unique_lock<std::mutex> lock(queueMutex);
// 设置停止标志为true
stop = true;
}
// 通知所有线程停止
condition.notify_all();
// 等待所有线程结束
for (auto& thread : threads) {
thread.join();
}
}
// 添加任务到任务队列
void addTask(Task task) {
{
// 锁定任务队列
std::lock_guard<std::mutex> lock(queueMutex);
// 将任务添加到队列
tasks.push(std::move(task));
}
// 通知等待中的线程有新任务可执行
condition.notify_one();
}
private:
std::vector<std::thread> threads; // 存储线程的容器
std::queue<Task> tasks; // 任务队列
std::mutex queueMutex; // 任务队列的互斥量
std::condition_variable condition; // 任务队列非空的条件变量
bool stop; // 停止标志,用于通知线程池停止
// 处理任务的函数
void handleTask(Task& task) {
// 处理客户端连接
std::cout << "处理客户端连接的线程: " << std::this_thread::get_id() << std::endl;
// 从客户端接收数据
char buffer[1024] = { 0 };
int bytesRead = recv(task.clientSocket, buffer, sizeof(buffer), 0);
if (bytesRead == SOCKET_ERROR) {
std::cerr << "错误: 无法从客户端接收数据" << std::endl;
closesocket(task.clientSocket);
return;
}
std::cout << "从客户端接收到数据: " << buffer << std::endl;
// 发送响应到客户端
const char* response = "你好,来自服务器的消息";
int bytesSent = send(task.clientSocket, response, strlen(response), 0);
if (bytesSent == SOCKET_ERROR) {
std::cerr << "错误: 无法向客户端发送响应" << std::endl;
}
else {
std::cout << "响应已发送到客户端" << std::endl;
}
// 关闭客户端套接字
closesocket(task.clientSocket);
}
};
int main() {
// 初始化Winsock
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
std::cerr << "错误: WSAStartup 失败" << std::endl;
return 1;
}
// 创建线程池
ThreadPool pool(4); // 使用4个线程的线程池
// 创建服务器套接字
SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serverSocket == INVALID_SOCKET) {
std::cerr << "错误: 无法创建服务器套接字" << std::endl;
WSACleanup();
return 1;
}
// 设置服务器地址
sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(8080); // 设置端口号为8080
// 绑定服务器套接字到地址
if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
std::cerr << "错误: 无法绑定服务器套接字" << std::endl;
closesocket(serverSocket);
WSACleanup();
return 1;
}
// 监听传入连接
if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR) {
std::cerr << "错误: 无法监听服务器套接字" << std::endl;
closesocket(serverSocket);
WSACleanup();
return 1;
}
std::cout << "服务器已启动,正在监听端口 8080..." << std::endl;
// 接受连接并添加到任务队列中
while (true) {
sockaddr_in clientAddr;
int clientAddrLen = sizeof(clientAddr);
SOCKET clientSocket = accept(serverSocket, (sockaddr*)&clientAddr, &clientAddrLen);
if (clientSocket == INVALID_SOCKET) {
std::cerr << "错误: 无法接受客户端连接" << std::endl;
closesocket(serverSocket);
WSACleanup();
return 1;
}
// 将任务添加到线程池的任务队列中
pool.addTask(Task(clientSocket));
}
// 关闭服务器套接字
closesocket(serverSocket);
// 清理Winsock
WSACleanup();
return 0;
}