IO Completion Port简明教程(含例程)(2)

2025-10-04

AcceptEx,汗...),当然,你可以用类似:while(true) { AcceptEx(...);}, 但是这种busy loop显然是极其恶劣的。 先来看.h

view plaincopy to clipboardprint?

1. #pragma once

2. #include \ 3. #include 4. using namespace std; 5.

6. class CTcpServer 7. {

8. WSADATA wsd;

9. SOCKET m_ListeningSocket; 10. ADDRINFOW* m_pAddrInfo; 11. HANDLE m_AcceptEvent; 12.

13. public:

14. CTcpServer(PCWSTR pIPAddress, PCWSTR port); 15.

16. ~CTcpServer(); 17.

18. SOCKET& Socket() 19. {

20. return m_ListeningSocket; 21. } 22.

23. bool StartListening(); 24.

25. int AddressFamily() 26. {

27. return m_pAddrInfo->ai_family; 28. } 29.

30. int SocketType() 31. {

32. return m_pAddrInfo->ai_socktype; 33. } 34.

35. int Protocol() 36. {

37. return m_pAddrInfo->ai_protocol; 38. }

39.

40. BOOL WaitForAcceptEvent(DWORD timeout); 41.

42. BOOL AcceptNewConnection(); 43. 44. };

没什么稀奇的,如我所说,关键在于WaitForAcceptEvent()上,下面的代码是具体实现:

view plaincopy to clipboardprint?

1. #include \ 2.

3. #include \ 4. #include \ 5.

6. //初始化socket,稍微改改可以支持ipv6,先不管它

7. CTcpServer::CTcpServer(PCWSTR pAddress, PCWSTR port)

8. :m_ListeningSocket(INVALID_SOCKET), m_pAddrInfo(NULL), m_AcceptEvent

(NULL) 9. {

10. int rc = WSAStartup(MAKEWORD(2, 2), &wsd); 11. if (rc != 0) {

12. wprintf(L\); 13. throw new exception(); 14. } 15.

16. // Initialize the hints to retrieve the server address for IPv4

17. ADDRINFOW *result = NULL, 18. *ptr = NULL, 19. hints = {0}; 20.

21. hints.ai_family = AF_INET; 22. hints.ai_socktype = SOCK_STREAM; 23. hints.ai_protocol = IPPROTO_TCP; 24.

25. rc =::GetAddrInfoW(pAddress, port, &hints, &m_pAddrInfo); 26. if (rc != 0) {

27. printf(\, rc ); 28. throw new exception(); 29. } 30.

31. m_ListeningSocket = WSASocket(AF_INET, 32. SOCK_STREAM,

33. IPPROTO_TCP, NULL, 0, WSA_FLAG_OVE

RLAPPED); 34. 35. } 36.

37. // dtor负责cleanup,没什么特别 38. CTcpServer::~CTcpServer() 39. {

40. if (m_ListeningSocket != INVALID_SOCKET) 41. {

42. closesocket(m_ListeningSocket); 43. } 44.

45. if (m_AcceptEvent != NULL && m_AcceptEvent != INVALID_HANDLE_VAL

UE) 46. {

47. ::CloseHandle(m_AcceptEvent); 48. } 49.

50. WSACleanup(); 51. } 52.

53. // bind和listen,然后构造FD_ACCEPT event 54. bool CTcpServer::StartListening() 55. {

56. if (bind(m_ListeningSocket, m_pAddrInfo->ai_addr, m_pAddrInfo->a

i_addrlen) == SOCKET_ERROR) 57. {

58. return false; 59. } 60.

61. if (listen(m_ListeningSocket, 1 ) == SOCKET_ERROR) 62. {

63. printf(\); 64. return false; 65. } 66.

67. //这个很重要,先创建event,然后用WSAEventSelect表示我们只对 68. // FD_ACCEPT感兴趣

69. m_AcceptEvent = WSACreateEvent();

70. if(SOCKET_ERROR == WSAEventSelect(m_ListeningSocket, m_AcceptEve

nt, FD_ACCEPT)) 71. {

72. printf(\, WSAGetLastError());

73. return false; 74. } 75.

