
[原]Libevent客户端例子
时间 -- :: luotuo44的专栏
原文 http://blog.****.net/luotuo44/article/details/34416429
主题 libevent Socket
网上关于Libevent的例子大多数都是服务器端的例子,我写一个客户端例子和大家分享。不多说了,上代码。 #include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<errno.h>
#include<unistd.h> #include<stdio.h>
#include<string.h>
#include<stdlib.h> #include<event.h>
#include<event2/bufferevent.h>
#include<event2/buffer.h>
#include<event2/util.h> int tcp_connect_server(const char* server_ip, int port); void cmd_msg_cb(int fd, short events, void* arg);
void server_msg_cb(struct bufferevent* bev, void* arg);
void error_cb(struct bufferevent *bev, short event, void *arg); int main(int argc, char** argv)
{
if( argc < )
{
printf("please input 2 parameter\n");
return -;
} //两个参数依次是服务器端的IP地址、端口号
int sockfd = tcp_connect_server(argv[], atoi(argv[]));
if( sockfd == -)
{
perror("tcp_connect error ");
return -;
} printf("connect to server successful\n"); struct event_base* base = event_base_new(); struct bufferevent* bev = bufferevent_socket_new(base, sockfd,
BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, server_msg_cb, NULL, error_cb, NULL);
bufferevent_enable(bev, EV_READ | EV_PERSIST); //监听终端输入事件
struct event* ev_cmd = event_new(base, STDIN_FILENO,
EV_READ | EV_PERSIST, cmd_msg_cb,
(void*)bev); event_add(ev_cmd, NULL); event_base_dispatch(base); printf("finished \n");
return ;
} void cmd_msg_cb(int fd, short events, void* arg)
{
char msg[]; int ret = read(fd, msg, sizeof(msg));
if( ret < )
{
perror("read fail ");
exit();
} struct bufferevent* bev = (struct bufferevent*)arg; //把终端的消息发送给服务器端
bufferevent_write(bev, msg, ret);
} void server_msg_cb(struct bufferevent* bev, void* arg)
{
char msg[]; size_t len = bufferevent_read(bev, msg, sizeof(msg));
msg[len] = '\0'; printf("recv %s from server\n", msg);
} void error_cb(struct bufferevent *bev, short event, void *arg)
{ if (event & BEV_EVENT_EOF)
printf("connection closed\n");
else if (event & BEV_EVENT_ERROR)
printf("some other error\n"); //这将自动close套接字和free读写缓冲区
bufferevent_free(bev);
} typedef struct sockaddr SA;
int tcp_connect_server(const char* server_ip, int port)
{
int sockfd, status, save_errno;
struct sockaddr_in server_addr; memset(&server_addr, , sizeof(server_addr) ); server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
status = inet_aton(server_ip, &server_addr.sin_addr); if( status == ) //the server_ip is not valid value
{
errno = EINVAL;
return -;
} sockfd = ::socket(PF_INET, SOCK_STREAM, );
if( sockfd == - )
return sockfd; status = ::connect(sockfd, (SA*)&server_addr, sizeof(server_addr) ); if( status == - )
{
save_errno = errno;
::close(sockfd);
errno = save_errno; //the close may be error
return -;
} evutil_make_socket_nonblocking(sockfd); return sockfd;
}
在main函数里面,要先connect上服务器,然后用套接字作为参数,新建一个struct buffereven。 我试过像下面这样写 struct bufferevent* bev = bufferevent_socket_new(base, -,
BEV_OPT_CLOSE_ON_FREE);
if( bev == NULL)
{
printf("bufferevent_socket_new fail\n");
return -;
} struct sockaddr_in server_addr; memset(&server_addr, , sizeof(server_addr) ); server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[]));
int status = inet_aton(argv[], &server_addr.sin_addr); int sockfd = bufferevent_socket_connect(bev, (struct sockaddr *)&server_addr,
sizeof(server_addr));
即通过bufferevent_socket_connect函数来获取一个连接上服务器的套接字。但总会一连上服务器端,sockfd就关闭了。 下面是对应服务器端的例子。 #include<stdio.h>
#include<string.h>
#include<errno.h> #include<event.h>
#include<event2/bufferevent.h> void accept_cb(int fd, short events, void* arg);
void socket_read_cb(bufferevent* bev, void* arg);
void error_cb(struct bufferevent *bev, short event, void *arg);
int tcp_server_init(int port, int listen_num); int main(int argc, char** argv)
{ int listener = tcp_server_init(, );
if( listener == - )
{
perror(" tcp_server_init error ");
return -;
} struct event_base* base = event_base_new(); //添加监听客户端请求连接事件
struct event* ev_listen = event_new(base, listener, EV_READ | EV_PERSIST,
accept_cb, base);
event_add(ev_listen, NULL); event_base_dispatch(base); return ;
} void accept_cb(int fd, short events, void* arg)
{
evutil_socket_t sockfd; struct sockaddr_in client;
socklen_t len; sockfd = ::accept(fd, (struct sockaddr*)&client, &len );
evutil_make_socket_nonblocking(sockfd); printf("accept a client %d\n", sockfd); struct event_base* base = (event_base*)arg; bufferevent* bev = bufferevent_socket_new(base, sockfd, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, socket_read_cb, NULL, error_cb, arg); bufferevent_enable(bev, EV_READ | EV_PERSIST);
} void socket_read_cb(bufferevent* bev, void* arg)
{
char msg[]; size_t len = bufferevent_read(bev, msg, sizeof(msg)); msg[len] = '\0';
printf("recv the client msg: %s", msg); char reply_msg[] = "I have recvieced the msg: "; strcat(reply_msg + strlen(reply_msg), msg);
bufferevent_write(bev, reply_msg, strlen(reply_msg));
} void error_cb(struct bufferevent *bev, short event, void *arg)
{ if (event & BEV_EVENT_EOF)
printf("connection closed\n");
else if (event & BEV_EVENT_ERROR)
printf("some other error\n"); //这将自动close套接字和free读写缓冲区
bufferevent_free(bev);
} typedef struct sockaddr SA;
int tcp_server_init(int port, int listen_num)
{
int errno_save;
evutil_socket_t listener; listener = ::socket(AF_INET, SOCK_STREAM, );
if( listener == - )
return -; //允许多次绑定同一个地址。要用在socket和bind之间
evutil_make_listen_socket_reuseable(listener); struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = ;
sin.sin_port = htons(port); if( ::bind(listener, (SA*)&sin, sizeof(sin)) < )
goto error; if( ::listen(listener, listen_num) < )
goto error; //跨平台统一接口,将套接字设置为非阻塞状态
evutil_make_socket_nonblocking(listener); return listener; error:
errno_save = errno;
evutil_closesocket(listener);
errno = errno_save; return -;
}
参考资料:
http://www.tuicool.com/articles/uuQvai
http://blog.****.net/nanjunxiao/article/details/9066077
http://blog.****.net/lulyon/article/details/51259109
http://cnodejs.org/topic/506d524c01d0b80148be69d8
https://www.zhihu.com/question/20278441