0

I have a simple single process server in C. It is bound to and listens on port k, and is intended to accept only one client at a time. For that, i set the listen() backlog to 0, but i still find clients waiting in queue, and they get accept()-ed when the client started before it terminates. Why?

Secondly, i start up clients one after another as c1, c2, c3, c4; and terminate c2 before terminating c1. Client c1 can still chat with the server. Interestingly, i find that when i terminate c1, the server stops as well. (Consequently, clients c3 and c4 report a read() error.) Any idea why does the server stop?

It works fine when i terminate clients in order. If all clients are terminated in order, the server will wait to accept() a new client. Does the server "remember" the order of the clients? (Although the listen() backlog is 0? Even so, why does the server stop?)

Thirdly, i tried both cases again with the listen() backlog set to INT_MAX. The same behaviour is seen.

Here's the gist of my code:

s = socket(…);
bind(…);
listen(s, 0);
while(to_serve_or_not_to_serve) {
   client = accept(s, …);
   if(client<0){
      perror("Client not accepted");
      continue;
   }       
   ​
   serve_this_client:
   r = read(client, buffer, …);
   if(r<0) {
      write(client, "You're dead!~\n", 14);
      close(client);
      continue;
   }
   // process some data
   w = write(client, data, …);
   if(w<0) {
      write(client, "Your dead ペン!~\n", 15);
      close(client);
      continue;
   }
   goto serve_this_client;
}
close(s);

[Let me know if you need more details.]

garyF
  • 521
  • 3
  • 15

2 Answers2

1

For that, i set the listen() backlog to 0, but i still find clients waiting in queue, and they get accept()-ed when the client started before it terminates. Why?

Because the backlog argument will not be set to 0 by the kernel. The argument is only advisory and the system might use a different value. For more details see listen() ignores the backlog argument?

i start up clients one after another as c1, c2, c3, c4; and terminate c2 before terminating c1. Client c1 can still chat with the server. Interestingly, i find that when i terminate c1, the server stops as well.

After you've terminated c1 the next accept will fail, because this was the new connection to c2 was already closed. But you don't handle failures for accept and instead continue to work with an invalid fd.

Community
  • 1
  • 1
Steffen Ullrich
  • 114,247
  • 10
  • 131
  • 172
  • nice, i now get the backlog part being advisory. But can you explain more on handling errors for `accept()`? What i think i understood so far is that as `c2` was in the queue, accept pulls that and assigns it a socket file descriptor. So i need to check on my `client` variable for any error produced by `accept()`? – garyF Aug 13 '15 at 20:57
  • 1
    @garyF: from the [documentation](http://linux.die.net/man/2/accept): "..On error, -1 is returned, and errno is set appropriately." – Steffen Ullrich Aug 13 '15 at 21:02
  • forgot to mention that i was already checking for an error in `accept()`. the server just stopped because of the [`write("You're dead")` operation on the non-existing socket](http://stackoverflow.com/questions/2216374/why-is-writing-a-closed-tcp-socket-worse-than-reading-one). managed to nail the culprit with SIGPIPE signal. – garyF Aug 13 '15 at 21:38
-1

The server stops without warning/error when there is a SIGPIPE interrupt. This happened as i was writing to the socket closed by the client:

if(r<0) {
   write(client, "You're dead!~\n", 14);
   …
}

Ref: Why is writing a closed TCP socket worse than reading one?

It doesn't happen all the time though. Noticed it happening only when c2 has written something from its end, and closed prematurely (i.e. other clients are waiting in queue after it). If c2 hasn't written anything, the write() operation on the closed socket (by server) does not throw the interrupt.

Interestingly, if the all clients have written something (from their end) and are stopped in order, writing to the closed socket (by server) does not throw this interrupt.

[Using Linux 3.13.0-61-generic kernel.]

Community
  • 1
  • 1
garyF
  • 521
  • 3
  • 15