TCP : recv() getting ECONNRESET - c

I'm trying to implement a simple client/server program on linux using TCP
and the standard socket.h library. server handling multiple clients and each client can close() or shutdown() the socket any time he wants.
on the server side (using a non blocking read):
int nBytes;
if ((nBytes = recv(socket, buffer, BUFFER_SIZE, MSG_DONTWAIT)) == -1)
{
if (errno != EAGAIN && errno != EWOULDBLOCK)
{
//print to log
}
}
if (nBytes == 0)
{
//other side closed the connection
}
I'm getting recv() returning -1 and error set to ECONNRESET. If the client has closed the connection shouldn't recv() return 0?

There are numerous causes, including but not limited to:
the peer deliberately reset the connection
the peer closed the connection while he still had unread data pending
you had sent data to a connection that had already been closed by the peer
you had pending write data and the TCP retries have timed out
TCP keepalive detected a lost connection.
This is a fatal error, and you should close the socket when you get it.
each client can close() or shutdown() the socket any time he wants.
No he can't. See above.

If the remote peer has cleanly closed the connection, and there are no more bytes waiting to be read on the local end, then yes, recv() should return 0. So if you get an ECONNRESET then it's reasonable to assume that an orderly closure is not what has happened.
An ECONNRESET often indicates that the remote peer sent a RST packet without having first closed the connection cleanly. There are many reasons why this might happen. Alternatively, as EJP observed, a reset may originate locally, too.
In any event, after an ECONNRESET it's not reasonable to suppose that you will be able to read anything further from the socket, so in your particular case you should probably handle it much the same as way as you handle recv() returning 0, +/- logging.

You may get ECONNRESET if client closed his side without reading sent data. If he closed connection properly you'll get 0.

Related

How to check if a TCP connection was closed by peer gracefully or not WITHOUT READING FROM IT? [duplicate]

This question already has answers here:
c++ how to use select to see if a socket has closed
(4 answers)
Closed 6 years ago.
I'm using Linux.
Can someone provide me an example of how to use select() or poll() to check whether a TCP connection was closed by peer through FIN or terminated by RST?
poll() can detect RST with setting POLLHUP or POLLERR in revents, but select() has no facilities like this.
In my test, when socket is terminated by RST, socket error got by getsockopt will be ECONNRESET. Is this a effective way or not?
Is it necessary to determine how the connection was closed?
Thanks!
If the socket was closed gracefully, the socket will be set for read.
Then, when you read, you will receive 0 data read because you got to EOF. You can also check with
ioctl(sock, FIONREAD, &n);
to determine if there is data to read. Here you have an example: c++ how to use select to see if a socket has closed
According to select man: "readfds will be watched to see if characters become available for reading (more precisely, to see if a read will not block; in particular, a file descriptor is also ready on end-of-file)"
If there is a pending socket error, the corresponding bit will be set in readfds or writefds. Then read or recv should return error. Also you can use this to determine if there is a pending error in the socket:
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &result, &resultLen) < 0) {
//getsockopt ERROR
} else if (result == 0) {
//NO ERROR
} else {
//SOCKET ERROR
}

Detect closed connection on non blocking socket

I'm really sorry if my question is a duplicate, but I didn't find useful infos in the site.
I'm using non blocking sockets and a select(). How can I detect if a client closed the connection on non-blocking socket? I saw that a read() returns -1 with errno = EWOULDBLOCK when no datas are available to be read and also when a connection is closed.
How can I discriminate above cases?
When the peer has closed the connection:
select() will return the socket as readable.
A recv() or read() on the socket will return zero.
I saw that a read() returns -1 with errno = EWOULDBLOCK when no datas are available to be read
Correct, but the connection isn't closed.
and also when a connection is closed.
No you didn't. That's not correct. It returns zero.
How can I discriminate above cases?
They aren't the same, and they don't manifest themselves in the same way.
When the peer had closed the connection for a specific socket, a call to read() on this socket would return 0. This behaviour is independent from the socket's blocking state.
From man 2 read (italics by me):
RETURN VALUE
On success, the number of bytes read is returned (zero indicates end of file)

Does socket error mean socket is closed

