├── .gitignore ├── LICENSE ├── README.md ├── makefile ├── src ├── io_buffer.h ├── lua_socket_mgr.cpp ├── lua_socket_mgr.h ├── lua_socket_node.cpp ├── lua_socket_node.h ├── main.cpp ├── socket_helper.cpp ├── socket_helper.h ├── socket_listener.cpp ├── socket_listener.h ├── socket_mgr.cpp ├── socket_mgr.h ├── socket_node.cpp ├── socket_node.h ├── socket_stream.cpp ├── socket_stream.h ├── tools.cpp └── tools.h └── test ├── client.lua ├── hive ├── lbus.so └── server.lua /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | *.pyc 4 | .DS_Store 5 | 6 | # Compiled Object files 7 | *.slo 8 | *.lo 9 | *.o 10 | *.obj 11 | 12 | # Precompiled Headers 13 | *.gch 14 | *.pch 15 | 16 | # Compiled Dynamic libraries 17 | *.so 18 | *.dylib 19 | *.dll 20 | 21 | # Fortran module files 22 | *.mod 23 | *.smod 24 | 25 | # Compiled Static libraries 26 | *.lai 27 | *.la 28 | *.a 29 | *.lib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.suo 36 | *.db 37 | *.opendb 38 | *.ilk 39 | *.log 40 | *.pdb 41 | *.tlog 42 | *.idb 43 | *.iobj 44 | *.ipdb 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 trumanzhao 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 | # `luabus` 2 | 3 | luabus是一个为LUA提供序列化消息传输的简易网络库. 4 | 5 | ## 编译环境 6 | 7 | 目前支持Windows, Linux, MacOS三平台,编译器必须支持C++17. 8 | 9 | - Windows: Visual studio 2017以上版本,需要自行编译lua的dll库. 10 | - MacOS: 需要自行编译安装lua. 11 | - Linux: 需要自行编译安装lua. 12 | 13 | ## 性能指标 14 | 15 | 目前尚未专门为性能做测试及优化,只是为了简单好用. 16 | 17 | ## 网络 18 | 首先需要一个socket\_mgr对象: 19 | 20 | ```lua 21 | lbus = require("lbus"); 22 | socket_mgr = lbus.create_socket_mgr(最大句柄数); 23 | --用户需要在主循环中调用wait函数,比如: 24 | --其参数为最大阻塞时长(实际是传给epoll_wait之类函数),单位ms. 25 | socket_mgr.wait(50); 26 | ``` 27 | 28 | 监听端口: 29 | 30 | ```lua 31 | --注意保持listener的生存期,一旦被gc,则端口就自动关了 32 | --第三个参数为listen的backlog,可以不传,默认16 33 | listener = mgr.listen("127.0.0.1", 8080, 32); 34 | 35 | --设置accept回调 36 | listener.on_accept = function(stream) 37 | stream.on_recv = function(msg, ...) 38 | -- ... 39 | end 40 | 41 | stream.on_error = function(err) 42 | -- ... 43 | end 44 | end 45 | ``` 46 | 47 | 发起连接: 48 | 49 | ```lua 50 | --注意保持stream对象生存期,被gc的话,连接会自动关闭 51 | --连接都是异步进行的,一直要stream上面触发'on_connect'事件后,连接才可用 52 | --connect(ip, port, timeout) 53 | stream = mgr.connect("127.0.0.1", 8080, 2000); 54 | --设置连接事件回调: 55 | stream.on_connect = function(result) 56 | --如果连接成功,result为"ok" 57 | --否则,result表明具体的错误 58 | end 59 | ``` 60 | 61 | 向对端发送消息: 62 | 63 | ```lua 64 | stream.send("on_login", acc, password); 65 | ``` 66 | 67 | 响应消息: 68 | 69 | ```lua 70 | stream.on_error = function (err) 71 | --发生任何错误时触发 72 | end 73 | 74 | stream.on_recv = function (msg, ...) 75 | --收到对端消息时触发. 76 | --通常这里是以...为参数调用msg对应的函数过程. 77 | end 78 | ``` 79 | 80 | 其他方法: 81 | ```lua 82 | --设置nodelay属性,默认设置为true 83 | stream.set_nodelay(true); 84 | --设置收发缓冲区,至少应该可以容纳一条消息,默认64K 85 | stream.set_recv_buffer_size(1024 * 64); 86 | stream.set_send_buffer_size(1024 * 64); 87 | stream.ip: 对端ip 88 | stream.token: 本连接的唯一标识 89 | ``` 90 | 91 | 主动断开: 92 | 93 | ```lua 94 | --调用close即可: 95 | stream.close(); 96 | ``` 97 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | product = lbus 2 | # execute, dynamic_shared, static_shared 3 | target_type = dynamic_shared 4 | define_macros = 5 | include_dir = ../luna 6 | # 依赖库列表,空格分开 7 | lib = 8 | # 最终产品目录: 9 | # 注意,只是对可执行文件和动态库而言,静态库忽略此项 10 | target_dir = . 11 | # 源码目录,注意不会递归 12 | src_dir_list = src 13 | # 依赖库目录,多个目录用空格分开: 14 | lib_dir = 15 | 16 | CC = gcc 17 | CXX = g++ 18 | CFLAGS = 19 | 20 | OS := $(shell uname) 21 | 22 | ifeq ($(OS), Linux) 23 | CFLAGS += -DLUA_USE_LINUX 24 | endif 25 | 26 | ifeq ($(OS), Darwin) 27 | CFLAGS += -DLUA_USE_MACOSX 28 | endif 29 | 30 | CXXFLAGS = $(CFLAGS) -Wno-invalid-offsetof -Wno-deprecated-declarations -std=c++17 31 | 32 | #----------------- 下面部分通常不用改 -------------------------- 33 | 34 | ifeq ($(target_type), execute) 35 | link_flags = -Wl,-rpath ./ 36 | ifeq ($(OS), Linux) 37 | link_flags += -Wl,-E 38 | endif 39 | endif 40 | 41 | ifeq ($(target_type), dynamic_shared) 42 | CFLAGS += -fPIC 43 | link_flags = -shared -ldl -fPIC -lpthread 44 | ifeq ($(OS), Darwin) 45 | link_flags += -undefined dynamic_lookup 46 | endif 47 | endif 48 | 49 | ifeq ($(target_type), static_shared) 50 | link_flags = 51 | endif 52 | 53 | ifeq ($(target_type), execute) 54 | target = $(target_dir)/$(product) 55 | endif 56 | 57 | ifeq ($(target_type), dynamic_shared) 58 | target = $(target_dir)/$(product).so 59 | endif 60 | 61 | ifeq ($(target_type), static_shared) 62 | target = $(target_dir)/lib$(product).a 63 | endif 64 | 65 | # exe and .so 66 | ifneq ($(target_type), static_shared) 67 | link = g++ -o $@ $^ $(link_flags) -m64 $(lib_dir:%=-L%) $(lib:%=-l%) 68 | endif 69 | 70 | # .a 71 | ifeq ($(target_type), static_shared) 72 | link = ar cr $@ $^ $(link_flags) 73 | endif 74 | 75 | the_goal = release 76 | ifneq ($(MAKECMDGOALS),) 77 | the_goal = $(MAKECMDGOALS) 78 | endif 79 | 80 | ifeq ($(the_goal),debug) 81 | CFLAGS += -g 82 | define_macros += _DEBUG 83 | endif 84 | 85 | ifeq ($(the_goal),release) 86 | CFLAGS += -O3 87 | endif 88 | 89 | src_c_pattern := $(foreach node, $(src_dir_list), $(node)/*.c) 90 | src_cpp_pattern := $(foreach node, $(src_dir_list), $(node)/*.cpp) 91 | clear_o_pattern := $(foreach node, $(src_dir_list), $(node)/*.o) 92 | clear_d_pattern := $(foreach node, $(src_dir_list), $(node)/*.d) 93 | make_c_list := $(wildcard $(src_c_pattern)) 94 | make_cpp_list := $(wildcard $(src_cpp_pattern)) 95 | clear_o_list := $(wildcard $(clear_o_pattern)) 96 | clear_d_list := $(wildcard $(clear_d_pattern)) 97 | make_c2o_list := $(patsubst %.c, %.c.o, $(make_c_list)) 98 | make_cpp2o_list := $(patsubst %.cpp, %.cpp.o, $(make_cpp_list)) 99 | env_param := $(include_dir:%=-I%) $(define_macros:%=-D%) 100 | 101 | comp_c_echo = @echo gcc $< ... 102 | comp_cxx_echo = @echo g++ $< ... 103 | 104 | .PHONY: release 105 | release: build_prompt $(target) 106 | 107 | .PHONY: debug 108 | debug: build_prompt $(target) 109 | 110 | .PHONY: clean 111 | clean: 112 | @echo rm "*.o" ... 113 | @rm -f $(clear_o_list) 114 | @echo rm "*.d" ... 115 | @rm -f $(clear_d_list) 116 | @echo rm $(target) 117 | @rm -f $(target) 118 | 119 | .PHONY: build_prompt 120 | build_prompt: 121 | @echo build $(product) $(the_goal) ... 122 | @echo cflags=$(CFLAGS) ... 123 | @echo c++flags=$(CXXFLAGS) ... 124 | @echo includes=$(include_dir) 125 | @echo defines=$(define_macros) 126 | @echo lib_dir=$(lib_dir) 127 | @echo libs=$(lib) 128 | 129 | -include ${make_c2o_list:.o=.d} ${make_cpp2o_list:.o=.d} 130 | %.c.o: %.c 131 | $(comp_c_echo) 132 | @$(CC) $(CFLAGS) $(env_param) -MM -MT $@ -MF $(@:.o=.d) $< 133 | @$(CC) $(CFLAGS) $(env_param) -c -o $@ $< 134 | 135 | %.cpp.o: %.cpp 136 | $(comp_cxx_echo) 137 | @$(CXX) $(CXXFLAGS) $(env_param) -MM -MT $@ -MF $(@:.o=.d) $< 138 | @$(CXX) $(CXXFLAGS) $(env_param) -c -o $@ $< 139 | 140 | $(target): $(make_c2o_list) $(make_cpp2o_list) | $(target_dir) 141 | @echo link "-->" $@ 142 | @$(link) 143 | $(after_link) 144 | 145 | $(target_dir): 146 | mkdir $(target_dir) 147 | 148 | 149 | -------------------------------------------------------------------------------- /src/io_buffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** repository: https://github.com/trumanzhao/luna 3 | ** trumanzhao, 2016-11-01, trumanzhao@foxmail.com 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | struct io_buffer { 13 | io_buffer() { } 14 | ~io_buffer() { SAFE_DELETE_ARRAY(m_buffer); } 15 | 16 | void resize(size_t size) { 17 | size_t data_len = 0; 18 | BYTE* data = peek_data(&data_len); 19 | if (size == m_buffer_size || size < data_len) 20 | return; 21 | 22 | if (data_len > 0) { 23 | BYTE* pbyBuffer = new BYTE[size]; 24 | memcpy(pbyBuffer, data, data_len); 25 | delete[] m_buffer; 26 | m_buffer = pbyBuffer; 27 | m_data_begin = m_buffer; 28 | m_data_end = m_data_begin + data_len; 29 | } else { 30 | // 这里只释放而不分配新的缓冲区,在需要用到的时候懒惰分配 31 | if (m_buffer != nullptr) { 32 | delete[] m_buffer; 33 | m_buffer = nullptr; 34 | } 35 | } 36 | m_buffer_size = size; 37 | } 38 | 39 | bool push_data(const void* data, size_t data_len) { 40 | if (m_buffer == nullptr) 41 | alloc_buffer(); 42 | 43 | size_t space_len = 0; 44 | auto* space = peek_space(&space_len); 45 | if (space_len < data_len) 46 | return false; 47 | 48 | memcpy(space, data, data_len); 49 | m_data_end += data_len; 50 | 51 | return true; 52 | } 53 | 54 | void pop_data(size_t uLen) { 55 | assert(m_data_begin + uLen <= m_data_end); 56 | m_data_begin += uLen; 57 | } 58 | 59 | void compact(bool try_free = false) { 60 | size_t data_len = (size_t)(m_data_end - m_data_begin); 61 | if (data_len > 0) { 62 | if (m_data_begin > m_buffer) { 63 | memmove(m_buffer, m_data_begin, data_len); 64 | m_data_begin = m_buffer; 65 | m_data_end = m_data_begin + data_len; 66 | } 67 | } else { 68 | if (try_free && m_buffer != nullptr) { 69 | delete[] m_buffer; 70 | m_buffer = nullptr; 71 | } 72 | m_data_begin = m_data_end = m_buffer; 73 | } 74 | } 75 | 76 | void clear(bool with_free = false) { 77 | if (with_free) { 78 | SAFE_DELETE_ARRAY(m_buffer); 79 | } 80 | m_data_begin = m_buffer; 81 | m_data_end = m_data_begin; 82 | } 83 | 84 | BYTE* peek_space(size_t* len) { 85 | if (m_buffer == nullptr) 86 | alloc_buffer(); 87 | 88 | auto buffer_end = m_buffer + m_buffer_size; 89 | *len = (size_t)(buffer_end - m_data_end); 90 | return m_data_end; 91 | } 92 | 93 | void pop_space(size_t pop_len) { 94 | if (m_buffer == nullptr) 95 | alloc_buffer(); 96 | 97 | assert(m_data_end + pop_len <= m_buffer + m_buffer_size); 98 | m_data_end += pop_len; 99 | } 100 | 101 | BYTE* pop_space(size_t* space_len, size_t pop_len) { 102 | if (m_buffer == nullptr) 103 | alloc_buffer(); 104 | 105 | auto buffer_end = m_buffer + m_buffer_size; 106 | if (m_data_end + pop_len > buffer_end) 107 | return nullptr; 108 | 109 | m_data_end += pop_len; 110 | if (space_len) { 111 | *space_len = (size_t)(buffer_end - m_data_end); 112 | } 113 | return m_data_end; 114 | } 115 | 116 | BYTE* peek_data(size_t* data_len) { 117 | *data_len = (size_t)(m_data_end - m_data_begin); 118 | return m_data_begin; 119 | } 120 | 121 | bool empty() { return m_data_end <= m_data_begin; } 122 | 123 | private: 124 | void alloc_buffer() { 125 | m_buffer = new BYTE[m_buffer_size]; 126 | m_data_begin = m_buffer; 127 | m_data_end = m_data_begin; 128 | } 129 | 130 | BYTE* m_data_begin = nullptr; 131 | BYTE* m_data_end = nullptr; 132 | BYTE* m_buffer = nullptr; 133 | size_t m_buffer_size = USHRT_MAX; 134 | }; 135 | -------------------------------------------------------------------------------- /src/lua_socket_mgr.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ** repository: https://github.com/trumanzhao/luabus.git 3 | ** trumanzhao, 2017-07-09, trumanzhao@foxmail.com 4 | */ 5 | 6 | #include "tools.h" 7 | #include "var_int.h" 8 | #include "lua_socket_mgr.h" 9 | #include "lua_socket_node.h" 10 | 11 | LUA_EXPORT_CLASS_BEGIN(lua_socket_mgr) 12 | LUA_EXPORT_METHOD(wait) 13 | LUA_EXPORT_METHOD(listen) 14 | LUA_EXPORT_METHOD(connect) 15 | LUA_EXPORT_METHOD(set_package_size) 16 | LUA_EXPORT_METHOD(set_lz_threshold) 17 | LUA_EXPORT_CLASS_END() 18 | 19 | lua_socket_mgr::~lua_socket_mgr() { 20 | } 21 | 22 | bool lua_socket_mgr::setup(lua_State* L, int max_fd) { 23 | m_lvm = L; 24 | m_mgr = std::make_shared(); 25 | m_archiver = std::make_shared(1024); 26 | return m_mgr->setup(max_fd); 27 | } 28 | 29 | int lua_socket_mgr::listen(lua_State* L) { 30 | const char* ip = lua_tostring(L, 1); 31 | int port = (int)lua_tonumber(L, 2); 32 | int backlog = (int)lua_tonumber(L, 3); 33 | 34 | if (ip == nullptr || port <= 0) { 35 | lua_pushnil(L); 36 | lua_pushstring(L, "invalid param"); 37 | return 2; 38 | } 39 | 40 | if (backlog <= 0) { 41 | backlog = 16; 42 | } 43 | 44 | std::string err; 45 | auto token = m_mgr->listen(err, ip, port, backlog); 46 | if (token == 0) { 47 | lua_pushnil(L); 48 | lua_pushstring(L, err.c_str()); 49 | return 2; 50 | } 51 | 52 | auto listener = new lua_socket_node(token, m_lvm, m_mgr, m_archiver); 53 | lua_push_object(L, listener); 54 | lua_pushstring(L, "ok"); 55 | return 2; 56 | } 57 | 58 | int lua_socket_mgr::connect(lua_State* L) { 59 | const char* ip = lua_tostring(L, 1); 60 | const char* port = lua_tostring(L, 2); 61 | int timeout = (int)lua_tonumber(L, 3); 62 | if (ip == nullptr || port == nullptr) { 63 | lua_pushnil(L); 64 | lua_pushstring(L, "invalid param"); 65 | return 2; 66 | } 67 | 68 | std::string err; 69 | auto token = m_mgr->connect(err, ip, port, timeout); 70 | if (token == 0) { 71 | lua_pushnil(L); 72 | lua_pushstring(L, err.c_str()); 73 | return 2; 74 | } 75 | 76 | auto stream = new lua_socket_node(token, m_lvm, m_mgr, m_archiver); 77 | lua_push_object(L, stream); 78 | lua_pushstring(L, "ok"); 79 | return 2; 80 | } 81 | 82 | void lua_socket_mgr::set_package_size(size_t size) { 83 | m_archiver->set_buffer_size(size); 84 | } 85 | 86 | void lua_socket_mgr::set_lz_threshold(size_t size) { 87 | m_archiver->set_lz_threshold(size); 88 | } 89 | 90 | -------------------------------------------------------------------------------- /src/lua_socket_mgr.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** repository: https://github.com/trumanzhao/luabus.git 3 | ** trumanzhao, 2017-07-09, trumanzhao@foxmail.com 4 | */ 5 | 6 | #pragma once 7 | #include 8 | #include 9 | #include 10 | #include "socket_mgr.h" 11 | #include "luna.h" 12 | #include "lua_archiver.h" 13 | 14 | struct lua_socket_mgr final { 15 | public: 16 | ~lua_socket_mgr(); 17 | bool setup(lua_State* L, int max_fd); 18 | int wait(int ms) { return m_mgr->wait(ms); } 19 | int listen(lua_State* L); 20 | int connect(lua_State* L); 21 | void set_package_size(size_t size); 22 | void set_lz_threshold(size_t size); 23 | 24 | private: 25 | lua_State* m_lvm = nullptr; 26 | std::shared_ptr m_mgr; 27 | std::shared_ptr m_archiver; 28 | 29 | public: 30 | DECLARE_LUA_CLASS(lua_socket_mgr); 31 | }; 32 | 33 | -------------------------------------------------------------------------------- /src/lua_socket_node.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ** repository: https://github.com/trumanzhao/luabus.git 3 | ** trumanzhao, 2017-07-09, trumanzhao@foxmail.com 4 | */ 5 | 6 | #include "tools.h" 7 | #include "var_int.h" 8 | #include "lua_socket_node.h" 9 | 10 | LUA_EXPORT_CLASS_BEGIN(lua_socket_node) 11 | LUA_EXPORT_METHOD(send) 12 | LUA_EXPORT_METHOD(close) 13 | LUA_EXPORT_METHOD(set_send_buffer_size) 14 | LUA_EXPORT_METHOD(set_recv_buffer_size) 15 | LUA_EXPORT_METHOD(set_nodelay) 16 | LUA_EXPORT_PROPERTY_AS(m_ip, "ip") 17 | LUA_EXPORT_PROPERTY_READONLY_AS(m_token, "token") 18 | LUA_EXPORT_CLASS_END() 19 | 20 | lua_socket_node::lua_socket_node(uint32_t token, lua_State* L, std::shared_ptr& mgr, std::shared_ptr& ar) 21 | : m_token(token), m_lvm(L), m_mgr(mgr), m_archiver(ar) { 22 | m_mgr->get_remote_ip(m_token, m_ip); 23 | 24 | m_mgr->set_accept_callback(token, [this](uint32_t steam_token) { 25 | lua_guard g(m_lvm); 26 | auto stream = new lua_socket_node(steam_token, m_lvm, m_mgr, m_archiver); 27 | lua_call_object_function(m_lvm, nullptr, this, "on_accept", std::tie(), stream); 28 | }); 29 | 30 | m_mgr->set_connect_callback(token, [this](bool ok, const char* reason) { 31 | if (ok) { 32 | m_mgr->get_remote_ip(m_token, m_ip); 33 | } 34 | 35 | lua_guard g(m_lvm); 36 | lua_call_object_function(m_lvm, nullptr, this, "on_connect", std::tie(), ok ? "ok" : reason); 37 | }); 38 | 39 | m_mgr->set_error_callback(token, [this](const char* err) { 40 | lua_guard g(m_lvm); 41 | lua_call_object_function(m_lvm, nullptr, this, "on_error", std::tie(), err); 42 | }); 43 | 44 | m_mgr->set_package_callback(token, [this](char* data, size_t data_len) { 45 | on_recv(data, data_len); 46 | }); 47 | } 48 | 49 | lua_socket_node::~lua_socket_node() { 50 | close(); 51 | } 52 | 53 | int lua_socket_node::send(lua_State* L) { 54 | int top = lua_gettop(L); 55 | if (top < 1) 56 | return 0; 57 | 58 | size_t data_len = 0; 59 | void* data = m_archiver->save(&data_len, L, 1, top); 60 | if (data == nullptr) 61 | return 0; 62 | 63 | m_mgr->send(m_token, data, data_len); 64 | lua_pushinteger(L, data_len); 65 | return 1; 66 | } 67 | 68 | void lua_socket_node::close() { 69 | if (m_token != 0) { 70 | m_mgr->close(m_token); 71 | m_token = 0; 72 | } 73 | } 74 | 75 | void lua_socket_node::on_recv(char* data, size_t data_len) { 76 | lua_guard g(m_lvm); 77 | 78 | if (!lua_get_object_function(m_lvm, this, "on_recv")) 79 | return; 80 | 81 | int param_count = m_archiver->load(m_lvm, data, data_len); 82 | if (param_count == 0) 83 | return; 84 | 85 | lua_call_function(m_lvm, nullptr, param_count, 0); 86 | } 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/lua_socket_node.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** repository: https://github.com/trumanzhao/luabus.git 3 | ** trumanzhao, 2017-07-09, trumanzhao@foxmail.com 4 | */ 5 | 6 | #pragma once 7 | #include 8 | #include 9 | #include 10 | #include "socket_mgr.h" 11 | #include "luna.h" 12 | #include "lua_archiver.h" 13 | 14 | struct lua_socket_node final { 15 | lua_socket_node(uint32_t token, lua_State* L, std::shared_ptr& mgr, std::shared_ptr& ar); 16 | ~lua_socket_node(); 17 | 18 | int send(lua_State* L); 19 | void close(); 20 | void set_send_buffer_size(size_t size) { m_mgr->set_send_buffer_size(m_token, size); } 21 | void set_recv_buffer_size(size_t size) { m_mgr->set_recv_buffer_size(m_token, size); } 22 | void set_nodelay(bool flag) { m_mgr->set_nodelay(m_token, flag); } 23 | 24 | private: 25 | void on_recv(char* data, size_t data_len); 26 | 27 | uint32_t m_token = 0; 28 | lua_State* m_lvm = nullptr; 29 | std::string m_ip; 30 | std::shared_ptr m_mgr; 31 | std::shared_ptr m_archiver; 32 | 33 | public: 34 | DECLARE_LUA_CLASS(lua_socket_node); 35 | }; 36 | 37 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ** repository: https://github.com/trumanzhao/luabus 3 | ** trumanzhao, 2017-06-25, trumanzhao@foxmail.com 4 | */ 5 | 6 | #include "lua_socket_mgr.h" 7 | 8 | int create_socket_mgr(lua_State* L) { 9 | int max_fd = (int)lua_tonumber(L, 1); 10 | lua_socket_mgr* mgr = new lua_socket_mgr(); 11 | if (!mgr->setup(L, max_fd)) { 12 | delete mgr; 13 | lua_pushnil(L); 14 | return 1; 15 | } 16 | lua_push_object(L, mgr); 17 | return 1; 18 | } 19 | 20 | #ifdef _MSC_VER 21 | #define LBUS_API _declspec(dllexport) 22 | #else 23 | #define LBUS_API 24 | #endif 25 | 26 | extern "C" int LBUS_API luaopen_lbus(lua_State* L) { 27 | lua_newtable(L); 28 | lua_set_table_function(L, -1, "create_socket_mgr", create_socket_mgr); 29 | return 1; 30 | } 31 | 32 | -------------------------------------------------------------------------------- /src/socket_helper.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ** repository: https://github.com/trumanzhao/luna 3 | ** trumanzhao, 2016-11-01, trumanzhao@foxmail.com 4 | */ 5 | 6 | #ifdef _MSC_VER 7 | #include 8 | #include 9 | #include 10 | #include 11 | #endif 12 | #ifdef __linux 13 | #include 14 | #endif 15 | #ifdef __APPLE__ 16 | #include 17 | #include 18 | #include 19 | #endif 20 | #if defined(__linux) || defined(__APPLE__) 21 | #include 22 | #include 23 | #include 24 | #include 25 | #endif 26 | #include 27 | #include "tools.h" 28 | #include "socket_helper.h" 29 | 30 | void set_no_delay(socket_t fd, int enable) { 31 | setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable)); 32 | } 33 | 34 | #if defined(__linux) || defined(__APPLE__) 35 | void set_no_block(socket_t fd) { 36 | fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); 37 | } 38 | 39 | void set_close_on_exec(socket_t fd) { 40 | fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); 41 | } 42 | #endif 43 | 44 | #ifdef _MSC_VER 45 | void set_no_block(socket_t fd) { 46 | u_long opt = 1; 47 | ioctlsocket(fd, FIONBIO, &opt); 48 | } 49 | 50 | void set_close_on_exec(socket_t fd){ } 51 | 52 | static char s_zero = 0; 53 | bool wsa_send_empty(socket_t fd, WSAOVERLAPPED& ovl) { 54 | DWORD bytes = 0; 55 | WSABUF ws_buf = { 0, &s_zero }; 56 | 57 | memset(&ovl, 0, sizeof(ovl)); 58 | int ret = WSASend(fd, &ws_buf, 1, &bytes, 0, &ovl, nullptr); 59 | if (ret == 0) { 60 | return true; 61 | } else if (ret == SOCKET_ERROR) { 62 | int err = get_socket_error(); 63 | if (err == WSA_IO_PENDING) { 64 | return true; 65 | } 66 | } 67 | return false; 68 | } 69 | 70 | bool wsa_recv_empty(socket_t fd, WSAOVERLAPPED& ovl) { 71 | DWORD bytes = 0; 72 | DWORD flags = 0; 73 | WSABUF ws_buf = { 0, &s_zero }; 74 | 75 | memset(&ovl, 0, sizeof(ovl)); 76 | int ret = WSARecv(fd, &ws_buf, 1, &bytes, &flags, &ovl, nullptr); 77 | if (ret == 0) { 78 | return true; 79 | } else if (ret == SOCKET_ERROR) { 80 | int err = get_socket_error(); 81 | if (err == WSA_IO_PENDING) { 82 | return true; 83 | } 84 | } 85 | return false; 86 | } 87 | #endif 88 | 89 | bool make_ip_addr(sockaddr_storage* addr, size_t* len, const char ip[], int port) { 90 | if (strchr(ip, ':')) { 91 | sockaddr_in6* ipv6 = (sockaddr_in6*)addr; 92 | memset(ipv6, 0, sizeof(*ipv6)); 93 | ipv6->sin6_family = AF_INET6; 94 | ipv6->sin6_port = htons(port); 95 | ipv6->sin6_addr = in6addr_any; 96 | *len = sizeof(*ipv6); 97 | return ip[0] == '\0' || inet_pton(AF_INET6, ip, &ipv6->sin6_addr) == 1; 98 | } 99 | 100 | sockaddr_in* ipv4 = (sockaddr_in*)addr; 101 | memset(ipv4, 0, sizeof(*ipv4)); 102 | ipv4->sin_family = AF_INET; 103 | ipv4->sin_port = htons(port); 104 | ipv4->sin_addr.s_addr = INADDR_ANY; 105 | *len = sizeof(*ipv4); 106 | return ip[0] == '\0' || inet_pton(AF_INET, ip, &ipv4->sin_addr) == 1; 107 | } 108 | 109 | bool get_ip_string(char ip[], size_t ip_size, const void* addr, size_t addr_len) { 110 | auto* saddr = (sockaddr*)addr; 111 | 112 | ip[0] = '\0'; 113 | 114 | if (addr_len >= sizeof(sockaddr_in) && saddr->sa_family == AF_INET) { 115 | auto* ipv4 = (sockaddr_in*)addr; 116 | return inet_ntop(ipv4->sin_family, &ipv4->sin_addr, ip, ip_size) != nullptr; 117 | } else if (addr_len >= sizeof(sockaddr_in6) && saddr->sa_family == AF_INET6) { 118 | auto* ipv6 = (sockaddr_in6*)addr; 119 | return inet_ntop(ipv6->sin6_family, &ipv6->sin6_addr, ip, ip_size) != nullptr; 120 | } 121 | return false; 122 | } 123 | -------------------------------------------------------------------------------- /src/socket_helper.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** repository: https://github.com/trumanzhao/luna 3 | ** trumanzhao, 2016-11-01, trumanzhao@foxmail.com 4 | */ 5 | 6 | #pragma once 7 | 8 | #ifdef _MSC_VER 9 | #include 10 | #include 11 | #include 12 | #include 13 | #endif 14 | #ifdef __linux 15 | #include 16 | #endif 17 | #ifdef __APPLE__ 18 | #include 19 | #include 20 | #include 21 | #endif 22 | #if defined(__linux) || defined(__APPLE__) 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #endif 33 | 34 | #if defined(__linux) || defined(__APPLE__) 35 | #include 36 | using socket_t = int; 37 | const socket_t INVALID_SOCKET = -1; 38 | const int SOCKET_ERROR = -1; 39 | inline int get_socket_error() { return errno; } 40 | inline void close_socket_handle(socket_t fd) { close(fd); } 41 | #endif 42 | 43 | #ifdef _MSC_VER 44 | using socket_t = SOCKET; 45 | inline int get_socket_error() { return WSAGetLastError(); } 46 | inline void close_socket_handle(socket_t fd) { closesocket(fd); } 47 | bool wsa_send_empty(socket_t fd, WSAOVERLAPPED& ovl); 48 | bool wsa_recv_empty(socket_t fd, WSAOVERLAPPED& ovl); 49 | #endif 50 | 51 | bool make_ip_addr(sockaddr_storage* addr, size_t* len, const char ip[], int port); 52 | // ip字符串建议大小: char ip[INET6_ADDRSTRLEN]; 53 | bool get_ip_string(char ip[], size_t ip_size, const void* addr, size_t addr_len); 54 | 55 | void set_no_block(socket_t fd); 56 | void set_no_delay(socket_t fd, int enable); 57 | void set_close_on_exec(socket_t fd); 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/socket_listener.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ** repository: https://github.com/trumanzhao/luna 3 | ** trumanzhao, 2016-11-01, trumanzhao@foxmail.com 4 | */ 5 | 6 | #ifdef _MSC_VER 7 | #include 8 | #include 9 | #include 10 | #include 11 | #endif 12 | #ifdef __linux 13 | #include 14 | #endif 15 | #ifdef __APPLE__ 16 | #include 17 | #include 18 | #include 19 | #endif 20 | #if defined(__linux) || defined(__APPLE__) 21 | #include 22 | #include 23 | #include 24 | #endif 25 | #include 26 | #include "tools.h" 27 | #include "var_int.h" 28 | #include "socket_mgr.h" 29 | #include "socket_listener.h" 30 | 31 | #ifdef _MSC_VER 32 | socket_listener::socket_listener(uint32_t token, socket_mgr* mgr, LPFN_ACCEPTEX accept_func, LPFN_GETACCEPTEXSOCKADDRS addrs_func) : socket_node(token) { 33 | mgr->increase_count(); 34 | m_mgr = mgr; 35 | m_accept_func = accept_func; 36 | m_addrs_func = addrs_func; 37 | memset(m_nodes, 0, sizeof(m_nodes)); 38 | for (auto& node : m_nodes) { 39 | node.fd = INVALID_SOCKET; 40 | } 41 | } 42 | #endif 43 | 44 | #if defined(__linux) || defined(__APPLE__) 45 | socket_listener::socket_listener(uint32_t token, socket_mgr* mgr) : socket_node(token) { 46 | mgr->increase_count(); 47 | m_mgr = mgr; 48 | } 49 | #endif 50 | 51 | socket_listener::~socket_listener() { 52 | #ifdef _MSC_VER 53 | for (auto& node : m_nodes) { 54 | if (node.fd != INVALID_SOCKET) { 55 | close_socket_handle(node.fd); 56 | node.fd = INVALID_SOCKET; 57 | } 58 | } 59 | #endif 60 | 61 | if (m_socket != INVALID_SOCKET) { 62 | m_mgr->unwatch(m_socket); 63 | close_socket_handle(m_socket); 64 | m_socket = INVALID_SOCKET; 65 | } 66 | m_mgr->decrease_count(); 67 | } 68 | 69 | bool socket_listener::setup(socket_t fd) { 70 | m_socket = fd; 71 | return true; 72 | } 73 | 74 | #ifdef _MSC_VER 75 | void socket_listener::on_complete(WSAOVERLAPPED* ovl) { 76 | listen_node* node = CONTAINING_RECORD(ovl, listen_node, ovl); 77 | assert(node >= m_nodes && node < m_nodes + _countof(m_nodes)); 78 | assert(node->fd != INVALID_SOCKET); 79 | 80 | if (m_mgr->is_full()) { 81 | close_socket_handle(node->fd); 82 | node->fd = INVALID_SOCKET; 83 | queue_accept(ovl); 84 | return; 85 | } 86 | 87 | sockaddr* local_addr = nullptr; 88 | sockaddr* remote_addr = nullptr; 89 | int local_addr_len = 0; 90 | int remote_addr_len = 0; 91 | char ip[INET6_ADDRSTRLEN]; 92 | 93 | (*m_addrs_func)(node->buffer, 0, sizeof(node->buffer[0]), sizeof(node->buffer[2]), &local_addr, &local_addr_len, &remote_addr, &remote_addr_len); 94 | get_ip_string(ip, sizeof(ip), remote_addr, (size_t)remote_addr_len); 95 | 96 | set_none_block(node->fd); 97 | 98 | auto token = m_mgr->accept_stream(node->fd, ip); 99 | if (token == 0) { 100 | close_socket_handle(node->fd); 101 | } else { 102 | m_accept_cb(token); 103 | } 104 | 105 | node->fd = INVALID_SOCKET; 106 | queue_accept(ovl); 107 | } 108 | 109 | void socket_listener::start_listen() { 110 | for (auto& node : m_nodes) { 111 | assert(node.fd == INVALID_SOCKET); 112 | queue_accept(&node.ovl); 113 | } 114 | } 115 | 116 | void socket_listener::queue_accept(WSAOVERLAPPED* ovl) { 117 | listen_node* node = CONTAINING_RECORD(ovl, listen_node, ovl); 118 | 119 | assert(node >= m_nodes && node < m_nodes + _countof(m_nodes)); 120 | assert(node->fd == INVALID_SOCKET); 121 | 122 | sockaddr_storage listen_addr; 123 | socklen_t listen_addr_len = sizeof(listen_addr); 124 | getsockname(m_socket, (sockaddr*)&listen_addr, &listen_addr_len); 125 | 126 | while (!m_closed) { 127 | memset(ovl, 0, sizeof(*ovl)); 128 | // 注,AF_INET6本身是可以支持ipv4的,但是...需要win10以上版本,win7不支持, 所以这里取listen_addr 129 | node->fd = socket(listen_addr.ss_family, SOCK_STREAM, IPPROTO_IP); 130 | if (node->fd == INVALID_SOCKET) { 131 | m_closed = true; 132 | m_error_cb("new-socket-failed"); 133 | return; 134 | } 135 | 136 | set_none_block(node->fd); 137 | 138 | DWORD bytes = 0; 139 | static_assert(sizeof(sockaddr_storage) >= sizeof(sockaddr_in6) + 16, "buffer too small"); 140 | auto ret = (*m_accept_func)(m_socket, node->fd, node->buffer, 0, sizeof(node->buffer[0]), sizeof(node->buffer[1]), &bytes, ovl); 141 | if (!ret) { 142 | int err = get_socket_error(); 143 | if (err != ERROR_IO_PENDING) { 144 | char txt[MAX_ERROR_TXT]; 145 | get_error_string(txt, sizeof(txt), err); 146 | close_socket_handle(node->fd); 147 | node->fd = INVALID_SOCKET; 148 | m_closed = true; 149 | m_error_cb(txt); 150 | return; 151 | } 152 | m_ovl_ref++; 153 | return; 154 | } 155 | 156 | if (m_mgr->is_full()) { 157 | close_socket_handle(node->fd); 158 | node->fd = INVALID_SOCKET; 159 | continue; 160 | } 161 | 162 | sockaddr* local_addr = nullptr; 163 | sockaddr* remote_addr = nullptr; 164 | int local_addr_len = 0; 165 | int remote_addr_len = 0; 166 | char ip[INET6_ADDRSTRLEN]; 167 | 168 | (*m_addrs_func)(node->buffer, 0, sizeof(node->buffer[0]), sizeof(node->buffer[2]), &local_addr, &local_addr_len, &remote_addr, &remote_addr_len); 169 | get_ip_string(ip, sizeof(ip), remote_addr, (size_t)remote_addr_len); 170 | 171 | auto token = m_mgr->accept_stream(node->fd, ip); 172 | if (token == 0) { 173 | close_socket_handle(node->fd); 174 | } else { 175 | m_accept_cb(token); 176 | } 177 | node->fd = INVALID_SOCKET; 178 | } 179 | } 180 | #endif 181 | 182 | #if defined(__linux) || defined(__APPLE__) 183 | void socket_listener::on_can_recv(size_t max_len, bool is_eof) { 184 | size_t total_accept = 0; 185 | while (total_accept < max_len && !m_closed) { 186 | sockaddr_storage addr; 187 | socklen_t addr_len = (socklen_t)sizeof(addr); 188 | char ip[INET6_ADDRSTRLEN]; 189 | 190 | socket_t fd = accept(m_socket, (sockaddr*)&addr, &addr_len); 191 | if (fd == INVALID_SOCKET) 192 | break; 193 | 194 | total_accept++; 195 | if (m_mgr->is_full()) { 196 | close_socket_handle(fd); 197 | continue; 198 | } 199 | 200 | #ifdef __APPLE__ 201 | int opt = 1; 202 | setsockopt(m_socket, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); 203 | #endif 204 | 205 | get_ip_string(ip, sizeof(ip), &addr, (size_t)addr_len); 206 | set_no_block(fd); 207 | set_close_on_exec(fd); 208 | set_no_delay(fd, 1); 209 | 210 | auto token = m_mgr->accept_stream(fd, ip); 211 | if (token != 0) { 212 | m_accept_cb(token); 213 | } else { 214 | close_socket_handle(fd); 215 | } 216 | } 217 | } 218 | #endif 219 | 220 | 221 | -------------------------------------------------------------------------------- /src/socket_listener.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** repository: https://github.com/trumanzhao/luna 3 | ** trumanzhao, 2016-11-01, trumanzhao@foxmail.com 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "socket_helper.h" 9 | #include "io_buffer.h" 10 | #include "socket_mgr.h" 11 | 12 | struct socket_listener : public socket_node { 13 | #ifdef _MSC_VER 14 | socket_listener(uint32_t token, socket_mgr* mgr, LPFN_ACCEPTEX accept_func, LPFN_GETACCEPTEXSOCKADDRS addrs_func); 15 | #endif 16 | 17 | #if defined(__linux) || defined(__APPLE__) 18 | socket_listener(uint32_t token, socket_mgr* mgr); 19 | #endif 20 | 21 | ~socket_listener(); 22 | bool setup(socket_t fd); 23 | bool get_remote_ip(std::string& ip) override { return false; } 24 | void set_accept_callback(const std::function& cb) override { m_accept_cb = cb; } 25 | void set_error_callback(const std::function& cb) override { m_error_cb = cb; } 26 | 27 | #ifdef _MSC_VER 28 | void on_complete(WSAOVERLAPPED* ovl); 29 | void start_listen(); 30 | void queue_accept(WSAOVERLAPPED* ovl); 31 | #endif 32 | 33 | #if defined(__linux) || defined(__APPLE__) 34 | void on_can_recv(size_t max_len, bool is_eof) override; 35 | #endif 36 | 37 | private: 38 | std::function m_error_cb; 39 | std::function m_accept_cb; 40 | 41 | #ifdef _MSC_VER 42 | struct listen_node { 43 | WSAOVERLAPPED ovl; 44 | socket_t fd; 45 | sockaddr_storage buffer[2]; 46 | }; 47 | listen_node m_nodes[16]; 48 | LPFN_ACCEPTEX m_accept_func = nullptr; 49 | LPFN_GETACCEPTEXSOCKADDRS m_addrs_func = nullptr; 50 | #endif 51 | }; 52 | -------------------------------------------------------------------------------- /src/socket_mgr.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ** repository: https://github.com/trumanzhao/luna 3 | ** trumanzhao, 2016-11-01, trumanzhao@foxmail.com 4 | */ 5 | 6 | #ifdef _MSC_VER 7 | #include 8 | #include 9 | #include 10 | #include 11 | #endif 12 | #ifdef __linux 13 | #include 14 | #endif 15 | #ifdef __APPLE__ 16 | #include 17 | #include 18 | #include 19 | #endif 20 | #if defined(__linux) || defined(__APPLE__) 21 | #include 22 | #include 23 | #include 24 | #endif 25 | #include "tools.h" 26 | #include "socket_helper.h" 27 | #include "io_buffer.h" 28 | #include "socket_mgr.h" 29 | #include "socket_stream.h" 30 | #include "socket_listener.h" 31 | 32 | #ifdef _MSC_VER 33 | #pragma comment(lib, "Ws2_32.lib") 34 | #endif 35 | 36 | socket_mgr::socket_mgr() { 37 | #ifdef _MSC_VER 38 | WORD wVersion = MAKEWORD(2, 2); 39 | WSADATA wsaData; 40 | WSAStartup(wVersion, &wsaData); 41 | #endif 42 | } 43 | 44 | socket_mgr::~socket_mgr() { 45 | for (auto& node : m_nodes) { 46 | delete node.second; 47 | } 48 | 49 | #ifdef _MSC_VER 50 | if (m_handle != INVALID_HANDLE_VALUE) { 51 | CloseHandle(m_handle); 52 | m_handle = INVALID_HANDLE_VALUE; 53 | } 54 | WSACleanup(); 55 | #endif 56 | 57 | #ifdef __linux 58 | if (m_handle != -1) { 59 | ::close(m_handle); 60 | m_handle = -1; 61 | } 62 | #endif 63 | 64 | #ifdef __APPLE__ 65 | if (m_handle != -1) { 66 | ::close(m_handle); 67 | m_handle = -1; 68 | } 69 | #endif 70 | } 71 | 72 | bool socket_mgr::setup(int max_connection) { 73 | #ifdef _MSC_VER 74 | m_handle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); 75 | if (m_handle == INVALID_HANDLE_VALUE) 76 | return false; 77 | 78 | if (!get_socket_funcs()) 79 | return false; 80 | #endif 81 | 82 | #ifdef __linux 83 | m_handle = epoll_create(max_connection); 84 | if (m_handle == -1) 85 | return false; 86 | #endif 87 | 88 | #ifdef __APPLE__ 89 | m_handle = kqueue(); 90 | if (m_handle == -1) 91 | return false; 92 | #endif 93 | 94 | m_max_count = max_connection; 95 | m_events.resize(max_connection); 96 | 97 | return true; 98 | } 99 | 100 | #ifdef _MSC_VER 101 | bool socket_mgr::get_socket_funcs() { 102 | bool result = false; 103 | int ret = 0; 104 | socket_t fd = INVALID_SOCKET; 105 | DWORD bytes = 0; 106 | GUID func_guid = WSAID_ACCEPTEX; 107 | 108 | fd = socket(AF_INET, SOCK_STREAM, 0); 109 | FAILED_JUMP(fd != INVALID_SOCKET); 110 | 111 | bytes = 0; 112 | func_guid = WSAID_ACCEPTEX; 113 | ret = WSAIoctl(fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &func_guid, sizeof(func_guid), &m_accept_func, sizeof(m_accept_func), &bytes, nullptr, nullptr); 114 | FAILED_JUMP(ret != SOCKET_ERROR); 115 | 116 | bytes = 0; 117 | func_guid = WSAID_CONNECTEX; 118 | ret = WSAIoctl(fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &func_guid, sizeof(func_guid), &m_connect_func, sizeof(m_connect_func), &bytes, nullptr, nullptr); 119 | FAILED_JUMP(ret != SOCKET_ERROR); 120 | 121 | bytes = 0; 122 | func_guid = WSAID_GETACCEPTEXSOCKADDRS; 123 | ret = WSAIoctl(fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &func_guid, sizeof(func_guid), &m_addrs_func, sizeof(m_addrs_func), &bytes, nullptr, nullptr); 124 | FAILED_JUMP(ret != SOCKET_ERROR); 125 | 126 | result = true; 127 | Exit0: 128 | if (fd != INVALID_SOCKET) { 129 | close_socket_handle(fd); 130 | fd = INVALID_SOCKET; 131 | } 132 | return result; 133 | } 134 | #endif 135 | 136 | int socket_mgr::wait(int timeout) { 137 | #ifdef _MSC_VER 138 | ULONG event_count = 0; 139 | int ret = GetQueuedCompletionStatusEx(m_handle, &m_events[0], (ULONG)m_events.size(), &event_count, (DWORD)timeout, false); 140 | if (ret) { 141 | for (ULONG i = 0; i < event_count; i++) { 142 | OVERLAPPED_ENTRY& oe = m_events[i]; 143 | auto node = (socket_node*)oe.lpCompletionKey; 144 | 145 | node->m_ovl_ref--; 146 | if (!node->m_closed) { 147 | node->m_io_handing = true; 148 | node->on_complete(oe.lpOverlapped); 149 | node->m_io_handing = false; 150 | } 151 | 152 | if (node->m_closed && node->m_ovl_ref == 0) { 153 | m_nodes.erase(node->m_token); 154 | delete node; 155 | } 156 | } 157 | } 158 | #endif 159 | 160 | #ifdef __linux 161 | int event_count = epoll_wait(m_handle, &m_events[0], (int)m_events.size(), timeout); 162 | for (int i = 0; i < event_count; i++) { 163 | epoll_event& ev = m_events[i]; 164 | auto node = (socket_node*)ev.data.ptr; 165 | 166 | node->m_io_handing = true; 167 | if ((ev.events & EPOLLIN) && !node->m_closed) { 168 | node->on_can_recv(); 169 | } 170 | 171 | if ((ev.events & EPOLLOUT) && !node->m_closed) { 172 | node->on_can_send(); 173 | } 174 | node->m_io_handing = false; 175 | 176 | if (node->m_closed) { 177 | m_nodes.erase(node->m_token); 178 | delete node; 179 | } 180 | } 181 | #endif 182 | 183 | #ifdef __APPLE__ 184 | timespec time_wait; 185 | time_wait.tv_sec = timeout / 1000; 186 | time_wait.tv_nsec = (timeout % 1000) * 1000000; 187 | int event_count = kevent(m_handle, nullptr, 0, &m_events[0], (int)m_events.size(), timeout >= 0 ? &time_wait : nullptr); 188 | for (int i = 0; i < event_count; i++) { 189 | struct kevent& ev = m_events[i]; 190 | auto node = (socket_node*)ev.udata; 191 | if (!node->m_closed) { 192 | node->m_io_handing = true; 193 | if (ev.filter == EVFILT_READ) { 194 | node->on_can_recv((size_t)ev.data, (ev.flags & EV_EOF) != 0); 195 | } else if (ev.filter == EVFILT_WRITE) { 196 | node->on_can_send((size_t)ev.data, (ev.flags & EV_EOF) != 0); 197 | } 198 | node->m_io_handing = false; 199 | if (node->m_closed) { 200 | m_close_list.push_back(node); 201 | } 202 | } 203 | } 204 | 205 | for (auto node : m_close_list) { 206 | m_nodes.erase(node->m_token); 207 | delete node; 208 | } 209 | m_close_list.clear(); 210 | #endif 211 | 212 | while (!m_delay_calls.empty()) { 213 | m_delay_calls.front()(); 214 | m_delay_calls.pop_front(); 215 | } 216 | 217 | auto now = get_time_ms(); 218 | auto it = m_timeout_list.begin(); 219 | while (it != m_timeout_list.end()) { 220 | auto node_it = m_nodes.find(it->m_token); 221 | if (node_it == m_nodes.end()){ 222 | it = m_timeout_list.erase(it); 223 | continue; 224 | } 225 | 226 | if (it->m_deadline > now) 227 | break; 228 | 229 | it = m_timeout_list.erase(it); 230 | socket_stream* node = (socket_stream*)node_it->second; 231 | if (!node->m_connected && !node->m_closed) { 232 | node->on_connect(false, "timeout"); 233 | if (node->m_ovl_ref == 0) { 234 | m_nodes.erase(node->m_token); 235 | delete node; 236 | } 237 | } 238 | } 239 | 240 | return (int)event_count; 241 | } 242 | 243 | uint32_t socket_mgr::listen(std::string& err, const char ip[], int port, int backlog) { 244 | int ret = false; 245 | socket_t fd = INVALID_SOCKET; 246 | sockaddr_storage addr; 247 | size_t addr_len = 0; 248 | int one = 1; 249 | auto token = new_token(); 250 | 251 | #ifdef _MSC_VER 252 | auto* listener = new socket_listener(token, this, m_accept_func, m_addrs_func); 253 | #endif 254 | 255 | #if defined(__linux) || defined(__APPLE__) 256 | auto* listener = new socket_listener(token, this); 257 | #endif 258 | 259 | ret = make_ip_addr(&addr, &addr_len, ip, port); 260 | FAILED_JUMP(ret); 261 | 262 | fd = socket(addr.ss_family, SOCK_STREAM, IPPROTO_IP); 263 | FAILED_JUMP(fd != INVALID_SOCKET); 264 | 265 | set_no_block(fd); 266 | set_close_on_exec(fd); 267 | 268 | ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)); 269 | FAILED_JUMP(ret != SOCKET_ERROR); 270 | 271 | // macOSX require addr_len to be the real len (ipv4/ipv6) 272 | ret = bind(fd, (sockaddr*)&addr, (int)addr_len); 273 | FAILED_JUMP(ret != SOCKET_ERROR); 274 | 275 | ret = ::listen(fd, backlog); 276 | FAILED_JUMP(ret != SOCKET_ERROR); 277 | 278 | if (watch_listen(fd, listener) && listener->setup(fd)) { 279 | m_nodes[token] = listener; 280 | return token; 281 | } 282 | 283 | #ifdef _MSC_VER 284 | m_delay_calls.push_back([this, token](){ 285 | auto node = (socket_listener*)find_node(token); 286 | if (node && !node->m_closed) { 287 | node->start_listen(); 288 | } 289 | }); 290 | #endif 291 | 292 | Exit0: 293 | get_error_string(err, get_socket_error()); 294 | delete listener; 295 | if (fd != INVALID_SOCKET) { 296 | close_socket_handle(fd); 297 | fd = INVALID_SOCKET; 298 | } 299 | return 0; 300 | } 301 | 302 | uint32_t socket_mgr::connect(std::string& err, const char node_name[], const char service_name[], int timeout) { 303 | if (is_full()) { 304 | err = "too-many-connection"; 305 | return 0; 306 | } 307 | 308 | auto token = new_token(); 309 | 310 | #ifdef _MSC_VER 311 | socket_stream* stm = new socket_stream(token, this, m_connect_func); 312 | #endif 313 | 314 | #if defined(__linux) || defined(__APPLE__) 315 | socket_stream* stm = new socket_stream(token, this); 316 | #endif 317 | 318 | m_delay_calls.push_back([this, token]() { 319 | auto node = (socket_stream*)find_node(token); 320 | if (node && !node->m_closed) { 321 | node->try_connect(); 322 | } 323 | }); 324 | 325 | if (timeout > 0) { 326 | m_timeout_list.insert({token, get_time_ms() + timeout}); 327 | } 328 | 329 | stm->connect(node_name, service_name, timeout); 330 | m_nodes[token] = stm; 331 | return token; 332 | } 333 | 334 | void socket_mgr::set_send_buffer_size(uint32_t token, size_t size) { 335 | auto node = find_node(token); 336 | if (node && size > 0) { 337 | node->set_send_buffer_size(size); 338 | } 339 | } 340 | 341 | void socket_mgr::set_recv_buffer_size(uint32_t token, size_t size) { 342 | auto node = find_node(token); 343 | if (node && size > 0) { 344 | node->set_recv_buffer_size(size); 345 | } 346 | } 347 | 348 | void socket_mgr::set_nodelay(uint32_t token, int flag) { 349 | auto node = find_node(token); 350 | if (node) { 351 | node->set_nodelay(flag); 352 | } 353 | } 354 | 355 | void socket_mgr::send(uint32_t token, const void* data, size_t data_len) { 356 | auto node = find_node(token); 357 | if (node) { 358 | node->send(data, data_len); 359 | } 360 | } 361 | 362 | void socket_mgr::close(uint32_t token) { 363 | auto node = find_node(token); 364 | if (node && !node->m_closed) { 365 | node->close(); 366 | if (node->m_ovl_ref == 0 && !node->m_io_handing) { 367 | m_nodes.erase(node->m_token); 368 | delete node; 369 | } 370 | } 371 | } 372 | 373 | bool socket_mgr::get_remote_ip(uint32_t token, std::string& ip) { 374 | auto node = find_node(token); 375 | if (node) { 376 | return node->get_remote_ip(ip); 377 | } 378 | return false; 379 | } 380 | 381 | void socket_mgr::set_accept_callback(uint32_t token, const std::function& cb) { 382 | auto node = find_node(token); 383 | if (node) { 384 | node->set_accept_callback(cb); 385 | } 386 | } 387 | 388 | void socket_mgr::set_connect_callback(uint32_t token, const std::function& cb) { 389 | auto node = find_node(token); 390 | if (node) { 391 | node->set_connect_callback(cb); 392 | } 393 | } 394 | 395 | void socket_mgr::set_package_callback(uint32_t token, const std::function& cb) { 396 | auto node = find_node(token); 397 | if (node) { 398 | node->set_package_callback(cb); 399 | } 400 | } 401 | 402 | void socket_mgr::set_error_callback(uint32_t token, const std::function& cb) { 403 | auto node = find_node(token); 404 | if (node) { 405 | node->set_error_callback(cb); 406 | } 407 | } 408 | 409 | bool socket_mgr::watch_listen(socket_t fd, socket_node* node) { 410 | #ifdef _MSC_VER 411 | return CreateIoCompletionPort((HANDLE)fd, m_handle, (ULONG_PTR)node, 0) == m_handle; 412 | #endif 413 | 414 | #ifdef __linux 415 | epoll_event ev; 416 | ev.data.ptr = node; 417 | ev.events = EPOLLIN | EPOLLET; 418 | return epoll_ctl(m_handle, EPOLL_CTL_ADD, fd, &ev) == 0; 419 | #endif 420 | 421 | #ifdef __APPLE__ 422 | struct kevent evt; 423 | EV_SET(&evt, fd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, node); 424 | return kevent(m_handle, &evt, 1, nullptr, 0, nullptr) == 0; 425 | #endif 426 | } 427 | 428 | bool socket_mgr::watch_accepted(socket_t fd, socket_node* node) { 429 | #ifdef _MSC_VER 430 | return CreateIoCompletionPort((HANDLE)fd, m_handle, (ULONG_PTR)node, 0) == m_handle; 431 | #endif 432 | 433 | #ifdef __linux 434 | epoll_event ev; 435 | ev.data.ptr = node; 436 | ev.events = EPOLLIN | EPOLLET; 437 | return epoll_ctl(m_handle, EPOLL_CTL_ADD, fd, &ev) == 0; 438 | #endif 439 | 440 | #ifdef __APPLE__ 441 | struct kevent evt[2]; 442 | EV_SET(&evt[0], fd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, node); 443 | EV_SET(&evt[1], fd, EVFILT_WRITE, EV_ADD | EV_CLEAR | EV_DISABLE, 0, 0, node); 444 | return kevent(m_handle, evt, _countof(evt), nullptr, 0, nullptr) == 0; 445 | #endif 446 | } 447 | 448 | bool socket_mgr::watch_connecting(socket_t fd, socket_node* node) { 449 | #ifdef _MSC_VER 450 | return CreateIoCompletionPort((HANDLE)fd, m_handle, (ULONG_PTR)node, 0) == m_handle; 451 | #endif 452 | 453 | #ifdef __linux 454 | epoll_event ev; 455 | ev.data.ptr = node; 456 | ev.events = EPOLLOUT | EPOLLET; 457 | return epoll_ctl(m_handle, EPOLL_CTL_ADD, fd, &ev) == 0; 458 | #endif 459 | 460 | #ifdef __APPLE__ 461 | struct kevent evt; 462 | EV_SET(&evt, fd, EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, node); 463 | return kevent(m_handle, &evt, 1, nullptr, 0, nullptr) == 0; 464 | #endif 465 | } 466 | 467 | bool socket_mgr::watch_connected(socket_t fd, socket_node* node) { 468 | #ifdef _MSC_VER 469 | return true; 470 | #endif 471 | 472 | #ifdef __linux 473 | epoll_event ev; 474 | ev.data.ptr = node; 475 | ev.events = EPOLLIN | EPOLLET; 476 | return epoll_ctl(m_handle, EPOLL_CTL_MOD, fd, &ev) == 0; 477 | #endif 478 | 479 | #ifdef __APPLE__ 480 | struct kevent evt[2]; 481 | EV_SET(&evt[0], fd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, node); 482 | EV_SET(&evt[1], fd, EVFILT_WRITE, EV_ADD | EV_CLEAR | EV_DISABLE, 0, 0, node); 483 | return kevent(m_handle, evt, _countof(evt), nullptr, 0, nullptr) == 0; 484 | #endif 485 | } 486 | 487 | bool socket_mgr::watch_send(socket_t fd, socket_node* node, bool enable) { 488 | #ifdef _MSC_VER 489 | return true; 490 | #endif 491 | 492 | #ifdef __linux 493 | epoll_event ev; 494 | ev.data.ptr = node; 495 | ev.events = EPOLLIN | (enable ? EPOLLOUT : 0) | EPOLLET; 496 | return epoll_ctl(m_handle, EPOLL_CTL_MOD, fd, &ev) == 0; 497 | #endif 498 | 499 | #ifdef __APPLE__ 500 | struct kevent evt; 501 | EV_SET(&evt, fd, EVFILT_WRITE, EV_ADD | EV_CLEAR | (enable ? 0 : EV_DISABLE), 0, 0, node); 502 | return kevent(m_handle, &evt, 1, nullptr, 0, nullptr) == 0; 503 | #endif 504 | } 505 | 506 | // 通常情况下,close一个socket后即自动从epoll/kqueue中移除 507 | // 之所以加一个unwatch显式的移除,是为了避免进程fork带来的问题 508 | void socket_mgr::unwatch(socket_t fd) { 509 | #ifdef __linux 510 | epoll_event ev; 511 | ev.data.ptr = nullptr; 512 | ev.events = 0; 513 | epoll_ctl(m_handle, EPOLL_CTL_DEL, fd, &ev); 514 | #endif 515 | 516 | #ifdef __APPLE__ 517 | struct kevent evt[2]; 518 | EV_SET(&evt[0], fd, EVFILT_READ, EV_DELETE, 0, 0, nullptr); 519 | EV_SET(&evt[1], fd, EVFILT_WRITE, EV_DELETE, 0, 0, nullptr); 520 | kevent(m_handle, evt, _countof(evt), nullptr, 0, nullptr); 521 | #endif 522 | } 523 | 524 | int socket_mgr::accept_stream(socket_t fd, const char ip[]) { 525 | auto token = new_token(); 526 | auto* stm = new socket_stream(token, this); 527 | if (watch_accepted(fd, stm) && stm->accept_socket(fd, ip)) { 528 | m_nodes[token] = stm; 529 | return token; 530 | } 531 | delete stm; 532 | return 0; 533 | } 534 | 535 | socket_node* socket_mgr::find_node(int token) { 536 | auto it = m_nodes.find(token); 537 | if (it != m_nodes.end()) { 538 | return it->second; 539 | } 540 | return nullptr; 541 | } 542 | 543 | uint32_t socket_mgr::new_token() { 544 | while (++m_next_token == 0 || m_nodes.find(m_next_token) != m_nodes.end()) { 545 | // nothing ... 546 | } 547 | return m_next_token; 548 | } 549 | 550 | -------------------------------------------------------------------------------- /src/socket_mgr.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** repository: https://github.com/trumanzhao/luna 3 | ** trumanzhao, 2016-11-01, trumanzhao@foxmail.com 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "socket_helper.h" 15 | #include "socket_node.h" 16 | 17 | class socket_mgr { 18 | public: 19 | socket_mgr(); 20 | ~socket_mgr(); 21 | 22 | bool setup(int max_connection); 23 | 24 | #ifdef _MSC_VER 25 | bool get_socket_funcs(); 26 | #endif 27 | 28 | int wait(int timeout); 29 | 30 | uint32_t listen(std::string& err, const char ip[], int port, int backlog); 31 | uint32_t connect(std::string& err, const char node_name[], const char service_name[], int timeout); 32 | 33 | void set_send_buffer_size(uint32_t token, size_t size); 34 | void set_recv_buffer_size(uint32_t token, size_t size); 35 | void set_nodelay(uint32_t token, int flag); 36 | void send(uint32_t token, const void* data, size_t data_len); 37 | void close(uint32_t token); 38 | bool get_remote_ip(uint32_t token, std::string& ip); 39 | 40 | void set_accept_callback(uint32_t token, const std::function& cb); 41 | void set_connect_callback(uint32_t token, const std::function& cb); 42 | void set_package_callback(uint32_t token, const std::function& cb); 43 | void set_error_callback(uint32_t token, const std::function& cb); 44 | 45 | bool watch_listen(socket_t fd, socket_node* node); 46 | bool watch_accepted(socket_t fd, socket_node* node); 47 | bool watch_connecting(socket_t fd, socket_node* node); 48 | bool watch_connected(socket_t fd, socket_node* node); 49 | bool watch_send(socket_t fd, socket_node* node, bool enable); 50 | void unwatch(socket_t fd); 51 | int accept_stream(socket_t fd, const char ip[]); 52 | 53 | void increase_count() { m_count++; } 54 | void decrease_count() { m_count--; } 55 | bool is_full() { return m_count >= m_max_count; } 56 | socket_node* find_node(int token); 57 | uint32_t new_token(); 58 | private: 59 | 60 | #ifdef _MSC_VER 61 | LPFN_ACCEPTEX m_accept_func = nullptr; 62 | LPFN_CONNECTEX m_connect_func = nullptr; 63 | LPFN_GETACCEPTEXSOCKADDRS m_addrs_func = nullptr; 64 | HANDLE m_handle = INVALID_HANDLE_VALUE; 65 | std::vector m_events; 66 | #endif 67 | 68 | #ifdef __linux 69 | int m_handle = -1; 70 | std::vector m_events; 71 | #endif 72 | 73 | #ifdef __APPLE__ 74 | int m_handle = -1; 75 | std::vector m_events; 76 | std::vector m_close_list; 77 | #endif 78 | 79 | struct timeout_node_t { 80 | uint32_t m_token; 81 | int64_t m_deadline; 82 | bool operator < (const timeout_node_t& other) const { 83 | if (m_deadline == other.m_deadline) { 84 | return m_token < other.m_token; 85 | } 86 | return m_deadline < other.m_deadline; 87 | } 88 | }; 89 | std::set m_timeout_list; 90 | std::list> m_delay_calls; 91 | 92 | int m_max_count = 0; 93 | int m_count = 0; 94 | uint32_t m_next_token = 0; 95 | int64_t m_next_update = 0; 96 | std::unordered_map m_nodes; 97 | }; 98 | -------------------------------------------------------------------------------- /src/socket_node.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ** repository: https://github.com/trumanzhao/luabus 3 | ** trumanzhao, 2020-01-05, trumanzhao@foxmail.com 4 | */ 5 | 6 | #ifdef _MSC_VER 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #endif 13 | #ifdef __linux 14 | #include 15 | #endif 16 | #ifdef __APPLE__ 17 | #include 18 | #include 19 | #include 20 | #endif 21 | #if defined(__linux) || defined(__APPLE__) 22 | #include 23 | #include 24 | #include 25 | #endif 26 | #include 27 | #include 28 | #include "tools.h" 29 | #include "var_int.h" 30 | #include "socket_mgr.h" 31 | #include "socket_node.h" 32 | 33 | void socket_node::close() { 34 | if (m_socket != INVALID_SOCKET) { 35 | m_mgr->unwatch(m_socket); 36 | close_socket_handle(m_socket); 37 | m_socket = INVALID_SOCKET; 38 | } 39 | m_closed = true; 40 | } 41 | -------------------------------------------------------------------------------- /src/socket_node.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** repository: https://github.com/trumanzhao/luabus 3 | ** trumanzhao, 2020-01-05, trumanzhao@foxmail.com 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include "tools.h" 10 | #include "socket_helper.h" 11 | 12 | class socket_mgr; 13 | 14 | struct socket_node { 15 | socket_node(uint32_t token) : m_token(token) {} 16 | virtual ~socket_node() {}; 17 | void close(); 18 | virtual bool get_remote_ip(std::string& ip) = 0; 19 | virtual void connect(const char node_name[], const char service_name[]) { } 20 | virtual void set_send_buffer_size(size_t size) { } 21 | virtual void set_recv_buffer_size(size_t size) { } 22 | virtual void set_nodelay(int flag) { } 23 | virtual void send(const void* data, size_t data_len) { } 24 | virtual void set_accept_callback(const std::function& cb) { } 25 | virtual void set_connect_callback(const std::function& cb) { } 26 | virtual void set_package_callback(const std::function& cb) { } 27 | virtual void set_error_callback(const std::function& cb) { } 28 | 29 | #ifdef _MSC_VER 30 | virtual void on_complete(WSAOVERLAPPED* ovl) = 0; 31 | #endif 32 | 33 | #if defined(__linux) || defined(__APPLE__) 34 | virtual void on_can_recv(size_t data_len = UINT_MAX, bool is_eof = false) {}; 35 | virtual void on_can_send(size_t data_len = UINT_MAX, bool is_eof = false) {}; 36 | #endif 37 | 38 | uint32_t m_token; 39 | socket_mgr* m_mgr = nullptr; 40 | socket_t m_socket = INVALID_SOCKET; 41 | bool m_connected = false; 42 | bool m_io_handing = false; 43 | bool m_closed = false; 44 | int m_ovl_ref = 0; 45 | }; 46 | -------------------------------------------------------------------------------- /src/socket_stream.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ** repository: https://github.com/trumanzhao/luna 3 | ** trumanzhao, 2016-11-01, trumanzhao@foxmail.com 4 | */ 5 | 6 | #ifdef _MSC_VER 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #endif 13 | #ifdef __linux 14 | #include 15 | #endif 16 | #ifdef __APPLE__ 17 | #include 18 | #include 19 | #include 20 | #endif 21 | #if defined(__linux) || defined(__APPLE__) 22 | #include 23 | #include 24 | #include 25 | #endif 26 | #include 27 | #include 28 | #include "tools.h" 29 | #include "var_int.h" 30 | #include "socket_mgr.h" 31 | #include "socket_stream.h" 32 | 33 | #ifdef __linux 34 | static const int s_send_flag = MSG_NOSIGNAL; 35 | #endif 36 | 37 | #if defined(_MSC_VER) || defined(__APPLE__) 38 | static const int s_send_flag = 0; 39 | #endif 40 | 41 | #ifdef _MSC_VER 42 | socket_stream::socket_stream(uint32_t token, socket_mgr* mgr, LPFN_CONNECTEX connect_func) : socket_node(token) { 43 | mgr->increase_count(); 44 | m_mgr = mgr; 45 | m_connect_func = connect_func; 46 | m_ip[0] = 0; 47 | } 48 | #endif 49 | 50 | socket_stream::socket_stream(uint32_t token, socket_mgr* mgr) : socket_node(token) { 51 | mgr->increase_count(); 52 | m_mgr = mgr; 53 | m_ip[0] = 0; 54 | } 55 | 56 | socket_stream::~socket_stream() { 57 | if (m_socket != INVALID_SOCKET) { 58 | m_mgr->unwatch(m_socket); 59 | close_socket_handle(m_socket); 60 | m_socket = INVALID_SOCKET; 61 | } 62 | 63 | if (m_addr != nullptr) { 64 | freeaddrinfo(m_addr); 65 | m_addr = nullptr; 66 | } 67 | m_mgr->decrease_count(); 68 | } 69 | 70 | bool socket_stream::get_remote_ip(std::string& ip) { 71 | ip = m_ip; 72 | return true; 73 | } 74 | 75 | bool socket_stream::accept_socket(socket_t fd, const char ip[]) { 76 | #ifdef _MSC_VER 77 | if (!wsa_recv_empty(fd, m_recv_ovl)) 78 | return false; 79 | m_ovl_ref++; 80 | #endif 81 | 82 | safe_cpy(m_ip, ip); 83 | m_socket = fd; 84 | m_connected = true; 85 | return true; 86 | } 87 | 88 | void socket_stream::connect(const char node_name[], const char service_name[], int timeout) { 89 | m_node_name = node_name; 90 | m_service_name = service_name; 91 | } 92 | 93 | #ifdef _MSC_VER 94 | static bool bind_any(socket_t s) { 95 | struct sockaddr_in6 v6addr; 96 | 97 | memset(&v6addr, 0, sizeof(v6addr)); 98 | v6addr.sin6_family = AF_INET6; 99 | v6addr.sin6_addr = in6addr_any; 100 | v6addr.sin6_port = 0; 101 | auto ret = bind(s, (sockaddr*)&v6addr, (int)sizeof(v6addr)); 102 | if (ret != SOCKET_ERROR) 103 | return true; 104 | 105 | struct sockaddr_in v4addr; 106 | memset(&v4addr, 0, sizeof(v4addr)); 107 | v4addr.sin_family = AF_INET; 108 | v4addr.sin_addr.s_addr = INADDR_ANY; 109 | v4addr.sin_port = 0; 110 | 111 | ret = bind(s, (sockaddr*)&v4addr, (int)sizeof(v4addr)); 112 | return ret != SOCKET_ERROR; 113 | } 114 | 115 | bool socket_stream::do_connect() { 116 | if (!bind_any(m_socket)) { 117 | on_connect(false, "bind-failed"); 118 | return false; 119 | } 120 | 121 | if (!m_mgr->watch_connecting(m_socket, this)) { 122 | on_connect(false, "watch-failed"); 123 | return false; 124 | } 125 | 126 | memset(&m_send_ovl, 0, sizeof(m_send_ovl)); 127 | 128 | auto ret = (*m_connect_func)(m_socket, (SOCKADDR*)m_next->ai_addr, (int)m_next->ai_addrlen, nullptr, 0, nullptr, &m_send_ovl); 129 | if (!ret) { 130 | m_next = m_next->ai_next; 131 | int err = get_socket_error(); 132 | if (err == ERROR_IO_PENDING) { 133 | m_ovl_ref++; 134 | return true; 135 | } 136 | 137 | m_closed = true; 138 | on_connect(false, "connect-failed"); 139 | return false; 140 | } 141 | 142 | if (!wsa_recv_empty(m_socket, m_recv_ovl)) { 143 | on_connect(false, "connect-failed"); 144 | return false; 145 | } 146 | 147 | m_ovl_ref++; 148 | on_connect(true, "ok"); 149 | return true; 150 | } 151 | #endif 152 | 153 | #if defined(__linux) || defined(__APPLE__) 154 | bool socket_stream::do_connect() { 155 | while (true) { 156 | auto ret = ::connect(m_socket, m_next->ai_addr, (int)m_next->ai_addrlen); 157 | if (ret != SOCKET_ERROR) { 158 | on_connect(true, "ok"); 159 | break; 160 | } 161 | 162 | int err = get_socket_error(); 163 | if (err == EINTR) 164 | continue; 165 | 166 | m_next = m_next->ai_next; 167 | 168 | if (err != EINPROGRESS) 169 | return false; 170 | 171 | if (!m_mgr->watch_connecting(m_socket, this)) { 172 | on_connect(false, "watch-failed"); 173 | return false; 174 | } 175 | break; 176 | } 177 | return true; 178 | } 179 | #endif 180 | 181 | void socket_stream::try_connect() { 182 | if (m_addr == nullptr) { 183 | addrinfo hints; 184 | struct addrinfo* addr = nullptr; 185 | 186 | memset(&hints, 0, sizeof hints); 187 | hints.ai_family = AF_UNSPEC; // use AF_INET6 to force IPv6 188 | hints.ai_socktype = SOCK_STREAM; 189 | 190 | int ret = getaddrinfo(m_node_name.c_str(), m_service_name.c_str(), &hints, &addr); 191 | if (ret != 0 || addr == nullptr) { 192 | on_connect(false, "addr-error"); 193 | return; 194 | } 195 | 196 | m_addr = addr; 197 | m_next = addr; 198 | } 199 | 200 | // socket connecting 201 | if (m_socket != INVALID_SOCKET) 202 | return; 203 | 204 | while (m_next != nullptr && !m_closed) { 205 | if (m_next->ai_family != AF_INET && m_next->ai_family != AF_INET6) { 206 | m_next = m_next->ai_next; 207 | continue; 208 | } 209 | 210 | m_socket = socket(m_next->ai_family, m_next->ai_socktype, m_next->ai_protocol); 211 | if (m_socket == INVALID_SOCKET) { 212 | m_next = m_next->ai_next; 213 | continue; 214 | } 215 | 216 | #ifdef __APPLE__ 217 | int opt = 1; 218 | setsockopt(m_socket, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); 219 | #endif 220 | 221 | set_no_block(m_socket); 222 | set_close_on_exec(m_socket); 223 | set_no_delay(m_socket, 1); 224 | get_ip_string(m_ip, sizeof(m_ip), m_next->ai_addr, m_next->ai_addrlen); 225 | 226 | if (do_connect()) 227 | return; 228 | 229 | if (m_socket != INVALID_SOCKET) { 230 | close_socket_handle(m_socket); 231 | m_socket = INVALID_SOCKET; 232 | } 233 | } 234 | 235 | on_connect(false, "connect-failed"); 236 | } 237 | 238 | void socket_stream::send(const void* data, size_t data_len) { 239 | if (m_closed) 240 | return; 241 | 242 | unsigned char buffer[2048]; 243 | size_t header_len = encode_u64(buffer, sizeof(buffer), data_len); 244 | size_t total_len = header_len + data_len; 245 | if (total_len <= sizeof(buffer)) { 246 | memcpy(buffer + header_len, data, data_len); 247 | stream_send((char*)buffer, total_len); 248 | } else { 249 | stream_send((char*)buffer, header_len); 250 | stream_send((char*)data, data_len); 251 | } 252 | } 253 | 254 | void socket_stream::stream_send(const char* data, size_t data_len) { 255 | if (m_closed) 256 | return; 257 | 258 | if (!m_send_buffer.empty()) { 259 | if (!m_send_buffer.push_data(data, data_len)) { 260 | on_error("send-buffer-full"); 261 | } 262 | return; 263 | } 264 | 265 | while (data_len > 0) { 266 | int send_len = ::send(m_socket, data, (int)data_len, s_send_flag); 267 | if (send_len == SOCKET_ERROR) { 268 | int err = get_socket_error(); 269 | 270 | #ifdef _MSC_VER 271 | if (err == WSAEWOULDBLOCK) { 272 | if (!m_send_buffer.push_data(data, data_len)) { 273 | on_error("send-buffer-full"); 274 | return; 275 | } 276 | 277 | if (!wsa_send_empty(m_socket, m_send_ovl)) { 278 | on_error("send-failed"); 279 | return; 280 | } 281 | m_ovl_ref++; 282 | return; 283 | } 284 | #endif 285 | 286 | #if defined(__linux) || defined(__APPLE__) 287 | if (err == EINTR) 288 | continue; 289 | 290 | if (err == EAGAIN) { 291 | if (!m_send_buffer.push_data(data, data_len)) { 292 | on_error("send-buffer-full"); 293 | return; 294 | } 295 | 296 | if (!m_mgr->watch_send(m_socket, this, true)) { 297 | on_error("watch-error"); 298 | return; 299 | } 300 | 301 | return; 302 | } 303 | #endif 304 | 305 | on_error("send-failed"); 306 | return; 307 | } 308 | 309 | if (send_len == 0) { 310 | on_error("connection-lost"); 311 | return; 312 | } 313 | 314 | data += send_len; 315 | data_len -= send_len; 316 | } 317 | } 318 | 319 | #ifdef _MSC_VER 320 | void socket_stream::on_complete(WSAOVERLAPPED* ovl) { 321 | if (m_connected) { 322 | if (ovl == &m_recv_ovl) { 323 | do_recv(UINT_MAX, false); 324 | } else { 325 | do_send(UINT_MAX, false); 326 | } 327 | return; 328 | } 329 | 330 | int seconds = 0; 331 | socklen_t sock_len = (socklen_t)sizeof(seconds); 332 | auto ret = getsockopt(m_socket, SOL_SOCKET, SO_CONNECT_TIME, (char*)&seconds, &sock_len); 333 | if (ret == 0 && seconds != 0xffffffff) { 334 | if (!wsa_recv_empty(m_socket, m_recv_ovl)) { 335 | on_connect(false, "connect-failed"); 336 | return; 337 | } 338 | 339 | m_ovl_ref++; 340 | on_connect(true, "ok"); 341 | return; 342 | } 343 | 344 | // socket连接失败,还可以继续dns解析的下一个地址继续尝试 345 | close_socket_handle(m_socket); 346 | m_socket = INVALID_SOCKET; 347 | if (m_next == nullptr) { 348 | on_connect(false, "connect-failed"); 349 | } 350 | } 351 | #endif 352 | 353 | #if defined(__linux) || defined(__APPLE__) 354 | void socket_stream::on_can_recv(size_t max_len, bool is_eof) { 355 | do_recv(max_len, is_eof); 356 | } 357 | 358 | void socket_stream::on_can_send(size_t max_len, bool is_eof) { 359 | if (m_connected) { 360 | do_send(max_len, is_eof); 361 | return; 362 | } 363 | 364 | int err = 0; 365 | socklen_t sock_len = sizeof(err); 366 | auto ret = getsockopt(m_socket, SOL_SOCKET, SO_ERROR, (char*)&err, &sock_len); 367 | if (ret == 0 && err == 0 && !is_eof) { 368 | if (!m_mgr->watch_connected(m_socket, this)) { 369 | on_connect(false, "watch-error"); 370 | return; 371 | } 372 | 373 | on_connect(true, "ok"); 374 | return; 375 | } 376 | 377 | // socket连接失败,还可以继续dns解析的下一个地址继续尝试 378 | m_mgr->unwatch(m_socket); 379 | close_socket_handle(m_socket); 380 | m_socket = INVALID_SOCKET; 381 | if (m_next == nullptr) { 382 | on_connect(false, "connect-failed"); 383 | } 384 | } 385 | #endif 386 | 387 | 388 | void socket_stream::do_send(size_t max_len, bool is_eof) { 389 | size_t total_send = 0; 390 | while (total_send < max_len && !m_closed) { 391 | size_t data_len = 0; 392 | auto* data = m_send_buffer.peek_data(&data_len); 393 | if (data_len == 0) { 394 | if (!m_mgr->watch_send(m_socket, this, false)) { 395 | on_error("watch-error"); 396 | return; 397 | } 398 | break; 399 | } 400 | 401 | size_t try_len = std::min(data_len, max_len - total_send); 402 | int send_len = ::send(m_socket, (char*)data, (int)try_len, s_send_flag); 403 | if (send_len == SOCKET_ERROR) { 404 | int err = get_socket_error(); 405 | 406 | #ifdef _MSC_VER 407 | if (err == WSAEWOULDBLOCK) { 408 | if (!wsa_send_empty(m_socket, m_send_ovl)) { 409 | on_error("send-failed"); 410 | return; 411 | } 412 | m_ovl_ref++; 413 | break; 414 | } 415 | #endif 416 | 417 | #if defined(__linux) || defined(__APPLE__) 418 | if (err == EINTR) 419 | continue; 420 | 421 | if (err == EAGAIN) 422 | break; 423 | #endif 424 | 425 | on_error("send-failed"); 426 | return; 427 | } 428 | 429 | if (send_len == 0) { 430 | on_error("connection-lost"); 431 | return; 432 | } 433 | 434 | total_send += send_len; 435 | m_send_buffer.pop_data((size_t)send_len); 436 | } 437 | 438 | m_send_buffer.compact(true); 439 | 440 | if (is_eof || max_len == 0) { 441 | on_error("connection-lost"); 442 | } 443 | } 444 | 445 | void socket_stream::do_recv(size_t max_len, bool is_eof) { 446 | size_t total_recv = 0; 447 | while (total_recv < max_len && !m_closed) { 448 | size_t space_len = 0; 449 | auto* space = m_recv_buffer.peek_space(&space_len); 450 | if (space_len == 0) { 451 | on_error("package-too-large"); 452 | return; 453 | } 454 | 455 | size_t try_len = std::min(space_len, max_len - total_recv); 456 | int recv_len = recv(m_socket, (char*)space, (int)try_len, 0); 457 | if (recv_len < 0) { 458 | int err = get_socket_error(); 459 | 460 | #ifdef _MSC_VER 461 | if (err == WSAEWOULDBLOCK) { 462 | if (!wsa_recv_empty(m_socket, m_recv_ovl)) { 463 | on_error("recv-failed"); 464 | return; 465 | } 466 | m_ovl_ref++; 467 | break; 468 | } 469 | #endif 470 | 471 | #if defined(__linux) || defined(__APPLE__) 472 | if (err == EINTR) 473 | continue; 474 | 475 | if (err == EAGAIN) 476 | break; 477 | #endif 478 | 479 | on_error("recv-failed"); 480 | return; 481 | } 482 | 483 | if (recv_len == 0) { 484 | on_error("connection-lost"); 485 | return; 486 | } 487 | 488 | total_recv += recv_len; 489 | m_recv_buffer.pop_space(recv_len); 490 | dispatch_package(); 491 | } 492 | 493 | if (is_eof || max_len == 0) { 494 | on_error("connection-lost"); 495 | } 496 | } 497 | 498 | void socket_stream::dispatch_package() { 499 | while (!m_closed) { 500 | size_t data_len = 0; 501 | uint64_t package_size = 0; 502 | auto* data = m_recv_buffer.peek_data(&data_len); 503 | size_t header_len = decode_u64(&package_size, data, data_len); 504 | if (header_len == 0) 505 | break; 506 | 507 | if (data_len < header_len + package_size) 508 | break; 509 | 510 | m_package_cb((char*)data + header_len, (size_t)package_size); 511 | 512 | m_recv_buffer.pop_data(header_len + (size_t)package_size); 513 | } 514 | 515 | m_recv_buffer.compact(); 516 | } 517 | 518 | void socket_stream::on_error(const char err[]) { 519 | if (!m_closed) { 520 | // kqueue实现下,如果eof时不及时关闭或unwatch,则会触发很多次eof 521 | if (m_socket != INVALID_SOCKET) { 522 | m_mgr->unwatch(m_socket); 523 | close_socket_handle(m_socket); 524 | m_socket = INVALID_SOCKET; 525 | } 526 | 527 | m_closed = true; 528 | m_error_cb(err); 529 | } 530 | } 531 | 532 | void socket_stream::on_connect(bool ok, const char reason[]) { 533 | m_next = nullptr; 534 | if (m_addr != nullptr) { 535 | freeaddrinfo(m_addr); 536 | m_addr = nullptr; 537 | } 538 | 539 | if (!m_closed) { 540 | if (!ok) { 541 | if (m_socket != INVALID_SOCKET) { 542 | m_mgr->unwatch(m_socket); 543 | close_socket_handle(m_socket); 544 | m_socket = INVALID_SOCKET; 545 | } 546 | m_closed = true; 547 | } 548 | m_connected = ok; 549 | m_connect_cb(ok, reason); 550 | } 551 | } 552 | 553 | -------------------------------------------------------------------------------- /src/socket_stream.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** repository: https://github.com/trumanzhao/luna 3 | ** trumanzhao, 2016-11-01, trumanzhao@foxmail.com 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "socket_helper.h" 9 | #include "io_buffer.h" 10 | #include "socket_mgr.h" 11 | 12 | struct socket_stream : public socket_node { 13 | #ifdef _MSC_VER 14 | socket_stream(uint32_t token, socket_mgr* mgr, LPFN_CONNECTEX connect_func); 15 | #endif 16 | socket_stream(uint32_t token, socket_mgr* mgr); 17 | 18 | ~socket_stream(); 19 | bool get_remote_ip(std::string& ip) override; 20 | bool accept_socket(socket_t fd, const char ip[]); 21 | void connect(const char node_name[], const char service_name[], int timeout); 22 | bool do_connect(); 23 | void try_connect(); 24 | void set_package_callback(const std::function& cb) override { m_package_cb = cb; } 25 | void set_error_callback(const std::function& cb) override { m_error_cb = cb; } 26 | void set_connect_callback(const std::function& cb) override { m_connect_cb = cb; } 27 | void set_send_buffer_size(size_t size) override { m_send_buffer.resize(size); } 28 | void set_recv_buffer_size(size_t size) override { m_recv_buffer.resize(size); } 29 | void set_nodelay(int flag) override { set_no_delay(m_socket, flag); } 30 | void send(const void* data, size_t data_len) override; 31 | void stream_send(const char* data, size_t data_len); 32 | 33 | #ifdef _MSC_VER 34 | void on_complete(WSAOVERLAPPED* ovl) override; 35 | #endif 36 | 37 | #if defined(__linux) || defined(__APPLE__) 38 | void on_can_recv(size_t max_len, bool is_eof) override; 39 | void on_can_send(size_t max_len, bool is_eof) override; 40 | #endif 41 | 42 | void do_send(size_t max_len, bool is_eof); 43 | void do_recv(size_t max_len, bool is_eof); 44 | 45 | void dispatch_package(); 46 | void on_error(const char err[]); 47 | void on_connect(bool ok, const char reason[]); 48 | 49 | io_buffer m_recv_buffer; 50 | io_buffer m_send_buffer; 51 | 52 | std::string m_node_name; 53 | std::string m_service_name; 54 | struct addrinfo* m_addr = nullptr; 55 | struct addrinfo* m_next = nullptr; 56 | char m_ip[INET6_ADDRSTRLEN]; 57 | 58 | #ifdef _MSC_VER 59 | LPFN_CONNECTEX m_connect_func = nullptr; 60 | WSAOVERLAPPED m_send_ovl; 61 | WSAOVERLAPPED m_recv_ovl; 62 | #endif 63 | 64 | std::function m_package_cb; 65 | std::function m_error_cb; 66 | std::function m_connect_cb; 67 | }; 68 | -------------------------------------------------------------------------------- /src/tools.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ** repository: https://github.com/trumanzhao/luna 3 | ** trumanzhao, 2016/10/19, trumanzhao@foxmail.com 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #ifdef _MSC_VER 15 | #include 16 | #endif 17 | #ifdef __linux 18 | #include 19 | #include 20 | #endif 21 | #ifdef __APPLE__ 22 | #include 23 | #include 24 | #include 25 | #endif 26 | #include "tools.h" 27 | 28 | time_t get_file_time(const char* file_name) { 29 | if (file_name == nullptr) 30 | return 0; 31 | 32 | struct stat file_info; 33 | int ret = stat(file_name, &file_info); 34 | if (ret != 0) 35 | return 0; 36 | 37 | #ifdef __APPLE__ 38 | return file_info.st_mtimespec.tv_sec; 39 | #endif 40 | 41 | #if defined(_MSC_VER) || defined(__linux) 42 | return file_info.st_mtime; 43 | #endif 44 | } 45 | 46 | char* get_error_string(char buffer[], int len, int no) { 47 | buffer[0] = '\0'; 48 | 49 | #ifdef _MSC_VER 50 | FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, no, 0, buffer, len, nullptr); 51 | #endif 52 | 53 | #if defined(__linux) || defined(__APPLE__) 54 | strerror_r(no, buffer, len); 55 | #endif 56 | 57 | return buffer; 58 | } 59 | 60 | void get_error_string(std::string& err, int no) { 61 | char txt[MAX_ERROR_TXT]; 62 | get_error_string(txt, sizeof(txt), no); 63 | err = txt; 64 | } 65 | -------------------------------------------------------------------------------- /src/tools.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** repository: https://github.com/trumanzhao/luna 3 | ** trumanzhao, 2016/10/19, trumanzhao@foxmail.com 4 | */ 5 | 6 | #pragma once 7 | 8 | #ifdef _MSC_VER 9 | #include 10 | #include 11 | #include 12 | using int64_t = long long; 13 | using uint64_t = unsigned long long; 14 | #define getcwd _getcwd 15 | #define strdup _strdup 16 | #define tzset _tzset 17 | #endif 18 | 19 | #if defined(__linux) || defined(__APPLE__) 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | using BYTE = unsigned char; 26 | #endif 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | inline int64_t get_time_ns() { return std::chrono::high_resolution_clock::now().time_since_epoch().count(); } 34 | inline int64_t get_time_ms() { return get_time_ns() / 1000 / 1000; } 35 | inline void sleep_ms(int ms) { std::this_thread::sleep_for(std::chrono::milliseconds(ms)); } 36 | 37 | time_t get_file_time(const char* file_name); 38 | 39 | template 40 | void safe_cpy(char (&buffer)[N], const char* str) { 41 | if (str == nullptr) { 42 | buffer[0] = 0; 43 | return; 44 | } 45 | 46 | strncpy(buffer, str, N); 47 | buffer[N - 1] = 0; 48 | } 49 | 50 | #ifdef _MSC_VER 51 | inline struct tm* localtime_r(const time_t* timep, struct tm* result) { 52 | errno_t nErr = localtime_s(result, timep); 53 | return (nErr == 0) ? result : nullptr; 54 | } 55 | #endif 56 | 57 | #ifdef _MSC_VER 58 | inline bool make_dir(const char cszDir[]) { return (_mkdir(cszDir) != -1); } 59 | #endif 60 | 61 | #if defined(__linux) || defined(__APPLE__) 62 | inline bool make_dir(const char cszDir[]) { return (mkdir(cszDir, 0777) != -1); } 63 | #endif 64 | 65 | 66 | #ifdef _MSC_VER 67 | inline uint64_t get_thread_id() { return GetCurrentThreadId(); } 68 | #endif 69 | 70 | #if defined(__linux) || defined(__APPLE__) 71 | inline uint64_t get_thread_id() { return (uint64_t)pthread_self(); } 72 | #endif 73 | 74 | 75 | #define FAILED_JUMP(C) \ 76 | do { \ 77 | if (!(C)) goto Exit0; \ 78 | } while (0) 79 | 80 | #define SAFE_FREE(p) \ 81 | do { \ 82 | if (p) { \ 83 | free(p); \ 84 | (p) = nullptr; \ 85 | } \ 86 | } while (0) 87 | 88 | #define SAFE_DELETE(p) \ 89 | do { \ 90 | if (p) { \ 91 | delete (p); \ 92 | (p) = nullptr; \ 93 | } \ 94 | } while (0) 95 | 96 | #define SAFE_DELETE_ARRAY(p) \ 97 | do { \ 98 | if (p) { \ 99 | delete[] (p); \ 100 | (p) = nullptr; \ 101 | } \ 102 | } while (0) 103 | 104 | #define SAFE_RELEASE(p) \ 105 | do { \ 106 | if (p) { \ 107 | (p)->release(); \ 108 | (p) = nullptr; \ 109 | } \ 110 | } while (0) 111 | 112 | 113 | #if defined(__linux) || defined(__APPLE__) 114 | template 115 | constexpr int _countof(T(&_array)[N]) { return N; } 116 | #endif 117 | 118 | #define MAX_ERROR_TXT 128 119 | 120 | char* get_error_string(char buffer[], int len, int no); 121 | void get_error_string(std::string& err, int no); 122 | -------------------------------------------------------------------------------- /test/client.lua: -------------------------------------------------------------------------------- 1 | #!./hive 2 | 3 | lbus = require("lbus") 4 | 5 | socket_mgr = lbus.create_socket_mgr(10000); 6 | 7 | sessions = {}; 8 | 9 | session_count = 0; 10 | message_count = 0; 11 | error_count = 0; 12 | errors = {}; 13 | 14 | function init_session(ss) 15 | ss.count = 0; 16 | ss.on_connect = function(res) 17 | if res == "ok" then 18 | session_count = session_count + 1; 19 | ss.send("hello", 123); 20 | return; 21 | end 22 | sessions[ss.token] = nil; 23 | end 24 | 25 | ss.on_recv = function(msg, ...) 26 | ss.send(msg, ...); 27 | ss.count = ss.count + 1; 28 | message_count = message_count + 1; 29 | 30 | if ss.count > 100 then 31 | sessions[ss.token] = nil; 32 | session_count = session_count - 1; 33 | ss.close(); 34 | end 35 | end 36 | 37 | ss.on_error = function(err) 38 | sessions[ss.token] = nil; 39 | session_count = session_count - 1; 40 | error_count = error_count + 1; 41 | if errors[err] == nil then 42 | errors[err] = 1; 43 | print(err); 44 | end 45 | end 46 | 47 | sessions[ss.token] = ss; 48 | end 49 | 50 | print_time = hive.get_time_ms(); 51 | hive.run = function() 52 | socket_mgr.wait(100); 53 | 54 | local now = hive.get_time_ms(); 55 | if now > print_time then 56 | print("session_count="..session_count..", message_count="..message_count..", error_count="..error_count); 57 | print_time = now + 2000; 58 | end 59 | 60 | local ss = socket_mgr.connect("127.0.0.1", 9999, 2000); 61 | if ss then 62 | init_session(ss); 63 | end 64 | end 65 | 66 | -------------------------------------------------------------------------------- /test/hive: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trumanzhao/luabus/a417168a9119ed0f33ecc821b94160b8c64c50c2/test/hive -------------------------------------------------------------------------------- /test/lbus.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trumanzhao/luabus/a417168a9119ed0f33ecc821b94160b8c64c50c2/test/lbus.so -------------------------------------------------------------------------------- /test/server.lua: -------------------------------------------------------------------------------- 1 | #!./hive 2 | 3 | lbus = require("lbus") 4 | 5 | socket_mgr = lbus.create_socket_mgr(10000); 6 | sessions = {}; 7 | 8 | listener = socket_mgr.listen("127.0.0.1", 9999); 9 | 10 | session_count = 0; 11 | message_count = 0; 12 | message_speed = 0; 13 | error_count = 0; 14 | errors = {}; 15 | 16 | listener.on_accept = function(ss) 17 | ss.on_recv = function(msg, ...) 18 | ss.send(msg, ...); 19 | message_count = message_count + 1; 20 | message_speed = message_speed + 1; 21 | end 22 | 23 | ss.on_error = function(err) 24 | sessions[ss.token] = nil; 25 | session_count = session_count - 1; 26 | error_count = error_count + 1; 27 | if errors[err] == nil then 28 | errors[err] = 1; 29 | print(err); 30 | end 31 | end 32 | 33 | sessions[ss.token] = ss; 34 | session_count = session_count + 1; 35 | end 36 | 37 | print_time = hive.get_time_ms(); 38 | 39 | hive.run = function() 40 | socket_mgr.wait(100); 41 | 42 | local now = hive.get_time_ms(); 43 | if now > print_time then 44 | local speed = message_speed // 2; 45 | print("session_count="..session_count..", message_count="..message_count..", message_speed="..speed); 46 | print_time = now + 2000; 47 | message_speed = 0; 48 | end 49 | end 50 | 51 | --------------------------------------------------------------------------------