分享我写的IOCP:源码+思路
首先说明,下面的代码仅是一个IOCP的demo,很多地方的设计非常差,当然也有一些设计还算可以:)。此篇仅供对IOCP有些了解但又不深入的、需要一个稍微完整示例的、对网络编程感兴趣的同学参考。点击这里下载代码
整个程序的流程如下:

流程完全是无阻塞的,主线程里,将收到的消息全都一次性取出后,然后派发。所有欲发送的消息都缓存起来,等到更新的时候一起发送。有些地方代码没有完善,比如断开连接后,socket、内存等资源的关闭回收。要注意MAXRECEIVEDBUFFLENGTH这个宏,它是定义每个socket消息收发时缓冲区的大小。如果很大,会非常吃内存的。我在这里也没有做粘包、分包的情况处理。工程还带了一个C#写的测试客户端。在我的机器上,能有12W的连接。

关键代码如下:
void MyIOCP::Execute()
{
PerHandleData* pPerHandleData = nullptr;
PerIOData* pPerIOData = nullptr;
LPOVERLAPPED lpOverLapped = nullptr;
DWORD byteTransferred;
BOOL bRet = FALSE; while (true)
{
bRet = GetQueuedCompletionStatus(mCompletionPort,
&byteTransferred,
(PULONG_PTR)&pPerHandleData,
/*(LPOVERLAPPED*)&pPerIOData*/&lpOverLapped,
INFINITE);
pPerIOData = (PerIOData*)CONTAINING_RECORD(lpOverLapped, PerIOData, overlapped);
if (bRet == FALSE)
{
if (pPerIOData == nullptr)
{
// 退出线程。服务器要关了。。
break;
}
// 对方主动断开了 continue;
} // 处理成功的完成端口请求
switch (pPerIOData->operationType)
{
case CDH::E_INVALID:
{
}
break;
case CDH::E_ACCEPT:
{
/************************************************************************/
/*
inet_ntoa(ClientAddr->sin_addr) 是客户端IP地址 ntohs(ClientAddr->sin_port) 是客户端连入的端口 inet_ntoa(LocalAddr ->sin_addr) 是本地IP地址 ntohs(LocalAddr -AZ>sin_port) 是本地通讯的端口 pIoContext->m_wsaBuf.buf 是存储客户端发来第一组数据的缓冲区
*/
/************************************************************************/
SOCKADDR_IN* ClientAddr = NULL;
SOCKADDR_IN* LocalAddr = NULL;
int remoteLen = sizeof(SOCKADDR_IN), localLen = sizeof(SOCKADDR_IN);
mlpfnGetAcceptExSockAddrs(pPerIOData->buffer, , sizeof(SOCKADDR_IN)+, sizeof(SOCKADDR_IN)+, (LPSOCKADDR*)&LocalAddr, &localLen, (LPSOCKADDR*)&ClientAddr, &remoteLen); WrapSocket* connectedSocket = GetSocketAndEraseFromTheMap(pPerIOData->remoteSocket, WRAPESOCKETTYPE::E_READYTOBECONNECTED);
if (connectedSocket == nullptr)
{
closesocket(pPerIOData->remoteSocket);
continue;
}
if (!AssociateDeviceWithCompletionPort(connectedSocket))
{
closesocket(pPerIOData->remoteSocket);
continue;
}
// 添加到已经连接列表
InsertWrapSocketToMap(connectedSocket, WRAPESOCKETTYPE::E_CONNECTED); PostReceive(connectedSocket); // 这次消耗了一个准备好了的socket, 现在再生成一个socket待连接。
PostAcceptEx(connectedSocket->GetPerHandleData().hasListenSocket);
}
break;
case CDH::E_RECV:
{
int socket = pPerHandleData->selfSocket;
WrapSocket* wrapSocket = nullptr;
GetSocketForSendOrRecvData(socket, wrapSocket); if (wrapSocket != nullptr)
{
wrapSocket->GetPerIODataReceive().bufferLen = byteTransferred;
Receive(wrapSocket);
}
}
break;
case CDH::E_SEND:
{
int socket = pPerHandleData->selfSocket;
WrapSocket* wrapSocket = nullptr;
GetSocketForSendOrRecvData(socket, wrapSocket);
if (wrapSocket != nullptr)
{
wrapSocket->SendingData(false);
} }
break;
case CDH::E_CONNECT:
break;
default:
break;
} }
}
注意CDH::E_RECV,收到消息后,将消息缓存,然后直接又进行投递PostReceive(),这其实是非常不好的。应该分情况来选择是否立即投递。
以上,整个代码,基本是用C++的格式写C代码。希望以后能有机会与大家共同分享一个比较完整的、面向对象风格的IOCP。
分享我写的IOCP:源码+思路的更多相关文章
- 【学】jQuery的源码思路1——后代选择器
jQuery的源码思路1--后代选择器 这里探讨一下jQuery中后代选择器的封装原理,并自己写一下 getEle('#div1 ul li .box');接受的参数就是个后代选择器,类似于这样: # ...
- 手写Koa.js源码
用Node.js写一个web服务器,我前面已经写过两篇文章了: 第一篇是不使用任何框架也能搭建一个web服务器,主要是熟悉Node.js原生API的使用:使用Node.js原生API写一个web服务器 ...
- 手写@koa/router源码
上一篇文章我们讲了Koa的基本架构,可以看到Koa的基本架构只有中间件内核,并没有其他功能,路由功能也没有.要实现路由功能我们必须引入第三方中间件,本文要讲的路由中间件是@koa/router,这个中 ...
- 【学】jQuery的源码思路2——$符号是如何封装的
jQuery中的$符号功能很强大,原因在于对函数参数的个数以及种类的控制,还有对于面向对象思想的运用 function jQuery(args){ //接受参数,并对其判断 this.elements ...
- 分享45个android实例源码,很好很强大
分享45个android实例源码,很好很强大 http://www.apkbus.com/android-20978-1-1.html 分享45个android实例源码,很好很强大http://www ...
- C写的扫描器源码
Title:C写的扫描器源码 --2010-10-27 20:02 无意间看见的一个源代码,弄回来读下. ----------------------------------------------- ...
- 分享45个android实例源码,很好很强大.收藏吧!!!
andriod闹钟源代码 http://www.apkbus.com/android-20974-1-1.html android源码分享之指南针程序 http://www.apkbus.com/an ...
- Visual Studio 2015开发Qt项目实战经验分享(附项目示例源码)
Visual Studio 2015开发Qt项目实战经验分享(附项目示例源码) 转 https://blog.csdn.net/lhl1124281072/article/details/800 ...
- 【腾讯Bugly干货分享】微信iOS SQLite源码优化实践
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57b58022433221be01499480 作者:张三华 前言 随着微信iO ...
- 手写Express.js源码
上一篇文章我们讲了怎么用Node.js原生API来写一个web服务器,虽然代码比较丑,但是基本功能还是有的.但是一般我们不会直接用原生API来写,而是借助框架来做,比如本文要讲的Express.通过上 ...
随机推荐
- Struts2之异常处理
一.学习案例:通过在input.jsp页面输入登录账号和password測试异常处理机制. 二.案例分析:struts2提供了局部异常处理机制和全局异常处理机制.局部优先于全局异常处理,当异常找不到局 ...
- Android下将图片载入到内存中
Android的系统的标准默认每一个应用程序分配的内存是16M.所以来说是很宝贵的,在创建应用的时候要尽可能的去节省内存,可是在载入一些大的文件的时候,比方图片是相当耗内存的,一个1.3M的图片,分辨 ...
- jquery中获取当前点击对象
jquery中获取当前点击对象的简单方法就是,在点击事件click中传入event对象 click(function(event)); 调用当前对象就是$(event.target);
- C++ 排序函数 sort(),qsort()的使用方法
想起来自己天天排序排序,冒泡啊,二分查找啊,结果在STL中就自带了排序函数sort,qsort,总算把自己解脱了~ 所以自己总结了一下,首先看sort函数见下表: 函数名 功能描写叙述 sort 对给 ...
- UEditor-JSP版部署说明
昨天项目中需要在UEditor用到图片上传功能,于是昨天中午便开始做这一块的整合,一不小心就弄到了现在, 坑还挺多的,比如一开始一直报ActionEnter cannot be resolved to ...
- QT的三种协议说明
关于Qt的三种协议以及是否收费,有以下引文: 引文一: 最近一直在学习 Qt.Qt 有两个许可证:LGPL 和商业协议.这两个协议在现在的 Qt 版本中的代码是完全一致的(潜在含义是,Qt 的早期版本 ...
- const int * pi/int * const pi的区别
前面有一篇文章:数组名就是常量指针 参考文章:http://blog.pfan.cn/whyhappy/5164.html const int * pi .int const * pi与int * ...
- eclipse 的小技巧
1. ctrl+o:快速outline 如果想要查看当前类的方法或某个特定方法,但又不想把代码拉上拉下,也不想使用查找功能的话,就用ctrl+o吧.它可以列出当前类中的所有方法及属性,你只需输入你想要 ...
- 中兴电信光纤猫F612管理员密码获取方法
1.telnet 192.168.1.1 账号:root 密码:Zte521 2.输入命令: sendcmd 1 DB p DevAuthInfo 得到管理员账号密码如下: <DM name=& ...
- BootStrap2学习日记8---表单
<form> <label for="username">用户名</label> <input id="username&quo ...