刚开始接触windows iocp 模型,对网上的代码做了比较详细的注释,刚开始学习,有什么不对的地方希望大神们多多指导,以后会持续更新学习心得。
参考的连接为http://www.cnblogs.com/xing901022/archive/2012/10/17/2728316.html
#define BUFFER_SIZE 1024 //定义数据缓存区大小
typedef struct _PER_HANDLE_DATA{ //定义结构体 端口号 地址
SOCKET s;
sockaddr_in addr;
}PER_HANDLE_DATA, *PPER_HANDLE_DATA;
typedef struct _PER_IO_DATA{ 定义结构体 重叠的IO 创建缓存区大小是BUFFER_SIZE
OVERLAPPED ol;
char buf[BUFFER_SIZE];
int nOperationType; //nOperationType可能是一个标志位
#define OP_READ 1
#define OP_WRITE 2
#define OP_ACCEPT 3
}PER_IO_DATA, *PPER_IO_DATA;
void main()
{
int nPort = 4567; //端口号是4567
HANDLE hCompletion = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0); //创建一个未关联任何文件的I/O完成端口
//ExistingCompletionPort是已经存在的完成端口。如果为NULL,则为新建一个IOCP。
//CompletionKey是传送给处理函数的参数。
::CreateThread(NULL, 0, ServerThread, (LPVOID)hCompletion, 0, 0); //创建一个线程 NULL表示使用默认的安全性 0 表示使用与调用者相同的堆栈大小 ServerThread 指向线程函数的指针
::CreateThread(NULL, 0, ServerThread, (LPVOID)hCompletion, 0, 0);
//(LPVOID)hCompletion 强制转换 格式不正确会调用不成功 0 向线程函数传递的参数,是一个指向结构的指针,不需传递参数时,为NULL
// 0 线程标志
//hThread = CreateThread(&security_attributes, dwStackSize, ThreadProc, pParam, dwFlags, &idThread);
SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, 0); //创建监听
//int socket(int domain, int type, int protocol);
// 协议域 指定Socket类型 指定协议 这个例子中表示使用ipv4协议 面向连接的Socket 默认使用第二个参数的协议 用 slitens储存返回值
SOCKADDR_IN si; // //
si.sin_family = AF_INET; //使用ipv4
si.sin_port = htons(nPort); //必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字
si.sin_addr.S_un.S_addr = INADDR_ANY; //表示任意地址
::bind(sListen, (sockaddr*)&si, sizeof(si)); //bind()函数通过给一个未命名套接口分配一个本地名字来为套接口建立本地捆绑(主机地址/端口号)
//套接字描述符 , 一个sockaddr结构指针,该结构中包含了要结合的地址和端口号 , 确定address缓冲区的长度 如果函数执行成功,返回值为0,否则为SOCKET_ERROR。 //将监听
::listen(sListen, 5); //slisten用于标识一个已捆绑未连接套接口的描述字 5 表示最大队列长度
//创建一个套接口并监听申请的连接.
while (TRUE)
{
//等待接收未决的请求
SOCKADDR_IN saRemote; //创建结构体 “ saRemote”
int nRemoteLen = sizeof(saRemote); //储存上面结构体占用字节数
SOCKET sNew = ::accept(sListen, (sockaddr*)&saRemote, &nRemoteLen); //int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//套接字描述符 //储存ip地址,等价于SOCKADDR_IN 保存结构体占用字节数
//创建per-handle
PPER_HANDLE_DATA pPerHandle = (PPER_HANDLE_DATA)::GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA)); // 在堆中分配 一个 ”PER_HANDLE_DATA“ 大小的的内存强制转换成PER_HANDLE_DATA赋值给pPerHandle
pPerHandle->s = sNew;
memcpy(&pPerHandle->addr, &saRemote, nRemoteLen); //内存拷贝 将saRemote 指向的内存块中储存的数据拷贝nRemoteLen数据长度到第一个形参里面
::CreateIoCompletionPort((HANDLE)pPerHandle->s, hCompletion, (DWORD)pPerHandle, 0); //函数关联一个已打开的文件实例和新建的或已存在的I/0完成端口,或者创建一个未关联任何文件的I/O完成端口。
//投递一个接收请求
PPER_IO_DATA pPerIO = (PPER_IO_DATA)::GlobalAlloc(GPTR, sizeof(PER_IO_DATA));
pPerIO->nOperationType = OP_READ;
WSABUF buf;
buf.buf = pPerIO->buf;
buf.len = BUFFER_SIZE;
DWORD dwRecv;
DWORD dwFlags = 0;
::WSARecv(pPerHandle->s, &buf, 1, &dwRecv, &dwFlags, &pPerIO->ol, NULL);//接收投递求
//投递一个发送请求
::WSASend(pPerHandle->s, &buf, 1, &dwRecv, 0, &pPerIO->ol, NULL);
}
}
DWORD WINAPI ServerThread(LPVOID lpParam) //没有类型的指针
{
HANDLE hCompletion = (HANDLE)lpParam; // 得到完成端口对象句柄
DWORD dwTrans; //四个字节的变量
PPER_HANDLE_DATA pPerHandle; //定义一个结构体
PPER_IO_DATA pPerIO; //定义一个结构体
while (TRUE)
{
// 在关联到此完成端口的所有套节字上等待I/O完成
BOOL bOK = ::GetQueuedCompletionStatus(hCompletion, &dwTrans, (LPDWORD)&pPerHandle, (LPOVERLAPPED*)&pPerIO, WSA_INFINITE);
//句柄 一次完成后的I/O操作所传送数据的字节数 当文件I/O操作完成后,用于存放与之关联的CK 为调用IOCP机制所引用的OVERLAPPED结构。 用于指定调用者等待CP的时间
//调用成功,则返回非零数值,相关数据存于lpNumberOfBytes、lpCompletionKey、lpoverlapped变量中。失败则返回零值。
if (!bOK) //如果返回0
{
::closesocket(pPerHandle->s); //它释放套接口描述字s
::GlobalFree(pPerHandle); //释放句柄指向的内存块
::GlobalFree(pPerIO); //释放句柄指向的内存块
continue; //结束本次循环,进入下次循环
}
if (dwTrans == 0 && (pPerIO->nOperationType == OP_READ || pPerIO->nOperationType == OP_WRITE)) // 如果传送字节数等于0 或者操作为读或写
{
::closesocket(pPerHandle->s); //它释放套接口描述字s
::GlobalFree(pPerHandle); //释放句柄指向的内存块
::GlobalFree(pPerIO); //释放句柄指向的内存块
continue; //结束本次循环,进入下次循环
}
switch (pPerIO->nOperationType)
{
case OP_READ: //操作字为读
{
pPerIO->buf[dwTrans] = '\0'; //缓存区最后一个字符为结束符
printf(pPerIO->buf);
WSABUF buf; //用来接收WSASocket数据的缓冲
buf.buf = pPerIO->buf; //记录缓存区首地址
buf.len = BUFFER_SIZE; //记录缓存区的大小
pPerIO->nOperationType = OP_READ;
DWORD nFlags = 0; //定义一个双字
::WSARecv(pPerHandle->s, &buf, 1,
//一个标识已连接套接口的描述字。lpBuffers数组中WSABUF结构的数目。lpBuffers数组中WSABUF结构的数目。
&dwTrans, &nFlags, &pPerIO->ol, NULL);
//如果接收操作立即结束,一个指向本调用所接收的字节数的指针。 一个指向标志位的指针 一个指向重叠结构的指针 一个指向接收操作结束后调用的例程的指针
}
break; //结束switch
case OP_WRITE:
{
//再投递一个发送请求
::WSASend(pPerHandle->s, "&buf ", 1, &dwRecv, 0, &pPerIO->ol, NULL);
}
case OP_ACCEPT:
break; //结束switch
}
}
return 0;
}