原文:Windows socket之最简单的socket程序

最简单的服务器的socket程序流程如下(面向连接的TCP连接 ):

1. WSAStartup(); 初始化网络库的使用。

2. socket(); 获得一个socket。

3. bind(); 把获得的socket绑定到一个ip 和端口。既然作为服务器, ip通常为本地IP127.0.0.1。

4. listen(); 监听已经绑定了指定端口的socket。

5. accept(); 接受一个来自客户端的连接。

accept()返回一个新的socket,该socket代表着本地服务器与某一个连接过来的客户端的链接。以该socket为参数,可以调用send函数往客户端发送数据,也可以调用recv函数接受客户端发送过来的函数。

最后服务器程序结束的时候调用closesocket()关闭socket, WSACleanup()终止网络库的使用,清理资源。

最简单的客户端的socket程序流程如下(同样是面向连接的TCP连接):

1. WSAStartup();初始化网络库的使用。

2. socket(); 获得一个socket。

3. connect(); 连接到一个 服务器。

连接成功后就可以收发数据了。收发完毕后调用closesocket()关闭socket,最后程序结束前调用 WSACleanup()清理资源。

下面直接上代码

需包含以下头文件和定义

#include <stdlib.h>

#include <stdio.h>

#include <WinSock2.h>

#pragma comment(lib,"ws2_32.lib")



#define SERVE_ADDRESS "127.0.0.1"

#define SERVE_PORT    7001

  1. // ---------------------------- WSAStartup() ----------------------------//
  2.  
  3. WSADATA wsd;
  4. int resStartup = WSAStartup(MAKEWORD(2,2),&wsd);
  5. if(0 != resStartup)
  6. {
  7. printf("failed to WSAStartup!\n");
  8. goto Main_End;
  9. }
  10. //------------------------------------------------------------------------------//
  11.  
  12. // ---------------------------- socket() ----------------------------//
  13.  
  14. SOCKET serverSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
  15. if(INVALID_SOCKET == serverSocket)
  16. {
  17. printf("failed to invoke socket, the socket returned is invalid!\n");
  18. goto Main_End;
  19. }
  20. // ------------------------------------------------------------------------------------//
  21.  
  22. //---------------------------- bind() ----------------------------//
  23.  
  24. // 初始化 struct sockaddr 结构体, SOCKADDR_IN就是 struct sockaddr的宏定义
  25. SOCKADDR_IN localAddr;
  26. localAddr.sin_family = AF_INET;
  27. localAddr.sin_addr.S_un.S_addr = inet_addr(SERVE_ADDRESS);
  28. localAddr.sin_port = htons(SERVE_PORT);
  29. memset(localAddr.sin_zero,0x0,sizeof(localAddr.sin_zero));
  30.  
  31. //
  32. int resBind = bind(serverSocket,(sockaddr*)&localAddr,sizeof(SOCKADDR_IN));
  33. if(0 != resBind)
  34. {
  35. printf("failed to bind ! \n");
  36. goto Main_End;
  37. }
  38. //------------------------------------------------------------------------------------//
  39.  
  40. //---------------------------- listen() ----------------------------//
  41. int resListen = listen(serverSocket,5);
  42. if(0 != resListen)
  43. {
  44. printf("failed to listen! \n");
  45. goto Main_End;
  46. }
  47.  
  48. printf("the server is listening now!\n");
  49. //------------------------------------------------------------------------------------//
  50.  
  51. //---------------------------- accept() ----------------------------//
  52. SOCKADDR_IN clientAddr;
  53. int addrLen = sizeof(clientAddr);
  54. SOCKET acceptedSocket = accept(serverSocket,(sockaddr*)&clientAddr,&addrLen);
  55. if(INVALID_SOCKET == acceptedSocket)
  56. {
  57. printf("accept error!\n");
  58. goto Main_End;
  59. }
  60. printf("a client has connected to the server!\n");
  61.  
  62. //------------------------------------------------------------------------------------//
  63.  
  64. char recvBuffer[256];
  65. char sendBuffer[256];
  66.  
  67. strcpy(sendBuffer,"server:Welcome to connect !");
  68. int sendBufLen = strlen(sendBuffer);
  69. int resSend = send(acceptedSocket,sendBuffer,sendBufLen,0);
  70.  
  71. while(true)
  72. {
  73. if(resSend != sendBufLen) //发送的长度与需要发送的长度不等
  74. {
  75. printf("send data error!!\n");
  76. break;
  77. }
  78.  
  79. int recvLen = recv(acceptedSocket,recvBuffer,sizeof(recvBuffer),0);
  80. if(0 == recvLen)
  81. {
  82. printf("a client close the socket!\n");
  83. break;
  84. }
  85. else if(recvLen < 0)
  86. {
  87. printf("an error has happen when receiving\n");
  88. break;
  89. }
  90.  
  91. recvBuffer[recvLen] = '\0';
  92. printf("client:%s\n",recvBuffer);
  93.  
  94. //在客户发过来的数据前面加上server:再发回给客户端
  95. strcpy(sendBuffer,"server:");
  96. strcat(sendBuffer,recvBuffer);
  97. sendBufLen = strlen(sendBuffer);
  98.  
  99. resSend = send(acceptedSocket,sendBuffer,sendBufLen,0);
  100. }
  101.  
  102. closesocket(acceptedSocket);
  103. closesocket(serverSocket);
  104.  
  105. Main_End:
  106.  
  107. WSACleanup();
  108. system("pause");
  109. return 0;

客户端代码:

  1. //---------------------------- WSAStartup() ----------------------------//
  2. WSADATA wsd;
  3. int resStartup = WSAStartup(MAKEWORD(2,2),&wsd);
  4. if(0 != resStartup)
  5. {
  6. printf("failed to WSAStartup!\n");
  7. goto Main_End;
  8. }
  9. //------------------------------------------------------------------------------------//
  10.  
  11. //---------------------------- socket() ----------------------------//
  12. SOCKET connSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
  13. if(INVALID_SOCKET == connSocket)
  14. {
  15. printf("the socket returned is invalid!\n");
  16. goto Main_End;
  17. }
  18. //------------------------------------------------------------------------------------//
  19.  
  20. //---------------------------- connect() ----------------------------//
  21. //初始化struct sockaddr 结构体
  22. SOCKADDR_IN serverAddr;
  23. serverAddr.sin_family = AF_INET;
  24. serverAddr.sin_addr.S_un.S_addr = inet_addr(SERVE_ADDRESS);
  25. serverAddr.sin_port = htons(SERVE_PORT);
  26. memset(serverAddr.sin_zero,0x0,sizeof(serverAddr.sin_zero));
  27.  
  28. //connect
  29. int resConn = connect(connSocket,(sockaddr*)&serverAddr,sizeof(serverAddr));
  30. if(0 != resConn)
  31. {
  32. printf("failed to connect to server!!\n");
  33. goto Main_End;
  34. }
  35. //------------------------------------------------------------------------------------//
  36.  
  37. char sendBuffer[256];
  38. char recvBuffer[256];
  39. while(true)
  40. {
  41. int recvLen = recv(connSocket,recvBuffer,256,0);
  42. if(recvLen < 0)
  43. {
  44. printf("receive error!!\n");
  45. break;
  46. }
  47. else if(0 == recvLen)
  48. {
  49. printf("the server close the socket!\n");
  50. }
  51.  
  52. recvBuffer[recvLen] = '\0';
  53. printf("the data recv:%s\n\n\n",recvBuffer);
  54.  
  55. printf("please input what you want to send:\n");
  56. gets(sendBuffer);
  57. if(0 == strcmp(sendBuffer,"exit"))
  58. {
  59. break;
  60. }
  61.  
  62. int sendDataLen = strlen(sendBuffer);
  63. int nDataSent = send(connSocket,sendBuffer,sendDataLen,0);
  64. if(nDataSent != sendDataLen)
  65. {
  66. printf("failed to send data!!\n");
  67. break;
  68. }
  69. }
  70.  
  71. closesocket(connSocket);
  72. printf("the connection is closed!\n");
  73.  
  74. Main_End:
  75. WSACleanup();
  76. system("pause");
  77. return 0;

