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等待全局完成端口的完成队列。
完成端口的工作原理是,把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);
}
}
}
}
原因还是因为为了解决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);
}
}
}
}
相关文章推荐
- Socket模型(二):完成端口(IOCP)
- 完成端口重叠I/O模型的服务器中,如何存储和管理数万个socket句柄—IOCP代码
- 手把手教你玩转SOCKET模型:完成端口(Completion Port)详解
- Windows完成端口 IOCP模型(一)
- Socket I/O模型之完成端口
- 手把手教你玩转SOCKET模型:完成端口(Completion Port)详解
- 很幽默的讲解六种Socket IO模型 Delphi版本(自己Select查看,WM_SOCKET消息通知,WSAEventSelect自动收取,Overlapped I/O 事件通知模型,Overlapped I/O 完成例程模型,IOCP模型机器人)
- 可伸缩的IO完成端口服务器模型(IOCP)(中文版)
- 可伸缩的IO完成端口服务器模型(IOCP)(英文版)
- 手把手教你玩转SOCKET模型:完成端口(Completion Port)详解
- 手把手教你玩转SOCKET模型:完成端口(Completion Port)详解
- DELPHI高性能大容量SOCKET并发:IOCP完成端口例子介绍
- 完成端口重叠I/O模型的服务器中,存储和管理数万个socket句柄
- [置顶] 手把手教你玩转SOCKET模型:完成端口(Completion Port)详解
- 完成端口(IOCP)的另一种设想——Socket与CompletionPort的多次关联
- Windows完成端口 IOCP模型(二)
- 完成端口封装(修复Windows 网络与通信程序设计 可伸缩IOCP模型的bug)
- 使用IOCP进行客户socket与完成端口关联操作
- 手把手教你玩转SOCKET模型:完成端口(Completion Port)详解
- 完成端口通讯服务器(IOCP Socket Server)设计(二)内存管理(AWE)