手把手教你玩转SOCKET模型之重叠I/O篇(上)
“身为一个初学者,时常能体味到初学者入门的艰辛,所以总是想抽空作点什么来尽我所能的帮助那些需要帮助的人。我也希望大家能把自己的所学和他人一起分享,不要去鄙视别人索取时的贪婪,因为最应该被鄙视的是不肯付出时的吝啬。”
----- 题记 By PiggyXP(小猪)
前 言
其实我首先应该道歉,因为7月份的时候曾信誓旦旦的说要写一套关于SOCKET所有模型的入门文章以及配套代码,不过没想到后天竟然被美女所迷出去度假了,刚刚回来不久。。。。。。-_-b其实那些模型的配套代码我已经基本写完了,只是没写配套文字,不过我想还是先从稍微难一点的模型写起吧,因为其他模型的入门毕竟要简单一些。
不过由于也是初学者,疏漏之处还望不吝指正。
本文凝聚着笔者心血,如要转载,请指明原作者及出处,谢谢!^_^
OK, Let’s go ! Have fun!! q^_^p
本文配套的示例源码下载地址
(VC.net 2003编写的多客户端MFC代码,配有详尽注释,只是简单的显示一下客户端发来的字符,稍加改进就是个聊天室了):
http://www.haha567.com/PiggyXP/OverlappedModel.rar
(unix系统,千万注意链接大小写)
非常感谢网络版的limin兄弟为我无偿提供的空间,以及在我学习过程中给我的大力帮助与支持,真的非常感谢他,感激涕零啊~~~~~T_T
也欢迎大家光临他的Blog一起讨论网络技术
http://blog.haha567.com/
(本文假设你已经具备用SOCKET简单模型编程的能力,如果对SOCKET一无所知请关注本系列其他文章)
目录:
1. 重叠模型的优点
2. 重叠模型的基本原理
3. 关于重叠模型的基础知识
4. 重叠模型的实现步骤
5. 多客户端情况的注意事项
一. 重叠模型的优点
- 可以运行在支持Winsock2的所有windows平台 ,而不像完成端口只是支持NT系统。
- 比起阻塞、select、WSAAsyncSelect以及WSAEventSelect等模型,重叠I/O(Overlapped I/O)模型使应用程序能达到更佳的系统性能。
因为它和这4种模型不同的是,使用重叠模型的应用程序通知缓冲区收发系统直接使用数据,也就是说,如果应用程序投递了一个10KB大小的缓冲区来接收数据,且数据已经到达套接字,则该数据将直接被拷贝到投递的缓冲区。
而这4种模型种,数据到达并拷贝到单套接字接收缓冲区中,此时应用程序会被告知可以读入的容量。当应用程序调用接收函数之后,数据才从单套接字缓冲区拷贝到应用程序的缓冲区,差别就体现出来了。
- 从《windows网络编程》中提供的试验结果中可以看到,在使用了P4 1.7G Xero处理器(CPU很强啊)以及768MB的回应服务器中,最大可以处理4万多个SOCKET连接,在处理1万2千个连接的时候CPU占用率才40% 左右 ―― 非常好的性能,已经直逼完成端口了^_^
二. 重叠模型的基本原理
说了这么多的好处,你一定也跃跃欲试了吧,不过我们还是要先提一下重叠模型的基本原理。
概括一点说,重叠模型是让应用程序使用重叠数据结构(WSAOVERLAPPED),一次投递一个或多个Winsock I/O请求。针对这些提交的请求,在它们完成之后,应用程序会收到通知,于是就可以通过自己另外的代码来处理这些数据了。
需要注意的是,有两个方法可以用来管理重叠IO请求的完成情况(就是说接到重叠操作完成的通知):
- 事件对象通知(event object notification)
- 完成例程(completion routines) ,注意,这里并不是完成端口
而本文只是讲述如何来使用事件通知的的方法实现重叠IO模型,完成例程的方法准备放到下一篇讲 :) (内容太多了,一篇写不完啊) ,如没有特殊说明,本文的重叠模型默认就是指的基于事件通知的重叠模型。
既然是基于事件通知,就要求将windows事件对象与WSAOVERLAPPED结构关联在一起(WSAOVERLAPPED结构中专门有对应的参数),通俗一点讲,就是。。。。对了,忘了说了,既然要使用重叠结构,我们常用的send, sendto, recv, recvfrom也都要被WSASend, WSASendto, WSARecv, WSARecvFrom替换掉了, 它们的用法我后面会讲到,这里只需要注意一点,它们的参数中都有一个Overlapped参数,我们可以假设是把我们的WSARecv这样的操作操作“绑定”到这个重叠结构上,提交一个请求,其他的事情就交给重叠结构去操心,而其中重叠结构又要与windows的事件对象“绑定”在一起,这样我们调用完WSARecv以后就可以“坐享其成”,等到重叠操作完成以后,自然会有与之对应的事件来通知我们操作完成,然后我们就可以来根据重叠操作的结果取得我们想要德数据了。
也许说了半天你还是不大明白,那就继续往后面看吧。。。。。。。-_-b,语言表达能力有限啊~~~
三. 关于重叠模型的基础知识
下面来介绍并举例说明一下编写重叠模型的程序中将会使用到的几个关键函数。
- 1. WSAOVERLAPPED结构
这个结构自然是重叠模型里的核心,它是这么定义的
typedef struct _WSAOVERLAPPED {
DWORD Internal;
DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
WSAEVENT hEvent; // 唯一需要关注的参数,用来关联WSAEvent对象
} WSAOVERLAPPED, *LPWSAOVERLAPPED;
我们需要把WSARecv等操作投递到一个重叠结构上,而我们又需要一个与重叠结构“绑定”在一起的事件对象来通知我们操作的完成,看到了和hEvent参数,不用我说你们也该知道如何来来把事件对象绑定到重叠结构上吧?大致如下:
WSAEVENT event; // 定义事件
WSAOVERLAPPED AcceptOverlapped ; // 定义重叠结构
event = WSACreateEvent(); // 建立一个事件对象句柄
ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED)); // 初始化重叠结构
AcceptOverlapped.hEvent = event; // Done !!
- 2. WSARecv系列函数
在重叠模型中,接收数据就要靠它了,它的参数也比recv要多,因为要用刀重叠结构嘛,它是这样定义的:
int WSARecv(
SOCKET s, // 当然是投递这个操作的套接字
LPWSABUF lpBuffers, // 接收缓冲区,与Recv函数不同
// 这里需要一个由WSABUF结构构成的数组
DWORD dwBufferCount, // 数组中WSABUF结构的数量
LPDWORD lpNumberOfBytesRecvd, // 如果接收操作立即完成,这里会返回函数调用
// 所接收到的字节数
LPDWORD lpFlags, // 说来话长了,我们这里设置为0 即可
LPWSAOVERLAPPED lpOverlapped, // “绑定”的重叠结构
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
// 完成例程中将会用到的参数,我们这里设置为 NULL
);
返回值:
WSA_IO_PENDING : 最常见的返回值,这是说明我们的WSARecv操作成功了,但是
I/O操作还没有完成,所以我们就需要绑定一个事件来通知我们操作何时完成
举个例子:(变量的定义顺序和上面的说明的顺序是对应的,下同)
SOCKET s;
WSABUF DataBuf; // 定义WSABUF结构的缓冲区
// 初始化一下DataBuf
#define DATA_BUFSIZE 5096
char buffer[DATA_BUFSIZE];
ZeroMemory(buffer, DATA_BUFSIZE);
DataBuf.len = DATA_BUFSIZE;
DataBuf.buf = buffer;
DWORD dwBufferCount = 1, dwRecvBytes = 0, Flags = 0;
// 建立需要的重叠结构
WSAOVERLAPPED AcceptOverlapped ;// 如果要处理多个操作,这里当然需要一个
// WSAOVERLAPPED数组
WSAEVENT event; // 如果要多个事件,这里当然也需要一个WSAEVENT数组
// 需要注意的是可能一个SOCKET同时会有一个以上的重叠请求,
// 也就会对应一个以上的WSAEVENT
Event = WSACreateEvent();
ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED));
AcceptOverlapped.hEvent = event; // 关键的一步,把事件句柄“绑定”到重叠结构上
// 作了这么多工作,终于可以使用WSARecv来把我们的请求投递到重叠结构上了,呼。。。。
WSARecv(s, &DataBuf, dwBufferCount, &dwRecvBytes,
&Flags, &AcceptOverlapped, NULL);
其他的函数我这里就不一一介绍了,因为我们毕竟还有MSDN这么个好帮手,而且在讲后面的完成例程和完成端口的时候我还会讲到一些 ^_^
- 3. WSAWaitForMultipleEvents函数
熟悉WSAEventSelect模型的朋友对这个函数肯定不会陌生,不对,其实大家都不应该陌生,这个函数与线程中常用的WaitForMultipleObjects函数有些地方还是比较像的,因为都是在等待某个事件的触发嘛。
因为我们需要事件来通知我们重叠操作的完成,所以自然需要这个等待事件的函数与之配套。
DWORD WSAWaitForMultipleEvents(
DWORD cEvents, // 等候事件的总数量
const WSAEVENT* lphEvents, // 事件数组的指针
BOOL fWaitAll, // 这个要多说两句:
// 如果设置为 TRUE,则事件数组中所有事件被传信的时候函数才会返回
// FALSE则任何一个事件被传信函数都要返回
// 我们这里肯定是要设置为FALSE的
DWORD dwTimeout, // 超时时间,如果超时,函数会返回 WSA_WAIT_TIMEOUT
// 如果设置为0,函数会立即返回
// 如果设置为 WSA_INFINITE只有在某一个事件被传信后才会返回
// 在这里不建议设置为WSA_INFINITE,因为。。。后面再讲吧..-_-b
BOOL fAlertable // 在完成例程中会用到这个参数,这里我们先设置为FALSE
);
返回值:
WSA_WAIT_TIMEOUT :最常见的返回值,我们需要做的就是继续Wait
WSA_WAIT_FAILED : 出现了错误,请检查cEvents和lphEvents两个参数是否有效
如果事件数组中有某一个事件被传信了,函数会返回这个事件的索引值,但是这个索引值需要减去预定义值 WSA_WAIT_EVENT_0才是这个事件在事件数组中的位置。
具体的例子就先不在这里举了,后面还会讲到
注意:WSAWaitForMultipleEvents函数只能支持由WSA_MAXIMUM_WAIT_EVENTS对象定义的一个最大值,是 64,就是说WSAWaitForMultipleEvents只能等待64个事件,如果想同时等待多于64个事件,就要 创建额外的工作者线程,就不得不去管理一个线程池,这一点就不如下一篇要讲到的完成例程模型了。
- 4. WSAGetOverlappedResult函数
既然我们可以通过WSAWaitForMultipleEvents函数来得到重叠操作完成的通知,那么我们自然也需要一个函数来查询一下重叠操作的结果,定义如下
BOOL WSAGetOverlappedResult(
SOCKET s, // SOCKET,不用说了
LPWSAOVERLAPPED lpOverlapped, // 这里是我们想要查询结果的那个重叠结构的指针
LPDWORD lpcbTransfer, // 本次重叠操作的实际接收(或发送)的字节数
BOOL fWait, // 设置为TRUE,除非重叠操作完成,否则函数不会返回
// 设置FALSE,而且操作仍处于挂起状态,那么函数就会返回FALSE
// 错误为WSA_IO_INCOMPLETE
// 不过因为我们是等待事件传信来通知我们操作完成,所以我们这里设
// 置成什么都没有作用…..-_-b 别仍鸡蛋啊,我也想说得清楚一些…
LPDWORD lpdwFlags // 指向DWORD的指针,负责接收结果标志
);
这个函数没什么难的,这里我们也不需要去关注它的返回值,直接把参数填好调用就可以了,这里就先不举例了
唯一需要注意一下的就是如果WSAGetOverlappedResult完成以后,第三个参数返回是 0 ,则说明通信对方已经关闭连接,我们这边的SOCKET, Event之类的也就可以关闭了。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=114883
[推荐本文] [点击此处收藏本文] 发表于 2004年09月23日 10:39 PM
手把手教你玩转SOCKET模型之重叠I/O篇(上)的更多相关文章
- 转:变手把手教你玩转SOCKET模型之重叠I/O篇
手把手教你玩转SOCKET模型之重叠I/O篇 “身为一个初学者,时常能体味到初学者入门的艰辛,所以总是想抽空作点什么来尽我所能的帮助那些需要帮助的人.我也希望大家能把自己的所学和他人一起分享,不要去鄙 ...
- 手把手教你玩转SOCKET模型之重叠I/O篇(下)
四. 实现重叠模型的步骤 作 了这么多的准备工作,费了这么多的笔墨,我们终于可以开始着手编码了.其实慢慢的你就会明白,要想透析重叠结构的内部原理也许是要费点功夫,但是只是学会 如何来使用它,却 ...
- 手把手教你玩转 CSS3 3D 技术
css3的3d起步 要玩转css3的3d,就必须了解几个词汇,便是透视(perspective).旋转(rotate)和移动(translate).透视即是以现实的视角来看屏幕上的2D事物,从而展现3 ...
- 手把手教你玩转CSS3 3D技术
手把手教你玩转 CSS3 3D 技术 要玩转css3的3d,就必须了解几个词汇,便是透视(perspective).旋转(rotate)和移动(translate).透视即是以现实的视角来看屏幕上 ...
- 知识全聚集 .Net Core 技术突破 | 我用C#手把手教你玩微信自动化一
知识全聚集 .Net Core 技术突破 | 我用C#手把手教你玩微信自动化一 教程 01 | 模块化方案一 02 | 模块化方案二 03 | 简单说说工作单元 其他教程预览 分库分表项目实战教程 G ...
- 手把手教你从零写一个简单的 VUE--模板篇
教程目录1.手把手教你从零写一个简单的 VUE2.手把手教你从零写一个简单的 VUE--模板篇 Hello,我又回来了,上一次的文章教会了大家如何书写一个简单 VUE,里面实现了VUE 的数据驱动视图 ...
- 完毕port(CompletionPort)具体解释 - 手把手教你玩转网络编程系列之三
手把手叫你玩转网络编程系列之三 完毕port(Completion Port)具体解释 ...
- 手把手教你玩微信小程序跳一跳
最近微信小程序火的半边天都红了,虽然不会写,但是至少也可以仿照网上大神开发外挂吧!下面手把手教妹纸们(汉纸们请自觉的眼观耳听)怎么愉快的在微信小游戏中获得高分. 废话不多说,咱们这就发车了!呸!咱们这 ...
- (转载)完成端口(CompletionPort)详解 - 手把手教你玩转网络编程系列之三
转自:http://blog.csdn.net/piggyxp/article/details/6922277 前 言 本系列里完成端口的代码在两年前就已经写好了,但是由于许久没有写东西了,不知该如何 ...
随机推荐
- 【转载】Windows 7下使用bcdedit删除多余启动项的命令
在Windows 7中是使用bcdedit来代替Windows XP中的boot.ini bcdedit位置:C:\Windows\System32 (直接使用命令bcdedit即可) bcdedi ...
- Java之网络编程笔记
网络通讯要素: 1.IP地址 IP地址:用于标记一台计算机的身份证. IP地址由网络地址(确定网络)和主机地址(网络中的主机)组成. IP地址分为A类地址.B类地址.C类地址(常用).D类地址.E类地 ...
- urlrewrite 地址重写
环境: Maven 3.0.4 Urlrewrite 2.5.2 Myeclipse 8.6.1 借此机会顺便提一下 Maven Project 的创建,会了的朋友或还不想了解 Maven 的朋友,可 ...
- xcode的ios工程目录结构
目录结构: a.supporting files: main.m和资源文件 xxx-info.plist:包含应用程序相关属性列表,如版本,程序名等 .pch文件:预编译头文件,相当于MFC里的std ...
- mipmap 目录和drawable 目录有什么区别
Q :最近使用studio 发现drawle-hdpi 都没有了换成了mipmap-hdpi,这两个目录有什么区别呢,哪个比较好呢??? A: 我简单总结一下: 使用上没有任何区别,你把它当drawa ...
- 挺好看的CSS
<?php header("content-type:text/html;charset=utf-8"); echo "<style>h1{ color ...
- 服务器 libevent中epoll使用实例demo
名词解释:man epoll之后,得到如下结果: NAME epoll - I/O event notification facility SYNOPSIS #include ...
- 编辑时snapping的添加
原文 编辑时snapping的添加 注意需要在编辑模式下进行snapping的添加(也即先需要使用IEngineEditor进入编辑状态): IMapControl3 mMap = (IMapCont ...
- 俄罗斯方块游戏 --- java
俄罗斯方块游戏 如有疑问请查看:http://zh.wikipedia.org/zh-tw/%E4%BF%84%E7%BD%97%E6%96%AF%E6%96%B9%E5%9D%97 更多疑问请参考: ...
- 开扒php内核函数,第三篇 implode
一开始觉得implode挺容易实现,但是写着写着才发现是挺复杂的,不说啦 来看看implode的用法吧 <?php $arr = array('Hello','World!','Beautifu ...