如何在C++ Web服务器中为多个客户端修复使用select()和send()时的Error 141。

huangapple go评论108阅读模式
英文:

How to fix Error 141 when using select() and send() in C++ web server for multiple clients

问题

我正在构建一个用C++编写的Web服务器,需要同时为多个客户端提供服务,而不会阻塞任何单个客户端。为了实现这一目标,我正在使用select()函数。

当一个客户端在我发送响应时(在这种情况下,是一个长视频)关闭连接时,我遇到了一个错误。程序以状态141退出,在使用LLDB进行调试时,似乎正是在send()函数中发生的,当尝试发送到一个不再存在的套接字时,因为客户端关闭了连接。

在这种情况下,我是否遗漏了什么,或者需要以不同的方式处理?

在我的代码中,我有以下设置:

  1. // 代码部分不翻译

如果您需要进一步的帮助或解释,请随时提出。

英文:

I'm working on building a web server in C++ that needs to serve multiple clients simultaneously without blocking any individual client. To achieve this, I'm using the select() function.

I'm encountering an error when a client closes the connection while I'm sending a response (in this case, a long video). The program exits with status 141, and upon debugging with LLDB, it seems to happen exactly in the send() function when trying to send to a socket that no longer exists because the client closed the connection.

Is there something I'm missing, or need to handle differently, in this situation?

I have the following setup in my code:

  1. void Error(const char *msg)
  2. {
  3. perror(msg);
  4. exit(1);
  5. }
  6. void Server::Init()
  7. {
  8. memset(&server_infos, 0, sizeof(server_infos));
  9. server_infos.ai_family = AF_INET;
  10. server_infos.ai_protocol = SOCK_STREAM;
  11. getaddrinfo(LOCALHOST, PORT, &server_infos, &sinfo_ptr);
  12. }
  13. void Server::CreateServer()
  14. {
  15. Init();
  16. if ((server_socket = socket(sinfo_ptr->ai_family, sinfo_ptr->ai_protocol, 0)) == -1)
  17. Error("Error: Creating socket failed\n");
  18. int optval = 1;
  19. setsockopt(server_socket, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
  20. if (bind(server_socket, sinfo_ptr->ai_addr, sinfo_ptr->ai_addrlen) == -1)
  21. Error("Error: Binding failed\n");
  22. if (listen(server_socket, FD_SETSIZE) == -1)
  23. Error("Error: Listening failed\n");
  24. }
  25. void Server::SendResponseHeader(int clt_skt)
  26. {
  27. char response_header[] = "HTTP/1.1 200 OK\r\n"
  28. "Server: Allah Y7ssen L3wan\r\n"
  29. "Content-Length: 82013359\r\n"
  30. "Content-Type: video/mp4\r\n\r\n";
  31. if (send(clt_skt, response_header, strlen(response_header), 0) == -1)
  32. Error("Error (Send) -> ");
  33. }
  34. void Server::DropClient()
  35. {
  36. close(active_clt);
  37. FD_CLR(active_clt, &readfds);
  38. FD_CLR(active_clt, &writefds);
  39. _clients.erase(itb);
  40. close(itb->GetCltFd());
  41. std::cerr << "Connection Closed\n";
  42. }
  43. int Server::AcceptAddClientToSet()
  44. {
  45. int newconnection = accept(server_socket, (struct sockaddr *)&storage_sock, &clt_addr);
  46. if (newconnection == -1)
  47. Error("Error (Accept) -> ");
  48. const char *path = "/Users/me/Desktop/video.mp4";
  49. _clients.push_back(Client(newconnection, open(path, O_RDONLY)));
  50. client_write_ready = false;
  51. FD_SET(_clients.back().GetCltSocket(), &readfds);
  52. FD_SET(_clients.back().GetCltSocket(), &writefds);
  53. if (_clients.back().GetCltSocket() > maxfds)
  54. maxfds = _clients.back().GetCltSocket();
  55. return (newconnection);
  56. }
  57. void Server::Start()
  58. {
  59. CreateServer();
  60. FD_ZERO(&readfds);
  61. FD_ZERO(&writefds);
  62. FD_SET(server_socket, &readfds);
  63. FD_SET(server_socket, &writefds);
  64. maxfds = server_socket;
  65. bytesreceived = 0;
  66. while (TRUE)
  67. {
  68. tmpfdsread = readfds;
  69. tmpfdswrite = writefds;
  70. activity = select(maxfds + 1, &tmpfdsread, &tmpfdswrite, NULL, NULL);
  71. if (activity == -1)
  72. Error("Error (Select) -> ");
  73. if (FD_ISSET(server_socket, &tmpfdsread))
  74. client_socket = AcceptAddClientToSet();
  75. for (itb = _clients.begin(); itb != _clients.end(); itb++)
  76. {
  77. active_clt = itb->GetCltSocket();
  78. if (FD_ISSET(active_clt, &tmpfdsread) && !client_write_ready)
  79. {
  80. bytesreceived = recv(active_clt, requested_data, sizeof(requested_data), 0);
  81. if (bytesreceived <= 0)
  82. {
  83. DropClient();
  84. std::cerr << "Connection Closed\n";
  85. }
  86. else
  87. {
  88. client_write_ready = true;
  89. SendResponseHeader(active_clt);
  90. }
  91. }
  92. if (FD_ISSET(active_clt, &tmpfdswrite) && client_write_ready)
  93. {
  94. bytesread = read(itb->GetCltFd(), buffer, sizeof(buffer));
  95. if (bytesread == -1)
  96. Error("Error (Read) -> ");
  97. bytessent = send(active_clt, buffer, bytesread, 0);
  98. if (bytessent == -1)
  99. {
  100. std::cout << "Cannot Send\n";
  101. Error("Error (Send) -> ");
  102. }
  103. if (bytesread == 0)
  104. {
  105. DropClient();
  106. std::cout << "Done With This Client ->" << itb->GetCltFd() << "\n";
  107. }
  108. }
  109. }
  110. }
  111. close(server_socket);
  112. }

答案1

得分: 0

Ignoring the SIGPIPE signal using signal(SIGPIPE, SIG_IGN) 允许程序继续运行,即使写入/发送操作由于关闭/无效的套接字而失败。

https://stackoverflow.com/questions/108183/ 有帮助。

英文:

Ignoring the SIGPIPE signal using signal(SIGPIPE, SIG_IGN) allows the program to continue running even if the write/send operation fails due to a closed/invalid socket.

https://stackoverflow.com/questions/108183/ is helpful.

huangapple
  • 本文由 发表于 2023年6月6日 15:19:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/76412255.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定