#include <winsock2.h>
#include <Windows.h>
#include <iostream>
#include <vector>
#include <thread>
#include <chrono>
#include <map>
#include <memory>
#pragma comment(lib,"ws2_32.lib")
#define BUFFER_SIZE 1024
#define PORT 8040
//客户端上下文结构,保存客户端的信息
struct Clientcontext {
SOCKET socket;
char buffer[BUFFER_SIZE];//缓冲区大小
WSABUF wsabuf; //winsock数据缓冲区的结构
/* **重叠数据结构,异步io使用,保存IO操作的状态信息,和操作系统通信,操作系统通过
OVERLAPPED结构的指针,跟踪管理每个异步I/O操作的进度,操作完成后,操作系统将结果
与OVERLAPPED结构绑定,通知应用程序。
typedef struct _OVERLAPPED {
ULONG_PTR Internal; // 内部使用的成员,通常用于错误或状态码
ULONG_PTR InternalHigh; // 内部使用的成员,通常用于保存字节数
union {
struct {
DWORD Offset; // 读写操作的文件偏移量(低32位)
DWORD OffsetHigh; // 读写操作的文件偏移量(高32位)
};
PVOID Pointer; // 用于 I/O 操作的指针
};
HANDLE hEvent; // I/O 操作完成后触发的事件句柄
//hEvent:这是一个事件句柄,当异步 I/O 操作完成时,系统会设置这个事件为信号状态。你可以选择使用
//GetOverlappedResult 或通过 WaitForSingleObject 等函数等待这个事件,来确定 I/O 操作的完成。
} OVERLAPPED, *LPOVERLAPPED;
*/
OVERLAPPED overlapped;
DWORD byteReceived; //实际接收到的字节数
};
#include <mutex>
#include <condition_variable>
#include <queue>
#include <future>
#include <functional>
#include <type_traits>
//线程池:
class ThreadPool {
public:
ThreadPool(int n):nthread_count(n),brun(true) {
for (int i = 0; i < nthread_count; ++i) {
m_vecthread.emplace_back([this]() {
while (brun) {
std::function<void()>func;
{
std::unique_lock<std::mutex>lck(mtx);
cv.wait(lck, [this]() {
return !m_quetasks.empty();
});
func=std::move(m_quetasks.front());
m_quetasks.pop();
}
func();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
});
}
}
template<typename F,typename ...Args>
auto Enqueue(F &&f,Args&& ...args)->std::future<std::invoke_result_t<F,Args...>> {
using return_type = std::invoke_result_t<F, Args...>;
auto ptr = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f),std::forward<Args>(args)...));
std::future<return_type> fut = ptr->get_future();
{
std::lock_guard<std::mutex>lck(mtx);
//lamda表达式捕获ptr,也会让ptr的引用计数+1
m_quetasks.emplace([ptr]() {//关于为什么要用指针,如果分布在栈上的话,当m_quetasks在别的地方调用的时候早就失效了
(*ptr)();
});
}
return fut;
}
~ThreadPool() {
while (!m_quetasks.empty()) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
brun = false;
for (auto& thread : m_vecthread)
thread.join();
}
private:
int nthread_count;
std::condition_variable cv;
std::queue<std::function<void()>> m_quetasks;
std::mutex mtx;
bool brun;
std::vector<std::thread>m_vecthread;
};
class IOCPReactor {
private:
HANDLE iocpHandle_;
SOCKET listenSocket_;
bool brecAccept;
bool isrun;
std::thread thAccept;
public:
IOCPReactor() :iocpHandle_(nullptr), listenSocket_(INVALID_SOCKET), brecAccept(true), isrun(true){}
~IOCPReactor() {
if (iocpHandle_ != nullptr)
CloseHandle(iocpHandle_);
if (listenSocket_ != INVALID_SOCKET)
closesocket(listenSocket_);
brecAccept = false;
thAccept.join();
WSACleanup();//清理winsock库
}
bool initReactor() {
//首先初始化winsock库
WSADATA wsadata;
if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0) {//使用2.2版本
std::cerr << "wassatartup failed" << std::endl;
return false;
}
//创建IO完成端口
/*
* 参数意义:
* 1.需要绑定的socket句柄
* 2.完成端口句柄
* 3.completionKey完成键,作为唯一的标识,通常是客户端上下文指针
* 4.最大工作线程数量,如果为0,就是cpu内核数量
*/
iocpHandle_ = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (iocpHandle_ != NULL) {
std::cerr << "iocpHandle create fail" << std::endl;
return false;
}
//创建服务端监听套接字
listenSocket_ = socket(AF_INET, SOCK_STREAM,0);//ipv4下使用tcp连接
if (listenSocket_ == INVALID_SOCKET) {
std::cerr << "listen socket create fail" << std::endl;
return false;
}
//监听套接字绑定本机所有端口
sockaddr_in serverAddr{};//服务器地址结构体
serverAddr.sin_family = AF_INET;//ipv4地址族
serverAddr.sin_port = htons(PORT); //端口号转为网络字节序
serverAddr.sin_addr.s_addr = INADDR_ANY;//绑定到本机所有可用网络端口
if (bind(listenSocket_, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
std::cerr << "bind fail" << std::endl;
return false;
}
if (listen(listenSocket_, SOMAXCONN) == SOCKET_ERROR)
{
std::cerr << "listen fail" << std::endl;
return false;
}
//将服务器套接字加入到完成端口
/*
* 1.socket的句柄
* 2.iocphandle句柄
* 3.completionKey:套接字的唯一标识符
* 4.
*/
if (CreateIoCompletionPort((HANDLE)listenSocket_, iocpHandle_, 0, 0) == NULL) {
std::cerr << "failed to jiarushibai" << std::endl;
return false;
}
thAccept = std::move(std::thread(&IOCPReactor::acceptclients,this));
return true;
}
void run() {
while (isrun)
{
DWORD bytesTransferred; // 存储传输的字节数
ULONG_PTR completionKey; // 存储完成键(可以是客户端上下文指针)
LPOVERLAPPED overlapped; // 存储重叠 I/O 的结构指针
// 等待 IO 完成端口事件 (阻塞式,线程是挂起状态,也是通过overlapped的完成时间发送才响应)
/*
* completionkey就是当时关联IOCP时的指针,用于标记客户端的,当使用GetQueuedCompletionStatus时,他会将
* 之前传入的指针传递给你,所以这个可以传入一个clientContext的上下文,上下文包含所有内容信息
*/
BOOL result = GetQueuedCompletionStatus(
iocpHandle_, &bytesTransferred, &completionKey, &overlapped, INFINITE);
if (!result) {
std::cerr << "getqueuedcompletion fail" << std::endl;
continue;
}
if (completionKey == 0) {//新客户端连接 (因为创建listenSocket的时候给的唯一标识符是0,这个得到0说明就是listenSocket得到的消息)
Clientcontext* clientcontext = (Clientcontext*)completionKey;
std::cout << "new client connect" << std::endl;
DWORD flags=0;
WSARecv(clientcontext->socket, &clientcontext->wsabuf, 1, &clientcontext->byteReceived, &flags, &clientcontext->overlapped, NULL);
}
else
{
std::shared_ptr<Clientcontext>context_ptr(reinterpret_cast<Clientcontext*>(completionKey));
//Clientcontext* clientcontext = (Clientcontext*)completionKey;
if (bytesTransferred == 0)//没有数据,客户端断开连接
{
std::cerr << "client disconnected" << std::endl;
closesocket(context_ptr->socket);
}
else
{
context_ptr->buffer[context_ptr->byteReceived] = '\0';
std::cout << "receive from client" << context_ptr->buffer << std::endl;
/对于接收到的消息可以进行处理(建议使用线程池封装)
//使用lamda表达式将任务封装到线程池的队列中(也可以使用一个任务分发器,例如http请求的get/post等)
DWORD flags = 1;
WSARecv(context_ptr->socket, &context_ptr->wsabuf, context_ptr->wsabuf.len, &context_ptr->byteReceived, &flags, &context_ptr->overlapped, NULL);
}
}
}
}
private:
void acceptclients() {
while (brecAccept){
sockaddr_in clientAddr;
int clientArrrlen = sizeof(clientAddr);
//监听socket只接收连接请求,成功后返回一个用于通信的socket
SOCKET clientSocket = accept(listenSocket_, (sockaddr*)&clientAddr, &clientArrrlen);
if (clientSocket == INVALID_SOCKET) {
std::cerr << "Accept failed" << std::endl;
continue;
}
//创建客户端上下文与iocp关联
Clientcontext* clientcontext = new Clientcontext();
clientcontext->socket = clientSocket;
clientcontext->wsabuf.buf = clientcontext->buffer;
clientcontext->wsabuf.len = BUFFER_SIZE;
memset(&clientcontext->overlapped, 0, sizeof(OVERLAPPED));
//将客户端和IOCP关联上
if (CreateIoCompletionPort((HANDLE)clientSocket, iocpHandle_, (ULONG_PTR)clientcontext, 0) == NULL)
{
std::cerr << "client lianjie fail" << std::endl;
closesocket(clientSocket);
delete clientcontext;
continue;
}
//客户端关联上了IOCP,启动第一次异步接收数据
DWORD flags = 0;
WSARecv(clientSocket, &clientcontext->wsabuf, 1, &clientcontext->byteReceived, &flags, &clientcontext->overlapped, NULL);
}
}
};```
windows下使用IOCP实现reactor模式,使用线程池增加任务处理
最新推荐文章于 2024-10-17 21:38:17 发布