最基础的IOCP例子, 没有使用扩展函数AcceptEx: IOCP模型
* 关于iocp的核心就一点:
GetQueuedCompletionStatus 将携带返回2个重要的参数, 一个lpCompletionKey, 一个lpOverlapped.
lpCompletionKey : 是 CreateIoCompletionPort((HANDLE)clientSocket , hIOCP,(ULONG_PTR)自定义的结构,0); 跟
iocp绑定的一个自定义参数;
lpOverlapped : 是传递给 WSASend / WSARecv 的参数;
这2个参数最终会被GetQueuedCompletionStatus 携带回来.
同样的 , AcceptEx 也要传递一个Overlapped结构,现在问题来了,如果只调用了AcceptEx ,
GetQueuedCompletionStatus 是不会返回的, 因为只有跟 iocp 关联(CreateIoCompletionPort)的HANDLE / SOCKET 才会
被触发, 因此只需要把 监听套接字 跟iocp 关联即可;
下面代码使用了AccpetEx 和一个用于获取地址的扩展函数[此函数可以先忽略].
总体来说就是预先分配一些socket , 以及相关的内存块[到时有客户进来后,直接使用此内存块接受数据];
不再让accept系统调用来创建socket了.
所有需要注意的点都写在注释里了.
下面代码里没有使用 CancelIo 之类的函数,如果实际需要直接用 CancelIoEx 来取消无关线程的Overlapped操作,
另:在发送数据[WSASend] 完成后 , 需要检查是否发送完成, 如果没有发完需要继续发送.
#include <mswsock.h>
#include <deque>
#include <vector>
#define IO_ACCEPT 1
#define IO_READ 2
#define IO_WRITE 3
#define BUFFSIZE 4096
#define SPINCOUNT 4000
struct Per_IO_Data;
std::deque<Per_IO_Data*> io_pool;
CRITICAL_SECTION io_pool_cs;
LPFN_ACCEPTEX FuncAcceptEx = NULL;
LPFN_GETACCEPTEXSOCKADDRS FuncGetAddr = NULL;
struct Per_Sock_Data{
SOCKET sock;
SOCKADDR_IN addr;
HANDLE iocp;
Per_Sock_Data():sock(INVALID_SOCKET), iocp(INVALID_HANDLE_VALUE){}
};
struct Per_IO_Data{
OVERLAPPED ol;
WSABUF wsabuf;
char *buf;
int ioMode; //读 写 接受
SOCKET sAcceptSock; //acceptex 预先创建的socket
int nTotalBytes;
int nSendBytes;
Per_IO_Data(): buf(NULL) ,ioMode(-1) , sAcceptSock(INVALID_SOCKET)
,nSendBytes(0), nTotalBytes(0)
{
memset(&ol, 0 , sizeof(ol));
}
~Per_IO_Data(){
if(buf) delete buf;
}
};
void resetPerIOData(Per_IO_Data * pdata , int mode = -1){
if(!pdata) return;
memset(&pdata->ol, 0 ,sizeof(OVERLAPPED));
if(pdata->buf)
memset(&pdata->buf,0,sizeof (char) * BUFFSIZE);
pdata->wsabuf.buf=pdata->