1 iocp回声服务器
-------------------------------------ThreadPool.h----------------------------------------
//线程池是iocp回声服务器的基础
#pragma once
#include <atomic>
#include <process.h>
#include <vector>
#include <mutex>
#include <WinSock2.h>
#include <atlstr.h>
#include <iostream>
typedef int (*FUNCTYPE)(void*);
class ThreadWorker {
public:
ThreadWorker() : func(NULL), arg(arg) {};
ThreadWorker(FUNCTYPE f, void* arg = NULL) : func(f), arg(arg) {}
ThreadWorker(const ThreadWorker& worker) {
func = worker.func;
arg = worker.arg;
}
ThreadWorker& operator=(const ThreadWorker& worker) {
if (this != &worker) {
func = worker.func;
arg = worker.arg;
}
return *this;
}
int operator()(void* arg) {
if (IsValid()) {
return func(arg);
}
return -1;
}
bool IsValid() const {
return (func != NULL);
}
public:
FUNCTYPE func;
void* arg;
};
class MyThread
{
public:
MyThread() {
m_hThread = NULL;
m_bStatus = false;
}
~MyThread() {
}
/*
* 判断线程状态:
* 返回true表示有效 返回false表示线程异常或者已经终止
*/
bool IsValid() {
if (m_hThread == NULL || (m_hThread == INVALID_HANDLE_VALUE))return false;
return WaitForSingleObject(m_hThread, 0) == WAIT_TIMEOUT;
}
/*
* 启动线程:
* 返回true表示成功 返回false表示失败
*/
bool Start() {
m_bStatus = true;
m_hThread = (HANDLE)_beginthread(&MyThread::ThreadEntry, 0, this);
if (!IsValid()) {
m_bStatus = false;
}
return m_bStatus;
}
/*
* 关闭线程:
* 返回true表示成功 返回false表示失败
*/
bool Stop() {
if (m_bStatus == false)return true;
m_bStatus = false;
bool ret = WaitForSingleObject(m_hThread, INFINITE) == WAIT_OBJECT_0;
UpdateWorker();
return ret;
}
void UpdateWorker(const ::ThreadWorker& worker = ::ThreadWorker()) {
if (m_worker.load() != NULL && m_worker.load() != &worker) {
::ThreadWorker* pWorker = m_worker.load();
m_worker.store(NULL);
delete pWorker;
}
if (!worker.IsValid()) {
m_worker.store(NULL);
return;
}
m_worker.store(new ::ThreadWorker(worker));
}
/*
* 检测当前线程是否被分配任务:
* true:分配;false:未分配
*/
bool IsIdle() {
if (m_worker == NULL)return true;
return !m_worker.load()->IsValid();
}
private:
/*
* 开启线程:
* 调用系统API,开启一个系统线程
*/
static void ThreadEntry(void* arg) {
MyThread* thiz = (MyThread*)arg;
if (thiz) {
thiz->ThreadWorker();
}
_endthread();
}
/*
* 执行线程:
* 如果m_worker一直没有被分配任务,则一直循环,不会执行
* 一旦被分配任务,就会执行函数
*/
void ThreadWorker() {
while (m_bStatus) {
if (m_worker == NULL) {
Sleep(1);
continue;
}
::ThreadWorker worker = *m_worker.load();
if (worker.IsValid()) {
int ret = worker(worker.arg);
if (ret == -1) {
std::cout << "thread found warning" << std::endl;
}
else {
m_worker.store(NULL);
}
}
else {
Sleep(1);
}
}
}
private:
HANDLE m_hThread;//线程句柄
bool m_bStatus;//线程当前状态
std::atomic<::ThreadWorker*> m_worker;//存放一个线程类,主要包括函数
};
class MyThreadPool
{
public:
MyThreadPool(size_t size) {
m_threads.resize(size);
for (size_t i = 0; i < size; i++)
m_threads[i] = new MyThread();
}
MyThreadPool() {}
~MyThreadPool() {
Stop();
for (size_t i = 0; i < m_threads.size(); i++)
{
MyThread* pThread = m_threads[i];
m_threads[i] = NULL;
delete pThread;
}
m_threads.clear();
}
//激活线程池
bool Invoke() {
bool ret = true;
for (size_t i = 0; i < m_threads.size(); i++) {
if (m_threads[i]->Start() == false) {
ret = false;
break;
}
}
if (ret == false) {
for (size_t i = 0; i < m_threads.size(); i++) {
m_threads[i]->Stop();
}
}
return ret;
}
void Stop() {
for (size_t i = 0; i < m_threads.size(); i++) {
m_threads[i]->Stop();
}
}
//返回-1 表示分配失败,所有线程都在忙 大于等于0,表示第n个线程分配来做这个事情
int DispatchWorker(const ThreadWorker& worker) {
int index = -1;
m_lock.lock();
for (size_t i = 0; i < m_threads.size(); i++) {
if (m_threads[i] != NULL && m_threads[i]->IsIdle()) {
m_threads[i]->UpdateWorker(worker);
index = i;
break;
}
}
m_lock.unlock();
return index;
}
bool CheckThreadValid(size_t index) {
if (index < m_threads.size()) {
return m_threads[index]->IsValid();
}
return false;
}
public:
std::mutex m_lock;
std::vector<MyThread*> m_threads;
};
----------------------------------------iocpServer.cpp-----------------------------------
#include"ThreadPool.h"
#include <WinSock2.h>
#include <iostream>
#include <map>
#pragma comment(lib, "ws2_32.lib")
using namespace std;
MyThreadPool pool(10);
#define BUF_SIZE 100
#define READ 1
#define WRITE 2
class ClientMessage { //客户端信息
public:
SOCKET hclientSocket;
SOCKADDR_IN clientAddr;
};
class MyOverlapped {
public:
MyOverlapped() {
memset(&m_overlapped, 0, sizeof(m_overlapped));
m_wsabuffer.len = BUF_SIZE;
m_wsabuffer.buf = buffer;
}
public:
OVERLAPPED m_overlapped;
int m_operator;//操作 1 读;2 写
char buffer[BUF_SIZE];//缓冲区
ThreadWorker m_worker;//处理函数
ClientMessage client;
WSABUF m_wsabuffer;
};
int ReadyReadThread(void* arg) {
DWORD flags = 0;
MyOverlapped* overlap = (MyOverlapped*)arg;
memset(&(overlap->m_overlapped), 0, sizeof(OVERLAPPED));
overlap->m_operator = WRITE;
//客户端socket 缓存数组地址 数组长度 保存实际字节数变量的地址 数据传输特性 时间状态 Routine函数
WSASend(overlap->client.hclientSocket, &(overlap->m_wsabuffer), 1, NULL, 0, &(overlap->m_overlapped), NULL);
cout << "发送完数据" << endl;
return 0;
}
int ReadyWriteThread(void* arg) {
DWORD flags = 0;
MyOverlapped* lpoverlap = (MyOverlapped*)arg;
MyOverlapped* overlap = new MyOverlapped();
overlap->m_operator = READ;
overlap->client = lpoverlap->client;
WSARecv(overlap->client.hclientSocket, &(overlap->m_wsabuffer), 1, NULL, &flags, &(overlap->m_overlapped), NULL);
cout << "继续准备接受数据" << endl;
delete lpoverlap;
return 0;
}
unsigned WINAPI EchoThreadMain(LPVOID pComPort)
{
cout << "线程 NO. " << GetCurrentThreadId() << endl;
cout << "EchoThreadMain() 初始化" << endl;
HANDLE hComPort = (HANDLE)pComPort;
SOCKET sock;
DWORD bytesTrans = 0;
ClientMessage* handleInfo;
MyOverlapped* overlap;
DWORD flags = 0;
while (1) {
//在操作队列中获取IO完成的客户端信息
GetQueuedCompletionStatus(hComPort, &bytesTrans, (PULONG_PTR)&handleInfo, (LPOVERLAPPED*)&overlap, INFINITE);
if (bytesTrans != 0) {
sock = handleInfo->hclientSocket;
if (overlap->m_operator == READ) { //判断客户端是读还是写
cout << "准备好已读!" << endl;
cout << overlap->buffer << endl;
overlap->m_wsabuffer.len = bytesTrans;
while (1) {
if (pool.DispatchWorker(ThreadWorker(&ReadyReadThread, overlap)) != -1) {
break;
}Sleep(100);
}
}
else {
cout << "准备好已写!" << endl;
while (1) {
if (pool.DispatchWorker(ThreadWorker(&ReadyWriteThread, overlap)) != -1) {
break;
}Sleep(100);
}
}
}
}
return 0;
}
int main() {
pool.Invoke();
WSADATA wsadata;
HANDLE hComPort; //完成端口句柄
MyOverlapped* overlap; //ioMessage
ClientMessage* handleInfo;//key
SOCKET serverSocket;
SOCKADDR_IN serverAddr;
DWORD recvBytes, flags = 0;
//1.初始化套接字
if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
cout << "WSAStartup() error" << endl;
//2.创建iocp对象(第二个参数为0,所以是新建的一个iocp),最后一个参数为0 创建和CPU核数相同的线程 返回NULL即为创建失败
hComPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (hComPort == NULL) return 0;
for (int i = 0; i < 3; i++) {
_beginthreadex(NULL, 0, EchoThreadMain, (LPVOID)hComPort, 0, NULL);
}
//3.创建服务器的监听套接字
serverSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
if (serverSocket == INVALID_SOCKET)
cout << "socket() error" << endl;
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(8000);
if (bind(serverSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
cout << "bind () error" << endl;
listen(serverSocket, 5);
cout << "服务器启动成功!" << endl;
//4.开始监听
while (1) {
SOCKET clientSocket;
SOCKADDR_IN clientAddr;
int addrLen = sizeof(clientAddr);
cout << "wait connect!" << endl;
clientSocket = accept(serverSocket, (SOCKADDR*)&clientAddr, &addrLen);
if (clientSocket != INVALID_SOCKET) cout << "已有客户端连接:" << clientSocket << endl;
//(1)为该socket增加key->handleInfo
handleInfo = new ClientMessage();
handleInfo->hclientSocket = clientSocket;
memcpy(&(handleInfo->clientAddr), &clientAddr, addrLen);
//(2)加入完成端口
CreateIoCompletionPort((HANDLE)clientSocket, hComPort, (DWORD)handleInfo, 0);//绑定一个客户端socket给完成端口
//(3)准备接收
overlap = new MyOverlapped(); //创建事件对象等捆绑信息的句柄
overlap->m_operator = READ;
overlap->client = *handleInfo;
WSARecv(handleInfo->hclientSocket, &(overlap->m_wsabuffer), 1, &recvBytes, &flags, &(overlap->m_overlapped), NULL);//异步等待接收客户端信息
//参数:1重叠IO套接字 2保存接受信息的结构体数组 3第二个参数数组的长度 4保存接受消息大小的变量地址 5设置或读取传输特性的消息
// 6事件对象的状态 7Routine函数地址
}
}
2 iocp服务器创建步骤
注:(只映射和客户端通信的socket,暂时不考虑服务器监听的socket,因此socket要么准备好读,要么准备好写)
1.创建一个完成端口
2.开启一个(多个)线程来获取队列完成状态,也就是判断完成端口中哪些socke准备好读or写(额外开启线程来处理读写操作)
3.服务器创建监听socket检测客户端连接
4.检测到客户端连接后,将其socket加入到完成端口
iocp为什么采用线程池?
如果服务器每接入一个用户就开启一个线程为其服务,会大大增加服务器的负荷,iocp只有在socke确实需要操作才会额外开启线程,并且线程池可以控制服务器最大开启线程数量