重叠I/O是Win32文件操作的一项技术,使用它可以在一个重叠结构上提交多个I/O请求,并在数据传输结束后通知应用程序,系统通知应用程序的形式有两种,事件通知和完成例程。

1、常用函数
(1)WSASocket函数

SOCKET WSASocket(int af,int type,int protocol,LPWSAPROTOCOL_INFO lpProtocolInfo,GROUP g,DWORD dwFlags)

af:地址家族,通常用AF_INET
type:新建Socket类型,SOCK_STREAM基于流的Socket,SOCK_DGRAM基于数据报的Socket;
protocol:Socket使用的协议;
lpProtocolInfo:新建Socket特性,一般NULL
g:预留字段,一般为0;
dwFlags:指定Socket属性的标识,在重叠I/O模型中,dwFlags需要设置为WSA_FLAG_OVERLAPPED
重叠Socket可以使用WSASend、WSASendTo、WSARecv、WARecvFrom和WSAIoctl等函数执行重叠I/O操作,即同时初始化和处理多个操作。

(2)WSASend函数

int WSASend(
SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDOWRD lpNumberOfBytesSend,
DWORD dwFlags,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);

s:Socket;
lpBuffers:指向WSABUF结构体指针;
dwBufferCount:lpBuffers数组中WSABUF数量;
lpNumberOfBytesSend:如果I/O立即完成,该参数为指定发送数据的字节数;
dwFlags:用于修改WSASend函数行为的标识位;
lpOverlapped:指向WSAOVERLAPPED结构体指针,该参数对非重叠Socket无效;
lpCompletionRoutine:指向完成例程,该参数对非重叠Socket无效。

(3)WSARecv函数

int WSASend(
    SOCKET s,
    LPWSABUF lpBuffers,
    DWORD dwBufferCount,
    LPDOWRD lpNumberOfBytesRecv,
    DWORD dwFlags,
    LPWSAOVERLAPPED lpOverlapped,
    LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
s:Socket;

lpBuffers:指向WSABUF结构体指针;
dwBufferCount:lpBuffers数组中WSABUF数量;
lpNumberOfBytesRecv:如果I/O立即完成,该参数为指定接收数据的字节数;
dwFlags:用于修改WSASend函数行为的标识位;
lpOverlapped:指向WSAOVERLAPPED结构体指针,该参数对非重叠Socket无效;
lpCompletionRoutine:指向完成例程,该参数对非重叠Socket无效。

(4)GetOverlappedResult函数

BOOL GetOverlappedResult(
HANDLE hFile,
LPOVERLAPPED lpOverlapped,
LPDWORD lpNumberOfBytesTransferred,
BOOL bWait);

hFile:指定文件、管道、通信设备的句柄;
lpOverlapped:重叠操作开始时指向OVERLAPPED结构体;
bWait:如果该参数为TRUE,则函数会一直等待到操作完成后返回;否则函数直接返回。

2、使用事件通知来管理重叠I/O操作
WSASend()WSARecv()函数中,当重叠操作完成后,如果lpCompletionRoutine参数为NULL,则lpOverlapped中的hEvent参数被设置为已授信状态。应用程序可以调用WSAWaitForMultipleEvents()函数或WSAGetOverlapped()函数等待或轮循时间对象变成授信对象。
(1)定义变量

WSABUF DataBuf;  // 传输结构              
char buffer[BUFSIZE];   // 缓冲区
DWORD EventTotal=0,RecvBytes=0,Flags=0,BytesTransferred=0;  
WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];  // 用来通知重叠事件完成的时间句柄数组
WSAOVERLAPPED AcceptOverlapped;  // 重叠结构
SOCKET server,client;
WSADATA wsa;

(2)创建一个套接字,开始在指定的端口上监听连接请求

WSAStartup(MAKEWORD(2,2),&wsa);
server=WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,0,WSA_FLAG_OVERLAPPED);
sockaddr_in saddr;
saddr.sin_family=AF_INET;
saddr.sin_port=htons(9000);
saddr.sin_addr.s_addr=htonl(ADDR_ANY);
bind(server,(sockaddr*)&saddr,sizeof(saddr));
listen(server,5);
client=accept(server,NULL,NULL);

(3)建立、初始化重叠结构

EventArray[EventTotal]=WSACreateEvent();
ZeroMemory(buffer,BUFSIZE);
ZeroMemory(&AcceptOverlapped,sizeof(AcceptOverlapped));
AcceptOverlapped.hEvent=EventArray[EventTotal];
DataBuf.len=BUFSIZE;
DataBuf.buf=buffer;
EventTotal++;

(4)以WSAOVERLAPPED结构为参数,套接字上投递WSARecv请求

ret=WSARecv(client,&DataBuf,1,&RecvBytes,&Flags,&AcceptOverlapped,NULL);
if(ret==SOCKET_ERROR)
    if(WSAGetLastError()!=WSA_IO_PENDING)
    {
        cout<<"Error occured at WSARecv"<<endl;
        closesocket(client);
        WSACloseEvent(EventArray[EventTotal]);
    }

