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

Re: [Libevent-users] strange exit with hand-made socket and bufferevent



Thanks Brian! Indeed, if I put the two lines that enable writing and
write to the bufferevent right in the connected callback, the darn
thing works fine!

However, this was just a minimal example. I'd like it, however, if
libevent supported this by marking inside the bufferevent structure
that the socket fd is connected or isn't connected, and only write to
it when it has connected. I suspect libevent checks if the fd==-1, and
only then issues the connect(). if fd!=-1, it assumes it's connected.
So this would be simplified if libevent somehow knew about my fd, and
whether it's connected or not... There seems to be a getpeername()
function that might do the trick.

I'm using this relatively new feature of bufferevents that allows you
to write data to them before they're connected.

Perhaps there can be another function, such as:

bufferevent_socket_new_unconnected(base, fd, options);

and that would fix everything.

On a personal note, i consider the SIGPIPE a libevent issue. It's a
signal just like SIGSEGV that occurs inside libevent functions.

Let me paste the real code here:

As you can see, i'd have to create another evbuffer to store the data
until the socket gets connected, which defeats the purpose of that
"write before connected" feature of the bufferevents.

It begins by calling initmasterserver before the loop begins.

#include <event2/event.h>
#include <event2/event_struct.h>
#include <event2/dns.h>
#include <errno.h>
#include <arpa/inet.h>

#include "cube.h"

VARN(updatemaster, allowupdatemaster, 0, 1, 1);
SVAR(mastername, server::defaultmaster());

bufferevent *masterbuf = NULL;
in_addr_t masteraddr = 0;
event registermaster_timer;
event masterdns_timer;

extern int serverport;
void updatemasterserver() {
	if(!mastername[0] || !allowupdatemaster) return;
	printf("Updating master server...\n");
	requestmasterf("regserv %d\n", serverport);
}
COMMAND(updatemasterserver, "");

void registermaster_timer_handler(int, short, void *) {
	updatemasterserver();
	timeval one_hr; one_hr.tv_sec = 3600; one_hr.tv_usec = 0;
	DEBUGF(event_add(&registermaster_timer, &one_hr));
}

static void masterreadcb(struct bufferevent *buf, void *arg) {
	printf("masterreadcb\n");
	char *ln;
	while((ln = evbuffer_readln(bufferevent_get_input(buf), NULL,
EVBUFFER_EOL_ANY))) {
		char *args = ln;
		while(*args && !isspace(*args)) args++;
		int cmdlen = args - ln;
		while(*args && isspace(*args)) args++;
		if(!strncmp(ln, "failreg", cmdlen)) {
			server::log("Master server registration failed: %s", args);
			irc.speak("\00314Master server registration failed: %s", args);
		} else if(!strncmp(ln, "succreg", cmdlen)) {
			server::log("Master server registration succeeded.");
			irc.speak("\00314Master server registration succeeded.");
		} else
			server::processmasterinput(ln, cmdlen, args);
		free(ln);
	}
}

static void masterwritecb(struct bufferevent *buf, void *arg) {
}

extern ENetAddress serveraddress;
extern char *serverip;
static int mkmastersock() {
	int fd = socket(AF_INET, SOCK_STREAM, 0);
	if(fd && evutil_make_socket_nonblocking(fd)>=0) {
		struct sockaddr_in addr;
		addr.sin_family = AF_INET;
		inet_pton(AF_INET, serverip, &addr.sin_addr);
		addr.sin_port = 0;
		printf("binding...\n");
		bind(fd, (sockaddr *)&addr, sizeof(addr));
	}
	return fd;
}

static void mastereventcb(struct bufferevent *buf, short what, void *arg) {
	printf("mastereventcb\n");
	if(what == BEV_EVENT_CONNECTED) {
		printf("Connected to masterserver\n");
		DEBUGF(bufferevent_enable(masterbuf, EV_READ));
	} else {
		if(what != (BEV_EVENT_EOF & EV_READ)) bufferevent_print_error(what,
"Disconnected from \"%s\" master server:", mastername);
		struct sockaddr_in addr;
		addr.sin_addr.s_addr = masteraddr;
		addr.sin_port = htons(server::masterport());
		addr.sin_family = AF_INET;

		DEBUGF(bufferevent_free(masterbuf));
		DEBUGF(masterbuf = bufferevent_socket_new(evbase, mkmastersock(),
BEV_OPT_CLOSE_ON_FREE));
		DEBUGF(bufferevent_setcb(masterbuf, masterreadcb, masterwritecb,
mastereventcb, NULL));
		DEBUGF(bufferevent_socket_connect(masterbuf, (sockaddr *)&addr,
sizeof(struct sockaddr_in)));
	}
}

bool requestmasterf(const char *fmt, ...) {
	if(!mastername[0] || !allowupdatemaster) return false;

	va_list ap;
	va_start(ap, fmt);
	bufferevent_write_vprintf(masterbuf, fmt, ap);
	va_end(ap);

	return true;
}

