IOCPModel.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816
  1. #include "StdAfx.h"
  2. #include "IOCPModel.h"
  3. //#include "MainDlg.h"
  4. // 每一个处理器上产生多少个线程(为了最大限度的提升服务器性能,详见配套文档)
  5. #define WORKER_THREADS_PER_PROCESSOR 2
  6. // 同时投递的Accept请求的数量(这个要根据实际的情况灵活设置)
  7. #define MAX_POST_ACCEPT 10
  8. // 传递给Worker线程的退出信号
  9. #define EXIT_CODE NULL
  10. // 释放指针和句柄资源的宏
  11. // 释放指针宏
  12. #define RELEASE(x) {if(x != NULL ){delete x;x=NULL;}}
  13. // 释放句柄宏
  14. #define RELEASE_HANDLE(x) {if(x != NULL && x!=INVALID_HANDLE_VALUE){ CloseHandle(x);x = NULL;}}
  15. // 释放Socket宏
  16. #define RELEASE_SOCKET(x) {if(x !=INVALID_SOCKET) { closesocket(x);x=INVALID_SOCKET;}}
  17. CIOCPModel::CIOCPModel(void):
  18. m_nThreads(0),
  19. m_hShutdownEvent(NULL),
  20. m_hIOCompletionPort(NULL),
  21. m_phWorkerThreads(NULL),
  22. m_strIP(DEFAULT_IP),
  23. m_nPort(DEFAULT_PORT),
  24. m_pMain(NULL),
  25. m_lpfnAcceptEx( NULL ),
  26. m_pListenContext( NULL )
  27. {
  28. }
  29. CIOCPModel::~CIOCPModel(void)
  30. {
  31. // 确保资源彻底释放
  32. this->Stop();
  33. }
  34. ///////////////////////////////////////////////////////////////////
  35. // 工作者线程: 为IOCP请求服务的工作者线程
  36. // 也就是每当完成端口上出现了完成数据包,就将之取出来进行处理的线程
  37. ///////////////////////////////////////////////////////////////////
  38. DWORD WINAPI CIOCPModel::_WorkerThread(LPVOID lpParam)
  39. {
  40. THREADPARAMS_WORKER* pParam = (THREADPARAMS_WORKER*)lpParam;
  41. CIOCPModel* pIOCPModel = (CIOCPModel*)pParam->pIOCPModel;
  42. int nThreadNo = (int)pParam->nThreadNo;
  43. pIOCPModel->_ShowMessage(_T("工作者线程启动,ID: %d."),nThreadNo);
  44. OVERLAPPED *pOverlapped = NULL;
  45. PER_SOCKET_CONTEXT *pSocketContext = NULL;
  46. DWORD dwBytesTransfered = 0;
  47. // 循环处理请求,知道接收到Shutdown信息为止
  48. while (WAIT_OBJECT_0 != WaitForSingleObject(pIOCPModel->m_hShutdownEvent, 0))
  49. {
  50. BOOL bReturn = GetQueuedCompletionStatus(
  51. pIOCPModel->m_hIOCompletionPort,
  52. &dwBytesTransfered,
  53. (PULONG_PTR)&pSocketContext,
  54. &pOverlapped,
  55. INFINITE);
  56. // 如果收到的是退出标志,则直接退出
  57. if ( EXIT_CODE==(DWORD)pSocketContext )
  58. {
  59. break;
  60. }
  61. // 判断是否出现了错误
  62. if( !bReturn )
  63. {
  64. DWORD dwErr = GetLastError();
  65. // 显示一下提示信息
  66. if( !pIOCPModel->HandleError( pSocketContext,dwErr ) )
  67. {
  68. break;
  69. }
  70. continue;
  71. }
  72. else
  73. {
  74. // 读取传入的参数
  75. PER_IO_CONTEXT* pIoContext = CONTAINING_RECORD(pOverlapped, PER_IO_CONTEXT, m_Overlapped);
  76. // 判断是否有客户端断开了
  77. if((0 == dwBytesTransfered) && ( RECV_POSTED==pIoContext->m_OpType || SEND_POSTED==pIoContext->m_OpType))
  78. {
  79. pIOCPModel->_ShowMessage( _T("客户端 %s:%d 断开连接."),inet_ntoa(pSocketContext->m_ClientAddr.sin_addr), ntohs(pSocketContext->m_ClientAddr.sin_port) );
  80. // 释放掉对应的资源
  81. pIOCPModel->_RemoveContext( pSocketContext );
  82. continue;
  83. }
  84. else
  85. {
  86. switch( pIoContext->m_OpType )
  87. {
  88. // Accept
  89. case ACCEPT_POSTED:
  90. {
  91. // 为了增加代码可读性,这里用专门的_DoAccept函数进行处理连入请求
  92. pIOCPModel->_DoAccpet( pSocketContext, pIoContext );
  93. }
  94. break;
  95. // RECV
  96. case RECV_POSTED:
  97. {
  98. // 为了增加代码可读性,这里用专门的_DoRecv函数进行处理接收请求
  99. pIOCPModel->_DoRecv( pSocketContext,pIoContext );
  100. }
  101. break;
  102. // SEND
  103. // 这里略过不写了,要不代码太多了,不容易理解,Send操作相对来讲简单一些
  104. case SEND_POSTED:
  105. {
  106. }
  107. break;
  108. default:
  109. // 不应该执行到这里
  110. TRACE(_T("_WorkThread中的 pIoContext->m_OpType 参数异常.\n"));
  111. break;
  112. } //switch
  113. }//if
  114. }//if
  115. }//while
  116. TRACE(_T("工作者线程 %d 号退出.\n"),nThreadNo);
  117. // 释放线程参数
  118. RELEASE(lpParam);
  119. return 0;
  120. }
  121. //====================================================================================
  122. //
  123. // 系统初始化和终止
  124. //
  125. //====================================================================================
  126. ////////////////////////////////////////////////////////////////////
  127. // 初始化WinSock 2.2
  128. bool CIOCPModel::LoadSocketLib()
  129. {
  130. WSADATA wsaData;
  131. int nResult;
  132. nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
  133. // 错误(一般都不可能出现)
  134. if (NO_ERROR != nResult)
  135. {
  136. this->_ShowMessage(_T("初始化WinSock 2.2失败!\n"));
  137. return false;
  138. }
  139. return true;
  140. }
  141. //////////////////////////////////////////////////////////////////
  142. // 启动服务器
  143. bool CIOCPModel::Start(unsigned int port)
  144. {
  145. // 初始化线程互斥量
  146. InitializeCriticalSection(&m_csContextList);
  147. // 建立系统退出的事件通知
  148. m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  149. // 初始化IOCP
  150. if (false == _InitializeIOCP())
  151. {
  152. this->_ShowMessage(_T("初始化IOCP失败!\n"));
  153. return false;
  154. }
  155. else
  156. {
  157. this->_ShowMessage(_T("\nIOCP初始化完毕\n."));
  158. }
  159. // 初始化Socket
  160. if( false==_InitializeListenSocket(port) )
  161. {
  162. this->_ShowMessage(_T("Listen Socket初始化失败!\n"));
  163. this->_DeInitialize();
  164. return false;
  165. }
  166. else
  167. {
  168. this->_ShowMessage(_T("Listen Socket初始化完毕."));
  169. }
  170. this->_ShowMessage(_T("系统准备就绪,等候连接....\n"));
  171. return true;
  172. }
  173. ////////////////////////////////////////////////////////////////////
  174. // 开始发送系统退出消息,退出完成端口和线程资源
  175. void CIOCPModel::Stop()
  176. {
  177. if( m_pListenContext!=NULL && m_pListenContext->m_Socket!=INVALID_SOCKET )
  178. {
  179. // 激活关闭消息通知
  180. SetEvent(m_hShutdownEvent);
  181. for (int i = 0; i < m_nThreads; i++)
  182. {
  183. // 通知所有的完成端口操作退出
  184. PostQueuedCompletionStatus(m_hIOCompletionPort, 0, (DWORD)EXIT_CODE, NULL);
  185. }
  186. // 等待所有的客户端资源退出
  187. WaitForMultipleObjects(m_nThreads, m_phWorkerThreads, TRUE, INFINITE);
  188. // 清除客户端列表信息
  189. this->_ClearContextList();
  190. // 释放其他资源
  191. this->_DeInitialize();
  192. this->_ShowMessage(_T("停止监听\n"));
  193. }
  194. }
  195. ////////////////////////////////
  196. // 初始化完成端口
  197. bool CIOCPModel::_InitializeIOCP()
  198. {
  199. // 建立第一个完成端口
  200. m_hIOCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0 );
  201. if ( NULL == m_hIOCompletionPort)
  202. {
  203. this->_ShowMessage(_T("建立完成端口失败!错误代码: %d!\n"), WSAGetLastError());
  204. return false;
  205. }
  206. // 根据本机中的处理器数量,建立对应的线程数
  207. m_nThreads = WORKER_THREADS_PER_PROCESSOR * _GetNoOfProcessors();
  208. // 为工作者线程初始化句柄
  209. m_phWorkerThreads = new HANDLE[m_nThreads];
  210. // 根据计算出来的数量建立工作者线程
  211. DWORD nThreadID;
  212. for (int i = 0; i < m_nThreads; i++)
  213. {
  214. THREADPARAMS_WORKER* pThreadParams = new THREADPARAMS_WORKER;
  215. pThreadParams->pIOCPModel = this;
  216. pThreadParams->nThreadNo = i+1;
  217. m_phWorkerThreads[i] = ::CreateThread(0, 0, _WorkerThread, (void *)pThreadParams, 0, &nThreadID);
  218. }
  219. TRACE(" 建立 _WorkerThread %d 个.\n", m_nThreads );
  220. return true;
  221. }
  222. /////////////////////////////////////////////////////////////////
  223. // 初始化Socket
  224. bool CIOCPModel::_InitializeListenSocket(unsigned int port)
  225. {
  226. // AcceptEx 和 GetAcceptExSockaddrs 的GUID,用于导出函数指针
  227. GUID GuidAcceptEx = WSAID_ACCEPTEX;
  228. GUID GuidGetAcceptExSockAddrs = WSAID_GETACCEPTEXSOCKADDRS;
  229. // 服务器地址信息,用于绑定Socket
  230. struct sockaddr_in ServerAddress;
  231. // 生成用于监听的Socket的信息
  232. m_pListenContext = new PER_SOCKET_CONTEXT;
  233. // 需要使用重叠IO,必须得使用WSASocket来建立Socket,才可以支持重叠IO操作
  234. m_pListenContext->m_Socket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
  235. if (INVALID_SOCKET == m_pListenContext->m_Socket)
  236. {
  237. this->_ShowMessage(_T("初始化Socket失败,错误代码: %d.\n"), WSAGetLastError());
  238. return false;
  239. }
  240. else
  241. {
  242. TRACE("WSASocket() 完成.\n");
  243. }
  244. // 将Listen Socket绑定至完成端口中
  245. if( NULL== CreateIoCompletionPort( (HANDLE)m_pListenContext->m_Socket, m_hIOCompletionPort,(DWORD)m_pListenContext, 0))
  246. {
  247. this->_ShowMessage(_T("绑定 Listen Socket至完成端口失败!错误代码: %d/n"), WSAGetLastError());
  248. RELEASE_SOCKET( m_pListenContext->m_Socket );
  249. return false;
  250. }
  251. else
  252. {
  253. TRACE("Listen Socket绑定完成端口 完成.\n");
  254. }
  255. // 填充地址信息
  256. ZeroMemory((char *)&ServerAddress, sizeof(ServerAddress));
  257. ServerAddress.sin_family = AF_INET;
  258. // 这里可以绑定任何可用的IP地址,或者绑定一个指定的IP地址
  259. ServerAddress.sin_addr.s_addr = htonl(INADDR_ANY);
  260. //ServerAddress.sin_addr.s_addr = inet_addr(m_strIP.GetString());
  261. ServerAddress.sin_port = htons(port);
  262. // 绑定地址和端口
  263. if (SOCKET_ERROR == bind(m_pListenContext->m_Socket, (struct sockaddr *) &ServerAddress, sizeof(ServerAddress)))
  264. {
  265. this->_ShowMessage(_T("bind()函数执行错误! 错误代码: %d/n"), WSAGetLastError());
  266. return false;
  267. }
  268. else
  269. {
  270. TRACE("bind() 完成.\n");
  271. }
  272. // 开始进行监听
  273. if (SOCKET_ERROR == listen(m_pListenContext->m_Socket,SOMAXCONN))
  274. {
  275. this->_ShowMessage(_T("Listen()函数执行出现错误; 错误代码: %d/n"), WSAGetLastError());
  276. return false;
  277. }
  278. else
  279. {
  280. TRACE("Listen() 完成.\n");
  281. }
  282. // 使用AcceptEx函数,因为这个是属于WinSock2规范之外的微软另外提供的扩展函数
  283. // 所以需要额外获取一下函数的指针,
  284. // 获取AcceptEx函数指针
  285. DWORD dwBytes = 0;
  286. if(SOCKET_ERROR == WSAIoctl(
  287. m_pListenContext->m_Socket,
  288. SIO_GET_EXTENSION_FUNCTION_POINTER,
  289. &GuidAcceptEx,
  290. sizeof(GuidAcceptEx),
  291. &m_lpfnAcceptEx,
  292. sizeof(m_lpfnAcceptEx),
  293. &dwBytes,
  294. NULL,
  295. NULL))
  296. {
  297. this->_ShowMessage(_T("WSAIoctl 未能获取AcceptEx函数指针。错误代码: %d\n"), WSAGetLastError());
  298. this->_DeInitialize();
  299. return false;
  300. }
  301. // 获取GetAcceptExSockAddrs函数指针,也是同理
  302. if(SOCKET_ERROR == WSAIoctl(
  303. m_pListenContext->m_Socket,
  304. SIO_GET_EXTENSION_FUNCTION_POINTER,
  305. &GuidGetAcceptExSockAddrs,
  306. sizeof(GuidGetAcceptExSockAddrs),
  307. &m_lpfnGetAcceptExSockAddrs,
  308. sizeof(m_lpfnGetAcceptExSockAddrs),
  309. &dwBytes,
  310. NULL,
  311. NULL))
  312. {
  313. this->_ShowMessage(_T("WSAIoctl 未能获取GuidGetAcceptExSockAddrs函数指针。错误代码: %d\n"), WSAGetLastError());
  314. this->_DeInitialize();
  315. return false;
  316. }
  317. // 为AcceptEx 准备参数,然后投递AcceptEx I/O请求
  318. for( int i=0;i<MAX_POST_ACCEPT;i++ )
  319. {
  320. // 新建一个IO_CONTEXT
  321. PER_IO_CONTEXT* pAcceptIoContext = m_pListenContext->GetNewIoContext();
  322. if( false==this->_PostAccept( pAcceptIoContext ) )
  323. {
  324. m_pListenContext->RemoveContext(pAcceptIoContext);
  325. return false;
  326. }
  327. }
  328. this->_ShowMessage( _T("投递 %d 个AcceptEx请求完毕"),MAX_POST_ACCEPT );
  329. return true;
  330. }
  331. ////////////////////////////////////////////////////////////
  332. // 最后释放掉所有资源
  333. void CIOCPModel::_DeInitialize()
  334. {
  335. // 删除客户端列表的互斥量
  336. DeleteCriticalSection(&m_csContextList);
  337. // 关闭系统退出事件句柄
  338. RELEASE_HANDLE(m_hShutdownEvent);
  339. // 释放工作者线程句柄指针
  340. for( int i=0;i<m_nThreads;i++ )
  341. {
  342. RELEASE_HANDLE(m_phWorkerThreads[i]);
  343. }
  344. RELEASE(m_phWorkerThreads);
  345. // 关闭IOCP句柄
  346. RELEASE_HANDLE(m_hIOCompletionPort);
  347. // 关闭监听Socket
  348. RELEASE(m_pListenContext);
  349. this->_ShowMessage(_T("释放资源完毕.\n"));
  350. }
  351. //====================================================================================
  352. //
  353. // 投递完成端口请求
  354. //
  355. //====================================================================================
  356. //////////////////////////////////////////////////////////////////
  357. // 投递Accept请求
  358. bool CIOCPModel::_PostAccept( PER_IO_CONTEXT* pAcceptIoContext )
  359. {
  360. ASSERT( INVALID_SOCKET!=m_pListenContext->m_Socket );
  361. // 准备参数
  362. DWORD dwBytes = 0;
  363. pAcceptIoContext->m_OpType = ACCEPT_POSTED;
  364. WSABUF *p_wbuf = &pAcceptIoContext->m_wsaBuf;
  365. OVERLAPPED *p_ol = &pAcceptIoContext->m_Overlapped;
  366. // 为以后新连入的客户端先准备好Socket( 这个是与传统accept最大的区别 )
  367. pAcceptIoContext->m_sockAccept = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
  368. if( INVALID_SOCKET==pAcceptIoContext->m_sockAccept )
  369. {
  370. _ShowMessage(_T("创建用于Accept的Socket失败!错误代码: %d"), WSAGetLastError());
  371. return false;
  372. }
  373. // 投递AcceptEx
  374. if(FALSE == m_lpfnAcceptEx( m_pListenContext->m_Socket, pAcceptIoContext->m_sockAccept, p_wbuf->buf, p_wbuf->len - ((sizeof(SOCKADDR_IN)+16)*2),
  375. sizeof(SOCKADDR_IN)+16, sizeof(SOCKADDR_IN)+16, &dwBytes, p_ol))
  376. {
  377. if(WSA_IO_PENDING != WSAGetLastError())
  378. {
  379. _ShowMessage(_T("投递 AcceptEx 请求失败,错误代码: %d"), WSAGetLastError());
  380. return false;
  381. }
  382. }
  383. return true;
  384. }
  385. ////////////////////////////////////////////////////////////
  386. // 在有客户端连入的时候,进行处理
  387. // 流程有点复杂,你要是看不懂的话,就看配套的文档吧....
  388. // 如果能理解这里的话,完成端口的机制你就消化了一大半了
  389. // 总之你要知道,传入的是ListenSocket的Context,我们需要复制一份出来给新连入的Socket用
  390. // 原来的Context还是要在上面继续投递下一个Accept请求
  391. //
  392. bool CIOCPModel::_DoAccpet( PER_SOCKET_CONTEXT* pSocketContext, PER_IO_CONTEXT* pIoContext )
  393. {
  394. SOCKADDR_IN* ClientAddr = NULL;
  395. SOCKADDR_IN* LocalAddr = NULL;
  396. int remoteLen = sizeof(SOCKADDR_IN), localLen = sizeof(SOCKADDR_IN);
  397. ///////////////////////////////////////////////////////////////////////////
  398. // 1. 首先取得连入客户端的地址信息
  399. // 这个 m_lpfnGetAcceptExSockAddrs 不得了啊~~~~~~
  400. // 不但可以取得客户端和本地端的地址信息,还能顺便取出客户端发来的第一组数据
  401. this->m_lpfnGetAcceptExSockAddrs(pIoContext->m_wsaBuf.buf, pIoContext->m_wsaBuf.len - ((sizeof(SOCKADDR_IN)+16)*2),
  402. sizeof(SOCKADDR_IN)+16, sizeof(SOCKADDR_IN)+16, (LPSOCKADDR*)&LocalAddr, &localLen, (LPSOCKADDR*)&ClientAddr, &remoteLen);
  403. this->_ShowMessage( _T("客户端 %s:%d 连入."), inet_ntoa(ClientAddr->sin_addr), ntohs(ClientAddr->sin_port) );
  404. //this->_ShowMessage( _T("客户额 %s:%d 信息:%s."),inet_ntoa(ClientAddr->sin_addr), ntohs(ClientAddr->sin_port),pIoContext->m_wsaBuf.buf );
  405. OnEventWriteLog(pIoContext);
  406. //////////////////////////////////////////////////////////////////////////////////////////////////////
  407. // 2. 这里需要注意,这里传入的这个是ListenSocket上的Context,这个Context我们还需要用于监听下一个连接
  408. // 所以我还得要将ListenSocket上的Context复制出来一份为新连入的Socket新建一个SocketContext
  409. PER_SOCKET_CONTEXT* pNewSocketContext = new PER_SOCKET_CONTEXT;
  410. pNewSocketContext->m_Socket = pIoContext->m_sockAccept;
  411. memcpy(&(pNewSocketContext->m_ClientAddr), ClientAddr, sizeof(SOCKADDR_IN));
  412. // 参数设置完毕,将这个Socket和完成端口绑定(这也是一个关键步骤)
  413. if( false==this->_AssociateWithIOCP( pNewSocketContext ) )
  414. {
  415. RELEASE( pNewSocketContext );
  416. return false;
  417. }
  418. ///////////////////////////////////////////////////////////////////////////////////////////////////
  419. // 3. 继续,建立其下的IoContext,用于在这个Socket上投递第一个Recv数据请求
  420. PER_IO_CONTEXT* pNewIoContext = pNewSocketContext->GetNewIoContext();
  421. pNewIoContext->m_OpType = RECV_POSTED;
  422. pNewIoContext->m_sockAccept = pNewSocketContext->m_Socket;
  423. // 如果Buffer需要保留,就自己拷贝一份出来
  424. //memcpy( pNewIoContext->m_szBuffer,pIoContext->m_szBuffer,MAX_BUFFER_LEN );
  425. // 绑定完毕之后,就可以开始在这个Socket上投递完成请求了
  426. if( false==this->_PostRecv( pNewIoContext) )
  427. {
  428. pNewSocketContext->RemoveContext( pNewIoContext );
  429. return false;
  430. }
  431. /////////////////////////////////////////////////////////////////////////////////////////////////
  432. // 4. 如果投递成功,那么就把这个有效的客户端信息,加入到ContextList中去(需要统一管理,方便释放资源)
  433. this->_AddToContextList( pNewSocketContext );
  434. ////////////////////////////////////////////////////////////////////////////////////////////////
  435. // 5. 使用完毕之后,把Listen Socket的那个IoContext重置,然后准备投递新的AcceptEx
  436. pIoContext->ResetBuffer();
  437. return this->_PostAccept( pIoContext );
  438. }
  439. ////////////////////////////////////////////////////////////////////
  440. // 投递接收数据请求
  441. bool CIOCPModel::_PostRecv( PER_IO_CONTEXT* pIoContext )
  442. {
  443. // 初始化变量
  444. DWORD dwFlags = 0;
  445. DWORD dwBytes = 0;
  446. WSABUF *p_wbuf = &pIoContext->m_wsaBuf;
  447. OVERLAPPED *p_ol = &pIoContext->m_Overlapped;
  448. pIoContext->ResetBuffer();
  449. pIoContext->m_OpType = RECV_POSTED;
  450. // 初始化完成后,,投递WSARecv请求
  451. int nBytesRecv = WSARecv( pIoContext->m_sockAccept, p_wbuf, 1, &dwBytes, &dwFlags, p_ol, NULL );
  452. // 如果返回值错误,并且错误的代码并非是Pending的话,那就说明这个重叠请求失败了
  453. if ((SOCKET_ERROR == nBytesRecv) && (WSA_IO_PENDING != WSAGetLastError()))
  454. {
  455. this->_ShowMessage(_T("投递第一个WSARecv失败!"));
  456. return false;
  457. }
  458. return true;
  459. }
  460. /////////////////////////////////////////////////////////////////
  461. // 在有接收的数据到达的时候,进行处理
  462. bool CIOCPModel::_DoRecv( PER_SOCKET_CONTEXT* pSocketContext, PER_IO_CONTEXT* pIoContext )
  463. {
  464. // 先把上一次的数据显示出现,然后就重置状态,发出下一个Recv请求
  465. SOCKADDR_IN* ClientAddr = &pSocketContext->m_ClientAddr;
  466. //this->_ShowMessage( _T("收到 %s:%d 信息:%s"),inet_ntoa(ClientAddr->sin_addr), ntohs(ClientAddr->sin_port), pIoContext->m_wsaBuf.buf );
  467. OnEventWriteLog(pIoContext);
  468. // 然后开始投递下一个WSARecv请求
  469. return _PostRecv( pIoContext );
  470. }
  471. /////////////////////////////////////////////////////
  472. // 将句柄(Socket)绑定到完成端口中
  473. bool CIOCPModel::_AssociateWithIOCP( PER_SOCKET_CONTEXT *pContext )
  474. {
  475. // 将用于和客户端通信的SOCKET绑定到完成端口中
  476. HANDLE hTemp = CreateIoCompletionPort((HANDLE)pContext->m_Socket, m_hIOCompletionPort, (DWORD)pContext, 0);
  477. if (NULL == hTemp)
  478. {
  479. this->_ShowMessage(_T("执行CreateIoCompletionPort()出现错误.错误代码:%d"),GetLastError());
  480. return false;
  481. }
  482. return true;
  483. }
  484. //====================================================================================
  485. //
  486. // ContextList 相关操作
  487. //
  488. //====================================================================================
  489. //////////////////////////////////////////////////////////////
  490. // 将客户端的相关信息存储到数组中
  491. void CIOCPModel::_AddToContextList( PER_SOCKET_CONTEXT *pHandleData )
  492. {
  493. EnterCriticalSection(&m_csContextList);
  494. m_arrayClientContext.Add(pHandleData);
  495. LeaveCriticalSection(&m_csContextList);
  496. }
  497. ////////////////////////////////////////////////////////////////
  498. // 移除某个特定的Context
  499. void CIOCPModel::_RemoveContext( PER_SOCKET_CONTEXT *pSocketContext )
  500. {
  501. EnterCriticalSection(&m_csContextList);
  502. for( int i=0;i<m_arrayClientContext.GetCount();i++ )
  503. {
  504. if( pSocketContext==m_arrayClientContext.GetAt(i) )
  505. {
  506. RELEASE( pSocketContext );
  507. m_arrayClientContext.RemoveAt(i);
  508. break;
  509. }
  510. }
  511. LeaveCriticalSection(&m_csContextList);
  512. }
  513. ////////////////////////////////////////////////////////////////
  514. // 清空客户端信息
  515. void CIOCPModel::_ClearContextList()
  516. {
  517. EnterCriticalSection(&m_csContextList);
  518. for( int i=0;i<m_arrayClientContext.GetCount();i++ )
  519. {
  520. delete m_arrayClientContext.GetAt(i);
  521. }
  522. m_arrayClientContext.RemoveAll();
  523. LeaveCriticalSection(&m_csContextList);
  524. }
  525. //====================================================================================
  526. //
  527. // 其他辅助函数定义
  528. //
  529. //====================================================================================
  530. ////////////////////////////////////////////////////////////////////
  531. // 获得本机的IP地址
  532. CString CIOCPModel::GetLocalIP()
  533. {
  534. // 获得本机主机名
  535. char hostname[MAX_PATH] = {0};
  536. gethostname(hostname,MAX_PATH);
  537. struct hostent FAR* lpHostEnt = gethostbyname(hostname);
  538. if(lpHostEnt == NULL)
  539. {
  540. return DEFAULT_IP;
  541. }
  542. // 取得IP地址列表中的第一个为返回的IP(因为一台主机可能会绑定多个IP)
  543. LPSTR lpAddr = lpHostEnt->h_addr_list[0];
  544. // 将IP地址转化成字符串形式
  545. struct in_addr inAddr;
  546. memmove(&inAddr,lpAddr,4);
  547. m_strIP = CString( inet_ntoa(inAddr) );
  548. return m_strIP;
  549. }
  550. ///////////////////////////////////////////////////////////////////
  551. // 获得本机中处理器的数量
  552. int CIOCPModel::_GetNoOfProcessors()
  553. {
  554. SYSTEM_INFO si;
  555. GetSystemInfo(&si);
  556. return si.dwNumberOfProcessors;
  557. }
  558. /////////////////////////////////////////////////////////////////////
  559. // 在主界面中显示提示信息
  560. void CIOCPModel::_ShowMessage(const CString szFormat,...) const
  561. {
  562. // 根据传入的参数格式化字符串
  563. CString strMessage;
  564. va_list arglist;
  565. // 处理变长参数
  566. va_start(arglist, szFormat);
  567. strMessage.FormatV(szFormat,arglist);
  568. va_end(arglist);
  569. #if 0
  570. // 在主界面中显示
  571. CMainDlg* pMain = (CMainDlg*)m_pMain;
  572. if( m_pMain!=NULL )
  573. {
  574. pMain->AddInformation(strMessage);
  575. TRACE( strMessage+_T("\n") );
  576. }
  577. #else
  578. Global::WriteTextLog(strMessage);
  579. #endif
  580. }
  581. void CIOCPModel::OnEventWriteLog(PER_IO_CONTEXT* pIoContext)
  582. {
  583. if ( pIoContext == NULL)
  584. return;
  585. // 解析Json字符串;
  586. cJSON *pJson = cJSON_Parse(Global::DeCode_URLUNICODE(pIoContext->m_wsaBuf.buf).c_str());
  587. if ( pJson )
  588. {
  589. std::string report_type = cJSON_GetObjectItem(pJson, _T("ReportType")) ? cJSON_GetObjectItem(pJson, _T("ReportType"))->valuestring : "";
  590. if ( _tcsicmp(report_type.c_str(), _T("printLog")) == 0 )
  591. {
  592. if ( Global::g_bEnableLog )
  593. {
  594. std::string report_data = cJSON_GetObjectItem(pJson, _T("prinMsg")) ? cJSON_GetObjectItem(pJson, _T("prinMsg"))->valuestring : "";
  595. Global::WritePythonLog(report_data.c_str());
  596. Global::g_time = time(NULL);
  597. Global::g_lastTime = COleDateTime::GetCurrentTime();
  598. }
  599. }
  600. else if (_tcsicmp(report_type.c_str(), _T("shutdown")) == 0 || _tcsicmp(report_type.c_str(), _T("reboot")) == 0)
  601. {
  602. // 记录时间;
  603. Global::g_notify.notify = true;
  604. Global::g_notify.report_type = report_type;
  605. Global::g_notify.datetime = cJSON_GetObjectItem(pJson, _T("prinMsg")) ? cJSON_GetObjectItem(pJson, _T("prinMsg"))->valueint : 0; // 发生的时间;
  606. #ifdef _DEBUG
  607. TRACE2("通知时间%ld, 通知类型%s\n\n", Global::g_notify.datetime, Global::g_notify.report_type.c_str());
  608. #endif
  609. }
  610. cJSON_Delete(pJson);
  611. pJson = NULL;
  612. }
  613. }
  614. /////////////////////////////////////////////////////////////////////
  615. // 判断客户端Socket是否已经断开,否则在一个无效的Socket上投递WSARecv操作会出现异常
  616. // 使用的方法是尝试向这个socket发送数据,判断这个socket调用的返回值
  617. // 因为如果客户端网络异常断开(例如客户端崩溃或者拔掉网线等)的时候,服务器端是无法收到客户端断开的通知的
  618. bool CIOCPModel::_IsSocketAlive(SOCKET s)
  619. {
  620. int nByteSent=send(s,"",0,0);
  621. if (-1 == nByteSent) return false;
  622. return true;
  623. }
  624. ///////////////////////////////////////////////////////////////////
  625. // 显示并处理完成端口上的错误
  626. bool CIOCPModel::HandleError( PER_SOCKET_CONTEXT *pContext,const DWORD& dwErr )
  627. {
  628. // 如果是超时了,就再继续等吧
  629. if(WAIT_TIMEOUT == dwErr)
  630. {
  631. // 确认客户端是否还活着...
  632. if( !_IsSocketAlive( pContext->m_Socket) )
  633. {
  634. this->_ShowMessage( _T("检测到客户端异常退出!") );
  635. this->_RemoveContext( pContext );
  636. return true;
  637. }
  638. else
  639. {
  640. this->_ShowMessage( _T("网络操作超时!重试中...") );
  641. return true;
  642. }
  643. }
  644. // 可能是客户端异常退出了
  645. else if( ERROR_NETNAME_DELETED==dwErr )
  646. {
  647. this->_ShowMessage( _T("检测到客户端异常退出!") );
  648. this->_RemoveContext( pContext );
  649. return true;
  650. }
  651. else
  652. {
  653. this->_ShowMessage( _T("完成端口操作出现错误,线程退出。错误代码:%d"),dwErr );
  654. return false;
  655. }
  656. }