介绍

默认情况下,建立的套接字都是阻塞的,程序会停在等待网络事件的地方(accept),直到网络io条件满足,才会继续向下执行
image.png

代码实现

  1. #include <WinSock2.h>
  2. #include <Windows.h>
  3. #include <WS2tcpip.h>
  4. #include <stdlib.h>
  5. #include <stdio.h>
  6. #pragma comment(lib,"ws2_32.lib")
  7. #define DEFAULT_BUFLEN 512
  8. #define DEFAULT_PORT 27015
  9. int __cdecl main(int argc,TCHAR* argv[]) {
  10. WSADATA wsaData;
  11. int iResult;
  12. SOCKET ServerSocket = INVALID_SOCKET;
  13. SOCKET AcceptSocket = INVALID_SOCKET;
  14. char recvbuf[DEFAULT_BUFLEN];
  15. int recvBufLen = DEFAULT_BUFLEN;
  16. sockaddr_in addrClient;
  17. int addrClinetLen = sizeof(sockaddr_in);
  18. iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
  19. if (iResult!=0)
  20. {
  21. printf_s("wsatartup faild with error code %d\n", iResult);
  22. return 1;
  23. }
  24. ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
  25. if (ServerSocket==INVALID_SOCKET)
  26. {
  27. printf_s("SOCKET FAILED WITH ERROR CODE %d",WSAGetLastError());
  28. WSACleanup();
  29. return 1;
  30. }
  31. SOCKADDR_IN addrServ;
  32. addrServ.sin_family = AF_INET;
  33. addrServ.sin_port = htons(DEFAULT_PORT);
  34. addrServ.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
  35. iResult = bind(ServerSocket,(const struct sockaddr*)&addrServ,sizeof(SOCKADDR_IN));
  36. if (iResult==SOCKET_ERROR)
  37. {
  38. printf_s("bind falied with error code %d\n",iResult);
  39. closesocket(ServerSocket);
  40. WSACleanup();
  41. return 1;
  42. }
  43. iResult = listen(ServerSocket,SOMAXCONN);
  44. if (iResult==SOCKET_ERROR)
  45. {
  46. printf_s("listen failed with error code %d",iResult);
  47. closesocket(ServerSocket);
  48. WSACleanup();
  49. return -1;
  50. }
  51. printf_s("TCP server starting");
  52. int err;
  53. while (true)
  54. {
  55. AcceptSocket = accept(ServerSocket, (sockaddr FAR*) & addrClient, &addrClinetLen);
  56. if (AcceptSocket == INVALID_SOCKET)
  57. {
  58. printf_s("accetp failed \n");
  59. closesocket(ServerSocket);
  60. WSACleanup();
  61. return 1;
  62. }
  63. while (true)
  64. {
  65. memset(recvbuf, 0, recvBufLen);
  66. iResult = recv(AcceptSocket, recvbuf, recvBufLen, 0);
  67. if (iResult>0)
  68. {
  69. printf_s("recv data %d byte\n",iResult);
  70. int iSendResult = send(AcceptSocket, recvbuf, iResult, 0);
  71. if (iSendResult == SOCKET_ERROR) {
  72. printf("send failed with error: %d\n", WSAGetLastError());
  73. closesocket(AcceptSocket);
  74. WSACleanup();
  75. return 1;
  76. }
  77. printf("Bytes sent: %d\n", iSendResult);
  78. continue;
  79. }
  80. else if (iResult == 0)
  81. {
  82. printf_s("conn closeing ....\n");
  83. closesocket(AcceptSocket);
  84. break;
  85. }
  86. else
  87. {
  88. printf_s("%d",iResult);
  89. printf_s("recv failed with error%d\n",WSAGetLastError());
  90. closesocket(AcceptSocket);
  91. closesocket(ServerSocket);
  92. WSACleanup();
  93. return 1;
  94. }
  95. }
  96. }
  97. closesocket(ServerSocket);
  98. closesocket(AcceptSocket);
  99. WSACleanup();
  100. return 0;
  101. }

评价

  • 优点:使用非常简单直接
  • 缺点:
    • 无法同时处理多个套接字
    • 程序会在等待io处卡死,程序效率低下

改进思路:

  1. 多线程并发处理多个io,即一个线程处理一个socket,
    • 问题:线程频繁创建,唤醒,睡眠。导致用户态内核态频繁切换。增大系统开销
    • 线程同步,条件竞争,需要研究锁问题
  2. 异步,非阻塞处理多个io
    • 这种情况下,程序仍然为单线程,但是对io处理上使用回调机制,捕获io条件满足时机达到一对多效果
    • 问题:确定网络事件发生、网络io消息,回调机制