写了一个简单的例子,把libevent中的bufferevent网络收发服务和protobuf里面的序列反序列结合起来。

protobuf文件message.proto:

message PMessage {
required int32 id = 1;
optional int32 num = 2;
optional string str = 3;
}

生成接口命令:

protoc -I=proto --cpp_out=src proto/message.proto

服务器端 lserver.cc:

#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h> #include <stdio.h>
#include <string.h> #include <event.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <event2/thread.h> #include "message.pb.h" using namespace std; void listener_cb(evconnlistener *listener, evutil_socket_t fd,
sockaddr *sock, int socklen, void *arg); void socket_read_cb(bufferevent *bev, void *arg); void socket_event_cb(bufferevent *bev, short events, void *arg); int main(int argc, char **argv) {
sockaddr_in sin;
memset(&sin, 0, sizeof(sockaddr_in)); sin.sin_family = AF_INET;
sin.sin_port = htons(8899); event_base *base = event_base_new();
evconnlistener *listener
= evconnlistener_new_bind(base,
listener_cb, base,
LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE,
10, (sockaddr*)&sin, sizeof(sockaddr_in)); event_base_dispatch(base); evconnlistener_free(listener);
event_base_free(base); } void listener_cb(evconnlistener *listener, evutil_socket_t fd,
sockaddr *sock, int socklen, void *arg) { printf("accept a client %d\n", fd);
event_base *base = (event_base *)arg; bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE); bufferevent_setcb(bev, socket_read_cb, NULL, socket_event_cb, NULL);
bufferevent_enable(bev, EV_READ|EV_PERSIST); } void socket_read_cb(bufferevent *bev, void *arg) {
char msg[4096];
size_t len = bufferevent_read(bev, msg, sizeof(msg)-1);
msg[len] = '\0'; PMessage pmsg;
pmsg.ParseFromArray((const void*)msg, len); printf("Server read the data:%i, %i, %s\n", pmsg.id(), pmsg.num(), pmsg.str().c_str()); pmsg.set_str("I have read your data.");
string sendbuf;
pmsg.SerializeToString(&sendbuf); bufferevent_write(bev, sendbuf.c_str(), sendbuf.length()); } void socket_event_cb(bufferevent *bev, short events, void *arg) {
if (events & BEV_EVENT_EOF) {
printf("connection close\n");
}
else if (events & BEV_EVENT_ERROR) {
printf("some other error\n");
} bufferevent_free(bev);
}

客户端lclient.cc

#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> #include "message.pb.h" using namespace std; void cmd_msg_cb(int fd, short events, void *arg); void server_msg_cb(bufferevent *bev, void *arg); void event_cb(bufferevent *bev, short event, void *arg); static int gid = 1; int main(int argc, char **argv) {
if (argc < 3) {
printf("please input IP and port\n");
return 1;
} event_base *base = event_base_new();
bufferevent *bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); event *ev_cmd = event_new(base, STDIN_FILENO,
EV_READ|EV_PERSIST,
cmd_msg_cb, (void *)bev);
event_add(ev_cmd, NULL); sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1], &server_addr.sin_addr); bufferevent_socket_connect(bev, (sockaddr*)&server_addr, sizeof(server_addr)); bufferevent_setcb(bev, server_msg_cb, NULL, event_cb, (void*)ev_cmd);
bufferevent_enable(bev, EV_READ|EV_PERSIST); event_base_dispatch(base); printf("Finish\n");
return 0; } void cmd_msg_cb(int fd, short events, void *arg) {
char msg[1024];
int ret = read(fd, msg, sizeof(msg));
if (ret < 0) {
perror("read error.\n");
exit(1);
} // protobuf
PMessage pmsg;
pmsg.set_id(gid++);
pmsg.set_num(rand());
pmsg.set_str(msg); string sendbuf;
pmsg.SerializeToString(&sendbuf); // processing network transfer
bufferevent *bev = (bufferevent *)arg;
bufferevent_write(bev, sendbuf.c_str(), sendbuf.length());
} void server_msg_cb(bufferevent *bev, void *arg) {
char msg[1024]; size_t len = bufferevent_read(bev, msg, sizeof(msg)-1);
msg[len] = '\0'; PMessage pmsg;
pmsg.ParseFromArray((const void*)msg, len); printf("Recv %d, %d, %s from server.\n", pmsg.id(), pmsg.num(), pmsg.str().c_str());
} void event_cb(bufferevent *bev, short eventid, void *arg) {
if (eventid & BEV_EVENT_EOF) {
printf("Connection closed.\n");
}
else if (eventid & BEV_EVENT_ERROR) {
printf("Some other error.\n");
}
else if (eventid & BEV_EVENT_CONNECTED) {
printf("Client has successfully connected.\n");
return;
} bufferevent_free(bev);
event *ev = (event *)arg;
event_free(ev);
}