客户端连接到服务端后,每次给服务端发送一段内容,服务器在内容前面加上server:再发送给客户端。

当客户端发送的内容是exit时,客户端程序跳出循环,关闭socket断开连接。服务端发现客户端断开连接后也关闭套接字结束程序。

当然上面程序只为了演示最简单的网络编程。有若干漏洞。

1. 服务器只能接受一个客户端连接。当然加一个循环语句进去可以重复地接受客户端的连接,但是仍然是每次只处理一个客户端连接。

2.accept, connect,send,recv函数默认均是阻塞函数。当没有客户连接到服务端时,服务端阻塞在accept函数,无法退出程序。当服务器在接受客户端的数据时,如果客户端不发送数据,也不断开连接,那么服务端阻塞在recv函数,无法退出程序。

改进该程序,使得服务端随时都可以停止服务退出程序,无论有多少个用户已经在连接。

为了多个客户端可以同时连接,最容易理解的便是利用多线程。每一个连接的客户端都用一个线程去处理它的通信。

至于为了随时可以退出服务端,不能再调用永久阻塞的函数了。利用select函数,可以阻塞指定的时间,阻塞期间不占CPU。

int select( __in int nfds, __in_out fd_set*readfds, __in_out fd_set*writefds, __in_out fd_set*exceptfds,
__in const struct timeval*timeout);

nfds

用于兼容Berkeley sockets.不用理会,随便给个0值就OK。

readfds

用于检查是否存在可读socket的的一个socket集合。可为空。

writefds

用于检查是否存在可写socket的一个socket集合。可为空。

exceptfds

用于检查是否存在有错误的socket的一个 socket集合,可为空。

timeout

TIMEVAL结构体,用于指定该函数阻塞多长时间。

在 调用select时,当readfds不为空时,当readfds中任何一个socket就绪可读时,或者当writefds不为空且writefds中任何一个socket准备就绪可写,或者当exceptfds不为空且任何一个socket发生socket错误时,select就立即返回。否则,直到timeout指定的时间过去以后才返回。

返回值,返回准备就绪的socket的个数。如果为0,说明该函数超时了,如果大于0,说明至少有一个socket就绪。如果小于0,说明发生错误了。

fd_set 是一种集合类型。

typedef struct fd_set {

        u_int fd_count;               /* how many are SET? */

        SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */

} fd_set;

记录着一个socket数组,以及里面的socket个数。

struct timeval是一个表示等待时间的结构体。

struct timeval {

        long    tv_sec;         /* seconds */

        long    tv_usec;        /* and microseconds */

};

tv_sec表示有多少秒,tv_usec表示有多少毫秒。

对于fd_set类型,用到几个宏定义函数。

FD_ZERO(fd_set*), 清空fd_set集合

FD_SET(SOCKET,fd_set*),把socket加入fd_set集合。

FD_ISSET(SOCKET,fd_set*),判断socket是否在集合fd_set中,并且socket准备就绪。

FD_CLR(SOCKET,fd_set*),如果fd_set存在该SOCKET,则移除它。