static void masterdnscb(int result, char type, int count, int ttl,
void *addresses, void *arg) {
	printf("masterdnscb\n");
	if(result == DNS_ERR_NONE) {
		if(type == DNS_IPv4_A) {
			masteraddr = ((in_addr_t *)addresses)[0];
			struct sockaddr_in addr;
			addr.sin_addr.s_addr = masteraddr;
			addr.sin_port = htons(server::masterport());
			addr.sin_family = AF_INET;
			DEBUGF(bufferevent_socket_connect(masterbuf, (sockaddr *)&addr,
sizeof(struct sockaddr_in)));
		}
	} else {
		evdns_print_error(result, "Error resolving %s:", mastername);
		timeval ten_secs;
		ten_secs.tv_sec = 10;
		ten_secs.tv_usec = 0;
		DEBUGF(event_add(&masterdns_timer, &ten_secs));
	}
}

void masterdns_timer_handler(int, short, void *) {
	printf("Resolving \"%s\"...\n", mastername);
	evdns_base_resolve_ipv4(dnsbase, mastername, 0, masterdnscb, NULL);
}

void initmasterserver() {
	if(!mastername[0] || !allowupdatemaster) {
		printf("Not registering with master server.\n");
		return;
	}

	DEBUGF(evtimer_assign(&registermaster_timer, evbase,
&registermaster_timer_handler, NULL));
	DEBUGF(evtimer_assign(&masterdns_timer, evbase,
&masterdns_timer_handler, NULL));
	DEBUGF(masterbuf = bufferevent_socket_new(evbase, mkmastersock(),
BEV_OPT_CLOSE_ON_FREE));
	DEBUGF(bufferevent_setcb(masterbuf, masterreadcb, masterwritecb,
mastereventcb, NULL));
	DEBUGF(registermaster_timer_handler(0, 0, NULL));
	DEBUGF(masterdns_timer_handler(0, 0, NULL));
}



On Tue, Feb 16, 2010 at 2:46 PM, Brian Pane <brianp@xxxxxxxxxx> wrote:
> On Tue, Feb 16, 2010 at 12:11 AM, Mihai Draghicioiu
> <mihai.draghicioiu@xxxxxxxxx> wrote:
>
>> As you can see, it stops with a SIGPIPE, and i suspect it has
>> something to do with the fact that at #2 evbuffer_write_iovec, the fd
>> parameter is 1 and at #3 evbuffer_write_atmost the fd parameter is 10.
>> The fd created by my socket() call is 10 here.
>
> The fact that the program stops with SIGPIPE isn't actually
> a libevent issue. ÂThat's just standard Unix behavior: a write
> to a socket that isn't able to handle the write will raise SIGPIPE,
> and the default behavior of SIGPIPE is to exit the process.
>
> I've developed a habit of setting up the signal handling
> to ignore SIGPIPE in any program that uses sockets,
> using code like this:
>
> Â Â#include <signal.h>
> Â Âstatic void setup_signals()
> Â Â{
> Â Â Â Âstruct sigaction sa;
> Â Â Â Âsa.sa_handler = SIG_IGN;
> Â Â Â Âsa.sa_flags = 0;
> Â Â Â Âsigemptyset(&(sa.sa_mask));
> Â Â Â Âsigaction(SIGPIPE, &sa, 0);
> Â Â}
>
> Annoying default signal-handling behavior aside, though,
> there's still the question of why your program ends up
> trying to do a write on an invalid socket in the first place.
> The most common cause of SIGPIPE is that the remote
> host has closed its end of the connection, but that's not
> what's happening in this case. ÂI believe the reason you
> get SIGPIPE is that the write(2) ends up happening
> before the socket gets connected. ÂThe order of operations
> in your code is basically:
>
> 1. Push some data into the bufferevent and enable EV_WRITE
> 2. Request an asynchronous connection to the remote host:port
> 3. Start up the event loop
> 4. Some time later, process the event callback when
> Â the async connect completes
>
> I think what's happening is that, once the libevent main
> loop starts in step 3, it sees that you've requested a write
> on the bufferevent and, because EV_WRITE is enabled,
> it attempts to write to the socket before the connection
> is established -- and possibly before the nonblocking
> connect(2) syscall is even invoked.
>
> If that's the root of the problem, the solution is to not
> enable EV_WRITE on the bufferevent until you get the
> callback indicating that the socket is connected. ÂI.e.,
> do the operations listed above in the order 2, 3, 4, 1
> instead of 1, 2, 3, 4.
>
> -Brian
> ***********************************************************************
> To unsubscribe, send an e-mail to majordomo@xxxxxxxxxxxxx with
> unsubscribe libevent-users  Âin the body.
>
***********************************************************************
To unsubscribe, send an e-mail to majordomo@xxxxxxxxxxxxx with
unsubscribe libevent-users    in the body.