(通以灵码)socket tcp中,连接的客户端进程终止,为什么我的服务端进程也终止了

服务端在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");

    }

回答:

在你的示例代码中,服务端进程在循环中不断地向客户端发送数据。如果客户端进程突然终止,服务端继续向已关闭的连接发送数据时,可能会遇到以下问题导致服务端进程终止:

  1. SIGPIPE 信号

    • 当服务端尝试通过已关闭的连接发送数据时,默认情况下会收到一个 SIGPIPE 信号。
    • 如果服务端没有正确处理这个信号,进程就会终止。
  2. 资源引用问题

    • 如果客户端关闭了连接,而服务端仍然持有指向该连接的文件描述符,那么当服务端尝试使用该文件描述符时,可能会引发错误。

为了防止服务端进程因客户端断开连接而终止,你需要做以下几点调整:

忽略 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() 后检查返回值。如果检测到客户端已经断开连接,我们关闭该连接并退出内部循环,以便服务端可以继续接受新的连接。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值