下面是改进后的服务端代码

  1. typedef struct _ThreadInfo
  2. {
  3. HANDLE hThread;
  4. bool bRunning;
  5. SOCKET sock;
  6. }ThreadInfo;
  7.  
  8. typedef struct _AcceptThreadParam
  9. {
  10. bool bRunning;
  11. SOCKET listeningSocket;
  12. }AcceptThreadParam;
  13.  
  14. std::list<ThreadInfo*> g_threadInfoList;
  15. CRITICAL_SECTION g_csForList;
  16.  
  17. DWORD WINAPI ListeningThread(LPVOID lpParameter);
  18. DWORD WINAPI CommunicationThread(LPVOID lpParameter);
  19.  
  20. int _tmain(int argc, _TCHAR* argv[])
  21. {
  22. _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
  23.  
  24. // ---------------------------- WSAStartup() ----------------------------//
  25.  
  26. WSADATA wsd;
  27. int resStartup = WSAStartup(MAKEWORD(2,2),&wsd);
  28. if(0 != resStartup)
  29. {
  30. printf("failed to WSAStartup!\n");
  31. return -1;
  32. }
  33. //------------------------------------------------------------------------------//
  34.  
  35. InitializeCriticalSection(&g_csForList);
  36.  
  37. // ---------------------------- socket() ----------------------------//
  38.  
  39. SOCKET serverSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
  40. if(INVALID_SOCKET == serverSocket)
  41. {
  42. printf("failed to invoke socket, the socket returned is invalid!\n");
  43. goto Main_End;
  44. }
  45. // ------------------------------------------------------------------------------------//
  46.  
  47. //---------------------------- bind() ----------------------------//
  48.  
  49. // 初始化 struct sockaddr 结构体, SOCKADDR_IN就是 struct sockaddr的宏定义
  50. SOCKADDR_IN localAddr;
  51. localAddr.sin_family = AF_INET;
  52. localAddr.sin_addr.S_un.S_addr = inet_addr(SERVE_ADDRESS);
  53. localAddr.sin_port = htons(SERVE_PORT);
  54. memset(localAddr.sin_zero,0x0,sizeof(localAddr.sin_zero));
  55.  
  56. //
  57. int resBind = bind(serverSocket,(sockaddr*)&localAddr,sizeof(SOCKADDR_IN));
  58. if(0 != resBind)
  59. {
  60. printf("failed to bind ! \n");
  61. goto Main_End;
  62. }
  63. //------------------------------------------------------------------------------------//
  64.  
  65. //---------------------------- listen() ----------------------------//
  66. int resListen = listen(serverSocket,5);
  67. if(0 != resListen)
  68. {
  69. printf("failed to listen! \n");
  70. goto Main_End;
  71. }
  72.  
  73. //------------------------------------------------------------------------------------//
  74.  
  75. AcceptThreadParam threadParam;
  76. threadParam.bRunning = true;
  77. threadParam.listeningSocket = serverSocket;
  78.  
  79. HANDLE hListeningThread = CreateThread(0,0,ListeningThread,&threadParam,0,0);
  80. if(0 == hListeningThread)
  81. {
  82. printf("failed to create the listening thread!\n");
  83. goto Main_End;
  84. }
  85. else
  86. {
  87. printf("the server is listening now!pass any key to close the server!\n");
  88. }
  89.  
  90. while(true)
  91. {
  92. char ch = getchar();
  93. threadParam.bRunning = false;
  94. DWORD resWait = WaitForSingleObject(hListeningThread,3000);
  95. if(WAIT_TIMEOUT == resWait)
  96. {
  97. printf("failed to wait for the listening thread exiting!\n");
  98. }
  99. else
  100. {
  101. printf("the listening thread has exited!\n");
  102. }
  103.  
  104. break;
  105.  
  106. }
  107.  
  108. Main_End:
  109. if(INVALID_SOCKET != serverSocket)
  110. {
  111. closesocket(serverSocket);
  112. serverSocket = INVALID_SOCKET;
  113. }
  114.  
  115. WSACleanup();
  116. DeleteCriticalSection(&g_csForList);
  117. system("pause");
  118. return 0;
  119. }
  120.  
  121. DWORD WINAPI ListeningThread(LPVOID lpParameter)
  122. {
  123. AcceptThreadParam* pAcceptThreadParam = (AcceptThreadParam*)lpParameter;
  124. SOCKET serverSocket = pAcceptThreadParam->listeningSocket;
  125.  
  126. while(pAcceptThreadParam->bRunning)
  127. {
  128. //---------------------------- accept() ----------------------------//
  129.  
  130. fd_set fdAccept;
  131. FD_ZERO(&fdAccept);
  132. FD_SET(serverSocket,&fdAccept);
  133.  
  134. TIMEVAL acceptTimeVal;
  135. acceptTimeVal.tv_sec = 1;
  136. acceptTimeVal.tv_usec = 0;
  137.  
  138. int selRes = select(0,&fdAccept,0,0,&acceptTimeVal);
  139. if(selRes > 0)
  140. {
  141. SOCKADDR_IN clientAddr;
  142. int addrLen = sizeof(clientAddr);
  143. SOCKET acceptedSocket = accept(serverSocket,(sockaddr*)&clientAddr,&addrLen);
  144. if(INVALID_SOCKET == acceptedSocket)
  145. {
  146. printf("accept error!\n");
  147. break;
  148. }
  149. printf("a client has connected to the server!\n");
  150.  
  151. ThreadInfo* pTI = new ThreadInfo;
  152. pTI->bRunning = true;
  153. pTI->sock = acceptedSocket;
  154. pTI->hThread = CreateThread(0,0,CommunicationThread,(LPVOID)pTI,0,0);
  155. if(0 == pTI->hThread)
  156. {
  157. printf("failed to create a thread!\n");
  158. delete pTI;
  159. pTI = 0;
  160. }
  161. else
  162. {
  163. EnterCriticalSection(&g_csForList);
  164. g_threadInfoList.push_back(pTI);
  165. LeaveCriticalSection(&g_csForList);
  166. }
  167. }
  168. else if(selRes < 0)
  169. {
  170. printf("an error has occured when listening !\n");
  171. break;
  172. }
  173.  
  174. }
  175.  
  176. std::list<ThreadInfo*> tempList;
  177.  
  178. EnterCriticalSection(&g_csForList);
  179. std::list<ThreadInfo*>::iterator listIter;
  180. for(listIter = g_threadInfoList.begin(); listIter != g_threadInfoList.end(); listIter++)
  181. {
  182. (*listIter)->bRunning = false;
  183. tempList.push_back(*listIter);
  184. }
  185.  
  186. g_threadInfoList.clear();
  187. LeaveCriticalSection(&g_csForList);
  188.  
  189. int nSuccessfullyExit = 0;
  190. for(listIter = tempList.begin(); listIter != tempList.end(); listIter++)
  191. {
  192. DWORD resWait = WaitForSingleObject((*listIter)->hThread,2000);
  193. if(WAIT_TIMEOUT == resWait)
  194. {
  195. printf("failed to wait for a communication thread exiting!\n");
  196. }
  197. else
  198. {
  199. nSuccessfullyExit++;
  200. }
  201.  
  202. delete (*listIter);
  203. }
  204.  
  205. printf("succeed waiting for %d thread exiting!\n",nSuccessfullyExit);
  206. tempList.clear();
  207.  
  208. printf("listening thread is exiting!\n");
  209. return 0;
  210. }
  211.  
  212. DWORD WINAPI CommunicationThread(LPVOID lpParameter)
  213. {
  214. ThreadInfo* pThreadInfo = (ThreadInfo*)lpParameter;
  215.  
  216. SOCKET clientSocket = pThreadInfo->sock;
  217. fd_set fdRead,fdWrite;
  218. FD_ZERO(&fdRead);
  219. FD_ZERO(&fdWrite);
  220.  
  221. FD_SET(clientSocket,&fdRead);
  222. FD_SET(clientSocket,&fdWrite);
  223.  
  224. TIMEVAL sendTimeVal;
  225. sendTimeVal.tv_sec = 0;
  226. sendTimeVal.tv_usec = 500;
  227.  
  228. int selRes = select(0,0,&fdWrite,0,&sendTimeVal);
  229. if(selRes <= 0)
  230. {
  231. goto ThreadOver;
  232. }
  233.  
  234. char recvBuffer[256];
  235. char sendBuffer[256];
  236. strcpy(sendBuffer,"server:Welcome to connect !");
  237. int sendBufLen = strlen(sendBuffer);
  238. int resSend = send(clientSocket,sendBuffer,sendBufLen,0);
  239. if(resSend != sendBufLen)
  240. {
  241. printf("there are %d bytes to send, but it just succeeded sending %d bytes!\n",sendBufLen,resSend);
  242. goto ThreadOver;
  243. }
  244.  
  245. while(pThreadInfo->bRunning)
  246. {
  247. FD_ZERO(&fdRead);
  248. FD_SET(pThreadInfo->sock,&fdRead);
  249. TIMEVAL recvTimeVal;
  250. recvTimeVal.tv_sec = 0;
  251. recvTimeVal.tv_usec = 500;
  252.  
  253. int recvSelRes = select(0,&fdRead,0,0,&recvTimeVal);
  254. if(recvSelRes < 0)
  255. {
  256. printf("socket error when receiving!\n");
  257. break;
  258. }
  259. else if(recvSelRes > 0)
  260. {
  261. int recvLen = recv(clientSocket,recvBuffer,sizeof(recvBuffer),0);
  262. if(0 == recvLen)
  263. {
  264. printf("a client close the socket!\n");
  265. break;
  266. }
  267. else if(recvLen < 0)
  268. {
  269. printf("an error has happen when recving\n");
  270. break;
  271. }
  272. else
  273. {
  274. recvBuffer[recvLen] = '\0';
  275. printf("a client:%s\n",recvBuffer);
  276. strcpy(sendBuffer,"server:");
  277. strcat(sendBuffer,recvBuffer);
  278. sendBufLen = strlen(sendBuffer);
  279.  
  280. FD_ZERO(&fdWrite);
  281. FD_SET(pThreadInfo->sock,&fdWrite);
  282.  
  283. sendTimeVal.tv_sec = 0;
  284. sendTimeVal.tv_usec = 500;
  285.  
  286. int sendSelRes = select(0,0,&fdWrite,0,&sendTimeVal);
  287. if(sendSelRes > 0)
  288. {
  289. int bytesSent = send(clientSocket,sendBuffer,sendBufLen,0);
  290. if(bytesSent != sendBufLen)
  291. {
  292. printf("there are %d bytes to be sent,but only %d bytes are sent!\n",sendBufLen,bytesSent);
  293. break;
  294. }
  295. }
  296. else
  297. {
  298. printf("failed to send in 500 ms!\n");
  299. break;
  300. }
  301.  
  302. }
  303. }
  304. }
  305.  
  306. ThreadOver:
  307. closesocket(pThreadInfo->sock);
  308. bool bMainThreadWaiting = true;
  309.  
  310. EnterCriticalSection(&g_csForList);
  311. std::list<ThreadInfo*>::iterator listIter;
  312. for(listIter = g_threadInfoList.begin(); listIter != g_threadInfoList.end(); listIter++)
  313. {
  314. if(pThreadInfo == (*listIter))
  315. {
  316. bMainThreadWaiting = false;
  317. g_threadInfoList.erase(listIter);
  318. break;
  319. }
  320. }
  321. LeaveCriticalSection(&g_csForList);
  322.  
  323. if(false == bMainThreadWaiting)
  324. {
  325. CloseHandle(pThreadInfo->hThread);
  326. delete pThreadInfo;
  327. pThreadInfo = 0;
  328. }
  329.  
  330. return 0;
  331. }

