Blocking socket receive timeout

Hello everybody!
I’m trying to make a simple blocking socket connection between only 2 computers running QNX 6.0.
My problem is that the SO_RCVTIMEO receive timeout option that I need on the server does not work.

If the client sends messages to the server, they are received on the server properly.
Unfortunately, if the client no longer sends messages, the server remains blocked in read even it must exit by timeout after 5 seconds!

Maybe SO_RCVTIMEO is not working in QNX 6.0?
Any help will be highly appreciated.

Best regards,
Theodor

My C code is:

#include <stdio.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdbool.h>
#include <sys/neutrino.h>
#include <sys/time.h>

#define MAX 80
#define PORT 42222
#define SA struct sockaddr

int sockfd;

// Function designed for chat between client and server.
void func(int connfd)
{
char buff[MAX];
int n;
int contor_read;

// infinite loop for chat
for (;;) {
    bzero(buff, MAX);

    // read the message from client
    contor_read = read(connfd, buff, sizeof(buff));
    if ( contor_read < 0 )
    {
        printf("Read timeout!\n");
    }
    // print buffer which contains the client contents
    printf("From client: %s \n", buff);
    }
}

}

int main()
{
int sockfd, connfd, len;
struct sockaddr_in servaddr, cli;

int rcode;
struct timeval tv;

// socket create and verification
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
    printf("socket creation failed...\n");
    exit(0);
}
else
    printf("Socket successfully created..\n");
bzero(&servaddr, sizeof(servaddr));

// assign IP, PORT
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(PORT);

  tv.tv_sec = 5;
  tv.tv_usec = 0;
 setsockopt(sockfd, SOL_SOCKET,  , &tv, sizeof(tv));


// Binding newly created socket to given IP and verification
if ((bind(sockfd, (SA*)&servaddr, sizeof(servaddr))) != 0) {
    printf("socket bind failed...\n");
    exit(0);
}
else
    printf("Socket successfully binded..\n");

// Now server is ready to listen and verification
if ((listen(sockfd, 5)) != 0) {
    printf("Listen failed...\n");
    exit(0);
}
else
    printf("Server listening..\n");
len = sizeof(cli);

// Accept the data packet from client and verification
connfd = accept(sockfd, (SA*)&cli, &len);
if (connfd < 0) {
    printf("server accept failed...\n");
    exit(0);
}
else
    printf("server accept the client...\n");

// Function for chatting between client and server
func(connfd);

// After chatting close the socket
close(sockfd);

}

You should take a look at the read() documentation here

https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/r/read.html

Specifically this:

When attempting to read from a file (other than a pipe or FIFO) that supports nonblocking reads and has no data currently available:

If O_NONBLOCK is set, read() returns -1, and errno is set to EAGAIN.
If O_NONBLOCK is clear, read() blocks until some data is available.

You are blocking forever since you don’t have O_NONBLOCK set.

Just be aware that if you change to O_NONBLOCK, you will return immediately with -1 over and over again while waiting for data (ie you will consume a lot of CPU in a hard loop) . If you want to block with a timeout, use the select() call with a timeout.

Tim

Hello Tim,

My TCP socket is a blocking socket and not a non blocking socket.
So, the O_NONBLOCK is and must remain clear.

In QNX documentation is written that is possible to set options for socket.
Using setsockopt() as in documentation:
https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/g/getsockopt.html#getsockopt__SO_RCVTIMEO
is possible to use SO_RCVTIMEO for receive timeout blocking:

SO_RCVTIMEO

level: SOL_SOCKET

Gets or sets a timeout value for input operations. It accepts a struct timeval parameter (defined in <sys/time.h>) with the number of seconds and microseconds used to limit waits for input operations to complete.

In the current implementation, this timer is restarted each time additional data is received by the protocol, so the limit is in effect an inactivity timer. If a receive operation has been blocked for this much time without receiving additional data, it returns with a short count or, if no data was received, with the error EWOULDBLOCK.

In Linux is working perfectly. Why not in QNX 6.0 ?

It’s working exactly as the documentation says it’s supposed to work.

The problem isn’t the SO_RCVTIMEO option or sockets. The problem is that QNX read() is different than Linux read().

https://www.qnx.com/developers/docs/7.1/index.html#com.qnx.doc.neutrino.lib_ref/topic/r/read.html
https://man7.org/linux/man-pages/man2/read.2.html

The read() function itself is blocking, not the socket or the timeout. Linux read() handles sockets differently than QNX does.

If you don’t want to use the select() that I suggested above then use the actual function recv() that is designed for reading from sockets. That call will respect your socket timeout settings.

Tim

Tim has done a great job (as always) responding to the details here. I’d just like to add a small note. It is almost impossible to believe that you are running QNX 6.0. This version was handed out at the first delivery of QNX 6 on a CD or DVD. It was fairly quickly superceeded by versions 6.1, 6.2, 6.3 and then 6.32 which was fairly stable. I did do some legacy work for a customer once on 6.21 but I never saw anything deployed on anything with a lower version.

Maybe by 6.0 you mean 6, the generic term for everything up to 6.6.

Thanks Tim,

Do you have a link to an QNX example for using select()?
Also, for my information, which is the use of the SO_RCVTIMEO option if it cannot be used for the purpose described?
** If a receive operation has been blocked for this much time without receiving additional data, it returns with a short count or, if no data was received, with the error EWOULDBLOCK.**

I’ve used this guys site for probably 15 years (he’s refined it a lot in that time period).

https://beej.us/guide/bgnet/html/#select

His first sample shows waiting for 2.5 seconds for something from standard input. That’s how you set up a timer to timeout (the 5th argument to select).

His second sample shows a chat server sample which is what you are trying to do (his has multiple clients and handles connect/disconnect while yours only has 1 client). Scroll down to that sample and look in his //main loop code.

You want a timeout in your server, so make sure you put one in the 5th argument as he did in his 1st sample. If the timeout happens, the return from select() will be 0, so you check for that case specifically.

Tim

P.S. The SO_RCVTIMEO option is meant to be used when you are calling recv() and recvfrom(). Those are the socket library calls that read data so they respect the SO_RCVTIMEO option. The general read() call is meant to read from all kinds of sources (sockets, files, serial ports, resource managers etc) so it doesn’t handle specific settings for specific sources. If the select() example above is too complex for your needs, just use recv() instead of read() since it will do the timeout.