[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;
}