- 官方sample
- 原文 2016年的
异步api ,处理网络事件
- This is a simple and fast proxy written in C. This makes use of libevent library.
This library basically provides asynchronous API’s to deal with networking events.
非阻塞的api ,高效
- As it makes use of non blocking API calls, the result are extremely efficient executable.
消费者一直都是低cpu并且从不会阻塞等待任何网络调用
- This consumer low cpu resources and never waits in blocking manner to any networking call made.
libevent 底层处理的非常棒
- Under the hood, libevent makes use of epoll in most linux systems. However, being a cross platform library, if you make use of this library it should work on other platforms such as windows too.
使用event buffer 传输数据
- The example provided here, makes use of event buffer provided in the library itself.
数据从后端到前端非常的高效,这是因为event buffer 最小化了内存拷贝,用户不用关心底层的字节级别内存管理
- This makes transfer of data from backend server connection to front end client extremely efficient. It is the feature of event buffer that minimized data copies as much as possible. As a user of the library, you need not worry about low level memory management while keeping count of bytes.
Alright enough of talking, time to go through the code.
le_proxy.c
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
static struct event_base *base;
static struct sockaddr_storage listen_on_addr;
static struct sockaddr_storage connect_to_addr;
static int connect_to_addrlen;
#define MAX_OUTPUT (512*1024)
static void drained_writecb(struct bufferevent *bev, void *ctx);
static void eventcb(struct bufferevent *bev, short what, void *ctx);
static void
readcb(struct bufferevent *bev, void *ctx)
{
struct bufferevent *partner = ctx;
struct evbuffer *src, *dst;
size_t len;
src = bufferevent_get_input(bev);
len = evbuffer_get_length(src);
if (!partner) {
printf("Failed to connect with Partner\n");
evbuffer_drain(src, len);
return;
}
dst = bufferevent_get_output(partner);
evbuffer_add_buffer(dst, src);
if (evbuffer_get_length(dst) >= MAX_OUTPUT) {
bufferevent_setcb(partner, readcb, drained_writecb,
eventcb, bev);
bufferevent_setwatermark(partner, EV_WRITE, MAX_OUTPUT/2,
MAX_OUTPUT);
bufferevent_disable(bev, EV_READ);
}
}
static void
drained_writecb(struct bufferevent *bev, void *ctx)
{
struct bufferevent *partner = ctx;
bufferevent_setcb(bev, readcb, NULL, eventcb, partner);
bufferevent_setwatermark(bev, EV_WRITE, 0, 0);
if (partner)
bufferevent_enable(partner, EV_READ);
}
static void
close_on_finished_writecb(struct bufferevent *bev, void *ctx)
{
struct evbuffer *b = bufferevent_get_output(bev);
if (evbuffer_get_length(b) == 0) {
bufferevent_free(bev);
}
}
static void
eventcb(struct bufferevent *bev, short what, void *ctx)
{
struct bufferevent *partner = ctx;
if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
if (what & BEV_EVENT_ERROR) {
perror("connection error");
}
if (partner) {
readcb(bev, ctx);
if (evbuffer_get_length(
bufferevent_get_output(partner))) {
bufferevent_setcb(partner,
NULL, close_on_finished_writecb,
eventcb, NULL);
bufferevent_disable(partner, EV_READ);
} else {
bufferevent_free(partner);
}
}
bufferevent_free(bev);
}
}
static void
syntax(void)
{
fputs("Syntax:\n", stderr);
fputs(" mego-proxy <listen-on-addr> <connect-to-addr>\n", stderr);
fputs("Example:\n", stderr);
fputs(" mego-proxy 127.0.0.1:8888 1.2.3.4:80\n", stderr);
exit(1);
}
static void
accept_cb(struct evconnlistener *listener, evutil_socket_t fd,
struct sockaddr *a, int slen, void *p)
{
struct bufferevent *b_out, *b_in;
b_in = bufferevent_socket_new(base, fd,
BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
b_out = bufferevent_socket_new(base, -1,
BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
assert(b_in && b_out);
if (bufferevent_socket_connect(b_out,
(struct sockaddr*)&connect_to_addr, connect_to_addrlen)<0) {
perror("bufferevent_socket_connect");
bufferevent_free(b_out);
bufferevent_free(b_in);
return;
}
bufferevent_setcb(b_in, readcb, NULL, eventcb, b_out);
bufferevent_setcb(b_out, readcb, NULL, eventcb, b_in);
bufferevent_enable(b_in, EV_READ|EV_WRITE);
bufferevent_enable(b_out, EV_READ|EV_WRITE);
}
int
main(int argc, char **argv)
{
int i;
int socklen;
struct evconnlistener *listener;
if (argc < 3)
syntax();
for (i=1; i < argc; ++i) {
if (argv[i][0] == '-') {
syntax();
} else
break;
}
if (i+2 != argc)
syntax();
memset(&listen_on_addr, 0, sizeof(listen_on_addr));
socklen = sizeof(listen_on_addr);
if (evutil_parse_sockaddr_port(argv[i],
(struct sockaddr*)&listen_on_addr, &socklen)<0)
syntax();
memset(&connect_to_addr, 0, sizeof(connect_to_addr));
connect_to_addrlen = sizeof(connect_to_addr);
if (evutil_parse_sockaddr_port(argv[i+1],
(struct sockaddr*)&connect_to_addr, &connect_to_addrlen)<0)
syntax();
base = event_base_new();
if (!base) {
perror("event_base_new()");
return 1;
}
listener = evconnlistener_new_bind(base, accept_cb, NULL,
LEV_OPT_CLOSE_ON_FREE|LEV_OPT_CLOSE_ON_EXEC|LEV_OPT_REUSEABLE,
-1, (struct sockaddr*)&listen_on_addr, socklen);
event_base_dispatch(base);
evconnlistener_free(listener);
event_base_free(base);
return 0;
}