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

protobuf文件message.proto:

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

生成接口命令:

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

服务器端 lserver.cc:

  1. #include <netinet/in.h>
  2. #include <sys/socket.h>
  3. #include <unistd.h>
  4.  
  5. #include <stdio.h>
  6. #include <string.h>
  7.  
  8. #include <event.h>
  9. #include <event2/listener.h>
  10. #include <event2/bufferevent.h>
  11. #include <event2/thread.h>
  12.  
  13. #include "message.pb.h"
  14.  
  15. using namespace std;
  16.  
  17. void listener_cb(evconnlistener *listener, evutil_socket_t fd,
  18. sockaddr *sock, int socklen, void *arg);
  19.  
  20. void socket_read_cb(bufferevent *bev, void *arg);
  21.  
  22. void socket_event_cb(bufferevent *bev, short events, void *arg);
  23.  
  24. int main(int argc, char **argv) {
  25. sockaddr_in sin;
  26. memset(&sin, 0, sizeof(sockaddr_in));
  27.  
  28. sin.sin_family = AF_INET;
  29. sin.sin_port = htons(8899);
  30.  
  31. event_base *base = event_base_new();
  32. evconnlistener *listener
  33. = evconnlistener_new_bind(base,
  34. listener_cb, base,
  35. LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE,
  36. 10, (sockaddr*)&sin, sizeof(sockaddr_in));
  37.  
  38. event_base_dispatch(base);
  39.  
  40. evconnlistener_free(listener);
  41. event_base_free(base);
  42.  
  43. }
  44.  
  45. void listener_cb(evconnlistener *listener, evutil_socket_t fd,
  46. sockaddr *sock, int socklen, void *arg) {
  47.  
  48. printf("accept a client %d\n", fd);
  49. event_base *base = (event_base *)arg;
  50.  
  51. bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
  52.  
  53. bufferevent_setcb(bev, socket_read_cb, NULL, socket_event_cb, NULL);
  54. bufferevent_enable(bev, EV_READ|EV_PERSIST);
  55.  
  56. }
  57.  
  58. void socket_read_cb(bufferevent *bev, void *arg) {
  59. char msg[4096];
  60. size_t len = bufferevent_read(bev, msg, sizeof(msg)-1);
  61. msg[len] = '\0';
  62.  
  63. PMessage pmsg;
  64. pmsg.ParseFromArray((const void*)msg, len);
  65.  
  66. printf("Server read the data:%i, %i, %s\n", pmsg.id(), pmsg.num(), pmsg.str().c_str());
  67.  
  68. pmsg.set_str("I have read your data.");
  69. string sendbuf;
  70. pmsg.SerializeToString(&sendbuf);
  71.  
  72. bufferevent_write(bev, sendbuf.c_str(), sendbuf.length());
  73.  
  74. }
  75.  
  76. void socket_event_cb(bufferevent *bev, short events, void *arg) {
  77. if (events & BEV_EVENT_EOF) {
  78. printf("connection close\n");
  79. }
  80. else if (events & BEV_EVENT_ERROR) {
  81. printf("some other error\n");
  82. }
  83.  
  84. bufferevent_free(bev);
  85. }

