重叠IO(overlapped IO)是可以是应用程序达到好的系统性能;
设计的原理便是:让应用程序使用一个重叠的数据结构,一次投递一个或者多个IO请求。
1.创建一个套接字,并在指定的端口上监听。
使用下面的函数,并设置WSA_FLAG_OVERLAPPED这个标志创建套接字,创建套接字时候假如使用的是socket则默认设置好了该标志。
s=WSASocket(AF_INET,SOCK_STREAM,0,WSA_FLAG_OVERLAPPED);
2.创建成功以后,将其与本地的接口绑定到一起,便可以进行重叠的IO操作。
3.接收一个进入的请求。
4.为接受的套接字新建一个WSAOVERLAPPED结构,并为该结构分配一个事件对象句柄,把该句柄分配一个事件数组,以便稍后在WSAWaitForMultipleEvents中使用。
结构体的说明:
typedef struct _WSAOVERLAPPED
{ ULONG_PTR Internal;
ULONG_PTR InternalHigh;
union
{
struct
{
DWORD Offset;
DWORD OffsetHigh; //前4个参数有系统内部使用,程序不直接使用处理
};
PVOID Pointer;
};
HANDLE hEvent;//允许将一个套接字和和事件关联在一起;
} WSAOVERLAPPED, *LPWSAOVERLAPPED;
5.在套接字上投递一个异步的WSARecv请求,指定参数WSAOVERLAPPED结构。
注:函数通常会失败,返回SOCKET_ERROR状态WSA_IO_PENGING(io操作未完成);
6.使用4中的事件数组,调用WSAWaitForMultipleEvents等待与重叠关联事件变成传信状态。
说明:一个重叠请求操作最终完成之后,在事件的通知方法中,winsock会把WSAOVERLAPPED结构中的事件由未传信状态变为已传信状态;事件已经分配给了WSAOVERLAPPED所以只需要简单的调用WSAWaitForEvents就可以判断出一个IO调用在什么时候完成。
7.WSAWaitForMultipleEvent函数完成后,针对事件数组,调用WSAResetEvent重设事件对象,并对完成的重叠请求进行处理;
8.使用WSAGetOverlappedResult函数,判断重叠调用的返回状态是什么。
说明:发现一次重叠请求完成之后,需要调用WSAGetOverlappedResult(获得重叠结构)函数,来判断那个重叠请
求到底是成功,还是失败。
BOOL WSAGetOverlappedResult
(
IN SOCKET s, //重叠操作开始时,与之对应的套接字
IN LPWSAOVERLAPPED lpOverlapped, //WSAOVERLAPPED
OUT LPDWORD lpcbTransfer, //负责接收一次重叠发送或者接收实际传输的字节
IN BOOL fWait, //确定函数是否应该等待一次未完成的重叠操作完成;TRUE表示一直等到操作完成再返回;
OUT LPDWORD lpdwFlags //负责接收结果标志
)
返回值的分析:该函数成功返回的TRUE,表示我们的重叠IO已经完成。而且lpcbTransfer函数指向的值已经得到更新,如果返回的是FALSE则可能是一下的原因:1.重叠的IO仍然在待决的状态。
2.重叠IO完成,但是有错误。
3.重叠IO的完成状态不可以判断,因为传递给该函数的一个或者多个参数存在错误;
9.在套接字上投递里另外一个重叠WSARecv请求。
10.重复6--9;
示例代码:
#include "stdafx.h"
#include <stdio.h>
#include <WinSock2.h>
#include <Windows.h>
#pragma comment(lib,"ws2_32.lib")
#define PORT 5150
#define DATA_BUFFER 4096
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA WSAdata;
SOCKET sListen,sAccept;
WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];
WSAOVERLAPPED overlapped;
DWORD EventTotal=0;
DWORD byteRecv=0;
DWORD Flags=0;
DWORD ByteTranfer=0;
WSABUF wsabuf;
char buffer[DATA_BUFFER];
if ((WSAStartup(MAKEWORD(2,2),&WSAdata))!=0)
{
printf("WSAStartup() Failed with error %d",WSAGetLastError());
return 0;
}
sListen=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if (sListen==INVALID_SOCKET)
{
printf("socket() Failed with error %d",WSAGetLastError());
return 0;
}
SOCKADDR_IN listenAddr;
listenAddr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
listenAddr.sin_family=AF_INET;
listenAddr.sin_port=htons(PORT);
if (bind(sListen, (PSOCKADDR)&listenAddr, sizeof(listenAddr)) == SOCKET_ERROR)
{
printf("bind() failed with error %d\n", WSAGetLastError());
return 0;
}
if (listen(sListen, 5))
{
printf("listen() failed with error %d\n", WSAGetLastError());
return 0;
}
printf("Listening...\n");
sAccept=accept(sListen,NULL,NULL);
if (sListen==INVALID_SOCKET)
{
printf("accept() Failed with error %d",WSAGetLastError());
return 0;
}
EventArray[EventTotal]=WSACreateEvent();
ZeroMemory(&overlapped,sizeof(overlapped));
overlapped.hEvent=EventArray[EventTotal];
EventTotal++;
wsabuf.buf=buffer;
wsabuf.len=DATA_BUFFER;
if ((WSARecv(sAccept,&wsabuf,1,&byteRecv,&Flags,&overlapped,NULL))==SOCKET_ERROR)
{
if (WSAGetLastError()!=WSA_IO_PENDING)
{
printf("Error occurred at WSARecv()\n");
return 0;
}
}
while(TRUE)
{
DWORD index;
index=WSAWaitForMultipleEvents(EventTotal,EventArray,FALSE,WSA_INFINITE,FALSE);
WSAResetEvent(EventArray[index - WSA_WAIT_EVENT_0]);
WSAGetOverlappedResult(sAccept, &overlapped,&ByteTranfer, FALSE, &Flags);
//if the connection close,if so closesocket;
if (ByteTranfer==0)
{
printf("Closing Socket %d\n", sAccept);
closesocket(sAccept);
WSACloseEvent(EventArray[index - WSA_WAIT_EVENT_0]);
return 0;
}
if (WSASend(sAccept, &wsabuf, 1, &byteRecv, Flags, &overlapped, NULL) == SOCKET_ERROR)
{
printf("WSASend() is busted\n");
}
//post another WSARecv on socket;
Flags=0;
ZeroMemory(&overlapped,sizeof(overlapped));
overlapped.hEvent=EventArray[index-WSA_WAIT_EVENT_0];
wsabuf.buf=buffer;
wsabuf.len=DATA_BUFFER;
if ((WSARecv(sAccept,&wsabuf,1,&byteRecv,&Flags,&overlapped,NULL))==SOCKET_ERROR)
{
if (WSAGetLastError()!=WSA_IO_PENDING)
{
printf("Error occurred at WSARecv()\n");
return 0;
}
}
}
return 0;
}