I am writing a client-server program. The server is select()ing on readfd1 waiting for the readiness of readfd1 to be read. If it is ready, server is collecting the data and printing. Everything is fine for a while, but after some time, socket recv() failed with errno set to ETIMEDOUT. Now I want to rewrite my program to thwart these error condtions. So I went through "Unix Network Programming" by Richard Stevens, which states 4 conditions for select() to unblock. Following are the 2 conditions that grab my attention
A. client sent FIN, here return value of `recv()` will be `0`
B. some socket error, here return value of `recv()` will be `-1`.
My question is, Does socket error closes the connection? If that is so, then why is above two conditions are separated. If not, does next recv() on socket work?
If recv() returns 0, the other end has actively and gracefully closed the connection.
If recv() returns -1, there has (possibly) been an error on the connection, and it is no longer usable.
This means you can tell the difference between the peer closing the connection, and an error happening on the connection. The common thing to do in both cases is to close() your end of the socket.
There is 2 more points to consider though:
In the case of recv() returning -1, you should inspect errno, as it might not indicate a real error.
errno can be EAGAIN/EWOULDBLOCK if you have placed the socket in non-blocking mode or it could be EINTR if the system call was interrupted by a signal. All other errno values means the connection is broken, and you should close it.
TCP can operate in half duplex. If the peer has closed only its writing end of the connection, recv() returns 0 at your end. The common thing to do is to consider the connection as finished, and close your end of the connection too, but you can continue to write to it and the other end can continue to read from it. Whether to close just the reading or writing end of a TCP connection is controlled by the shutdown() function.
A socket error doesn't have to mean that the connection is closed, consider for example what happens if somehow the network cable between you and your peer is cut then you would typically get that ETIMEDOUT error. Most errors are unrecoverable, so closing your end of the connection on error is almost always advisable.
The difference between the two states when select can unblock, is because either the other end closed their connection in a nice way (the first case), or that there are some actual error (the second case).

epoll recv return value

I'm using epoll as level triggered. According to recv(3), if recv returns 0 "no messages are available to be received and the peer has performed an orderly shutdown". Does this mean that the whole request has been received and the socket has been closed on the other side? That the socket has been closed on both ends? Or that simply the whole request has been received and the socket is awaiting a response to be written to it? Any help would be greatly appreciated.
According to What value will recv() return, it sounds like if recv returns 0, the connection is closed on the other side. If this is the case, what needs to be done on the epoll side? Does the socket need to be completely removed with a call to epoll_ctl with EPOLL_CTL_DEL and a call to close?
It means that you have already read all the data that has been sent overthe connection, and the only thing left is a TCP FIN, which could be the result of either a full close by the remote or a shutdown for output by the remote. You can't do much but close the socket at this point, unless you know the peer has only shutdown, in which case you can still write to the socket. Your application protocol determines all that.
There is no such thing as a request or response in TCP. There is just a bidirectional byte-stream.

select says socket is ready to read when it is definitely not (actually is closed already)

In my server I check if any socket is ready to read using select() to determine it. As a result in main loop select() is executed every time it iterates.
To test the server I wrote a simple client that sends only one message and then quits. BTW. I use protocol buffers to send information - message means an object of type class Message in this library.
The test session looks like:
select()
server's socket ready to read
accept() client's socket
read message from client's socket
select()
server's socket not ready to read, client's one ready
read message from client's socket
The last step is wrong because client has already closed connection. As a result protobuf library gets Segmentation fault. I wonder why FD_ISSET says the socket is ready in step 6 when it is closed. How can I check if a socket is closed?
EDIT:
I've found how to check if the socket is open
int error = 0;
socklen_t len = sizeof (error);
int retval = getsockopt (socket_fd, SOL_SOCKET, SO_ERROR, &error, &len );
the socket is "readable" if the remote peer closes it, you need to call recv and handle both the case where it returns an error, and the case where it returns 0, which indicates that the peer shut down the connection in an orderly fashion.
Reading the SO_ERROR sockopt is not the correct way, as it returns the current pending error (from, eg. a non-blocking connect)
The socket used for communication between a client and your server will be flagged as readable (i.e. select() will return) when there is data to read, or when there's an EOF to read (i.e. the peer closed the connection).
Just read() when select() returns and your fd is flagged. If read() returns a positive number, you got data. If it returns 0, you got EOF. If it returns -1, you have a problem (unless errno is EAGAIN).

Resources