Skip to content
Advertisement

C++ + linux handle SIGPIPE signal

Yes, I understand this issue has been discussed many times. And yes, I’ve seen and read these and other discussions:

1 2 3
and I still can’t fix my code myself.
I am writing my own web server. In the next cycle, it listens on a socket, connects each new client and writes it to a vector. Into my class i have this struct:

struct Connection
{
    int socket;
    std::chrono::system_clock::time_point tp;
    std::string request;
};

with next data structures:

std::mutex connected_clients_mux_;
std::vector<HttpServer::Connection> connected_clients_;

and the cycle itself:

//...
bind  (listen_socket_, (struct sockaddr *)&addr_, sizeof(addr_));
listen(listen_socket_, 4 );
while(1){
    connection_socket_ = accept(listen_socket_, NULL, NULL);
    //...
    Connection connection_;
    //...
    connected_clients_mux_.lock();
    this->connected_clients_.push_back(connection_);
    connected_clients_mux_.unlock();
}

it works, clients connect, send and receive requests. But the problem is that if the connection is broken (“^C” for client), then my program will not know about it even at the moment:

    void SendRespons(HttpServer::Connection socket_){   
    write(socket_.socket,( socket_.request + std::to_string(socket_.socket)).c_str(), 1024);
}

as the title of this question suggests, my app receives a SIGPIPE signal. Again, I have seen “solutions”.

signal(SIGPIPE, &SigPipeHandler);

void SigPipeHandler(int s) {
    //printf("Caught SIGPIPEn%d",s);
}

but it does not help. At this moment, we have the “№” of the socket to which the write was made, is it possible to “remember” it and close this particular connection in the handler method?
my system:

Operating System: Ubuntu 20.04.2 LTS
Kernel: Linux 5.8.0-43-generic
g++ --version
g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0

Advertisement

Answer

As stated in the links you give, the solution is to ignore SIGPIPE, and CHECK THE RETURN VALUE of the write calls. This latter is needed for correct operation (short writes) in all but the most trivial, unloaded cases anyways. Also the fixed write size of 1024 that you are using is probably not what you want — if your response string is shorter, you’ll send a bunch of random garbage along with it. You probably really want something like:

void SendRespons(HttpServer::Connection socket_){
    auto data = socket_.request + std::to_string(socket_.socket);
    int sent = 0;
    while (sent < data.size()) { 
        int len = write(socket_.socket, &data[sent], data.size() - sent);
        if (len < 0) {
            // there was an error -- might be EPIPE or EAGAIN or EINTR or ever a few other
            // obscure corner cases.  For EAGAIN or EINTR (which can only happen if your
            // program is set up to allow them), you probably want to try again.
            // Anything else, probably just close the socket and clean up.
            if (errno == EINTR)
                continue;
            close(socket_.socket);
            // should tell someone about it?
            break; }
        sent += len; }
}
User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement