清单 2 给出一个更完整的示例,它构建一个非常简单的回显服务器:
清单 2. 构建简单的回显服务器
#include <event.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <unistd.h> #define SERVER_PORT 8080 int debug = 0; struct client { int fd; struct bufferevent *buf_ev; }; int setnonblock(int fd) { int flags; flags = fcntl(fd, F_GETFL); flags |= O_NONBLOCK; fcntl(fd, F_SETFL, flags); } void buf_read_callback(struct bufferevent *incoming, void *arg) { struct evbuffer *evreturn; char *req; req = evbuffer_readline(incoming->input); if (req == NULL) return; evreturn = evbuffer_new(); evbuffer_add_printf(evreturn,"You said %s\n",req); bufferevent_write_buffer(incoming,evreturn); evbuffer_free(evreturn); free(req); } void buf_write_callback(struct bufferevent *bev, void *arg) { } void buf_error_callback(struct bufferevent *bev, short what, void *arg) { struct client *client = (struct client *)arg; bufferevent_free(client->buf_ev); close(client->fd); free(client); } void accept_callback(int fd, short ev, void *arg) { int client_fd; struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); struct client *client; client_fd = accept(fd, (struct sockaddr *)&client_addr, &client_len); if (client_fd < 0) { warn("Client: accept() failed"); return; } setnonblock(client_fd); client = calloc(1, sizeof(*client)); if (client == NULL) err(1, "malloc failed"); client->fd = client_fd; client->buf_ev = bufferevent_new(client_fd, buf_read_callback, buf_write_callback, buf_error_callback, client); bufferevent_enable(client->buf_ev, EV_READ); } int main(int argc, char **argv) { int socketlisten; struct sockaddr_in addresslisten; struct event accept_event; int reuse = 1; event_init(); socketlisten = socket(AF_INET, SOCK_STREAM, 0); if (socketlisten < 0) { fprintf(stderr,"Failed to create listen socket"); return 1; } memset(&addresslisten, 0, sizeof(addresslisten)); addresslisten.sin_family = AF_INET; addresslisten.sin_addr.s_addr = INADDR_ANY; addresslisten.sin_port = htons(SERVER_PORT); if (bind(socketlisten, (struct sockaddr *)&addresslisten, sizeof(addresslisten)) < 0) { fprintf(stderr,"Failed to bind"); return 1; } if (listen(socketlisten, 5) < 0) { fprintf(stderr,"Failed to listen to socket"); return 1; } setsockopt(socketlisten, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); setnonblock(socketlisten); event_set(&accept_event, socketlisten, EV_READ|EV_PERSIST, accept_callback, NULL); event_add(&accept_event, NULL); event_dispatch(); close(socketlisten); return 0; }
下面讨论各个函数及其操作:
-
main()
:主函数创建用来监听连接的套接字,然后创建accept()
的回调函数以便通过事件处理函数处理每个连接。 -
accept_callback()
:当接受连接时,事件系统调用此函数。此函数接受到客户端的连接;添加客户端套接字信息和一个 bufferevent 结构;在事件结构中为客户端套接字上的读/写/错误事件添加回调函数;作为参数传递客户端结构(和嵌入的 eventbuffer 和客户端套接字)。每当对应的客户端套接字包含读、写或错误操作时,调用对应的回调函数。 -
buf_read_callback()
:当客户端套接字有要读的数据时调用它。作为回显服务,此函数把 "you said..." 写回客户端。套接字仍然打开,可以接受新请求。 -
buf_write_callback()
:当有要写的数据时调用它。在这个简单的服务中,不需要此函数,所以定义是空的。 -
buf_error_callback()
:当出现错误时调用它。这包括客户端中断连接。在出现错误的所有场景中,关闭客户端套接字,从事件列表中删除客户端套接字的事件条目,释放客户端结构的内存。 -
setnonblock()
:设置网络套接字以开放 I/O。
当客户端连接时,在事件队列中添加新事件以处理客户端连接;当客户端中断连接时删除事件。在幕后,libevent 处理网络套接字,识别需要服务的客户端,分别调用对应的函数。
为了构建这个应用程序,需要编译 C 源代码并添加 libevent 库:$ gcc -o basic basic.c -levent
。
从客户端的角度来看,这个服务器仅仅把发送给它的任何文本发送回来(见 清单 3)。
清单 3. 服务器把发送给它的文本发送回来
$ telnet localhost 8080 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. Hello! You said Hello!
这样的网络应用程序非常适合需要处理多个连接的大规模分布式部署,比如 IBM Cloud 系统。
很难通过简单的解决方案观察处理大量并发连接的情况和性能改进。可以使用嵌入的 HTTP 实现帮助了解可伸缩性。
FROM:
使用 libevent 和 libev 提高网络应用性能
http://www.ibm.com/developerworks/cn/aix/library/au-libev/=====================================================================
下面附上无注释的echo server源码,110行,多抄几遍,就能完全弄懂啦!更复杂的例子参见官方文档里面的【Example: A simpler ROT13 server with Libevent】
# include <stdlib.h>
# include <errno.h>
# include <assert.h>
# include <event2/event.h>
# include <event2/bufferevent.h>
# define LISTEN_PORT 9999
# define LISTEN_BACKLOG 32
void do_accept(evutil_socket_t listener, short event, void *arg);
void read_cb( struct bufferevent *bev, void *arg);
void error_cb( struct bufferevent *bev, short event, void *arg);
void write_cb( struct bufferevent *bev, void *arg);
int main( int argc, char *argv[])
{
int ret;
evutil_socket_t listener;
listener = socket(AF_INET, SOCK_STREAM, 0);
assert(listener > 0);
evutil_make_listen_socket_reuseable(listener);
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0;
sin.sin_port = htons(LISTEN_PORT);
if (bind(listener, ( struct sockaddr *)&sin, sizeof(sin)) < 0) {
perror("bind");
return 1;
}
if (listen(listener, LISTEN_BACKLOG) < 0) {
perror("listen");
return 1;
}
printf ("Listening...\n");
evutil_make_socket_nonblocking(listener);
struct event_base *base = event_base_new();
assert(base != NULL);
struct event *listen_event;
listen_event = event_new(base, listener, EV_READ|EV_PERSIST, do_accept, ( void*)base);
event_add(listen_event, NULL);
event_base_dispatch(base);
printf("The End.");
return 0;
}
void do_accept(evutil_socket_t listener, short event, void *arg)
{
struct event_base *base = ( struct event_base *)arg;
evutil_socket_t fd;
struct sockaddr_in sin;
socklen_t slen;
fd = accept(listener, ( struct sockaddr *)&sin, &slen);
if (fd < 0) {
perror("accept");
return;
}
if (fd > FD_SETSIZE) {
perror("fd > FD_SETSIZE\n");
return;
}
printf("ACCEPT: fd = %u\n", fd);
struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, read_cb, NULL, error_cb, arg);
bufferevent_enable(bev, EV_READ|EV_WRITE|EV_PERSIST);
}
void read_cb( struct bufferevent *bev, void *arg)
{
# define MAX_LINE 256
char line[MAX_LINE+1];
int n;
evutil_socket_t fd = bufferevent_getfd(bev);
while (n = bufferevent_read(bev, line, MAX_LINE), n > 0) {
line[n] = '\0';
printf("fd=%u, read line: %s\n", fd, line);
bufferevent_write(bev, line, n);
}
}
void write_cb( struct bufferevent *bev, void *arg) {}
void error_cb( struct bufferevent *bev, short event, void *arg)
{
evutil_socket_t fd = bufferevent_getfd(bev);
printf("fd = %u, ", fd);
if (event & BEV_EVENT_TIMEOUT) {
printf("Timed out\n"); // if bufferevent_set_timeouts() called
}
else if (event & BEV_EVENT_EOF) {
printf("connection closed\n");
}
else if (event & BEV_EVENT_ERROR) {
printf("some other error\n");
}
bufferevent_free(bev);
}
libevent入门
http://blog.csdn.net/funkri/article/details/9352955
=======================================================================================
- eventtest : eventtest.c
- gcc -Wall -g -levent -lpthread -o eventtest eventtest.c
- .PHONY : clean
- clean :
- rm eventtest -f
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <pthread.h>
-
- #include <sys/types.h>
- #include <sys/socket.h>
-
- #include <event.h>
-
-
- struct event_base *workerbase;
- int fd[2];
-
- static void
- thread_libevent_process(int fd, short which, void *arg)
- {
- int ret;
- char buf[128];
-
- ret = read(fd, buf, 128);
- if (ret > 0) {
- buf[ret] = '\0';
- printf("thread receive message : %s\n", buf);
- }
-
- sleep(1);
-
- write(fd, buf, ret);
-
-
- return;
- }
-
- static void *
- worker_thread(void *arg)
- {
- int fd = *(int *)arg;
-
- struct event ev;
-
- event_set(&ev, fd, EV_READ | EV_PERSIST, thread_libevent_process, NULL);
- event_base_set(workerbase, &ev);
- event_add(&ev, 0);
-
- event_base_loop(workerbase, 0);
-
-
- return NULL;
- }
-
- static void
- main_event_process(int fd, short which, void *arg)
- {
- int ret;
- char buf[128];
-
- ret = read(fd, buf, 128);
- if (ret > 0) {
- buf[ret]='\0';
- printf("main thread receive echo message : %s\n", buf);
- }
-
-
- return;
- }
-
- static void
- timeout_cb(int fdd, short event, void *arg)
- {
- struct timeval tv;
- struct event *timeout = arg;
-
- write(fd[0], "Hello world!", sizeof("Hello world!") - 1);
-
- evutil_timerclear(&tv);
- tv.tv_sec = 1;
- event_add(timeout, &tv);
- }
-
- int
- main (int argc, char *argv[])
- {
- int ret;
-
- pthread_t tid;
-
- struct event ev;
- struct event evtimeout;
- struct timeval tv;
-
- struct event_base *base;
-
- base = event_init();
- if (base == NULL) {
- perror("event_init( base )");
- return 1;
- }
-
- workerbase = event_init();
- if (workerbase == NULL) {
- perror("event_init( workerbase )");
- return 1;
- }
-
-
-
- ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, fd);
- if (ret == -1) {
- perror("socketpair() : ");
- return 1;
- }
-
-
- event_set(&ev, fd[0], EV_READ | EV_PERSIST, main_event_process, NULL);
- event_base_set(base, &ev);
- event_add(&ev, 0);
-
- evtimer_set(&evtimeout, timeout_cb, &evtimeout);
- event_base_set(base, &evtimeout);
- evutil_timerclear(&tv);
- tv.tv_sec = 1;
- event_add(&evtimeout, &tv);
-
- ret = pthread_create(&tid, NULL, worker_thread, (void *)&fd[1]);
- if (ret != 0) {
- perror("pthread_create()");
- return 1;
- }
-
-
- event_base_loop(base, 0);
-
-
- return 0;
- }
FROM: 用libevent多线程方式实现echo server
http://blog.chinaunix.net/uid-756931-id-353296.html
===============================================================
C语言之libevent和socket示例(一)
源码和注释:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <event.h> #include <sys/types.h> #include <sys/socket.h> #include <errno.h> #include <fcntl.h> //内部函数,只能被本文件中的函数调用 static short ListenPort = 8080; static long ListenAddr = INADDR_ANY;//任意地址,值就是0 static int MaxConnections = 1024; static int ServerSocket; //创建event static struct event ServerEvent; //将一个socket设置成非阻塞模式 //不论什么平台编写网络程序,都应该使用NONBLOCK socket的方式。这样可以保证你的程序至少不会在recv/send/accept/connect这些操作上发生block从而将整个网络服务都停下来 int SetNonblock(int fd) { int flags; //fcntl()用来操作文件描述符的一些特性 if ((flags = fcntl(fd, F_GETFL)) == -1) { return -1; } if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { return -1; } return 0; } //这个函数当客户端的socket可读时由libevent调用 void ServerRead(int fd, short ev, void *arg) { struct client *client = (struct client *)arg; u_char buf[8196]; int len, wlen; //会把参数fd 所指的文件传送count个字节到buf指针所指的内存中 len = read(fd, buf, sizeof(buf)); if (len == 0) { /* 客户端断开连接,在这里移除读事件并且释放客户数据结构 */ printf("disconnected\n"); close(fd); event_del(&ServerEvent); free(client); return; } else if (len < 0) { /* 出现了其它的错误,在这里关闭socket,移除事件并且释放客户数据结构 */ printf("socket fail %s\n", strerror(errno)); close(fd); event_del(&ServerEvent); free(client); return; } /* 为了简便,我们直接将数据写回到客户端。通常我们不能在非阻塞的应用程序中这么做, 我们应该将数据放到队列中,等待可写事件的时候再写回客户端。 如果使用多个终端进行socket连接会出现错误socket fail Bad file descriptor */ wlen = write(fd, buf, len); if (wlen < len) { printf("not all data write back to client\n"); } } /* 当有一个连接请求准备被接受时,这个函数将被libevent调用并传递给三个变量: int fd:触发事件的文件描述符. short event:触发事件的类型EV_TIMEOUT,EV_SIGNAL, EV_READ, or EV_WRITE. void* :由arg参数指定的变量. */ void ServerAccept(int fd, short ev, void *arg) { int cfd; struct sockaddr_in addr; socklen_t addrlen = sizeof(addr); int yes = 1; int retval; //将从连接请求队列中获得连接信息,创建新的套接字,并返回该套接字的文件描述符。 //新创建的套接字用于服务器与客户机的通信,而原来的套接字仍然处于监听状态。 //该函数的第一个参数指定处于监听状态的流套接字 cfd = accept(fd, (struct sockaddr *)&addr, &addrlen); if (cfd == -1) { printf("accept(): can not accept client connection"); return; } if (SetNonblock(cfd) == -1) { close(cfd); return; } //设置与某个套接字关联的选项 //参数二 IPPROTO_TCP:TCP选项 //参数三 TCP_NODELAY 不使用Nagle算法 选择立即发送数据而不是等待产生更多的数据然后再一次发送 // 更多参数TCP_NODELAY 和 TCP_CORK //参数四 新选项TCP_NODELAY的值 if (setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) { printf("setsockopt(): TCP_NODELAY %s\n", strerror(errno)); close(cfd); return; } event_set(&ServerEvent, cfd, EV_READ | EV_PERSIST, ServerRead, NULL); event_add(&ServerEvent, NULL); printf("Accepted connection from %s\n", inet_ntoa(addr.sin_addr)); } int NewSocket(void) { struct sockaddr_in sa; //socket函数来创建一个能够进行网络通信的套接字。 //第一个参数指定应用程序使用的通信协议的协议族,对于TCP/IP协议族,该参数置AF_INET; //第二个参数指定要创建的套接字类型 //流套接字类型为SOCK_STREAM、数据报套接字类型为SOCK_DGRAM、原始套接字SOCK_RAW //第三个参数指定应用程序所使用的通信协议。 ServerSocket = socket(AF_INET, SOCK_STREAM, 0); if (ServerSocket == -1) { printf("socket(): can not create server socket\n"); return -1; } if (SetNonblock(ServerSocket) == -1) { return -1; } //清空内存数据 memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; //htons将一个无符号短整型数值转换为网络字节序 sa.sin_port = htons(ListenPort); //htonl将主机的无符号长整形数转换成网络字节顺序 sa.sin_addr.s_addr = htonl(ListenAddr); //(struct sockaddr*)&sa将sa强制转换为sockaddr类型的指针 /*struct sockaddr 数据结构用做bind、connect、recvfrom、sendto等函数的参数,指明地址信息。 但一般编程中并不直接针对此数据结构操作,而是使用另一个与sockaddr等价的数据结构 struct sockaddr_in sockaddr_in和sockaddr是并列的结构,指向sockaddr_in的结构体的指针也可以指向 sockadd的结构体,并代替它。也就是说,你可以使用sockaddr_in建立你所需要的信息, 在最后用进行类型转换就可以了 */ //bind函数用于将套接字绑定到一个已知的地址上 if (bind(ServerSocket, (struct sockaddr*)&sa, sizeof(sa)) == -1) { close(ServerSocket); printf("bind(): can not bind server socket"); return -1; } //执行listen 之后套接字进入被动模式 //MaxConnections 连接请求队列的最大长度,队列满了以后,将拒绝新的连接请求 if (listen(ServerSocket, MaxConnections) == -1) { printf("listen(): can not listen server socket"); close(ServerSocket); return -1; } /* event_set的参数: + 参数1: 为要创建的event + 参数2: file descriptor,创建纯计时器可以设置其为-1,即宏evtimer_set定义的那样 + 参数3: 设置event种类,常用的EV_READ, EV_WRITE, EV_PERSIST, EV_SIGNAL, EV_TIMEOUT,纯计时器设置该参数为0 + 参数4: event被激活之后触发的callback函数 + 参数5: 传递给callback函数的参数 备注: 如果初始化event的时候设置其为persistent的(设置了EV_PERSIST), 则使用event_add将其添加到侦听事件集合后(pending状态), 该event会持续保持pending状态,即该event可以无限次参加libevent的事件侦听。 每当其被激活触发callback函数执行之后,该event自动从active转回为pending状态, 继续参加libevent的侦听(当激活条件满足,又可以继续执行其callback)。 除非在代码中使用event_del()函数将该event从libevent的侦听事件集合中删除。 如果不通过设置EV_PERSIST使得event是persistent的,需要在event的callback中再次调用event_add (即在每次pending变为active之后,在callback中再将其设置为pending) */ event_set(&ServerEvent, ServerSocket, EV_READ | EV_PERSIST, ServerAccept, NULL); //将event添加到libevent侦听的事件集中 if (event_add(&ServerEvent, 0) == -1) { printf("event_add(): can not add accept event into libevent"); close(ServerSocket); return -1; } return 0; } int main(int argc, char *argv[]) { int retval; //初始化event base 使用默认的全局current_base event_init(); retval = NewSocket(); if (retval == -1) { exit(-1); } //event_dispatch() 启动事件队列系统,开始监听(并接受)请求 event_dispatch(); return 0; }
编译方法:
先安装libevent
wget http://httpsqs.googlecode.com/files/libevent-2.0.12-stable.tar.gz tar zxvf libevent-2.0.12-stable.tar.gz cd libevent-2.0.12-stable/ ./configure --prefix=/usr/local/libevent-2.0.12-stable/ make make install cd ../
编译
gcc socket.c -o socket -Wl,-rpath,/usr/local/libevent-2.0.12-stable/lib/ -L/usr/local/libevent-2.0.12-stable/lib/ -levent -I/usr/local/libevent-2.0.12-stable/include/
测试:
终端执行 ./socket 另外开一个终端 telnet 127.0.0.1 8080
socket.c (7.34 K, 下载次数:191, 上传时间:2012-01-27 00:08)
FROM: http://blog.linuxphp.org/archives/1482/