11.1 HelloWorld服务器
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#ifndef WIN32
#include <netinet/in.h>
# ifdef _XOPEN_SOURCE_EXTENDED
# include <arpa/inet.h>
# endif
#include <sys/socket.h>
#endif
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <event2/event.h>
static const char MESSAGE[] = "Hello, World!\n";
static const int PORT = 9995;
static void listener_cb(struct evconnlistener *, evutil_socket_t,
struct sockaddr *, int socklen, void *);
static void conn_writecb(struct bufferevent *, void *);
static void conn_eventcb(struct bufferevent *, short, void *);
static void signal_cb(evutil_socket_t, short, void *);
int
main(int argc, char **argv)
{
struct event_base *base;
struct evconnlistener *listener;
struct event *signal_event;
struct sockaddr_in sin;
#ifdef WIN32
WSADATA wsa_data;
WSAStartup(0x0201, &wsa_data);
#endif
base = event_base_new();
if (!base) {
fprintf(stderr, "Could not initialize libevent!\n");
return 1;
}
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,
(struct sockaddr*)&sin,
sizeof(sin));
if (!listener) {
fprintf(stderr, "Could not create a listener!\n");
return 1;
}
signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base);
if (!signal_event || event_add(signal_event, NULL)<0) {
fprintf(stderr, "Could not create/add a signal event!\n");
return 1;
}
event_base_dispatch(base);
evconnlistener_free(listener);
event_free(signal_event);
event_base_free(base);
printf("done\n");
return 0;
}
static void
listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
struct sockaddr *sa, int socklen, void *user_data)
{
struct event_base *base = user_data;
struct bufferevent *bev;
bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
if (!bev) {
fprintf(stderr, "Error constructing bufferevent!");
event_base_loopbreak(base);
return;
}
bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL);
bufferevent_enable(bev, EV_WRITE);
bufferevent_disable(bev, EV_READ);
bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
}
static void
conn_writecb(struct bufferevent *bev, void *user_data)
{
struct evbuffer *output = bufferevent_get_output(bev);
if (evbuffer_get_length(output) == 0) {
printf("flushed answer\n");
bufferevent_free(bev);
}
}
static void
conn_eventcb(struct bufferevent *bev, short events, void *user_data)
{
if (events & BEV_EVENT_EOF) {
printf("Connection closed.\n");
} else if (events & BEV_EVENT_ERROR) {
printf("Got an error on the connection: %s\n",
strerror(errno));/*XXX win32*/
}
/* None of the other events can happen here, since we haven't enabled
* timeouts */
bufferevent_free(bev);
}
static void
signal_cb(evutil_socket_t sig, short events, void *user_data)
{
struct event_base *base = user_data;
struct timeval delay = { 2, 0 };
printf("Caught an interrupt signal; exiting cleanly in two seconds.\n");
event_base_loopexit(base, &delay);
}
11.2 基于事件服务器
11.2.1 服务端
#include <event2/event-config.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/queue.h>
#include <unistd.h>
#include <sys/time.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <event.h>
static void
fifo_read(evutil_socket_t fd, short event, void *arg)
{
char buf[255];
int len;
struct event *ev = arg;
/* Reschedule this event */
event_add(ev, NULL);
fprintf(stderr, "fifo_read called with fd: %d, event: %d, arg: %p\n",
(int)fd, event, arg);
len = read(fd, buf, sizeof(buf) - 1);
if (len == -1) {
perror("read");
return;
} else if (len == 0) {
fprintf(stderr, "Connection closed\n");
return;
}
buf[len] = '\0';
fprintf(stdout, "Read: %s\n", buf);
}
int
main(int argc, char **argv)
{
struct event evfifo;
struct stat st;
const char *fifo = "event.fifo";
int socket;
if (lstat(fifo, &st) == 0) {
if ((st.st_mode & S_IFMT) == S_IFREG) {
errno = EEXIST;
perror("lstat");
exit(1);
}
}
unlink(fifo);
if (mkfifo(fifo, 0600) == -1) {
perror("mkfifo");
exit(1);
}
/* Linux pipes are broken, we need O_RDWR instead of O_RDONLY */
socket = open(fifo, O_RDONLY | O_NONBLOCK, 0);
if (socket == -1) {
perror("open");
exit(1);
}
fprintf(stderr, "Write data to %s\n", fifo);
/* Initalize the event library */
event_init();
/* Initalize one event */
event_set(&evfifo, socket, EV_READ, fifo_read, &evfifo);
/* Add it to the active events, without a timeout */
event_add(&evfifo, NULL);
event_dispatch();
return (0);
}
11.2.2 客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
int main(int argc, char *argv[])
{
int fd = 0;
char *str = "hello libevent!";
fd = open("event.fifo", O_RDWR);
if (fd < 0) {
perror("open error");
exit(1);
}
while (1) {
write(fd, str, strlen(str));
sleep(1);
}
close(fd);
return 0;
}
11.3 回显服务器
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
static void
echo_read_cb(struct bufferevent *bev, void *ctx)
{
/* This callback is invoked when there is data to read on bev. */
struct evbuffer *input = bufferevent_get_input(bev);
struct evbuffer *output = bufferevent_get_output(bev);
/* Copy all the data from the input buffer to the output buffer. */
evbuffer_add_buffer(output, input);
}
static void
echo_event_cb(struct bufferevent *bev, short events, void *ctx)
{
if (events & BEV_EVENT_ERROR)
perror("Error from bufferevent");
if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
bufferevent_free(bev);
}
}
static void
accept_conn_cb(struct evconnlistener *listener,
evutil_socket_t fd, struct sockaddr *address, int socklen,
void *ctx)
{
/* We got a new connection! Set up a bufferevent for it. */
struct event_base *base = evconnlistener_get_base(listener);
struct bufferevent *bev = bufferevent_socket_new(
base, fd, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, echo_read_cb, NULL, echo_event_cb, NULL);
bufferevent_enable(bev, EV_READ|EV_WRITE);
}
static void
accept_error_cb(struct evconnlistener *listener, void *ctx)
{
struct event_base *base = evconnlistener_get_base(listener);
int err = EVUTIL_SOCKET_ERROR();
fprintf(stderr, "Got an error %d (%s) on the listener. "
"Shutting down.\n", err, evutil_socket_error_to_string(err));
event_base_loopexit(base, NULL);
}
int
main(int argc, char **argv)
{
struct event_base *base;
struct evconnlistener *listener;
struct sockaddr_in sin;
int port = 9876;
if (argc > 1) {
port = atoi(argv[1]);
}
if (port<=0 || port>65535) {
puts("Invalid port");
return 1;
}
base = event_base_new();
if (!base) {
puts("Couldn't open event base");
return 1;
}
/* Clear the sockaddr before using it, in case there are extra
* platform-specific fields that can mess us up. */
memset(&sin, 0, sizeof(sin));
/* This is an INET address */
sin.sin_family = AF_INET;
/* Listen on 0.0.0.0 */
sin.sin_addr.s_addr = htonl(0);
/* Listen on the given port. */
sin.sin_port = htons(port);
listener = evconnlistener_new_bind(base, accept_conn_cb, NULL,
LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, -1,
(struct sockaddr*)&sin, sizeof(sin));
if (!listener) {
perror("Couldn't create listener");
return 1;
}
evconnlistener_set_error_cb(listener, accept_error_cb);
event_base_dispatch(base);
return 0;
}
11.4 http服务器
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> //for getopt, fork
#include <string.h> //for strcat
//for struct evkeyvalq
#include <sys/queue.h>
#include <event.h>
//for http
//#include <evhttp.h>
#include <event2/http.h>
#include <event2/http_struct.h>
#include <event2/http_compat.h>
#include <event2/util.h>
#include <signal.h>
#define MYHTTPD_SIGNATURE "myhttpd v 0.0.1"
//处理模块
void httpd_handler(struct evhttp_request *req, void *arg) {
char output[2048] = "\0";
char tmp[1024];
//获取客户端请求的URI(使用evhttp_request_uri或直接req->uri)
const char *uri;
uri = evhttp_request_uri(req);
sprintf(tmp, "uri=%s\n", uri);
strcat(output, tmp);
sprintf(tmp, "uri=%s\n", req->uri);
strcat(output, tmp);
//decoded uri
char *decoded_uri;
decoded_uri = evhttp_decode_uri(uri);
sprintf(tmp, "decoded_uri=%s\n", decoded_uri);
strcat(output, tmp);
//解析URI的参数(即GET方法的参数)
struct evkeyvalq params;
//将URL数据封装成key-value格式,q=value1, s=value2
evhttp_parse_query(decoded_uri, ¶ms);
//得到q所对应的value
sprintf(tmp, "q=%s\n", evhttp_find_header(¶ms, "q"));
strcat(output, tmp);
//得到s所对应的value
sprintf(tmp, "s=%s\n", evhttp_find_header(¶ms, "s"));
strcat(output, tmp);
free(decoded_uri);
//获取POST方法的数据
char *post_data = (char *) EVBUFFER_DATA(req->input_buffer);
sprintf(tmp, "post_data=%s\n", post_data);
strcat(output, tmp);
/*
具体的:可以根据GET/POST的参数执行相应操作,然后将结果输出
...
*/
/* 输出到客户端 */
//HTTP header
evhttp_add_header(req->output_headers, "Server", MYHTTPD_SIGNATURE);
evhttp_add_header(req->output_headers, "Content-Type", "text/plain; charset=UTF-8");
evhttp_add_header(req->output_headers, "Connection", "close");
//输出的内容
struct evbuffer *buf;
buf = evbuffer_new();
evbuffer_add_printf(buf, "It works!\n%s\n", output);
evhttp_send_reply(req, HTTP_OK, "OK", buf);
evbuffer_free(buf);
}
void show_help() {
char *help = "http://localhost:8080\n"
"-l <ip_addr> interface to listen on, default is 0.0.0.0\n"
"-p <num> port number to listen on, default is 1984\n"
"-d run as a deamon\n"
"-t <second> timeout for a http request, default is 120 seconds\n"
"-h print this help and exit\n"
"\n";
fprintf(stderr,"%s",help);
}
//当向进程发出SIGTERM/SIGHUP/SIGINT/SIGQUIT的时候,终止event的事件侦听循环
void signal_handler(int sig) {
switch (sig) {
case SIGTERM:
case SIGHUP:
case SIGQUIT:
case SIGINT:
event_loopbreak(); //终止侦听event_dispatch()的事件侦听循环,执行之后的代码
break;
}
}
int main(int argc, char *argv[]) {
//自定义信号处理函数
signal(SIGHUP, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
signal(SIGQUIT, signal_handler);
//默认参数
char *httpd_option_listen = "0.0.0.0";
int httpd_option_port = 8080;
int httpd_option_daemon = 0;
int httpd_option_timeout = 120; //in seconds
//获取参数
int c;
while ((c = getopt(argc, argv, "l:p:dt:h")) != -1) {
switch (c) {
case 'l' :
httpd_option_listen = optarg;
break;
case 'p' :
httpd_option_port = atoi(optarg);
break;
case 'd' :
httpd_option_daemon = 1;
break;
case 't' :
httpd_option_timeout = atoi(optarg);
break;
case 'h' :
default :
show_help();
exit(EXIT_SUCCESS);
}
}
//判断是否设置了-d,以daemon运行
if (httpd_option_daemon) {
pid_t pid;
pid = fork();
if (pid < 0) {
perror("fork failed");
exit(EXIT_FAILURE);
}
if (pid > 0) {
//生成子进程成功,退出父进程
exit(EXIT_SUCCESS);
}
}
/* 使用libevent创建HTTP Server */
//初始化event API
event_init();
//创建一个http server
struct evhttp *httpd;
httpd = evhttp_start(httpd_option_listen, httpd_option_port);
evhttp_set_timeout(httpd, httpd_option_timeout);
//指定generic callback
evhttp_set_gencb(httpd, httpd_handler, NULL);
//也可以为特定的URI指定callback
//evhttp_set_cb(httpd, "/", specific_handler, NULL);
//循环处理events
event_dispatch();
evhttp_free(httpd);
return 0;
}