前面的代码与之前的一样,改变的地方在于accept的地方。对于一个监听的socket,如果该socket可读,说明有用户连接过来了。

全局维护了一个纪录创建的线程的信息的链表,每创建一个线程都有一个标识该线程是否应该继续循环执行的bool变量。当bRunning变为false的时候,线程函数跳出循环,返回。

当需要停止服务端运行时,服务端只需要按任何一个键和回车,就会通知线程退出,并且调用WaitForSingleObject(),来确认线程已退出。还有利用了 EnterCriticalSection()和LeaveCriticalSection()临界区函数来保证只有一个线程在操作全局的链表。

使用多线程要消耗一定的资源。对于fd_set,默认最多可以容纳64个socket.所以可以用1个线程去处理64个客户端的连接。而不必每个客户端都创建一个线程。

代码如下:

  1. typedef struct _AcceptThreadParam
  2. {
  3. bool bRunning;
  4. SOCKET listeningSocket;
  5. }AcceptThreadParam;
  6.  
  7. #define SOCKET_ARRAY_SIZE 64
  8.  
  9. SOCKET g_socketArray[SOCKET_ARRAY_SIZE];
  10. int g_socketCount = 0;
  11. CRITICAL_SECTION g_csForSocketArray;
  12.  
  13. DWORD WINAPI ListeningThread(LPVOID lpParameter);
  14. DWORD WINAPI CommunicationThread(LPVOID lpParameter);
  15.  
  16. int _tmain(int argc, _TCHAR* argv[])
  17. {
  18. _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
  19.  
  20. // ---------------------------- WSAStartup() ----------------------------//
  21.  
  22. WSADATA wsd;
  23. int resStartup = WSAStartup(MAKEWORD(2,2),&wsd);
  24. if(0 != resStartup)
  25. {
  26. printf("failed to WSAStartup!\n");
  27. return -1;
  28. }
  29. //------------------------------------------------------------------------------//
  30.  
  31. InitializeCriticalSection(&g_csForSocketArray);
  32. g_socketCount = 0;
  33.  
  34. // ---------------------------- socket() ----------------------------//
  35.  
  36. SOCKET serverSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
  37. if(INVALID_SOCKET == serverSocket)
  38. {
  39. printf("failed to invoke socket, the socket returned is invalid!\n");
  40. goto Main_End;
  41. }
  42. // ------------------------------------------------------------------------------------//
  43.  
  44. //---------------------------- bind() ----------------------------//
  45.  
  46. // 初始化 struct sockaddr 结构体, SOCKADDR_IN就是 struct sockaddr的宏定义
  47. SOCKADDR_IN localAddr;
  48. localAddr.sin_family = AF_INET;
  49. localAddr.sin_addr.S_un.S_addr = inet_addr(SERVE_ADDRESS);
  50. localAddr.sin_port = htons(SERVE_PORT);
  51. memset(localAddr.sin_zero,0x0,sizeof(localAddr.sin_zero));
  52.  
  53. //
  54. int resBind = bind(serverSocket,(sockaddr*)&localAddr,sizeof(SOCKADDR_IN));
  55. if(0 != resBind)
  56. {
  57. printf("failed to bind ! \n");
  58. goto Main_End;
  59. }
  60. //------------------------------------------------------------------------------------//
  61.  
  62. //---------------------------- listen() ----------------------------//
  63. int resListen = listen(serverSocket,5);
  64. if(0 != resListen)
  65. {
  66. printf("failed to listen! \n");
  67. goto Main_End;
  68. }
  69.  
  70. //------------------------------------------------------------------------------------//
  71.  
  72. AcceptThreadParam threadParam;
  73. threadParam.bRunning = true;
  74. threadParam.listeningSocket = serverSocket;
  75.  
  76. bool bCommunicationThreadRunning = true;
  77.  
  78. HANDLE hListeningThread = CreateThread(0,0,ListeningThread,&threadParam,0,0);
  79. HANDLE hCommunicationThread = CreateThread(0,0,CommunicationThread,&bCommunicationThreadRunning,0,0);
  80. if(0 == hListeningThread || 0 == hCommunicationThread)
  81. {
  82. printf("failed to create a thread!\n");
  83.  
  84. if(0 != hListeningThread)
  85. {
  86. threadParam.bRunning = false;
  87. WaitForSingleObject(hListeningThread,2000);
  88. CloseHandle(hListeningThread);
  89. }
  90.  
  91. if(0 != hCommunicationThread)
  92. {
  93. bCommunicationThreadRunning = false;
  94. WaitForSingleObject(hCommunicationThread,2000);
  95. CloseHandle(hCommunicationThread);
  96. }
  97.  
  98. goto Main_End;
  99. }
  100. else
  101. {
  102. printf("the server is listening now!pass any key to close the server!\n");
  103. }
  104.  
  105. while(true)
  106. {
  107. char ch = getchar();
  108. threadParam.bRunning = false;
  109. bCommunicationThreadRunning = false;
  110. DWORD resWait = WaitForSingleObject(hListeningThread,3000);
  111. if(WAIT_TIMEOUT == resWait)
  112. {
  113. printf("failed to wait for the listening thread exiting!\n");
  114. }
  115. else
  116. {
  117. printf("the listening thread has exited!\n");
  118. }
  119.  
  120. CloseHandle(hListeningThread);
  121.  
  122. resWait = WaitForSingleObject(hCommunicationThread,3000);
  123. if(WAIT_TIMEOUT == resWait)
  124. {
  125. printf("failed to wait for the communication thread exiting!\n");
  126. }
  127. else
  128. {
  129. printf("the communication thread has exited!\n");
  130. }
  131.  
  132. CloseHandle(hCommunicationThread);
  133.  
  134. break;
  135.  
  136. }
  137.  
  138. Main_End:
  139. if(INVALID_SOCKET != serverSocket)
  140. {
  141. closesocket(serverSocket);
  142. serverSocket = INVALID_SOCKET;
  143. }
  144.  
  145. WSACleanup();
  146. DeleteCriticalSection(&g_csForSocketArray);
  147. system("pause");
  148. return 0;
  149. }
  150.  
  151. DWORD WINAPI ListeningThread(LPVOID lpParameter)
  152. {
  153. AcceptThreadParam* pAcceptThreadParam = (AcceptThreadParam*)lpParameter;
  154. SOCKET serverSocket = pAcceptThreadParam->listeningSocket;
  155.  
  156. while(pAcceptThreadParam->bRunning)
  157. {
  158. //---------------------------- accept() ----------------------------//
  159.  
  160. fd_set fdAccept;
  161. FD_ZERO(&fdAccept);
  162. FD_SET(serverSocket,&fdAccept);
  163.  
  164. TIMEVAL acceptTimeVal;
  165. acceptTimeVal.tv_sec = 1;
  166. acceptTimeVal.tv_usec = 0;
  167.  
  168. int selRes = select(0,&fdAccept,0,0,&acceptTimeVal);
  169. if(selRes > 0)
  170. {
  171. SOCKADDR_IN clientAddr;
  172. int addrLen = sizeof(clientAddr);
  173. SOCKET acceptedSocket = accept(serverSocket,(sockaddr*)&clientAddr,&addrLen);
  174. if(INVALID_SOCKET == acceptedSocket)
  175. {
  176. printf("accept error!\n");
  177. break;
  178. }
  179. printf("a client has connected to the server!\n");
  180.  
  181. fd_set fdWrite;
  182. FD_ZERO(&fdWrite);
  183. FD_SET(acceptedSocket,&fdWrite);
  184. TIMEVAL writeTimeVal;
  185. writeTimeVal.tv_sec = 0;
  186. writeTimeVal.tv_usec = 500;
  187.  
  188. int writeSelRes = select(0,0,&fdWrite,0,&writeTimeVal);
  189. if(writeSelRes > 0)
  190. {
  191. int sendBufferLen = strlen("server:Welcome to connect!");
  192. int bytesSent = send(acceptedSocket,"server:Welcome to connect!",sendBufferLen,0);
  193. if(bytesSent == sendBufferLen)
  194. {
  195. EnterCriticalSection(&g_csForSocketArray);
  196. if(g_socketCount < 64)
  197. {
  198. g_socketArray[g_socketCount] = acceptedSocket;
  199. g_socketCount++;
  200. }
  201. else
  202. {
  203. printf("the server has accepted more than 64 clients!\n");
  204. closesocket(acceptedSocket);
  205. }
  206. LeaveCriticalSection(&g_csForSocketArray);
  207. }
  208. else
  209. {
  210. printf("send error, there are %d bytes to be sent, but only %d bytes are sent!\n",sendBufferLen,bytesSent);
  211. closesocket(acceptedSocket);
  212. }
  213. }
  214. else
  215. {
  216. printf("select error of can not wait for sending data when select!\n");
  217. closesocket(acceptedSocket);
  218. }
  219. }
  220. else if(selRes < 0)
  221. {
  222. printf("an error has occured when listening !\n");
  223. break;
  224. }
  225.  
  226. }
  227.  
  228. printf("listening thread is exiting!\n");
  229. return 0;
  230. }
  231.  
  232. DWORD WINAPI CommunicationThread(LPVOID lpParameter)
  233. {
  234. bool* pBRunning = (bool*)lpParameter;
  235. char recvBuffer[256];
  236. char tempBuffer[256];
  237.  
  238. while(true == *pBRunning)
  239. {
  240. int currentSocketCount = 0;
  241.  
  242. EnterCriticalSection(&g_csForSocketArray);
  243.  
  244. if(0 == g_socketCount)
  245. {
  246. LeaveCriticalSection(&g_csForSocketArray);
  247. Sleep(200);
  248. continue;
  249. }
  250.  
  251. currentSocketCount = g_socketCount;
  252. LeaveCriticalSection(&g_csForSocketArray);
  253.  
  254. fd_set fdRead;
  255. FD_ZERO(&fdRead);
  256. for(int i = 0; i < currentSocketCount; i++)
  257. {
  258. FD_SET(g_socketArray[i],&fdRead);
  259. }
  260.  
  261. TIMEVAL readTimeVal;
  262. readTimeVal.tv_sec = 1;
  263. readTimeVal.tv_usec = 0;
  264.  
  265. int selRes = select(0,&fdRead,0,0,&readTimeVal);
  266. if(selRes > 0)
  267. {
  268. for(int i = 0; i < currentSocketCount; i++)
  269. {
  270. if(FD_ISSET(g_socketArray[i],&fdRead) != 0)
  271. {
  272. int bytesRecv = recv(g_socketArray[i],recvBuffer,sizeof(recvBuffer),0);
  273. if(bytesRecv > 0)
  274. {
  275. recvBuffer[bytesRecv] = '\0';
  276. printf("the %d client: %s\n",i + 1,recvBuffer);
  277. sprintf(tempBuffer,"the server:%s",recvBuffer);
  278.  
  279. fd_set fdWrite;
  280. FD_ZERO(&fdWrite);
  281. FD_SET(g_socketArray[i],&fdWrite);
  282. TIMEVAL writeTimeVal;
  283. writeTimeVal.tv_sec = 0;
  284. writeTimeVal.tv_usec = 500;
  285.  
  286. int writeSelRes = select(g_socketArray[i],0,&fdWrite,0,&writeTimeVal);
  287. if(writeSelRes > 0)
  288. {
  289. int sendBufLen = strlen(tempBuffer);
  290. int bytesSent = send(g_socketArray[i],tempBuffer,sendBufLen,0);
  291. if(bytesSent == sendBufLen)
  292. {
  293. break;
  294. }
  295. else
  296. {
  297. printf("there are %d bytes to be sent,but only %d bytes are sent!\n",sendBufLen,bytesSent);
  298. }
  299. }
  300. else
  301. {
  302. printf("select error!\n");
  303. }
  304. }
  305. else if(0 == bytesRecv)
  306. {
  307. printf("the %d client has closed the socket!\n",i + 1);
  308. }
  309. else
  310. {
  311. printf("recv error!\n");
  312. }
  313.  
  314. closesocket(g_socketArray[i]);
  315. EnterCriticalSection(&g_csForSocketArray);
  316. g_socketArray[i] = g_socketArray[g_socketCount - 1];
  317. g_socketCount--;
  318. LeaveCriticalSection(&g_csForSocketArray);
  319. }
  320. }
  321. }
  322. else if(selRes < 0)
  323. {
  324. printf("select error in communication thread!\n");
  325. }
  326. }
  327.  
  328. EnterCriticalSection(&g_csForSocketArray);
  329. for(int i = 0; i < g_socketCount; i++)
  330. {
  331. closesocket(g_socketArray[i]);
  332. }
  333. LeaveCriticalSection(&g_csForSocketArray);
  334.  
  335. printf("the communication thread is exiting!\n");
  336. return 0;
  337. }

