Re: ORBit blocking in connect(). And how to deal with SIGPIPE.




Elliot Lee <sopwith@redhat.com> wrote:
> > 
> > (gdb) bt
> > #0  0x40517d72 in __libc_connect ()
> > #1  0x40317e30 in iiop_connection_new (host=0x8084f78 "retrans.mit.edu", port=1075) at connection.c:841
> 
> Oh, I see what is happening - it cannot connect to the dead server via
> UNIX sockets, so it tries to use TCP (like it should) and that sits there
> because the kernel is too dumb to realize that the socket is dead.

    Your prognosis seems correct, but I'm really not sure how the
kernel would figure that out.  The socket isn't really dead, since it
was never alive.

> Unfortunately, I don't have any especially bright ideas on fixing
> this nicely. Making the socket non-blocking before connect might
> help, but it would be a pain to hack into libIIOP in its present
> state. If there's a way to use setsockopt() to set a connect()
> timeout, let me know.

    I wrote a small demo of a timeout-connect.  It's pasted to the end
of this mail.


> This is a gross hack - SIGPIPE is useless in GUI applications anyways.

    I really have to disagree here.  There's nothing special about GUI
applications that means that they aren't going to make network
connections, talk to pipes, or do anything else that could generate a
SIGPIPE.  Think about a multiplayer network game, just to name one
example.

    The hack is kind of gross, though.

> I think that even trying to allow multiple signal handlers for the
> same signal is useless until it is done right - i.e. glib 1.4. For
> now, the best thing is just to force an ignore.

    What about putting something like this into ORBit:

    /*
     * Cache the user's signal handler.
     */
    oldhandler = signal (SIGPIPE, SIG_IGN);

    result = writev ( ... );

    if (result < 0 && errno == EPIPE) {
        /*
         * I know a SIGPIPE would have occured here, and I can deal
         * with it nicely.
         */
    }

    /*
     * Restore the user's old signal handler.
     */
    signal (SIGPIPE, oldhandler);

I guess you probably won't like the overhead of that ;-) I can't say I
blame you.

Word to your proverbial mother,
Nat

/*
 * Some sample code demonstrating non-blocking connect() with a
 * timeout.
 *
 * Nat Friedman (nat@nat.org)
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

static int
net_resolve (const char *host, struct in_addr *addr)
{
	struct hostent *hp;

	addr->s_addr = inet_addr (host);

	if (addr->s_addr != -1)
		return 0;

	hp = gethostbyname (host);

	if (hp == NULL)
		return -1;

	memcpy ( (void *)addr, (void *)hp->h_addr, hp->h_length);

	return 0;
}

static int
net_tcp_connect_with_timeout (int *fd, struct sockaddr_in *sock, int timeout_secs)
{
	struct timeval timeout;
	fd_set write_fds;

	*fd = socket (PF_INET, SOCK_STREAM, 0);

	if (*fd < 0) {
		perror ("socket");
		return -1;
	}

	/*
	 * Set the socket to be non-blocking so that connect()
	 * doesn't block.
	 */
	if (fcntl (*fd, F_SETFL, O_NONBLOCK) < 0)
		return -1;

	/*
	 * Setup the connection timeout.
	 */
	timeout.tv_sec = timeout_secs;
	timeout.tv_usec = 0;

	while (1) {

		/*
		 * Try to connect.
		 */
		if (connect (*fd, (struct sockaddr *) sock,
			     sizeof (struct sockaddr_in)) < 0) {

			if (errno != EAGAIN &&
			    errno != EINPROGRESS) {
				perror ("connect");
				return -1;
			}
				
		} else {
			fprintf (stderr, "Connected succesfully!\n");
			return 0;
		}

		/*
		 * We couldn't connect, so we select on the fd and
		 * wait for the timer to run out, or for the fd to be
		 * ready.
		 */
		FD_ZERO (&write_fds);
		FD_SET (*fd, &write_fds);

		while (select (getdtablesize (), NULL, &write_fds, NULL, &timeout) < 0) {
			if (errno != EINTR) {
				perror ("select");
				return -1;
			}
		}

		if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
			fprintf (stderr, "Connection timed out!\n");
			return -1;
		}
		
	}

	return 0;
}

int
main (int argc, char **argv)
{
	struct sockaddr_in sock;
	unsigned short port;
	char *hostname;
	int fd;

	if (argc < 3) {
		fprintf (stderr, "Usage: %s hostname port\n", argv[0]);
		return 1;
	}

	port = atoi (argv [2]);
	hostname = argv [1];

	sock.sin_family = AF_INET;
	sock.sin_port = htons (port);

	/*
	 * We still need a timeout-based way to do this.
	 */
	if (net_resolve (hostname, & sock.sin_addr) < 0)
		return -1;

	/*
	 * Try to connect with a 5 second timeout.
	 */
	net_tcp_connect_with_timeout (&fd, &sock, 5);

	return 0;
}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]