服务器端和客户端共用的Makefile:

CXX=/opt/compiler/gcc-4.8.2/bin/g++

INCPATH= \
/home/work/.jumbo/include/ DEP_LDFLAGS= \
-L/home/work/.jumbo/lib/ DEP_LDLIBS= \
-levent \
-lprotobuf \
-lpthread TARGET= lserver lclient all : $(TARGET) lserver : lserver.cc message.pb.cc
$(CXX) -o $@ $^ -I$(INCPATH) $(DEP_LDFLAGS) $(DEP_LDLIBS) lclient : lclient.cc message.pb.cc
$(CXX) -o $@ $^ -I$(INCPATH) $(DEP_LDFLAGS) $(DEP_LDLIBS) .PHONY : all clean clean :
rm -rf $(TARGET)

服务器命令及输出:

src]$ ./lserver
accept a client 7
Server read the data:1, 1804289383, aaaaaaaaaaaaaaaaaaaaaaaaa Server read the data:2, 846930886, aa Server read the data:3, 1681692777, bb Server read the data:4, 1714636915, abcdefg Server read the data:5, 1957747793, connection close
accept a client 7
Server read the data:1, 1804289383, 2aa Server read the data:2, 846930886, 2bb Server read the data:3, 1681692777, 111111111111111111111222222222222222222222223333333333333333333333333 Server read the data:4, 1714636915, ^C

客户端命令及输出:

src]$ ./lclient localhost 8899
Client has successfully connected.
aaaaaaaaaaaaaaaaaaaaaaaaa
Recv 1, 1804289383, I have read your data. from server.
aa
Recv 2, 846930886, I have read your data. from server.
bb
Recv 3, 1681692777, I have read your data. from server.
abcdefg
Recv 4, 1714636915, I have read your data. from server. Recv 5, 1957747793, I have read your data. from server.
^C
[src]$ ./lclient localhost 8899
Client has successfully connected.
2aa
Recv 1, 1804289383, I have read your data. from server.
2bb
Recv 2, 846930886, I have read your data. from server.
111111111111111111111222222222222222222222223333333333333333333333333
Recv 3, 1681692777, I have read your data. from server. Recv 4, 1714636915, I have read your data. from server.
Connection closed.
Finish

注意:

1. 先后开了两个客户端。客户端退出,不影响服务器端。但是服务器端退出会让客户端一起退出,因为客户端在收到网络error信号处理的最后,会free掉从命令行读数据的监听event,这样eventbase就不会再有event需要监听了,所以会退出。

2. 开始在命令行输入的时候,在char数组中没有添加'\0',传输时会造成如下错误。

[libprotobuf ERROR google/protobuf/wire_format.cc:1053] String field contains invalid UTF-8 data when serializing a protocol buffer. Use the 'bytes' type if you intend to send raw bytes.

根据读入函数返回的长度,设置'\0'即可避免这个错误。

