完成端口封装(修复Windows 网络与通信程序设计 可伸缩IOCP模型的bug)

前言

看过《Windows网络与通信程序设计》的人都知道,里面有一段有关于IOCP的经典封装。大大方便了“伸手党”服务器端程序的开发(我也是其中之一)。但是应用到实际程序中你会发现经常出现一个莫名奇妙的问题:一旦客户端发送的字节数过多,服务器端接受其中几条后就“死掉”了,我也深受其害,于是乎 今天花了2小时时间通读了代码,把其中的bug找到(PS:不敢保证是否还是其他bug,暂时还未发现)


问题

引起上述现象的是由于作者的小疏忽导致的,我们知道采用完成端口时要开辟多个线程(一般为CPU核心数)来监听请求,如果客户端在短时间内发送了一大堆字节,
这些字节在客户端上肯定是分多次顺序进行发送。服务端接受的时候却是多个线程(单线程CPU例外)各自接受各自的原来的顺序就被打乱了,如果只是简单拼凑起来肯定会出现问题,如何解决这个问题呢,作者已经帮我想好了。虽然各个线程处理数据的顺序可能不一直,但是投递读请求的顺序肯定是与客户端发送的顺序是一直的,于是作者CIOCPBuffer中添加了一个顺序标识 nSequenceNumber 用来标识当前读取数据的发送顺序,并建立了一个队列将当前接受CIOCPBuffer联系起来
   
   
  1. // 这是per-I/O数据。它包含了在套节字上处理I/O操作的必要信息
  2. struct CIOCPBuffer
  3. {
  4. WSAOVERLAPPED ol;
  5. SOCKET sClient; // AcceptEx接收的客户方套节字
  6. char *buff; // I/O操作使用的缓冲区
  7. int nLen; // buff缓冲区(使用的)大小
  8. ULONG nSequenceNumber; // 此I/O的序列号
  9. int nOperation; // 操作类型
  10. #define OP_ACCEPT 1
  11. #define OP_WRITE 2
  12. #define OP_READ 3
  13. CIOCPBuffer *pNext;
  14. };

读取数据的时候先判断读取数据CIOCPBuffer的nSequenceNumber与当前队列头的CIOCPBuffer是否一致,一致则循环将队列中的所有Buffer按顺序传递给用户,不一致就将它按顺序加入队列。
这种设计是不是很巧妙。


作者的代码:
   
   
  1. CIOCPBuffer *CIOCPServer::GetNextReadBuffer(CIOCPContext *pContext, CIOCPBuffer *pBuffer)
  2. {
  3. if(pBuffer != NULL)
  4. {
  5. // 如果与要读的下一个序列号相等,则读这块缓冲区
  6. if(pBuffer->nSequenceNumber == pContext->nCurrentReadSequence)
  7. {
  8. return pBuffer;
  9. }
  10. // 如果不相等,则说明没有按顺序接收数据,将这块缓冲区保存到连接的pOutOfOrderReads列表中
  11. // 列表中的缓冲区是按照其序列号从小到大的顺序排列的
  12. pBuffer->pNext = NULL;
  13. CIOCPBuffer *ptr = pContext->pOutOfOrderReads;
  14. CIOCPBuffer *pPre = NULL;
  15. while(ptr != NULL)
  16. {
  17. if(pBuffer->nSequenceNumber < ptr->nSequenceNumber)
  18. break;
  19. pPre = ptr;
  20. ptr = ptr->pNext;
  21. }
  22. if(pPre == NULL) // 应该插入到表头
  23. {
  24. pBuffer->pNext = pContext->pOutOfOrderReads;
  25. pContext->pOutOfOrderReads = pBuffer;
  26. }
  27. else // 应该插入到表的中间
  28. {
  29. pBuffer->pNext = pPre->pNext;
  30. pPre->pNext = pBuffer->pNext;
  31. }
  32. }
  33. // 检查表头元素的序列号,如果与要读的序列号一致,就将它从表中移除,返回给用户
  34. CIOCPBuffer *ptr = pContext->pOutOfOrderReads;
  35. if(ptr != NULL && (ptr->nSequenceNumber == pContext->nCurrentReadSequence))
  36. {
  37. pContext->pOutOfOrderReads = ptr->pNext;
  38. return ptr;
  39. }
  40. return NULL;
  41. }

我处理后的代码
  
  
  1. CIOCPBuffer *CIOCPServer::GetNextReadBuffer(CIOCPContext *pContext, CIOCPBuffer *pBuffer)
  2. {
  3. if(pBuffer != NULL)
  4. {
  5. // 如果与要读的下一个序列号相等,则读这块缓冲区
  6. if(pBuffer->nSequenceNumber == pContext->nCurrentReadSequence)
  7. {
  8. return pBuffer;
  9. }
  10. // 如果不相等,则说明没有按顺序接收数据,将这块缓冲区保存到连接的pOutOfOrderReads列表中
  11. // 列表中的缓冲区是按照其序列号从小到大的顺序排列的
  12. pBuffer->pNext = NULL;
  13. CIOCPBuffer *ptr = pContext->pOutOfOrderReads;
  14. CIOCPBuffer *pPre = NULL;
  15. while(ptr != NULL)
  16. {
  17. if(pBuffer->nSequenceNumber < ptr->nSequenceNumber)
  18. break;
  19. pPre = ptr;
  20. ptr = ptr->pNext;
  21. }
  22. if(pPre == NULL) // 应该插入到表头
  23. {
  24. pBuffer->pNext = pContext->pOutOfOrderReads;
  25. pContext->pOutOfOrderReads = pBuffer;
  26. }
  27. else // 应该插入到表的中间
  28. {
  29. pBuffer->pNext = pPre->pNext;
  30. pPre->pNext = pBuffer;
  31. }
  32. }
  33. // 检查表头元素的序列号,如果与要读的序列号一致,就将它从表中移除,返回给用户
  34. CIOCPBuffer *ptr = pContext->pOutOfOrderReads;
  35. if(ptr != NULL && (ptr->nSequenceNumber == pContext->nCurrentReadSequence))
  36. {
  37. pContext->pOutOfOrderReads = ptr->pNext;
  38. return ptr;
  39. }
  40. return NULL;
  41. }

对,你没有看错,仅仅是这么简单一句代码,就造成了整个程序的错误。 看不懂的同学自己去看看如何向链表中见插入一个元素吧。

