英文:
How to fix Error 141 when using select() and send() in C++ web server for multiple clients
问题
我正在构建一个用C++编写的Web服务器,需要同时为多个客户端提供服务,而不会阻塞任何单个客户端。为了实现这一目标,我正在使用select()
函数。
当一个客户端在我发送响应时(在这种情况下,是一个长视频)关闭连接时,我遇到了一个错误。程序以状态141退出,在使用LLDB进行调试时,似乎正是在send()
函数中发生的,当尝试发送到一个不再存在的套接字时,因为客户端关闭了连接。
在这种情况下,我是否遗漏了什么,或者需要以不同的方式处理?
在我的代码中,我有以下设置:
// 代码部分不翻译
如果您需要进一步的帮助或解释,请随时提出。
英文:
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:
void Error(const char *msg)
{
perror(msg);
exit(1);
}
void Server::Init()
{
memset(&server_infos, 0, sizeof(server_infos));
server_infos.ai_family = AF_INET;
server_infos.ai_protocol = SOCK_STREAM;
getaddrinfo(LOCALHOST, PORT, &server_infos, &sinfo_ptr);
}
void Server::CreateServer()
{
Init();
if ((server_socket = socket(sinfo_ptr->ai_family, sinfo_ptr->ai_protocol, 0)) == -1)
Error("Error: Creating socket failed\n");
int optval = 1;
setsockopt(server_socket, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
if (bind(server_socket, sinfo_ptr->ai_addr, sinfo_ptr->ai_addrlen) == -1)
Error("Error: Binding failed\n");
if (listen(server_socket, FD_SETSIZE) == -1)
Error("Error: Listening failed\n");
}
void Server::SendResponseHeader(int clt_skt)
{
char response_header[] = "HTTP/1.1 200 OK\r\n"
"Server: Allah Y7ssen L3wan\r\n"
"Content-Length: 82013359\r\n"
"Content-Type: video/mp4\r\n\r\n";
if (send(clt_skt, response_header, strlen(response_header), 0) == -1)
Error("Error (Send) -> ");
}
void Server::DropClient()
{
close(active_clt);
FD_CLR(active_clt, &readfds);
FD_CLR(active_clt, &writefds);
_clients.erase(itb);
close(itb->GetCltFd());
std::cerr << "Connection Closed\n";
}
int Server::AcceptAddClientToSet()
{
int newconnection = accept(server_socket, (struct sockaddr *)&storage_sock, &clt_addr);
if (newconnection == -1)
Error("Error (Accept) -> ");
const char *path = "/Users/me/Desktop/video.mp4";
_clients.push_back(Client(newconnection, open(path, O_RDONLY)));
client_write_ready = false;
FD_SET(_clients.back().GetCltSocket(), &readfds);
FD_SET(_clients.back().GetCltSocket(), &writefds);
if (_clients.back().GetCltSocket() > maxfds)
maxfds = _clients.back().GetCltSocket();
return (newconnection);
}
void Server::Start()
{
CreateServer();
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_SET(server_socket, &readfds);
FD_SET(server_socket, &writefds);
maxfds = server_socket;
bytesreceived = 0;
while (TRUE)
{
tmpfdsread = readfds;
tmpfdswrite = writefds;
activity = select(maxfds + 1, &tmpfdsread, &tmpfdswrite, NULL, NULL);
if (activity == -1)
Error("Error (Select) -> ");
if (FD_ISSET(server_socket, &tmpfdsread))
client_socket = AcceptAddClientToSet();
for (itb = _clients.begin(); itb != _clients.end(); itb++)
{
active_clt = itb->GetCltSocket();
if (FD_ISSET(active_clt, &tmpfdsread) && !client_write_ready)
{
bytesreceived = recv(active_clt, requested_data, sizeof(requested_data), 0);
if (bytesreceived <= 0)
{
DropClient();
std::cerr << "Connection Closed\n";
}
else
{
client_write_ready = true;
SendResponseHeader(active_clt);
}
}
if (FD_ISSET(active_clt, &tmpfdswrite) && client_write_ready)
{
bytesread = read(itb->GetCltFd(), buffer, sizeof(buffer));
if (bytesread == -1)
Error("Error (Read) -> ");
bytessent = send(active_clt, buffer, bytesread, 0);
if (bytessent == -1)
{
std::cout << "Cannot Send\n";
Error("Error (Send) -> ");
}
if (bytesread == 0)
{
DropClient();
std::cout << "Done With This Client ->" << itb->GetCltFd() << "\n";
}
}
}
}
close(server_socket);
}
答案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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论