您的位置:首页 > 理论基础 > 计算机网络

网络编程(56)—— Windows下使用IOCP构建socket服务端

2017-01-10 15:20 483 查看

一、完成端口对象

        不同于IO重叠对象在IO完成时通过触发事件或者触发CompletionRoutine回调函数,IOCP模型将socket和完成端口对象(CompletionPort,简称CP对象)绑定,当IO操作完成时,会改变该对象的状态,而我们通过完成端口对象,便可以确认IO操作是否完成。

       创建完成端口对象的API如下:

HANDLE WINAPI CreateIoCompletionPort(
__in HANDLE FileHandle,
__in_opt HANDLE ExistingCompletionPort,
__in ULONG_PTR CompletionKey,
__in DWORD NumberOfConcurrentThreads
);
CreateIoCompletionPort既可以创建完成端口对象,也可以用来将socket和完成对象绑定,通过对其赋予不同的参数,可以实现不同的功能:
FileHandle ——创建CP对象时传入INVALID_HANDLE_VALUE;绑定socket和CP对象时传入socket描述符。
ExistingCompletionPort——创建CP对象时传入NULL;绑定socket和CP对象传入完成端口对象的句柄;
CompletionKey —— 创建CP对象是传入0;绑定socket和CP对象时作为参数传递给GetQueuedCompletionStatus
NumberOfConcurrentThreads—— 分配给CP对象用于处理IO的线程数。如果参数是0,系统中的CPU个数就是最大的线程数。
返回值 —— 返回CP对象的句柄。
        如下,我们创建了一个完成端口对象:
hComPort=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);

二、进行socket对象和CP对象的绑定

        我们仍然用CreateIoCompletionPort将socket和CP对象进行绑定:
CreateIoCompletionPort((HANDLE)clntSock,hComPort,(DWORD)handleInfo,0);
        clntSock是我们要进行绑定的socket描述符,hComPort使我们之前创建的CP对象,而handleInfo是我们自定义的结构体,作为参数可以传给GetQueuedCompletionStatus函数。

三、使用GetQueuedCompletionStatus获取CP对象的状态

        GetQueuedCompletionStatus在我们定义的线程函数中调用,用于获取CP对象的状态。当IO操作未完成时,该函数会发生阻塞;若IO操作完成,函数发生返回。其函数原型如下:
BOOL WINAPI GetQueuedCompletionStatus(
__in HANDLE CompletionPort,
__out LPDWORD lpNumberOfBytes,
__out PULONG_PTR lpCompletionKey,
__out LPOVERLAPPED *lpOverlapped,
__in DWORD dwMilliseconds
);
CompletionPort —— 进行注册过的完成对象的句柄。
lpNumberOfBytes —— 完成IO对象时传递或者接受的字节数。
lpCompletionKey —— 使用CreateIoCompletionPort注册时传递的参数,参数可以传递我们自定义的结构信息。
lpOverlapped —— 调用WSARecv或者WSASend时传递的OVERLAPPED对象指针。
dwMilliseconds —— GetQueuedCompletionStatus阻塞的时间,如果设置成INFINITE时无限期等待。

四、代码示例

#include "stdafx.h"
#include "stdio.h"
#include "process.h"
#include "stdlib.h"
#include "WinSock2.h"
#include "Windows.h"

#pragma comment(lib,"ws2_32.lib")

#define BUF_SIZE 100
#define READ 3
#define WRITE 5

typedef struct
{
SOCKEThClntSock;
SOCKADDRclntAdr;
}HANDLE_DATA,*LPHANDLE_DATA;

typedef struct
{
OVERLAPPEDoverlapped;
WSABUFwsaBuf;
char buffer[BUF_SIZE];
int rwMode;
}IO_DATA,*LPIO_DATA;

unsigned WINAPI ThreadMain(LPVOID CompletionPortIO);
void ErrorHandler(char* message);