完成的功能一样。只需要一个线程就可以处理多个客户端了。

还可以用异步IO来实现该服务器,以下是用完成端口来实现同样功能的服务器。

  1. typedef struct _RepeatAcceptingThreadParam
  2. {
  3. SOCKET listeningSocket;
  4. bool* pBRunning;
  5. }RepeatAcceptingThreadParam;
  6.  
  7. typedef struct _CompletionPortWorkerThreadParam
  8. {
  9. HANDLE hCompletionPort;
  10. bool* pBRunning;
  11. }CompletionPortWorkerThreadParam;
  12.  
  13. #define MESSAGE_BUF_SIZE 1024
  14.  
  15. enum OPERATION_TYPE
  16. {
  17. OPERATION_SEND,
  18. OPERATION_RECV
  19. };
  20.  
  21. typedef struct
  22. {
  23. SOCKET sock;
  24. WSAOVERLAPPED overlap;
  25. WSABUF wsaBuf;
  26. char message[1024];
  27. DWORD bytesRecv;
  28. DWORD flags;
  29. OPERATION_TYPE operationType;
  30. }PER_IO_OPERATION_DATA;
  31.  
  32. //global vector, which saves the information of the client sockets connected to the server
  33. std::vector<PER_IO_OPERATION_DATA*> g_perIoDataPointerVec;
  34.  
  35. //accept sockets connected to the server's listening socket in a recycle - while
  36. DWORD WINAPI RepeatAcceptingThread(LPVOID lpParameter);
  37.  
  38. //the worker thread that deal with the communications between the server and the clients.
  39. DWORD WINAPI CompletionPortWorkerThread(LPVOID lpParameter);
  40.  
  41. int _tmain(int argc,_TCHAR* argv[])
  42. {
  43. _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
  44.  
  45. // ---------------------------- WSAStartup() ----------------------------//
  46.  
  47. WSADATA wsd;
  48. int resStartup = WSAStartup(MAKEWORD(2,2),&wsd);
  49. if(0 != resStartup)
  50. {
  51. printf("failed to WSAStartup!\n");
  52. return -1;
  53. }
  54. //------------------------------------------------------------------------------//
  55.  
  56. // ---------------------------- socket() ----------------------------//
  57.  
  58. SOCKET serverSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
  59. if(INVALID_SOCKET == serverSocket)
  60. {
  61. printf("failed to invoke socket, the socket returned is invalid!\n");
  62. return -1;
  63. }
  64. // ------------------------------------------------------------------------------------//
  65.  
  66. //---------------------------- bind() ----------------------------//
  67.  
  68. // 初始化 struct sockaddr 结构体, SOCKADDR_IN就是 struct sockaddr的宏定义
  69. SOCKADDR_IN localAddr;
  70. localAddr.sin_family = AF_INET;
  71. localAddr.sin_addr.S_un.S_addr = inet_addr(SERVE_ADDRESS);
  72. localAddr.sin_port = htons(SERVE_PORT);
  73. memset(localAddr.sin_zero,0x0,sizeof(localAddr.sin_zero));
  74.  
  75. //
  76. int resBind = bind(serverSocket,(sockaddr*)&localAddr,sizeof(SOCKADDR_IN));
  77. if(0 != resBind)
  78. {
  79. printf("failed to bind ! \n");
  80. closesocket(serverSocket);
  81. return -1;
  82. }
  83. //------------------------------------------------------------------------------------//
  84.  
  85. //---------------------------- listen() ----------------------------//
  86. int resListen = listen(serverSocket,5);
  87. if(0 != resListen)
  88. {
  89. printf("failed to listen! \n");
  90. closesocket(serverSocket);
  91. return -1;
  92. }
  93.  
  94. //------------------------------------------------------------------------------------//
  95.  
  96. bool bRepeatAcceptingThreadRunning = true; // a bool variable that take control of terminating the RepeatAcceptingThread.
  97.  
  98. //init the parameter for the RepeatAcceptingThread.
  99. RepeatAcceptingThreadParam rtp;
  100. rtp.listeningSocket = serverSocket;
  101. rtp.pBRunning = &bRepeatAcceptingThreadRunning;
  102.  
  103. HANDLE hRepeatAcceptingThread = CreateThread(0,0,RepeatAcceptingThread,&rtp,0,0);
  104. if(0 == hRepeatAcceptingThread)
  105. {
  106. printf("failed to create the repeat-accepting thread!\n");
  107. closesocket(serverSocket);
  108. return -1;
  109. }
  110.  
  111. printf("the repeat-accepting thread has run!\n");
  112.  
  113. while(true)
  114. {
  115. // pass any key
  116. char ch = getchar();
  117. bRepeatAcceptingThreadRunning = false;//to notify the RepeatAcceptingThread to exit safely
  118.  
  119. DWORD waitRes = WaitForSingleObject(hRepeatAcceptingThread,3000);
  120. if(WAIT_TIMEOUT == waitRes)
  121. {
  122. printf("failed to wait for the repeatAcceptingThread exiting!\n");
  123. }
  124. else
  125. {
  126. printf("the repeat accepting thread has exited!\n");
  127. }
  128.  
  129. CloseHandle(hRepeatAcceptingThread);
  130.  
  131. break;
  132. }
  133.  
  134. system("pause");
  135. return 0;
  136. }
  137.  
  138. DWORD WINAPI RepeatAcceptingThread(LPVOID lpParameter)
  139. {
  140. //get the parameters passed by the creator of the thread.
  141. RepeatAcceptingThreadParam* pParam = (RepeatAcceptingThreadParam*)lpParameter;
  142. SOCKET listeningSocket = pParam->listeningSocket;
  143. bool* pStillRun = pParam->pBRunning;
  144.  
  145. // create a completion port
  146. HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,0,0,0);
  147. if(0 == hCompletionPort)
  148. {
  149. printf("failed to CreateIoCompletionPort!\n");
  150. return -1;
  151. }
  152.  
  153. // a bool variable for notifying the worker threads of exiting.
  154. bool bWorkThreadRunning = true;
  155.  
  156. // a vector of HANDLEs,which will be used for synchronization of waiting the worker threads to exit.
  157. std::vector<HANDLE> threadHandlesVec;
  158.  
  159. SYSTEM_INFO systemInfo;
  160. GetSystemInfo(&systemInfo);
  161.  
  162. //the parameter to be passed to the worker thread.
  163. CompletionPortWorkerThreadParam cpwtp;
  164. cpwtp.pBRunning = &bWorkThreadRunning;
  165. cpwtp.hCompletionPort = hCompletionPort;
  166.  
  167. for(int i = 0; i < systemInfo.dwNumberOfProcessors; i++)
  168. {
  169. HANDLE hThread = CreateThread(0,0,CompletionPortWorkerThread,&cpwtp,0,0);
  170. if(0 == hThread)
  171. {
  172. printf("failed to create a completion port worker thread!\n");
  173. bWorkThreadRunning = false;
  174.  
  175. // terminate all threads created safely.
  176. std::vector<HANDLE>::iterator vecIter;
  177. for(vecIter = threadHandlesVec.begin(); vecIter != threadHandlesVec.end(); vecIter++)
  178. {
  179. DWORD waitRes = WaitForSingleObject(*vecIter,2000);
  180. if(WAIT_TIMEOUT == waitRes)
  181. {
  182. printf("failed the wait for the completion port worker thread!\n");
  183. }
  184.  
  185. CloseHandle(*vecIter);
  186. }
  187.  
  188. threadHandlesVec.clear();
  189.  
  190. CloseHandle(hCompletionPort);
  191. return -1;
  192. }
  193. else
  194. {
  195. threadHandlesVec.push_back(hThread); //add the handle to the vector
  196. }
  197. }
  198.  
  199. printf("succeed creating completion port worker threads!\n");
  200.  
  201. while(true == *pStillRun)
  202. {
  203. fd_set fdAccept;
  204. FD_ZERO(&fdAccept);
  205. FD_SET(listeningSocket,&fdAccept);
  206.  
  207. TIMEVAL acceptTimeVal;
  208. acceptTimeVal.tv_sec = 1;
  209. acceptTimeVal.tv_usec = 0;
  210.  
  211. int selRes = select(0,&fdAccept,0,0,&acceptTimeVal);
  212. if(selRes > 0) // a client connected
  213. {
  214. SOCKADDR_IN clientAddr;
  215. int addrLen = sizeof(clientAddr);
  216.  
  217. SOCKET acceptedSocket = WSAAccept(listeningSocket,(struct sockaddr*)&clientAddr,&addrLen,0,0);
  218. if(0 == acceptedSocket)
  219. {
  220. printf("failed to accept a connection!\n");
  221. }
  222. else
  223. {
  224. printf("a clent %s:%d has connected!\n",inet_ntoa(clientAddr.sin_addr),ntohs(clientAddr.sin_port));
  225.  
  226. PER_IO_OPERATION_DATA* perIoData = new PER_IO_OPERATION_DATA;
  227. if(0 == perIoData)
  228. {
  229. closesocket(acceptedSocket);
  230. printf("failed to new a struct! there is not enough memory!\n\n");
  231. }
  232. else
  233. {
  234. //associate the newly connected client socket with the completion port.
  235. if(0 == CreateIoCompletionPort((HANDLE)acceptedSocket,hCompletionPort,(ULONG_PTR)perIoData,0))
  236. {
  237. printf("failed to associate the newly connected client socket with the completion port!\n");
  238. closesocket(acceptedSocket);
  239. delete perIoData;
  240. perIoData = 0;
  241. }
  242. else
  243. {
  244. //associated successfully, Set the information of the client socket in A PER_IO_OPERATION_DATA struct.
  245. //when a IO operation is completed, we can get notified with the struct to be one of the parameters.
  246. perIoData->sock = acceptedSocket;
  247. perIoData->operationType = OPERATION_SEND;
  248. perIoData->wsaBuf.buf = perIoData->message;
  249. perIoData->overlap.hEvent = INVALID_HANDLE_VALUE;
  250.  
  251. strcpy(perIoData->message,"Welcome to connect to the server!");
  252. perIoData->wsaBuf.len = strlen(perIoData->message);
  253.  
  254. int sendRes = WSASend(acceptedSocket,&(perIoData->wsaBuf),1,&(perIoData->bytesRecv),0,0,0);
  255. if(0 == sendRes) //finished immediately
  256. {
  257. // asynchronously invoke a receive operation. When the reception finished,we can get its information by
  258. // invoking GetQueuedCompletionStatus()
  259. perIoData->wsaBuf.buf = perIoData->message;
  260. perIoData->wsaBuf.len = MESSAGE_BUF_SIZE;
  261. perIoData->flags = 0;
  262. perIoData->operationType = OPERATION_RECV;
  263. ZeroMemory(&perIoData->overlap,sizeof(perIoData->overlap));
  264.  
  265. int recvRes = WSARecv(acceptedSocket,&perIoData->wsaBuf,1,&perIoData->bytesRecv,&perIoData->flags,&perIoData->overlap,0);
  266. if(0 == recvRes) //the receiving operation finished immediately , the information of the operation has been queued.
  267. {
  268. g_perIoDataPointerVec.push_back(perIoData);
  269. }
  270. else if(SOCKET_ERROR == recvRes && WSA_IO_PENDING == WSAGetLastError()) //the receiving operation will finish later
  271. {
  272. g_perIoDataPointerVec.push_back(perIoData);
  273. }
  274. else
  275. {
  276. printf("failed to WSARecv!\n");
  277. closesocket(acceptedSocket);
  278. delete perIoData;
  279. perIoData = 0;
  280. }
  281.  
  282. }
  283. else if(SOCKET_ERROR == sendRes && WSA_IO_PENDING == WSAGetLastError()) //the sending operation will finish later
  284. {
  285. g_perIoDataPointerVec.push_back(perIoData);
  286. }
  287. else
  288. {
  289. //int lastErr = WSAGetLastError();
  290. printf("send data error!\n");
  291. closesocket(acceptedSocket);
  292. delete perIoData;
  293. perIoData = 0;
  294. }
  295. }
  296. }
  297.  
  298. }
  299. }
  300. else if(selRes < 0)
  301. {
  302. printf("select error!\n");
  303. }
  304. }
  305.  
  306. bWorkThreadRunning = false; //notifies the worker threads of exiting
  307.  
  308. // terminate all threads created safely.
  309. std::vector<HANDLE>::iterator vecIter;
  310. for(vecIter = threadHandlesVec.begin(); vecIter != threadHandlesVec.end(); vecIter++)
  311. {
  312. DWORD waitRes = WaitForSingleObject(*vecIter,2000);
  313. if(WAIT_TIMEOUT == waitRes)
  314. {
  315. printf("failed the wait for the completion port worker thread!\n");
  316. }
  317.  
  318. CloseHandle(*vecIter);
  319. }
  320.  
  321. threadHandlesVec.clear();
  322.  
  323. CloseHandle(hCompletionPort);
  324.  
  325. //delete the structs of PER_IO_OPERATION_DATA newed for clients connected.
  326. std::vector<PER_IO_OPERATION_DATA*>::iterator pIoDataPointerIter;
  327. for(pIoDataPointerIter = g_perIoDataPointerVec.begin(); pIoDataPointerIter != g_perIoDataPointerVec.end(); pIoDataPointerIter++)
  328. {
  329. closesocket((*pIoDataPointerIter)->sock);
  330. delete (*pIoDataPointerIter);
  331. *pIoDataPointerIter = 0;
  332. }
  333.  
  334. g_perIoDataPointerVec.clear();
  335.  
  336. printf(" the repeat accepting thread is exiting!\n");
  337. return 0;
  338. }
  339.  
  340. bool ReleaseIOOperationData(PER_IO_OPERATION_DATA* & pDataToBeDeleted)
  341. {
  342. bool retVal = false;
  343.  
  344. std::vector<PER_IO_OPERATION_DATA*>::iterator vecIter;
  345. for(vecIter = g_perIoDataPointerVec.begin(); vecIter != g_perIoDataPointerVec.end(); vecIter++)
  346. {
  347. if(pDataToBeDeleted == (*vecIter))
  348. {
  349. g_perIoDataPointerVec.erase(vecIter);
  350. closesocket(pDataToBeDeleted->sock);
  351. delete pDataToBeDeleted;
  352. pDataToBeDeleted = 0;
  353. retVal = true;
  354. break;
  355. }
  356. }
  357.  
  358. return retVal;
  359. }
  360.  
  361. DWORD WINAPI CompletionPortWorkerThread(LPVOID lpParameter)
  362. {
  363. CompletionPortWorkerThreadParam* pParam = (CompletionPortWorkerThreadParam*)lpParameter;
  364. bool* pStillRun = pParam->pBRunning;
  365. HANDLE hCompletionPort = pParam->hCompletionPort;
  366.  
  367. DWORD dwBytesTransfered;
  368. PER_IO_OPERATION_DATA* pIoData;
  369. WSAOVERLAPPED* pOverlap;
  370.  
  371. while(true == *pStillRun)
  372. {
  373. dwBytesTransfered = 0;
  374. pIoData = 0;
  375. pOverlap = 0;
  376. BOOL bGetStatus = GetQueuedCompletionStatus(hCompletionPort,&dwBytesTransfered,(PULONG_PTR)&pIoData,&pOverlap,500);
  377. if(FALSE == bGetStatus)
  378. {
  379. if(0 == pOverlap) //did not get a packet from the queue.
  380. {
  381. continue;
  382. }
  383. else
  384. {
  385. //get a packet for a failed I/O operation.
  386. }
  387. }
  388.  
  389. if(OPERATION_SEND == pIoData->operationType)
  390. {
  391. if(0 == dwBytesTransfered) //a packet for a failed I/O operation.
  392. {
  393. printf("the client %d has close the socket!\n",pIoData->sock);
  394. ReleaseIOOperationData(pIoData);
  395. }
  396. else
  397. {
  398. // receive operation.
  399. pIoData->operationType = OPERATION_RECV;
  400. pIoData->wsaBuf.buf = pIoData->message;
  401. pIoData->wsaBuf.len = MESSAGE_BUF_SIZE;
  402. pIoData->flags = 0;
  403. ZeroMemory(&pIoData->overlap,sizeof(pIoData->overlap));
  404.  
  405. int recvRes = WSARecv(pIoData->sock,&pIoData->wsaBuf,1,&pIoData->bytesRecv,&pIoData->flags,&pIoData->overlap,0);
  406. if(0 != recvRes && WSA_IO_PENDING != WSAGetLastError())
  407. {
  408. printf("recv error, may be the client %d has close the socket!\n",pIoData->sock);
  409. ReleaseIOOperationData(pIoData);
  410. }
  411. }
  412.  
  413. }
  414. else if(OPERATION_RECV == pIoData->operationType)
  415. {
  416. if(0 == dwBytesTransfered) //a packet for a failed I/O operation.
  417. {
  418. printf("the client %d has close the socket!\n",pIoData->sock);
  419. ReleaseIOOperationData(pIoData);
  420. }
  421. else
  422. {
  423. // show the data received
  424. pIoData->message[dwBytesTransfered] = '\0';
  425. printf("the client %d:%s \n",pIoData->sock,pIoData->message);
  426.  
  427. //send back the data received add a "server:" in the front
  428. char tempBuf[MESSAGE_BUF_SIZE];
  429. sprintf(tempBuf,"server:%s",pIoData->message);
  430. strcpy(pIoData->message,tempBuf);
  431.  
  432. pIoData->operationType = OPERATION_SEND;
  433. pIoData->wsaBuf.buf = pIoData->message;
  434. pIoData->wsaBuf.len = strlen(pIoData->message);
  435.  
  436. int sendRes = WSASend(pIoData->sock,&pIoData->wsaBuf,1,&pIoData->bytesRecv,0,0,0);
  437. if(0 == sendRes)
  438. {
  439. pIoData->operationType = OPERATION_RECV;
  440. pIoData->wsaBuf.buf = pIoData->message;
  441. pIoData->wsaBuf.len = MESSAGE_BUF_SIZE;
  442. pIoData->flags = 0;
  443. ZeroMemory(&pIoData->overlap,sizeof(pIoData->overlap));
  444.  
  445. int recvRes = WSARecv(pIoData->sock,&pIoData->wsaBuf,1,&pIoData->bytesRecv,&pIoData->flags,&pIoData->overlap,0);
  446. if(0 != recvRes && WSA_IO_PENDING != WSAGetLastError())
  447. {
  448. printf("recv error, may be the client %d has close the socket!\n",pIoData->sock);
  449. ReleaseIOOperationData(pIoData);
  450. }
  451. }
  452. else if(SOCKET_ERROR == sendRes && WSA_IO_PENDING == WSAGetLastError())
  453. {
  454.  
  455. }
  456. else
  457. {
  458. printf("send error, maybe the client %d has close the socket!\n",pIoData->sock);
  459. ReleaseIOOperationData(pIoData);
  460. }
  461. }
  462.  
  463. }
  464. }
  465.  
  466. printf("a completion port thread is exiting!\n");
  467. return 0;
  468. }

