观's profileTo the world,you may jus...PhotosBlogListsMore ![]() | Help |
|
03/11/2009 SOCKET模型之重叠I/O最近的项目中有WIFI模块来和其他设备进行音视频的通信,在网络编程中发现有很多东西需要学习,所以就想记录下来。整合了网上的一些文章。 目录:
一. 重叠模型的优点
二. 重叠模型的基本原理 说了这么多的好处,你一定也跃跃欲试了吧,不过我们还是要先提一下重叠模型的基本原理。概括一点说,重叠模型是让应用程序使用重叠数据结构(WSAOVERLAPPED),一次投递一个或多个Winsock I/O请求。针对这些提交的请求,在它们完成之后,应用程序会收到通知,于是就可以通过自己另外的代码来处理这些数据了。需要注意的是,有两个方法可以用来管理重叠IO请求的完成情况(就是说接到重叠操作完成的通知):
而本文只是讲述如何来使用事件通知的的方法实现重叠IO模型,完成例程的方法准备放到下一篇讲 :) (内容太多了,一篇写不完啊) ,如没有特殊说明,本文的重叠模型默认就是指的基于事件通知的重叠模型。既然是基于事件通知,就要求将Windows事件对象与WSAOVERLAPPED结构关联在一起(WSAOVERLAPPED结构中专门有对应的参数),通俗一点讲,就是。。。。对了,忘了说了,既然要使用重叠结构,我们常用的send, sendto, recv, recvfrom也都要被WSASend, WSASendto, WSARecv, WSARecvFrom替换掉了, 它们的用法我后面会讲到,这里只需要注意一点,它们的参数中都有一个Overlapped参数,我们可以假设是把我们的WSARecv这样的操作操作“绑定”到这个重叠结构上,提交一个请求,其他的事情就交给重叠结构去操心,而其中重叠结构又要与Windows的事件对象“绑定”在一起,这样我们调用完WSARecv以后就可以“坐享其成”,等到重叠操作完成以后,自然会有与之对应的事件来通知我们操作完成,然后我们就可以来根据重叠操作的结果取得我们想要的数据了。 三. 关于重叠模型的基础知识 下面来介绍并举例说明一下编写重叠模型的程序中将会使用到的几个关键函数。 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 5096char 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同时会有一个以上的重叠请求,也就会对应一个以上的WSAEVENTEvent = 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,因为。。。后面再讲吧..-_-bBOOL fAlertable // 在完成例程中会用到这个参数,这里我们先设置为FALSE);返回值:WSA_WAIT_TIMEOUT :最常见的返回值,我们需要做的就是继续WaitWSA_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之类的也就可以关闭了。TrackbacksThe trackback URL for this entry is: http://giwawe.spaces.live.com/blog/cns!92AFEF096943066B!255.trak Weblogs that reference this entry
|
|
|