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

Re: [Libevent-users] Constraining size of output buffer



On Fri, Jun 24, 2011 at 8:22 PM, Mina Naguib
<mina.naguib@xxxxxxxxxxxxxxxx> wrote:
>
> Hi everyone
>
> I'm trying to keep a hostile client from causing a libevent server from consuming too much memory in buffered data.
>
> Using bufferevents, I let libevent keep the input buffer within limits using its watermark mechanism, and I added a simple mechanism in the server to, essentially, stop draining the input buffer if the output buffer is past a certain size.
>
> The problem I'm observing using this pattern (and various variations I've tried), is that at some point libevent stops calling the write-ready callback set on the bufferevent.
>
> I've simplified the server code to the bare essentials needed to demonstrate the problem,  source posted here:
> http://pastebin.com/CzpVRRAy

Tentatively, I'm going to blame this one on some kind of buffering or
stalling issue in netcat or in your terminal, not on libevent.  When I
write a trivial libevent-based client program to hammer your server
program, it works fine.  See  the attached file -- I used your server
program as a template.

The underlying pattern you're using here is reasonable, though you'd
probably want to polish it a little.  Instead of handling as much as
possible from the inbuf whenever the outbuf starts out less-than-full,
I'd suggest draining the inbuf and adding to the outbuf only until the
outbuf becomes full.

Also, for a situation like this one, I'd suggest setting the
low-watermark for write to something nonzero: In some cases like this
you don't want to let your write buffer drop to 0 either.

yrs,
-- 
Nick
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <time.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <event.h>
#include <event2/bufferevent.h>

#define PORT 6565

unsigned long long bytes_read = 0;
struct timeval start_time;

void
timer_cb(evutil_socket_t fd, short events, void *arg)
{
	struct timeval now;
	double seconds_elapsed, bytes_per_second;
	gettimeofday(&now, NULL);
	seconds_elapsed = now.tv_sec - start_time.tv_sec;
	seconds_elapsed += (now.tv_usec - start_time.tv_usec) / 1.0e6;
	bytes_per_second = bytes_read / seconds_elapsed;
	printf("%f bytes per second\n", bytes_per_second);
}

void
handle_client_buffer_in(struct bufferevent *bev, void *hint) {
	struct evbuffer *input = bufferevent_get_input(bev);
	size_t inlen = evbuffer_get_length(input);

	evbuffer_drain(input, inlen);
	
	bytes_read += inlen;
}

void
handle_client_buffer_out(struct bufferevent *bev, void *hint) {
	struct evbuffer *output = bufferevent_get_output(bev);
	size_t outlen = evbuffer_get_length(output);

	int i, n;

	n = 64*1024;

	for (i=outlen / 4; i < n; ++i)
		bufferevent_write(bev, "hi\r\n", 4);

}

void
handle_client_buffer_evt(struct bufferevent *bev, short revents, void *hint) {

	if (revents & BEV_EVENT_CONNECTED) {
		printf("Connected\n");
		bufferevent_write(bev, "hi\r\nhi\r\n", 8);
	}
	else if (revents & BEV_EVENT_ERROR) {
		printf("Client error: %s\n", strerror(errno));
		bufferevent_free(bev);
	}
	else if (revents & BEV_EVENT_TIMEOUT) {
		printf("Client timed out\n");
		bufferevent_free(bev);
	}
	else if (revents & BEV_EVENT_EOF) {
		printf("Client disconnected\n");
		bufferevent_free(bev);
	}
	else {
		printf("Spurious event from client: %d\n", revents);
		bufferevent_free(bev);
	}
}


int main(int argc, char ** argv) {
	struct bufferevent *bev;
	struct event_base *eb = NULL;
	struct sockaddr_in sa;
	struct timeval five_seconds = { 5, 0 };

	eb = event_base_new();
	struct event timer;

	memset(&sa, 0, sizeof(sa));
	sa.sin_family = AF_INET;
	sa.sin_addr.s_addr = htonl(0x7f000001);
	sa.sin_port = htons(PORT);

	bev = bufferevent_socket_new(eb, -1, BEV_OPT_CLOSE_ON_FREE); 
	bufferevent_setcb(bev, 
                        handle_client_buffer_in,
                        handle_client_buffer_out,
                        handle_client_buffer_evt,
                        NULL
                        );

	bufferevent_enable(bev, EV_READ|EV_WRITE);
	bufferevent_socket_connect(bev, (struct sockaddr*)&sa, sizeof(sa));


	event_assign(&timer, eb, -1, EV_PERSIST, timer_cb, NULL);

	gettimeofday(&start_time, NULL);
	event_add(&timer, &five_seconds);

	event_base_dispatch(eb);
	return 0;
}