socket keep alive not working on Linux as expected - linux

I am using this code to setup a socket to detect a network error:
int socket_keepalive(int s, int ktime, int kinterval, int kprobes) {
int enable = 1;
if(setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(int)) < 0) {
printf("setsockopt SO_KEEPALIVE failed (%s)\n", strerror(errno));
return -1;
}
if(setsockopt(s, IPPROTO_TCP, TCP_KEEPIDLE, &ktime, sizeof(int) < 0)) {
printf("setsockopt TCP_KEEPIDLE failed\n");
perror("SO_KEEPALIVE: ");
return -1;
}
if(setsockopt(s, IPPROTO_TCP, TCP_KEEPINTVL, &kinterval, sizeof(int) < 0)) {
printf("setsockopt TCP_KEEPINTVL failed\n");
perror("TCP_KEEPINTVL: ");
return -1;
}
if(setsockopt(s, IPPROTO_TCP, TCP_KEEPCNT, &kprobes, sizeof(int) < 0)) {
printf("setsockopt TCP_KEEPCNT failed\n");
perror("TCP_KEEPCNT: ");
return -1;
}
return 0;
}
I am calling the above function like this:
socket_keepalive(sock, 30, 10, 1);
I expect the socket to be disconnected after 40 seconds after a network error. However, it takes almost 170 seconds after a network error on the remote side.
Any idea what I am doing wrong?

Related

Linux Socket timeout works on WSL, but not on Ubuntu