Windows socket之最简单的socket程序的更多相关文章

  1. python socket编程 实现简单p2p聊天程序

    目标是写一个python的p2p聊天的项目,这里先说一下python socket的基础课程 一.Python Socket 基础课程 Socket就是套接字,作为BSD UNIX的进程通信机制,取后 ...

  2. [JavaWeb基础] 024.Socket编程之简单的聊天程序

    1.Socket的简介 1)什么是Socket 网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket.Socket通常用来实现客户方和服务方的连接.Socket ...

  3. 基于socket实现的简单的聊天程序

    记得八年前第一次使用socket做的一个五子棋程序,需要序列化棋子对象,传递到对方的电脑上. 一个偶然的机会,第二次使用socket做点事情.先看聊天服务器端的实现: 服务器端要实现以下功能:     ...

  4. Java网络编程以及简单的聊天程序

    网络编程技术是互联网技术中的主流编程技术之一,懂的一些基本的操作是非常必要的.这章主要讲解网络编程,UDP和Socket编程,以及使用Socket做一个简单的聊天软件. 全部代码下载:链接 1.网络编 ...

  5. Socket编程实践(3) 多连接服务器实现与简单P2P聊天程序例程

    SO_REUSEADDR选项 在上一篇文章的最后我们贴出了一个简单的C/S通信的例程.在该例程序中,使用"Ctrl+c"结束通信后,服务器是无法立即重启的,如果尝试重启服务器,将被 ...

  6. 简单的聊天程序,主要用到的是Socket

    服务端: import java.io.*; import java.net.*; import java.util.*; public class ChatServer { boolean stat ...

  7. socket实例C语言:一个简单的聊天程序

    我们老师让写一个简单的聊天软件,并且实现不同机子之间的通信,我用的是SOCKET编程.不废话多说了,先附上代码: 服务器端server.c #include <stdio.h> #incl ...

  8. 局域网内python socket实现windows与linux间简单的消息传送

    有个需求,就是在windows上看见一篇介绍linux相关的文章,想在局域网内的另外一台linux电脑上尝试一下, 于是就需要把该网页链接发送给linux,不想一点一点敲链接,又苦于没有找到其它好的方 ...

  9. 【总结】学习Socket编写的聊天室小程序

    1.前言 在学习Socket之前,先来学习点网络相关的知识吧,自己学习过程中的一些总结,Socket是一门很高深的学问,本文只是Socket一些最基础的东西,大神请自觉绕路. 传输协议 TCP:Tra ...

随机推荐

  1. 大约Android 了解权限管理

    如Android应用程序开发人员.为android权限机制一直觉得很奇怪.为什么要这个东西权限?为什么要AndroidManifest里面写的uses-permission 这样的事情?我一直搞不清楚 ...

  2. 【Java技术位】——代理模式及其事务包

    背景 项目中我们会遇到这种情况:在几个方法中增加同样的代码,这些代码是与业务无关的,而且以后有可能因为考虑不周或需求变动再或者是其它原因,我们须要对他们进行逐一进行修改.举个详细的样例,比方程序中的日 ...

  3. Java中关于OOM的场景及解决方法(转)

    http://developer.51cto.com/art/201112/305696.htm 1.OOM for Heap=>例如:java.lang.OutOfMemoryError: J ...

  4. ubuntu突然卡住

    ctrl+alt+f1.进username和password.然后进入: killall gnome-sesseion sudo pkill X 版权声明:本文博主原创文章,博客,未经同意不得转载.

  5. Redis被攻击

    记一次Redis被攻击的事件   最近几个月非常忙,所以很少有时间写博客,这几天终于闲了一些,于是就在整理平时的一些笔记.恰好这几天Redis服务器发生了问题,就记录一下. 我司有两款分别是2B和2C ...

  6. 通过配置Windows 防火墙允许使用TCP/IP协议远程访问数据库

    原文:通过配置Windows 防火墙允许使用TCP/IP协议远程访问数据库 本文适用于:2005.2008.2008R2所有版本 为了可以通过TCP/IP协议远程访问SQLServer数据库,需要做以 ...

  7. Vijos.1096 津津储蓄计划

    见问题: https://vijos.org/p/1096 主题概述 津津的零花钱一直都是自己的管理.每月初的母亲津津300美元,津津将于本月支出预算.而且总是做同样的实际支出与预算.  为了让津津学 ...

  8. 它们的定义TextView使之具有跑马灯的效果

    一.引入问题 使用通用textview快乐效应,焦点事件不启动滚动,button目前的焦点事件,但丑,因此,需要定制TextView 天生焦点 个textview FocusedTextView.ja ...

  9. ASP.Net中使用XMLDataSource

    在Web开发中,程序和数据库打交道是常有的事情.在平时使用过程中,使用较多的是MS SQLSERVER,因此经常用到SQLDataSource将数据绑定的数据控件上.有时数据量较小,无需要在数据库中创 ...

  10. BI—脚不一样的感觉

    在这个网络智能的时代,假设生活和智能挂不上边那就太落后啦!尤其IT行业更是如此,前不久还在用微软的office做报表,这几天就吵吵着换成BI,那么BI是什么?有什么用?怎么用?等等带着这一系列的问题来 ...