Socket I/O模型之完成端口(completion port)

 Socket I/O模型之完成端口(completion port)
C++代码
1. // write by larry
2. // 2009-8-20
3. // This is a server using completion port.
4. #include "stdafx.h"
5. #include <WINSOCK2.H>
6. #include <stdio.h>
7. #pragma comment(lib, "ws2_32.lib")
8. #define PORT 5150
9. #define MSGSIZE 1024
10. typedef enum
11. {
12. RECV_POSTED
13. } OPERATION_TYPE;
14. typedef struct
15. {
16. WSAOVERLAPPED overlap;
17. WSABUF Buffer;
18. char szMessage[MSGSIZE];
19. DWORD NumberOfBytesRecvd;
20. DWORD Flags;
21. OPERATION_TYPE OperationType;
22. } PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA;
23. DWORD WINAPI WorkerThread(LPVOID CompletionPortID);
24.
25. int main(int argc, char* argv[])
26. {
27. WSADATA wsaData;
28. SOCKET sListen, sClient;
29. SOCKADDR_IN local, client;
30. DWORD i, dwThreadId;
31. int iAddrSize = sizeof(SOCKADDR_IN);
32. HANDLE CompletionPort = INVALID_HANDLE_VALUE;
33. SYSTEM_INFO sysinfo;
34. LPPER_IO_OPERATION_DATA lpPerIOData = NULL;
35. // Initialize windows socket library
36. WSAStartup(0x0202, &wsaData);
37. // Create completion port
38. CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
39. // Create worker thread
40. GetSystemInfo(&sysinfo);
41. for (i = 0; i < sysinfo.dwNumberOfProcessors; i++)
42. {
43. CreateThread(NULL, 0, WorkerThread, CompletionPort, 0, &dwThreadId);
44. }
45. // Create listening socket
46. sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
47. // Bind
48. local.sin_family = AF_INET;
49. local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
50. local.sin_port = htons(PORT);
51. bind(sListen, (sockaddr*)&local, sizeof(SOCKADDR_IN));
52. // Listen
53. listen(sListen, 3);
54. while (TRUE)
55. {
56. // Accept a connection
57. sClient = accept(sListen, (sockaddr*)&client, &iAddrSize);
58. printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
59. // Associate the newly arrived client socket with completion port
60. CreateIoCompletionPort((HANDLE)sClient, CompletionPort, (DWORD)sClient, 0);
61. // Launch an asynchronous operation for new arrived connection
62. lpPerIOData = (LPPER_IO_OPERATION_DATA)HeapAlloc(
63. GetProcessHeap(),
64. HEAP_ZERO_MEMORY,
65. sizeof(PER_IO_OPERATION_DATA));
66. lpPerIOData->Buffer.len = MSGSIZE;
67. lpPerIOData->Buffer.buf = lpPerIOData->szMessage;
68. lpPerIOData->OperationType = RECV_POSTED;
69. WSARecv(sClient,
70. &lpPerIOData->Buffer,
71. 1,
72. &lpPerIOData->NumberOfBytesRecvd,
73. &lpPerIOData->Flags,
74. &lpPerIOData->overlap,
75. NULL);
76. }
77. PostQueuedCompletionStatus(CompletionPort, 0xFFFFFFFF, 0, NULL);
78. CloseHandle(CompletionPort);
79. closesocket(sListen);
80. WSACleanup();
81. return 0;
82. }
83. DWORD WINAPI WorkerThread(LPVOID CompletionPortID)
84. {
85. HANDLE CompletionPort = (HANDLE)CompletionPortID;
86. DWORD dwBytesTransferred;
87. SOCKET sClient;
88. LPPER_IO_OPERATION_DATA lpPerIOData = NULL;
89. while (TRUE)
90. {
91. GetQueuedCompletionStatus(
92. CompletionPort,
93. &dwBytesTransferred,
94. (DWORD*)&sClient,
95. (LPOVERLAPPED*)&lpPerIOData,
96. INFINITE);
97. if (dwBytesTransferred == 0xFFFFFFFF)
98. {
99. return 0;
100. }
101. if (lpPerIOData->OperationType == RECV_POSTED)
102. {
103. if (dwBytesTransferred == 0)
104. {
105. // Connection was closed by client
106. closesocket(sClient);
107. HeapFree(GetProcessHeap(), 0, lpPerIOData);
108. }
109. else
110. {
111. lpPerIOData->szMessage[dwBytesTransferred] = '\0';
112. send(sClient, lpPerIOData->szMessage, dwBytesTransferred, 0);
113.
114. // Launch another asynchronous operation for sClient
115. memset(lpPerIOData, 0, sizeof(PER_IO_OPERATION_DATA));
116. lpPerIOData->Buffer.len = MSGSIZE;
117. lpPerIOData->Buffer.buf = lpPerIOData->szMessage;
118. lpPerIOData->OperationType = RECV_POSTED;
119. WSARecv(sClient,
120. &lpPerIOData->Buffer,
121. 1,
122. &lpPerIOData->NumberOfBytesRecvd,
123. &lpPerIOData->Flags,
124. &lpPerIOData->overlap,
125. NULL);
126. }
127. }
128. }
129. return 0;
130. }
服务器端得主要流程: 1.创建完成端口对象2.创建工作者线程(这里工作者线程的数量是按照CPU的个数来决定的,这样可以达到最佳性能) 3.创建监听套接字,绑定,监听,然后程序进入循环4.在循环中,我做了以下几件事情: (1).接受一个客户端连接
(2).将该客户端套接字与完成端口绑定到一起(还是调用CreateIoCompletionPort,但这次的作用不同),注意,按道理来讲,此时传递给CreateIoCompletionPort的第三个参数应该是一个完成键,一般来讲,程序都是传递一个单句柄数据结构的地址,该单句柄数据包含了和该客户端连接有关的信息,由于我们只关心套接字句柄,所以直接将套接字句柄作为完成键传递; (3).触发一个WSARecv异步调用,这次又用到了“尾随数据”,使接收数据所用的缓冲区紧跟在WSAOVERLAPPED对象之后,此外,还有操作类型等重要信息。在工作者线程的循环中,我们 1.调用GetQueuedCompletionStatus取得本次I/O的相关信息(例如套接字句柄、传送的字节数、单I/O数据结构的地址等等) 2.通过单I/O数据结构找到接收数据缓冲区,然后将数据原封不动的发送到客户端3.再次触发一个WSARecv异步操作
国人开发强悍IOCP代码,全部代码 其中一个单元的代码: unit uIOCompletionPort; interface uses Windows, WinSock2, uWin32Const, uException, uDIProtocol, uDIClientChannel; type TIOCompletionPort = class private m_hCompletionPort: Thandle; private procedure CreateCompletionPort; public function AssociateSocketWithCompletionPort( hDevice: THandle; dwCompletionKey: DWORD): Boolean; function GetIOCompletionStatus( var FClientChannel: TDIClientChannel; var pHandleData: PPerHandleData; var dwIoSize: DWORD): Boolean; overload; function GetIOCompletionStatus( var FClientChannel: TDIClientChannel; var pHandleData: PPerHandleData; var dwIoSize: DWORD; dwMilliseconds: DWORD): Boolean; overload; function PostIOCompletionStatus( lpCompletionKey: DWORD; lpOverlapped: POverlapped; lpNumberOfBytesTransferred: DWORD): Boolean; public constructor Create; destructor Destroy; override; end; implementation constructor TIOCompletionPort.Create; begin inherited Create; CreateCompletionPort; end; destructor TIOCompletionPort.Destroy; begin CloseHandle(m_hCompletionPort); inherited Destroy; end; procedure TIOCompletionPort.CreateCompletionPort; var s: TSocket; begin s := Winsock2.socket(AF_INET, SOCK_STREAM, IPPROTO_IP); if (s = Winsock2.INVALID_SOCKET) then raise TException.Create(ErrWin32Error, GetLastError(), 'Winsock2.socket'); m_hCompletionPort := CreateIOCompletionPort(s, 0, 0, 0); if (m_hCompletionPort = 0) then raise TException.Create(ErrWin32Error, GetLastError(), 'CreateIOCompletionPort'); Winsock2.closesocket(s); end; function TIOCompletionPort.AssociateSocketWithCompletionPort( hDevice: THandle; dwCompletionKey: DWORD ): Boolean; var h: THandle; begin Result := TRUE; h := CreateIOCompletionPort(hDevice, m_hCompletionPort, dwCompletionKey, 0); if (h m_hCompletionPort) then begin Result := FALSE; raise TException.Create(ErrWin32Error, GetLastError(), 'AssociateSocketWithCompletionPort'); end; end; function TIOCompletionPort.GetIOCompletionStatus( var FClientChannel: TDIClientChannel; var pHandleData: PPerHandleData; var dwIoSize: DWORD): Boolean; begin Result := GetQueuedCompletionStatus( m_hCompletionPort, dwIOSize, DWORD(FClientChannel), POVERLAPPED(pHandleData), INFINITE ); end; function TIOCompletionPort.GetIOCompletionStatus( var FClientChannel: TDIClientChannel; var pHandleData: PPerHandleData; var dwIoSize: DWORD; dwMilliseconds: DWORD): Boolean; var bRet: Boolean; nLastError: DWORD; begin bRet := TRUE; if FALSE = GetQueuedCompletionStatus( m_hCompletionPort, dwIOSize, DWORD(FClientChannel), POVERLAPPED(pHandleData), INFINITE ) then begin nLastError := GetLastError(); if (nLastError WAIT_TIMEOUT) then raise TException.Create(ErrWin32Error, GetLastError(), 'GetQueuedCompletionStatus'); bRet := FALSE; end; Result := bRet; end; function TIOCompletionPort.PostIOCompletionStatus( lpCompletionKey: DWORD; lpOverlapped: POverlapped; lpNumberOfBytesTransferred: DWORD): Boolean; begin Result := PostQueuedCompletionStatus( m_hCompletionPort, lpNumberOfBytesTransferred, lpCompletionKey, lpOverlapped ); end; end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值