废话不多说上一段完整程序:
   
   
  1. // IOCP.h文件
  2. #ifndef __IOCP_H__
  3. #define __IOCP_H__
  4. #include <winsock2.h>
  5. #include <windows.h>
  6. #include <Mswsock.h>
  7. #define BUFFER_SIZE 1024*2 // I/O请求的缓冲区大小
  8. // 这是per-I/O数据。它包含了在套节字上处理I/O操作的必要信息
  9. struct CIOCPBuffer
  10. {
  11. WSAOVERLAPPED ol;
  12. SOCKET sClient; // AcceptEx接收的客户方套节字
  13. char *buff; // I/O操作使用的缓冲区
  14. int nLen; // buff缓冲区(使用的)大小
  15. ULONG nSequenceNumber; // 此I/O的序列号
  16. int nOperation; // 操作类型
  17. #define OP_ACCEPT 1
  18. #define OP_WRITE 2
  19. #define OP_READ 3
  20. CIOCPBuffer *pNext;
  21. };
  22. // 这是per-Handle数据。它包含了一个套节字的信息
  23. struct CIOCPContext
  24. {
  25. SOCKET s; // 套节字句柄
  26. SOCKADDR_IN addrLocal; // 连接的本地地址
  27. SOCKADDR_IN addrRemote; // 连接的远程地址
  28. BOOL bClosing; // 套节字是否关闭
  29. int nOutstandingRecv; // 此套节字上抛出的重叠操作的数量
  30. int nOutstandingSend;
  31. ULONG nReadSequence; // 安排给接收的下一个序列号
  32. ULONG nCurrentReadSequence; // 当前要读的序列号
  33. CIOCPBuffer *pOutOfOrderReads; // 记录没有按顺序完成的读I/O
  34. CRITICAL_SECTION Lock; // 保护这个结构
  35. CIOCPContext *pNext;
  36. };
  37. class CIOCPServer // 处理线程
  38. {
  39. public:
  40. CIOCPServer();
  41. ~CIOCPServer();
  42. // 开始服务
  43. BOOL Start(int nPort = 4567, int nMaxConnections = 2000,
  44. int nMaxFreeBuffers = 200, int nMaxFreeContexts = 100, int nInitialReads = 4);
  45. // 停止服务
  46. void Shutdown();
  47. // 关闭一个连接和关闭所有连接
  48. void CloseAConnection(CIOCPContext *pContext);
  49. void CloseAllConnections();
  50. // 取得当前的连接数量
  51. ULONG GetCurrentConnection() { return m_nCurrentConnection; }
  52. // 向指定客户发送文本
  53. BOOL SendText(CIOCPContext *pContext, char *pszText, int nLen);
  54. // 获得本机处理器的数量
  55. static int _GetNoOfProcessors();
  56. protected:
  57. // 申请和释放缓冲区对象
  58. CIOCPBuffer *AllocateBuffer(int nLen);
  59. void ReleaseBuffer(CIOCPBuffer *pBuffer);
  60. // 申请和释放套节字上下文
  61. CIOCPContext *AllocateContext(SOCKET s);
  62. void ReleaseContext(CIOCPContext *pContext);
  63. // 释放空闲缓冲区对象列表和空闲上下文对象列表
  64. void FreeBuffers();
  65. void FreeContexts();
  66. // 向连接列表中添加一个连接
  67. BOOL AddAConnection(CIOCPContext *pContext);
  68. // 插入和移除未决的接受请求
  69. BOOL InsertPendingAccept(CIOCPBuffer *pBuffer);
  70. BOOL RemovePendingAccept(CIOCPBuffer *pBuffer);
  71. // 取得下一个要读取的
  72. CIOCPBuffer *GetNextReadBuffer(CIOCPContext *pContext, CIOCPBuffer *pBuffer);
  73. // 投递接受I/O、发送I/O、接收I/O
  74. BOOL PostAccept(CIOCPBuffer *pBuffer);
  75. BOOL PostSend(CIOCPContext *pContext, CIOCPBuffer *pBuffer);
  76. BOOL PostRecv(CIOCPContext *pContext, CIOCPBuffer *pBuffer);
  77. void HandleIO(DWORD dwKey, CIOCPBuffer *pBuffer, DWORD dwTrans, int nError);
  78. // 事件通知函数
  79. // 建立了一个新的连接
  80. virtual void OnConnectionEstablished(CIOCPContext *pContext, CIOCPBuffer *pBuffer);
  81. // 一个连接关闭
  82. virtual void OnConnectionClosing(CIOCPContext *pContext, CIOCPBuffer *pBuffer);
  83. // 在一个连接上发生了错误
  84. virtual void OnConnectionError(CIOCPContext *pContext, CIOCPBuffer *pBuffer, int nError);
  85. // 一个连接上的读操作完成
  86. virtual void OnReadCompleted(CIOCPContext *pContext, CIOCPBuffer *pBuffer);
  87. // 一个连接上的写操作完成
  88. virtual void OnWriteCompleted(CIOCPContext *pContext, CIOCPBuffer *pBuffer);
  89. protected:
  90. // 记录空闲结构信息
  91. CIOCPBuffer *m_pFreeBufferList;
  92. CIOCPContext *m_pFreeContextList;
  93. int m_nFreeBufferCount;
  94. int m_nFreeContextCount;
  95. CRITICAL_SECTION m_FreeBufferListLock;
  96. CRITICAL_SECTION m_FreeContextListLock;
  97. // 记录抛出的Accept请求
  98. CIOCPBuffer *m_pPendingAccepts; // 抛出请求列表。
  99. long m_nPendingAcceptCount;
  100. CRITICAL_SECTION m_PendingAcceptsLock;
  101. // 记录连接列表
  102. CIOCPContext *m_pConnectionList;
  103. int m_nCurrentConnection;
  104. CRITICAL_SECTION m_ConnectionListLock;
  105. // 用于投递Accept请求
  106. HANDLE m_hAcceptEvent;
  107. HANDLE m_hRepostEvent;
  108. LONG m_nRepostCount;
  109. int m_nThread;
  110. int m_nPort; // 服务器监听的端口
  111. int m_nInitialAccepts;
  112. int m_nInitialReads;
  113. int m_nMaxAccepts;
  114. int m_nMaxSends;
  115. int m_nMaxFreeBuffers;
  116. int m_nMaxFreeContexts;
  117. int m_nMaxConnections;
  118. HANDLE m_hListenThread; // 监听线程
  119. HANDLE m_hCompletion; // 完成端口句柄
  120. SOCKET m_sListen; // 监听套节字句柄
  121. LPFN_ACCEPTEX m_lpfnAcceptEx; // AcceptEx函数地址
  122. LPFN_GETACCEPTEXSOCKADDRS m_lpfnGetAcceptExSockaddrs; // GetAcceptExSockaddrs函数地址
  123. BOOL m_bShutDown; // 用于通知监听线程退出
  124. BOOL m_bServerStarted; // 记录服务是否启动
  125. HANDLE m_hMutex;
  126. private: // 线程函数
  127. static DWORD WINAPI _ListenThreadProc(LPVOID lpParam);
  128. static DWORD WINAPI _WorkerThreadProc(LPVOID lpParam);
  129. };
  130. #endif // __IOCP_H__

   
   
  1. //
  2. // IOCP.cpp文件
  3. #include "StdAfx.h"
  4. #include "iocp.h"
  5. #pragma comment(lib, "WS2_32.lib")
  6. #include <stdio.h>
  7. CIOCPServer::CIOCPServer()
  8. {
  9. // 列表
  10. m_pFreeBufferList = NULL;
  11. m_pFreeContextList = NULL;
  12. m_pPendingAccepts = NULL;
  13. m_pConnectionList = NULL;
  14. m_nFreeBufferCount = 0;
  15. m_nFreeContextCount = 0;
  16. m_nPendingAcceptCount = 0;
  17. m_nCurrentConnection = 0;
  18. ::InitializeCriticalSection(&m_FreeBufferListLock);
  19. ::InitializeCriticalSection(&m_FreeContextListLock);
  20. ::InitializeCriticalSection(&m_PendingAcceptsLock);
  21. ::InitializeCriticalSection(&m_ConnectionListLock);
  22. // Accept请求
  23. m_hAcceptEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
  24. m_hRepostEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
  25. m_nRepostCount = 0;
  26. m_nThread = 0;
  27. m_nPort = 4567;
  28. m_nInitialAccepts = 10;
  29. m_nInitialReads = 4;
  30. m_nMaxAccepts = 100;
  31. m_nMaxSends = 20;
  32. m_nMaxFreeBuffers = 200;
  33. m_nMaxFreeContexts = 100;
  34. m_nMaxConnections = 2000;
  35. m_hListenThread = NULL;
  36. m_hCompletion = NULL;
  37. m_sListen = INVALID_SOCKET;
  38. m_lpfnAcceptEx = NULL;
  39. m_lpfnGetAcceptExSockaddrs = NULL;
  40. m_bShutDown = FALSE;
  41. m_bServerStarted = FALSE;
  42. m_hMutex = CreateMutex(NULL,FALSE,"LOCK");
  43. // 初始化WS2_32.dll
  44. WSADATA wsaData;
  45. WORD sockVersion = MAKEWORD(2, 2);
  46. ::WSAStartup(sockVersion, &wsaData);
  47. }
  48. CIOCPServer::~CIOCPServer()
  49. {
  50. Shutdown();
  51. if(m_sListen != INVALID_SOCKET)
  52. ::closesocket(m_sListen);
  53. if(m_hListenThread != NULL)
  54. ::CloseHandle(m_hListenThread);
  55. ::CloseHandle(m_hRepostEvent);
  56. ::CloseHandle(m_hAcceptEvent);
  57. ::DeleteCriticalSection(&m_FreeBufferListLock);
  58. ::DeleteCriticalSection(&m_FreeContextListLock);
  59. ::DeleteCriticalSection(&m_PendingAcceptsLock);
  60. ::DeleteCriticalSection(&m_ConnectionListLock);
  61. ::WSACleanup();
  62. }
  63. ///
  64. // 自定义帮助函数
  65. CIOCPBuffer *CIOCPServer::AllocateBuffer(int nLen)
  66. {
  67. CIOCPBuffer *pBuffer = NULL;
  68. if(nLen > BUFFER_SIZE)
  69. return NULL;
  70. // 为缓冲区对象申请内存
  71. ::EnterCriticalSection(&m_FreeBufferListLock);
  72. if(m_pFreeBufferList == NULL) // 内存池为空,申请新的内存
  73. {
  74. pBuffer = (CIOCPBuffer *)::HeapAlloc(GetProcessHeap(),
  75. HEAP_ZERO_MEMORY, sizeof(CIOCPBuffer) + BUFFER_SIZE);
  76. }
  77. else // 从内存池中取一块来使用
  78. {
  79. pBuffer = m_pFreeBufferList;
  80. m_pFreeBufferList = m_pFreeBufferList->pNext;
  81. pBuffer->pNext = NULL;
  82. m_nFreeBufferCount --;
  83. }
  84. ::LeaveCriticalSection(&m_FreeBufferListLock);
  85. // 初始化新的缓冲区对象
  86. if(pBuffer != NULL)
  87. {
  88. pBuffer->buff = (char*)(pBuffer + 1);
  89. pBuffer->nLen = nLen;
  90. }
  91. return pBuffer;
  92. }
  93. void CIOCPServer::ReleaseBuffer(CIOCPBuffer *pBuffer)
  94. {
  95. ::EnterCriticalSection(&m_FreeBufferListLock);
  96. if(m_nFreeBufferCount <= m_nMaxFreeBuffers) // 将要释放的内存添加到空闲列表中
  97. {
  98. memset(pBuffer, 0, sizeof(CIOCPBuffer) + BUFFER_SIZE);
  99. pBuffer->pNext = m_pFreeBufferList;
  100. m_pFreeBufferList = pBuffer;
  101. m_nFreeBufferCount ++ ;
  102. }
  103. else // 已经达到最大值,真正的释放内存
  104. {
  105. ::HeapFree(::GetProcessHeap(), 0, pBuffer);
  106. }
  107. ::LeaveCriticalSection(&m_FreeBufferListLock);
  108. }
  109. CIOCPContext *CIOCPServer::AllocateContext(SOCKET s)
  110. {
  111. CIOCPContext *pContext;
  112. // 申请一个CIOCPContext对象
  113. ::EnterCriticalSection(&m_FreeContextListLock);
  114. if(m_pFreeContextList == NULL)
  115. {
  116. pContext = (CIOCPContext *)
  117. ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CIOCPContext));
  118. ::InitializeCriticalSection(&pContext->Lock);
  119. }
  120. else
  121. {
  122. // 在空闲列表中申请
  123. pContext = m_pFreeContextList;
  124. m_pFreeContextList = m_pFreeContextList->pNext;
  125. pContext->pNext = NULL;
  126. m_nFreeBufferCount --;
  127. }
  128. ::LeaveCriticalSection(&m_FreeContextListLock);
  129. // 初始化对象成员
  130. if(pContext != NULL)
  131. {
  132. pContext->s = s;
  133. }
  134. return pContext;
  135. }
  136. void CIOCPServer::ReleaseContext(CIOCPContext *pContext)
  137. {
  138. if(pContext->s != INVALID_SOCKET)
  139. ::closesocket(pContext->s);
  140. // 首先释放(如果有的话)此套节字上的没有按顺序完成的读I/O的缓冲区
  141. CIOCPBuffer *pNext;
  142. while(pContext->pOutOfOrderReads != NULL)
  143. {
  144. pNext = pContext->pOutOfOrderReads->pNext;
  145. ReleaseBuffer(pContext->pOutOfOrderReads);
  146. pContext->pOutOfOrderReads = pNext;
  147. }
  148. ::EnterCriticalSection(&m_FreeContextListLock);
  149. if(m_nFreeContextCount <= m_nMaxFreeContexts) // 添加到空闲列表
  150. {
  151. // 先将关键代码段变量保存到一个临时变量中
  152. CRITICAL_SECTION cstmp = pContext->Lock;
  153. // 将要释放的上下文对象初始化为0
  154. memset(pContext, 0, sizeof(CIOCPContext));
  155. // 再放会关键代码段变量,将要释放的上下文对象添加到空闲列表的表头
  156. pContext->Lock = cstmp;
  157. pContext->pNext = m_pFreeContextList;
  158. m_pFreeContextList = pContext;
  159. // 更新计数
  160. m_nFreeContextCount ++;
  161. }
  162. else
  163. {
  164. ::DeleteCriticalSection(&pContext->Lock);
  165. ::HeapFree(::GetProcessHeap(), 0, pContext);
  166. }
  167. ::LeaveCriticalSection(&m_FreeContextListLock);
  168. }
  169. void CIOCPServer::FreeBuffers()
  170. {
  171. // 遍历m_pFreeBufferList空闲列表,释放缓冲区池内存
  172. ::EnterCriticalSection(&m_FreeBufferListLock);
  173. CIOCPBuffer *pFreeBuffer = m_pFreeBufferList;
  174. CIOCPBuffer *pNextBuffer;
  175. while(pFreeBuffer != NULL)
  176. {
  177. pNextBuffer = pFreeBuffer->pNext;
  178. if(!::HeapFree(::GetProcessHeap(), 0, pFreeBuffer))
  179. {
  180. #ifdef _DEBUG
  181. ::OutputDebugString(" FreeBuffers释放内存出错!");
  182. #endif // _DEBUG
  183. break;
  184. }
  185. pFreeBuffer = pNextBuffer;
  186. }
  187. m_pFreeBufferList = NULL;
  188. m_nFreeBufferCount = 0;
  189. ::LeaveCriticalSection(&m_FreeBufferListLock);
  190. }
  191. void CIOCPServer::FreeContexts()
  192. {
  193. // 遍历m_pFreeContextList空闲列表,释放缓冲区池内存
  194. ::EnterCriticalSection(&m_FreeContextListLock);
  195. CIOCPContext *pFreeContext = m_pFreeContextList;
  196. CIOCPContext *pNextContext;
  197. while(pFreeContext != NULL)
  198. {
  199. pNextContext = pFreeContext->pNext;
  200. ::DeleteCriticalSection(&pFreeContext->Lock);
  201. if(!::HeapFree(::GetProcessHeap(), 0, pFreeContext))
  202. {
  203. #ifdef _DEBUG
  204. ::OutputDebugString(" FreeBuffers释放内存出错!");
  205. #endif // _DEBUG
  206. break;
  207. }
  208. pFreeContext = pNextContext;
  209. }
  210. m_pFreeContextList = NULL;
  211. m_nFreeContextCount = 0;
  212. ::LeaveCriticalSection(&m_FreeContextListLock);
  213. }
  214. BOOL CIOCPServer::AddAConnection(CIOCPContext *pContext)
  215. {
  216. // 向客户连接列表添加一个CIOCPContext对象
  217. ::EnterCriticalSection(&m_ConnectionListLock);
  218. if(m_nCurrentConnection <= m_nMaxConnections)
  219. {
  220. // 添加到表头
  221. pContext->pNext = m_pConnectionList;
  222. m_pConnectionList = pContext;
  223. // 更新计数
  224. m_nCurrentConnection ++;
  225. ::LeaveCriticalSection(&m_ConnectionListLock);
  226. return TRUE;
  227. }
  228. ::LeaveCriticalSection(&m_ConnectionListLock);
  229. return FALSE;
  230. }
  231. void CIOCPServer::CloseAConnection(CIOCPContext *pContext)
  232. {
  233. // 首先从列表中移除要关闭的连接
  234. ::EnterCriticalSection(&m_ConnectionListLock);
  235. CIOCPContext* pTest = m_pConnectionList;
  236. if(pTest == pContext)
  237. {
  238. m_pConnectionList = pContext->pNext;
  239. m_nCurrentConnection --;
  240. }
  241. else
  242. {
  243. while(pTest != NULL && pTest->pNext != pContext)
  244. pTest = pTest->pNext;
  245. if(pTest != NULL)
  246. {
  247. pTest->pNext = pContext->pNext;
  248. m_nCurrentConnection --;
  249. }
  250. }
  251. ::LeaveCriticalSection(&m_ConnectionListLock);
  252. // 然后关闭客户套节字
  253. ::EnterCriticalSection(&pContext->Lock);
  254. if(pContext->s != INVALID_SOCKET)
  255. {
  256. ::closesocket(pContext->s);
  257. pContext->s = INVALID_SOCKET;
  258. }
  259. pContext->bClosing = TRUE;
  260. ::LeaveCriticalSection(&pContext->Lock);
  261. }
  262. void CIOCPServer::CloseAllConnections()
  263. {
  264. // 遍历整个连接列表,关闭所有的客户套节字
  265. ::EnterCriticalSection(&m_ConnectionListLock);
  266. CIOCPContext *pContext = m_pConnectionList;
  267. while(pContext != NULL)
  268. {
  269. ::EnterCriticalSection(&pContext->Lock);
  270. if(pContext->s != INVALID_SOCKET)
  271. {
  272. ::closesocket(pContext->s);
  273. pContext->s = INVALID_SOCKET;
  274. }
  275. pContext->bClosing = TRUE;
  276. ::LeaveCriticalSection(&pContext->Lock);
  277. pContext = pContext->pNext;
  278. }
  279. m_pConnectionList = NULL;
  280. m_nCurrentConnection = 0;
  281. ::LeaveCriticalSection(&m_ConnectionListLock);
  282. }
  283. BOOL CIOCPServer::InsertPendingAccept(CIOCPBuffer *pBuffer)
  284. {
  285. // 将一个I/O缓冲区对象插入到m_pPendingAccepts表中
  286. ::EnterCriticalSection(&m_PendingAcceptsLock);
  287. if(m_pPendingAccepts == NULL)
  288. m_pPendingAccepts = pBuffer;
  289. else
  290. {
  291. pBuffer->pNext = m_pPendingAccepts;
  292. m_pPendingAccepts = pBuffer;
  293. }
  294. m_nPendingAcceptCount ++;
  295. ::LeaveCriticalSection(&m_PendingAcceptsLock);
  296. return TRUE;
  297. }
  298. BOOL CIOCPServer::RemovePendingAccept(CIOCPBuffer *pBuffer)
  299. {
  300. BOOL bResult = FALSE;
  301. // 遍历m_pPendingAccepts表,从中移除pBuffer所指向的缓冲区对象
  302. ::EnterCriticalSection(&m_PendingAcceptsLock);
  303. CIOCPBuffer *pTest = m_pPendingAccepts;
  304. if(pTest == pBuffer) // 如果是表头元素
  305. {
  306. m_pPendingAccepts = pBuffer->pNext;
  307. bResult = TRUE;
  308. }
  309. else // 不是表头元素的话,就要遍历这个表来查找了
  310. {
  311. while(pTest != NULL && pTest->pNext != pBuffer)
  312. pTest = pTest->pNext;
  313. if(pTest != NULL)
  314. {
  315. pTest->pNext = pBuffer->pNext;
  316. bResult = TRUE;
  317. }
  318. }
  319. // 更新计数
  320. if(bResult)
  321. m_nPendingAcceptCount --;
  322. ::LeaveCriticalSection(&m_PendingAcceptsLock);
  323. return bResult;
  324. }
  325. CIOCPBuffer *CIOCPServer::GetNextReadBuffer(CIOCPContext *pContext, CIOCPBuffer *pBuffer)
  326. {
  327. if(pBuffer != NULL)
  328. {
  329. // 如果与要读的下一个序列号相等,则读这块缓冲区
  330. if(pBuffer->nSequenceNumber == pContext->nCurrentReadSequence)
  331. {
  332. return pBuffer;
  333. }
  334. // 如果不相等,则说明没有按顺序接收数据,将这块缓冲区保存到连接的pOutOfOrderReads列表中
  335. // 列表中的缓冲区是按照其序列号从小到大的顺序排列的
  336. pBuffer->pNext = NULL;
  337. CIOCPBuffer *ptr = pContext->pOutOfOrderReads;
  338. CIOCPBuffer *pPre = NULL;
  339. while(ptr != NULL)
  340. {
  341. if(pBuffer->nSequenceNumber < ptr->nSequenceNumber)
  342. break;
  343. pPre = ptr;
  344. ptr = ptr->pNext;
  345. }
  346. if(pPre == NULL) // 应该插入到表头
  347. {
  348. pBuffer->pNext = pContext->pOutOfOrderReads;
  349. pContext->pOutOfOrderReads = pBuffer;
  350. }
  351. else // 应该插入到表的中间
  352. {
  353. pBuffer->pNext = pPre->pNext;
  354. pPre->pNext = pBuffer;
  355. }
  356. }
  357. // 检查表头元素的序列号,如果与要读的序列号一致,就将它从表中移除,返回给用户
  358. CIOCPBuffer *ptr = pContext->pOutOfOrderReads;
  359. if(ptr != NULL && (ptr->nSequenceNumber == pContext->nCurrentReadSequence))
  360. {
  361. pContext->pOutOfOrderReads = ptr->pNext;
  362. return ptr;
  363. }
  364. return NULL;
  365. }
  366. BOOL CIOCPServer::PostAccept(CIOCPBuffer *pBuffer) // 在监听套节字上投递Accept请求
  367. {
  368. // 设置I/O类型
  369. pBuffer->nOperation = OP_ACCEPT;
  370. // 投递此重叠I/O
  371. DWORD dwBytes;
  372. pBuffer->sClient = ::WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
  373. BOOL b = m_lpfnAcceptEx(m_sListen,
  374. pBuffer->sClient,
  375. pBuffer->buff,
  376. pBuffer->nLen - ((sizeof(sockaddr_in) + 16) * 2),
  377. sizeof(sockaddr_in) + 16,
  378. sizeof(sockaddr_in) + 16,
  379. &dwBytes,
  380. &pBuffer->ol);
  381. if(!b && ::WSAGetLastError() != WSA_IO_PENDING)
  382. {
  383. return FALSE;
  384. }
  385. return TRUE;
  386. };
  387. BOOL CIOCPServer::PostRecv(CIOCPContext *pContext, CIOCPBuffer *pBuffer)
  388. {
  389. // 设置I/O类型
  390. pBuffer->nOperation = OP_READ;
  391. ::EnterCriticalSection(&pContext->Lock);
  392. // 设置序列号
  393. pBuffer->nSequenceNumber = pContext->nReadSequence;
  394. // 投递此重叠I/O
  395. DWORD dwBytes;
  396. DWORD dwFlags = 0;
  397. WSABUF buf;
  398. buf.buf = pBuffer->buff;
  399. buf.len = pBuffer->nLen;
  400. if(::WSARecv(pContext->s, &buf, 1, &dwBytes, &dwFlags, &pBuffer->ol, NULL) != NO_ERROR)
  401. {
  402. if(::WSAGetLastError() != WSA_IO_PENDING)
  403. {
  404. ::LeaveCriticalSection(&pContext->Lock);
  405. return FALSE;
  406. }
  407. }
  408. // 增加套节字上的重叠I/O计数和读序列号计数
  409. pContext->nOutstandingRecv ++;
  410. pContext->nReadSequence ++;
  411. ::LeaveCriticalSection(&pContext->Lock);
  412. return TRUE;
  413. }
  414. BOOL CIOCPServer::PostSend(CIOCPContext *pContext, CIOCPBuffer *pBuffer)
  415. {
  416. // 跟踪投递的发送的数量,防止用户仅发送数据而不接收,导致服务器抛出大量发送操作
  417. if(pContext->nOutstandingSend > m_nMaxSends)
  418. return FALSE;
  419. // 设置I/O类型,增加套节字上的重叠I/O计数
  420. pBuffer->nOperation = OP_WRITE;
  421. // 投递此重叠I/O
  422. DWORD dwBytes;
  423. DWORD dwFlags = 0;
  424. WSABUF buf;
  425. buf.buf = pBuffer->buff;
  426. buf.len = pBuffer->nLen;
  427. if(::WSASend(pContext->s,
  428. &buf, 1, &dwBytes, dwFlags, &pBuffer->ol, NULL) != NO_ERROR)
  429. {
  430. if(::WSAGetLastError() != WSA_IO_PENDING)
  431. return FALSE;
  432. }
  433. // 增加套节字上的重叠I/O计数
  434. ::EnterCriticalSection(&pContext->Lock);
  435. pContext->nOutstandingSend ++;
  436. ::LeaveCriticalSection(&pContext->Lock);
  437. return TRUE;
  438. }
  439. BOOL CIOCPServer::Start(int nPort, int nMaxConnections,
  440. int nMaxFreeBuffers, int nMaxFreeContexts, int nInitialReads)
  441. {
  442. // 检查服务是否已经启动
  443. if(m_bServerStarted)
  444. return FALSE;
  445. OutputDebugString("测试!/r\n");
  446. // 保存用户参数
  447. m_nPort = nPort;
  448. m_nMaxConnections = nMaxConnections;
  449. m_nMaxFreeBuffers = nMaxFreeBuffers;
  450. m_nMaxFreeContexts = nMaxFreeContexts;
  451. m_nInitialReads = nInitialReads;
  452. // 初始化状态变量
  453. m_bShutDown = FALSE;
  454. m_bServerStarted = TRUE;
  455. // 创建监听套节字,绑定到本地端口,进入监听模式
  456. m_sListen = ::WSASocket(AF_INET, SOCK_STREAM,IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED);
  457. SOCKADDR_IN si;
  458. si.sin_family = AF_INET;
  459. si.sin_port = ::ntohs(m_nPort);
  460. si.sin_addr.S_un.S_addr = INADDR_ANY;
  461. if(::bind(m_sListen, (sockaddr*)&si, sizeof(si)) == SOCKET_ERROR)
  462. {
  463. m_bServerStarted = FALSE;
  464. return FALSE;
  465. }
  466. ::listen(m_sListen, 200);
  467. // 创建完成端口对象
  468. m_hCompletion = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
  469. // 加载扩展函数AcceptEx
  470. GUID GuidAcceptEx = WSAID_ACCEPTEX;
  471. DWORD dwBytes;
  472. ::WSAIoctl(m_sListen,
  473. SIO_GET_EXTENSION_FUNCTION_POINTER,
  474. &GuidAcceptEx,
  475. sizeof(GuidAcceptEx),
  476. &m_lpfnAcceptEx,
  477. sizeof(m_lpfnAcceptEx),
  478. &dwBytes,
  479. NULL,
  480. NULL);
  481. // 加载扩展函数GetAcceptExSockaddrs
  482. GUID GuidGetAcceptExSockaddrs = WSAID_GETACCEPTEXSOCKADDRS;
  483. ::WSAIoctl(m_sListen,
  484. SIO_GET_EXTENSION_FUNCTION_POINTER,
  485. &GuidGetAcceptExSockaddrs,
  486. sizeof(GuidGetAcceptExSockaddrs),
  487. &m_lpfnGetAcceptExSockaddrs,
  488. sizeof(m_lpfnGetAcceptExSockaddrs),
  489. &dwBytes,
  490. NULL,
  491. NULL
  492. );
  493. // 将监听套节字关联到完成端口,注意,这里为它传递的CompletionKey为0
  494. ::CreateIoCompletionPort((HANDLE)m_sListen, m_hCompletion, (DWORD)0, 0);
  495. // 注册FD_ACCEPT事件。
  496. // 如果投递的AcceptEx I/O不够,线程会接收到FD_ACCEPT网络事件,说明应该投递更多的AcceptEx I/O
  497. WSAEventSelect(m_sListen, m_hAcceptEvent, FD_ACCEPT);
  498. // 创建监听线程
  499. m_hListenThread = ::CreateThread(NULL, 0, _ListenThreadProc, this, 0, NULL);
  500. return TRUE;
  501. }
  502. void CIOCPServer::Shutdown()
  503. {
  504. if(!m_bServerStarted)
  505. return;
  506. // 通知监听线程,马上停止服务
  507. m_bShutDown = TRUE;
  508. ::SetEvent(m_hAcceptEvent);
  509. // 等待监听线程退出
  510. ::WaitForSingleObject(m_hListenThread, INFINITE);
  511. ::CloseHandle(m_hListenThread);
  512. m_hListenThread = NULL;
  513. m_bServerStarted = FALSE;
  514. }
  515. DWORD WINAPI CIOCPServer::_ListenThreadProc(LPVOID lpParam)
  516. {
  517. CIOCPServer *pThis = (CIOCPServer*)lpParam;
  518. // 先在监听套节字上投递几个Accept I/O
  519. CIOCPBuffer *pBuffer;
  520. for(int i=0; i<pThis->m_nInitialAccepts; i++)
  521. {
  522. pBuffer = pThis->AllocateBuffer(BUFFER_SIZE);
  523. if(pBuffer == NULL)
  524. return -1;
  525. pThis->InsertPendingAccept(pBuffer);
  526. pThis->PostAccept(pBuffer);
  527. }
  528. // 构建事件对象数组,以便在上面调用WSAWaitForMultipleEvents函数
  529. pThis->m_nThread = _GetNoOfProcessors();
  530. HANDLE *hWaitEvents = new HANDLE[2 + pThis->m_nThread ];
  531. int nEventCount = 0;
  532. hWaitEvents[nEventCount ++] = pThis->m_hAcceptEvent;
  533. hWaitEvents[nEventCount ++] = pThis->m_hRepostEvent;
  534. // 创建指定数量的工作线程在完成端口上处理I/O
  535. for(int i=0; i<pThis->m_nThread ; i++)
  536. {
  537. hWaitEvents[nEventCount ++] = ::CreateThread(NULL, 0, _WorkerThreadProc, pThis, 0, NULL);
  538. }
  539. // 下面进入无限循环,处理事件对象数组中的事件
  540. while(TRUE)
  541. {
  542. //::OutputDebugString("WSAWaitForMultipleEvents begin\n");
  543. int nIndex = ::WSAWaitForMultipleEvents(nEventCount, hWaitEvents, FALSE,30*1000, FALSE);
  544. //::OutputDebugString("WSAWaitForMultipleEvents end\n");
  545. char temp[256];
  546. //::OutputDebugString(itoa(nIndex,temp,10));
  547. // 首先检查是否要停止服务
  548. if(pThis->m_bShutDown || nIndex == WSA_WAIT_FAILED)
  549. {::OutputDebugString("pThis->m_bShutDown\n");
  550. // 关闭所有连接
  551. pThis->CloseAllConnections();
  552. ::Sleep(0); // 给I/O工作线程一个执行的机会
  553. // 关闭监听套节字
  554. ::closesocket(pThis->m_sListen);
  555. pThis->m_sListen = INVALID_SOCKET;
  556. ::Sleep(0); // 给I/O工作线程一个执行的机会
  557. // 通知所有I/O处理线程退出
  558. for(int i=2; i<pThis->m_nThread + 2; i++)
  559. {
  560. ::PostQueuedCompletionStatus(pThis->m_hCompletion, -1, 0, NULL);
  561. }
  562. // 等待I/O处理线程退出
  563. ::WaitForMultipleObjects(pThis->m_nThread , &hWaitEvents[2], TRUE, 5*1000);
  564. for(int i=2; i<pThis->m_nThread + 2; i++)
  565. {
  566. ::CloseHandle(hWaitEvents[i]);
  567. }
  568. ::CloseHandle(pThis->m_hCompletion);
  569. pThis->FreeBuffers();
  570. pThis->FreeContexts();
  571. ::ExitThread(0);
  572. }
  573. // 1)定时检查所有未返回的AcceptEx I/O的连接建立了多长时间
  574. if(nIndex == WSA_WAIT_TIMEOUT)
  575. {::OutputDebugString("WSA_WAIT_TIMEOUT\n");
  576. pBuffer = pThis->m_pPendingAccepts;
  577. while(pBuffer != NULL)
  578. {
  579. int nSeconds;
  580. int nLen = sizeof(nSeconds);
  581. // 取得连接建立的时间
  582. ::getsockopt(pBuffer->sClient,
  583. SOL_SOCKET, SO_CONNECT_TIME, (char *)&nSeconds, &nLen);
  584. // 如果超过2分钟客户还不发送初始数据,就让这个客户go away
  585. if(nSeconds != -1 && nSeconds > 2*60)
  586. {
  587. closesocket(pBuffer->sClient);
  588. pBuffer->sClient = INVALID_SOCKET;
  589. }
  590. pBuffer = pBuffer->pNext;
  591. }
  592. }
  593. else
  594. {
  595. nIndex = nIndex - WAIT_OBJECT_0;
  596. //int nRet = ::WSAWaitForMultipleEvents(1, &hWaitEvents[nIndex], TRUE, 0, FALSE);
  597. WSANETWORKEVENTS ne;
  598. int nLimit=0;
  599. if(nIndex == 0) // 2)m_hAcceptEvent事件对象受信,说明投递的Accept请求不够,需要增加
  600. {
  601. ::WSAEnumNetworkEvents(pThis->m_sListen, hWaitEvents[nIndex], &ne);
  602. if(ne.lNetworkEvents & FD_ACCEPT)
  603. {
  604. nLimit = 50; // 增加的个数,这里设为50个
  605. }
  606. }
  607. else if(nIndex == 1) // 3)m_hRepostEvent事件对象受信,说明处理I/O的线程接受到新的客户
  608. {
  609. nLimit = InterlockedExchange(&pThis->m_nRepostCount, 0);
  610. ::OutputDebugString("处理I/O的线程接受到新的客户\n");
  611. }
  612. else if(nIndex > 1) // I/O服务线程退出,说明有错误发生,关闭服务器
  613. {
  614. pThis->m_bShutDown = TRUE;
  615. continue;
  616. }
  617. ::OutputDebugString("投递nLimit个AcceptEx I/O请求\n");
  618. // 投递nLimit个AcceptEx I/O请求
  619. int i = 0;
  620. while(i++ < nLimit && pThis->m_nPendingAcceptCount < pThis->m_nMaxAccepts)
  621. {
  622. pBuffer = pThis->AllocateBuffer(BUFFER_SIZE);
  623. if(pBuffer != NULL)
  624. {
  625. pThis->InsertPendingAccept(pBuffer);
  626. pThis->PostAccept(pBuffer);
  627. }
  628. }
  629. }
  630. }
  631. delete []hWaitEvents;
  632. return 0;
  633. }
  634. DWORD WINAPI CIOCPServer::_WorkerThreadProc(LPVOID lpParam)
  635. {
  636. #ifdef _DEBUG
  637. ::OutputDebugString(" WorkerThread 启动... \n");
  638. #endif // _DEBUG
  639. CIOCPServer *pThis = (CIOCPServer*)lpParam;
  640. CIOCPBuffer *pBuffer;
  641. DWORD dwKey;
  642. DWORD dwTrans;
  643. LPOVERLAPPED lpol;
  644. while(TRUE)
  645. {
  646. ::OutputDebugString("GetQueuedCompletionStatus begin\n");
  647. // 在关联到此完成端口的所有套节字上等待I/O完成
  648. BOOL bOK = ::GetQueuedCompletionStatus(pThis->m_hCompletion,
  649. &dwTrans, (LPDWORD)&dwKey, (LPOVERLAPPED*)&lpol, WSA_INFINITE);
  650. ::OutputDebugString("GetQueuedCompletionStatus completed\n");
  651. if(dwTrans == -1) // 用户通知退出
  652. {
  653. #ifdef _DEBUG
  654. ::OutputDebugString(" WorkerThread 退出 \n");
  655. #endif // _DEBUG
  656. ::ExitThread(0);
  657. }
  658. pBuffer = CONTAINING_RECORD(lpol, CIOCPBuffer, ol);
  659. int nError = NO_ERROR;
  660. if(!bOK) // 在此套节字上有错误发生
  661. {
  662. SOCKET s;
  663. if(pBuffer->nOperation == OP_ACCEPT)
  664. {
  665. s = pThis->m_sListen;
  666. }
  667. else
  668. {
  669. if(dwKey == 0)
  670. break;
  671. s = ((CIOCPContext*)dwKey)->s;
  672. }
  673. DWORD dwFlags = 0;
  674. if(!::WSAGetOverlappedResult(s, &pBuffer->ol, &dwTrans, FALSE, &dwFlags))
  675. {
  676. nError = ::WSAGetLastError();
  677. }
  678. }
  679. WaitForSingleObject(pThis->m_hMutex,INFINITE);
  680. pThis->HandleIO(dwKey, pBuffer, dwTrans, nError);
  681. ReleaseMutex(pThis->m_hMutex);
  682. }
  683. #ifdef _DEBUG
  684. ::OutputDebugString(" WorkerThread 退出 \n");
  685. #endif // _DEBUG
  686. return 0;
  687. }
  688. void CIOCPServer::HandleIO(DWORD dwKey, CIOCPBuffer *pBuffer, DWORD dwTrans, int nError)
  689. {
  690. CIOCPContext *pContext = (CIOCPContext *)dwKey;
  691. #ifdef _DEBUG
  692. ::OutputDebugString(" HandleIO... \n");
  693. #endif // _DEBUG
  694. // 1)首先减少套节字上的未决I/O计数
  695. if(pContext != NULL)
  696. {
  697. ::EnterCriticalSection(&pContext->Lock);
  698. if(pBuffer->nOperation == OP_READ)
  699. pContext->nOutstandingRecv --;
  700. else if(pBuffer->nOperation == OP_WRITE)
  701. pContext->nOutstandingSend --;
  702. ::LeaveCriticalSection(&pContext->Lock);
  703. // 2)检查套节字是否已经被我们关闭
  704. if(pContext->bClosing)
  705. {
  706. #ifdef _DEBUG
  707. ::OutputDebugString(" 检查到套节字已经被我们关闭 \n");
  708. #endif // _DEBUG
  709. if(pContext->nOutstandingRecv == 0 && pContext->nOutstandingSend == 0)
  710. {
  711. ReleaseContext(pContext);
  712. }
  713. // 释放已关闭套节字的未决I/O
  714. ReleaseBuffer(pBuffer);
  715. return;
  716. }
  717. }
  718. else
  719. {
  720. RemovePendingAccept(pBuffer);
  721. #ifdef _DEBUG
  722. ::OutputDebugString("RemovePendingAccept(pBuffer);\n");
  723. #endif // _DEBUG
  724. }
  725. // 3)检查套节字上发生的错误,如果有的话,通知用户,然后关闭套节字
  726. if(nError != NO_ERROR)
  727. {
  728. if(pBuffer->nOperation != OP_ACCEPT)
  729. {
  730. OnConnectionError(pContext, pBuffer, nError);
  731. CloseAConnection(pContext);
  732. if(pContext->nOutstandingRecv == 0 && pContext->nOutstandingSend == 0)
  733. {
  734. ReleaseContext(pContext);
  735. }
  736. #ifdef _DEBUG
  737. ::OutputDebugString(" 检查到客户套节字上发生错误 \n");
  738. #endif // _DEBUG
  739. }
  740. else // 在监听套节字上发生错误,也就是监听套节字处理的客户出错了
  741. {
  742. // 客户端出错,释放I/O缓冲区
  743. if(pBuffer->sClient != INVALID_SOCKET)
  744. {
  745. ::closesocket(pBuffer->sClient);
  746. pBuffer->sClient = INVALID_SOCKET;
  747. }
  748. #ifdef _DEBUG
  749. ::OutputDebugString(" 检查到监听套节字上发生错误 \n");
  750. #endif // _DEBUG
  751. }
  752. ReleaseBuffer(pBuffer);
  753. return;
  754. }
  755. //printf("%d\n",pBuffer->nOperation);
  756. //#ifdef _DEBUG
  757. // ::OutputDebugString((const char *)pBuffer->nOperation);
  758. //#endif // _DEBUG
  759. // 开始处理
  760. if(pBuffer->nOperation == OP_ACCEPT)
  761. {
  762. if(dwTrans == 0)
  763. {
  764. #ifdef _DEBUG
  765. ::OutputDebugString(" 监听套节字上客户端关闭 \n");
  766. #endif // _DEBUG
  767. if(pBuffer->sClient != INVALID_SOCKET)
  768. {
  769. ::closesocket(pBuffer->sClient);
  770. pBuffer->sClient = INVALID_SOCKET;
  771. }
  772. }
  773. else
  774. {
  775. // 为新接受的连接申请客户上下文对象
  776. CIOCPContext *pClient = AllocateContext(pBuffer->sClient);
  777. if(pClient != NULL)
  778. {
  779. if(AddAConnection(pClient))
  780. {
  781. // 取得客户地址
  782. ::OutputDebugString("AddAConnection(pClient)\n");
  783. int nLocalLen, nRmoteLen;
  784. LPSOCKADDR pLocalAddr, pRemoteAddr;
  785. m_lpfnGetAcceptExSockaddrs(
  786. pBuffer->buff,
  787. pBuffer->nLen - ((sizeof(sockaddr_in) + 16) * 2),
  788. sizeof(sockaddr_in) + 16,
  789. sizeof(sockaddr_in) + 16,
  790. (SOCKADDR **)&pLocalAddr,
  791. &nLocalLen,
  792. (SOCKADDR **)&pRemoteAddr,
  793. &nRmoteLen);
  794. memcpy(&pClient->addrLocal, pLocalAddr, nLocalLen);
  795. memcpy(&pClient->addrRemote, pRemoteAddr, nRmoteLen);
  796. // 关联新连接到完成端口对象
  797. ::CreateIoCompletionPort((HANDLE)pClient->s, m_hCompletion, (DWORD)pClient, 0);
  798. // 通知用户
  799. pBuffer->nLen = dwTrans;
  800. OnConnectionEstablished(pClient, pBuffer);
  801. // 向新连接投递几个Read请求,这些空间在套节字关闭或出错时释放
  802. ::OutputDebugString("New Connect Post 5 Recv Request\n");
  803. for(int i=0; i<5; i++)
  804. {
  805. CIOCPBuffer *p = AllocateBuffer(BUFFER_SIZE);
  806. if(p != NULL)
  807. {
  808. if(!PostRecv(pClient, p))
  809. {
  810. CloseAConnection(pClient);
  811. break;
  812. }
  813. }
  814. }
  815. }
  816. else // 连接数量已满,关闭连接
  817. {
  818. CloseAConnection(pClient);
  819. ReleaseContext(pClient);
  820. }
  821. }
  822. else
  823. {
  824. // 资源不足,关闭与客户的连接即可
  825. ::closesocket(pBuffer->sClient);
  826. pBuffer->sClient = INVALID_SOCKET;
  827. }
  828. }
  829. // Accept请求完成,释放I/O缓冲区
  830. ReleaseBuffer(pBuffer);
  831. // 通知监听线程继续再投递一个Accept请求
  832. ::InterlockedIncrement(&m_nRepostCount);
  833. ::SetEvent(m_hRepostEvent);
  834. }
  835. else if(pBuffer->nOperation == OP_READ)
  836. {
  837. if(dwTrans == 0) // 对方关闭套节字
  838. {
  839. // 先通知用户
  840. pBuffer->nLen = 0;
  841. OnConnectionClosing(pContext, pBuffer);
  842. // 再关闭连接
  843. CloseAConnection(pContext);
  844. // 释放客户上下文和缓冲区对象
  845. if(pContext->nOutstandingRecv == 0 && pContext->nOutstandingSend == 0)
  846. {
  847. ReleaseContext(pContext);
  848. }
  849. ReleaseBuffer(pBuffer);
  850. }
  851. else
  852. {
  853. pBuffer->nLen = dwTrans;
  854. // 按照I/O投递的顺序读取接收到的数据
  855. CIOCPBuffer *p = GetNextReadBuffer(pContext, pBuffer);
  856. while(p != NULL)
  857. {
  858. // 通知用户
  859. OnReadCompleted(pContext, p);
  860. // 增加要读的序列号的值
  861. ::InterlockedIncrement((LONG*)&pContext->nCurrentReadSequence);
  862. // 释放这个已完成的I/O
  863. ReleaseBuffer(p);
  864. p = GetNextReadBuffer(pContext, NULL);
  865. }
  866. // 继续投递一个新的接收请求
  867. pBuffer = AllocateBuffer(BUFFER_SIZE);
  868. if(pBuffer == NULL || !PostRecv(pContext, pBuffer))
  869. {
  870. CloseAConnection(pContext);
  871. }
  872. }
  873. }
  874. else if(pBuffer->nOperation == OP_WRITE)
  875. {
  876. if(dwTrans == 0) // 对方关闭套节字
  877. {
  878. // 先通知用户
  879. pBuffer->nLen = 0;
  880. OnConnectionClosing(pContext, pBuffer);
  881. // 再关闭连接
  882. CloseAConnection(pContext);
  883. // 释放客户上下文和缓冲区对象
  884. if(pContext->nOutstandingRecv == 0 && pContext->nOutstandingSend == 0)
  885. {
  886. ReleaseContext(pContext);
  887. }
  888. ReleaseBuffer(pBuffer);
  889. }
  890. else
  891. {
  892. // 写操作完成,通知用户
  893. pBuffer->nLen = dwTrans;
  894. OnWriteCompleted(pContext, pBuffer);
  895. // 释放SendText函数申请的缓冲区
  896. ReleaseBuffer(pBuffer);
  897. }
  898. }
  899. }
  900. BOOL CIOCPServer::SendText(CIOCPContext *pContext, char *pszText, int nLen)
  901. {
  902. CIOCPBuffer *pBuffer = AllocateBuffer(nLen);
  903. if(pBuffer != NULL)
  904. {
  905. memcpy(pBuffer->buff, pszText, nLen);
  906. return PostSend(pContext, pBuffer);
  907. }
  908. return FALSE;
  909. }
  910. int CIOCPServer::_GetNoOfProcessors()
  911. {
  912. SYSTEM_INFO si;
  913. GetSystemInfo(&si);
  914. return si.dwNumberOfProcessors;
  915. }
  916. void CIOCPServer::OnConnectionEstablished(CIOCPContext *pContext, CIOCPBuffer *pBuffer)
  917. {
  918. }
  919. void CIOCPServer::OnConnectionClosing(CIOCPContext *pContext, CIOCPBuffer *pBuffer)
  920. {
  921. }
  922. void CIOCPServer::OnReadCompleted(CIOCPContext *pContext, CIOCPBuffer *pBuffer)
  923. {
  924. }
  925. void CIOCPServer::OnWriteCompleted(CIOCPContext *pContext, CIOCPBuffer *pBuffer)
  926. {
  927. }
  928. void CIOCPServer::OnConnectionError(CIOCPContext *pContext, CIOCPBuffer *pBuffer, int nError)
  929. {
  930. }


PS:不排除其他bug,如有问题请指正,喷子滚一表去。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值