客户端lclient.cc

  1. #include <sys/types.h>
  2. #include <sys/socket.h>
  3. #include <netinet/in.h>
  4. #include <arpa/inet.h>
  5. #include <errno.h>
  6. #include <unistd.h>
  7.  
  8. #include <stdio.h>
  9. #include <string.h>
  10. #include <stdlib.h>
  11.  
  12. #include <event.h>
  13. #include <event2/bufferevent.h>
  14. #include <event2/buffer.h>
  15. #include <event2/util.h>
  16.  
  17. #include "message.pb.h"
  18.  
  19. using namespace std;
  20.  
  21. void cmd_msg_cb(int fd, short events, void *arg);
  22.  
  23. void server_msg_cb(bufferevent *bev, void *arg);
  24.  
  25. void event_cb(bufferevent *bev, short event, void *arg);
  26.  
  27. static int gid = 1;
  28.  
  29. int main(int argc, char **argv) {
  30. if (argc < 3) {
  31. printf("please input IP and port\n");
  32. return 1;
  33. }
  34.  
  35. event_base *base = event_base_new();
  36. bufferevent *bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
  37.  
  38. event *ev_cmd = event_new(base, STDIN_FILENO,
  39. EV_READ|EV_PERSIST,
  40. cmd_msg_cb, (void *)bev);
  41. event_add(ev_cmd, NULL);
  42.  
  43. sockaddr_in server_addr;
  44. memset(&server_addr, 0, sizeof(server_addr));
  45.  
  46. server_addr.sin_family = AF_INET;
  47. server_addr.sin_port = htons(atoi(argv[2]));
  48. inet_aton(argv[1], &server_addr.sin_addr);
  49.  
  50. bufferevent_socket_connect(bev, (sockaddr*)&server_addr, sizeof(server_addr));
  51.  
  52. bufferevent_setcb(bev, server_msg_cb, NULL, event_cb, (void*)ev_cmd);
  53. bufferevent_enable(bev, EV_READ|EV_PERSIST);
  54.  
  55. event_base_dispatch(base);
  56.  
  57. printf("Finish\n");
  58. return 0;
  59.  
  60. }
  61.  
  62. void cmd_msg_cb(int fd, short events, void *arg) {
  63. char msg[1024];
  64. int ret = read(fd, msg, sizeof(msg));
  65. if (ret < 0) {
  66. perror("read error.\n");
  67. exit(1);
  68. }
  69.  
  70. // protobuf
  71. PMessage pmsg;
  72. pmsg.set_id(gid++);
  73. pmsg.set_num(rand());
  74. pmsg.set_str(msg);
  75.  
  76. string sendbuf;
  77. pmsg.SerializeToString(&sendbuf);
  78.  
  79. // processing network transfer
  80. bufferevent *bev = (bufferevent *)arg;
  81. bufferevent_write(bev, sendbuf.c_str(), sendbuf.length());
  82. }
  83.  
  84. void server_msg_cb(bufferevent *bev, void *arg) {
  85. char msg[1024];
  86.  
  87. size_t len = bufferevent_read(bev, msg, sizeof(msg)-1);
  88. msg[len] = '\0';
  89.  
  90. PMessage pmsg;
  91. pmsg.ParseFromArray((const void*)msg, len);
  92.  
  93. printf("Recv %d, %d, %s from server.\n", pmsg.id(), pmsg.num(), pmsg.str().c_str());
  94. }
  95.  
  96. void event_cb(bufferevent *bev, short eventid, void *arg) {
  97. if (eventid & BEV_EVENT_EOF) {
  98. printf("Connection closed.\n");
  99. }
  100. else if (eventid & BEV_EVENT_ERROR) {
  101. printf("Some other error.\n");
  102. }
  103. else if (eventid & BEV_EVENT_CONNECTED) {
  104. printf("Client has successfully connected.\n");
  105. return;
  106. }
  107.  
  108. bufferevent_free(bev);
  109. event *ev = (event *)arg;
  110. event_free(ev);
  111. }

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

  1. CXX=/opt/compiler/gcc-4.8.2/bin/g++
  2.  
  3. INCPATH= \
  4. /home/work/.jumbo/include/
  5.  
  6. DEP_LDFLAGS= \
  7. -L/home/work/.jumbo/lib/
  8.  
  9. DEP_LDLIBS= \
  10. -levent \
  11. -lprotobuf \
  12. -lpthread
  13.  
  14. TARGET= lserver lclient
  15.  
  16. all : $(TARGET)
  17.  
  18. lserver : lserver.cc message.pb.cc
  19. $(CXX) -o $@ $^ -I$(INCPATH) $(DEP_LDFLAGS) $(DEP_LDLIBS)
  20.  
  21. lclient : lclient.cc message.pb.cc
  22. $(CXX) -o $@ $^ -I$(INCPATH) $(DEP_LDFLAGS) $(DEP_LDLIBS)
  23.  
  24. .PHONY : all clean
  25.  
  26. clean :
  27. rm -rf $(TARGET)