I try to run a TCP client without a server. The idea is simply to periodically try to connect.
For this, the client tries to connect to port 1500 on localhost.
Piece of code:
// Create socket
if ((create_socket=socket (AF_INET, SOCK_STREAM, PF_UNSPEC)) > 0)
printf ("Socket created\n");
address.sin_family = AF_INET;
address.sin_port = htons (1500);
inet_aton (argv[1], &address.sin_addr);
// Connect to server
connect ( create_socket,
(struct sockaddr *) &address,
sizeof (address));
FD_ZERO(&fdset);
FD_SET(create_socket, &fdset);
tv.tv_sec = 2; /* 2 seconds timeout */
tv.tv_usec = 0;
rv = select(create_socket + 1, NULL, &fdset, NULL, &tv);
if (rv == 1)
{
int so_error;
socklen_t len = sizeof so_error;
getsockopt(create_socket, SOL_SOCKET, SO_ERROR, &so_error, &len);
if (so_error == 0)
{
printf ("Connection with server (%s) established \n",
inet_ntoa (address.sin_addr));
}
else
{
printf("Error on connect: unsuccessfull\n");
close (create_socket);
continue;
}
}
else if (rv == 0)
{
printf("Timeout on connect\n");
close (create_socket);
continue;
}
else
{
printf("Error on connect\n");
close (create_socket);
continue;
}
I've set it up in Ubuntu 18.04 on WSL. There, the code waits on select for the defined timeout of 2 seconds and returns appropriate return values. (0 on timeout, 1 on connect).
The return value of connect is -1 on WSL and VMware.
In Ubuntu 18 (VMware) there is no pause in that line. In any case, even without any server listening on that port, I get immediately a return value of 1.
Why is there this difference?
There is a similar behavior later on in that code:
tv.tv_sec = 2;
tv.tv_usec = 0;
if (setsockopt(create_socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv) < 0)
{
printf("Error on setsockopt SO_RCVTIMEO");
exit(EXIT_FAILURE);
}
// INNER LOOP: Receive data
do
{
size = recv(create_socket, buffer, BUF-1, 0);
if( size > 0)
{
buffer[size] = '\0';
printf ("Message received: %s\n", buffer);
}
else if (size == -1)
{
// on VMware, errno is 107 if there is no server, but coming to that line was not intended
printf ("Timeout\n");
}
else //
{
printf("Server offline\n");
// GO BACK TO OUTER LOOP and reconnect
break;
}
Here, in WSL the recv takes up to 2 seconds, while waiting for any incoming data. (But only if the aforementioned block (connect, select) indicates a valid connection)
In VMware I directly get the feedback. (even without connection)
Does it simply work on WSL by chance?
The argument contains the server IP and is 127.0.0.1.
lsof shows no connection.
Update 2020-11-18
Here's the full code as requested by Bodo
#include <iostream>
#include <vector>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#define BUF 1024
using namespace std;
int main (int argc, char **argv) {
int create_socket;
char *buffer = (char*)malloc(BUF);
struct sockaddr_in address;
int size;
int rv;
struct timeval tv;
fd_set fdset;
// HERE STARTS THE OUTER LOOP - Connect and restart connection
do
{
// Create socket
if ((create_socket=socket (AF_INET, SOCK_STREAM, PF_UNSPEC)) > 0)
printf ("Socket created\n");
address.sin_family = AF_INET;
address.sin_port = htons (15000);
inet_aton ("127.0.0.1", &address.sin_addr);
// Connect to server
int flags = fcntl(create_socket, F_GETFL, 0);
if (flags == -1) return false;
rv = connect ( create_socket,
(struct sockaddr *) &address,
sizeof (address));
printf ("Connect. rv = %i\n", rv);
if (rv == -1)
{
switch (errno)
{
case ECONNREFUSED: printf ("errno = %i (ECONNREFUSED)\n", errno); break;
default: printf ("errno = %i (ECONNREFUSED)\n", errno); break;
}
}
FD_ZERO(&fdset);
FD_SET(create_socket, &fdset);
tv.tv_sec = 2;
tv.tv_usec = 0;
rv = select(create_socket + 1, NULL, &fdset, NULL, &tv);
if (rv == 1)
{
int so_error;
socklen_t len = sizeof so_error;
getsockopt(create_socket, SOL_SOCKET, SO_ERROR, &so_error, &len);
if (so_error == 0)
{
printf ("Connection with server (%s) established \n",
inet_ntoa (address.sin_addr));
}
else
{
printf("Error on connect: unsuccessfull\n");
close (create_socket);
continue;
}
}
else if (rv == 0)
{
printf("Timeout on connect\n");
close (create_socket);
continue;
}
else
{
printf("Error on connect\n");
close (create_socket);
continue;
}
if (setsockopt(create_socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv) < 0)
{
printf("Error on setsockopt SO_RCVTIMEO");
exit(EXIT_FAILURE);
}
// INNER LOOP: Receive data
do
{
size = recv(create_socket, buffer, BUF-1, 0);
if( size > 0)
{
buffer[size] = '\0';
printf ("Data received: %s\n", buffer);
}
else if (size == -1)
{
printf ("Timeout\n");
}
else //
{
printf("Server offline\n");
// GO BACK TO OUTER LOOP and reconnect
break;
}
} while (strcmp (buffer, "quit\n") != 0);
close (create_socket);
} while (strcmp (buffer, "quit\n") != 0);
return EXIT_SUCCESS;
}
In WSL the output is
Socket created
Connect. rv = -1
errno = 111 (ECONNREFUSED)
then nothing for 2 seconds
afterwards
Timeout on connect
Socket created
Connect. rv = -1
errno = 111 (ECONNREFUSED)
and again nothing for 2 seconds ...
Output in VMware
Socket created
Connect. rv = -1
errno = 111 (ECONNREFUSED)
Connection with server (127.0.0.1) established
Timeout
Timeout
Timeout
Timeout
Where no timeout is fulfilled.
The idea of timeout has been to try to connect on a regular basis, but not as fast as possible.
Obviously there is something wrong when errno = 111 (ECONNREFUSED) is followed by Connection with server (127.0.0.1) established.
When connect returns -1 and errno is NOT EINPROGRESS you should not use selectand getsockopt(...SO_ERROR...). According to https://man7.org/linux/man-pages/man2/connect.2.html, this is only documented for EINPROGRESS.
Both on real Linux and WSL you get errno = 111 (ECONNREFUSED) after a failed connect. I consider the timeout in WSL wrong as the error (conection refused) was already reported, so it does not make sense to wait for a result. But as the behavior is not specified, it may be implementation dependent.
If you want to have a delay before the next connection attempt, you should not use select but for example sleep followed by repeating the loop.
I suggest something like this:
rv = connect ( create_socket,
(struct sockaddr *) &address,
sizeof (address));
printf ("Connect. rv = %i\n", rv);
if (rv == -1)
{
switch (errno)
{
case ECONNREFUSED: printf ("errno = %i (ECONNREFUSED) %s\n", errno, strerror(errno)); break;
default: printf ("errno = %i (other) %s\n", errno, strerror(errno)); break;
}
if(errno != EINPROGRESS)
{
sleep(10); // chose a suitable delay before next connection attempt
continue;
}
}

Why is the Write system call in not writing anything to the sockets file?

I am writing a simple Server Client program that exchanges the data. After I write to the socket file, the write doesn't fail or it is not even partial write. but when I check the details of the socket file using ls -l , I still see it's size as zero and server doesn't recieve anything. Can anyone help me out with what I am doing wrong in here??
This is server.c
int main()
{
int socket_fd = 0;
struct sockaddr_un addr;
int result = -1;
char buffer[MAX_BUFFER_SIZE];
printf("Creating a socket\n");
socket_fd = socket(AF_UNIX,SOCK_STREAM,0);
if(socket_fd == -1)
{
perror("SOCKET");
return 0;
}
printf("Socket has been created %d\n",socket_fd);
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path,_SOCKET_PATH,sizeof(addr.sun_path)-1);
printf("PATH : %s\n",addr.sun_path);
if(remove(_SOCKET_PATH) == -1)
{
perror("REMOVE");
return 0;
}
printf("Binding the socket\n");
result = bind(socket_fd,(struct sockaddr *)&addr,sizeof(struct sockaddr_un));
if(result == -1)
{
perror("BIND");
return 0;
}
printf("Binding the socket is done\n");
printf("Listening to the socket\n");
if(listen(socket_fd,1) == -1)
{
perror("Listen");
return 0;
}
if((result = accept(socket_fd,NULL,NULL)) == -1)
{
perror("ACCEPT");
return 0;
}
printf("Connection Accepted\n");
while (1)
{
while (result = read(socket_fd,buffer,sizeof(buffer)-1) > 0)
{
printf("Client said : %s\n",buffer);
}
}
}
This is client.c
int main()
{
int socket_fd = 0;
struct sockaddr_un addr;
int result = -1;
char buffer[MAX_BUFFER_SIZE];
printf("Creating a socket\n");
socket_fd = socket(AF_UNIX,SOCK_STREAM,0);
if(socket_fd == -1)
{
perror("SOCKET");
return 0;
}
printf("Socket has been found %d\n",socket_fd);
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path,_SOCKET_PATH,sizeof(addr.sun_path)-1);
printf("Connecting to the socket\n");
result = connect(socket_fd,(struct sockaddr *)&addr,sizeof(struct sockaddr_un));
if(result == -1)
{
perror("CONNECT");
return 0;
}
printf("The client is connected to the server.\n");
while (1)
{
memset(buffer,0,sizeof(buffer));
scanf("%s",buffer);
printf("DATA WRITTEN %s,%d\n",buffer,strlen(buffer)+1);
result = write(socket_fd,buffer,strlen(buffer)+1);
printf("result = %d\n",result);
sleep(5);
}
}
Thanks for any help!
if((result = accept(socket_fd,NULL,NULL)) == -1)
...
while (result = read(socket_fd,buffer,sizeof(buffer)-1) > 0)
You are trying to read from the server socket (socket_fd). Instead you need to read from the new socket returned by accept, i.e. what you call result in result = accept.... To cite from man accept:
On success, these system calls return a nonnegative integer that is a
file descriptor for the accepted socket. On error, -1 is returned,
and errno is set appropriately.