融合libevent和protobuf的更多相关文章

  1. RPC的学习 & gprotobuf 和 thrift的比较

    参考 http://blog.csdn.net/pi9nc/article/details/17336663 集成libevent,google protobuf的RPC框架 RPC(Remote P ...

  2. 教你成为全栈工程师(Full Stack Developer) 〇-什么是全栈工程师

    作为一个编码12年的工程师老将,讲述整段工程师的往事,顺便把知识都泄露出去,希望读者能少走一些弯路. 这段往事包括:从不会动的静态网页到最流行的网站开发.实现自己的博客网站.在云里雾里的云中搜索.大数 ...

  3. libevent源码深度剖析

    原文地址: http://blog.csdn.net/sparkliang/article/details/4957667 第一章 1,前言 Libevent是一个轻量级的开源高性能网络库,使用者众多 ...

  4. Libevent浅析

    前段时间对Libevent的源码进行了阅读,现整理如下: 介绍 libevent是一个轻量级的开源高性能事件驱动网络库,是一个典型的Reactor模型.其主要特点有事件驱动,高性能,跨平台,统一事件源 ...

  5. protobuf使用详解

    https://blog.csdn.net/skh2015java/article/details/78404235 原文地址:http://blog.csdn.net/lyjshen/article ...

  6. Libevent学习之SocketPair实现

    Libevent设计的精化之一在于把Timer事件.Signal事件和IO事件统一集成在一个Reactor中,以统一的方式去处理这三种不同的事件,更确切的说是把Timer事件和Signal事件融合到了 ...

  7. libevent源码剖析

    libevent是一个使用C语言编写的,轻量级的开源高性能网络库,使用者很多,研究者也很多.由于代码简洁,设计思想简明巧妙,因此很适合用来学习,提升自己C语言的能力. libevent有这样显著地几个 ...

  8. libevent(了解)

    1 前言 Libevent是一个轻量级的开源高性能网络库,使用者众多,研究者更甚,相关文章也不少.写这一系列文章的用意在于,一则分享心得:二则对libevent代码和设计思想做系统的.更深层次的分析, ...

  9. libevent源码深度剖析九

    libevent源码深度剖析九 ——集成定时器事件 张亮 现在再来详细分析libevent中I/O事件和Timer事件的集成,与Signal相比,Timer事件的集成会直观和简单很多.Libevent ...

随机推荐

  1. POJ 2019 Cornfields [二维RMQ]

    题目传送门 Cornfields Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 7963   Accepted: 3822 ...

  2. Maven入门使用(一)

    一.什么是maven 一般认为maven是项目构建工具+依赖管理工具+项目信息管理工具. maven是一个强大的构建工具,能够帮助我们自动化构建过程. 清理.编译.测试.生成报告.打包.部署都是可以通 ...

  3. Mutex 的正确打开方式

    在使用 Mutex 在给线程/进程间加锁时,需要注意的问题. 1 AbandonedMutexException 在使用 mutex.WaitOne 时,可能抛出异常 AbandonedMutexEx ...

  4. hdu 1596 find the safest road (dijkstra)

    Problem Description XX星球有很多城市,每个城市之间有一条或多条飞行通道,但是并不是所有的路都是很安全的,每一条路有一个安全系数s,s是在 0 和 1 间的实数(包括0,1),一条 ...

  5. SRPG Studio 教程(一) 创建游戏及引用素材

    儿时玩红白机的时候,火纹和机器人大战这类战棋类的游戏就是博主的最爱,恰逢最近steam上上架了一款SRPG Studio用于制作火纹,趁这个机会学习一下,顺便记录下来. 秉承着一个程序猿的自我修养,以 ...

  6. python 学习笔记 - Queue & Pipes,进程间通讯

    上面写了Python如何创建多个进程,但是前面文章中创建的进程都是哑巴和聋子,自己顾自己执行,不会相互交流.那么如何让进程间相互说说话呢?Python为我们提供了一个函数multiprocessing ...

  7. 【贪心】BZOJ3668-[NOI2014]起床困难综合症

    [题目大意] 给定n次操作(与,或,异或),在0~m中选择一个数,使这个数经过n次操作后得到的值最大. [思路] 水题orz 枚举这个数每一位的取值是0还是1,然后根据它经过n次操作后的结果判断: ( ...

  8. 3524: [Poi2014]Couriers -- 主席树

    3524: [Poi2014]Couriers Time Limit: 20 Sec  Memory Limit: 256 MB Description 给一个长度为n的序列a.1≤a[i]≤n.m组 ...

  9. USACO 2017 FEB Platinum mincross 可持久化线段树

    题意 上下有两个位置分别对应的序列A.B,长度为n,两序列为n的一个排列.当Ai == Bj时,上下会连一条边.你可以选择序列A或者序列B进行旋转任意K步,如 3 4 1 5 2 旋转两步为 5 2 ...

  10. VK Cup 2016 - Qualification Round 2 B. Making Genome in Berland 水题

    B. Making Genome in Berland 题目连接: http://www.codeforces.com/contest/638/problem/B Description Berlan ...