您的位置:首页 > 其它

Socket模型(二):完成端口(IOCP)

2011-05-17 00:18 435 查看
首先来说为什么要使用完成端口:

原因还是因为为了解决recv方法为阻塞式的问题,WinSocket封装的WSARecv方法为非堵塞的方法。

int
WSARecv(

SOCKET
s,


LPWSABUF
lpBuffers,


DWORD
dwBufferCount,


LPDWORD
lpNumberOfBytesRecvd,


LPDWORD
lpFlags,


LPWSAOVERLAPPED
lpOverlapped,


LPWSAOVERLAPPED_COMPLETION_ROUTINE
lpCompletionRoutine

);
WSARecv为非阻塞的方法,其中第二个参数是I/O请求成功时,数据保存的地址。

Socket的触发是属于网卡硬件的中断信号,只是此信号CPU不能直接获取状态,此时我们可以使之绑定Event事件,Event内核对象的状态时可以监听到的。

这也就是WSAEventSelect模型的原理,当然重叠模型的最终原理也是如此。但Event的方法有着其弊病:当模型处理多线程事件时要调用WSAWaitForMultipleEvents函数,

WSAWaitForMultipleEvents函数一次最多只能等待64个事件对象。所以当海量客户端连接服务器时,服务器将没有能力应对,于是我们使用完成端口。

完成端口:

HANDLE CreateIoCompletionPort(

HANDLE FileHandle,             //要链接的Socket

HANDLE ExistingCompletionPort,     //全局完成端口

//同完成端口关联到一起的句柄,此处可为链接的socket,或是id等等(目地使接收到的socket知道是哪个socket)

DWORD CompletionKey,             

DWORD NumberOfConcurrentThreads

);

此函数创建创建Socket与完成端口的链接,CreateIoCompletionPort函数被用于完成两个工作:

用于创建—个完成端口对象。

将一个句柄同完成端口关联到一起。

用函数GetQueuedCompletionStatus等待全局完成端口的完成队列。

BOOL
GetQueuedCompletionStatus(


HANDLE
CompletionPort,


LPDWORD
lpNumberOfBytes,


PULONG_PTR
lpCompletionKey,    //此参数为CreateIoCompletionPort第三个参数传过来的句柄,通过此参数获得socket


LPOVERLAPPED*
lpOverlapped,


DWORD
dwMilliseconds


);


完成端口的工作原理是,把Socket和完成端口绑定,通过关联句柄传递传递参数,使得获取到的Socket能得知是那个socket,参数可以自定义可以是socket本身也可以是id等等。

#include
"WinSock2.h"

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

 

#define
MESSAGESIZE 1024

SOCKET
serverSocket;

DWORD
WINAPI
SocketProcAccept(LPVOID
pParam);

DWORD
WINAPI
SocketProcMain(LPVOID
pParam);

 

enum
SOCKETOPERATE

{

    soREVC

};

struct
SOCKETDATA

{

    WSAOVERLAPPED        overlapped;

    WSABUF                buf;

    char                sMessage[MESSAGESIZE];

    DWORD                dwBytes;

    DWORD                flag;

    SOCKETOPERATE        socketType;

    void
Clear(SOCKETOPERATE
type)

    {

        ZeroMemory(this, sizeof(SOCKETDATA));

        buf.buf = sMessage;

        buf.len = MESSAGESIZE;

        socketType = type;

    }

};

SOCKET
CreateServiceSocket(int
Port)

{

    int
iError;

    WSAData
data;

    iError = WSAStartup(0x0202, &data);

    SOCKET
tmp = socket(AF_INET,SOCK_STREAM,0);

    if(tmp == INVALID_SOCKET)

    {

        return
INVALID_SOCKET;

    }

    SOCKADDR_IN
addr;

    addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    addr.sin_family = AF_INET;

    addr.sin_port = htons(Port);

    if((bind(tmp, (sockaddr*)&addr, sizeof(addr))) != 0)

    {

        closesocket(tmp);

        return
INVALID_SOCKET;

    }

    if((listen(tmp, INFINITE)) != 0)

    {

        closesocket(tmp);

        return
INVALID_SOCKET;

    }

    return
tmp;

 

}

int
_tmain(int
argc, _TCHAR* argv[])

{

    HANDLE
CP = INVALID_HANDLE_VALUE;

    CP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);

 

    SYSTEM_INFO
systemInfo;

    GetSystemInfo(&systemInfo);

    for (int
i = 0; i<systemInfo.dwNumberOfProcessors; i++)

    {

        CreateThread(NULL, NULL, &SocketProcMain, CP, NULL, NULL);

    }

    serverSocket = CreateServiceSocket(6565);

    if (serverSocket == INVALID_SOCKET)

    {

        return 0;

    }

    CreateThread(NULL, NULL, &SocketProcAccept, CP, NULL, NULL);

    while(1)

    {

        Sleep(10000);

    }

    CloseHandle(CP);

    closesocket(serverSocket);

    WSACleanup();

    return 0;

}

DWORD
WINAPI
SocketProcAccept(LPVOID
pParam)

{

    HANDLE
CP = (HANDLE)pParam;

    SOCKADDR_IN
addr;

    int
len = sizeof(SOCKADDR_IN);

    SOCKET
tmp;

    SOCKETDATA *lpSocketData;

    while(1)

    {

        tmp = accept(serverSocket, (sockaddr*)&addr, &len);

        printf("Client Accept:%s\t:%d\n", inet_ntoa(addr.sin_addr), htons(addr.sin_port));

        CreateIoCompletionPort((HANDLE)tmp, CP, (DWORD)tmp, INFINITE);

        lpSocketData = (SOCKETDATA *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SOCKETDATA));

        lpSocketData->Clear(soREVC);

        WSARecv(tmp, &lpSocketData->buf, 1,&lpSocketData->dwBytes, &lpSocketData->flag, &lpSocketData->overlapped, NULL);

    }

}

DWORD
WINAPI
SocketProcMain(LPVOID
pParam)

{

    HANDLE
CP = (HANDLE)pParam;

    SOCKADDR_IN
addr;

    DWORD
dwBytes;

    SOCKETDATA *lpSocketData;

    SOCKET
clientSocket;

    while(1)

    {

        GetQueuedCompletionStatus(CP, &dwBytes, (PULONG_PTR)&clientSocket, (LPOVERLAPPED*)&lpSocketData, INFINITE);

        if(dwBytes == 0xFFFFFFFF)

        {

            return 0;

        }

        if(lpSocketData->socketType == soREVC)

        {

            if(dwBytes == 0)

            {

                closesocket(clientSocket);

                HeapFree(GetProcessHeap(), 0, lpSocketData);

            }else

            {

                lpSocketData->sMessage[dwBytes] = '\0';

                printf("%x\t:%s\n", (DWORD)clientSocket, lpSocketData->sMessage);

                lpSocketData->Clear(soREVC);

                WSARecv(clientSocket, &lpSocketData->buf, 1, &lpSocketData->dwBytes, &lpSocketData->flag, &lpSocketData->overlapped, NULL);

            }

        }

    }

    

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