By various reasons I would like to implement timeout on reading and writing to socket in a server but fail to get it running and therefore kindly ask for some insight into wherein the problem may reside.
In order to set the timeout on the read and write to the socket I'm trying to use of the functions setsocketopt()
and getsocketopt()
. However I must be doing something wrong as the return value indicates that a problem have occurred and perror outputs "Invalid argument". Strangely enough the error does not always occur at the first usage of setsocketopt()
and getsocketopt()
, which puzzles me bit.
The following server and client codes reproduces my problems (compiled using gcc)
server code:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main() {
struct sockaddr_in saddr,caddr;
socklen_t clen;
int sock, csock, reuse = 1, ret=0;
socklen_t ntrcv, ntsnd;
struct timeval tout, tsnd, trcv;
// create a new stream socket
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) perror("failed to create socket");
else {
// enable the socket to reuse the address
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) perror("failed allowing server socket to reuse address");
else {
// set up the server address
memset((char *) &saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
saddr.sin_port = htons(45454);
// bind socket to address
if (bind(sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) perror("failed to bind");
else {
// listen to the socket for connections
if (listen(sock,5) < 0) perror("failed to listen");
else {
clen = sizeof(caddr);
if ((csock = accept(sock, (struct sockaddr *) &caddr, &clen)) < 0) perror("failed to accept");
else {
tout.tv_sec=0;
tout.tv_usec=10000;
// check value of errno prior to setting timeout
perror("errno prior to timeout");
if (getsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &trcv, &ntrcv) < 0) perror("2");
else if (getsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tsnd, &ntsnd) < 0) perror("3");
else if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tout, sizeof(tout)) < 0) perror("4");
else if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tout, sizeof(tout)) < 0) perror("5");
else {
printf ("all ok so far in server\n");
sleep(1);
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &trcv, ntrcv) < 0) perror("6");
else if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tsnd, ntsnd) < 0) perror("7");
}
if (close(csock) < 0) perror("failed to close csock");
}
}
}
}
}
if (close(sock) < 0) perror("failed to close sock");
return ret;
}
client code:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main() {
struct sockaddr_in addr;
struct hostent *server;
int sock = 0;
// resolve server name
if (!(server = gethostbyname("127.0.0.1"))) perror("failed to resolve host");
else {
// prepare the server address
memset((char *) &addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
bcopy((char *)server->h_addr, (char *)&addr.sin_addr.s_addr, server->h_length);
addr.sin_port = htons(45454);
// create a socket and connect to the server
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) perror("failed to create client socket");
else {
if (connect(sock,(struct sockaddr *) &addr,sizeof(addr)) < 0) perror("failed to connect to server");
else {
printf("Connection is established will sleep now\n");
sleep(3);
printf("done sleeping\n");
close(sock);
printf("socket is closed\n");
}
}
}
return 0;
}
In the present example the second call to getsockopts()
in the server fails resulting in perror("3")
to be called. If I however comment this line out as well as the last call to setsockopts()
the first call to getsockopts()
fails (which previously seemed to work).
Any insights to where I go wrong are appreciated
You are not initialising ntsnd
and ntrcv
to the size of the buffer available to getsockopt
. Therefore if the random value there greater or equal to the size needed, the call will succeed. Else it will fail with EINVAL
. From the man page:
The arguments
optval
andoptlen
are used to access option values forsetsockopt()
. Forgetsockopt()
they identify a buffer in which the value for the requested option(s) are to be returned. Forgetsockopt()
,optlen
is a value-result argument, initially containing the size of the buffer pointed to byoptval
, and modified on return to indicate the actual size of the value returned. If no option value is to be supplied or returned,optval
may beNULL
.
To fix this intialize them both to sizeof(struct timeval)
.