0

I'm writing my own web server in C. And I'm kind of stuck with an annoying problem. I'm waiting for incoming connections like this:

struct sockaddr_in caddr;
uint32_t caddr_len = sizeof(caddr);
int fd = accept(sfd, (struct sockaddr *)&caddr, &caddr_len);
if(fd < 0) {
  err(EXIT_FAILURE, "accept()");
}

And when accept() succeeded, I'm starting to receive the data with:

errno = 0;
ssize_t r = recv(fd, buf, sizeof(buf), 0);

Sometimes it happens that I don't receive any data, when accessing with firefox. When I set the timeout to 1s, errno is set to EAGAIN. And when I set the timeout to 5s, errno will not be set but I still not receiving any data r == 0.

Is it possible to configure the socket so that accept() only returns when there is actual data available?

Note: I do not experience this behavior when accessing with Chrome.

EDIT: Some suggested that I should use poll()

When I use poll(), I have the same problem:

struct pollfd p[] = {{sfd, POLLIN}};
int r = poll(sfd, 1, 1000);
if(r <= 0) err("poll() -> %d", r);

r == 1, but I have still the same problem, because this poll() applies only to the listening socket. It doesn't tell me if there is actual payload when accepting.

Cosinus
  • 534
  • 4
  • 10
  • I've found the solution. `poll()` can observe multiple `fd`s at once. So I use the first entry for the listening fd (`sfd`). If there is some change i call `fd = accept(sfd)` and put the new `fd` into the same watch list. When one of the newly created `fd`s has a change, I know that this fd has data. – Cosinus Feb 15 '21 at 15:28
  • It's not clear how do you _set the timeout to...._ and how do you know that a timeout has occured. This is the reason we ask for [Minimal, complete and verifiable](https://stackoverflow.com/help/minimal-reproducible-example) examples. Please, see the page referenced before, and edit your question with such a complete and verifiable example. – Luis Colorado Feb 16 '21 at 18:57

2 Answers2

1

Accept returns when the connection is accepted. If you want to wait until data is available to read then you need to use poll or select (or blocking read).

Tom V
  • 4,827
  • 2
  • 5
  • 22
  • When I use poll() I have the same problem because, all that it tells me is, that there is a client that want's to connect. What I need is: (Want's to connect AND Has payload) – Cosinus Feb 15 '21 at 11:50
  • @Cosinus add that newly connected `fd` to `poll`/`select` argument and wait again. – alagner Feb 15 '21 at 12:26
  • @alagner That's what I'm trying to avoid, because firefox is blocking my runnier until the timeout occurs. In the mean time this runner could have served several requests from other clients. – Cosinus Feb 15 '21 at 12:32
  • @Cosinus no clue how exactly you are handling multiple clients, but bottom line is: connecting and sending are two different operations. You can `select`/`poll`/`put-your-async-call-here` on all connected clients and handle only those who are currently sending. Another option would be to use multithreaded/multprocessed server. But all that really depends on what you are exactly doing. – alagner Feb 15 '21 at 12:41
  • @Cosinus: What you want is not possible because whether a client wants to connect is a property of the listening socket, whereas whether a payload is available to read is a property of an already connected socket. The connected socket doesn't exist until you have called accept and it has returned. To do what you are asking would require adding another system call to the kernel to be able to query whether there is data available on an unaccepted connection that does not have a socket yet. – Tom V Feb 15 '21 at 13:19
  • @Cosinus one more idea: you can set the socket as non-blocking once it's connected. Trying to read it should return immediately with the appropriate error code if there is no data to be read. – alagner Feb 16 '21 at 07:00
1

You can use select, poll or similar. See: https://stackoverflow.com/a/12862015/4885321

alagner
  • 3,448
  • 1
  • 13
  • 25