|
Home > Archive > Unix Programming > March 2004 > maximum timeout on socket connection
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
maximum timeout on socket connection
|
|
|
| A few weeks ago I posted a question about setting a maximum timeout on
the connect() function. I also wanted to avoid the complexity of
making the function non-blocking, and working with select(). The
conclusion was unclear, but it seemed the best method was to set an
alarm. For some reason this didn't work at all--the alarm seemed to
have no affect. Here's my code:
int connectex(int sockfd, const struct sockaddr *serv_addr, socklen_t
addrlen, long maxTimeOut) {
sig_t oldhandler;
if (maxTimeOut != -1) {
oldhandler = signal(SIGALRM, SIG_IGN);
alarm(maxTimeOut);
}
int returned = connect(sockfd, serv_addr, addrlen);
if (maxTimeOut != -1) {
alarm(0);
signal(SIGALRM, oldhandler);
}
return returned;
}
I was told to set SIGALRM to SIG_IGN. Presumably, this would not
prevent the alarm from affecting connect(). In fact, the tests I did
with and without the signal() call were just the same.
I do have a signal handler, but it is only handles SIGSEGV.
| |
| Marc Rochkind 2004-03-18, 4:35 pm |
|
--
Marc Rochkind
"Advanced UNIX Programming" (publishing April 2004)
www.basepath.com/aup
"Jo" <JoJoTwilligo@hotmail.com> wrote in message
news:72ce7f0c.0403181136.3def0332@posting.google.com...
> A few weeks ago I posted a question about setting a maximum timeout on
> the connect() function. I also wanted to avoid the complexity of
> making the function non-blocking, and working with select(). The
> conclusion was unclear, but it seemed the best method was to set an
> alarm. For some reason this didn't work at all--the alarm seemed to
> have no affect. Here's my code:
>
> int connectex(int sockfd, const struct sockaddr *serv_addr, socklen_t
> addrlen, long maxTimeOut) {
> sig_t oldhandler;
> if (maxTimeOut != -1) {
> oldhandler = signal(SIGALRM, SIG_IGN);
> alarm(maxTimeOut);
> }
> int returned = connect(sockfd, serv_addr, addrlen);
> if (maxTimeOut != -1) {
> alarm(0);
> signal(SIGALRM, oldhandler);
> }
>
> return returned;
> }
>
> I was told to set SIGALRM to SIG_IGN. Presumably, this would not
> prevent the alarm from affecting connect(). In fact, the tests I did
> with and without the signal() call were just the same.
> I do have a signal handler, but it is only handles SIGSEGV.
The alarm system call generates a SIGALRM, so you don't want those signals
ignored. SIG_DFL is no good either, since the default action is to terminate
the process. You need a signal handler that just immediately returns, as it
is just the side effect of interrupting the system call that you're looking
for.
However, connect is unusual in that if it is interrupted by a signal it will
keep trying to connect, asynchronously. Not sure what this means for your
application.
(By the way, I'm assuming you have shown pseudo-code. Real code should check
every error return.)
--
Marc Rochkind
"Advanced UNIX Programming" (publishing April 2004)
www.basepath.com/aup
| |
| Sean Burke 2004-03-18, 4:35 pm |
|
"Marc Rochkind" <rochkind@basepath.com> writes:
> --
> Marc Rochkind
> "Advanced UNIX Programming" (publishing April 2004)
> www.basepath.com/aup
>
> "Jo" <JoJoTwilligo@hotmail.com> wrote in message
> news:72ce7f0c.0403181136.3def0332@posting.google.com...
>
> The alarm system call generates a SIGALRM, so you don't want those signals
> ignored. SIG_DFL is no good either, since the default action is to terminate
> the process. You need a signal handler that just immediately returns, as it
> is just the side effect of interrupting the system call that you're looking
> for.
>
> However, connect is unusual in that if it is interrupted by a signal it will
> keep trying to connect, asynchronously. Not sure what this means for your
> application.
>
> (By the way, I'm assuming you have shown pseudo-code. Real code should check
> every error return.)
I would suggest that you question your assumption that
using signals is the easist way to do it. You may find
that using select() is not nearly as complicated as you
expect.
-SEan
| |
|
| Sean Burke <foobar@mystery.org> wrote in message news:<x7ptb9eubz.fsf@bolo.xenadyne.com>...
> "Marc Rochkind" <rochkind@basepath.com> writes:
>
[snip]
[color=darkred]
>
> I would suggest that you question your assumption that
> using signals is the easist way to do it. You may find
> that using select() is not nearly as complicated as you
> expect.
I'm not quite sure what question to ask. Can you tell me how I
might use the select in a simple way, such that it would set a maximum
timeout on the connect()? I'd prefer it not interfere with the other
operations on the socket.
| |
| Sean Burke 2004-03-22, 1:34 am |
|
JoJoTwilligo@hotmail.com (Jo) writes:
> Sean Burke <foobar@mystery.org> wrote in message news:<x7ptb9eubz.fsf@bolo.xenadyne.com>...
>
> [snip]
>
>
> I'm not quite sure what question to ask. Can you tell me how I
> might use the select in a simple way, such that it would set a maximum
> timeout on the connect()? I'd prefer it not interfere with the other
> operations on the socket.
Something similar to this should do it.
Note that I haven't actually compiled it.
-SEan
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
/*
* Connect a socket.
*
* returns zero on success, ETIME on timeout, errno on connect failure.
*/
int connectex(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen, long maxTimeOut)
{
/* Make socket nonblocking.
*/
socket_set_nonblocking(sockfd, true);
/* Connect the socket.
*/
if (connect(sockfd, serv_addr, addr_len)) < 0)
{
if (errno == EINPROGRESS)
{
fd_set fds;
int numfds = 0;
struct timeval timeout;
int r, error;
/* Clear the fd_set.
*/
FD_ZERO(&fds);
FD_SET(sockfd, &fds);
numfds = sockfd + 1;
timeout.tv_sec = maxTimeOut;
timeout.tv_usec= 0;
switch (r = select(numfds, NULL, &fds, NULL, &timeout))
{
case -1:
perror("select failed");
return errno;
case 0:
/* select timed out.
*/
close(sockfd);
return ETIME;
case 1:
if ((error = socket_get_error(sockfd)) == 0)
{
/* Connect succeeded asynchronously.
*/
socket_set_nonblocking(sockfd, false);
return 0;
}
else
{
fprintf(stderr, "connect(%d) failed with error %d - %s",
sockfd, error, strerror(error));
return error;
}
}
}
else
{
fprintf(stderr, "connect(%d) failed with error %d - %s",
sockfd, errno, strerror(errno));
return errno;
}
}
else
{
/* Connect succeeded immediately.
*/
return 0;
}
}
static int
socket_set_nonblocking(int sock, bool mode)
{
int flags = fcntl(sock, F_GETFL, 0);
if (flags == -1)
return errno;
else if ( mode && !(flags & O_NONBLOCK))
flags |= O_NONBLOCK;
else if (!mode && (flags & O_NONBLOCK))
flags ^= O_NONBLOCK;
else
return OK; /* blocking mode is already good. */
if (fcntl(sock, F_SETFL, flags) < 0)
{
fprintf(stderr, "fcntl failed: errno %d - %s", errno, safe_strerror(errno));
return errno;
}
return 0;
}
static int
socket_get_error(int sock)
{
int error;
int len = sizeof(error);
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) == 0)
{
return error;
}
else
{
fprintf(stderr, "getsockopt(%d) failed with errno %d - %s", sock, errno, strerror(errno));
return errno;
}
}
| |
| Andrei Voropaev 2004-03-22, 3:33 am |
| On 2004-03-18, Jo <JoJoTwilligo@hotmail.com> wrote:
> A few weeks ago I posted a question about setting a maximum timeout on
> the connect() function. I also wanted to avoid the complexity of
> making the function non-blocking, and working with select(). The
> conclusion was unclear, but it seemed the best method was to set an
> alarm. For some reason this didn't work at all--the alarm seemed to
> have no affect. Here's my code:
>
> int connectex(int sockfd, const struct sockaddr *serv_addr, socklen_t
> addrlen, long maxTimeOut) {
> sig_t oldhandler;
> if (maxTimeOut != -1) {
> oldhandler = signal(SIGALRM, SIG_IGN);
> alarm(maxTimeOut);
> }
> int returned = connect(sockfd, serv_addr, addrlen);
> if (maxTimeOut != -1) {
> alarm(0);
> signal(SIGALRM, oldhandler);
> }
>
> return returned;
> }
There is one function called 'siginterrupt'. This function controls
whether the system calls would be interrupted when some signal comes.
Though I doubt that your approach with signal would work, because man
connect does not list EINTR among values for errno. Which probably means
that no signal can interrupt connect procedure.
Andrei
| |
|
| Sean Burke <foobar@mystery.org> wrote in message news:<x7isgxbfif.fsf@bolo.xenadyne.com>...
> JoJoTwilligo@hotmail.com (Jo) writes:
>
Ok, there were a few errors, but I soon got it to compile. You
might want to take the return 0; out of the else in connectex() and
put it at the end of the function. Also, what's safe_strerror()? Is
that like strerror_r()? This function isn't reentrant, so I'm not very
concerned there.
Unfortunately, it didn't work It seems that it set non-blocking
properly, and went right through the connect(), but got stuck
somewhere after that--probably the select(). The bottom line is that
connectex() took much longer than the specified timeout, and connected
successfully.
I tested it on a pop3 server that for some reason takes a whopping
10 seconds to pop from. I specified a timeout of 1 second. I'm
wondering why you specified the descriptors the way you did. Is there
any chance that the problem is there?
[color=darkred]
> Something similar to this should do it.
> Note that I haven't actually compiled it.
>
> -SEan
>
>
> #include <assert.h>
> #include <errno.h>
> #include <signal.h>
> #include <string.h>
>
> #include <unistd.h>
> #include <sys/socket.h>
> #include <netinet/in.h>
> #include <netinet/tcp.h>
> #include <arpa/inet.h>
> #include <netdb.h>
>
> /*
> * Connect a socket.
> *
> * returns zero on success, ETIME on timeout, errno on connect failure.
> */
> int connectex(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen, long maxTimeOut)
> {
> /* Make socket nonblocking.
> */
> socket_set_nonblocking(sockfd, true);
>
> /* Connect the socket.
> */
> if (connect(sockfd, serv_addr, addr_len)) < 0)
> {
> if (errno == EINPROGRESS)
> {
> fd_set fds;
> int numfds = 0;
> struct timeval timeout;
> int r, error;
>
> /* Clear the fd_set.
> */
> FD_ZERO(&fds);
> FD_SET(sockfd, &fds);
> numfds = sockfd + 1;
>
> timeout.tv_sec = maxTimeOut;
> timeout.tv_usec= 0;
>
> switch (r = select(numfds, NULL, &fds, NULL, &timeout))
> {
> case -1:
> perror("select failed");
> return errno;
>
> case 0:
> /* select timed out.
> */
> close(sockfd);
> return ETIME;
>
> case 1:
> if ((error = socket_get_error(sockfd)) == 0)
> {
> /* Connect succeeded asynchronously.
> */
> socket_set_nonblocking(sockfd, false);
> return 0;
> }
> else
> {
> fprintf(stderr, "connect(%d) failed with error %d - %s",
> sockfd, error, strerror(error));
> return error;
> }
> }
> }
> else
> {
> fprintf(stderr, "connect(%d) failed with error %d - %s",
> sockfd, errno, strerror(errno));
> return errno;
> }
> }
> else
> {
> /* Connect succeeded immediately.
> */
> return 0;
> }
> }
>
>
>
> static int
> socket_set_nonblocking(int sock, bool mode)
> {
> int flags = fcntl(sock, F_GETFL, 0);
> if (flags == -1)
> return errno;
> else if ( mode && !(flags & O_NONBLOCK))
> flags |= O_NONBLOCK;
> else if (!mode && (flags & O_NONBLOCK))
> flags ^= O_NONBLOCK;
> else
> return OK; /* blocking mode is already good. */
>
> if (fcntl(sock, F_SETFL, flags) < 0)
> {
> fprintf(stderr, "fcntl failed: errno %d - %s", errno, safe_strerror(errno));
> return errno;
> }
>
> return 0;
> }
>
>
> static int
> socket_get_error(int sock)
> {
> int error;
> int len = sizeof(error);
>
> if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) == 0)
> {
> return error;
> }
> else
> {
> fprintf(stderr, "getsockopt(%d) failed with errno %d - %s", sock, errno, strerror(errno));
> return errno;
> }
> }
| |
| Sean Burke 2004-03-23, 5:35 pm |
|
JoJoTwilligo@hotmail.com (Jo) writes:
> Sean Burke <foobar@mystery.org> wrote in message news:<x7isgxbfif.fsf@bolo.xenadyne.com>...
>
> Ok, there were a few errors, but I soon got it to compile. You
> might want to take the return 0; out of the else in connectex() and
> put it at the end of the function. Also, what's safe_strerror()? Is
> that like strerror_r()? This function isn't reentrant, so I'm not very
> concerned there.
Reviewing the code, I see that it fails to restore the socket
to blocking mode in the case where the connect succeeds
immediately, rather than returning with errno EINPROGRESS.
Probably you should decide whether connectex or its caller is
responsible to close the sockfd in case of failure - i see that
my example code was inconsistent about this.
The safe_strerror() call is one of our library calls, which crept
in to the example via cut-n-paste. Regular strerror returns null
if its argument is out of range, so we have a "safe" wrapper that
won't.
> Unfortunately, it didn't work It seems that it set non-blocking
> properly, and went right through the connect(), but got stuck
> somewhere after that--probably the select(). The bottom line is that
> connectex() took much longer than the specified timeout, and connected
> successfully.
I find that connections on the same host will usually connect
immediately, even when the socket is nonblocking, so to test
the EINPROGRESS path you'll need to connect to a remote host.
> I tested it on a pop3 server that for some reason takes a whopping
> 10 seconds to pop from. I specified a timeout of 1 second. I'm
> wondering why you specified the descriptors the way you did. Is there
> any chance that the problem is there?
When you are waiting for a socket to connect, you place it with
the "write" descriptor set in select. Select should wake when
the connect succeeds or fails. A connection can fail right away
if the host you addressed exists, and is not accepting connections
on the specified port. If the host doesn't exist, then the connect
can hang for a long time, but in no event should the select call
block for longer than the timeout.
You might try stepping through with the debugger to get a better
idea of where the delay is happening. If that doesn't shed light,
post the code with your corrections and I'll give it a spin.
-SEan
| |
|
| It turns out that your code wasn't blocking after all. I was testing
on a pop server who's socket connection is made right away, but the
introductory message takes forever to be delievered. I don't
understand that server. It's going to be difficult to come up with
test data.
Thanks for your help
Sean Burke <foobar@mystery.org> wrote in message news:<x7ad27b47c.fsf@bolo.xenadyne.com>...
> JoJoTwilligo@hotmail.com (Jo) writes:
>
>
> Reviewing the code, I see that it fails to restore the socket
> to blocking mode in the case where the connect succeeds
> immediately, rather than returning with errno EINPROGRESS.
> Probably you should decide whether connectex or its caller is
> responsible to close the sockfd in case of failure - i see that
> my example code was inconsistent about this.
>
> The safe_strerror() call is one of our library calls, which crept
> in to the example via cut-n-paste. Regular strerror returns null
> if its argument is out of range, so we have a "safe" wrapper that
> won't.
>
>
> I find that connections on the same host will usually connect
> immediately, even when the socket is nonblocking, so to test
> the EINPROGRESS path you'll need to connect to a remote host.
>
>
> When you are waiting for a socket to connect, you place it with
> the "write" descriptor set in select. Select should wake when
> the connect succeeds or fails. A connection can fail right away
> if the host you addressed exists, and is not accepting connections
> on the specified port. If the host doesn't exist, then the connect
> can hang for a long time, but in no event should the select call
> block for longer than the timeout.
>
> You might try stepping through with the debugger to get a better
> idea of where the delay is happening. If that doesn't shed light,
> post the code with your corrections and I'll give it a spin.
>
> -SEan
|
|
|
|
|