(5)用WSAWaitForMultipleEvents函数等待重叠操作返回的结果

Index=WSAWaitForMultipleEvents(EventTotal,EventArray,FALSE,WSA_INFINITE,FALSE);                  // 哪个事件被触发?
WSAGetOverlappedResult(client,&AcceptOverlapped,&BytesTransferred,FALSE,&Flags);                 // 操作结果?
if(BytesTransferred==0)
{
    cout<<"closing socket "<<client<<endl;
    closesocket(client);
    // Index-WSA_WAIT_EVENT_0:句柄->索引
    WSACloseEvent(EventArray[Index-WSA_WAIT_EVENT_0]);
    return -1;
}
cout<<DataBuf.buf<<endl;
ret=WSASend(client,&DataBuf,1,&RecvBytes,Flags,&AcceptOverlapped,NULL);
if(ret==SOCKET_ERROR)
        cout<<"Send is busted"<<endl;
// 重置Flags
Flags=0;
ZeroMemory(buffer,BUFSIZE);
ZeroMemory(&AcceptOverlapped,sizeof(AcceptOverlapped));
AcceptOverlapped.hEvent=EventArray[Index-WSA_WAIT_EVENT_0];
// 重置缓冲区
DataBuf.len=BUFSIZE;
DataBuf.buf=buffer;

3、使用完成例程来管理重叠I/O操作
在调用WSASend()WSARecv()函数时,,如果lpCompletionRoutine参数不为NULL,则hEvent参数将会被忽略,而是将上下文信息传递给完成例程函数,然后调用WSAGetOverlappedResult()函数查询重叠操作结果。
完成例程回调函数原型及传递方式

void CALLBACK CompletionRoutine(  
DWORD dwError, // 标志咱们投递的重叠操作,比如WSARecv,完成的状态是什么  
DWORD cbTransferred, // 指明了在重叠操作期间,实际传输的字节量是多大  
LPWSAOVERLAPPED lpOverlapped, // 参数指明传递到最初的IO调用内的一个重叠结构  
DWORD dwFlags);  // 返回操作结束时可能用的标志(0)  

(1)创建一个套接字,开始在指定的端口上监听连接请求

WSADATA wsa;
WSAStartup(MAKEWORD(2,2),&wsa);
SOCKET server;
sockaddr_in saddr,caddr;
unsigned int ThreadId;
server=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
saddr.sin_family=AF_INET;
saddr.sin_port=htons(9000);
saddr.sin_addr.s_addr=htonl(ADDR_ANY);
bind(server,(sockaddr*)&saddr,sizeof(saddr));
listen(server,5);

(2)创建工作线程

unsigned int _stdcall WorkerThread(void* lp)
{
    LPPER_IO_OPERATION_DATA lpPerIOData=NULL;
    while(TRUE)
    {
        if(NewConn)
        {
            lpPerIOData=(LPPER_IO_OPERATION_DATA)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(PER_IO_OPERATION_DATA));
            lpPerIOData->DataBuf.len=MSGSIZE;
            lpPerIOData->DataBuf.buf=lpPerIOData->Buffer;
            lpPerIOData->client=client;
            WSARecv(lpPerIOData->client,&lpPerIOData->DataBuf,1,&lpPerIOData->RecvBytes,&lpPerIOData->Flags,&lpPerIOData->overlap,CompletionROUTINE);
            NewConn=FALSE;
        }
        SleepEx(1000,TRUE);
    }
    return 0;
}

(3)完成例程

void CALLBACK CompletionROUTINE(DWORD dwError,DWORD cbTransferred,LPWSAOVERLAPPED lpoverlapped,DWORD Flags)
{
    // 指针转换而已
    LPPER_IO_OPERATION_DATA lpPerIOData=(LPPER_IO_OPERATION_DATA)lpoverlapped;
    if(dwError!=0 || cbTransferred==0)
    {
        closesocket(lpPerIOData->client);
        HeapFree(GetProcessHeap(),0,lpPerIOData);
    }
    else
    {
        lpPerIOData->Buffer[cbTransferred]='\0';
        send(lpPerIOData->client,lpPerIOData->Buffer,cbTransferred,0);
        memset(&lpPerIOData->overlap,0,sizeof(WSAOVERLAPPED));
        lpPerIOData->DataBuf.len=MSGSIZE;
        lpPerIOData->DataBuf.buf=lpPerIOData->Buffer;
        WSARecv(lpPerIOData->client,&lpPerIOData->DataBuf,1,&lpPerIOData->RecvBytes,&lpPerIOData->Flags,&lpPerIOData->overlap,CompletionROUTINE);
    }
}

(4)循环accept

 while(TRUE)
    {
        int len=sizeof(saddr);
        client=accept(server,(sockaddr*)&caddr,&len);
        NewConn=TRUE;
        cout<<"Accept client:"<<inet_ntoa(caddr.sin_addr)<<" port:"<<ntohs(caddr.sin_port)<<endl;
    }

标签: none

评论已关闭