├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── bin ├── client ├── myconf.ini └── server ├── client.cc ├── example ├── tcpEchoServer │ ├── client.cc │ ├── echoMsg.pb.cc │ ├── echoMsg.pb.h │ ├── echoMsg.proto │ ├── makefile │ ├── myconf.ini │ └── server.cc ├── timerServer │ ├── makefile │ └── server.cc └── udpEchoServer │ ├── client.cc │ ├── echoMsg.pb.cc │ ├── echoMsg.pb.h │ ├── echoMsg.proto │ ├── makefile │ └── server.cc ├── include ├── config_reader.h ├── easy_reactor.h ├── event_base.h ├── event_loop.h ├── io_buffer.h ├── msg_dispatcher.h ├── msg_head.h ├── net_commu.h ├── print_error.h ├── tcp_client.h ├── tcp_conn.h ├── tcp_server.h ├── thread_pool.h ├── thread_queue.h ├── timer_queue.h ├── udp_client.h └── udp_server.h ├── makefile ├── pictures ├── client-example.png ├── multi-thread-arch.png └── server-example.png ├── server.cc ├── src ├── config_reader.cc ├── event_loop.cc ├── io_buffer.cc ├── tcp_client.cc ├── tcp_conn.cc ├── tcp_server.cc ├── thread_pool.cc ├── timer_queue.cc ├── udp_client.cc └── udp_server.cc └── test ├── benchmark.cc ├── echoMsg.pb.cc ├── echoMsg.pb.h ├── echoMsg.proto ├── makefile ├── myconf.ini └── server.cc /.gitignore: -------------------------------------------------------------------------------- 1 | *.a 2 | *.o 3 | *.d 4 | *.prog 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(ereactor) 3 | 4 | set(CMAKE_CXX_STANDARD 11) 5 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O2 -Wall -fPIC -Wno-deprecated") 6 | 7 | file(GLOB_RECURSE SOURCES src/*.cc) 8 | list(REMOVE_ITEM SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/udp_server.cc") 9 | #list(REMOVE_ITEM SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/udp_server.cc") 10 | 11 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) 12 | 13 | add_executable(server 14 | server.cc 15 | ${SOURCES} 16 | ) 17 | 18 | add_executable(client 19 | client.cc 20 | ${SOURCES} 21 | ) 22 | 23 | target_link_libraries( 24 | client 25 | -lpthread 26 | ) 27 | 28 | 29 | target_link_libraries( 30 | server 31 | -lpthread 32 | ) 33 | 34 | 35 | set_target_properties(server PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin) 36 | set_target_properties(client PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin) 37 | 38 | # 将头文件复制到输出目录 39 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test/myconf.ini ${CMAKE_CURRENT_SOURCE_DIR}/bin COPYONLY) 40 | 41 | 42 | 43 | # Add any other dependencies and linker flags if necessary 44 | # target_link_libraries(ereactor ) 45 | 46 | # Add any executable targets if necessary 47 | # add_executable(my_executable src/main.cpp) 48 | # target_link_libraries(my_executable ereactor) 49 | 50 | # Optionally, you can install the library 51 | # install(TARGETS ereactor DESTINATION lib) 52 | # install(DIRECTORY include DESTINATION include) 53 | 54 | # Optionally, clean build files 55 | # add_custom_target(clean-all COMMAND ${CMAKE_MAKE_PROGRAM} clean && rm -rf bin lib) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 leechanx 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Easy Reactor 2 | 3 | Easy-Reactor是一个基于Reactor模式的Linux C++网络服务器框架,支持多线程TCP服务器,单线程TCP服务器,单线程UDP服务器等形式,可以让使用者完全专注于业务,快速开发出一个高效的服务器应用。 4 | 5 | 在工作中开发基础服务器的经验总结、以及阅读陈硕《muduo》一书的收获,使得我以沉淀的心态做了这个项目 6 | 7 | 此项目在我的另一个项目[易用稳定、高性能的服务间远程调用管理、调度、负载系统:Easy-Load-Balancer][1]中得到了全面应用,此项目的实战充分证明了Easy-Reactor服务框架的**性能很高、使用也很简单** 8 | 9 | [1]: https://github.com/LeechanX/Easy-Load-Balancer 10 | 11 | #### 性能一览 12 | 13 | >服务器参数: 14 | >CPU个数:24   内存:128GB   网卡队列个数:24 15 | > 16 | >测试程序: 17 | >echo pingpong服务,每次传递100字节内容,见目录test/ 18 | 19 | | TCP服务线程数 | benchmark情况 | QPS | 20 | | :-----: | :-----: | :-----: | 21 | |1线程|5个benchmark,各建立100个连接| `10.96W/s` | 22 | |3线程|5个benchmark,各建立100个连接| `31.06W/s` | 23 | |5线程|5个benchmark,各建立100个连接| `48.12W/s` | 24 | |8线程|9个benchmark,各建立100个连接| `59.79W/s` | 25 | 26 | ## 介绍 27 | 28 | ### IO Event: 基于epoll 29 | 30 | 一切IO事件设置为非阻塞,由Linux epoll进行管理,且TCP、UDP的可读、可写事件均以默认方式即水平模式注册 31 | 32 | 为什么不用EPOLLET边缘触发模式?见我的文章:[边缘模式的写事件(神坑!)][2] 33 | 34 | [2]: http://ec18ca33.wiz03.com/share/s/3I6cEP3LBkz72L4E1Y1Wmgi51eyPsS1W-4r625pXha0VtIjQ 35 | 36 | ### Timer Event: Timer Queue设计 37 | 38 | - 以最小堆管理Timers(注册Timer、删除Timer),以每个Timer的发生时间在最小堆中排序 39 | - 以timerfd作为通知方式,交给eventLoop监听,将超时事件转为IO事件 40 | - timerfd所设置的时间总是最小堆的堆顶Timer的发生时间 41 | 42 | ### EventLoop 43 | 44 | IO Event与Timer Event全部在EventLoop中注册、监听、运行,一个EventLoop独占一个线程 45 | 46 | ### 功能 47 | **对TCP服务器端:** 支持设置:收到某类消息后回调函数,连接接收后回调函数,连接关闭前回调函数; 48 | 49 | **对TCP客户端:** 支持设置:收到某类消息后回调函数,connect成功后回调函数,连接关闭前回调函数; 50 | 51 | **对UDP服务端、客户端:** 都仅支持收到某类消息后回调函数 52 | 53 | 各服务端、客户端都可以主动发消息 54 | 55 | 此外,对**多线程模式的TCP服务器** ,支持:向某个or ALL子线程发送待执行任务pendingTask回调函数,可以在一次poll循环后执行pendingTask内容 56 | 57 | ### TCP服务器架构:多线程Reactor 58 | 59 | ![Multi-Thread-Arch](pictures/multi-thread-arch.png) 60 | 61 | 多线程TCP服务器采用了one loop per thread模型(memcached、muduo也是这么干的) 62 | 63 | 主线程作为Accepter角色,线程池作为实际连接操作者TCPConnection,线程池每个线程运行EventLoop维护一定量的连接,管理这些连接的IO Event与Timer Event,线程池中每个线程初始时监听自己队列的eventfd,便于与主线程通信 64 | 65 | #### Accepter细节 66 | 1. Accepter收到新连接,以Round-Robin轮询方法将连接发送到一个线程的队列,以交给此线程管理这个连接 67 | 2. Accepter已达连接上限时,使用占坑法处理 68 | 3. Accepter使用大数组管理所有连接信息,当一个连接关闭,并不立即清理TCPConnection对象,而是等待新连接到来后复用此对象(memcached也是这么干的) 69 | 70 | #### 可写事件与busy loop 71 | 由于TCP socket写缓冲区总是准备好的(除非满了),如果一直监听EPOLLOUT事件会造成EventLoop产生busy loop现象,最严重会吃满CPU 72 | 故: 73 | - 有数据要写时,才监听EPOLLOUT; 74 | - 当所有数据已写到socket,立刻删除EPOLLOUT事件 75 | 76 | #### 粘包处理 77 | 使用固定长度包头8字节(4字节存放消息类型,4字节存放消息body长度),任何在Easy-Reactor中发送的消息都会被默默加上这个包头 78 | 79 | #### 消息分发 80 | Server对象提供了:不同消息类型注册不同的回调函数功能,方便用户可以根据不同消息类型编写不同的处理函数 81 | 当读取socket数据后,如果包完整,则会根据消息类型ID,将一条 **完整的消息** 发给对应的回调函数去处理 82 | 83 | #### 缓冲区管理 84 | 每个连接对象TCPConnection没必要一直持有着读缓冲区和写缓冲区: 85 | 预设我们需要的一个缓冲区大小64KB,则每个连接需要读、写缓冲区共128KB -> 当连接数变多,内存占用过大 86 | 87 | 实际上 88 | - 对于读缓冲区,当读完全部数据且被处理后,读缓冲区就可以释放了,除非有粘包发生 89 | - 对于写缓冲区,当全部数据被发送到socket,写缓冲区就可以释放了,除非有数据没发完 90 | 91 | 我设计了一个简单的缓冲区管理容器,预先分配各种大小的缓冲区若干: 92 | 4K缓冲区:5000个,16KB:1000个,64KB:500个,256KB:200个,1MB:50个,4MB:10个 93 | 94 | 而每个TCPConnection初始时是没有读写缓冲区的,当需要读写时就向管理器索要一个长度合适的缓冲区使用,使用完就归还。具体而言: 95 | 96 | ##### 每当TCPconnection读取数据: 97 | - 先会利用`fcntl+FIONREAD`获取可读数据大小 98 | - 如果有读缓冲区(说明上次粘包了,没归还),则看剩余缓冲区空间是否放得下新数据,能就放,不能就跟管理器要个新缓冲区替代了旧的,copy了旧缓冲区的数据后把旧的归还,把新可读数据放进来 99 | - 如果没有读缓冲区(说明上次没粘包,归还了),则跟管理器要个缓冲区,把新可读数据放进来 100 | - 当业务回调处理后发现缓冲区没更多数据了,归还;否则,将剩余数据移动到缓冲区头部,继续持有等待下次使用 101 | 102 | ##### 每当TCPconnection写数据: 103 | - 用户调用sendData,如果有写缓冲区(说明上次每写完,不归还),则追加数据,如果放不下了,就跟管理器要个新缓冲区替代了旧的,copy了旧缓冲区的数据后把旧的归还,把新待发数据放进来 104 | - 一次性把写缓冲区的数据发送给socket后,如果发送完成,则归还缓冲区;否则把剩余待发数据移动到缓冲区头部,继续持有等待下次使用 105 | 106 | ### UDP服务器架构:单线程Reactor 107 | 108 | 由于UDP是无连接的,多线程操作同一个UDP socket效率未必很高,故选择了单线程模型,图略因为很简单 109 | 110 | 使用者完全可以使用多线程,每个线程运行Easy-Reactor UDP服务器,即每个线程一个地址不同的UDP socket,可得到一定的并行能力 111 | 112 | ### TCP、UDP客户端 113 | 114 | 都是eventLoop方式 115 | 116 | ### 使用方法 117 | 118 | 以一个TCP的`pingpong echo server`为例(具体TCP、UDP、timer例子见example目录) 119 | 120 | #### tcp server端: 121 | ![Server-Example](pictures/server-example.png) 122 | 123 | #### tcp client端: 124 | ![Client-Example](pictures/client-example.png) 125 | -------------------------------------------------------------------------------- /bin/client: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeechanX/Easy-Reactor/4e8b6d969d0a3043659123c0cbc2fc86ac3a0980/bin/client -------------------------------------------------------------------------------- /bin/myconf.ini: -------------------------------------------------------------------------------- 1 | [reactor] 2 | threadNum = 0 3 | maxConns = 1024 4 | ip = 127.0.0.1 5 | port = 12315 6 | -------------------------------------------------------------------------------- /bin/server: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeechanX/Easy-Reactor/4e8b6d969d0a3043659123c0cbc2fc86ac3a0980/bin/server -------------------------------------------------------------------------------- /client.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "easy_reactor.h" 7 | 8 | using namespace std; 9 | 10 | struct testQPS 11 | { 12 | testQPS(): lstTs(0), succ(0) {} 13 | long lstTs; 14 | int succ; 15 | }; 16 | 17 | void buz(const char* data, uint32_t len, int cmdid, net_commu* commu, void* usr_data) 18 | { 19 | testQPS* qps = (testQPS*)usr_data;//获取用户参数 20 | qps->succ++; 21 | long curTs = time(NULL); 22 | if (curTs - qps->lstTs >= 1) 23 | { 24 | printf(" ** qps:%d **\n", qps->succ); 25 | qps->lstTs = curTs; 26 | //qps->succ = 0; 27 | } 28 | std::cout << "client:" << data << std::endl; 29 | std::ostringstream convert1; 30 | convert1 << "I miss you data" <<" index: "<< qps->succ << ":" << qps->lstTs 31 | << "Thread ID: " << pthread_self(); 32 | 33 | string reqStr = convert1.str(); 34 | commu->send_data(reqStr.c_str(), reqStr.size(), cmdid);//回复消息 35 | } 36 | 37 | void whenConnectDone(tcp_client* client, void* args) 38 | { 39 | string reqStr = "I miss you"; 40 | client->send_data(reqStr.c_str(), strlen("I miss you"), 1);//主动发送消息 41 | } 42 | 43 | void* domain(void* args) 44 | { 45 | event_loop loop; 46 | tcp_client client(&loop, "127.0.0.1", 12315);//创建TCP客户端 47 | 48 | testQPS qps; 49 | client.add_msg_cb(1, buz, (void*)&qps);//设置:当收到消息id=1的消息时的回调函数 50 | //安装连接建立后调用的回调函数 51 | client.onConnection(whenConnectDone); 52 | 53 | loop.process_evs(); 54 | return NULL; 55 | } 56 | 57 | int main(int argc, char** argv) 58 | { 59 | int threadNum = atoi(argv[1]); 60 | pthread_t *tids; 61 | tids = new pthread_t[threadNum]; 62 | for (int i = 0;i < threadNum; ++i) 63 | pthread_create(&tids[i], NULL, domain, NULL); 64 | for (int i = 0;i < threadNum; ++i) 65 | pthread_join(tids[i], NULL); 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /example/tcpEchoServer/client.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "echoMsg.pb.h" 6 | #include "easy_reactor.h" 7 | 8 | using namespace std; 9 | using namespace echo; 10 | 11 | struct testQPS 12 | { 13 | testQPS(): lstTs(0), succ(0) {} 14 | long lstTs; 15 | int succ; 16 | }; 17 | 18 | void buz(const char* data, uint32_t len, int cmdid, net_commu* commu, void* usr_data) 19 | { 20 | testQPS* qps = (testQPS*)usr_data;//获取用户参数 21 | EchoString req, rsp; 22 | if (rsp.ParseFromArray(data, len) && rsp.content() == "I miss you i miss you i miss you i miss you i miss you i miss you i miss you i miss you i miss you!") 23 | { 24 | qps->succ++; 25 | } 26 | long curTs = time(NULL); 27 | if (curTs - qps->lstTs >= 1) 28 | { 29 | printf(" ** qps:%d **\n", qps->succ); 30 | qps->lstTs = curTs; 31 | qps->succ = 0; 32 | } 33 | 34 | req.set_id(rsp.id() + 1); 35 | req.set_content(rsp.content()); 36 | 37 | string reqStr; 38 | req.SerializeToString(&reqStr); 39 | commu->send_data(reqStr.c_str(), reqStr.size(), cmdid);//回复消息 40 | } 41 | 42 | void whenConnectDone(tcp_client* client, void* args) 43 | { 44 | EchoString req; 45 | req.set_id(100); 46 | req.set_content("I miss you i miss you i miss you i miss you i miss you i miss you i miss you i miss you i miss you!"); 47 | string reqStr; 48 | req.SerializeToString(&reqStr); 49 | client->send_data(reqStr.c_str(), reqStr.size(), 1);//主动发送消息 50 | } 51 | 52 | void* domain(void* args) 53 | { 54 | event_loop loop; 55 | tcp_client client(&loop, "127.0.0.1", 12315);//创建TCP客户端 56 | 57 | testQPS qps; 58 | client.add_msg_cb(1, buz, (void*)&qps);//设置:当收到消息id=1的消息时的回调函数 59 | //安装连接建立后调用的回调函数 60 | client.onConnection(whenConnectDone); 61 | 62 | loop.process_evs(); 63 | return NULL; 64 | } 65 | 66 | int main(int argc, char** argv) 67 | { 68 | int threadNum = atoi(argv[1]); 69 | pthread_t *tids; 70 | tids = new pthread_t[threadNum]; 71 | for (int i = 0;i < threadNum; ++i) 72 | pthread_create(&tids[i], NULL, domain, NULL); 73 | for (int i = 0;i < threadNum; ++i) 74 | pthread_join(tids[i], NULL); 75 | return 0; 76 | } 77 | -------------------------------------------------------------------------------- /example/tcpEchoServer/echoMsg.pb.cc: -------------------------------------------------------------------------------- 1 | // Generated by the protocol buffer compiler. DO NOT EDIT! 2 | // source: echoMsg.proto 3 | 4 | #define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION 5 | #include "echoMsg.pb.h" 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | // @@protoc_insertion_point(includes) 18 | 19 | namespace echo { 20 | 21 | namespace { 22 | 23 | const ::google::protobuf::Descriptor* EchoString_descriptor_ = NULL; 24 | const ::google::protobuf::internal::GeneratedMessageReflection* 25 | EchoString_reflection_ = NULL; 26 | 27 | } // namespace 28 | 29 | 30 | void protobuf_AssignDesc_echoMsg_2eproto() { 31 | protobuf_AddDesc_echoMsg_2eproto(); 32 | const ::google::protobuf::FileDescriptor* file = 33 | ::google::protobuf::DescriptorPool::generated_pool()->FindFileByName( 34 | "echoMsg.proto"); 35 | GOOGLE_CHECK(file != NULL); 36 | EchoString_descriptor_ = file->message_type(0); 37 | static const int EchoString_offsets_[2] = { 38 | GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EchoString, content_), 39 | GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EchoString, id_), 40 | }; 41 | EchoString_reflection_ = 42 | new ::google::protobuf::internal::GeneratedMessageReflection( 43 | EchoString_descriptor_, 44 | EchoString::default_instance_, 45 | EchoString_offsets_, 46 | GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EchoString, _has_bits_[0]), 47 | GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EchoString, _unknown_fields_), 48 | -1, 49 | ::google::protobuf::DescriptorPool::generated_pool(), 50 | ::google::protobuf::MessageFactory::generated_factory(), 51 | sizeof(EchoString)); 52 | } 53 | 54 | namespace { 55 | 56 | GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AssignDescriptors_once_); 57 | inline void protobuf_AssignDescriptorsOnce() { 58 | ::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_, 59 | &protobuf_AssignDesc_echoMsg_2eproto); 60 | } 61 | 62 | void protobuf_RegisterTypes(const ::std::string&) { 63 | protobuf_AssignDescriptorsOnce(); 64 | ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( 65 | EchoString_descriptor_, &EchoString::default_instance()); 66 | } 67 | 68 | } // namespace 69 | 70 | void protobuf_ShutdownFile_echoMsg_2eproto() { 71 | delete EchoString::default_instance_; 72 | delete EchoString_reflection_; 73 | } 74 | 75 | void protobuf_AddDesc_echoMsg_2eproto() { 76 | static bool already_here = false; 77 | if (already_here) return; 78 | already_here = true; 79 | GOOGLE_PROTOBUF_VERIFY_VERSION; 80 | 81 | ::google::protobuf::DescriptorPool::InternalAddGeneratedFile( 82 | "\n\rechoMsg.proto\022\004echo\")\n\nEchoString\022\017\n\007c" 83 | "ontent\030\001 \002(\t\022\n\n\002id\030\002 \002(\005", 64); 84 | ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( 85 | "echoMsg.proto", &protobuf_RegisterTypes); 86 | EchoString::default_instance_ = new EchoString(); 87 | EchoString::default_instance_->InitAsDefaultInstance(); 88 | ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_echoMsg_2eproto); 89 | } 90 | 91 | // Force AddDescriptors() to be called at static initialization time. 92 | struct StaticDescriptorInitializer_echoMsg_2eproto { 93 | StaticDescriptorInitializer_echoMsg_2eproto() { 94 | protobuf_AddDesc_echoMsg_2eproto(); 95 | } 96 | } static_descriptor_initializer_echoMsg_2eproto_; 97 | 98 | // =================================================================== 99 | 100 | #ifndef _MSC_VER 101 | const int EchoString::kContentFieldNumber; 102 | const int EchoString::kIdFieldNumber; 103 | #endif // !_MSC_VER 104 | 105 | EchoString::EchoString() 106 | : ::google::protobuf::Message() { 107 | SharedCtor(); 108 | // @@protoc_insertion_point(constructor:echo.EchoString) 109 | } 110 | 111 | void EchoString::InitAsDefaultInstance() { 112 | } 113 | 114 | EchoString::EchoString(const EchoString& from) 115 | : ::google::protobuf::Message() { 116 | SharedCtor(); 117 | MergeFrom(from); 118 | // @@protoc_insertion_point(copy_constructor:echo.EchoString) 119 | } 120 | 121 | void EchoString::SharedCtor() { 122 | ::google::protobuf::internal::GetEmptyString(); 123 | _cached_size_ = 0; 124 | content_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); 125 | id_ = 0; 126 | ::memset(_has_bits_, 0, sizeof(_has_bits_)); 127 | } 128 | 129 | EchoString::~EchoString() { 130 | // @@protoc_insertion_point(destructor:echo.EchoString) 131 | SharedDtor(); 132 | } 133 | 134 | void EchoString::SharedDtor() { 135 | if (content_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 136 | delete content_; 137 | } 138 | if (this != default_instance_) { 139 | } 140 | } 141 | 142 | void EchoString::SetCachedSize(int size) const { 143 | GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); 144 | _cached_size_ = size; 145 | GOOGLE_SAFE_CONCURRENT_WRITES_END(); 146 | } 147 | const ::google::protobuf::Descriptor* EchoString::descriptor() { 148 | protobuf_AssignDescriptorsOnce(); 149 | return EchoString_descriptor_; 150 | } 151 | 152 | const EchoString& EchoString::default_instance() { 153 | if (default_instance_ == NULL) protobuf_AddDesc_echoMsg_2eproto(); 154 | return *default_instance_; 155 | } 156 | 157 | EchoString* EchoString::default_instance_ = NULL; 158 | 159 | EchoString* EchoString::New() const { 160 | return new EchoString; 161 | } 162 | 163 | void EchoString::Clear() { 164 | if (_has_bits_[0 / 32] & 3) { 165 | if (has_content()) { 166 | if (content_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 167 | content_->clear(); 168 | } 169 | } 170 | id_ = 0; 171 | } 172 | ::memset(_has_bits_, 0, sizeof(_has_bits_)); 173 | mutable_unknown_fields()->Clear(); 174 | } 175 | 176 | bool EchoString::MergePartialFromCodedStream( 177 | ::google::protobuf::io::CodedInputStream* input) { 178 | #define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure 179 | ::google::protobuf::uint32 tag; 180 | // @@protoc_insertion_point(parse_start:echo.EchoString) 181 | for (;;) { 182 | ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127); 183 | tag = p.first; 184 | if (!p.second) goto handle_unusual; 185 | switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { 186 | // required string content = 1; 187 | case 1: { 188 | if (tag == 10) { 189 | DO_(::google::protobuf::internal::WireFormatLite::ReadString( 190 | input, this->mutable_content())); 191 | ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( 192 | this->content().data(), this->content().length(), 193 | ::google::protobuf::internal::WireFormat::PARSE, 194 | "content"); 195 | } else { 196 | goto handle_unusual; 197 | } 198 | if (input->ExpectTag(16)) goto parse_id; 199 | break; 200 | } 201 | 202 | // required int32 id = 2; 203 | case 2: { 204 | if (tag == 16) { 205 | parse_id: 206 | DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< 207 | ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( 208 | input, &id_))); 209 | set_has_id(); 210 | } else { 211 | goto handle_unusual; 212 | } 213 | if (input->ExpectAtEnd()) goto success; 214 | break; 215 | } 216 | 217 | default: { 218 | handle_unusual: 219 | if (tag == 0 || 220 | ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == 221 | ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { 222 | goto success; 223 | } 224 | DO_(::google::protobuf::internal::WireFormat::SkipField( 225 | input, tag, mutable_unknown_fields())); 226 | break; 227 | } 228 | } 229 | } 230 | success: 231 | // @@protoc_insertion_point(parse_success:echo.EchoString) 232 | return true; 233 | failure: 234 | // @@protoc_insertion_point(parse_failure:echo.EchoString) 235 | return false; 236 | #undef DO_ 237 | } 238 | 239 | void EchoString::SerializeWithCachedSizes( 240 | ::google::protobuf::io::CodedOutputStream* output) const { 241 | // @@protoc_insertion_point(serialize_start:echo.EchoString) 242 | // required string content = 1; 243 | if (has_content()) { 244 | ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( 245 | this->content().data(), this->content().length(), 246 | ::google::protobuf::internal::WireFormat::SERIALIZE, 247 | "content"); 248 | ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( 249 | 1, this->content(), output); 250 | } 251 | 252 | // required int32 id = 2; 253 | if (has_id()) { 254 | ::google::protobuf::internal::WireFormatLite::WriteInt32(2, this->id(), output); 255 | } 256 | 257 | if (!unknown_fields().empty()) { 258 | ::google::protobuf::internal::WireFormat::SerializeUnknownFields( 259 | unknown_fields(), output); 260 | } 261 | // @@protoc_insertion_point(serialize_end:echo.EchoString) 262 | } 263 | 264 | ::google::protobuf::uint8* EchoString::SerializeWithCachedSizesToArray( 265 | ::google::protobuf::uint8* target) const { 266 | // @@protoc_insertion_point(serialize_to_array_start:echo.EchoString) 267 | // required string content = 1; 268 | if (has_content()) { 269 | ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( 270 | this->content().data(), this->content().length(), 271 | ::google::protobuf::internal::WireFormat::SERIALIZE, 272 | "content"); 273 | target = 274 | ::google::protobuf::internal::WireFormatLite::WriteStringToArray( 275 | 1, this->content(), target); 276 | } 277 | 278 | // required int32 id = 2; 279 | if (has_id()) { 280 | target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(2, this->id(), target); 281 | } 282 | 283 | if (!unknown_fields().empty()) { 284 | target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( 285 | unknown_fields(), target); 286 | } 287 | // @@protoc_insertion_point(serialize_to_array_end:echo.EchoString) 288 | return target; 289 | } 290 | 291 | int EchoString::ByteSize() const { 292 | int total_size = 0; 293 | 294 | if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { 295 | // required string content = 1; 296 | if (has_content()) { 297 | total_size += 1 + 298 | ::google::protobuf::internal::WireFormatLite::StringSize( 299 | this->content()); 300 | } 301 | 302 | // required int32 id = 2; 303 | if (has_id()) { 304 | total_size += 1 + 305 | ::google::protobuf::internal::WireFormatLite::Int32Size( 306 | this->id()); 307 | } 308 | 309 | } 310 | if (!unknown_fields().empty()) { 311 | total_size += 312 | ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( 313 | unknown_fields()); 314 | } 315 | GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); 316 | _cached_size_ = total_size; 317 | GOOGLE_SAFE_CONCURRENT_WRITES_END(); 318 | return total_size; 319 | } 320 | 321 | void EchoString::MergeFrom(const ::google::protobuf::Message& from) { 322 | GOOGLE_CHECK_NE(&from, this); 323 | const EchoString* source = 324 | ::google::protobuf::internal::dynamic_cast_if_available( 325 | &from); 326 | if (source == NULL) { 327 | ::google::protobuf::internal::ReflectionOps::Merge(from, this); 328 | } else { 329 | MergeFrom(*source); 330 | } 331 | } 332 | 333 | void EchoString::MergeFrom(const EchoString& from) { 334 | GOOGLE_CHECK_NE(&from, this); 335 | if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { 336 | if (from.has_content()) { 337 | set_content(from.content()); 338 | } 339 | if (from.has_id()) { 340 | set_id(from.id()); 341 | } 342 | } 343 | mutable_unknown_fields()->MergeFrom(from.unknown_fields()); 344 | } 345 | 346 | void EchoString::CopyFrom(const ::google::protobuf::Message& from) { 347 | if (&from == this) return; 348 | Clear(); 349 | MergeFrom(from); 350 | } 351 | 352 | void EchoString::CopyFrom(const EchoString& from) { 353 | if (&from == this) return; 354 | Clear(); 355 | MergeFrom(from); 356 | } 357 | 358 | bool EchoString::IsInitialized() const { 359 | if ((_has_bits_[0] & 0x00000003) != 0x00000003) return false; 360 | 361 | return true; 362 | } 363 | 364 | void EchoString::Swap(EchoString* other) { 365 | if (other != this) { 366 | std::swap(content_, other->content_); 367 | std::swap(id_, other->id_); 368 | std::swap(_has_bits_[0], other->_has_bits_[0]); 369 | _unknown_fields_.Swap(&other->_unknown_fields_); 370 | std::swap(_cached_size_, other->_cached_size_); 371 | } 372 | } 373 | 374 | ::google::protobuf::Metadata EchoString::GetMetadata() const { 375 | protobuf_AssignDescriptorsOnce(); 376 | ::google::protobuf::Metadata metadata; 377 | metadata.descriptor = EchoString_descriptor_; 378 | metadata.reflection = EchoString_reflection_; 379 | return metadata; 380 | } 381 | 382 | 383 | // @@protoc_insertion_point(namespace_scope) 384 | 385 | } // namespace echo 386 | 387 | // @@protoc_insertion_point(global_scope) 388 | -------------------------------------------------------------------------------- /example/tcpEchoServer/echoMsg.pb.h: -------------------------------------------------------------------------------- 1 | // Generated by the protocol buffer compiler. DO NOT EDIT! 2 | // source: echoMsg.proto 3 | 4 | #ifndef PROTOBUF_echoMsg_2eproto__INCLUDED 5 | #define PROTOBUF_echoMsg_2eproto__INCLUDED 6 | 7 | #include 8 | 9 | #include 10 | 11 | #if GOOGLE_PROTOBUF_VERSION < 2006000 12 | #error This file was generated by a newer version of protoc which is 13 | #error incompatible with your Protocol Buffer headers. Please update 14 | #error your headers. 15 | #endif 16 | #if 2006001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION 17 | #error This file was generated by an older version of protoc which is 18 | #error incompatible with your Protocol Buffer headers. Please 19 | #error regenerate this file with a newer version of protoc. 20 | #endif 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | // @@protoc_insertion_point(includes) 28 | 29 | namespace echo { 30 | 31 | // Internal implementation detail -- do not call these. 32 | void protobuf_AddDesc_echoMsg_2eproto(); 33 | void protobuf_AssignDesc_echoMsg_2eproto(); 34 | void protobuf_ShutdownFile_echoMsg_2eproto(); 35 | 36 | class EchoString; 37 | 38 | // =================================================================== 39 | 40 | class EchoString : public ::google::protobuf::Message { 41 | public: 42 | EchoString(); 43 | virtual ~EchoString(); 44 | 45 | EchoString(const EchoString& from); 46 | 47 | inline EchoString& operator=(const EchoString& from) { 48 | CopyFrom(from); 49 | return *this; 50 | } 51 | 52 | inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { 53 | return _unknown_fields_; 54 | } 55 | 56 | inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { 57 | return &_unknown_fields_; 58 | } 59 | 60 | static const ::google::protobuf::Descriptor* descriptor(); 61 | static const EchoString& default_instance(); 62 | 63 | void Swap(EchoString* other); 64 | 65 | // implements Message ---------------------------------------------- 66 | 67 | EchoString* New() const; 68 | void CopyFrom(const ::google::protobuf::Message& from); 69 | void MergeFrom(const ::google::protobuf::Message& from); 70 | void CopyFrom(const EchoString& from); 71 | void MergeFrom(const EchoString& from); 72 | void Clear(); 73 | bool IsInitialized() const; 74 | 75 | int ByteSize() const; 76 | bool MergePartialFromCodedStream( 77 | ::google::protobuf::io::CodedInputStream* input); 78 | void SerializeWithCachedSizes( 79 | ::google::protobuf::io::CodedOutputStream* output) const; 80 | ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; 81 | int GetCachedSize() const { return _cached_size_; } 82 | private: 83 | void SharedCtor(); 84 | void SharedDtor(); 85 | void SetCachedSize(int size) const; 86 | public: 87 | ::google::protobuf::Metadata GetMetadata() const; 88 | 89 | // nested types ---------------------------------------------------- 90 | 91 | // accessors ------------------------------------------------------- 92 | 93 | // required string content = 1; 94 | inline bool has_content() const; 95 | inline void clear_content(); 96 | static const int kContentFieldNumber = 1; 97 | inline const ::std::string& content() const; 98 | inline void set_content(const ::std::string& value); 99 | inline void set_content(const char* value); 100 | inline void set_content(const char* value, size_t size); 101 | inline ::std::string* mutable_content(); 102 | inline ::std::string* release_content(); 103 | inline void set_allocated_content(::std::string* content); 104 | 105 | // required int32 id = 2; 106 | inline bool has_id() const; 107 | inline void clear_id(); 108 | static const int kIdFieldNumber = 2; 109 | inline ::google::protobuf::int32 id() const; 110 | inline void set_id(::google::protobuf::int32 value); 111 | 112 | // @@protoc_insertion_point(class_scope:echo.EchoString) 113 | private: 114 | inline void set_has_content(); 115 | inline void clear_has_content(); 116 | inline void set_has_id(); 117 | inline void clear_has_id(); 118 | 119 | ::google::protobuf::UnknownFieldSet _unknown_fields_; 120 | 121 | ::google::protobuf::uint32 _has_bits_[1]; 122 | mutable int _cached_size_; 123 | ::std::string* content_; 124 | ::google::protobuf::int32 id_; 125 | friend void protobuf_AddDesc_echoMsg_2eproto(); 126 | friend void protobuf_AssignDesc_echoMsg_2eproto(); 127 | friend void protobuf_ShutdownFile_echoMsg_2eproto(); 128 | 129 | void InitAsDefaultInstance(); 130 | static EchoString* default_instance_; 131 | }; 132 | // =================================================================== 133 | 134 | 135 | // =================================================================== 136 | 137 | // EchoString 138 | 139 | // required string content = 1; 140 | inline bool EchoString::has_content() const { 141 | return (_has_bits_[0] & 0x00000001u) != 0; 142 | } 143 | inline void EchoString::set_has_content() { 144 | _has_bits_[0] |= 0x00000001u; 145 | } 146 | inline void EchoString::clear_has_content() { 147 | _has_bits_[0] &= ~0x00000001u; 148 | } 149 | inline void EchoString::clear_content() { 150 | if (content_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 151 | content_->clear(); 152 | } 153 | clear_has_content(); 154 | } 155 | inline const ::std::string& EchoString::content() const { 156 | // @@protoc_insertion_point(field_get:echo.EchoString.content) 157 | return *content_; 158 | } 159 | inline void EchoString::set_content(const ::std::string& value) { 160 | set_has_content(); 161 | if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 162 | content_ = new ::std::string; 163 | } 164 | content_->assign(value); 165 | // @@protoc_insertion_point(field_set:echo.EchoString.content) 166 | } 167 | inline void EchoString::set_content(const char* value) { 168 | set_has_content(); 169 | if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 170 | content_ = new ::std::string; 171 | } 172 | content_->assign(value); 173 | // @@protoc_insertion_point(field_set_char:echo.EchoString.content) 174 | } 175 | inline void EchoString::set_content(const char* value, size_t size) { 176 | set_has_content(); 177 | if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 178 | content_ = new ::std::string; 179 | } 180 | content_->assign(reinterpret_cast(value), size); 181 | // @@protoc_insertion_point(field_set_pointer:echo.EchoString.content) 182 | } 183 | inline ::std::string* EchoString::mutable_content() { 184 | set_has_content(); 185 | if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 186 | content_ = new ::std::string; 187 | } 188 | // @@protoc_insertion_point(field_mutable:echo.EchoString.content) 189 | return content_; 190 | } 191 | inline ::std::string* EchoString::release_content() { 192 | clear_has_content(); 193 | if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 194 | return NULL; 195 | } else { 196 | ::std::string* temp = content_; 197 | content_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); 198 | return temp; 199 | } 200 | } 201 | inline void EchoString::set_allocated_content(::std::string* content) { 202 | if (content_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 203 | delete content_; 204 | } 205 | if (content) { 206 | set_has_content(); 207 | content_ = content; 208 | } else { 209 | clear_has_content(); 210 | content_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); 211 | } 212 | // @@protoc_insertion_point(field_set_allocated:echo.EchoString.content) 213 | } 214 | 215 | // required int32 id = 2; 216 | inline bool EchoString::has_id() const { 217 | return (_has_bits_[0] & 0x00000002u) != 0; 218 | } 219 | inline void EchoString::set_has_id() { 220 | _has_bits_[0] |= 0x00000002u; 221 | } 222 | inline void EchoString::clear_has_id() { 223 | _has_bits_[0] &= ~0x00000002u; 224 | } 225 | inline void EchoString::clear_id() { 226 | id_ = 0; 227 | clear_has_id(); 228 | } 229 | inline ::google::protobuf::int32 EchoString::id() const { 230 | // @@protoc_insertion_point(field_get:echo.EchoString.id) 231 | return id_; 232 | } 233 | inline void EchoString::set_id(::google::protobuf::int32 value) { 234 | set_has_id(); 235 | id_ = value; 236 | // @@protoc_insertion_point(field_set:echo.EchoString.id) 237 | } 238 | 239 | 240 | // @@protoc_insertion_point(namespace_scope) 241 | 242 | } // namespace echo 243 | 244 | #ifndef SWIG 245 | namespace google { 246 | namespace protobuf { 247 | 248 | 249 | } // namespace google 250 | } // namespace protobuf 251 | #endif // SWIG 252 | 253 | // @@protoc_insertion_point(global_scope) 254 | 255 | #endif // PROTOBUF_echoMsg_2eproto__INCLUDED 256 | -------------------------------------------------------------------------------- /example/tcpEchoServer/echoMsg.proto: -------------------------------------------------------------------------------- 1 | package echo; 2 | 3 | message EchoString 4 | { 5 | required string content = 1; 6 | required int32 id = 2; 7 | }; 8 | -------------------------------------------------------------------------------- /example/tcpEchoServer/makefile: -------------------------------------------------------------------------------- 1 | CXX=g++ 2 | CFLAGS=-g -O2 -Wall -fPIC -Wno-deprecated 3 | 4 | INC=-I../../include 5 | LIB=-L../../lib -lereactor /usr/local/lib/libprotobuf.a -lrt 6 | OBJS = $(addsuffix .o, $(basename $(wildcard *.cc))) 7 | 8 | all: 9 | $(CXX) -o server.prog $(CFLAGS) server.cc echoMsg.pb.cc $(INC) $(LIB) 10 | $(CXX) -o client.prog $(CFLAGS) client.cc echoMsg.pb.cc $(INC) $(LIB) 11 | 12 | clean: 13 | -rm -f *.o *.d server client 14 | -------------------------------------------------------------------------------- /example/tcpEchoServer/myconf.ini: -------------------------------------------------------------------------------- 1 | [reactor] 2 | threadNum = 0 3 | maxConns = 1024 4 | -------------------------------------------------------------------------------- /example/tcpEchoServer/server.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "echoMsg.pb.h" 3 | #include "easy_reactor.h" 4 | 5 | using namespace std; 6 | using namespace echo; 7 | 8 | void buz(const char* data, uint32_t len, int cmdid, net_commu* commu, void* usr_data) 9 | { 10 | EchoString req, rsp; 11 | req.ParseFromArray(data, len);//解包,data[0:len)保证是一个完整包 12 | rsp.set_id(req.id()); 13 | rsp.set_content(req.content()); 14 | string rspStr; 15 | rsp.SerializeToString(&rspStr); 16 | commu->send_data(rspStr.c_str(), rspStr.size(), cmdid);//回复消息 17 | } 18 | 19 | int main() 20 | { 21 | event_loop loop; 22 | 23 | config_reader::setPath("myconf.ini"); 24 | string ip = config_reader::ins()->GetString("reactor", "ip", "0.0.0.0"); 25 | short port = config_reader::ins()->GetNumber("reactor", "port", 12315); 26 | 27 | tcp_server server(&loop, ip.c_str(), port);//创建TCP服务器 28 | server.add_msg_cb(1, buz);//设置:当收到消息id = 1的消息调用的回调函数 我们约定EchoString消息的ID是1 29 | loop.process_evs(); 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /example/timerServer/makefile: -------------------------------------------------------------------------------- 1 | CXX=g++ 2 | CFLAGS=-g -O2 -Wall -fPIC -Wno-deprecated 3 | 4 | INC=-I../../include 5 | LIB=-L../../lib -lereactor -lrt 6 | OBJS = $(addsuffix .o, $(basename $(wildcard *.cc))) 7 | 8 | all: 9 | $(CXX) -o server.prog $(CFLAGS) server.cc $(INC) $(LIB) 10 | 11 | clean: 12 | -rm -f *.o *.d server.prog 13 | -------------------------------------------------------------------------------- /example/timerServer/server.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "easy_reactor.h" 5 | 6 | void buz1(event_loop* loop, void* usr_data) 7 | { 8 | FILE* fp = (FILE*)usr_data; 9 | fprintf(fp, "once display ts %ld\n", time(NULL)); 10 | fflush(fp); 11 | } 12 | 13 | void buz2(event_loop* loop, void* usr_data) 14 | { 15 | FILE* fp = (FILE*)usr_data; 16 | fprintf(fp, "always display ts %ld\n", time(NULL)); 17 | fflush(fp); 18 | } 19 | 20 | int main() 21 | { 22 | event_loop loop; 23 | config_reader::setPath("myconf.ini"); 24 | std::string ip = config_reader::ins()->GetString("reactor", "ip", "0.0.0.0"); 25 | short port = config_reader::ins()->GetNumber("reactor", "port", 12315); 26 | 27 | tcp_server server(&loop, ip.c_str(), port); 28 | 29 | FILE* fp = fopen("output.log", "w"); 30 | loop.run_after(buz1, fp, 10); 31 | loop.run_every(buz2, fp, 1, 500); 32 | 33 | loop.process_evs(); 34 | 35 | fclose(fp); 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /example/udpEchoServer/client.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "echoMsg.pb.h" 6 | #include "easy_reactor.h" 7 | 8 | using namespace std; 9 | using namespace echo; 10 | 11 | struct testQPS 12 | { 13 | testQPS(): lstTs(0), succ(0) {} 14 | long lstTs; 15 | int succ; 16 | }; 17 | 18 | void buz(const char* data, uint32_t len, int cmdid, net_commu* commu, void* usr_data) 19 | { 20 | testQPS* qps = (testQPS*)usr_data;//获取用户参数 21 | EchoString req, rsp; 22 | if (rsp.ParseFromArray(data, len) && rsp.content() == "I miss you i miss you i miss you i miss you i miss you i miss you i miss you i miss you i miss you!") 23 | { 24 | qps->succ++; 25 | } 26 | 27 | long curTs = time(NULL); 28 | if (curTs - qps->lstTs >= 1) 29 | { 30 | printf(" ** qps:%d **\n", qps->succ); 31 | qps->lstTs = curTs; 32 | qps->succ = 0; 33 | } 34 | 35 | req.set_id(rsp.id() + 1); 36 | req.set_content(rsp.content()); 37 | 38 | string reqStr; 39 | req.SerializeToString(&reqStr); 40 | commu->send_data(reqStr.c_str(), reqStr.size(), cmdid);//回复消息 41 | } 42 | 43 | int main(int argc, char** argv) 44 | { 45 | event_loop loop; 46 | udp_client client(&loop, "127.0.0.1", 12315);//创建UDP客户端 47 | 48 | testQPS qps; 49 | client.add_msg_cb(1, buz, (void*)&qps);//设置:当收到消息id=1的消息时的回调函数 50 | 51 | EchoString req; 52 | req.set_id(100); 53 | req.set_content("I miss you i miss you i miss you i miss you i miss you i miss you i miss you i miss you i miss you!"); 54 | string reqStr; 55 | req.SerializeToString(&reqStr); 56 | client.send_data(reqStr.c_str(), reqStr.size(), 1);//主动发送消息 57 | 58 | loop.process_evs(); 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /example/udpEchoServer/echoMsg.pb.cc: -------------------------------------------------------------------------------- 1 | // Generated by the protocol buffer compiler. DO NOT EDIT! 2 | // source: echoMsg.proto 3 | 4 | #define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION 5 | #include "echoMsg.pb.h" 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | // @@protoc_insertion_point(includes) 18 | 19 | namespace echo { 20 | 21 | namespace { 22 | 23 | const ::google::protobuf::Descriptor* EchoString_descriptor_ = NULL; 24 | const ::google::protobuf::internal::GeneratedMessageReflection* 25 | EchoString_reflection_ = NULL; 26 | 27 | } // namespace 28 | 29 | 30 | void protobuf_AssignDesc_echoMsg_2eproto() { 31 | protobuf_AddDesc_echoMsg_2eproto(); 32 | const ::google::protobuf::FileDescriptor* file = 33 | ::google::protobuf::DescriptorPool::generated_pool()->FindFileByName( 34 | "echoMsg.proto"); 35 | GOOGLE_CHECK(file != NULL); 36 | EchoString_descriptor_ = file->message_type(0); 37 | static const int EchoString_offsets_[2] = { 38 | GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EchoString, content_), 39 | GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EchoString, id_), 40 | }; 41 | EchoString_reflection_ = 42 | new ::google::protobuf::internal::GeneratedMessageReflection( 43 | EchoString_descriptor_, 44 | EchoString::default_instance_, 45 | EchoString_offsets_, 46 | GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EchoString, _has_bits_[0]), 47 | GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EchoString, _unknown_fields_), 48 | -1, 49 | ::google::protobuf::DescriptorPool::generated_pool(), 50 | ::google::protobuf::MessageFactory::generated_factory(), 51 | sizeof(EchoString)); 52 | } 53 | 54 | namespace { 55 | 56 | GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AssignDescriptors_once_); 57 | inline void protobuf_AssignDescriptorsOnce() { 58 | ::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_, 59 | &protobuf_AssignDesc_echoMsg_2eproto); 60 | } 61 | 62 | void protobuf_RegisterTypes(const ::std::string&) { 63 | protobuf_AssignDescriptorsOnce(); 64 | ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( 65 | EchoString_descriptor_, &EchoString::default_instance()); 66 | } 67 | 68 | } // namespace 69 | 70 | void protobuf_ShutdownFile_echoMsg_2eproto() { 71 | delete EchoString::default_instance_; 72 | delete EchoString_reflection_; 73 | } 74 | 75 | void protobuf_AddDesc_echoMsg_2eproto() { 76 | static bool already_here = false; 77 | if (already_here) return; 78 | already_here = true; 79 | GOOGLE_PROTOBUF_VERIFY_VERSION; 80 | 81 | ::google::protobuf::DescriptorPool::InternalAddGeneratedFile( 82 | "\n\rechoMsg.proto\022\004echo\")\n\nEchoString\022\017\n\007c" 83 | "ontent\030\001 \002(\t\022\n\n\002id\030\002 \002(\005", 64); 84 | ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( 85 | "echoMsg.proto", &protobuf_RegisterTypes); 86 | EchoString::default_instance_ = new EchoString(); 87 | EchoString::default_instance_->InitAsDefaultInstance(); 88 | ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_echoMsg_2eproto); 89 | } 90 | 91 | // Force AddDescriptors() to be called at static initialization time. 92 | struct StaticDescriptorInitializer_echoMsg_2eproto { 93 | StaticDescriptorInitializer_echoMsg_2eproto() { 94 | protobuf_AddDesc_echoMsg_2eproto(); 95 | } 96 | } static_descriptor_initializer_echoMsg_2eproto_; 97 | 98 | // =================================================================== 99 | 100 | #ifndef _MSC_VER 101 | const int EchoString::kContentFieldNumber; 102 | const int EchoString::kIdFieldNumber; 103 | #endif // !_MSC_VER 104 | 105 | EchoString::EchoString() 106 | : ::google::protobuf::Message() { 107 | SharedCtor(); 108 | // @@protoc_insertion_point(constructor:echo.EchoString) 109 | } 110 | 111 | void EchoString::InitAsDefaultInstance() { 112 | } 113 | 114 | EchoString::EchoString(const EchoString& from) 115 | : ::google::protobuf::Message() { 116 | SharedCtor(); 117 | MergeFrom(from); 118 | // @@protoc_insertion_point(copy_constructor:echo.EchoString) 119 | } 120 | 121 | void EchoString::SharedCtor() { 122 | ::google::protobuf::internal::GetEmptyString(); 123 | _cached_size_ = 0; 124 | content_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); 125 | id_ = 0; 126 | ::memset(_has_bits_, 0, sizeof(_has_bits_)); 127 | } 128 | 129 | EchoString::~EchoString() { 130 | // @@protoc_insertion_point(destructor:echo.EchoString) 131 | SharedDtor(); 132 | } 133 | 134 | void EchoString::SharedDtor() { 135 | if (content_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 136 | delete content_; 137 | } 138 | if (this != default_instance_) { 139 | } 140 | } 141 | 142 | void EchoString::SetCachedSize(int size) const { 143 | GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); 144 | _cached_size_ = size; 145 | GOOGLE_SAFE_CONCURRENT_WRITES_END(); 146 | } 147 | const ::google::protobuf::Descriptor* EchoString::descriptor() { 148 | protobuf_AssignDescriptorsOnce(); 149 | return EchoString_descriptor_; 150 | } 151 | 152 | const EchoString& EchoString::default_instance() { 153 | if (default_instance_ == NULL) protobuf_AddDesc_echoMsg_2eproto(); 154 | return *default_instance_; 155 | } 156 | 157 | EchoString* EchoString::default_instance_ = NULL; 158 | 159 | EchoString* EchoString::New() const { 160 | return new EchoString; 161 | } 162 | 163 | void EchoString::Clear() { 164 | if (_has_bits_[0 / 32] & 3) { 165 | if (has_content()) { 166 | if (content_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 167 | content_->clear(); 168 | } 169 | } 170 | id_ = 0; 171 | } 172 | ::memset(_has_bits_, 0, sizeof(_has_bits_)); 173 | mutable_unknown_fields()->Clear(); 174 | } 175 | 176 | bool EchoString::MergePartialFromCodedStream( 177 | ::google::protobuf::io::CodedInputStream* input) { 178 | #define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure 179 | ::google::protobuf::uint32 tag; 180 | // @@protoc_insertion_point(parse_start:echo.EchoString) 181 | for (;;) { 182 | ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127); 183 | tag = p.first; 184 | if (!p.second) goto handle_unusual; 185 | switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { 186 | // required string content = 1; 187 | case 1: { 188 | if (tag == 10) { 189 | DO_(::google::protobuf::internal::WireFormatLite::ReadString( 190 | input, this->mutable_content())); 191 | ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( 192 | this->content().data(), this->content().length(), 193 | ::google::protobuf::internal::WireFormat::PARSE, 194 | "content"); 195 | } else { 196 | goto handle_unusual; 197 | } 198 | if (input->ExpectTag(16)) goto parse_id; 199 | break; 200 | } 201 | 202 | // required int32 id = 2; 203 | case 2: { 204 | if (tag == 16) { 205 | parse_id: 206 | DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< 207 | ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( 208 | input, &id_))); 209 | set_has_id(); 210 | } else { 211 | goto handle_unusual; 212 | } 213 | if (input->ExpectAtEnd()) goto success; 214 | break; 215 | } 216 | 217 | default: { 218 | handle_unusual: 219 | if (tag == 0 || 220 | ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == 221 | ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { 222 | goto success; 223 | } 224 | DO_(::google::protobuf::internal::WireFormat::SkipField( 225 | input, tag, mutable_unknown_fields())); 226 | break; 227 | } 228 | } 229 | } 230 | success: 231 | // @@protoc_insertion_point(parse_success:echo.EchoString) 232 | return true; 233 | failure: 234 | // @@protoc_insertion_point(parse_failure:echo.EchoString) 235 | return false; 236 | #undef DO_ 237 | } 238 | 239 | void EchoString::SerializeWithCachedSizes( 240 | ::google::protobuf::io::CodedOutputStream* output) const { 241 | // @@protoc_insertion_point(serialize_start:echo.EchoString) 242 | // required string content = 1; 243 | if (has_content()) { 244 | ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( 245 | this->content().data(), this->content().length(), 246 | ::google::protobuf::internal::WireFormat::SERIALIZE, 247 | "content"); 248 | ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( 249 | 1, this->content(), output); 250 | } 251 | 252 | // required int32 id = 2; 253 | if (has_id()) { 254 | ::google::protobuf::internal::WireFormatLite::WriteInt32(2, this->id(), output); 255 | } 256 | 257 | if (!unknown_fields().empty()) { 258 | ::google::protobuf::internal::WireFormat::SerializeUnknownFields( 259 | unknown_fields(), output); 260 | } 261 | // @@protoc_insertion_point(serialize_end:echo.EchoString) 262 | } 263 | 264 | ::google::protobuf::uint8* EchoString::SerializeWithCachedSizesToArray( 265 | ::google::protobuf::uint8* target) const { 266 | // @@protoc_insertion_point(serialize_to_array_start:echo.EchoString) 267 | // required string content = 1; 268 | if (has_content()) { 269 | ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( 270 | this->content().data(), this->content().length(), 271 | ::google::protobuf::internal::WireFormat::SERIALIZE, 272 | "content"); 273 | target = 274 | ::google::protobuf::internal::WireFormatLite::WriteStringToArray( 275 | 1, this->content(), target); 276 | } 277 | 278 | // required int32 id = 2; 279 | if (has_id()) { 280 | target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(2, this->id(), target); 281 | } 282 | 283 | if (!unknown_fields().empty()) { 284 | target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( 285 | unknown_fields(), target); 286 | } 287 | // @@protoc_insertion_point(serialize_to_array_end:echo.EchoString) 288 | return target; 289 | } 290 | 291 | int EchoString::ByteSize() const { 292 | int total_size = 0; 293 | 294 | if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { 295 | // required string content = 1; 296 | if (has_content()) { 297 | total_size += 1 + 298 | ::google::protobuf::internal::WireFormatLite::StringSize( 299 | this->content()); 300 | } 301 | 302 | // required int32 id = 2; 303 | if (has_id()) { 304 | total_size += 1 + 305 | ::google::protobuf::internal::WireFormatLite::Int32Size( 306 | this->id()); 307 | } 308 | 309 | } 310 | if (!unknown_fields().empty()) { 311 | total_size += 312 | ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( 313 | unknown_fields()); 314 | } 315 | GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); 316 | _cached_size_ = total_size; 317 | GOOGLE_SAFE_CONCURRENT_WRITES_END(); 318 | return total_size; 319 | } 320 | 321 | void EchoString::MergeFrom(const ::google::protobuf::Message& from) { 322 | GOOGLE_CHECK_NE(&from, this); 323 | const EchoString* source = 324 | ::google::protobuf::internal::dynamic_cast_if_available( 325 | &from); 326 | if (source == NULL) { 327 | ::google::protobuf::internal::ReflectionOps::Merge(from, this); 328 | } else { 329 | MergeFrom(*source); 330 | } 331 | } 332 | 333 | void EchoString::MergeFrom(const EchoString& from) { 334 | GOOGLE_CHECK_NE(&from, this); 335 | if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { 336 | if (from.has_content()) { 337 | set_content(from.content()); 338 | } 339 | if (from.has_id()) { 340 | set_id(from.id()); 341 | } 342 | } 343 | mutable_unknown_fields()->MergeFrom(from.unknown_fields()); 344 | } 345 | 346 | void EchoString::CopyFrom(const ::google::protobuf::Message& from) { 347 | if (&from == this) return; 348 | Clear(); 349 | MergeFrom(from); 350 | } 351 | 352 | void EchoString::CopyFrom(const EchoString& from) { 353 | if (&from == this) return; 354 | Clear(); 355 | MergeFrom(from); 356 | } 357 | 358 | bool EchoString::IsInitialized() const { 359 | if ((_has_bits_[0] & 0x00000003) != 0x00000003) return false; 360 | 361 | return true; 362 | } 363 | 364 | void EchoString::Swap(EchoString* other) { 365 | if (other != this) { 366 | std::swap(content_, other->content_); 367 | std::swap(id_, other->id_); 368 | std::swap(_has_bits_[0], other->_has_bits_[0]); 369 | _unknown_fields_.Swap(&other->_unknown_fields_); 370 | std::swap(_cached_size_, other->_cached_size_); 371 | } 372 | } 373 | 374 | ::google::protobuf::Metadata EchoString::GetMetadata() const { 375 | protobuf_AssignDescriptorsOnce(); 376 | ::google::protobuf::Metadata metadata; 377 | metadata.descriptor = EchoString_descriptor_; 378 | metadata.reflection = EchoString_reflection_; 379 | return metadata; 380 | } 381 | 382 | 383 | // @@protoc_insertion_point(namespace_scope) 384 | 385 | } // namespace echo 386 | 387 | // @@protoc_insertion_point(global_scope) 388 | -------------------------------------------------------------------------------- /example/udpEchoServer/echoMsg.pb.h: -------------------------------------------------------------------------------- 1 | // Generated by the protocol buffer compiler. DO NOT EDIT! 2 | // source: echoMsg.proto 3 | 4 | #ifndef PROTOBUF_echoMsg_2eproto__INCLUDED 5 | #define PROTOBUF_echoMsg_2eproto__INCLUDED 6 | 7 | #include 8 | 9 | #include 10 | 11 | #if GOOGLE_PROTOBUF_VERSION < 2006000 12 | #error This file was generated by a newer version of protoc which is 13 | #error incompatible with your Protocol Buffer headers. Please update 14 | #error your headers. 15 | #endif 16 | #if 2006001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION 17 | #error This file was generated by an older version of protoc which is 18 | #error incompatible with your Protocol Buffer headers. Please 19 | #error regenerate this file with a newer version of protoc. 20 | #endif 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | // @@protoc_insertion_point(includes) 28 | 29 | namespace echo { 30 | 31 | // Internal implementation detail -- do not call these. 32 | void protobuf_AddDesc_echoMsg_2eproto(); 33 | void protobuf_AssignDesc_echoMsg_2eproto(); 34 | void protobuf_ShutdownFile_echoMsg_2eproto(); 35 | 36 | class EchoString; 37 | 38 | // =================================================================== 39 | 40 | class EchoString : public ::google::protobuf::Message { 41 | public: 42 | EchoString(); 43 | virtual ~EchoString(); 44 | 45 | EchoString(const EchoString& from); 46 | 47 | inline EchoString& operator=(const EchoString& from) { 48 | CopyFrom(from); 49 | return *this; 50 | } 51 | 52 | inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { 53 | return _unknown_fields_; 54 | } 55 | 56 | inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { 57 | return &_unknown_fields_; 58 | } 59 | 60 | static const ::google::protobuf::Descriptor* descriptor(); 61 | static const EchoString& default_instance(); 62 | 63 | void Swap(EchoString* other); 64 | 65 | // implements Message ---------------------------------------------- 66 | 67 | EchoString* New() const; 68 | void CopyFrom(const ::google::protobuf::Message& from); 69 | void MergeFrom(const ::google::protobuf::Message& from); 70 | void CopyFrom(const EchoString& from); 71 | void MergeFrom(const EchoString& from); 72 | void Clear(); 73 | bool IsInitialized() const; 74 | 75 | int ByteSize() const; 76 | bool MergePartialFromCodedStream( 77 | ::google::protobuf::io::CodedInputStream* input); 78 | void SerializeWithCachedSizes( 79 | ::google::protobuf::io::CodedOutputStream* output) const; 80 | ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; 81 | int GetCachedSize() const { return _cached_size_; } 82 | private: 83 | void SharedCtor(); 84 | void SharedDtor(); 85 | void SetCachedSize(int size) const; 86 | public: 87 | ::google::protobuf::Metadata GetMetadata() const; 88 | 89 | // nested types ---------------------------------------------------- 90 | 91 | // accessors ------------------------------------------------------- 92 | 93 | // required string content = 1; 94 | inline bool has_content() const; 95 | inline void clear_content(); 96 | static const int kContentFieldNumber = 1; 97 | inline const ::std::string& content() const; 98 | inline void set_content(const ::std::string& value); 99 | inline void set_content(const char* value); 100 | inline void set_content(const char* value, size_t size); 101 | inline ::std::string* mutable_content(); 102 | inline ::std::string* release_content(); 103 | inline void set_allocated_content(::std::string* content); 104 | 105 | // required int32 id = 2; 106 | inline bool has_id() const; 107 | inline void clear_id(); 108 | static const int kIdFieldNumber = 2; 109 | inline ::google::protobuf::int32 id() const; 110 | inline void set_id(::google::protobuf::int32 value); 111 | 112 | // @@protoc_insertion_point(class_scope:echo.EchoString) 113 | private: 114 | inline void set_has_content(); 115 | inline void clear_has_content(); 116 | inline void set_has_id(); 117 | inline void clear_has_id(); 118 | 119 | ::google::protobuf::UnknownFieldSet _unknown_fields_; 120 | 121 | ::google::protobuf::uint32 _has_bits_[1]; 122 | mutable int _cached_size_; 123 | ::std::string* content_; 124 | ::google::protobuf::int32 id_; 125 | friend void protobuf_AddDesc_echoMsg_2eproto(); 126 | friend void protobuf_AssignDesc_echoMsg_2eproto(); 127 | friend void protobuf_ShutdownFile_echoMsg_2eproto(); 128 | 129 | void InitAsDefaultInstance(); 130 | static EchoString* default_instance_; 131 | }; 132 | // =================================================================== 133 | 134 | 135 | // =================================================================== 136 | 137 | // EchoString 138 | 139 | // required string content = 1; 140 | inline bool EchoString::has_content() const { 141 | return (_has_bits_[0] & 0x00000001u) != 0; 142 | } 143 | inline void EchoString::set_has_content() { 144 | _has_bits_[0] |= 0x00000001u; 145 | } 146 | inline void EchoString::clear_has_content() { 147 | _has_bits_[0] &= ~0x00000001u; 148 | } 149 | inline void EchoString::clear_content() { 150 | if (content_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 151 | content_->clear(); 152 | } 153 | clear_has_content(); 154 | } 155 | inline const ::std::string& EchoString::content() const { 156 | // @@protoc_insertion_point(field_get:echo.EchoString.content) 157 | return *content_; 158 | } 159 | inline void EchoString::set_content(const ::std::string& value) { 160 | set_has_content(); 161 | if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 162 | content_ = new ::std::string; 163 | } 164 | content_->assign(value); 165 | // @@protoc_insertion_point(field_set:echo.EchoString.content) 166 | } 167 | inline void EchoString::set_content(const char* value) { 168 | set_has_content(); 169 | if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 170 | content_ = new ::std::string; 171 | } 172 | content_->assign(value); 173 | // @@protoc_insertion_point(field_set_char:echo.EchoString.content) 174 | } 175 | inline void EchoString::set_content(const char* value, size_t size) { 176 | set_has_content(); 177 | if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 178 | content_ = new ::std::string; 179 | } 180 | content_->assign(reinterpret_cast(value), size); 181 | // @@protoc_insertion_point(field_set_pointer:echo.EchoString.content) 182 | } 183 | inline ::std::string* EchoString::mutable_content() { 184 | set_has_content(); 185 | if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 186 | content_ = new ::std::string; 187 | } 188 | // @@protoc_insertion_point(field_mutable:echo.EchoString.content) 189 | return content_; 190 | } 191 | inline ::std::string* EchoString::release_content() { 192 | clear_has_content(); 193 | if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 194 | return NULL; 195 | } else { 196 | ::std::string* temp = content_; 197 | content_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); 198 | return temp; 199 | } 200 | } 201 | inline void EchoString::set_allocated_content(::std::string* content) { 202 | if (content_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 203 | delete content_; 204 | } 205 | if (content) { 206 | set_has_content(); 207 | content_ = content; 208 | } else { 209 | clear_has_content(); 210 | content_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); 211 | } 212 | // @@protoc_insertion_point(field_set_allocated:echo.EchoString.content) 213 | } 214 | 215 | // required int32 id = 2; 216 | inline bool EchoString::has_id() const { 217 | return (_has_bits_[0] & 0x00000002u) != 0; 218 | } 219 | inline void EchoString::set_has_id() { 220 | _has_bits_[0] |= 0x00000002u; 221 | } 222 | inline void EchoString::clear_has_id() { 223 | _has_bits_[0] &= ~0x00000002u; 224 | } 225 | inline void EchoString::clear_id() { 226 | id_ = 0; 227 | clear_has_id(); 228 | } 229 | inline ::google::protobuf::int32 EchoString::id() const { 230 | // @@protoc_insertion_point(field_get:echo.EchoString.id) 231 | return id_; 232 | } 233 | inline void EchoString::set_id(::google::protobuf::int32 value) { 234 | set_has_id(); 235 | id_ = value; 236 | // @@protoc_insertion_point(field_set:echo.EchoString.id) 237 | } 238 | 239 | 240 | // @@protoc_insertion_point(namespace_scope) 241 | 242 | } // namespace echo 243 | 244 | #ifndef SWIG 245 | namespace google { 246 | namespace protobuf { 247 | 248 | 249 | } // namespace google 250 | } // namespace protobuf 251 | #endif // SWIG 252 | 253 | // @@protoc_insertion_point(global_scope) 254 | 255 | #endif // PROTOBUF_echoMsg_2eproto__INCLUDED 256 | -------------------------------------------------------------------------------- /example/udpEchoServer/echoMsg.proto: -------------------------------------------------------------------------------- 1 | package echo; 2 | 3 | message EchoString 4 | { 5 | required string content = 1; 6 | required int32 id = 2; 7 | }; 8 | -------------------------------------------------------------------------------- /example/udpEchoServer/makefile: -------------------------------------------------------------------------------- 1 | CXX=g++ 2 | CFLAGS=-g -O2 -Wall -fPIC -Wno-deprecated 3 | 4 | INC=-I../../include 5 | LIB=-L../../lib -lereactor /usr/local/lib/libprotobuf.a -lrt 6 | OBJS = $(addsuffix .o, $(basename $(wildcard *.cc))) 7 | 8 | all: 9 | $(CXX) -o server.prog $(CFLAGS) server.cc echoMsg.pb.cc $(INC) $(LIB) 10 | $(CXX) -o client.prog $(CFLAGS) client.cc echoMsg.pb.cc $(INC) $(LIB) 11 | 12 | clean: 13 | -rm -f *.o *.d server client 14 | -------------------------------------------------------------------------------- /example/udpEchoServer/server.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "echoMsg.pb.h" 3 | #include "easy_reactor.h" 4 | 5 | using namespace std; 6 | using namespace echo; 7 | 8 | void buz(const char* data, uint32_t len, int cmdid, net_commu* commu, void* usr_data) 9 | { 10 | EchoString req, rsp; 11 | req.ParseFromArray(data, len);//解包,data[0:len)保证是一个完整包 12 | rsp.set_id(req.id()); 13 | rsp.set_content(req.content()); 14 | string rspStr; 15 | rsp.SerializeToString(&rspStr); 16 | commu->send_data(rspStr.c_str(), rspStr.size(), cmdid);//回复消息 17 | } 18 | 19 | int main() 20 | { 21 | event_loop loop; 22 | config_reader::setPath("myconf.ini"); 23 | string ip = config_reader::ins()->GetString("reactor", "ip", "0.0.0.0"); 24 | short port = config_reader::ins()->GetNumber("reactor", "port", 12315); 25 | 26 | udp_server server(&loop, ip.c_str(), port);//创建UDP服务器 27 | 28 | server.add_msg_cb(1, buz);//设置:当收到消息id = 1的消息调用的回调函数 我们约定EchoString消息的ID是1 29 | loop.process_evs(); 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /include/config_reader.h: -------------------------------------------------------------------------------- 1 | #ifndef __CONFIG_READER_HEADER__ 2 | #define __CONFIG_READER_HEADER__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | typedef std::map*> STR_MAP; 9 | typedef STR_MAP::iterator STR_MAP_ITER; 10 | 11 | class config_reader { 12 | public: 13 | ~config_reader(); 14 | std::string GetString(const std::string& section, const std::string& key, const std::string& default_value = ""); 15 | std::vector GetStringList(const std::string& section, const std::string& key); 16 | unsigned GetNumber(const std::string& section, const std::string& key, unsigned default_value = 0); 17 | bool GetBool(const std::string& section, const std::string& key, bool default_value = false); 18 | float GetFloat(const std::string& section, const std::string& key, const float& default_value); 19 | 20 | static bool setPath(const std::string& path); 21 | static config_reader *ins(); 22 | private: 23 | config_reader() { } 24 | 25 | bool isSection(std::string line, std::string& section); 26 | unsigned parseNumber(const std::string& s); 27 | std::string trimLeft(const std::string& s); 28 | std::string trimRight(const std::string& s); 29 | std::string trim(const std::string& s); 30 | bool Load(const std::string& path); 31 | 32 | static config_reader* config; 33 | 34 | STR_MAP _map; 35 | }; 36 | 37 | #endif -------------------------------------------------------------------------------- /include/easy_reactor.h: -------------------------------------------------------------------------------- 1 | #ifndef __EASY_REACTOR_H__ 2 | #define __EASY_REACTOR_H__ 3 | 4 | #include "tcp_server.h" 5 | #include "udp_server.h" 6 | #include "udp_client.h" 7 | #include "tcp_client.h" 8 | #include "net_commu.h" 9 | #include "msg_head.h" 10 | #include "thread_queue.h" 11 | #include "config_reader.h" 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /include/event_base.h: -------------------------------------------------------------------------------- 1 | #ifndef __EVENT_BASE_H__ 2 | #define __EVENT_BASE_H__ 3 | 4 | #include 5 | #include //NULL 6 | class event_loop; 7 | 8 | typedef void io_callback(event_loop* loop, int fd, void *args);//IO事件回调函数 9 | typedef void timer_callback(event_loop* loop, void* usr_data);//Timer事件回调函数 10 | 11 | //让当前loop在一次poll循环后执行指定任务 12 | typedef void (*pendingFunc)(event_loop*, void *); 13 | 14 | struct io_event//注册的IO事件 15 | { 16 | io_event(): read_cb(NULL), write_cb(NULL), rcb_args(NULL), wcb_args(NULL) { } 17 | int mask; //EPOLLIN EPOLLOUT 18 | io_callback* read_cb; //callback when EPOLLIN comming 19 | io_callback* write_cb; //callback when EPOLLOUT comming 20 | void* rcb_args; //extra arguments for read_cb 21 | void* wcb_args; //extra arguments for write_cb 22 | }; 23 | 24 | struct timer_event//注册的Timer事件 25 | { 26 | timer_event(timer_callback* timo_cb, void* data, uint64_t arg_ts, uint32_t arg_int = 0): 27 | cb(timo_cb), cb_data(data), ts(arg_ts), interval(arg_int) 28 | { 29 | } 30 | 31 | timer_callback* cb; 32 | void* cb_data; 33 | uint64_t ts; 34 | uint32_t interval;//interval millis 35 | int timer_id; 36 | }; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /include/event_loop.h: -------------------------------------------------------------------------------- 1 | #ifndef __EVENT_LOOP_H__ 2 | #define __EVENT_LOOP_H__ 3 | 4 | #include "event_base.h" 5 | #include "timer_queue.h" 6 | #include 7 | #include 8 | #include 9 | 10 | #define MAXEVENTS 10 11 | 12 | class event_loop 13 | { 14 | public: 15 | event_loop(); 16 | void process_evs(); 17 | 18 | //operator for IO event 19 | void add_ioev(int fd, io_callback* proc, int mask, void* args = NULL); 20 | //delete only mask event for fd in epoll 21 | void del_ioev(int fd, int mask); 22 | //delete event for fd in epoll 23 | void del_ioev(int fd); 24 | //get all fds this loop is listening 25 | void nlistenings(__gnu_cxx::hash_set& conns) { conns = listening; } 26 | 27 | //operator for timer event 28 | int run_at(timer_callback cb, void* args, uint64_t ts); 29 | int run_after(timer_callback cb, void* args, int sec, int millis = 0); 30 | int run_every(timer_callback cb, void* args, int sec, int millis = 0); 31 | void del_timer(int timer_id); 32 | 33 | void add_task(pendingFunc func, void* args); 34 | void run_task(); 35 | 36 | private: 37 | int _epfd; 38 | struct epoll_event _fired_evs[MAXEVENTS]; 39 | //map: fd->io_event 40 | __gnu_cxx::hash_map _io_evs; 41 | typedef __gnu_cxx::hash_map::iterator ioev_it; 42 | timer_queue* _timer_que; 43 | //此队列用于:暂存将要执行的任务 44 | std::vector > _pendingFactors; 45 | 46 | __gnu_cxx::hash_set listening; 47 | 48 | friend void timerqueue_cb(event_loop* loop, int fd, void *args); 49 | }; 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /include/io_buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef __IO_BUFFER_H__ 2 | #define __IO_BUFFER_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | struct io_buffer 12 | { 13 | io_buffer(int size): 14 | capacity(size), length(0), head(0), next(NULL) 15 | { 16 | data = new char[size]; 17 | assert(data); 18 | } 19 | 20 | void clear() 21 | { 22 | length = head = 0; 23 | } 24 | 25 | void adjust()//move data to head 26 | { 27 | if (head) 28 | { 29 | if (length) 30 | { 31 | ::memmove(data, data + head, length); 32 | } 33 | head = 0; 34 | } 35 | } 36 | 37 | void copy(const io_buffer* other) 38 | { 39 | //only copy data to myself 40 | ::memcpy(data, other->data + other->head, other->length); 41 | head = 0; 42 | length = other->length; 43 | } 44 | 45 | void pop(int len) 46 | { 47 | length -= len; 48 | head += len; 49 | } 50 | 51 | int capacity; 52 | int length; 53 | int head; 54 | io_buffer* next; 55 | char* data; 56 | }; 57 | 58 | class buffer_pool 59 | { 60 | public: 61 | enum MEM_CAP 62 | { 63 | u4K = 4096, 64 | u16K = 16384, 65 | u64K = 65536, 66 | u256K = 262144, 67 | u1M = 1048576, 68 | u4M = 4194304, 69 | u8M = 8388608 70 | }; 71 | 72 | static void init() 73 | { 74 | _ins = new buffer_pool(); 75 | assert(_ins); 76 | } 77 | 78 | static buffer_pool* ins() 79 | { 80 | pthread_once(&_once, init); 81 | return _ins; 82 | } 83 | 84 | io_buffer* alloc(int N); 85 | 86 | io_buffer* alloc() { return alloc(u4K); } 87 | 88 | void revert(io_buffer* buffer); 89 | 90 | private: 91 | buffer_pool(); 92 | 93 | buffer_pool(const buffer_pool&); 94 | const buffer_pool& operator=(const buffer_pool&); 95 | 96 | typedef __gnu_cxx::hash_map pool_t; 97 | pool_t _pool; 98 | uint64_t _total_mem; 99 | static buffer_pool* _ins; 100 | static pthread_mutex_t _mutex; 101 | static pthread_once_t _once; 102 | }; 103 | 104 | struct tcp_buffer 105 | { 106 | tcp_buffer(): _buf(NULL) { } 107 | 108 | ~tcp_buffer() { clear(); } 109 | 110 | const int length() const { return _buf? _buf->length: 0; } 111 | 112 | void pop(int len); 113 | 114 | void clear(); 115 | 116 | protected: 117 | io_buffer* _buf; 118 | }; 119 | 120 | class input_buffer: public tcp_buffer 121 | { 122 | public: 123 | int read_data(int fd); 124 | 125 | const char* data() const { return _buf? _buf->data + _buf->head: NULL; } 126 | 127 | void adjust() { if (_buf) _buf->adjust(); } 128 | }; 129 | 130 | class output_buffer: public tcp_buffer 131 | { 132 | public: 133 | int send_data(const char* data, int datlen); 134 | 135 | int write_fd(int fd); 136 | }; 137 | 138 | #endif 139 | -------------------------------------------------------------------------------- /include/msg_dispatcher.h: -------------------------------------------------------------------------------- 1 | #ifndef __MSG_DISPATCHER_H__ 2 | #define __MSG_DISPATCHER_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "net_commu.h" 8 | 9 | typedef void msg_callback(const char* data, uint32_t len, int cmdid, net_commu* commu, void* usr_data); 10 | 11 | class msg_dispatcher 12 | { 13 | public: 14 | msg_dispatcher() {} 15 | 16 | int add_msg_cb(int cmdid, msg_callback* msg_cb, void* usr_data) 17 | { 18 | if (_dispatcher.find(cmdid) != _dispatcher.end()) return -1; 19 | _dispatcher[cmdid] = msg_cb; 20 | _args[cmdid] = usr_data; 21 | return 0; 22 | } 23 | 24 | bool exist(int cmdid) const { return _dispatcher.find(cmdid) != _dispatcher.end(); } 25 | 26 | void cb(const char* data, uint32_t len, int cmdid, net_commu* commu) 27 | { 28 | assert(exist(cmdid)); 29 | msg_callback* func = _dispatcher[cmdid]; 30 | void* usr_data = _args[cmdid]; 31 | func(data, len, cmdid, commu, usr_data); 32 | } 33 | 34 | private: 35 | __gnu_cxx::hash_map _dispatcher; 36 | __gnu_cxx::hash_map _args; 37 | }; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /include/msg_head.h: -------------------------------------------------------------------------------- 1 | #ifndef __MSG_HEAD_H__ 2 | #define __MSG_HEAD_H__ 3 | 4 | class event_loop; 5 | 6 | struct commu_head 7 | { 8 | int cmdid; 9 | int length; 10 | }; 11 | 12 | //for accepter communicate with connections 13 | //for give task to sub-thread 14 | struct queue_msg 15 | { 16 | enum MSG_TYPE 17 | { 18 | NEW_CONN, 19 | STOP_THD, 20 | NEW_TASK, 21 | }; 22 | MSG_TYPE cmd_type; 23 | 24 | union { 25 | int connfd;//for NEW_CONN, 向sub-thread下发新连接 26 | struct 27 | { 28 | void (*task)(event_loop*, void*); 29 | void *args; 30 | };//for NEW_TASK, 向sub-thread下发待执行任务 31 | }; 32 | }; 33 | 34 | #define COMMU_HEAD_LENGTH 8 35 | 36 | #define MSG_LENGTH_LIMIT (65536 - COMMU_HEAD_LENGTH) 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /include/net_commu.h: -------------------------------------------------------------------------------- 1 | #ifndef __NET_COMMU_H__ 2 | #define __NET_COMMU_H__ 3 | 4 | #include 5 | 6 | class net_commu 7 | { 8 | public: 9 | net_commu(): parameter(NULL) {} 10 | 11 | virtual int send_data(const char* data, int datalen, int cmdid) = 0; 12 | virtual int get_fd() = 0; 13 | 14 | void* parameter;//每个TCP客户端连接类可以使用此参数设置自己的连接内变量 15 | }; 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /include/print_error.h: -------------------------------------------------------------------------------- 1 | #ifndef __PRINT_ERROR_H__ 2 | #define __PRINT_ERROR_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define error_if(condition, fmt, args...) \ 11 | do { \ 12 | if (condition) { \ 13 | if (errno) { \ 14 | fprintf(stderr, "ERROR: %s " fmt "\n", strerror(errno), ##args); \ 15 | } else { \ 16 | fprintf(stderr, "ERROR:" fmt "\n", ##args); \ 17 | } \ 18 | } \ 19 | } while (0) 20 | 21 | #define info_log(fmt, args...) fprintf(stdout, fmt "\n", ##args) 22 | 23 | #define error_log(fmt, args...) error_if(1, fmt, ##args) 24 | 25 | inline void exit_if(int condition, const char *fmt, ...) 26 | { 27 | va_list arglist; 28 | 29 | if (condition) 30 | { 31 | va_start(arglist, fmt); 32 | fprintf(stderr, "ERROR: "); 33 | vfprintf(stderr, fmt, arglist); 34 | if (errno) 35 | { 36 | fprintf(stderr, ": %s", strerror(errno)); 37 | } 38 | fprintf(stderr, "\n"); 39 | va_end(arglist); 40 | exit(EXIT_FAILURE); 41 | } 42 | } 43 | 44 | #define exit_log(fmt, args...) exit_if(1, fmt, ##args) 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /include/tcp_client.h: -------------------------------------------------------------------------------- 1 | #ifndef __TCP_CLIENT_H__ 2 | #define __TCP_CLIENT_H__ 3 | 4 | #include 5 | #include "net_commu.h" 6 | #include "io_buffer.h" 7 | #include "event_loop.h" 8 | #include "msg_dispatcher.h" 9 | 10 | class tcp_client: public net_commu 11 | { 12 | public: 13 | tcp_client(event_loop* loop, const char* ip, unsigned short port, const char* name = NULL); 14 | 15 | typedef void onconn_func(tcp_client* client, void* args); 16 | typedef void oncls_func(tcp_client* client, void* args); 17 | 18 | //set up function after connection ok 19 | void onConnection(onconn_func* func, void *args = NULL) 20 | { 21 | _onconnection = func; 22 | _onconn_args = args; 23 | } 24 | 25 | //set up function after connection closed 26 | void onClose(oncls_func* func, void *args = NULL) 27 | { 28 | _onclose = func; 29 | _onclose_args = args; 30 | } 31 | 32 | void call_onconnect() 33 | { 34 | if (_onconnection) 35 | _onconnection(this, _onconn_args); 36 | } 37 | 38 | void call_onclose() 39 | { 40 | if (_onclose) 41 | _onclose(this, _onclose_args); 42 | } 43 | 44 | void add_msg_cb(int cmdid, msg_callback* msg_cb, void* usr_data = NULL) { _dispatcher.add_msg_cb(cmdid, msg_cb, usr_data); } 45 | 46 | void do_connect(); 47 | 48 | virtual int send_data(const char* data, int datlen, int cmdid); 49 | 50 | virtual int get_fd() { return _sockfd; } 51 | 52 | int handle_read(); 53 | 54 | int handle_write(); 55 | 56 | ~tcp_client() { ::close(_sockfd); } 57 | 58 | void clean_conn(); 59 | 60 | event_loop* loop() { return _loop; } 61 | 62 | bool net_ok; 63 | io_buffer ibuf, obuf; 64 | struct sockaddr_in servaddr; 65 | private: 66 | int _sockfd; 67 | event_loop* _loop; 68 | socklen_t _addrlen; 69 | msg_dispatcher _dispatcher; 70 | //when connection success, call _onconnection(_onconn_args) 71 | onconn_func* _onconnection; 72 | void* _onconn_args; 73 | //when connection close, call _onclose(_onclose_args) 74 | oncls_func* _onclose; 75 | void* _onclose_args; 76 | const char* _name; 77 | }; 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /include/tcp_conn.h: -------------------------------------------------------------------------------- 1 | #ifndef __TCP_CONN_H__ 2 | #define __TCP_CONN_H__ 3 | 4 | #include "event_loop.h" 5 | #include "io_buffer.h" 6 | #include "net_commu.h" 7 | 8 | class tcp_conn: public net_commu 9 | { 10 | public: 11 | tcp_conn(int connfd, event_loop* loop) { init(connfd, loop); } 12 | 13 | void init(int connfd, event_loop* loop); 14 | 15 | void handle_read(); 16 | 17 | void handle_write(); 18 | 19 | virtual int send_data(const char* data, int datlen, int cmdid); 20 | 21 | virtual int get_fd() { return _connfd; } 22 | 23 | void clean_conn(); 24 | private: 25 | int _connfd; 26 | event_loop* _loop; 27 | input_buffer ibuf; 28 | output_buffer obuf; 29 | }; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /include/tcp_server.h: -------------------------------------------------------------------------------- 1 | #ifndef __TCP_SERVER_H__ 2 | #define __TCP_SERVER_H__ 3 | 4 | #include "event_loop.h" 5 | #include "thread_pool.h" 6 | #include "net_commu.h" 7 | #include "tcp_conn.h" 8 | #include "msg_dispatcher.h" 9 | #include 10 | 11 | class tcp_server 12 | { 13 | public: 14 | tcp_server(event_loop* loop, const char* ip, uint16_t port); 15 | 16 | ~tcp_server();//tcp_server类使用时往往具有程序的完全生命周期,其实并不需要析构函数 17 | 18 | void keep_alive() { _keepalive = true; } 19 | 20 | void do_accept(); 21 | 22 | void add_msg_cb(int cmdid, msg_callback* msg_cb, void* usr_data = NULL) { dispatcher.add_msg_cb(cmdid, msg_cb, usr_data); } 23 | 24 | static void inc_conn(); 25 | static void get_conn_num(int& cnt); 26 | static void dec_conn(); 27 | 28 | event_loop* loop() { return _loop; } 29 | 30 | thread_pool* threadPool() { return _thd_pool; } 31 | 32 | private: 33 | int _sockfd; 34 | int _reservfd; 35 | event_loop* _loop; 36 | thread_pool* _thd_pool; 37 | struct sockaddr_in _connaddr; 38 | socklen_t _addrlen; 39 | bool _keepalive; 40 | 41 | static int _conns_size; 42 | static int _max_conns; 43 | static int _curr_conns; 44 | static pthread_mutex_t _mutex; 45 | public: 46 | static msg_dispatcher dispatcher; 47 | static tcp_conn** conns; 48 | 49 | typedef void (*conn_callback)(net_commu* com); 50 | 51 | static conn_callback connBuildCb;//用户设置连接建立后的回调函数 52 | static conn_callback connCloseCb;//用户设置连接释放后的回调函数 53 | 54 | static void onConnBuild(conn_callback cb) { connBuildCb = cb; } 55 | static void onConnClose(conn_callback cb) { connCloseCb = cb; } 56 | }; 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /include/thread_pool.h: -------------------------------------------------------------------------------- 1 | #ifndef __THREAD_POOL_H__ 2 | #define __THREAD_POOL_H__ 3 | 4 | #include "msg_head.h" 5 | #include "thread_queue.h" 6 | #include 7 | 8 | extern void* thread_domain(void* args); 9 | 10 | class thread_pool 11 | { 12 | public: 13 | thread_pool(int thread_cnt); 14 | 15 | //~thread_pool(); 16 | //由于thread_pool是给tcp_server类使用的,而tcp_server类使用时往往具有程序的完全生命周期,即程序退出时会自动释放内存,不需要析构函数去delete内存 17 | 18 | thread_queue* get_next_thread(); 19 | 20 | void run_task(int thd_index, pendingFunc task, void* args = NULL); 21 | 22 | void run_task(pendingFunc task, void* args = NULL); 23 | 24 | private: 25 | int _curr_index; 26 | int _thread_cnt; 27 | thread_queue** _pool; 28 | pthread_t* _tids; 29 | }; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /include/thread_queue.h: -------------------------------------------------------------------------------- 1 | #ifndef __THREAD_QUEUE_H__ 2 | #define __THREAD_QUEUE_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "event_loop.h" 12 | 13 | template 14 | class thread_queue 15 | { 16 | public: 17 | thread_queue(): _loop(NULL) 18 | { 19 | ::pthread_mutex_init(&_mutex, NULL); 20 | _evfd = ::eventfd(0, EFD_NONBLOCK); 21 | if (_evfd == -1) 22 | { 23 | perror("eventfd(0, EFD_NONBLOCK)"); 24 | ::exit(1); 25 | } 26 | } 27 | 28 | ~thread_queue() 29 | { 30 | ::pthread_mutex_destroy(&_mutex); 31 | ::close(_evfd); 32 | } 33 | 34 | void send_msg(const T& item) 35 | { 36 | unsigned long long number = 1; 37 | ::pthread_mutex_lock(&_mutex); 38 | _queue.push(item); 39 | int ret = ::write(_evfd, &number, sizeof(unsigned long long)); 40 | if (ret == -1) perror("eventfd write"); 41 | ::pthread_mutex_unlock(&_mutex); 42 | } 43 | 44 | void recv_msg(std::queue& tmp_queue) 45 | { 46 | unsigned long long number; 47 | ::pthread_mutex_lock(&_mutex); 48 | int ret = ::read(_evfd, &number, sizeof(unsigned long long)); 49 | if (ret == -1) perror("eventfd read"); 50 | std::swap(tmp_queue, _queue); 51 | ::pthread_mutex_unlock(&_mutex); 52 | } 53 | 54 | event_loop* get_loop() { return _loop; } 55 | 56 | //set loop and install message comming event's callback: proc 57 | void set_loop(event_loop* loop, io_callback* proc, void* args = NULL) 58 | { 59 | _loop = loop; 60 | _loop->add_ioev(_evfd, proc, EPOLLIN, args); 61 | } 62 | 63 | private: 64 | int _evfd; 65 | event_loop* _loop; 66 | std::queue _queue; 67 | pthread_mutex_t _mutex; 68 | }; 69 | #endif 70 | -------------------------------------------------------------------------------- /include/timer_queue.h: -------------------------------------------------------------------------------- 1 | #ifndef __TIMER_QUEUE_H__ 2 | #define __TIMER_QUEUE_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "event_base.h" 8 | 9 | class timer_queue 10 | { 11 | public: 12 | timer_queue(); 13 | ~timer_queue(); 14 | 15 | int add_timer(timer_event& te); 16 | 17 | void del_timer(int timer_id); 18 | 19 | int notifier() const { return _timerfd; } 20 | int size() const { return _count; } 21 | 22 | void get_timo(std::vector& fired_evs); 23 | private: 24 | void reset_timo(); 25 | 26 | //heap operation 27 | void heap_add(timer_event& te); 28 | void heap_del(int pos); 29 | void heap_pop(); 30 | void heap_hold(int pos); 31 | 32 | std::vector _event_lst; 33 | typedef std::vector::iterator vit; 34 | 35 | __gnu_cxx::hash_map _position; 36 | 37 | typedef __gnu_cxx::hash_map::iterator mit; 38 | 39 | int _count; 40 | int _next_timer_id; 41 | int _timerfd; 42 | uint64_t _pioneer;//recent timer's millis 43 | }; 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /include/udp_client.h: -------------------------------------------------------------------------------- 1 | #ifndef __UDP_CLIENT_H__ 2 | #define __UDP_CLIENT_H__ 3 | 4 | #include "msg_head.h" 5 | #include "net_commu.h" 6 | #include "event_loop.h" 7 | #include 8 | #include "msg_dispatcher.h" 9 | 10 | class udp_client: public net_commu 11 | { 12 | public: 13 | udp_client(event_loop* loop, const char* ip, uint16_t port); 14 | 15 | ~udp_client(); 16 | 17 | void add_msg_cb(int cmdid, msg_callback* msg_cb, void* usr_data = NULL) { _dispatcher.add_msg_cb(cmdid, msg_cb, usr_data); } 18 | 19 | event_loop* loop() { return _loop; } 20 | 21 | void handle_read(); 22 | 23 | virtual int send_data(const char* data, int datlen, int cmdid); 24 | 25 | virtual int get_fd() { return _sockfd; } 26 | 27 | private: 28 | int _sockfd; 29 | char _rbuf[MSG_LENGTH_LIMIT]; 30 | char _wbuf[MSG_LENGTH_LIMIT]; 31 | event_loop* _loop; 32 | msg_dispatcher _dispatcher; 33 | }; 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /include/udp_server.h: -------------------------------------------------------------------------------- 1 | #ifndef __UDP_SERVER_H__ 2 | #define __UDP_SERVER_H__ 3 | 4 | #include "msg_head.h" 5 | #include "net_commu.h" 6 | #include "event_loop.h" 7 | #include 8 | #include "msg_dispatcher.h" 9 | 10 | class udp_server: public net_commu 11 | { 12 | public: 13 | udp_server(event_loop* loop, const char* ip, uint16_t port); 14 | 15 | ~udp_server(); 16 | 17 | void add_msg_cb(int cmdid, msg_callback* msg_cb, void* usr_data = NULL) { _dispatcher.add_msg_cb(cmdid, msg_cb, usr_data); } 18 | 19 | event_loop* loop() { return _loop; } 20 | 21 | void handle_read(); 22 | 23 | virtual int send_data(const char* data, int datlen, int cmdid); 24 | 25 | virtual int get_fd() { return _sockfd; } 26 | 27 | private: 28 | int _sockfd; 29 | char _rbuf[MSG_LENGTH_LIMIT]; 30 | char _wbuf[MSG_LENGTH_LIMIT]; 31 | event_loop* _loop; 32 | 33 | struct sockaddr_in _srcaddr; 34 | socklen_t _addrlen; 35 | msg_dispatcher _dispatcher; 36 | }; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | TARGET=lib/libereactor.a 2 | CXX=g++ 3 | CFLAGS=-g -O2 -Wall -fPIC -Wno-deprecated 4 | 5 | SRC=src 6 | 7 | INC=-Iinclude 8 | OBJS = $(addsuffix .o, $(basename $(wildcard $(SRC)/*.cc))) 9 | 10 | $(TARGET): $(OBJS) 11 | mkdir -p lib 12 | ar cqs $@ $^ 13 | 14 | -include $(OBJS:.o=.d) 15 | 16 | %.o: %.cc 17 | $(CXX) $(CFLAGS) -c -o $@ $< $(INC) 18 | @$(CXX) -MM $*.cc $(INC) > $*.d 19 | @mv -f $*.d $*.d.tmp 20 | @sed -e 's|.*:|$*.o:|' < $*.d.tmp > $*.d 21 | @sed -e 's/.*://' -e 's/\\$$//' < $*.d.tmp | fmt -1 | \ 22 | sed -e 's/^ *//' -e 's/$$/:/' >> $*.d 23 | @rm -f $*.d.tmp 24 | 25 | .PHONY: clean 26 | 27 | clean: 28 | -rm -f src/*.o src/*.d $(TARGET) 29 | -------------------------------------------------------------------------------- /pictures/client-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeechanX/Easy-Reactor/4e8b6d969d0a3043659123c0cbc2fc86ac3a0980/pictures/client-example.png -------------------------------------------------------------------------------- /pictures/multi-thread-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeechanX/Easy-Reactor/4e8b6d969d0a3043659123c0cbc2fc86ac3a0980/pictures/multi-thread-arch.png -------------------------------------------------------------------------------- /pictures/server-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeechanX/Easy-Reactor/4e8b6d969d0a3043659123c0cbc2fc86ac3a0980/pictures/server-example.png -------------------------------------------------------------------------------- /server.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "easy_reactor.h" 4 | 5 | using namespace std; 6 | 7 | void buz(const char* data, uint32_t len, int cmdid, net_commu* commu, void* usr_data) 8 | { 9 | std::cout << data << std::endl; 10 | string rspStr = data; 11 | commu->send_data(rspStr.c_str(), rspStr.size(), cmdid);//回复消息 12 | } 13 | 14 | int main() 15 | { 16 | event_loop loop; 17 | 18 | config_reader::setPath("myconf.ini"); 19 | string ip = config_reader::ins()->GetString("reactor", "ip", "0.0.0.0"); 20 | short port = config_reader::ins()->GetNumber("reactor", "port", 12315); 21 | 22 | tcp_server server(&loop, ip.c_str(), port);//创建TCP服务器 23 | server.add_msg_cb(1, buz);//设置:当收到消息id = 1的消息调用的回调函数 我们约定EchoString消息的ID是1 24 | loop.process_evs(); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /src/config_reader.cc: -------------------------------------------------------------------------------- 1 | #include "config_reader.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | config_reader* config_reader::config = NULL; 10 | 11 | config_reader::~config_reader() 12 | { 13 | for (STR_MAP_ITER it = _map.begin(); it != _map.end(); ++it) 14 | { 15 | delete it->second; 16 | } 17 | } 18 | 19 | std::string config_reader::GetString(const std::string& section, const std::string& key, const std::string& default_value) 20 | { 21 | STR_MAP_ITER it = _map.find(section); 22 | if (it != _map.end()) 23 | { 24 | std::map::const_iterator it1 = it->second->find(key); 25 | if (it1 != it->second->end()) 26 | { 27 | return it1->second; 28 | } 29 | } 30 | return default_value; 31 | } 32 | 33 | float config_reader::GetFloat(const std::string& section, const std::string& key, const float& default_value) 34 | { 35 | std::ostringstream convert1; 36 | convert1 << default_value; 37 | std::string default_value_str = convert1.str(); 38 | std::string text = GetString(section, key, default_value_str); 39 | std::istringstream convert2(text); 40 | float fresult; 41 | if (!(convert2 >> fresult)) //如果Result放不下text对应的数字,执行将返回0; 42 | fresult = 0; 43 | return fresult; 44 | } 45 | 46 | bool config_reader::Load(const std::string& path) 47 | { 48 | std::ifstream ifs(path.c_str()); 49 | if (!ifs.good()) 50 | { 51 | return false; 52 | } 53 | std::string line; 54 | std::map *m = NULL; 55 | 56 | while (!ifs.eof() && ifs.good()) 57 | { 58 | getline(ifs, line); 59 | std::string section; 60 | if (isSection(line, section)) 61 | { 62 | STR_MAP_ITER it = _map.find(section); 63 | if (it == _map.end()) 64 | { 65 | m = new std::map(); 66 | _map.insert(STR_MAP::value_type(section, m)); 67 | } 68 | else 69 | { 70 | m = it->second; 71 | } 72 | continue; 73 | } 74 | 75 | size_t equ_pos = line.find('='); 76 | if (equ_pos == std::string::npos) 77 | { 78 | continue; 79 | } 80 | std::string key = line.substr(0, equ_pos); 81 | std::string value = line.substr(equ_pos + 1); 82 | key = trim(key); 83 | value = trim(value); 84 | 85 | if (key.empty()) 86 | { 87 | continue; 88 | } 89 | if (key[0] == '#' || key[0] == ';') // skip comment 90 | { 91 | continue; 92 | } 93 | 94 | std::map::iterator it1 = m->find(key); 95 | if (it1 != m->end()) 96 | { 97 | it1->second = value; 98 | } 99 | else 100 | { 101 | m->insert(std::map::value_type(key, value)); 102 | } 103 | } 104 | 105 | ifs.close(); 106 | return true; 107 | } 108 | 109 | std::vector config_reader::GetStringList(const std::string& section, const std::string& key) 110 | { 111 | std::vector v; 112 | std::string str = GetString(section, key, ""); 113 | std::string sep = ", \t"; 114 | std::string substr; 115 | std::string::size_type start = 0; 116 | std::string::size_type index; 117 | 118 | while ((index = str.find_first_of(sep, start)) != std::string::npos) 119 | { 120 | substr = str.substr(start, index - start); 121 | v.push_back(substr); 122 | 123 | start = str.find_first_not_of(sep, index); 124 | if (start == std::string::npos) 125 | { 126 | return v; 127 | } 128 | } 129 | 130 | substr = str.substr(start); 131 | v.push_back(substr); 132 | return v; 133 | } 134 | 135 | unsigned config_reader::GetNumber(const std::string& section, const std::string& key, unsigned default_value) 136 | { 137 | STR_MAP_ITER it = _map.find(section); 138 | if (it != _map.end()) 139 | { 140 | std::map::const_iterator it1 = it->second->find(key); 141 | if (it1 != it->second->end()) 142 | { 143 | return parseNumber(it1->second); 144 | } 145 | } 146 | return default_value; 147 | } 148 | 149 | bool config_reader::GetBool(const std::string& section, const std::string& key, bool default_value) 150 | { 151 | STR_MAP_ITER it = _map.find(section); 152 | if (it != _map.end()) 153 | { 154 | std::map::const_iterator it1 = it->second->find(key); 155 | if (it1 != it->second->end()) 156 | { 157 | if (strcasecmp(it1->second.c_str(), "true") == 0) 158 | { 159 | return true; 160 | } 161 | } 162 | } 163 | return default_value; 164 | } 165 | 166 | bool config_reader::isSection(std::string line, std::string& section) 167 | { 168 | section = trim(line); 169 | 170 | if (section.empty() || section.length() <= 2) 171 | { 172 | return false; 173 | } 174 | 175 | if (section.at(0) != '[' || section.at(section.length() - 1) != ']') 176 | { 177 | return false; 178 | } 179 | 180 | section = section.substr(1, section.length() - 2); 181 | section = trim(section); 182 | 183 | return true; 184 | } 185 | 186 | unsigned config_reader::parseNumber(const std::string& s) 187 | { 188 | std::istringstream iss(s); 189 | long long v = 0; 190 | iss >> v; 191 | return v; 192 | } 193 | 194 | std::string config_reader::trimLeft(const std::string& s) 195 | { 196 | size_t first_not_empty = 0; 197 | 198 | std::string::const_iterator beg = s.begin(); 199 | while (beg != s.end()) 200 | { 201 | if (!isspace(*beg)) 202 | { 203 | break; 204 | } 205 | first_not_empty++; 206 | beg++; 207 | } 208 | return s.substr(first_not_empty); 209 | } 210 | 211 | std::string config_reader::trimRight(const std::string& s) 212 | { 213 | size_t last_not_empty = s.length(); 214 | std::string::const_iterator end = s.end(); 215 | while (end != s.begin()) 216 | { 217 | end--; 218 | if (!isspace(*end)) 219 | { 220 | break; 221 | } 222 | last_not_empty--; 223 | } 224 | return s.substr(0, last_not_empty); 225 | } 226 | 227 | std::string config_reader::trim(const std::string& s) 228 | { 229 | return trimLeft(trimRight(s)); 230 | } 231 | 232 | config_reader *config_reader::ins() 233 | { 234 | assert(config != NULL); 235 | return config; 236 | } 237 | 238 | bool config_reader::setPath(const std::string& path) 239 | { 240 | assert(config == NULL); 241 | config = new config_reader(); 242 | return config->Load(path); 243 | } -------------------------------------------------------------------------------- /src/event_loop.cc: -------------------------------------------------------------------------------- 1 | #include "event_loop.h" 2 | #include "timer_queue.h" 3 | #include "print_error.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | void timerqueue_cb(event_loop* loop, int fd, void *args) 12 | { 13 | std::vector fired_evs; 14 | loop->_timer_que->get_timo(fired_evs); 15 | for (std::vector::iterator it = fired_evs.begin(); 16 | it != fired_evs.end(); ++it) 17 | { 18 | it->cb(loop, it->cb_data); 19 | } 20 | } 21 | 22 | event_loop::event_loop() 23 | { 24 | _epfd = ::epoll_create1(0); 25 | exit_if(_epfd == -1, "when epoll_create1()"); 26 | _timer_que = new timer_queue(); 27 | exit_if(_timer_que == NULL, "when new timer_queue"); 28 | //register timer event to event loop 29 | add_ioev(_timer_que->notifier(), timerqueue_cb, EPOLLIN, _timer_que); 30 | } 31 | 32 | void event_loop::process_evs() 33 | { 34 | while (true) 35 | { 36 | //handle file event 37 | ioev_it it; 38 | int nfds = ::epoll_wait(_epfd, _fired_evs, MAXEVENTS, 10); 39 | for (int i = 0;i < nfds; ++i) 40 | { 41 | it = _io_evs.find(_fired_evs[i].data.fd); 42 | assert(it != _io_evs.end()); 43 | io_event* ev = &(it->second); 44 | 45 | if (_fired_evs[i].events & EPOLLIN) 46 | { 47 | void *args = ev->rcb_args; 48 | ev->read_cb(this, _fired_evs[i].data.fd, args); 49 | } 50 | else if (_fired_evs[i].events & EPOLLOUT) 51 | { 52 | void *args = ev->wcb_args; 53 | ev->write_cb(this, _fired_evs[i].data.fd, args); 54 | } 55 | else if (_fired_evs[i].events & (EPOLLHUP | EPOLLERR)) 56 | { 57 | if (ev->read_cb) 58 | { 59 | void *args = ev->rcb_args; 60 | ev->read_cb(this, _fired_evs[i].data.fd, args); 61 | } 62 | else if (ev->write_cb) 63 | { 64 | void *args = ev->wcb_args; 65 | ev->write_cb(this, _fired_evs[i].data.fd, args); 66 | } 67 | else 68 | { 69 | error_log("fd %d get error, delete it from epoll", _fired_evs[i].data.fd); 70 | del_ioev(_fired_evs[i].data.fd); 71 | } 72 | } 73 | } 74 | run_task(); 75 | } 76 | } 77 | 78 | /* 79 | * if EPOLLIN in mask, EPOLLOUT must not in mask; 80 | * if EPOLLOUT in mask, EPOLLIN must not in mask; 81 | * if want to register EPOLLOUT | EPOLLIN event, just call add_ioev twice! 82 | */ 83 | void event_loop::add_ioev(int fd, io_callback* proc, int mask, void* args) 84 | { 85 | int f_mask = 0;//finial mask 86 | int op; 87 | ioev_it it = _io_evs.find(fd); 88 | if (it == _io_evs.end()) 89 | { 90 | f_mask = mask; 91 | op = EPOLL_CTL_ADD; 92 | } 93 | else 94 | { 95 | f_mask = it->second.mask | mask; 96 | op = EPOLL_CTL_MOD; 97 | } 98 | if (mask & EPOLLIN) 99 | { 100 | _io_evs[fd].read_cb = proc; 101 | _io_evs[fd].rcb_args = args; 102 | } 103 | else if (mask & EPOLLOUT) 104 | { 105 | _io_evs[fd].write_cb = proc; 106 | _io_evs[fd].wcb_args = args; 107 | } 108 | 109 | _io_evs[fd].mask = f_mask; 110 | struct epoll_event event; 111 | event.events = f_mask; 112 | event.data.fd = fd; 113 | int ret = ::epoll_ctl(_epfd, op, fd, &event); 114 | error_if(ret == -1, "epoll_ctl"); 115 | listening.insert(fd);//加入到监听集合中 116 | } 117 | 118 | void event_loop::del_ioev(int fd, int mask) 119 | { 120 | ioev_it it = _io_evs.find(fd); 121 | if (it == _io_evs.end()) 122 | return ; 123 | int& o_mask = it->second.mask; 124 | int ret; 125 | //remove mask from o_mask 126 | o_mask = o_mask & (~mask); 127 | if (o_mask == 0) 128 | { 129 | _io_evs.erase(it); 130 | ret = ::epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, NULL); 131 | error_if(ret == -1, "epoll_ctl EPOLL_CTL_DEL"); 132 | listening.erase(fd);//从监听集合中删除 133 | } 134 | else 135 | { 136 | struct epoll_event event; 137 | event.events = o_mask; 138 | event.data.fd = fd; 139 | ret = ::epoll_ctl(_epfd, EPOLL_CTL_MOD, fd, &event); 140 | error_if(ret == -1, "epoll_ctl EPOLL_CTL_MOD"); 141 | } 142 | } 143 | 144 | void event_loop::del_ioev(int fd) 145 | { 146 | _io_evs.erase(fd); 147 | listening.erase(fd);//从监听集合中删除 148 | ::epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, NULL); 149 | } 150 | 151 | int event_loop::run_at(timer_callback cb, void* args, uint64_t ts) 152 | { 153 | timer_event te(cb, args, ts); 154 | return _timer_que->add_timer(te); 155 | } 156 | 157 | int event_loop::run_after(timer_callback cb, void* args, int sec, int millis) 158 | { 159 | struct timespec tpc; 160 | clock_gettime(CLOCK_REALTIME, &tpc); 161 | uint64_t ts = tpc.tv_sec * 1000 + tpc.tv_nsec / 1000000UL; 162 | ts += sec * 1000 + millis; 163 | timer_event te(cb, args, ts); 164 | return _timer_que->add_timer(te); 165 | } 166 | 167 | int event_loop::run_every(timer_callback cb, void* args, int sec, int millis) 168 | { 169 | uint32_t interval = sec * 1000 + millis; 170 | struct timespec tpc; 171 | clock_gettime(CLOCK_REALTIME, &tpc); 172 | uint64_t ts = tpc.tv_sec * 1000 + tpc.tv_nsec / 1000000UL + interval; 173 | timer_event te(cb, args, ts, interval); 174 | return _timer_que->add_timer(te); 175 | } 176 | 177 | void event_loop::del_timer(int timer_id) 178 | { 179 | _timer_que->del_timer(timer_id); 180 | } 181 | 182 | void event_loop::add_task(pendingFunc func, void* args) 183 | { 184 | std::pair item(func, args); 185 | _pendingFactors.push_back(item); 186 | } 187 | 188 | void event_loop::run_task() 189 | { 190 | std::vector >::iterator it; 191 | for (it = _pendingFactors.begin();it != _pendingFactors.end(); ++it) 192 | { 193 | pendingFunc func = it->first; 194 | void* args = it->second; 195 | func(this, args); 196 | } 197 | _pendingFactors.clear(); 198 | } 199 | -------------------------------------------------------------------------------- /src/io_buffer.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "io_buffer.h" 8 | #include "print_error.h" 9 | 10 | #define EXTRA_MEM_LIMIT (5U * 1024 * 1024) //unit is K, so EXTRA_MEM_LIMIT = 5GB 11 | 12 | io_buffer* buffer_pool::alloc(int N) 13 | { 14 | int index; 15 | if (N <= u4K) 16 | index = u4K; 17 | else if (N <= u16K) 18 | index = u16K; 19 | else if (N <= u64K) 20 | index = u64K; 21 | else if (N <= u256K) 22 | index = u256K; 23 | else if (N <= u1M) 24 | index = u1M; 25 | else if (N <= u4M) 26 | index = u4M; 27 | else if (N <= u8M) 28 | index = u8M; 29 | else 30 | return NULL; 31 | 32 | ::pthread_mutex_lock(&_mutex); 33 | if (!_pool[index]) 34 | { 35 | if (_total_mem + index / 1024 >= EXTRA_MEM_LIMIT) 36 | { 37 | exit_log("use too many memory"); 38 | ::exit(1); 39 | } 40 | io_buffer* new_buf = new io_buffer(index); 41 | exit_if(new_buf == NULL, "new io_buffer"); 42 | _total_mem += index / 1024; 43 | ::pthread_mutex_unlock(&_mutex); 44 | return new_buf; 45 | } 46 | io_buffer* target = _pool[index]; 47 | _pool[index] = target->next; 48 | ::pthread_mutex_unlock(&_mutex); 49 | 50 | target->next = NULL; 51 | return target; 52 | } 53 | 54 | void buffer_pool::revert(io_buffer* buffer) 55 | { 56 | int index = buffer->capacity; 57 | buffer->length = 0; 58 | buffer->head = 0; 59 | 60 | ::pthread_mutex_lock(&_mutex); 61 | 62 | assert(_pool.find(index) != _pool.end()); 63 | buffer->next = _pool[index]; 64 | _pool[index] = buffer; 65 | 66 | ::pthread_mutex_unlock(&_mutex); 67 | } 68 | 69 | buffer_pool::buffer_pool(): _total_mem(0) 70 | { 71 | io_buffer* prev; 72 | 73 | _pool[u4K] = new io_buffer(u4K); 74 | exit_if(_pool[u4K] == NULL, "new io_buffer"); 75 | 76 | prev = _pool[u4K]; 77 | //4K buffer count: 5000 ≈ 20MB 78 | for (int i = 1;i < 5000; ++i) 79 | { 80 | prev->next = new io_buffer(u4K); 81 | exit_if(prev->next == NULL, "new io_buffer"); 82 | prev = prev->next; 83 | } 84 | _total_mem += 4 * 5000; 85 | 86 | _pool[u16K] = new io_buffer(u16K); 87 | exit_if(_pool[u16K] == NULL, "new io_buffer"); 88 | 89 | prev = _pool[u16K]; 90 | //16K buffer count: 1000 ≈ 16MB 91 | for (int i = 1;i < 1000; ++i) 92 | { 93 | prev->next = new io_buffer(u16K); 94 | exit_if(prev->next == NULL, "new io_buffer"); 95 | prev = prev->next; 96 | } 97 | _total_mem += 16 * 1000; 98 | 99 | _pool[u64K] = new io_buffer(u64K); 100 | exit_if(_pool[u64K] == NULL, "new io_buffer"); 101 | 102 | prev = _pool[u64K]; 103 | //64K buffer count: 500 ≈ 32MB 104 | for (int i = 1;i < 500; ++i) 105 | { 106 | prev->next = new io_buffer(u64K); 107 | exit_if(prev->next == NULL, "new io_buffer"); 108 | prev = prev->next; 109 | } 110 | _total_mem += 64 * 500; 111 | 112 | _pool[u256K] = new io_buffer(u256K); 113 | exit_if(_pool[u256K] == NULL, "new io_buffer"); 114 | 115 | prev = _pool[u256K]; 116 | //256K buffer count: 100 ≈ 25MB 117 | for (int i = 1;i < 200; ++i) 118 | { 119 | prev->next = new io_buffer(u256K); 120 | exit_if(prev->next == NULL, "new io_buffer"); 121 | prev = prev->next; 122 | } 123 | _total_mem += 256 * 200; 124 | 125 | _pool[u1M] = new io_buffer(u1M); 126 | exit_if(_pool[u1M] == NULL, "new io_buffer"); 127 | 128 | prev = _pool[u1M]; 129 | //1M buffer count: 50 = 50MB 130 | for (int i = 1;i < 50; ++i) 131 | { 132 | prev->next = new io_buffer(u1M); 133 | exit_if(prev->next == NULL, "new io_buffer"); 134 | prev = prev->next; 135 | } 136 | _total_mem += 1024 * 50; 137 | 138 | _pool[u4M] = new io_buffer(u4M); 139 | exit_if(_pool[u4M] == NULL, "new io_buffer"); 140 | 141 | prev = _pool[u4M]; 142 | //4M buffer count: 20 = 80MB 143 | for (int i = 1;i < 20; ++i) 144 | { 145 | prev->next = new io_buffer(u4M); 146 | exit_if(prev->next == NULL, "new io_buffer"); 147 | prev = prev->next; 148 | } 149 | _total_mem += 4096 * 20; 150 | 151 | _pool[u8M] = new io_buffer(u8M); 152 | exit_if(_pool[u8M] == NULL, "new io_buffer"); 153 | 154 | prev = _pool[u8M]; 155 | //4M buffer count: 10 = 80MB 156 | for (int i = 1;i < 10; ++i) 157 | { 158 | prev->next = new io_buffer(u8M); 159 | exit_if(prev->next == NULL, "new io_buffer"); 160 | prev = prev->next; 161 | } 162 | _total_mem += 8192 * 10; 163 | } 164 | 165 | buffer_pool* buffer_pool::_ins = NULL; 166 | 167 | pthread_mutex_t buffer_pool::_mutex = PTHREAD_MUTEX_INITIALIZER; 168 | 169 | pthread_once_t buffer_pool::_once = PTHREAD_ONCE_INIT; 170 | 171 | void tcp_buffer::clear() 172 | { 173 | if (_buf) 174 | { 175 | buffer_pool::ins()->revert(_buf); 176 | _buf = NULL; 177 | } 178 | } 179 | 180 | void tcp_buffer::pop(int len) 181 | { 182 | assert(_buf && len <= _buf->length); 183 | _buf->pop(len); 184 | //buf is empty 185 | if (!_buf->length) 186 | { 187 | buffer_pool::ins()->revert(_buf); 188 | _buf = NULL; 189 | } 190 | } 191 | 192 | int input_buffer::read_data(int fd) 193 | { 194 | //一次性读出来所有数据 195 | int rn, ret; 196 | if (::ioctl(fd, FIONREAD, &rn) == -1) 197 | { 198 | error_log("ioctl FIONREAD"); 199 | return -1; 200 | } 201 | if (!_buf) 202 | { 203 | _buf = buffer_pool::ins()->alloc(rn); 204 | if (!_buf) 205 | { 206 | error_log("no idle for alloc io_buffer"); 207 | return -1; 208 | } 209 | } 210 | else 211 | { 212 | assert(_buf->head == 0); 213 | if (_buf->capacity - _buf->length < (int)rn) 214 | { 215 | //get new 216 | io_buffer* new_buf = buffer_pool::ins()->alloc(rn + _buf->length); 217 | if (!new_buf) 218 | { 219 | error_log("no idle for alloc io_buffer"); 220 | return -1; 221 | } 222 | new_buf->copy(_buf); 223 | buffer_pool::ins()->revert(_buf); 224 | _buf = new_buf; 225 | } 226 | } 227 | 228 | do 229 | { 230 | ret = ::read(fd, _buf->data + _buf->length, rn); 231 | } while (ret == -1 && errno == EINTR); 232 | 233 | if (ret > 0) 234 | { 235 | assert(ret == rn); 236 | _buf->length += ret; 237 | } 238 | return ret; 239 | } 240 | 241 | int output_buffer::send_data(const char* data, int datlen) 242 | { 243 | if (!_buf) 244 | { 245 | _buf = buffer_pool::ins()->alloc(datlen); 246 | if (!_buf) 247 | { 248 | error_log("no idle for alloc io_buffer"); 249 | return -1; 250 | } 251 | } 252 | else 253 | { 254 | assert(_buf->head == 0); 255 | if (_buf->capacity - _buf->length < datlen) 256 | { 257 | //get new 258 | io_buffer* new_buf = buffer_pool::ins()->alloc(datlen + _buf->length); 259 | if (!new_buf) 260 | { 261 | error_log("no idle for alloc io_buffer"); 262 | return -1; 263 | } 264 | new_buf->copy(_buf); 265 | buffer_pool::ins()->revert(_buf); 266 | _buf = new_buf; 267 | } 268 | } 269 | 270 | ::memcpy(_buf->data + _buf->length, data, datlen); 271 | _buf->length += datlen; 272 | return 0; 273 | } 274 | 275 | int output_buffer::write_fd(int fd) 276 | { 277 | assert(_buf && _buf->head == 0); 278 | int writed; 279 | do 280 | { 281 | writed = ::write(fd, _buf->data, _buf->length); 282 | } while (writed == -1 && errno == EINTR); 283 | if (writed > 0) 284 | { 285 | _buf->pop(writed); 286 | _buf->adjust(); 287 | } 288 | if (writed == -1 && errno == EAGAIN) 289 | { 290 | writed = 0;//不是错误,仅返回为0表示此时不可继续写 291 | } 292 | return writed; 293 | } 294 | -------------------------------------------------------------------------------- /src/tcp_client.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "msg_head.h" 14 | #include "tcp_client.h" 15 | #include "print_error.h" 16 | 17 | static void read_cb(event_loop* loop, int fd, void* args) 18 | { 19 | tcp_client* cli = (tcp_client*)args; 20 | cli->handle_read(); 21 | } 22 | 23 | static void write_cb(event_loop* loop, int fd, void* args) 24 | { 25 | tcp_client* cli = (tcp_client*)args; 26 | cli->handle_write(); 27 | } 28 | 29 | static void reconn_cb(event_loop* loop, void* usr_data) 30 | { 31 | tcp_client* cli = (tcp_client*)usr_data; 32 | cli->do_connect(); 33 | } 34 | 35 | static void connection_cb(event_loop* loop, int fd, void* args) 36 | { 37 | tcp_client* cli = (tcp_client*)args; 38 | loop->del_ioev(fd); 39 | int result; 40 | socklen_t result_len = sizeof(result); 41 | getsockopt(fd, SOL_SOCKET, SO_ERROR, &result, &result_len); 42 | if (result == 0) 43 | { 44 | //connect build success! 45 | cli->net_ok = true; 46 | info_log("connect %s:%d successfully", ::inet_ntoa(cli->servaddr.sin_addr), ntohs(cli->servaddr.sin_port)); 47 | 48 | //call on connection callback(if has) 49 | cli->call_onconnect(); 50 | 51 | loop->add_ioev(fd, read_cb, EPOLLIN, cli); 52 | if (cli->obuf.length) 53 | { 54 | loop->add_ioev(fd, write_cb, EPOLLOUT, cli); 55 | } 56 | } 57 | else 58 | { 59 | //connect build error! 60 | //reconnection after 2s 61 | info_log("connect %s:%d error, retry after 2s", ::inet_ntoa(cli->servaddr.sin_addr), ntohs(cli->servaddr.sin_port)); 62 | loop->run_after(reconn_cb, cli, 2); 63 | } 64 | } 65 | 66 | tcp_client::tcp_client(event_loop* loop, const char* ip, unsigned short port, const char* name): 67 | net_ok(false), 68 | ibuf(4194304), 69 | obuf(4194304), 70 | _sockfd(-1), 71 | _loop(loop), 72 | _onconnection(NULL), 73 | _onconn_args(NULL), 74 | _onclose(NULL), 75 | _onclose_args(NULL), 76 | _name(name) 77 | { 78 | //construct server address 79 | ::bzero(&servaddr, sizeof (servaddr)); 80 | servaddr.sin_family = AF_INET; 81 | int ret = ::inet_aton(ip, &servaddr.sin_addr); 82 | exit_if(ret == 0, "ip format %s", ip); 83 | servaddr.sin_port = htons(port); 84 | _addrlen = sizeof servaddr; 85 | 86 | //connect 87 | do_connect(); 88 | } 89 | 90 | void tcp_client::do_connect() 91 | { 92 | if (_sockfd != -1) 93 | ::close(_sockfd); 94 | //create socket 95 | _sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_TCP); 96 | exit_if(_sockfd == -1, "socket()"); 97 | 98 | int ret = ::connect(_sockfd, (const struct sockaddr*)&servaddr, _addrlen); 99 | if (ret == 0) 100 | { 101 | net_ok = true; 102 | //call on connection callback(if has) 103 | call_onconnect(); 104 | 105 | info_log("connect %s:%d successfully", ::inet_ntoa(servaddr.sin_addr), ntohs(servaddr.sin_port)); 106 | } 107 | else 108 | { 109 | if (errno == EINPROGRESS) 110 | { 111 | //add connection event 112 | _loop->add_ioev(_sockfd, connection_cb, EPOLLOUT, this); 113 | } 114 | else 115 | { 116 | exit_log("connect()"); 117 | } 118 | } 119 | } 120 | 121 | int tcp_client::send_data(const char* data, int datlen, int cmdid)//call by user 122 | { 123 | if (!net_ok) 124 | return -1; 125 | bool need = (obuf.length == 0) ? true: false;//if need to add to event loop 126 | if (datlen + COMMU_HEAD_LENGTH > obuf.capacity - obuf.length) 127 | { 128 | error_log("no more space to write socket"); 129 | return -1; 130 | } 131 | commu_head head; 132 | head.cmdid = cmdid; 133 | head.length = datlen; 134 | 135 | ::memcpy(obuf.data + obuf.length, &head, COMMU_HEAD_LENGTH); 136 | obuf.length += COMMU_HEAD_LENGTH; 137 | 138 | ::memcpy(obuf.data + obuf.length, data, datlen); 139 | obuf.length += datlen; 140 | 141 | if (need) 142 | { 143 | _loop->add_ioev(_sockfd, write_cb, EPOLLOUT, this); 144 | } 145 | return 0; 146 | } 147 | 148 | int tcp_client::handle_read() 149 | { 150 | //一次性读出来所有数据 151 | assert(net_ok); 152 | int rn; 153 | if (::ioctl(_sockfd, FIONREAD, &rn) == -1) 154 | { 155 | error_log("ioctl FIONREAD"); 156 | return -1; 157 | } 158 | assert(rn <= ibuf.capacity - ibuf.length); 159 | int ret; 160 | do 161 | { 162 | ret = ::read(_sockfd, ibuf.data + ibuf.length, rn); 163 | } while (ret == -1 && errno == EINTR); 164 | 165 | if (ret == 0) 166 | { 167 | //peer close connection 168 | if (_name) 169 | info_log("%s client: connection closed by peer", _name); 170 | else 171 | info_log("client: connection closed by peer"); 172 | clean_conn(); 173 | return -1; 174 | } 175 | else if (ret == -1) 176 | { 177 | assert(errno != EAGAIN); 178 | error_log("read()"); 179 | clean_conn(); 180 | return -1; 181 | } 182 | assert(ret == rn); 183 | ibuf.length += ret; 184 | 185 | commu_head head; 186 | int cmdid, length; 187 | while (ibuf.length >= COMMU_HEAD_LENGTH) 188 | { 189 | ::memcpy(&head, ibuf.data + ibuf.head, COMMU_HEAD_LENGTH); 190 | cmdid = head.cmdid; 191 | length = head.length; 192 | 193 | if (length + COMMU_HEAD_LENGTH < ibuf.length) 194 | { 195 | //sub-package 196 | break; 197 | } 198 | 199 | ibuf.pop(COMMU_HEAD_LENGTH); 200 | 201 | if (!_dispatcher.exist(cmdid)) 202 | { 203 | error_log("this message has no corresponding callback, close connection"); 204 | clean_conn(); 205 | return -1; 206 | } 207 | _dispatcher.cb(ibuf.data + ibuf.head, length, cmdid, this); 208 | 209 | ibuf.pop(length); 210 | } 211 | ibuf.adjust(); 212 | return 0; 213 | } 214 | 215 | int tcp_client::handle_write() 216 | { 217 | assert(obuf.head == 0 && obuf.length); 218 | int ret; 219 | while (obuf.length) 220 | { 221 | do 222 | { 223 | ret = ::write(_sockfd, obuf.data, obuf.length); 224 | } while (ret == -1 && errno == EINTR); 225 | if (ret > 0) 226 | { 227 | obuf.pop(ret); 228 | obuf.adjust(); 229 | } 230 | else if (ret == -1 && errno != EAGAIN) 231 | { 232 | error_log("write()"); 233 | clean_conn(); 234 | return -1; 235 | } 236 | else 237 | { 238 | //此时不可继续写 239 | break; 240 | } 241 | } 242 | 243 | if (!obuf.length) 244 | { 245 | _loop->del_ioev(_sockfd, EPOLLOUT); 246 | } 247 | return 0; 248 | } 249 | 250 | void tcp_client::clean_conn() 251 | { 252 | if (_sockfd != -1) 253 | { 254 | _loop->del_ioev(_sockfd); 255 | ::close(_sockfd); 256 | } 257 | net_ok = false; 258 | 259 | //call callback when on connection close 260 | call_onclose(); 261 | 262 | //connect 263 | do_connect(); 264 | } 265 | -------------------------------------------------------------------------------- /src/tcp_conn.cc: -------------------------------------------------------------------------------- 1 | #include "tcp_conn.h" 2 | #include "msg_head.h" 3 | #include "tcp_server.h" 4 | #include "print_error.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | static void tcp_rcb(event_loop* loop, int fd, void *args) 12 | { 13 | tcp_conn* conn = (tcp_conn*)args; 14 | conn->handle_read(); 15 | } 16 | 17 | static void tcp_wcb(event_loop* loop, int fd, void *args) 18 | { 19 | tcp_conn* conn = (tcp_conn*)args; 20 | conn->handle_write(); 21 | } 22 | 23 | void tcp_conn::init(int connfd, event_loop* loop) 24 | { 25 | _connfd = connfd; 26 | _loop = loop; 27 | //set NONBLOCK 28 | int flag = ::fcntl(_connfd, F_GETFL, 0); 29 | ::fcntl(_connfd, F_SETFL, O_NONBLOCK | flag); 30 | 31 | //set NODELAY 32 | int opend = 1; 33 | int ret = ::setsockopt(_connfd, IPPROTO_TCP, TCP_NODELAY, &opend, sizeof(opend)); 34 | error_if(ret < 0, "setsockopt TCP_NODELAY"); 35 | 36 | //调用用户设置的连接建立后回调函数,主要是用于初始化作为连接内变量的:parameter参数 37 | if (tcp_server::connBuildCb) 38 | tcp_server::connBuildCb(this); 39 | 40 | _loop->add_ioev(_connfd, tcp_rcb, EPOLLIN, this); 41 | 42 | tcp_server::inc_conn(); 43 | } 44 | 45 | void tcp_conn::handle_read() 46 | { 47 | int ret = ibuf.read_data(_connfd); 48 | if (ret == -1) 49 | { 50 | //read data error 51 | error_log("read data from socket"); 52 | clean_conn(); 53 | return ; 54 | } 55 | else if (ret == 0) 56 | { 57 | //The peer is closed, return -2 58 | info_log("connection closed by peer"); 59 | clean_conn(); 60 | return ; 61 | } 62 | commu_head head; 63 | while (ibuf.length() >= COMMU_HEAD_LENGTH) 64 | { 65 | ::memcpy(&head, ibuf.data(), COMMU_HEAD_LENGTH); 66 | if (head.length > MSG_LENGTH_LIMIT || head.length < 0) 67 | { 68 | //data format is messed up 69 | error_log("data format error in data head, close connection"); 70 | clean_conn(); 71 | break; 72 | } 73 | if (ibuf.length() < COMMU_HEAD_LENGTH + head.length) 74 | { 75 | //this is half-package 76 | break; 77 | } 78 | //find in dispatcher 79 | if (!tcp_server::dispatcher.exist(head.cmdid)) 80 | { 81 | //data format is messed up 82 | error_log("this message has no corresponding callback, close connection"); 83 | clean_conn(); 84 | break; 85 | } 86 | ibuf.pop(COMMU_HEAD_LENGTH); 87 | //domain: call user callback 88 | tcp_server::dispatcher.cb(ibuf.data(), head.length, head.cmdid, this); 89 | ibuf.pop(head.length); 90 | } 91 | ibuf.adjust(); 92 | } 93 | 94 | void tcp_conn::handle_write() 95 | { 96 | //循环写 97 | while (obuf.length()) 98 | { 99 | int ret = obuf.write_fd(_connfd); 100 | if (ret == -1) 101 | { 102 | error_log("write TCP buffer error, close connection"); 103 | clean_conn(); 104 | return ; 105 | } 106 | if (ret == 0) 107 | { 108 | //不是错误,仅返回为0表示此时不可继续写 109 | break; 110 | } 111 | } 112 | if (!obuf.length()) 113 | { 114 | _loop->del_ioev(_connfd, EPOLLOUT); 115 | } 116 | } 117 | 118 | int tcp_conn::send_data(const char* data, int datlen, int cmdid) 119 | { 120 | bool need_listen = false; 121 | if (!obuf.length()) 122 | need_listen = true; 123 | //write rsp head first 124 | commu_head head; 125 | head.cmdid = cmdid; 126 | head.length = datlen; 127 | //write head 128 | int ret = obuf.send_data((const char*)&head, COMMU_HEAD_LENGTH); 129 | if (ret != 0) 130 | return -1; 131 | //write content 132 | ret = obuf.send_data(data, datlen); 133 | if (ret != 0) 134 | { 135 | //只好取消写入的消息头 136 | obuf.pop(COMMU_HEAD_LENGTH); 137 | return -1; 138 | } 139 | 140 | if (need_listen) 141 | { 142 | _loop->add_ioev(_connfd, tcp_wcb, EPOLLOUT, this); 143 | } 144 | return 0; 145 | } 146 | 147 | void tcp_conn::clean_conn() 148 | { 149 | //调用用户设置的连接释放后回调函数,主要是用于销毁作为连接内变量的:parameter参数 150 | if (tcp_server::connCloseCb) 151 | tcp_server::connCloseCb(this); 152 | //连接清理工作 153 | tcp_server::dec_conn(); 154 | _loop->del_ioev(_connfd); 155 | _loop = NULL; 156 | ibuf.clear(); 157 | obuf.clear(); 158 | int fd = _connfd; 159 | _connfd = -1; 160 | ::close(fd); 161 | } -------------------------------------------------------------------------------- /src/tcp_server.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "msg_head.h" 13 | #include "tcp_conn.h" 14 | #include "tcp_server.h" 15 | #include "print_error.h" 16 | #include "config_reader.h" 17 | 18 | void accepter_cb(event_loop* loop, int fd, void *args) 19 | { 20 | tcp_server* server = (tcp_server*)args; 21 | server->do_accept(); 22 | } 23 | 24 | tcp_conn** tcp_server::conns = NULL; 25 | int tcp_server::_conns_size = 0; 26 | int tcp_server::_max_conns = 0; 27 | int tcp_server::_curr_conns = 0; 28 | pthread_mutex_t tcp_server::_mutex = PTHREAD_MUTEX_INITIALIZER; 29 | msg_dispatcher tcp_server::dispatcher; 30 | 31 | tcp_server::conn_callback tcp_server::connBuildCb = NULL;//用户设置连接建立后的回调函数 32 | tcp_server::conn_callback tcp_server::connCloseCb = NULL;//用户设置连接释放后的回调函数 33 | 34 | tcp_server::tcp_server(event_loop* loop, const char* ip, uint16_t port): _keepalive(false) 35 | { 36 | ::bzero(&_connaddr, sizeof (_connaddr)); 37 | //ignore SIGHUP and SIGPIPE 38 | if (::signal(SIGHUP, SIG_IGN) == SIG_ERR) 39 | { 40 | error_log("signal ignore SIGHUP"); 41 | } 42 | if (::signal(SIGPIPE, SIG_IGN) == SIG_ERR) 43 | { 44 | error_log("signal ignore SIGPIPE"); 45 | } 46 | 47 | //create socket 48 | _sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP); 49 | exit_if(_sockfd == -1, "socket()"); 50 | 51 | _reservfd = ::open("/tmp/reactor_accepter", O_CREAT | O_RDONLY | O_CLOEXEC, 0666); 52 | error_if(_reservfd == -1, "open()"); 53 | 54 | struct sockaddr_in servaddr; 55 | ::bzero(&servaddr, sizeof (servaddr)); 56 | servaddr.sin_family = AF_INET; 57 | int ret = ::inet_aton(ip, &servaddr.sin_addr); 58 | exit_if(ret == 0, "ip format %s", ip); 59 | servaddr.sin_port = htons(port); 60 | 61 | int opend = 1; 62 | ret = ::setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, &opend, sizeof(opend)); 63 | error_if(ret < 0, "setsockopt SO_REUSEADDR"); 64 | 65 | ret = ::bind(_sockfd, (const struct sockaddr*)&servaddr, sizeof servaddr); 66 | exit_if(ret == -1, "bind()"); 67 | 68 | ret = ::listen(_sockfd, 500); 69 | exit_if(ret == -1, "listen()"); 70 | 71 | info_log("server on %s:%u is running...", ip, port); 72 | 73 | _loop = loop; 74 | 75 | _addrlen = sizeof (struct sockaddr_in); 76 | 77 | //if mode is multi-thread reactor, create thread pool 78 | int thread_cnt = config_reader::ins()->GetNumber("reactor", "threadNum", 0); 79 | _thd_pool = NULL; 80 | if (thread_cnt) 81 | { 82 | _thd_pool = new thread_pool(thread_cnt); 83 | exit_if(_thd_pool == NULL, "new thread_pool"); 84 | } 85 | 86 | //create connection pool 87 | _max_conns = config_reader::ins()->GetNumber("reactor", "maxConns", 10000); 88 | int next_fd = ::dup(1); 89 | _conns_size = _max_conns + next_fd; 90 | ::close(next_fd); 91 | 92 | conns = new tcp_conn*[_conns_size]; 93 | exit_if(conns == NULL, "new conns[%d]", _conns_size); 94 | for (int i = 0;i < _max_conns + next_fd; ++i) 95 | conns[i] = NULL; 96 | 97 | //add accepter event 98 | _loop->add_ioev(_sockfd, accepter_cb, EPOLLIN, this); 99 | } 100 | 101 | //tcp_server类使用时往往具有程序的完全生命周期,其实并不需要析构函数 102 | tcp_server::~tcp_server() 103 | { 104 | _loop->del_ioev(_sockfd); 105 | ::close(_sockfd); 106 | ::close(_reservfd); 107 | } 108 | 109 | void tcp_server::do_accept() 110 | { 111 | int connfd; 112 | bool conn_full = false; 113 | while (true) 114 | { 115 | connfd = ::accept(_sockfd, (struct sockaddr*)&_connaddr, &_addrlen); 116 | if (connfd == -1) 117 | { 118 | if (errno == EINTR) 119 | { 120 | continue; 121 | } 122 | else if (errno == EMFILE) 123 | { 124 | conn_full = true; 125 | ::close(_reservfd); 126 | } 127 | else if (errno == EAGAIN) 128 | { 129 | break; 130 | } 131 | else 132 | { 133 | exit_log("accept()"); 134 | } 135 | } 136 | else if (conn_full) 137 | { 138 | ::close(connfd); 139 | _reservfd = ::open("/tmp/reactor_accepter", O_CREAT | O_RDONLY | O_CLOEXEC, 0666); 140 | error_if(_reservfd == -1, "open()"); 141 | } 142 | else 143 | { 144 | //connfd and max connections 145 | int curr_conns; 146 | get_conn_num(curr_conns); 147 | if (curr_conns >= _max_conns) 148 | { 149 | error_log("connection exceeds the maximum connection count %d", _max_conns); 150 | ::close(connfd); 151 | } 152 | else 153 | { 154 | assert(connfd < _conns_size); 155 | if (_keepalive) 156 | { 157 | int opend = 1; 158 | int ret = ::setsockopt(connfd, SOL_SOCKET, SO_KEEPALIVE, &opend, sizeof(opend)); 159 | error_if(ret < 0, "setsockopt SO_KEEPALIVE"); 160 | } 161 | 162 | //multi-thread reactor model: round-robin a event loop and give message to it 163 | if (_thd_pool) 164 | { 165 | thread_queue* cq = _thd_pool->get_next_thread(); 166 | queue_msg msg; 167 | msg.cmd_type = queue_msg::NEW_CONN; 168 | msg.connfd = connfd; 169 | cq->send_msg(msg); 170 | } 171 | else//register in self thread 172 | { 173 | tcp_conn* conn = conns[connfd]; 174 | if (conn) 175 | { 176 | conn->init(connfd, _loop); 177 | } 178 | else 179 | { 180 | conn = new tcp_conn(connfd, _loop); 181 | exit_if(conn == NULL, "new tcp_conn"); 182 | conns[connfd] = conn; 183 | } 184 | } 185 | } 186 | } 187 | } 188 | } 189 | 190 | void tcp_server::inc_conn() 191 | { 192 | ::pthread_mutex_lock(&_mutex); 193 | _curr_conns++; 194 | ::pthread_mutex_unlock(&_mutex); 195 | } 196 | 197 | void tcp_server::get_conn_num(int& cnt) 198 | { 199 | ::pthread_mutex_lock(&_mutex); 200 | cnt = _curr_conns; 201 | ::pthread_mutex_unlock(&_mutex); 202 | } 203 | 204 | void tcp_server::dec_conn() 205 | { 206 | ::pthread_mutex_lock(&_mutex); 207 | _curr_conns--; 208 | ::pthread_mutex_unlock(&_mutex); 209 | } 210 | -------------------------------------------------------------------------------- /src/thread_pool.cc: -------------------------------------------------------------------------------- 1 | #include "thread_pool.h" 2 | #include "tcp_conn.h" 3 | #include "msg_head.h" 4 | #include "event_loop.h" 5 | #include "print_error.h" 6 | #include "tcp_server.h" 7 | 8 | void msg_comming_cb(event_loop* loop, int fd, void *args) 9 | { 10 | thread_queue* queue = (thread_queue*)args; 11 | std::queue msgs; 12 | queue->recv_msg(msgs); 13 | while (!msgs.empty()) 14 | { 15 | queue_msg msg = msgs.front(); 16 | msgs.pop(); 17 | if (msg.cmd_type == queue_msg::NEW_CONN) 18 | { 19 | tcp_conn* conn = tcp_server::conns[msg.connfd]; 20 | if (conn) 21 | { 22 | conn->init(msg.connfd, loop); 23 | } 24 | else 25 | { 26 | tcp_server::conns[msg.connfd] = new tcp_conn(msg.connfd, loop); 27 | exit_if(tcp_server::conns[msg.connfd] == NULL, "new tcp_conn"); 28 | } 29 | } 30 | else if (msg.cmd_type == queue_msg::NEW_TASK) 31 | { 32 | loop->add_task(msg.task, msg.args); 33 | } 34 | else 35 | { 36 | //TODO: other message between threads 37 | } 38 | } 39 | } 40 | 41 | void* thread_domain(void* args) 42 | { 43 | thread_queue* queue = (thread_queue*)args; 44 | event_loop* loop = new event_loop(); 45 | exit_if(loop == NULL, "new event_loop()"); 46 | //set loop and install message comming event's callback: msg_comming_cb 47 | queue->set_loop(loop, msg_comming_cb, queue); 48 | loop->process_evs(); 49 | return NULL; 50 | } 51 | 52 | thread_pool::thread_pool(int thread_cnt): _curr_index(0), _thread_cnt(thread_cnt) 53 | { 54 | exit_if(thread_cnt <= 0 || thread_cnt > 30, "error thread_cnt %d", thread_cnt); 55 | _pool = new thread_queue*[thread_cnt]; 56 | _tids = new pthread_t[thread_cnt]; 57 | int ret; 58 | for (int i = 0;i < thread_cnt; ++i) 59 | { 60 | _pool[i] = new thread_queue(); 61 | ret = ::pthread_create(&_tids[i], NULL, thread_domain, _pool[i]); 62 | exit_if(ret == -1, "pthread_create"); 63 | 64 | ret = ::pthread_detach(_tids[i]); 65 | error_if(ret == -1, "pthread_detach"); 66 | } 67 | } 68 | 69 | thread_queue* thread_pool::get_next_thread() 70 | { 71 | if (_curr_index == _thread_cnt) 72 | _curr_index = 0; 73 | return _pool[_curr_index++]; 74 | } 75 | 76 | void thread_pool::run_task(int thd_index, pendingFunc task, void* args) 77 | { 78 | queue_msg msg; 79 | msg.cmd_type = queue_msg::NEW_TASK; 80 | msg.task = task; 81 | msg.args = args; 82 | thread_queue* cq = _pool[thd_index]; 83 | cq->send_msg(msg); 84 | } 85 | 86 | void thread_pool::run_task(pendingFunc task, void* args) 87 | { 88 | for (int i = 0;i < _thread_cnt; ++i) 89 | { 90 | run_task(i, task); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/timer_queue.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "timer_queue.h" 6 | #include "print_error.h" 7 | 8 | timer_queue::timer_queue(): _count(0), _next_timer_id(0), _pioneer(-1/*= uint32_t max*/) 9 | { 10 | _timerfd = ::timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC); 11 | exit_if(_timerfd == -1, "timerfd_create()"); 12 | } 13 | 14 | timer_queue::~timer_queue() 15 | { 16 | ::close(_timerfd); 17 | } 18 | 19 | int timer_queue::add_timer(timer_event& te) 20 | { 21 | te.timer_id = _next_timer_id++; 22 | heap_add(te); 23 | 24 | if (_event_lst[0].ts < _pioneer) 25 | { 26 | _pioneer = _event_lst[0].ts; 27 | reset_timo(); 28 | } 29 | return te.timer_id; 30 | } 31 | 32 | void timer_queue::del_timer(int timer_id) 33 | { 34 | mit it = _position.find(timer_id); 35 | if (it == _position.end()) 36 | { 37 | error_log("no such a timerid %d", timer_id); 38 | return ; 39 | } 40 | int pos = it->second; 41 | heap_del(pos); 42 | 43 | if (_count == 0) 44 | { 45 | _pioneer = -1; 46 | reset_timo(); 47 | } 48 | else if (_event_lst[0].ts < _pioneer) 49 | { 50 | _pioneer = _event_lst[0].ts; 51 | reset_timo(); 52 | } 53 | } 54 | 55 | void timer_queue::get_timo(std::vector& fired_evs) 56 | { 57 | std::vector _reuse_lst; 58 | while (_count != 0 && _pioneer == _event_lst[0].ts) 59 | { 60 | timer_event te = _event_lst[0]; 61 | fired_evs.push_back(te); 62 | if (te.interval) 63 | { 64 | te.ts += te.interval; 65 | _reuse_lst.push_back(te); 66 | } 67 | heap_pop(); 68 | } 69 | for (vit it = _reuse_lst.begin(); it != _reuse_lst.end(); ++it) 70 | { 71 | add_timer(*it); 72 | } 73 | //reset timeout 74 | if (_count == 0) 75 | { 76 | _pioneer = -1; 77 | reset_timo(); 78 | } 79 | else//_pioneer != _event_lst[0].ts 80 | { 81 | _pioneer = _event_lst[0].ts; 82 | reset_timo(); 83 | } 84 | } 85 | 86 | void timer_queue::reset_timo() 87 | { 88 | struct itimerspec old_ts, new_ts; 89 | ::bzero(&new_ts, sizeof(new_ts)); 90 | 91 | if (_pioneer != (uint64_t)-1) 92 | { 93 | new_ts.it_value.tv_sec = _pioneer / 1000; 94 | new_ts.it_value.tv_nsec = (_pioneer % 1000) * 1000000; 95 | } 96 | //when _pioneer = -1, new_ts = 0 will disarms the timer 97 | ::timerfd_settime(_timerfd, TFD_TIMER_ABSTIME, &new_ts, &old_ts); 98 | } 99 | 100 | void timer_queue::heap_add(timer_event& te) 101 | { 102 | _event_lst.push_back(te); 103 | //update position 104 | _position[te.timer_id] = _count; 105 | 106 | int curr_pos = _count; 107 | _count++; 108 | 109 | int prt_pos = (curr_pos - 1) / 2; 110 | while (prt_pos >= 0 && _event_lst[curr_pos].ts < _event_lst[prt_pos].ts) 111 | { 112 | timer_event tmp = _event_lst[curr_pos]; 113 | _event_lst[curr_pos] = _event_lst[prt_pos]; 114 | _event_lst[prt_pos] = tmp; 115 | //update position 116 | _position[_event_lst[curr_pos].timer_id] = curr_pos; 117 | _position[tmp.timer_id] = prt_pos; 118 | 119 | curr_pos = prt_pos; 120 | prt_pos = (curr_pos - 1) / 2; 121 | } 122 | } 123 | 124 | void timer_queue::heap_del(int pos) 125 | { 126 | timer_event to_del = _event_lst[pos]; 127 | 128 | //rear item 129 | timer_event tmp = _event_lst[_count - 1]; 130 | _event_lst[pos] = tmp; 131 | //update position 132 | 133 | _position[tmp.timer_id] = pos; 134 | 135 | //update position 136 | _position.erase(to_del.timer_id); 137 | 138 | _count--; 139 | _event_lst.pop_back(); 140 | 141 | heap_hold(pos); 142 | } 143 | 144 | void timer_queue::heap_pop() 145 | { 146 | if (_count <= 0) return ; 147 | //update position 148 | _position.erase(_event_lst[0].timer_id); 149 | if (_count > 1) 150 | { 151 | timer_event tmp = _event_lst[_count - 1]; 152 | _event_lst[0] = tmp; 153 | //update position 154 | _position[tmp.timer_id] = 0; 155 | 156 | _event_lst.pop_back(); 157 | _count--; 158 | heap_hold(0); 159 | } 160 | else if (_count == 1) 161 | { 162 | _event_lst.clear(); 163 | _count = 0; 164 | } 165 | } 166 | 167 | void timer_queue::heap_hold(int pos) 168 | { 169 | int left = 2 * pos + 1, right = 2 * pos + 2; 170 | int min_pos = pos; 171 | if (left < _count && _event_lst[min_pos].ts > _event_lst[left].ts) 172 | { 173 | min_pos = left; 174 | } 175 | if (right < _count && _event_lst[min_pos].ts > _event_lst[right].ts) 176 | { 177 | min_pos = right; 178 | } 179 | if (min_pos != pos) 180 | { 181 | timer_event tmp = _event_lst[min_pos]; 182 | _event_lst[min_pos] = _event_lst[pos]; 183 | _event_lst[pos] = tmp; 184 | //update position 185 | _position[_event_lst[min_pos].timer_id] = min_pos; 186 | _position[tmp.timer_id] = pos; 187 | 188 | heap_hold(min_pos); 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/udp_client.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "udp_client.h" 12 | #include "print_error.h" 13 | 14 | void read_cb(event_loop* loop, int fd, void *args) 15 | { 16 | udp_client* server = (udp_client*)args; 17 | server->handle_read(); 18 | } 19 | 20 | udp_client::udp_client(event_loop* loop, const char* ip, uint16_t port) 21 | { 22 | //create socket 23 | _sockfd = ::socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_UDP); 24 | exit_if(_sockfd == -1, "socket()"); 25 | 26 | struct sockaddr_in servaddr; 27 | ::bzero(&servaddr, sizeof (servaddr)); 28 | servaddr.sin_family = AF_INET; 29 | int ret = ::inet_aton(ip, &servaddr.sin_addr); 30 | exit_if(ret == 0, "ip format %s", ip); 31 | servaddr.sin_port = htons(port); 32 | 33 | ret = ::connect(_sockfd, (const struct sockaddr*)&servaddr, sizeof servaddr); 34 | exit_if(ret == -1, "connect()"); 35 | 36 | _loop = loop; 37 | 38 | //add accepter event 39 | _loop->add_ioev(_sockfd, read_cb, EPOLLIN, this); 40 | } 41 | 42 | udp_client::~udp_client() 43 | { 44 | _loop->del_ioev(_sockfd); 45 | ::close(_sockfd); 46 | } 47 | 48 | void udp_client::handle_read() 49 | { 50 | while (true) 51 | { 52 | int pkg_len = ::recvfrom(_sockfd, _rbuf, sizeof _rbuf, 0, NULL, NULL); 53 | if (pkg_len == -1) 54 | { 55 | if (errno == EINTR) 56 | { 57 | continue; 58 | } 59 | else if (errno == EAGAIN) 60 | { 61 | break; 62 | } 63 | else 64 | { 65 | error_log("recfrom()"); 66 | break; 67 | } 68 | } 69 | //handle package _rbuf[0:pkg_len) 70 | commu_head head; 71 | ::memcpy(&head, _rbuf, COMMU_HEAD_LENGTH); 72 | if (head.length > MSG_LENGTH_LIMIT || head.length < 0 || head.length != pkg_len - COMMU_HEAD_LENGTH) 73 | { 74 | //data format is messed up 75 | error_log("data format error in data head"); 76 | continue; 77 | } 78 | _dispatcher.cb(_rbuf + COMMU_HEAD_LENGTH, head.length, head.cmdid, this); 79 | } 80 | } 81 | 82 | int udp_client::send_data(const char* data, int datlen, int cmdid) 83 | { 84 | if (datlen > MSG_LENGTH_LIMIT) 85 | { 86 | error_log("udp response length too large"); 87 | return -1; 88 | } 89 | commu_head head; 90 | head.length = datlen; 91 | head.cmdid = cmdid; 92 | 93 | ::memcpy(_wbuf, &head, COMMU_HEAD_LENGTH); 94 | ::memcpy(_wbuf + COMMU_HEAD_LENGTH, data, datlen); 95 | 96 | int ret = ::sendto(_sockfd, _wbuf, datlen + COMMU_HEAD_LENGTH, 0, NULL, 0); 97 | if (ret == -1) 98 | { 99 | error_log("sendto()"); 100 | } 101 | return ret; 102 | } 103 | -------------------------------------------------------------------------------- /src/udp_server.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "udp_server.h" 12 | #include "print_error.h" 13 | 14 | void read_cb(event_loop* loop, int fd, void *args) 15 | { 16 | udp_server* server = (udp_server*)args; 17 | server->handle_read(); 18 | } 19 | 20 | udp_server::udp_server(event_loop* loop, const char* ip, uint16_t port) 21 | { 22 | //ignore SIGHUP and SIGPIPE 23 | if (::signal(SIGHUP, SIG_IGN) == SIG_ERR) 24 | { 25 | error_log("signal ignore SIGHUP"); 26 | } 27 | 28 | //create socket 29 | _sockfd = ::socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_UDP); 30 | exit_if(_sockfd == -1, "socket()"); 31 | 32 | struct sockaddr_in servaddr; 33 | ::bzero(&servaddr, sizeof (servaddr)); 34 | servaddr.sin_family = AF_INET; 35 | int ret = ::inet_aton(ip, &servaddr.sin_addr); 36 | exit_if(ret == 0, "ip format %s", ip); 37 | servaddr.sin_port = htons(port); 38 | 39 | ret = ::bind(_sockfd, (const struct sockaddr*)&servaddr, sizeof servaddr); 40 | exit_if(ret == -1, "bind()"); 41 | 42 | _loop = loop; 43 | 44 | ::bzero(&_srcaddr, sizeof (_srcaddr)); 45 | _addrlen = sizeof (struct sockaddr_in); 46 | 47 | info_log("server on %s:%u is running...", ip, port); 48 | 49 | //add accepter event 50 | _loop->add_ioev(_sockfd, read_cb, EPOLLIN, this); 51 | } 52 | 53 | udp_server::~udp_server() 54 | { 55 | _loop->del_ioev(_sockfd); 56 | ::close(_sockfd); 57 | } 58 | 59 | void udp_server::handle_read() 60 | { 61 | while (true) 62 | { 63 | int pkg_len = ::recvfrom(_sockfd, _rbuf, sizeof _rbuf, 0, (struct sockaddr *)&_srcaddr, &_addrlen); 64 | if (pkg_len == -1) 65 | { 66 | if (errno == EINTR) 67 | { 68 | continue; 69 | } 70 | else if (errno == EAGAIN) 71 | { 72 | break; 73 | } 74 | else 75 | { 76 | error_log("recfrom()"); 77 | break; 78 | } 79 | } 80 | //handle package _rbuf[0:pkg_len) 81 | commu_head head; 82 | ::memcpy(&head, _rbuf, COMMU_HEAD_LENGTH); 83 | if (head.length > MSG_LENGTH_LIMIT || head.length < 0 || head.length + COMMU_HEAD_LENGTH != pkg_len) 84 | { 85 | //data format is messed up 86 | error_log("data format error in data head, head[%d,%d], pkg_len %d", head.length, head.cmdid, pkg_len); 87 | continue; 88 | } 89 | _dispatcher.cb(_rbuf + COMMU_HEAD_LENGTH, head.length, head.cmdid, this); 90 | } 91 | } 92 | 93 | int udp_server::send_data(const char* data, int datlen, int cmdid) 94 | { 95 | if (datlen > MSG_LENGTH_LIMIT) 96 | { 97 | error_log("udp response length too large"); 98 | return -1; 99 | } 100 | commu_head head; 101 | head.length = datlen; 102 | head.cmdid = cmdid; 103 | 104 | ::memcpy(_wbuf, &head, COMMU_HEAD_LENGTH); 105 | ::memcpy(_wbuf + COMMU_HEAD_LENGTH, data, datlen); 106 | 107 | int ret = ::sendto(_sockfd, _wbuf, datlen + COMMU_HEAD_LENGTH, 0, (struct sockaddr *)&_srcaddr, _addrlen); 108 | if (ret == -1) 109 | { 110 | error_log("sendto()"); 111 | } 112 | return ret; 113 | } 114 | -------------------------------------------------------------------------------- /test/benchmark.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "echoMsg.pb.h" 8 | #include "easy_reactor.h" 9 | 10 | struct Config 11 | { 12 | Config(): hostip(NULL), hostPort(0), concurrency(0), total(0) {} 13 | char* hostip; 14 | short hostPort; 15 | int concurrency; 16 | long total; 17 | }; 18 | 19 | unsigned long getCurrentMills() 20 | { 21 | struct timeval tv; 22 | gettimeofday(&tv, NULL); 23 | return (unsigned long)tv.tv_sec * 1000 + tv.tv_usec / 1000; 24 | } 25 | 26 | Config config; 27 | unsigned long startTs, endTs; 28 | 29 | void parseOption(int argc, char** argv) 30 | { 31 | for (int i = 0;i < argc; ++i) 32 | { 33 | if (!strcmp(argv[i], "-h")) 34 | { 35 | config.hostip = argv[i + 1]; 36 | } 37 | else if (!strcmp(argv[i], "-p")) 38 | { 39 | config.hostPort = atoi(argv[i + 1]); 40 | } 41 | else if (!strcmp(argv[i], "-c")) 42 | { 43 | config.concurrency = atoi(argv[i + 1]); 44 | } 45 | else if (!strcmp(argv[i], "-n")) 46 | { 47 | config.total = atol(argv[i + 1]); 48 | } 49 | } 50 | if (!config.hostip || !config.hostPort || !config.concurrency || !config.total) 51 | { 52 | printf("./dss-benchmark -h ip -p port -c concurrency -n total\n"); 53 | exit(1); 54 | } 55 | } 56 | 57 | void echoBack(const char* data, uint32_t len, int msgId, net_commu* commu, void* usr_data) 58 | { 59 | long* count = (long*)usr_data; 60 | echo::EchoString req, rsp; 61 | if (rsp.ParseFromArray(data, len) && rsp.content() == "I miss you i miss you i miss you i miss you i miss you i miss you i miss you i miss you i miss you!") 62 | *count = *count + 1; 63 | 64 | if (*count >= config.total) 65 | { 66 | endTs = getCurrentMills(); 67 | printf("communicate %ld times\n", *count); 68 | printf("time use %ldms\n", endTs - startTs); 69 | printf("qps %.2f\n", (*count * 1000.0) / (endTs - startTs)); 70 | exit(1); 71 | } 72 | req.set_id(rsp.id() + 1); 73 | req.set_content(rsp.content()); 74 | std::string reqStr; 75 | req.SerializeToString(&reqStr); 76 | commu->send_data(reqStr.c_str(), reqStr.size(), 1);//回复消息 77 | } 78 | 79 | void onConnectionCb(tcp_client* client, void *args) 80 | { 81 | unsigned long* startTsPtr = (unsigned long*)args; 82 | if (!*startTsPtr) 83 | *startTsPtr = getCurrentMills(); 84 | //连接建立后,主动发送消息 85 | echo::EchoString req; 86 | req.set_id(100); 87 | req.set_content("I miss you i miss you i miss you i miss you i miss you i miss you i miss you i miss you i miss you!"); 88 | std::string reqStr; 89 | req.SerializeToString(&reqStr); 90 | client->send_data(reqStr.c_str(), reqStr.size(), 1);//主动发送消息 91 | } 92 | 93 | int main(int argc, char** argv) 94 | { 95 | parseOption(argc, argv); 96 | long done = 0; 97 | event_loop loop; 98 | std::vector clients; 99 | for (int i = 0;i < config.concurrency; ++i) 100 | { 101 | tcp_client* cli = new tcp_client(&loop, config.hostip, config.hostPort);//创建TCP客户端 102 | if (!cli) exit(1); 103 | cli->add_msg_cb(1, echoBack, &done);//设置:当收到消息id=GetRouteByAgentRspId的消息时的回调函数 104 | cli->onConnection(onConnectionCb, &startTs);//当连接建立后,执行函数onConnectionCb 105 | clients.push_back(cli); 106 | } 107 | 108 | loop.process_evs(); 109 | 110 | for (int i = 0;i < config.concurrency; ++i) 111 | { 112 | tcp_client* cli = clients[i]; 113 | delete cli; 114 | } 115 | return 0; 116 | } 117 | -------------------------------------------------------------------------------- /test/echoMsg.pb.cc: -------------------------------------------------------------------------------- 1 | // Generated by the protocol buffer compiler. DO NOT EDIT! 2 | // source: echoMsg.proto 3 | 4 | #define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION 5 | #include "echoMsg.pb.h" 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | // @@protoc_insertion_point(includes) 18 | 19 | namespace echo { 20 | 21 | namespace { 22 | 23 | const ::google::protobuf::Descriptor* EchoString_descriptor_ = NULL; 24 | const ::google::protobuf::internal::GeneratedMessageReflection* 25 | EchoString_reflection_ = NULL; 26 | 27 | } // namespace 28 | 29 | 30 | void protobuf_AssignDesc_echoMsg_2eproto() { 31 | protobuf_AddDesc_echoMsg_2eproto(); 32 | const ::google::protobuf::FileDescriptor* file = 33 | ::google::protobuf::DescriptorPool::generated_pool()->FindFileByName( 34 | "echoMsg.proto"); 35 | GOOGLE_CHECK(file != NULL); 36 | EchoString_descriptor_ = file->message_type(0); 37 | static const int EchoString_offsets_[2] = { 38 | GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EchoString, content_), 39 | GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EchoString, id_), 40 | }; 41 | EchoString_reflection_ = 42 | new ::google::protobuf::internal::GeneratedMessageReflection( 43 | EchoString_descriptor_, 44 | EchoString::default_instance_, 45 | EchoString_offsets_, 46 | GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EchoString, _has_bits_[0]), 47 | GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EchoString, _unknown_fields_), 48 | -1, 49 | ::google::protobuf::DescriptorPool::generated_pool(), 50 | ::google::protobuf::MessageFactory::generated_factory(), 51 | sizeof(EchoString)); 52 | } 53 | 54 | namespace { 55 | 56 | GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AssignDescriptors_once_); 57 | inline void protobuf_AssignDescriptorsOnce() { 58 | ::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_, 59 | &protobuf_AssignDesc_echoMsg_2eproto); 60 | } 61 | 62 | void protobuf_RegisterTypes(const ::std::string&) { 63 | protobuf_AssignDescriptorsOnce(); 64 | ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( 65 | EchoString_descriptor_, &EchoString::default_instance()); 66 | } 67 | 68 | } // namespace 69 | 70 | void protobuf_ShutdownFile_echoMsg_2eproto() { 71 | delete EchoString::default_instance_; 72 | delete EchoString_reflection_; 73 | } 74 | 75 | void protobuf_AddDesc_echoMsg_2eproto() { 76 | static bool already_here = false; 77 | if (already_here) return; 78 | already_here = true; 79 | GOOGLE_PROTOBUF_VERIFY_VERSION; 80 | 81 | ::google::protobuf::DescriptorPool::InternalAddGeneratedFile( 82 | "\n\rechoMsg.proto\022\004echo\")\n\nEchoString\022\017\n\007c" 83 | "ontent\030\001 \002(\t\022\n\n\002id\030\002 \002(\005", 64); 84 | ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( 85 | "echoMsg.proto", &protobuf_RegisterTypes); 86 | EchoString::default_instance_ = new EchoString(); 87 | EchoString::default_instance_->InitAsDefaultInstance(); 88 | ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_echoMsg_2eproto); 89 | } 90 | 91 | // Force AddDescriptors() to be called at static initialization time. 92 | struct StaticDescriptorInitializer_echoMsg_2eproto { 93 | StaticDescriptorInitializer_echoMsg_2eproto() { 94 | protobuf_AddDesc_echoMsg_2eproto(); 95 | } 96 | } static_descriptor_initializer_echoMsg_2eproto_; 97 | 98 | // =================================================================== 99 | 100 | #ifndef _MSC_VER 101 | const int EchoString::kContentFieldNumber; 102 | const int EchoString::kIdFieldNumber; 103 | #endif // !_MSC_VER 104 | 105 | EchoString::EchoString() 106 | : ::google::protobuf::Message() { 107 | SharedCtor(); 108 | // @@protoc_insertion_point(constructor:echo.EchoString) 109 | } 110 | 111 | void EchoString::InitAsDefaultInstance() { 112 | } 113 | 114 | EchoString::EchoString(const EchoString& from) 115 | : ::google::protobuf::Message() { 116 | SharedCtor(); 117 | MergeFrom(from); 118 | // @@protoc_insertion_point(copy_constructor:echo.EchoString) 119 | } 120 | 121 | void EchoString::SharedCtor() { 122 | ::google::protobuf::internal::GetEmptyString(); 123 | _cached_size_ = 0; 124 | content_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); 125 | id_ = 0; 126 | ::memset(_has_bits_, 0, sizeof(_has_bits_)); 127 | } 128 | 129 | EchoString::~EchoString() { 130 | // @@protoc_insertion_point(destructor:echo.EchoString) 131 | SharedDtor(); 132 | } 133 | 134 | void EchoString::SharedDtor() { 135 | if (content_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 136 | delete content_; 137 | } 138 | if (this != default_instance_) { 139 | } 140 | } 141 | 142 | void EchoString::SetCachedSize(int size) const { 143 | GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); 144 | _cached_size_ = size; 145 | GOOGLE_SAFE_CONCURRENT_WRITES_END(); 146 | } 147 | const ::google::protobuf::Descriptor* EchoString::descriptor() { 148 | protobuf_AssignDescriptorsOnce(); 149 | return EchoString_descriptor_; 150 | } 151 | 152 | const EchoString& EchoString::default_instance() { 153 | if (default_instance_ == NULL) protobuf_AddDesc_echoMsg_2eproto(); 154 | return *default_instance_; 155 | } 156 | 157 | EchoString* EchoString::default_instance_ = NULL; 158 | 159 | EchoString* EchoString::New() const { 160 | return new EchoString; 161 | } 162 | 163 | void EchoString::Clear() { 164 | if (_has_bits_[0 / 32] & 3) { 165 | if (has_content()) { 166 | if (content_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 167 | content_->clear(); 168 | } 169 | } 170 | id_ = 0; 171 | } 172 | ::memset(_has_bits_, 0, sizeof(_has_bits_)); 173 | mutable_unknown_fields()->Clear(); 174 | } 175 | 176 | bool EchoString::MergePartialFromCodedStream( 177 | ::google::protobuf::io::CodedInputStream* input) { 178 | #define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure 179 | ::google::protobuf::uint32 tag; 180 | // @@protoc_insertion_point(parse_start:echo.EchoString) 181 | for (;;) { 182 | ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127); 183 | tag = p.first; 184 | if (!p.second) goto handle_unusual; 185 | switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { 186 | // required string content = 1; 187 | case 1: { 188 | if (tag == 10) { 189 | DO_(::google::protobuf::internal::WireFormatLite::ReadString( 190 | input, this->mutable_content())); 191 | ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( 192 | this->content().data(), this->content().length(), 193 | ::google::protobuf::internal::WireFormat::PARSE, 194 | "content"); 195 | } else { 196 | goto handle_unusual; 197 | } 198 | if (input->ExpectTag(16)) goto parse_id; 199 | break; 200 | } 201 | 202 | // required int32 id = 2; 203 | case 2: { 204 | if (tag == 16) { 205 | parse_id: 206 | DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< 207 | ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( 208 | input, &id_))); 209 | set_has_id(); 210 | } else { 211 | goto handle_unusual; 212 | } 213 | if (input->ExpectAtEnd()) goto success; 214 | break; 215 | } 216 | 217 | default: { 218 | handle_unusual: 219 | if (tag == 0 || 220 | ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == 221 | ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { 222 | goto success; 223 | } 224 | DO_(::google::protobuf::internal::WireFormat::SkipField( 225 | input, tag, mutable_unknown_fields())); 226 | break; 227 | } 228 | } 229 | } 230 | success: 231 | // @@protoc_insertion_point(parse_success:echo.EchoString) 232 | return true; 233 | failure: 234 | // @@protoc_insertion_point(parse_failure:echo.EchoString) 235 | return false; 236 | #undef DO_ 237 | } 238 | 239 | void EchoString::SerializeWithCachedSizes( 240 | ::google::protobuf::io::CodedOutputStream* output) const { 241 | // @@protoc_insertion_point(serialize_start:echo.EchoString) 242 | // required string content = 1; 243 | if (has_content()) { 244 | ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( 245 | this->content().data(), this->content().length(), 246 | ::google::protobuf::internal::WireFormat::SERIALIZE, 247 | "content"); 248 | ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( 249 | 1, this->content(), output); 250 | } 251 | 252 | // required int32 id = 2; 253 | if (has_id()) { 254 | ::google::protobuf::internal::WireFormatLite::WriteInt32(2, this->id(), output); 255 | } 256 | 257 | if (!unknown_fields().empty()) { 258 | ::google::protobuf::internal::WireFormat::SerializeUnknownFields( 259 | unknown_fields(), output); 260 | } 261 | // @@protoc_insertion_point(serialize_end:echo.EchoString) 262 | } 263 | 264 | ::google::protobuf::uint8* EchoString::SerializeWithCachedSizesToArray( 265 | ::google::protobuf::uint8* target) const { 266 | // @@protoc_insertion_point(serialize_to_array_start:echo.EchoString) 267 | // required string content = 1; 268 | if (has_content()) { 269 | ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( 270 | this->content().data(), this->content().length(), 271 | ::google::protobuf::internal::WireFormat::SERIALIZE, 272 | "content"); 273 | target = 274 | ::google::protobuf::internal::WireFormatLite::WriteStringToArray( 275 | 1, this->content(), target); 276 | } 277 | 278 | // required int32 id = 2; 279 | if (has_id()) { 280 | target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(2, this->id(), target); 281 | } 282 | 283 | if (!unknown_fields().empty()) { 284 | target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( 285 | unknown_fields(), target); 286 | } 287 | // @@protoc_insertion_point(serialize_to_array_end:echo.EchoString) 288 | return target; 289 | } 290 | 291 | int EchoString::ByteSize() const { 292 | int total_size = 0; 293 | 294 | if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { 295 | // required string content = 1; 296 | if (has_content()) { 297 | total_size += 1 + 298 | ::google::protobuf::internal::WireFormatLite::StringSize( 299 | this->content()); 300 | } 301 | 302 | // required int32 id = 2; 303 | if (has_id()) { 304 | total_size += 1 + 305 | ::google::protobuf::internal::WireFormatLite::Int32Size( 306 | this->id()); 307 | } 308 | 309 | } 310 | if (!unknown_fields().empty()) { 311 | total_size += 312 | ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( 313 | unknown_fields()); 314 | } 315 | GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); 316 | _cached_size_ = total_size; 317 | GOOGLE_SAFE_CONCURRENT_WRITES_END(); 318 | return total_size; 319 | } 320 | 321 | void EchoString::MergeFrom(const ::google::protobuf::Message& from) { 322 | GOOGLE_CHECK_NE(&from, this); 323 | const EchoString* source = 324 | ::google::protobuf::internal::dynamic_cast_if_available( 325 | &from); 326 | if (source == NULL) { 327 | ::google::protobuf::internal::ReflectionOps::Merge(from, this); 328 | } else { 329 | MergeFrom(*source); 330 | } 331 | } 332 | 333 | void EchoString::MergeFrom(const EchoString& from) { 334 | GOOGLE_CHECK_NE(&from, this); 335 | if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { 336 | if (from.has_content()) { 337 | set_content(from.content()); 338 | } 339 | if (from.has_id()) { 340 | set_id(from.id()); 341 | } 342 | } 343 | mutable_unknown_fields()->MergeFrom(from.unknown_fields()); 344 | } 345 | 346 | void EchoString::CopyFrom(const ::google::protobuf::Message& from) { 347 | if (&from == this) return; 348 | Clear(); 349 | MergeFrom(from); 350 | } 351 | 352 | void EchoString::CopyFrom(const EchoString& from) { 353 | if (&from == this) return; 354 | Clear(); 355 | MergeFrom(from); 356 | } 357 | 358 | bool EchoString::IsInitialized() const { 359 | if ((_has_bits_[0] & 0x00000003) != 0x00000003) return false; 360 | 361 | return true; 362 | } 363 | 364 | void EchoString::Swap(EchoString* other) { 365 | if (other != this) { 366 | std::swap(content_, other->content_); 367 | std::swap(id_, other->id_); 368 | std::swap(_has_bits_[0], other->_has_bits_[0]); 369 | _unknown_fields_.Swap(&other->_unknown_fields_); 370 | std::swap(_cached_size_, other->_cached_size_); 371 | } 372 | } 373 | 374 | ::google::protobuf::Metadata EchoString::GetMetadata() const { 375 | protobuf_AssignDescriptorsOnce(); 376 | ::google::protobuf::Metadata metadata; 377 | metadata.descriptor = EchoString_descriptor_; 378 | metadata.reflection = EchoString_reflection_; 379 | return metadata; 380 | } 381 | 382 | 383 | // @@protoc_insertion_point(namespace_scope) 384 | 385 | } // namespace echo 386 | 387 | // @@protoc_insertion_point(global_scope) 388 | -------------------------------------------------------------------------------- /test/echoMsg.pb.h: -------------------------------------------------------------------------------- 1 | // Generated by the protocol buffer compiler. DO NOT EDIT! 2 | // source: echoMsg.proto 3 | 4 | #ifndef PROTOBUF_echoMsg_2eproto__INCLUDED 5 | #define PROTOBUF_echoMsg_2eproto__INCLUDED 6 | 7 | #include 8 | 9 | #include 10 | 11 | #if GOOGLE_PROTOBUF_VERSION < 2006000 12 | #error This file was generated by a newer version of protoc which is 13 | #error incompatible with your Protocol Buffer headers. Please update 14 | #error your headers. 15 | #endif 16 | #if 2006001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION 17 | #error This file was generated by an older version of protoc which is 18 | #error incompatible with your Protocol Buffer headers. Please 19 | #error regenerate this file with a newer version of protoc. 20 | #endif 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | // @@protoc_insertion_point(includes) 28 | 29 | namespace echo { 30 | 31 | // Internal implementation detail -- do not call these. 32 | void protobuf_AddDesc_echoMsg_2eproto(); 33 | void protobuf_AssignDesc_echoMsg_2eproto(); 34 | void protobuf_ShutdownFile_echoMsg_2eproto(); 35 | 36 | class EchoString; 37 | 38 | // =================================================================== 39 | 40 | class EchoString : public ::google::protobuf::Message { 41 | public: 42 | EchoString(); 43 | virtual ~EchoString(); 44 | 45 | EchoString(const EchoString& from); 46 | 47 | inline EchoString& operator=(const EchoString& from) { 48 | CopyFrom(from); 49 | return *this; 50 | } 51 | 52 | inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { 53 | return _unknown_fields_; 54 | } 55 | 56 | inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { 57 | return &_unknown_fields_; 58 | } 59 | 60 | static const ::google::protobuf::Descriptor* descriptor(); 61 | static const EchoString& default_instance(); 62 | 63 | void Swap(EchoString* other); 64 | 65 | // implements Message ---------------------------------------------- 66 | 67 | EchoString* New() const; 68 | void CopyFrom(const ::google::protobuf::Message& from); 69 | void MergeFrom(const ::google::protobuf::Message& from); 70 | void CopyFrom(const EchoString& from); 71 | void MergeFrom(const EchoString& from); 72 | void Clear(); 73 | bool IsInitialized() const; 74 | 75 | int ByteSize() const; 76 | bool MergePartialFromCodedStream( 77 | ::google::protobuf::io::CodedInputStream* input); 78 | void SerializeWithCachedSizes( 79 | ::google::protobuf::io::CodedOutputStream* output) const; 80 | ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; 81 | int GetCachedSize() const { return _cached_size_; } 82 | private: 83 | void SharedCtor(); 84 | void SharedDtor(); 85 | void SetCachedSize(int size) const; 86 | public: 87 | ::google::protobuf::Metadata GetMetadata() const; 88 | 89 | // nested types ---------------------------------------------------- 90 | 91 | // accessors ------------------------------------------------------- 92 | 93 | // required string content = 1; 94 | inline bool has_content() const; 95 | inline void clear_content(); 96 | static const int kContentFieldNumber = 1; 97 | inline const ::std::string& content() const; 98 | inline void set_content(const ::std::string& value); 99 | inline void set_content(const char* value); 100 | inline void set_content(const char* value, size_t size); 101 | inline ::std::string* mutable_content(); 102 | inline ::std::string* release_content(); 103 | inline void set_allocated_content(::std::string* content); 104 | 105 | // required int32 id = 2; 106 | inline bool has_id() const; 107 | inline void clear_id(); 108 | static const int kIdFieldNumber = 2; 109 | inline ::google::protobuf::int32 id() const; 110 | inline void set_id(::google::protobuf::int32 value); 111 | 112 | // @@protoc_insertion_point(class_scope:echo.EchoString) 113 | private: 114 | inline void set_has_content(); 115 | inline void clear_has_content(); 116 | inline void set_has_id(); 117 | inline void clear_has_id(); 118 | 119 | ::google::protobuf::UnknownFieldSet _unknown_fields_; 120 | 121 | ::google::protobuf::uint32 _has_bits_[1]; 122 | mutable int _cached_size_; 123 | ::std::string* content_; 124 | ::google::protobuf::int32 id_; 125 | friend void protobuf_AddDesc_echoMsg_2eproto(); 126 | friend void protobuf_AssignDesc_echoMsg_2eproto(); 127 | friend void protobuf_ShutdownFile_echoMsg_2eproto(); 128 | 129 | void InitAsDefaultInstance(); 130 | static EchoString* default_instance_; 131 | }; 132 | // =================================================================== 133 | 134 | 135 | // =================================================================== 136 | 137 | // EchoString 138 | 139 | // required string content = 1; 140 | inline bool EchoString::has_content() const { 141 | return (_has_bits_[0] & 0x00000001u) != 0; 142 | } 143 | inline void EchoString::set_has_content() { 144 | _has_bits_[0] |= 0x00000001u; 145 | } 146 | inline void EchoString::clear_has_content() { 147 | _has_bits_[0] &= ~0x00000001u; 148 | } 149 | inline void EchoString::clear_content() { 150 | if (content_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 151 | content_->clear(); 152 | } 153 | clear_has_content(); 154 | } 155 | inline const ::std::string& EchoString::content() const { 156 | // @@protoc_insertion_point(field_get:echo.EchoString.content) 157 | return *content_; 158 | } 159 | inline void EchoString::set_content(const ::std::string& value) { 160 | set_has_content(); 161 | if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 162 | content_ = new ::std::string; 163 | } 164 | content_->assign(value); 165 | // @@protoc_insertion_point(field_set:echo.EchoString.content) 166 | } 167 | inline void EchoString::set_content(const char* value) { 168 | set_has_content(); 169 | if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 170 | content_ = new ::std::string; 171 | } 172 | content_->assign(value); 173 | // @@protoc_insertion_point(field_set_char:echo.EchoString.content) 174 | } 175 | inline void EchoString::set_content(const char* value, size_t size) { 176 | set_has_content(); 177 | if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 178 | content_ = new ::std::string; 179 | } 180 | content_->assign(reinterpret_cast(value), size); 181 | // @@protoc_insertion_point(field_set_pointer:echo.EchoString.content) 182 | } 183 | inline ::std::string* EchoString::mutable_content() { 184 | set_has_content(); 185 | if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 186 | content_ = new ::std::string; 187 | } 188 | // @@protoc_insertion_point(field_mutable:echo.EchoString.content) 189 | return content_; 190 | } 191 | inline ::std::string* EchoString::release_content() { 192 | clear_has_content(); 193 | if (content_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 194 | return NULL; 195 | } else { 196 | ::std::string* temp = content_; 197 | content_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); 198 | return temp; 199 | } 200 | } 201 | inline void EchoString::set_allocated_content(::std::string* content) { 202 | if (content_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) { 203 | delete content_; 204 | } 205 | if (content) { 206 | set_has_content(); 207 | content_ = content; 208 | } else { 209 | clear_has_content(); 210 | content_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); 211 | } 212 | // @@protoc_insertion_point(field_set_allocated:echo.EchoString.content) 213 | } 214 | 215 | // required int32 id = 2; 216 | inline bool EchoString::has_id() const { 217 | return (_has_bits_[0] & 0x00000002u) != 0; 218 | } 219 | inline void EchoString::set_has_id() { 220 | _has_bits_[0] |= 0x00000002u; 221 | } 222 | inline void EchoString::clear_has_id() { 223 | _has_bits_[0] &= ~0x00000002u; 224 | } 225 | inline void EchoString::clear_id() { 226 | id_ = 0; 227 | clear_has_id(); 228 | } 229 | inline ::google::protobuf::int32 EchoString::id() const { 230 | // @@protoc_insertion_point(field_get:echo.EchoString.id) 231 | return id_; 232 | } 233 | inline void EchoString::set_id(::google::protobuf::int32 value) { 234 | set_has_id(); 235 | id_ = value; 236 | // @@protoc_insertion_point(field_set:echo.EchoString.id) 237 | } 238 | 239 | 240 | // @@protoc_insertion_point(namespace_scope) 241 | 242 | } // namespace echo 243 | 244 | #ifndef SWIG 245 | namespace google { 246 | namespace protobuf { 247 | 248 | 249 | } // namespace google 250 | } // namespace protobuf 251 | #endif // SWIG 252 | 253 | // @@protoc_insertion_point(global_scope) 254 | 255 | #endif // PROTOBUF_echoMsg_2eproto__INCLUDED 256 | -------------------------------------------------------------------------------- /test/echoMsg.proto: -------------------------------------------------------------------------------- 1 | package echo; 2 | 3 | message EchoString 4 | { 5 | required string content = 1; 6 | required int32 id = 2; 7 | }; 8 | -------------------------------------------------------------------------------- /test/makefile: -------------------------------------------------------------------------------- 1 | CXX=g++ 2 | CFLAGS=-g -O2 -Wall -fPIC -Wno-deprecated 3 | 4 | INC=-I../include 5 | LIB=-L../lib -lereactor /usr/local/lib/libprotobuf.a -lrt -lpthread 6 | OBJS = $(addsuffix .o, $(basename $(wildcard *.cc))) 7 | 8 | all: 9 | $(CXX) -o server.prog $(CFLAGS) server.cc echoMsg.pb.cc $(INC) $(LIB) 10 | $(CXX) -o benchmark.prog $(CFLAGS) benchmark.cc echoMsg.pb.cc $(INC) $(LIB) 11 | 12 | clean: 13 | -rm -f *.o *.d server client 14 | -------------------------------------------------------------------------------- /test/myconf.ini: -------------------------------------------------------------------------------- 1 | [reactor] 2 | threadNum = 0 3 | maxConns = 1024 4 | -------------------------------------------------------------------------------- /test/server.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "echoMsg.pb.h" 3 | #include "easy_reactor.h" 4 | 5 | using namespace std; 6 | using namespace echo; 7 | 8 | void buz(const char* data, uint32_t len, int cmdid, net_commu* commu, void* usr_data) 9 | { 10 | EchoString req, rsp; 11 | req.ParseFromArray(data, len);//解包,data[0:len)保证是一个完整包 12 | rsp.set_id(req.id()); 13 | rsp.set_content(req.content()); 14 | string rspStr; 15 | rsp.SerializeToString(&rspStr); 16 | commu->send_data(rspStr.c_str(), rspStr.size(), cmdid);//回复消息 17 | } 18 | 19 | int main() 20 | { 21 | event_loop loop; 22 | 23 | config_reader::setPath("myconf.ini"); 24 | string ip = config_reader::ins()->GetString("reactor", "ip", "0.0.0.0"); 25 | short port = config_reader::ins()->GetNumber("reactor", "port", 12315); 26 | 27 | tcp_server server(&loop, ip.c_str(), port);//创建TCP服务器 28 | server.add_msg_cb(1, buz);//设置:当收到消息id = 1的消息调用的回调函数 我们约定EchoString消息的ID是1 29 | loop.process_evs(); 30 | return 0; 31 | } 32 | --------------------------------------------------------------------------------