基于libevent实现了一个简单的echoclient。之前在网上看到的都是echoserver。这里演示一下使用libevent进行客户端编程的基本步骤。
先看代码:
- #include "stdafx.h"
- #include "event2/event.h"
- #include "event2/util.h"
-
- #define ECHO_PORT 8888
- #define ECHO_SERVER "127.0.0.1"
-
- struct echo_context{
- struct event_base *base;
- struct event *event_write;
- struct event *event_read;
- const char * echo_contents;
- int echo_contents_len;
- int recved;
- };
-
- void write_cb(evutil_socket_t sock, short flags, void * args)
- {
- struct echo_context *ec = (struct echo_context *)args;
-
- int ret = send(sock, ec->echo_contents, ec->echo_contents_len, 0);
- printf("connected, write to echo server: %d\n", ret);
- event_add(ec->event_read, 0);
- }
-
- void read_cb(evutil_socket_t sock, short flags, void * args)
- {
- struct echo_context *ec = (struct echo_context *)args;
- char buf[128];
- int ret = recv(sock, buf, 128, 0);
-
- printf("read_cb, read %d bytes\n", ret);
- if(ret > 0)
- {
- ec->recved += ret;
- buf[ret] = 0;
- printf("recv:%s\n", buf);
- }
- else if(ret == 0)
- {
- printf("read_cb connection closed\n");
- event_base_loopexit(ec->base, NULL);
- return;
- }
- if(ec->recved < ec->echo_contents_len)
- {
- event_add(ec->event_read, 0);
- }
- }
-
- static evutil_socket_t make_tcp_socket()
- {
- int on = 1;
- evutil_socket_t sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
-
- evutil_make_socket_nonblocking(sock);
- #ifdef WIN32
- setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (const char *)&on, sizeof(on));
- #else
- setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on));
- #endif
-
- return sock;
- }
-
- static void echo_client(struct event_base *base)
- {
- evutil_socket_t sock = make_tcp_socket();
- struct sockaddr_in serverAddr;
- struct event * ev_write = 0;
- struct event * ev_read = 0;
- struct timeval tv={10, 0};
- struct echo_context *ec = (struct echo_context*)calloc(1, sizeof(struct echo_context));
-
- serverAddr.sin_family = AF_INET;
- serverAddr.sin_port = htons(ECHO_PORT);
- #ifdef WIN32
- serverAddr.sin_addr.S_un.S_addr = inet_addr(ECHO_SERVER);
- #else
- serverAddr.sin_addr.s_addr = inet_addr(ECHO_SERVER);
- #endif
- memset(serverAddr.sin_zero, 0x00, 8);
-
- connect(sock, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
-
- ev_write = event_new(base, sock, EV_WRITE, write_cb, (void*)ec);
- ev_read = event_new(base, sock, EV_READ , read_cb, (void*)ec);
-
- ec->event_write = ev_write;
- ec->event_read = ev_read;
- ec->base = base;
- ec->echo_contents = strdup("echo client tneilc ohce\n");
- ec->echo_contents_len = strlen(ec->echo_contents);
- ec->recved = 0;
-
- event_add(ev_write, &tv);
- }
-
- int _tmain(int argc, _TCHAR* argv[])
- {
- struct event_base * base = 0;
- #ifdef WIN32
- WORD wVersionRequested;
- WSADATA wsaData;
-
- wVersionRequested = MAKEWORD(2, 2);
-
- (void) WSAStartup(wVersionRequested, &wsaData);
- #endif
-
- base = event_base_new();
- echo_client(base);
- event_base_dispatch(base);
- event_base_free(base);
-
- return 0;
- }
代码是在windows上实现的。
使用libevent进行客户端编程的基本步骤,在《libevent实现http client》中已经提过,基本如下:
- 初始化event_base(后续要运行事件循环)
- 创建socket,设置为异步,连接server
- 创建写读写事件,先将写事件加入事件循环
- 在写事件回调中向server端发送请求并将读事件加入事件循环
- 在读事件回调中处理数据,并根据数据是否读取完毕决定是否继续添加读事件
结合本文的示例代码,通过event_base_new()创建event_base,在初始化客户端socket(echo_client)之后,调用event_base_dispatch运行事件循环。
echo_client函数初始化了socket,设置为非阻塞模式,调用connect连接到echoserver,然后添加一个写事件到事件循环中。当连接成功后,会触发写事件。这里将write_cb作为写事件的回调,在调用event_new()时传入。
write_cb()写入一个字符串,然后添加一个读事件,在读事件中读取echoserver的回应。
基本的过程就是这样子了。这是个简单的例子,更复杂的例子,基本流程也是一样的。