0

I first use epoll to listen to the local socket_fd , if a non-local socket_fd triggers epoll , the socket_fd will be passed to a thread pool for processing.

The processing flow is like this.

  1. unlisten the socket_fd from epoll
  2. read the request (recv) from this socket_fd
  3. parse the request
  4. send a response
  5. read the request (recv) from this socket_fd
  6. ......

recv is "non-blocking" (ET), If recv returns a value less than 0 when reading the request, disconnect the socket_fd ( if the return value of recv is less than 0, it means that the client is no longer sending requests, so the connection is closed, close(socket_fd)).

Unlisten to socket_fd from the beginning because I don't want this socket_fd to trigger epoll_wait() when it resends a request, causing multiple threads to operate on one socket_fd (I want this socket_fd to be hosted by the assigned thread, But it seems that my fears are superfluous, because epoll works in ET).

This is what I don't understand, am I doing something wrong?

I don't know if it's because I have a problem with my thinking (a problem with the understanding of http 1.1 long connections)

The following code is the code to handle the client connection

void Handling_connections(int client_socket_fd)
{
    //Remove the specified socket_fd from the monitoring queue of epoll
    DEL_epoll_evs(client_socket);

    for (; true; )  
    {
        //Read the http header sent by the client
        char* client_header_cstr = new char[1024];
        memset(client_header_cstr, 0, 1024);
        int read_size = recv(client_socket_fd, client_header_cstr, 1024);

        //Disconnect the client if the content read is empty
        if (read_size < 1)
        {
            std::cout << "Client disconnection \n\n\n";
            close(client_socket_fd);
            return;
        }
  
        //Ensure that the read http header is complete
        std::string client_header(client_header_cstr);
        for (; read_size == 1024; )
        {
            memset(client_header_cstr, 0, 1024);
            read_size = read(client_socket_fd, client_header_cstr, 1024);
            client_header += client_header_cstr;
        }
        delete[](client_header_cstr);
  
        //http_header is an object I wrote to parse the http header
        //it just parses the http header
        http_header tmp_header(client_header);
  
        //write_client sends the data to the client via the parsed http header
        //and it does not disconnect the client regardless of the success of the transmission.
        write_client(tmp_header);

        //I thought the return value of the next recv was less than 0,
        //because the interval between re-reading the data and the last data sent was too short 
        //(the client did not receive the data yet, and the server started reading the request message again), 
        //so I tried to do a short wait (100ms) before the next recv request message, 
        //but there was no difference with the previous result (the return value of the next recv was less than 0)
        //
        //std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }

}

I thought I could use one tcp connection to handle multiple requests, but every time I finished sending data and re-fetching the socket_fd data, the return value of recv would be less than 0(It's as if the client is no longer sending requests, But sometimes, multiple requests can reuse this one socket connection, maybe 3 out of 50 requests.), causing the connection to be disconnected(just like http 1.0).

Nullptr
  • 19
  • 1
  • 1
    Introducing artificial delays is never the right fix for anything. Perhaps examining what's in `errno` will point you towards more clues. In any case, it's unlikely that anyone on Stackoverflow will be able to tell you anything, since the authoritative answer will require having a full and complete understanding of the actual code, and the full access to it, which only you have. – Sam Varshavchik Apr 02 '23 at 11:32
  • @SamVarshavchik Sorry for not adding the code before – Nullptr Apr 02 '23 at 12:55
  • It's true that there was no [mre] before. Unfortunately the shown code still fails to meet the requirements for a [mre]. And I suggested that `errno` will provide more information, did you overlook that part of what I wrote? – Sam Varshavchik Apr 02 '23 at 17:56
  • 1
    This code is not handling non-blocking reads correctly. If `recv()` returns `< 0` and `errno` reports `EAGAIN`/`EWOULDBLOCK`, that is NOT a disconnect! You have to wait for the socket to be readable (ie, as reported by `epoll()`) and then call `recv()` again. But worse, even if you were reading correctly, this code is still not even remotely close to being a viable HTTP server, let alone one that can handle persistent connections. You have to **parse** the HTTP data (which this code is not doing) to determine the request end, and whether the client even *wants* a persistent connection. – Remy Lebeau Apr 02 '23 at 18:37
  • @SamVarshavchik Sorry, I'm a newbie and I can't give a fully reproducible level (because the code is just too long and badly written) ......... Thank you and RemyLebeau for your help, I think I found the problem As you and RemyLebeau said, I wasn't handling errno correctly I was incorrectly handling both "EAGAIN/EWOULDBLOCK" and "ECONNRESET/EPIPE" as "ECONNRESET/EPIPE" This action causes "the client has not sent data / the client's data has not yet reached the buffer" and I unilaterally think "the client does not have a new request" and disconnect – Nullptr Apr 03 '23 at 06:38
  • @RemyLebeau Sorry, I'm a newbie and I can't give a fully reproducible level (because the code is just too long and badly written) , Thanks for your help, the problem should be that I didn't handle ``errno`` correctly The ``http_header`` in the above code is a class that parses http data (into a ``std::map`` and generates a response string) Then the ``write_client`` function will be used to send the string to the client (the actual code written is even worse) I don't have a lot of development experience, should I use this idea for http server development? – Nullptr Apr 03 '23 at 06:59
  • `EAGAIN/EWOULDBLOCK` indicates a non-blocking socket, and non-blocking socket semantics must be correctly implemented in order to use them. `ECONRESET/EPIPE` is normal with persistent connections, and indicates that the peer decides to shut down the socket connection, and this must be correctly handled, too. – Sam Varshavchik Apr 03 '23 at 11:37
  • @Nullptr even if you have a working parser, and you were handling non-blocking reads correctly, your code would still be wrong. You are reading arbitrary bytes until you read less than 1024 bytes, and that is simply not how TCP or HTTP work. You need to read until a `\r\n\r\n` sequence is received indicating the end of headers, then you need to parse the headers to determine the body's transfer format, and then you need to read the body accordingly until its logical end. Have you even read the HTTP protocol specs yet? – Remy Lebeau Apr 03 '23 at 14:41
  • @SamVarshavchik `ECONRESET`/`EPIPE` does not I dictate a peer-initiated disconnect. That is indicated by `recv()`/`read()` returning 0 bytes on a socket that is in a readable state. Anything else is a connection error. – Remy Lebeau Apr 03 '23 at 14:43
  • @RemyLebeau I've read the HTTP protocol specification, but I'm not sure if I'm handling it correctly The way I think of it is this, the client sends only one request in a tcp connection, then waits for a response, the loop here is to read the complete request, each time it is read and appended to ``std::string``, when the number of characters read is less than 1024, it means there is no data in the buffer. then respond to the request, the response format also conforms to the HTTP protocol specification (the code works well now, but not very pretty) – Nullptr Apr 03 '23 at 16:16
  • @RemyLebeau Can you recommend an http server project for newbies? – Nullptr Apr 03 '23 at 16:21
  • 1
    @Nullptr "*I'm not sure if I'm handling it [the HTTP protocol] correctly*" - no, you are not. See my [previous answers](https://stackoverflow.com/search?q=user%3A65863+http+pseudo) related to that topic. "*when the number of characters read is less than 1024, it means there is no data in the buffer*" - that is the completely wrong way to handle the reading of HTTP messages. "*Can you recommend an http server project for newbies?*" - no, and asking for recommendations is [off-topic](https://stackoverflow.com/help/on-topic) for StackOverflow anyway. – Remy Lebeau Apr 03 '23 at 17:46

0 Answers0