int _tmain(int argc, _TCHAR* argv[])
{
WSADatawsaData;
HANDLEhComPort;
SYSTEM_INFOsysInfo;
LPIO_DATAioInfo;
LPHANDLE_DATAhandleInfo;

SOCKETservSock;
SOCKADDR_INservAddr;
DWORDrecvBytes,i,flags=0;
WSAStartup(MAKEWORD(2,2),&wsaData);

hComPort=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);
GetSystemInfo(&sysInfo);

for(i=0;i<sysInfo.dwNumberOfProcessors;i++)
_beginthreadex(NULL,0,ThreadMain,(LPVOID)hComPort,0,NULL);

servSock=WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
if(servSock==INVALID_SOCKET)
ErrorHandler("WSASocket Error");

memset(&servAddr,0,sizeof(servAddr));
servAddr.sin_family=AF_INET;
servAddr.sin_addr.s_addr=htonl(INADDR_ANY);
servAddr.sin_port=htons(atoi("8888"));

if(bind(servSock,(SOCKADDR*)&servAddr,sizeof(servAddr))==SOCKET_ERROR)
ErrorHandler("bind error");

if(listen(servSock,5)==SOCKET_ERROR)
ErrorHandler("listen error");

while(1)
{
SOCKETclntSock;
SOCKADDR_INclntAddr;
int clntAddrSz;

clntSock=accept(servSock,(SOCKADDR*)&clntAddr,&clntAddrSz);
handleInfo=(LPHANDLE_DATA)malloc(sizeof(HANDLE_DATA));
handleInfo->hClntSock=clntSock;
memcpy(&(handleInfo->clntAdr),&clntAddr,sizeof(clntAddr));

CreateIoCompletionPort((HANDLE)clntSock,hComPort,(DWORD)handleInfo,0);

ioInfo=(LPIO_DATA)malloc(sizeof(IO_DATA));
memset(&(ioInfo->overlapped),0,sizeof(OVERLAPPED));
ioInfo->wsaBuf.len=BUF_SIZE;
ioInfo->wsaBuf.buf=ioInfo->buffer;
ioInfo->rwMode=READ;
WSARecv(handleInfo->hClntSock,&(ioInfo->wsaBuf),1,&recvBytes,&flags,&(ioInfo->overlapped),NULL);
}

WSACleanup();
return 0;
}

unsigned WINAPI ThreadMain(LPVOID CompletionPortIO)
{
HANDLEhComPort=(HANDLE)CompletionPortIO;
SOCKET sock;
DWORDbytesTrans;
LPHANDLE_DATAhandleInfo;
LPIO_DATAioInfo;
DWORD flags;

while(1)
{
GetQueuedCompletionStatus(hComPort,&bytesTrans,(PULONG_PTR)&handleInfo,(LPOVERLAPPED*)&ioInfo,INFINITE);
sock=handleInfo->hClntSock;

if(ioInfo->rwMode==READ)
{
puts("message received!");
if(bytesTrans==0)
{
closesocket(sock);
free(handleInfo);
free(ioInfo);
continue;
}
memset(&(ioInfo->overlapped),0,sizeof(OVERLAPPED));
ioInfo->wsaBuf.len=bytesTrans;
ioInfo->rwMode=WRITE;
WSASend(sock,&(ioInfo->wsaBuf),1,NULL,0,&(ioInfo->overlapped),NULL);

ioInfo=(LPIO_DATA)malloc(sizeof(IO_DATA));
memset(&(ioInfo->overlapped),0,sizeof(OVERLAPPED));
ioInfo->wsaBuf.len=BUF_SIZE;
ioInfo->wsaBuf.buf=ioInfo->buffer;
ioInfo->rwMode=READ;
WSARecv(sock,&(ioInfo->wsaBuf),1,NULL,&flags,&(ioInfo->overlapped),NULL);
}
else
{
puts("message sent");
free(ioInfo);
}
}
return 0;
}

void ErrorHandler(char* message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}

 Github位置:
https://github.com/HymanLiuTS/NetDevelopment
克隆本项目:
git clone git@github.com:HymanLiuTS/NetDevelopment.git
获取本文源代码:
git checkout NL56
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