博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
IOCP基础封装
阅读量:6279 次
发布时间:2019-06-22

本文共 10732 字,大约阅读时间需要 35 分钟。

前几篇文章介绍了集中linux epoll的封装方式,本篇回归windows,介绍一个IOCP的封装。

首先介绍要达到的目的:

1) 导出基本接口,作为一个更高层跨平台网络库的基础组件

2) 导出的接口用法与epoll版本大致相当,以方便日后的跨平台封装

3) 默认的使用方式是单线程的,与epoll一致。

 

下面是接口文件:

/*        Copyright (C) <2012>  
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see
.*/ #ifndef _KENDYNET_H#define _KENDYNET_H//重叠结构struct OverLapContext{ OVERLAPPED m_overLapped; WSABUF* wbuf; DWORD buf_count; unsigned char m_Type;};struct Socket;//socket的简单封装typedef struct Socket{ SOCKET sock; HANDLE complete_port; void (*RecvFinish)(struct Socket*,struct OverLapContext*,DWORD); void (*SendFinish)(struct Socket*,struct OverLapContext*,DWORD);}*Socket_t;int InitNetSystem();void CleanNetSystem();HANDLE CreateNetEngine(DWORD NumberOfConcurrentThreads);void CloseNetEngine(HANDLE);int RunEngine(HANDLE,DWORD timeout);int Bind2Engine(HANDLE,Socket_t);//now表示是否立即发起操作int WSA_Send(Socket_t,struct OverLapContext*,int now,DWORD *lastErrno);int WSA_Recv(Socket_t,struct OverLapContext*,int now,DWORD *lastErrno);#endif

 

其中的Engine就是IOCP,使用方式就是RunEngine,在函数内部调用GetQueuedCompletionStatus以处理事件.事件处理完成后,

回调RecvFinish/SendFinish.在简单的单线程程序中,只需要创建一个engine,并在主线程调用RunEngine即可.当然,因为完成

队列其实是线程安全的,所以多个线程同时对一个IOCP调用engine也是可以的。但我不建议这么做,如果需要多线程,可以创建

N个engine和工作线程,各工作线程在自己的engine上执行RunEngine即可(与epoll版本一致).

下面贴出实现:

