-1

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 SIGPIPE\n%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
tgoerg
  • 113
  • 3
  • 8
  • Why not somehow ping the client within your while loop and if it fails initialize a timeout sequence? – user7778287 Feb 16 '21 at 16:19
  • @user7778287 each client on every write? "Real" servers do that? – tgoerg Feb 16 '21 at 16:21
  • They must do something. If there is a way to discover the state of each *x* without iterating through the list I eagerly await to learn of it.. – user7778287 Feb 16 '21 at 16:24
  • 1
    The solution is `signal(SIGPIPE, SIG_IGN);`, not what you posted as an alleged solution. You also need to handle `write` call return and error code. – Maxim Egorushkin Feb 16 '21 at 19:02

1 Answers1

1

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; }
}
Chris Dodd
  • 119,907
  • 13
  • 134
  • 226