服务端在send函数发送给客户端client_fd时,客户端关闭导致服务端进程终止,出现问题代码:
while(1){
//发送protobuf序列化的数据
send(client_fd, data.c_str(), data.size(), 0);
//延迟一秒
std::this_thread::sleep_for(std::chrono::seconds(1));
printf("bbbbbbbbbbbbbbb\n");
}
回答:
在你的示例代码中,服务端进程在循环中不断地向客户端发送数据。如果客户端进程突然终止,服务端继续向已关闭的连接发送数据时,可能会遇到以下问题导致服务端进程终止:
-
SIGPIPE 信号:
- 当服务端尝试通过已关闭的连接发送数据时,默认情况下会收到一个
SIGPIPE信号。 - 如果服务端没有正确处理这个信号,进程就会终止。
- 当服务端尝试通过已关闭的连接发送数据时,默认情况下会收到一个
-
资源引用问题:
- 如果客户端关闭了连接,而服务端仍然持有指向该连接的文件描述符,那么当服务端尝试使用该文件描述符时,可能会引发错误。
为了防止服务端进程因客户端断开连接而终止,你需要做以下几点调整:
忽略 SIGPIPE 信号
在服务端启动之初,可以通过忽略 SIGPIPE 信号来避免进程意外终止:
#include <signal.h>
void ignore_sigpipe() {
signal(SIGPIPE, SIG_IGN);
}
int main() {
ignore_sigpipe();
// 其他代码...
}
检查 send() 返回值
每次调用 send() 后检查其返回值,确保发送成功或处理错误
ssize_t bytes_sent = send(client_fd, data.c_str(), data.size(), 0);
if (bytes_sent == -1) {
if (errno == EPIPE) {
// 客户端已经关闭连接
std::cerr << "Client disconnected." << std::endl;
close(client_fd);
// 可以选择退出循环或重新接受连接
break;
} else {
// 其他错误
perror("Send failed");
// 根据具体情况处理
}
}
完整示例
下面是一个修改后的示例,展示了如何处理客户端断开连接的情况:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
#include <iostream>
#include <string>
#include <thread>
#include <chrono>
void ignore_sigpipe() {
signal(SIGPIPE, SIG_IGN);
}
int main() {
int serverSocket, clientSocket;
struct sockaddr_in serverAddr, clientAddr;
socklen_t clientAddrLen = sizeof(clientAddr);
// 创建 socket
serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket == -1) {
perror("Socket creation failed");
return -1;
}
// 设置 socket 地址结构
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(8080); // 监听端口
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); // 监听所有地址
// 绑定 socket 到地址
if (bind(serverSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) == -1) {
perror("Binding failed");
close(serverSocket);
return -1;
}
// 设置 socket 为非阻塞
if (fcntl(serverSocket, F_SETFL, O_NONBLOCK) == -1) {
perror("Setting non-blocking failed");
close(serverSocket);
return -1;
}
// 开始监听
if (listen(serverSocket, 5) == -1) {
perror("Listening failed");
close(serverSocket);
return -1;
}
std::cout << "Server is listening on port 8080..." << std::endl;
ignore_sigpipe(); // 忽略 SIGPIPE 信号
while (true) {
// 接受连接
clientSocket = accept(serverSocket, (struct sockaddr *)&clientAddr, &clientAddrLen);
if (clientSocket == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
std::cout << "No connections to accept yet." << std::endl;
continue; // 没有连接可接受
} else {
perror("Accept failed");
break;
}
}
std::cout << "Accepted connection from " << inet_ntoa(clientAddr.sin_addr) << ":" << ntohs(clientAddr.sin_port) << std::endl;
// 设置客户端 socket 为非阻塞
if (fcntl(clientSocket, F_SETFL, O_NONBLOCK) == -1) {
perror("Setting non-blocking failed for client socket");
close(clientSocket);
continue;
}
std::string data = "Hello, client!";
while (true) {
ssize_t bytes_sent = send(clientSocket, data.c_str(), data.size(), 0);
if (bytes_sent == -1) {
if (errno == EPIPE) {
// 客户端已经关闭连接
std::cerr << "Client disconnected." << std::endl;
close(clientSocket);
break;
} else {
// 其他错误
perror("Send failed");
close(clientSocket);
break;
}
}
std::this_thread::sleep_for(std::chrono::seconds(1));
printf("bbbbbbbbbbbbbbb\n");
}
}
close(serverSocket);
return 0;
}
在这个示例中,我们添加了对 SIGPIPE 信号的忽略,并在每次 send() 后检查返回值。如果检测到客户端已经断开连接,我们关闭该连接并退出内部循环,以便服务端可以继续接受新的连接。
2925

被折叠的 条评论
为什么被折叠?