服务器命令及输出:

  1. src]$ ./lserver
  2. accept a client 7
  3. Server read the data:1, 1804289383, aaaaaaaaaaaaaaaaaaaaaaaaa
  4.  
  5. Server read the data:2, 846930886, aa
  6.  
  7. Server read the data:3, 1681692777, bb
  8.  
  9. Server read the data:4, 1714636915, abcdefg
  10.  
  11. Server read the data:5, 1957747793,
  12.  
  13. connection close
  14. accept a client 7
  15. Server read the data:1, 1804289383, 2aa
  16.  
  17. Server read the data:2, 846930886, 2bb
  18.  
  19. Server read the data:3, 1681692777, 111111111111111111111222222222222222222222223333333333333333333333333
  20.  
  21. Server read the data:4, 1714636915,
  22.  
  23. ^C

客户端命令及输出:

  1. src]$ ./lclient localhost 8899
  2. Client has successfully connected.
  3. aaaaaaaaaaaaaaaaaaaaaaaaa
  4. Recv 1, 1804289383, I have read your data. from server.
  5. aa
  6. Recv 2, 846930886, I have read your data. from server.
  7. bb
  8. Recv 3, 1681692777, I have read your data. from server.
  9. abcdefg
  10. Recv 4, 1714636915, I have read your data. from server.
  11.  
  12. Recv 5, 1957747793, I have read your data. from server.
  13. ^C
  14. [src]$ ./lclient localhost 8899
  15. Client has successfully connected.
  16. 2aa
  17. Recv 1, 1804289383, I have read your data. from server.
  18. 2bb
  19. Recv 2, 846930886, I have read your data. from server.
  20. 111111111111111111111222222222222222222222223333333333333333333333333
  21. Recv 3, 1681692777, I have read your data. from server.
  22.  
  23. Recv 4, 1714636915, I have read your data. from server.
  24. Connection closed.
  25. Finish

注意:

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

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

  1. [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. out与ref修饰符

    out修饰符 定义 作用 使用注意 总结 定义 out意为output,所以被out修饰的参数叫做输出参数. 通过使用out修饰的参数,方法可以返回对应参数的值   作用 先看一个例子 定义变量:   ...

  2. C++ 实现的一个打印日历程序

    C++ 实现的一个打印日历程序 说明:总共有三个文件 1.month.h 为定义函数的头文件 2.month.cpp 为函数的实现代码 3.mainprog.cpp 为主函数的实现代码 month.h ...

  3. 第一个ajax小demo

    第一个ajax小demo 文章来源:http://blog.csdn.net/magi1201/article/details/44569657

  4. here with you

    Here With You - Asher Book To all my friends对我所有好友来讲The night is young夜未央The music's loud乐未殇They pla ...

  5. 【Java学习】调用ByteBuffer.getInt()方法得到808464432

    调用ByteBuffer.getInt()方法遇到的奇怪错误 最近在参加阿里的中间件比赛,中间用到了RocketMQ的思想,并且主要集中在使用NIO来读写文件.其中遇到了一个很蛋疼的问题,想了半天想不 ...

  6. RxSwift 系列(七)

    前言 本篇文章将要学习RxSwift中连接操作符.Connectable Observable在订阅时不发射事件消息,而是仅当调用它们的connect()方法时才发射消息,这样就可以等待所有我们想要的 ...

  7. nyoj 737 石子合并 经典区间 dp

    石子合并(一) 时间限制:1000 ms  |  内存限制:65535 KB 难度:3 描述     有N堆石子排成一排,每堆石子有一定的数量.现要将N堆石子并成为一堆.合并的过程只能每次将相邻的两堆 ...

  8. Codeforces 798D

    这两天后缀数组整多了整点有意思的,随机算法. 题意给你两个数组,让你求一个排列使得这个排列对应的两个数组前n/2+1个数之和的二倍大于每个序列总和. 下面先贴下这题正解 二维贪心,按a从大到小排,把第 ...

  9. 51nod1805 小树 prufer序列 + 容斥原理

    首先考虑$prufer$序列,那么问题转化为求 一个长为$n - 2$的序列,总共有$n$个元素,恰有$m$个元素不出现在序列中的方案数 考虑容斥,答案即为 至少$m$个元素不出现 - 至少$m + ...

  10. 【2-SAT】【DFS】【分类讨论】Gym - 101617K - Unsatisfying

    题意:给你一张2-SAT,问你加至少几句a V b(不能用非运算)这样的语句,使得其无法全为真. 如果最开始没有左右两项都含非运算的析取表达式,则无解,因为显然你可以对每一项的不含非的那项规定为真,使 ...