#include 
#include
#include
#include "KendyNet.h"#include "stdio.h"enum{ IO_RECVREQUEST = 1<<1, //应用层接收请求 IO_SENDREQUEST = 1<<3, //应用层发送请求 IO_RECVFINISH = 1<<2,//接收完成 IO_SENDFINISH = 1<<4, //发送完成};enum{ IO_RECV = (1<<1) + (1<<2), IO_SEND = (1<<3) + (1<<4), IO_REQUEST = (1<<1) + (1<<3),};int InitNetSystem(){ int nResult; WSADATA wsaData; nResult = WSAStartup(MAKEWORD(2,2), &wsaData); if (NO_ERROR != nResult) { printf("\nError occurred while executing WSAStartup()."); return -1; //error } return 0;}void CleanNetSystem(){ WSACleanup();}HANDLE CreateNetEngine(DWORD NumberOfConcurrentThreads){ HANDLE CompletePort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, NumberOfConcurrentThreads); if(NULL == CompletePort) { printf("\nError occurred while creating IOCP: %d.", WSAGetLastError()); } return CompletePort;}void CloseNetEngine(HANDLE CompletePort){ CloseHandle(CompletePort);}static int raw_Send(Socket_t s,struct OverLapContext *overLapped,DWORD *lastErrno){ DWORD dwFlags = 0; DWORD dwBytes = 0; int nBytesSend = WSASend(s->sock, overLapped->wbuf, overLapped->buf_count, &dwBytes, dwFlags, (OVERLAPPED*)overLapped, NULL); if(SOCKET_ERROR == nBytesSend) *lastErrno = WSAGetLastError(); return dwBytes;}static int raw_Recv(Socket_t s,struct OverLapContext *overLapped,DWORD *lastErrno){ DWORD dwFlags = 0; DWORD dwBytes = 0; int nBytesSend = WSARecv(s->sock, overLapped->wbuf, overLapped->buf_count, &dwBytes, &dwFlags, (OVERLAPPED*)overLapped, NULL); if(SOCKET_ERROR == nBytesSend) *lastErrno = WSAGetLastError(); return dwBytes;}typedef void (*CallBack)(struct Socket*,struct OverLapContext*,DWORD);int RunEngine(HANDLE CompletePort,DWORD timeout){ DWORD bytesTransfer; Socket_t socket; struct OverLapContext *overLapped = 0; DWORD lastErrno = 0; BOOL bReturn; CallBack call_back; for( ; ; ) { call_back = 0; bReturn = GetQueuedCompletionStatus( CompletePort,&bytesTransfer, (LPDWORD)&socket, (OVERLAPPED**)&overLapped,timeout); if(FALSE == bReturn || socket == NULL || overLapped == NULL) { break; } if(0 == bytesTransfer) { //连接中断 if(overLapped->m_Type & IO_RECV) call_back = socket->RecvFinish; else call_back = socket->SendFinish; bytesTransfer = 0; } else { if(overLapped->m_Type & IO_REQUEST) { overLapped->m_Type = overLapped->m_Type << 1; if(overLapped->m_Type & IO_RECVFINISH) bytesTransfer = raw_Recv(socket,overLapped,&lastErrno); else if(overLapped->m_Type & IO_SENDFINISH) bytesTransfer = raw_Send(socket,overLapped,&lastErrno); else { //出错 continue; } if(bytesTransfer <= 0 && lastErrno != 0 && lastErrno != WSA_IO_PENDING) bytesTransfer = 0; else continue; } if(overLapped->m_Type & IO_RECVFINISH) call_back = socket->RecvFinish; else if(overLapped->m_Type & IO_SENDFINISH) call_back = socket->SendFinish; else { //出错 continue; } } if(call_back) call_back(socket,overLapped,bytesTransfer); } return 0;}int Bind2Engine(HANDLE CompletePort,Socket_t socket){ HANDLE hTemp; if(!socket->RecvFinish || !socket->SendFinish) return -1; hTemp = CreateIoCompletionPort((HANDLE)socket->sock, CompletePort,(ULONG_PTR)socket, 0); if (NULL == hTemp) return -1; socket->complete_port = CompletePort; return 0;}int WSA_Send(Socket_t socket,struct OverLapContext *OverLap,int now,DWORD *lastErrno){ if(!socket->complete_port) return -1; ZeroMemory(&OverLap->m_overLapped, sizeof(OVERLAPPED)); if(!now) { OverLap->m_Type = IO_SENDREQUEST; PostQueuedCompletionStatus(socket->complete_port,1,(ULONG_PTR)socket,(OVERLAPPED*)OverLap); } else { OverLap->m_Type = IO_SENDFINISH; return raw_Send(socket,overLapped,lastErrno); } return 0;}int WSA_Recv(Socket_t socket,struct OverLapContext *OverLap,int now,DWORD *lastErrno){ if(!socket->complete_port) return -1; ZeroMemory(&OverLap->m_overLapped, sizeof(OVERLAPPED)); if(!now) { OverLap->m_Type = IO_RECVREQUEST; PostQueuedCompletionStatus(socket->complete_port,1,(ULONG_PTR)socket,(OVERLAPPED*)OverLap); return 0; } else { OverLap->m_Type = IO_RECVFINISH; return raw_Recv(socket,overLapped,lastErrno); }}

 

代码一点点,不做介绍了,下面是一个简单的echo测试

