多线程下的神奇的IOCP
https://blog.csdn.net/lijia626482312/article/details/40858061
一个人从接到项目到昨天终于完成,用了差不多4个月,其中各种心酸和眼泪。我的项目是通过网络从客户端上采集数据,通讯原则是客户端有数据要上传,如果网络允许就连接服务器,首先客户端发送一个消息判断服务器是不是处于忙碌和资源空闲状态,然后发送文件等等。可以说是一个基于C/S模式的多线程socket程序。
我刚开始那到这个项目,我们经理把项目给我一看,你一个人可以做的出来吗,我一看,额。。。。。很简单吗!must,领导要求,必须做出来啊,从此在这个项目陷进去了。。。。。。
由于以前没有做过网络方面的程序,我的开始是从传统模型开始的,就是accept一个连接就开启一个线程,这也是我那到这个项目,觉得简单的地方,很快我就实现了这个问题,我通过连接几台客户端,简单的测试一下,OK,可以正常的采集,基本的功能都可以实现。那好,我把项目一个技术经理,经理一看,小李,你效率挺快的吗,OK,你做个压力测试一下。然后我立马写了一个测试程序,写完后,我自信满满的拿给测试部门测试,我碰到了这个项目的第一个方向问题。测试部门的主管跟我说,客户端连接服务器,30、40台客户端,可以正常运行,客户端数量再往上的话,就比较容易出现乱码(也就是乱序),这个程序拿给用户不行,这是砸我们公司的招牌,我当时就懵了,怎么可能,代码没有问题啊!我回去一查,可能是粘包,我的处理方案是精简代码结构!写好后,我自己模拟用100台,测试了一晚上,没有问题,应该没有问题了吧,然后我又自信满满的拿到测试部门,我又懵了,(⊙o⊙)…,又不行,上面的问题,还是出现了,我想这是人品问题啊,怎么可能,然后在这个问题上,我纠结了差不多快两个月,代码再怎么优化都有问题,我都准备彻底放弃了!!!
一次偶然的机会在网上看到windows操作系统有六种模型:select模型,WSAAsyncSelect异步模型,WSAEventSelect事件模型,重叠I/O事件模型,重叠I/O完成例程,IOCP完成端口。我一个一个模型差不多都用了一遍,好像都不好用,前面4种,有windows平台数量限制(64台客户端),我一看这不行,那就用IOCP吧!
iocp步骤是:
1、创建工作在线程;
SYSTEM_INFO sysinfo;
GetSysteminfo(&sysinfo);
int threadcount = sysinfo.sysinfo.dwNumberOfProcessors*2;
for (DWORD i = 0;i<sysinfo.dwNumberOfProcessors*2;i++)
{
HANDLE m_pWorkThread;
m_pWorkThread = CreateThread(NULL,0,ServerWorkerThread,m_ComletionPort,0,NULL);
CloseHandle(m_pWorkThread);
}
2、创建socket,不同传统模型,m_listenSock = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
3、绑定socket,CreateIoCompletionPort((HANDLE)m_listenSock,m_ComletionPort,0,0);
4、传统模式的bind,listen;
5、accept(acceptex更高效),将iocp绑定客户端socket
SOCKADDR_IN saRemote;
int RemoteLen=sizeof(SOCKADDR_IN);
SOCKET Accept = INVALID_SOCKET;
Accept = WSAAccept(pDlg->m_listenSock,(SOCKADDR*)&saRemote, &RemoteLen,NULL,0));
PerHandleData[index].Socket = Accept;
memcpy(&PerHandleData[index].ClientAddr,&saRemote,RemoteLen);
HANDLE m_retcompletion = CreateIoCompletionPort((HANDLE)PerHandleData[index].Socket,m_ComletionPort,(DWORD)&PerHandleData[index],0);
6、绑定iocp成功后,投递接收请求WSARecv(PerHandleData[index].Socket, &(PerIoData[index].DataBuf), 1, &RecvBytes, &Flags,&(PerIoData[index].Overlapped), NULL);
以上就是iocp的大概步骤,我使用上面的模型创建好iocp模型后,发现确实好用不少,3Mb的短连接数据传输,接收速度比传统模型几乎快了十几秒。
然后我又进行了模拟100台客户端压力测试,发现运行了10几分钟系统出现崩溃,错误提示是:Debug Assertion Failed!File:Dbgheap.c Line:1011,然后我这个问题上又堵了好几天,这是什么情况呢,程序的逻辑是收完一段数据,给变一个标志位,然后通过检索这个标志位,在另外的线程处理,逻辑没有问题啊,原来windows多线程开发是线程不安全的,多线程中共享数据的访问,都要加锁。不然的话,出现内存溢出,数组越界等等奇怪的问题。
还有iocp如何区分每一个socket,我是采用检索全局socket,GetQueuedCompletionStatus被激活后,将接收到的数据放到各自的全局变量区域。全局变量怎么申请呢,如果连接数量够多,申请固定大小的全局内存空间,在程序启动的时候会出现内存不足,解决办法就是在堆中申请内存(也就是malloc,注意malloc之后别忘记free,如果频繁的申请释放内存空间,容易出现磁盘碎片,不鼓励使用这种办法)。
投递多个wsarecv有必要吗,投递多个能充分的使用cpu,这个不可否认,但是投递之后我们需要花更多的内存处理时间去组包和处理数据,如何数据还没有收完的话,还需要进一步的投递多个包,然后再组包和处理数据。。。。。。这样的效率更慢,我觉得一来一回(接收数据先判断自己的数据是否收满,没有收满再投递一个wsarecv)的方式已经可以满足自己的需要了,但是一来一回的处理方式,需要注意,GetQueuedCompletionStatus所在的工作着线程最好是不要做过多的数据处理工作,以免影响客户端发送的太快,服务器接收的太慢(如果出现这个问题,容易引起远程主机主动关闭socket错误)。
iocp中是如何进行超时判断的?服务器资源一定,客户端如果一直连接而不发送数据或者出现各种意外客户端断开连接服务器而没有及时发现,服务器会出现资源不够用的情况,这时就要超时判断。工作者线程中记录wsarecv和WSASend操作的时间,然后再另外开一个线程判断上一次的处理wsarecv和wsasend操作跟当前时间是否超时(可以根据自己需求设定,mfc中可以使用CTimespan::GetTotalSeconds()取得时间差),如果超时就关闭socket,释放相应的内存等等。
与IO打交道,估计是所有iocp的难题,据相关资料记载(我也不知道是在哪里看到的,IO外设的任何操作速度相对cpu处理速度来说,cpu开了1000多倍),而iocp工作者线程池设计目的为了充分发挥cpu的性能,所以工作者线程中最好不要处理IO(这就出现一个问题,空间换时间,内存不够使用)。
工作者不处理IO,那就带来了内存的使用问题,IO处理慢,未处理的IO比较多,未处理内存积压越来越多,内存不够用。32位操作系统,内存使用机制,内核默认2G内存,程序员自己可用2G。可以通过设置c盘的boot.ini隐藏文件设置3GB程序员可用内存(具体方法可自己上网搜索)。
昨天终于写好iocp服务器程序,在内存大小限制情况,短连接,每个连接都只上传6MB以内的数据,能跑500台端机,不过IO处理是关键,如果服务器忙碌的时候,在服务器上做其他操作如刷屏、打印、键盘输入数据等,会出现写文件很慢,内存很容易出现不够用的情况,不过,这已经能满足我的性能需求了。
终于能长长的松一口气。。。。。。好开心啊!
多线程下的神奇的IOCP的更多相关文章
- 多线程下NSOperation、NSBlockOperation、NSInvocationOperation、NSOperationQueue的使用
本篇文章主要介绍下多线程下NSOperation.NSBlockOperation.NSInvocationOperation.NSOperationQueue的使用,列举几个简单的例子. 默认情况下 ...
- python 类变量 在多线程下的共享与释放问题
最近被多线程给坑了下,没意识到类变量在多线程下是共享的,还有一个就是没意识到 内存释放问题,导致越累越大 1.python 类变量 在多线程情况 下的 是共享的 2.python 类变量 在多线程情况 ...
- Java多线程21:多线程下的其他组件之CyclicBarrier、Callable、Future和FutureTask
CyclicBarrier 接着讲多线程下的其他组件,第一个要讲的就是CyclicBarrier.CyclicBarrier从字面理解是指循环屏障,它可以协同多个线程,让多个线程在这个屏障前等待,直到 ...
- Java多线程20:多线程下的其他组件之CountDownLatch、Semaphore、Exchanger
前言 在多线程环境下,JDK给开发者提供了许多的组件供用户使用(主要在java.util.concurrent下),使得用户不需要再去关心在具体场景下要如何写出同时兼顾线程安全性与高效率的代码.之前讲 ...
- 多线程下C#如何保证线程安全?
多线程编程相对于单线程会出现一个特有的问题,就是线程安全的问题.所谓的线程安全,就是如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码.如果每次运行结果和单线程运行的结果是 ...
- 多线程下HashMap的死循环问题
多线程下[HashMap]的问题: 1.多线程put操作后,get操作导致死循环.2.多线程put非NULL元素后,get操作得到NULL值.3.多线程put操作,导致元素丢失. 本次主要关注[Has ...
- ASP.NET多线程下使用HttpContext.Current为null解决方案 2015-01-22 15:23 349人阅读 评论(0) 收藏
问题一:多线程下获取文件绝对路径 当我们使用HttpContext.Current.Server.MapPath(strPath)获取绝对路径时HttpContext.Current为null,解决办 ...
- ASP.NET多线程下使用HttpContext.Current为null解决方案 2015-01-22 15:23 350人阅读 评论(0) 收藏
问题一:多线程下获取文件绝对路径 当我们使用HttpContext.Current.Server.MapPath(strPath)获取绝对路径时HttpContext.Current为null,解决办 ...
- ASP.NET多线程下使用HttpContext.Current
本来要实现asp.net下使用tcp通讯方式向服务器获取数据,开始采用的方式是 参考: ASP.NET多线程下使用HttpContext.Current为null解决方案 http://www.cnb ...
随机推荐
- 一款基于SSM框架技术的全栈Java web项目(已部署可直接体验)
概述 此项目基于SSM框架技术的Java Web项目,是全栈项目,涉及前端.后端.插件.上线部署等各个板块,项目所有的代码都是自己编码所得,每一步.部分都有清晰的注释,完全不用担心代码混乱,可以轻松. ...
- 【转载】php如何给APP端写接口
如何写好接口 目的:清楚明白所写接口的用途 安全性:做好接口的安全性,防止接口数据泄露,做好必要的参数加密措施 按需分配: 接受值和返回值要实用,不接受和返回不需要的数据,返回值返回什么应与使用者时时 ...
- 阿里云RDS实例内不同数据库之间的数据迁移
适用场景 本文适用于使用DTS实现相同实例下库名不同的数据库之间的数据迁移.本文以使用DTS将同一RDS实例下的amptest库迁移到jiangliu_amptest库为例来说明如何使用DTS实现相同 ...
- dbcp2连接池获取数据库连接Connection
一.先来看看手工创建的方式 public static Connection getConnection() { Connection conn = null; try { Class.forName ...
- [Jobdu] 题目1544:数字序列区间最小值
题目描述: 给定一个数字序列,查询任意给定区间内数字的最小值. 输入: 输入包含多组测试用例,每组测试用例的开头为一个整数n(1<=n<=100000),代表数字序列的长度.接下去一行给出 ...
- 【Android】13.3 使用SQLite.NET-PCL访问SQLite数据库
分类:C#.Android.VS2015: 创建日期:2016-02-26 一.简介 本章开头已经说过了,SQLite.NET-PCL用起来很爽,这一节咱们看看怎样使用吧. 二.示例3运行截图 下面左 ...
- 深入理解Spark 2.1 Core (十一):Shuffle Reduce 端的原理与源代码分析
http://blog.csdn.net/u011239443/article/details/56843264 在<深入理解Spark 2.1 Core (九):迭代计算和Shuffle的原理 ...
- IIS添加域名
前提:域名可用 1.打开网站,点击右侧 绑定 2.添加域名 点击确定 3.结果: ok 配置完成.
- Ubuntu 下添加OpenERP command 快捷启动方式
编辑home目录下的.bashrc文件 alias xjerp="~/odoo/xj/openerp-server -r openerp --addons-path='~/odoo/xj/o ...
- Android——onCreate( )方法详解(转)
android开发之onCreate( )方法详解 onCreate( )方法是android应用程序中最常见的方法之一,那么,我们在使用onCreate()方法的时候应该注意哪些问题呢? 先看看Go ...