0

I have a server based on BSD sockets, with select to detect events. It keeps closing connections when clients still have data to send (the clients throw Connection reset by peer).

This is my code on server side:

if (FD_ISSET(sd,&readfds)){//we have an event from select               
   int count=0;
   ioctl(sd,FIONREAD,&count);//should return available data 
   if(count==0){    
    u_int8_t c;
    count=recv(sd, &c, 1, MSG_PEEK);//this should just check if data available without reading
    if(count==0)close(sd);
        }
   else if(count>expected_message_size)//do something
   else //do nothing until enough data arrives
   }
            

Google says that if recv returns 0, it means client closed socket, but it does not seem to work. As far as I know, it should not work, because recv/read returns the amount of data read, but we are dealing with internet communication here - what if there is no data right now, because we've read everything the client has sent so far, but there is more to come?

man recv actually says:

Datagram sockets in various domains (e.g., the UNIX and Internet domains) permit zero-length datagrams. When such a datagram is received, the return value is 0.

More searching led me to the MSG_PEEK method, but that did not help. What am I doing wrong?

I am on Linux, and do not need to support any other OS.

  • the code is strange ... why call recv if you know count is zero ? it doesn't make sense ... also, if FD_ISSET is true you could just call recv and accumulate whatever the client has sent, no need to mess around with ioctl – zentrunix May 09 '23 at 14:00

3 Answers3

1

If no data are available recv either blocks (blocking socket) or returns -1 to signal an error. If the error is EAGAIN (also known as EWOULDBLOCK) this means that currently no data are available.

If recv returns 0 on a TCP socket it means that the peer has closed the socket. With datagram sockets (UDP) this is different since there is no concept of a connection and thus not of a connection establishment and closing either. Here a return 0 might actually indicate that a datagram with size 0 was received.

Steffen Ullrich
  • 114,247
  • 10
  • 131
  • 172
  • I am using TCP sockets, so this just confirms what I read elsewhere - that return value of 0 should mean peer has closed the socket. Yet, it just does not work. – Damir Gruncl May 09 '23 at 13:44
  • 1
    @DamirGruncl: *" it just does not work"* - please provide a more complete example to demonstrate your problem. See [How to create a Minimal, Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example) and make sure to provide everything so that your problem is actually reproducible by others. – Steffen Ullrich May 09 '23 at 13:57
0

if you are using TCP sockets, you should just be able to recv and if that returns 0, then you close the connection. using both ioctl and recv with MSG_PEEK seems unnecessary. You can do something like this:

int count = recv(sd, buf, MAX_SIZE, 0);

if (count == 0)
{
    // the client has closed the connection
    close(sd);
}
else if (count == -1)
{
    // there was an error receiving data from the client
}
else 
{
    // do stuff with buf
}

if you have set O_NONBLOCK on sd and you want to check if recv would have blocked, you can #include <errno.h> and check for EWOULDBLOCK and EAGAIN:

...
if (count == -1)
{
    if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
    {
         // recv would have blocked
    }
    else 
    {
         // a different error occurred 
    }
}
...
boreddad420
  • 187
  • 1
  • 7
0

So I figured it out - I had a listen function on a socket to accept incoming connections, and the backlog parameter was 3. Increasing it to 100 fixed the problem. I have no idea why that produced Connection reset by peer rather than Connection refused, but that's what happened.

  • Well, if it is a linux box and the sysctl tcp_abort_on_overflow is true and the accept queue is full (3 connections waiting to be accepted) then it will reply with a reset. – JimD. May 12 '23 at 10:00