#include 
#include
#include
#include
#include "KendyNet.h"struct connection{ struct Socket socket; WSABUF wsendbuf; WSABUF wrecvbuf; char sendbuf[128]; char recvbuf[128]; struct OverLapContext send_overlap; struct OverLapContext recv_overlap;};void RecvFinish(struct Socket *s,struct OverLapContext *overLap,DWORD bytestransfer){ struct connection *c = (struct connection*)s; if(bytestransfer <= 0) free(c); else { DWORD lastErrno = 0; memcpy(c->sendbuf,c->recvbuf,bytestransfer); c->wsendbuf.len = bytestransfer; WSA_Send(s,&c->send_overlap,1,&lastErrno); }}void SendFinish(struct Socket *s,struct OverLapContext *overLap,DWORD bytestransfer){ struct connection *c = (struct connection*)s; if(bytestransfer <= 0) free(c); else { DWORD lastErrno = 0; WSA_Recv((Socket_t)c,&c->recv_overlap,1,&lastErrno); }}struct connection* CreateConnection(SOCKET s){ struct connection *c; c = malloc(sizeof(*c)); ZeroMemory(c, sizeof(*c)); c->socket.sock = s; c->socket.RecvFinish = &RecvFinish; c->socket.SendFinish = &SendFinish; c->wrecvbuf.buf = c->recvbuf; c->wrecvbuf.len = 128; c->recv_overlap.buf_count = 1; c->recv_overlap.wbuf = &c->wrecvbuf; c->wsendbuf.buf = c->sendbuf; c->wsendbuf.len = 0; c->send_overlap.buf_count = 1; c->send_overlap.wbuf = &c->wsendbuf; return c;}DWORD WINAPI Listen(void *arg){ HANDLE *iocp = (HANDLE*)arg; HANDLE complete_port = *iocp; struct sockaddr_in addr; int optval=1; //Socket属性值 unsigned long ul=1; struct linger lng; struct sockaddr_in ClientAddress; int nClientLength = sizeof(ClientAddress); struct connection *c; SOCKET ListenSocket; ListenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED); if (INVALID_SOCKET == ListenSocket) { printf("\nError occurred while opening socket: %d.", WSAGetLastError()); return 0; } addr.sin_family = AF_INET; addr.sin_port = htons(8010); addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); setsockopt(ListenSocket,SOL_SOCKET,SO_EXCLUSIVEADDRUSE,(char*)&optval,sizeof(optval)); //禁止端口多用 setsockopt(ListenSocket,IPPROTO_TCP,TCP_NODELAY,(char*)&optval,sizeof(optval)); //不采用延时算法 setsockopt(ListenSocket,SOL_SOCKET,SO_DONTLINGER,(char*)&optval,sizeof(optval)); //执行立即关闭 lng.l_linger=0; lng.l_onoff=1; setsockopt(ListenSocket,SOL_SOCKET,SO_LINGER,(char*)&lng,sizeof(lng)); //禁止重用本地端口 if ((bind(ListenSocket, (struct sockaddr *)&addr, sizeof( struct sockaddr_in))) == SOCKET_ERROR) { closesocket(ListenSocket); return 0; } if((listen(ListenSocket, 5)) == SOCKET_ERROR) { closesocket(ListenSocket); return 0; } printf("listener 启动\n"); while(1) { SOCKET client = accept(ListenSocket, (struct sockaddr*)&ClientAddress, &nClientLength); if (INVALID_SOCKET == client) { continue; } if(ioctlsocket(client,FIONBIO,(unsigned long*)&ul)==SOCKET_ERROR) { closesocket(client); continue; } c = CreateConnection(client); Bind2Engine(complete_port,(Socket_t)c); //发出第一个读请求 WSA_Recv((Socket_t)c,&c->recv_overlap,0,0); } return 0;}int main(){ HANDLE iocp; DWORD dwThread; InitNetSystem(); iocp = CreateNetEngine(0); CreateThread(NULL,0,Listen,&iocp,0,&dwThread); while(1) RunEngine(iocp,10); return 0;}

转载于:https://www.cnblogs.com/sniperHW/archive/2012/05/04/2482870.html

你可能感兴趣的文章
地包天如何矫正?
查看>>
中间件
查看>>
Android SharedPreferences
查看>>
css面试题
查看>>
Vue组建通信
查看>>
用CSS画一个带阴影的三角形
查看>>
前端Vue:函数式组件
查看>>
程鑫峰:1.26特朗.普力挺美元力挽狂澜,伦敦金行情分析
查看>>
safari下video标签无法播放视频的问题
查看>>
01 iOS中UISearchBar 如何更改背景颜色,如何去掉两条黑线
查看>>
对象的继承及对象相关内容探究
查看>>
Spring: IOC容器的实现
查看>>
Serverless五大优势,成本和规模不是最重要的,这点才是
查看>>
Nginx 极简入门教程!
查看>>
iOS BLE 开发小记[4] 如何实现 CoreBluetooth 后台运行模式
查看>>
Item 23 不要在代码中使用新的原生态类型(raw type)
查看>>
为网页添加留言功能
查看>>
JavaScript—数组(17)
查看>>
Android 密钥保护和 C/S 网络传输安全理论指南
查看>>
以太坊ERC20代币合约优化版
查看>>