#include <ws2tcpip.h>
#include <mswsock.h>
#include <windows.h>
#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <functional>
#pragma comment(lib, "ws2_32.lib") // 链接Winsock库
#pragma comment(lib, "mswsock.lib") // 链接MSWSock库
#define PORT "8888"
#define MAX_CLIENTS 100 // 定义最大客户端数量
#define BUFFER_SIZE 4096 // 定义缓冲区大小
// 操作类型枚举,用于区分不同的I/O操作
enum OperationType {
OP_ACCEPT, // 接受连接操作
OP_READ, // 读取数据操作
OP_WRITE // 写入数据操作
};
// 重叠结构,扩展了WSAOVERLAPPED以包含更多自定义信息
struct OverlappedEx {
WSAOVERLAPPED overlapped; // 标准重叠结构
OperationType opType; // 操作类型
char buffer[BUFFER_SIZE]; // 数据缓冲区
WSABUF wsabuf; // Windows Sockets缓冲区结构
SOCKET clientSocket; // 客户端套接字
};
class IOCPChatServer { // IOCP服务器类
private:
SOCKET listenSocket; // 监听套接字
HANDLE completionPort; // 完成端口句柄
std::vector<std::thread> workerThreads; // 工作线程向量
bool running; // 服务器运行状态标志
int threadCount; // 工作线程数量
public:
// 构造函数,初始化服务器对象
IOCPChatServer(int numThreads = 4) : listenSocket(INVALID_SOCKET), completionPort(NULL), running(false), threadCount(numThreads) {
// 初始化Winsock
WSADATA wsaData;
int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (result != 0) { // 检查启动是否成功
std::cerr << "WSAStartup failed: " << result << std::endl;
return;
}
}
~IOCPChatServer() {
Stop();
WSACleanup();
}
// 初始化
bool Initialize() {
// 创建监听套接字
ADDRINFO hints, * addrInfo = NULL; // 地址信息结构
ZeroMemory(&hints, sizeof(hints)); // 清零hints结构
hints.ai_family = AF_INET; // 设置地址族为IPv4
hints.ai_socktype = SOCK_STREAM; // 设置套接字类型为流式
hints.ai_protocol = IPPROTO_TCP; // 设置协议为TCP
hints.ai_flags = AI_PASSIVE; // 设置为被动模式(用于监听)
int result = getaddrinfo(NULL, PORT, &hints, &addrInfo);
if (result != 0) {
std::cerr << "getaddrinfo failed: " << result << std::endl;
return false;
}
// 创建套接字
listenSocket = socket(addrInfo->ai_family, addrInfo->ai_socktype, addrInfo->ai_protocol);
if (listenSocket == INVALID_SOCKET) {
std::cerr << "Error at socket(): " << WSAGetLastError() << std::endl;
freeaddrinfo(addrInfo);
return false;
}
// 绑定套接字到地址
result = bind(listenSocket, addrInfo->ai_addr, (int)addrInfo->ai_addrlen);
if (result == SOCKET_ERROR) {
std::cerr << "bind failed with error: " << WSAGetLastError() << std::endl;
freeaddrinfo(addrInfo);
closesocket(listenSocket);
return false;
}
freeaddrinfo(addrInfo); // 释放地址信息
// 创建完成端口
completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (completionPort == NULL) {
std::cerr << "CreateIoCompletionPort failed: " << GetLastError() << std::endl;
closesocket(listenSocket);
return false;
}
// 将监听套接字关联到完成端口
HANDLE ret = CreateIoCompletionPort((HANDLE)listenSocket, completionPort, (ULONG_PTR)listenSocket, 0);
if (ret == NULL) {
std::cerr << "CreateIoCompletionPort associate failed: " << GetLastError() << std::endl;
CloseHandle(completionPort);
closesocket(listenSocket);
return false; // 返回失败
}
// 开始监听连接
if (listen(listenSocket, SOMAXCONN) == SOCKET_ERROR) {
std::cerr << "listen failed with error: " << WSAGetLastError() << std::endl;
closesocket(listenSocket);
return false;
}
std::cout << "Server listening on port " << PORT << std::endl;
return true;
}
// 启动服务器
void Start() {
// 检查初始化是否成功
if (!Initialize()) {
return;
}
running = true;
// 启动工作线程
for (int i = 0; i < threadCount; ++i) { // 循环创建工作线程
workerThreads.emplace_back(&IOCPChatServer::WorkerThread, this); // 创建并启动工作线程
}
// 启动接受连接的循环
AcceptLoop();
}
void Stop() {
if (!running) return;
running = false;
// 关闭监听套接字以中断accept
if (listenSocket != INVALID_SOCKET) { // 检查监听套接字是否有效
closesocket(listenSocket);
listenSocket = INVALID_SOCKET; // 设置为无效值
}
// 投递NULL事件唤醒所有工作线程
// 循环向每个工作线程投递停止信号
for (int i = 0; i < threadCount; ++i) {
PostQueuedCompletionStatus(completionPort, 0, 0, NULL);
}
// 等待工作线程结束
for (auto& t : workerThreads) {
if (t.joinable()) {
t.join();
}
}
if (completionPort) {
CloseHandle(completionPort);
completionPort = NULL;
}
}
private:
void AcceptLoop() {
while (running) {
SOCKET clientSocket = accept(listenSocket, NULL, NULL);
if (clientSocket == INVALID_SOCKET) {
if (running) {
std::cerr << "accept failed: " << WSAGetLastError() << std::endl; // 输出错误信息
}
break; // 退出循环
}
// 关联新客户端套接字到完成端口
HANDLE ret = CreateIoCompletionPort((HANDLE)clientSocket, completionPort, (ULONG_PTR)clientSocket, 0);
if (ret == NULL) {
std::cerr << "Associate client socket with IOCP failed: " << GetLastError() << std::endl;
closesocket(clientSocket);
continue;
}
// 设置套接字为非阻塞模式
u_long nonBlocking = 1; // 非阻塞标志
ioctlsocket(clientSocket, FIONBIO, &nonBlocking);
// 投递接收操作
PostReceive(clientSocket); // 为客户端投递接收操作
}
}
// 工作线程函数
void WorkerThread() {
DWORD bytesTransferred; // 传输的字节数
ULONG_PTR completionKey; // 完成键
OVERLAPPED* pOverlapped; // 重叠结构指针
while (running) {
BOOL result = GetQueuedCompletionStatus( // 从完成端口获取完成事件
completionPort, // 完成端口句柄
&bytesTransferred, // 传输字节数的输出参数
&completionKey, // 完成键的输出参数
&pOverlapped, // 重叠结构的输出参数
INFINITE // 等待超时时间(无限等待)
);
if (!running) break;
// 检查是否是停止信号
if (pOverlapped == NULL) {
// 当我们投递NULL事件唤醒线程时会发生这种情况
continue; // 继续下一次循环
}
// 将重叠结构转换为扩展重叠结构
OverlappedEx* pOverEx = (OverlappedEx*)pOverlapped;
if (!result) {
DWORD error = GetLastError();
std::cout << "GetQueuedCompletionStatus failed, error: " << error << std::endl;
closesocket(pOverEx->clientSocket); // 关闭客户端套接字
delete pOverEx; // 释放扩展重叠结构内存
continue; // 继续处理下一个事件
}
// 检查是否是连接断开
if (bytesTransferred == 0 && (pOverEx->opType == OP_READ || pOverEx->opType == OP_WRITE)) {
std::cout << "Client disconnected." << std::endl; // 输出断开连接信息
closesocket(pOverEx->clientSocket); // 关闭客户端套接字
delete pOverEx; // 释放扩展重叠结构内存
continue; // 继续处理下一个事件
}
switch (pOverEx->opType) { // 根据操作类型处理完成事件
case OP_READ: // 读取操作完成
HandleRead(pOverEx, bytesTransferred);
break;
case OP_WRITE: // 写入操作完成
HandleWrite(pOverEx, bytesTransferred);
break;
default:
std::cerr << "Unknown operation type!" << std::endl; // 输出错误信息
break;
}
}
}
// 处理读取操作完成
void HandleRead(OverlappedEx* pOverEx, DWORD bytesTransferred) {
pOverEx->buffer[bytesTransferred] = '\0'; // 在接收数据末尾添加字符串结束符
std::string message = "Echo: " + std::string(pOverEx->buffer, bytesTransferred); // 构建回显消息
std::cout << "Received: " << pOverEx->buffer << " from client " << pOverEx->clientSocket << std::endl; // 输出接收信息
ZeroMemory(&(pOverEx->overlapped), sizeof(pOverEx->overlapped)); // 重置重叠结构
pOverEx->opType = OP_WRITE; // 设置操作类型为写入
strcpy_s(pOverEx->buffer, message.c_str()); // 复制回显消息到缓冲区
pOverEx->wsabuf.buf = pOverEx->buffer; // 设置WSABUF缓冲区指针
pOverEx->wsabuf.len = static_cast<ULONG>(message.length()); // 设置WSABUF缓冲区长度
DWORD flags = 0; // 传输标志
// 发送数据
int sendResult = WSASend(pOverEx->clientSocket, &(pOverEx->wsabuf), 1, NULL, flags, &(pOverEx->overlapped), NULL);
if (sendResult == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) { // 检查发送是否失败(排除异步操作挂起的情况)
std::cerr << "WSASend failed: " << WSAGetLastError() << std::endl; // 输出错误信息
closesocket(pOverEx->clientSocket); // 关闭客户端套接字
delete pOverEx; // 释放扩展重叠结构内存
}
// 如果是WSA_IO_PENDING,操作挂起稍后完成
}
void HandleWrite(OverlappedEx* pOverEx, DWORD bytesTransferred) {
PostReceive(pOverEx->clientSocket); // 为客户端投递下一个接收操作
}
// 投递接收操作
void PostReceive(SOCKET clientSocket) {
OverlappedEx* pOverEx = new OverlappedEx(); // 创建新的扩展重叠结构
ZeroMemory(pOverEx, sizeof(OverlappedEx)); // 清零扩展重叠结构
pOverEx->opType = OP_READ; // 设置操作类型为读取
pOverEx->clientSocket = clientSocket; // 设置客户端套接字
pOverEx->wsabuf.buf = pOverEx->buffer; // 设置WSABUF缓冲区指针
pOverEx->wsabuf.len = BUFFER_SIZE; // 设置WSABUF缓冲区长度
DWORD flags = 0; // 接收标志
// 投递接收操作
int recvResult = WSARecv(clientSocket, &(pOverEx->wsabuf), 1, NULL, &flags, &(pOverEx->overlapped), NULL);
if (recvResult == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) { // 检查接收操作是否失败(排除异步操作挂起的情况)
std::cerr << "WSARecv failed: " << WSAGetLastError() << std::endl; // 输出错误信息
closesocket(clientSocket); // 关闭客户端套接字
delete pOverEx; // 释放扩展重叠结构内存
}
// 如果是WSA_IO_PENDING,操作挂起稍后完成
}
};
int main() {
IOCPChatServer server(4); // 创建服务器对象,使用4个工作线程
server.Start();
server.Stop();
return 0;
}