C: socket connection timeout
I have a simple program to check if a port is open, but I want to shorten the timeout length on the socket connection because the default is far too long. I'm not sure how to do this though. Here's the code:
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char **argv) {
u_short port; /* user specified port number */
char addr[1023]; /* will be a copy of the address entered by u */
struct sockaddr_in address; /* the libc network address data structure */
short int sock = -1; /* file descriptor for the network socket */
if (argc != 3) {
fprintf(stderr, "Usage %s <port_num> <address>", argv[0]);
return EXIT_FAILURE;
}
address.sin_addr.s_addr = inet_addr(argv[2]); /* assign the address */
address.sin_port = htons(atoi(argv[2])); /* translate int2port num */
sock = socket(AF_INET, SOCK_STREAM, 0);
if (connect(sock,(struct sockaddr *)&address,sizeof(address)) == 0) {
printf("%i is open\n", port);
}
close(sock);
return 0;
}
Solution 1:
Set the socket non-blocking, and use select()
(which takes a timeout parameter). If a non-blocking socket is trying to connect, then select()
will indicate that the socket is writeable when the connect()
finishes (either successfully or unsuccessfully). You then use getsockopt()
to determine the outcome of the connect()
:
int main(int argc, char **argv) {
u_short port; /* user specified port number */
char *addr; /* will be a pointer to the address */
struct sockaddr_in address; /* the libc network address data structure */
short int sock = -1; /* file descriptor for the network socket */
fd_set fdset;
struct timeval tv;
if (argc != 3) {
fprintf(stderr, "Usage %s <port_num> <address>\n", argv[0]);
return EXIT_FAILURE;
}
port = atoi(argv[1]);
addr = argv[2];
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr(addr); /* assign the address */
address.sin_port = htons(port); /* translate int2port num */
sock = socket(AF_INET, SOCK_STREAM, 0);
fcntl(sock, F_SETFL, O_NONBLOCK);
connect(sock, (struct sockaddr *)&address, sizeof(address));
FD_ZERO(&fdset);
FD_SET(sock, &fdset);
tv.tv_sec = 10; /* 10 second timeout */
tv.tv_usec = 0;
if (select(sock + 1, NULL, &fdset, NULL, &tv) == 1)
{
int so_error;
socklen_t len = sizeof so_error;
getsockopt(sock, SOL_SOCKET, SO_ERROR, &so_error, &len);
if (so_error == 0) {
printf("%s:%d is open\n", addr, port);
}
}
close(sock);
return 0;
}
Solution 2:
This article might help:
Connect with timeout (or another use for select() )
Looks like you put the socket into non-blocking mode until you've connected, and then put it back into blocking mode once the connection's established.
void connect_w_to(void) {
int res;
struct sockaddr_in addr;
long arg;
fd_set myset;
struct timeval tv;
int valopt;
socklen_t lon;
// Create socket
soc = socket(AF_INET, SOCK_STREAM, 0);
if (soc < 0) {
fprintf(stderr, "Error creating socket (%d %s)\n", errno, strerror(errno));
exit(0);
}
addr.sin_family = AF_INET;
addr.sin_port = htons(2000);
addr.sin_addr.s_addr = inet_addr("192.168.0.1");
// Set non-blocking
if( (arg = fcntl(soc, F_GETFL, NULL)) < 0) {
fprintf(stderr, "Error fcntl(..., F_GETFL) (%s)\n", strerror(errno));
exit(0);
}
arg |= O_NONBLOCK;
if( fcntl(soc, F_SETFL, arg) < 0) {
fprintf(stderr, "Error fcntl(..., F_SETFL) (%s)\n", strerror(errno));
exit(0);
}
// Trying to connect with timeout
res = connect(soc, (struct sockaddr *)&addr, sizeof(addr));
if (res < 0) {
if (errno == EINPROGRESS) {
fprintf(stderr, "EINPROGRESS in connect() - selecting\n");
do {
tv.tv_sec = 15;
tv.tv_usec = 0;
FD_ZERO(&myset);
FD_SET(soc, &myset);
res = select(soc+1, NULL, &myset, NULL, &tv);
if (res < 0 && errno != EINTR) {
fprintf(stderr, "Error connecting %d - %s\n", errno, strerror(errno));
exit(0);
}
else if (res > 0) {
// Socket selected for write
lon = sizeof(int);
if (getsockopt(soc, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon) < 0) {
fprintf(stderr, "Error in getsockopt() %d - %s\n", errno, strerror(errno));
exit(0);
}
// Check the value returned...
if (valopt) {
fprintf(stderr, "Error in delayed connection() %d - %s\n", valopt, strerror(valopt)
);
exit(0);
}
break;
}
else {
fprintf(stderr, "Timeout in select() - Cancelling!\n");
exit(0);
}
} while (1);
}
else {
fprintf(stderr, "Error connecting %d - %s\n", errno, strerror(errno));
exit(0);
}
}
// Set to blocking mode again...
if( (arg = fcntl(soc, F_GETFL, NULL)) < 0) {
fprintf(stderr, "Error fcntl(..., F_GETFL) (%s)\n", strerror(errno));
exit(0);
}
arg &= (~O_NONBLOCK);
if( fcntl(soc, F_SETFL, arg) < 0) {
fprintf(stderr, "Error fcntl(..., F_SETFL) (%s)\n", strerror(errno));
exit(0);
}
// I hope that is all
}
Solution 3:
The answers about using select()
/poll()
are right and code should be written this way to be portable.
However, since you're on Linux, you can do this:
int synRetries = 2; // Send a total of 3 SYN packets => Timeout ~7s
setsockopt(fd, IPPROTO_TCP, TCP_SYNCNT, &synRetries, sizeof(synRetries));
See man 7 tcp
and man setsockopt
.
I used this to speed up the connect-timeout in a program I needed to patch quickly. Hacking it to timeout via select()
/poll()
was not an option.
Solution 4:
Here is a modern connect_with_timeout
implementation, using poll
, with proper error and signal handling:
#include <sys/socket.h>
#include <fcntl.h>
#include <poll.h>
#include <time.h>
int connect_with_timeout(int sockfd, const struct sockaddr *addr, socklen_t addrlen, unsigned int timeout_ms) {
int rc = 0;
// Set O_NONBLOCK
int sockfd_flags_before;
if((sockfd_flags_before=fcntl(sockfd,F_GETFL,0)<0)) return -1;
if(fcntl(sockfd,F_SETFL,sockfd_flags_before | O_NONBLOCK)<0) return -1;
// Start connecting (asynchronously)
do {
if (connect(sockfd, addr, addrlen)<0) {
// Did connect return an error? If so, we'll fail.
if ((errno != EWOULDBLOCK) && (errno != EINPROGRESS)) {
rc = -1;
}
// Otherwise, we'll wait for it to complete.
else {
// Set a deadline timestamp 'timeout' ms from now (needed b/c poll can be interrupted)
struct timespec now;
if(clock_gettime(CLOCK_MONOTONIC, &now)<0) { rc=-1; break; }
struct timespec deadline = { .tv_sec = now.tv_sec,
.tv_nsec = now.tv_nsec + timeout_ms*1000000l};
// Wait for the connection to complete.
do {
// Calculate how long until the deadline
if(clock_gettime(CLOCK_MONOTONIC, &now)<0) { rc=-1; break; }
int ms_until_deadline = (int)( (deadline.tv_sec - now.tv_sec)*1000l
+ (deadline.tv_nsec - now.tv_nsec)/1000000l);
if(ms_until_deadline<0) { rc=0; break; }
// Wait for connect to complete (or for the timeout deadline)
struct pollfd pfds[] = { { .fd = sockfd, .events = POLLOUT } };
rc = poll(pfds, 1, ms_until_deadline);
// If poll 'succeeded', make sure it *really* succeeded
if(rc>0) {
int error = 0; socklen_t len = sizeof(error);
int retval = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len);
if(retval==0) errno = error;
if(error!=0) rc=-1;
}
}
// If poll was interrupted, try again.
while(rc==-1 && errno==EINTR);
// Did poll timeout? If so, fail.
if(rc==0) {
errno = ETIMEDOUT;
rc=-1;
}
}
}
} while(0);
// Restore original O_NONBLOCK state
if(fcntl(sockfd,F_SETFL,sockfd_flags_before)<0) return -1;
// Success
return rc;
}
Solution 5:
On Linux you can also use:
struct timeval timeout;
timeout.tv_sec = 7; // after 7 seconds connect() will timeout
timeout.tv_usec = 0;
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
connect(...)
Don't forget to clear SO_SNDTIMEO
after connect()
if you don't need it.