76. return true; 77. } 78.

79. // 等待FD_ACCEPT event

80. // 注意,跟普通event不一样,我们无需用ResetEvent()/SetEvent() 81. // 这些API,这个类似于Pulse

82. BOOL CTcpServer::WaitForAcceptEvent(DWORD timeout) 83. {

84. DWORD ret = WSAWaitForMultipleEvents(1, &m_AcceptEvent, FALSE, t

imeout, FALSE);

85. if (WSA_WAIT_TIMEOUT == ret || WSA_WAIT_FAILED == ret) 86. {

87. printf(\); 88. return false; 89. }

90. // 收到event

91. printf(\); 92.

93. WSANETWORKEVENTS events; 94.

95. // 检查该event是否在正确socket上

96. int nRet = WSAEnumNetworkEvents(m_ListeningSocket, m_AcceptEvent

, &events); 97.

98. if (nRet == SOCKET_ERROR) 99. {

100. printf(\); 101. return false; 102. } 103.

104. // 确认是FD_ACCEPT event

105. if (events.lNetworkEvents & FD_ACCEPT) 106. {

107. printf(\); 108. return true; 109. } 110.

111. return false; 112. } 113.

114. // 接受新连接请求

115. BOOL CTcpServer::AcceptNewConnection() 116. {

117. DWORD dwBytes; 118.

119. // OVERLAPPEDPLUS的初始化比较繁琐,所以专门用一个function

120. OVERLAPPEDPLUS* pOverlapPlus = ::CreateOverlappedPlus(); 121.

122. pOverlapPlus->serverSock = m_ListeningSocket; 123. pOverlapPlus->OpCode = OP_ACCEPT; 124.

125. pOverlapPlus->clientSock = socket(m_pAddrInfo->ai_family, m

_pAddrInfo->ai_socktype, m_pAddrInfo->ai_protocol); 126.

127. // 注意 AcceptEx是立即返回的,而且一般都是FALSE,如果要确认 128. // 是否出错,需要再调用WSAGetLastError()看是否是IO_PENDING,如 129. // 果是pending就没关系

130. // 另外, AcceptEx把client address放在接收buffer的最后,所以下 131. // 面会看到有一些\

132. return AcceptEx(m_ListeningSocket, pOverlapPlus->clientSock

, pOverlapPlus->wbuf.buf, pOverlapPlus->wbuf.len - ((sizeof(sockaddr_in) + 16) * 2), sizeof(sockaddr_in) + 16,sizeof(sockaddr_in) + 16, &dwBytes, &pOverlapPlus->overlapped); 133. } 134.

135. //创建OVERLAPPEDPLUS

136. // 其实还有个Free的function,也就是多free一下mbuf,就 137. //不写了:D

138. OVERLAPPEDPLUS* CreateOverlappedPlus() 139. {

140. OVERLAPPEDPLUS* pOverlapPlus = (OVERLAPPEDPLUS *)malloc(sizeof(

OVERLAPPEDPLUS));

141. memset(pOverlapPlus, 0, sizeof(OVERLAPPEDPLUS)); 142.

143. char* pBuffer = (char*)malloc(DATA_BUFSIZE); 144. pOverlapPlus->wbuf.buf = pBuffer; 145. pOverlapPlus->wbuf.len = DATA_BUFSIZE; 146.

147. return pOverlapPlus; 148. }

差不多就酱紫。另外说明在thread中,收到的Accept是带了第一个TCP payload的,所以如果你在AcceptEx之后去WSARecvFrom,是收不到东西的(因为已经收到了, 注意AcceptEx用pOverlapPlus->wbuf.buf 接收数据) 偶认为本人这篇是世界上最清晰易懂的IO Completion 教程:D


IO Completion Port简明教程(含例程)(2).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:Unit10 Places of Interest_ppt

相关阅读
本类排行
× 游客快捷下载通道(下载后可以自由复制和排版)

下载本文档需要支付 7

支付方式:

开通VIP包月会员 特价:29元/月

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信:xuecool-com QQ:370150219