when closing my tcp/ip socket connection thread is killing serial UART read thread in unix

I have two threads. One reads/writes serial UART and the other one reads/writes a server socket. When I close the client connected to the server its killing my other serial read/write thread. What could be the problem? See below for the code in question.
EDITED
The threads are created in the main function:
/* client socket to write/read and close on if data there*/
if( pthread_create(&t1 ,NULL,TCP_EventUploadHandler,NULL) < 0)
{
perror("could not create thread for TCP_EventUploadHandler");
return 1;
}
/*create thread for main server*/
if( pthread_create(&t2 ,NULL,command_handler,NULL) < 0)
{
perror("could not create thread for command handler");
return 1;
}
/*create thread serial read*/
if( pthread_create(&t3 ,NULL,ReadCard,NULL) < 0)
{
perror("could not create thread for Irish device");
return 1;
}
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
All 3 threads are continuously running. Whenever I close the other side of the TCP connection, my serial read is affected and stops reading and writing.
void *command_handler(void *)
{
int opt = TRUE;
int master_socket , addrlen , new_socket , client_socket[2] , max_clients = 2 , activity, i , valread , sd;
int max_sd;
struct sockaddr_in address;
unsigned char buffer[9000]; //data buffer of 9K
fd_set readfds;
port_address();
for (i = 0; i < max_clients; i++)
{
client_socket[i] = 0;
}
if( (master_socket = socket(AF_INET , SOCK_STREAM , 0)) == 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
if( setsockopt(master_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0 )
{
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( SERVPORT );
if (bind(master_socket, (struct sockaddr *)&address, sizeof(address))<0)
{
perror("bind failed");
exit(EXIT_FAILURE);
}
if (listen(master_socket, 3) < 0)
{
perror("listen");
exit(EXIT_FAILURE);
}
addrlen = sizeof(address);
if(GUC_Debug_handle)
puts("Waiting for connections ...");
while(TRUE)
{
FD_ZERO(&readfds);
FD_SET(master_socket, &readfds);
max_sd = master_socket;
for ( i = 0 ; i < max_clients ; i++)
{
//socket descriptor
sd = client_socket[i];
if(sd > 0)
FD_SET( sd , &readfds);
if(sd > max_sd)
max_sd = sd;
}
activity = select( max_sd + 1 , &readfds , NULL , NULL , NULL);
if ((activity < 0) && (errno!=EINTR))
{
printf("select error");
}
if (FD_ISSET(master_socket, &readfds))
{
if ((new_socket = accept(master_socket, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0)
{
perror("accept");
exit(EXIT_FAILURE);
}
for (i = 0; i < max_clients; i++)
{
if( client_socket[i] == 0 )
{
client_socket[i] = new_socket;
break;
}
}
}
for (i = 0; i < max_clients; i++)
{
sd = client_socket[i];
if (FD_ISSET( sd , &readfds))
{
if ((valread = read( sd , buffer, 9000)) == 0 || E_pipe_error == 1)
{
E_pipe_error = 0;
getpeername(sd , (struct sockaddr*)&address , (socklen_t*)&addrlen);
if(GUC_Debug_handle)
printf("Host disconnected , ip %s , port %d \n" , inet_ntoa(address.sin_addr) , ntohs(address.sin_port));
//Close the socket and mark as 0 in list for reuse
close(sd);
client_socket[i] = 0;
}
else
{
/*printf("\n");
for(i=0;i< valread;i++)
{
printf("%d ",buffer[i]);
}
printf("\n");*/
CMD_ProcessRecvdPacket(buffer,valread,sd);
}
}
}
}
return 0;
/********************************************************/
void *TCP_EventUploadHandler(void *)
{
struct sockaddr_in Event_clien_addr;
struct hostent *server;
fd_set myset,readfds;
struct timeval tv;
while(1)
{
Even_port_address();
memset(buffer, '0',sizeof(buffer));
if((sockfd_event = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("\n Error : Could not create socket \n");
return 0;
}
server = gethostbyname(event_port);
memset(&Event_clien_addr, '0', sizeof(Event_clien_addr));
Event_clien_addr.sin_family = AF_INET;
Event_clien_addr.sin_port = htons(CLIENTPORT);
bcopy((char *)server->h_addr,
(char *)&Event_clien_addr.sin_addr.s_addr,
server->h_length);
//puts(event_port);
//fcntl(sockfd_event, F_SETFL, O_NONBLOCK);
if(TXN_PendingCount())
{
errno = 0;
n = connect(sockfd_event, (struct sockaddr *)&Event_clien_addr, sizeof(Event_clien_addr));
if(n <= 0)
{
//printf("\nerror in event: %d\n",errno);
switch(errno)
{
case EINPROGRESS:
tv.tv_sec = 20;
tv.tv_usec = 0;
FD_ZERO(&myset);
FD_ZERO(&readfds);
FD_SET(sockfd_event, &myset);
n = select(sockfd_event+1,&readfds, &myset, NULL, &tv);// printf("\n\n");
if (n < 0 && errno != EINTR)
{
fprintf(stderr, "Error connecting %d - %s\n", errno, strerror(errno));
}
else if (n > 0)
{
printf("\nerror in event: %d\n",errno);
}
break;
case 0:
printf("\nevent upload\n");
Send_read_event();
close(sockfd_event);
break;
case EISCONN : //56 Socket is already connected
printf("\nsocket is already connected\n");
Send_read_event();
close(sockfd_event);
break;
case ENOTCONN: //ENOTCONN 57 Socket is not connected
printf("\nSocket is not connected\n");
close(sockfd_event);
break;
case EALREADY: //37 Operation already in progress
printf("\nOperation already in progress\n");
break;
case ETIMEDOUT: // 60 Connection timed out
printf("\nConnection timed out\n");
close(sockfd_event);
break;
case ECONNREFUSED: // 61 Connection refused
printf("\nConnection refused\n");
close(sockfd_event);
break;
case EHOSTDOWN: //64 Host is down
printf("\nHost is down\n");
close(sockfd_event);
break;
}
}
}
}
}
/*****************************************************/
void ReadCard(void)
{
for(;;){
char CMD_READ[14],str[14];
unsigned char CMD[5]={0xA,0x02,0x01,0x9};
memset(CMD_READ,0x00,sizeof(CMD_READ));
memset(str,0x00,sizeof(str));
//open_port();
write(fd_serial_mifareread, CMD,4);
usleep(40*1000);
read(fd_serial_mifareread, CMD_READ,14);
procees_read(CMD_READ);
}
}

Non blocking connect call doesn't return connection refused

I have setup a timeout on a non-blocking connect call, which timesout correctly when the connection is attempted to an address that will not respond.
However, when the connection is refused by the destination, it does not seem to return the connection refused call.
int x = fcntl(iSock,F_GETFL, 0);
fcntl(iSock,F_SETFL, x | O_NONBLOCK);
fd_set writeFD;
FD_ZERO(&writeFD);
FD_SET(iSock, &writeFD);
timeval timeout;
timeout.tv_sec = 30;
timeout.tv_usec = 0;
errno = 0;
if ((iStat = connect(iSock, (struct sockaddr *)&addr, sizeof(addr))) < 0)
{
if(errno == EINPROGRESS)
{
if(int retval = (select (iSock+1, (fd_set *)NULL, &writeFD, (fd_set *)NULL, (struct timeval *)(&timeout)) > 0) )
{
socklen_t len = 0;
int error = 0;
if (getsockopt(iSock, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
return (-1);
if (error == EINPROGRESS)
{
close(iSock);
errno = error;
return (-1);
}
else
{
printf("Connected \n");
return 0;
}
}
else
{
printf("Not Connected - %d\n", errno);
close(iSock);
return iStat;
}
}
else
{
close(iSock);
return iStat;
}
}
From the connect call, it always seems to return EINPROGRESS, then the select call will return > 0 with error set as 0.
When I change this to a blocking connect call, I get the return code of CONNECTIONREFUSED immediately from the connect call.
The socklen_t len = 0; is incorrect.
This needs to be set to
socklen_t len = sizeof(int);
Setting this value fixed the issue, and the correct return code (CONNREFUSED) was set from the select call
I think that socklen_t len = sizeof(error); better than sizeof(int).
It is same result.

"resource temporarily unavailable" in recv in socket programming

I want to read and write over Wanpipe driver (a network device driver for Sangoma cards) via socket programming but i get this message error: "resource temporarily unavailable". The card is working and i see it send and receive packets in ifconfig. I have included my code and would be very pleased if somebody help me in this.
A related question: I set the socket to blocking mode but the recv message does not block? how could i block the recv?
int main(void)
{
int sd;
int buflen=WP_HEADER + MAX_PACKET;
char buf[buflen];
struct wan_sockaddr_ll sa;
sd = socket(AF_WANPIPE, SOCK_RAW,0);
if (sd < 0) /* if socket failed to initialize, exit */
{
perror("Error Creating Socket");
exit(1);
}
printf("Socket Descriptor:%d\n",sd);
memset(&sa,0,sizeof(struct wan_sockaddr_ll));
strncpy((char*)sa.sll_card,"wanpipe1",sizeof(sa.sl l_card));
strncpy((char*)sa.sll_device,"w1g1",sizeof(sa.sll_ device));
sa.sll_protocol = htons(PVC_PROT);
sa.sll_family = AF_WANPIPE;
if(bind(sd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
{
perror("error bind failed");
close(sd);
exit(1);
}
int data=0;
int ret=ioctl(sd,FIONBIO,&data);
if (ret < 0)
{
perror("ioctl error!");
close(sd);
return 1;
}
fd_set read_fds;
struct timeval timeout;
timeout.tv_sec = 10;
timeout.tv_usec = 0;
FD_ZERO(&read_fds);
FD_SET(sd,&read_fds);
if(select(sd+1, &read_fds, NULL, NULL, &timeout) < 0)
{
perror("select() error!");
exit(1);
}
if (FD_ISSET(sd,&read_fds))
printf("There is data for reading\n");
else
printf("There is no data for reading\n");*/
// MSG_WAITALL | MSG_PEEK | MSG_OOB
int r=recv(sd,buf,buflen,0);
if (r < 0)
{
perror("Wanpipe raw socket reading");
close(sd);
exit(1);
}
printf("\nNumber of bytes read into the buffer: %d",r);
printf("\nThe read buffer: ");
puts(buf);
close(sd);
}
thank you in advance.

Resources