├── .gitignore ├── .ycm_extra_conf.py ├── LICENSE ├── Makefile ├── README.md ├── docs ├── INSTALL ├── README └── benchmark.md ├── examples ├── helper.cpp ├── helper.h ├── io.cpp ├── io.h ├── redis.cpp ├── redis.h ├── xcurl.cpp └── xcurl.h ├── include ├── event.h ├── network.h └── threads.h ├── libevlite.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── libevlite.xccheckout └── xcshareddata │ └── xcschemes │ ├── echoserver.xcscheme │ ├── pingpong.xcscheme │ └── pingpong_client.xcscheme ├── src ├── channel.c ├── channel.h ├── config.h ├── driver.c ├── driver.h ├── ephashtable.c ├── ephashtable.h ├── epoll.c ├── event-internal.h ├── event.c ├── ikcp.c ├── ikcp.h ├── kqueue.c ├── lock.h ├── message.c ├── message.h ├── network-internal.h ├── network.c ├── queue.h ├── session.c ├── session.h ├── threads-internal.h ├── threads.c ├── timer.c ├── utils.c └── utils.h └── test ├── accept-lock-echoserver.c ├── chatroom.h ├── chatroom_client.cpp ├── chatroom_server.cpp ├── echoclient.cpp ├── echoserver.cpp ├── echostress.c ├── pingpong.c ├── pingpongclient.c ├── raw_echoserver.c ├── slice.h ├── test_addtimer.c ├── test_events.c ├── test_iothreads.c ├── test_multicurl.cpp ├── test_queue.c └── test_sidlist.c /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.opannotate 3 | *.opreport 4 | tags 5 | *.o 6 | *.swp 7 | !xcshareddata 8 | xcuserdata 9 | *.pbxuser 10 | *~.nib 11 | .vscode/* 12 | -------------------------------------------------------------------------------- /.ycm_extra_conf.py: -------------------------------------------------------------------------------- 1 | # This file is NOT licensed under the GPLv3, which is the license for the rest 2 | # of YouCompleteMe. 3 | # 4 | # Here's the license text for this file: 5 | # 6 | # This is free and unencumbered software released into the public domain. 7 | # 8 | # Anyone is free to copy, modify, publish, use, compile, sell, or 9 | # distribute this software, either in source code form or as a compiled 10 | # binary, for any purpose, commercial or non-commercial, and by any 11 | # means. 12 | # 13 | # In jurisdictions that recognize copyright laws, the author or authors 14 | # of this software dedicate any and all copyright interest in the 15 | # software to the public domain. We make this dedication for the benefit 16 | # of the public at large and to the detriment of our heirs and 17 | # successors. We intend this dedication to be an overt act of 18 | # relinquishment in perpetuity of all present and future rights to this 19 | # software under copyright law. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24 | # IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 25 | # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 26 | # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | # OTHER DEALINGS IN THE SOFTWARE. 28 | # 29 | # For more information, please refer to 30 | 31 | from distutils.sysconfig import get_python_inc 32 | import platform 33 | import os.path as p 34 | import subprocess 35 | 36 | DIR_OF_THIS_SCRIPT = p.abspath( p.dirname( __file__ ) ) 37 | DIR_OF_THIRD_PARTY = p.join( DIR_OF_THIS_SCRIPT, 'third_party' ) 38 | DIR_OF_WATCHDOG_DEPS = p.join( DIR_OF_THIRD_PARTY, 'watchdog_deps' ) 39 | SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ] 40 | 41 | database = None 42 | 43 | # These are the compilation flags that will be used in case there's no 44 | # compilation database set (by default, one is not set). 45 | # CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR. 46 | flags = [ 47 | '-Wall', 48 | '-Wextra', 49 | #'-Werror', 50 | '-Wno-long-long', 51 | '-Wno-variadic-macros', 52 | '-fexceptions', 53 | '-DNDEBUG', 54 | # You 100% do NOT need -DUSE_CLANG_COMPLETER and/or -DYCM_EXPORT in your flags; 55 | # only the YCM source code needs it. 56 | '-DUSE_CLANG_COMPLETER', 57 | '-DYCM_EXPORT=', 58 | '-DYCM_ABSEIL_SUPPORTED', 59 | # THIS IS IMPORTANT! Without the '-x' flag, Clang won't know which language to 60 | # use when compiling headers. So it will guess. Badly. So C++ headers will be 61 | # compiled as C headers. You don't want that so ALWAYS specify the '-x' flag. 62 | # For a C project, you would set this to 'c' instead of 'c++'. 63 | '-x', 64 | 'c++', 65 | '-std=c++17', 66 | '-I', 'src', 67 | '-I', 'include', 68 | '-I', 'src/test', 69 | '-I', 'src/examples', 70 | '-isystem', '/usr/local/include', 71 | '-isystem', '/usr/include/c++/9', 72 | '-isystem', '/usr/include', 73 | '-isystem', '/usr/include/x86_64-linux-gnu/c++/9', 74 | '-isystem', '/usr/include/x86_64-linux-gnu', 75 | '-isystem', 'cpp/absl', 76 | '-isystem', 'cpp/pybind11', 77 | '-isystem', 'cpp/whereami', 78 | '-isystem', 'cpp/BoostParts', 79 | '-isystem', get_python_inc(), 80 | '-isystem', 'cpp/llvm/include', 81 | '-isystem', 'cpp/llvm/tools/clang/include', 82 | '-I', 'cpp/ycm', 83 | '-I', 'cpp/ycm/ClangCompleter', 84 | '-isystem', 'cpp/ycm/tests/gmock/googlemock/include', 85 | '-isystem', 'cpp/ycm/tests/gmock/googletest/include', 86 | '-isystem', 'cpp/ycm/benchmarks/benchmark/include', 87 | ] 88 | 89 | # Set this to the absolute path to the folder (NOT the file!) containing the 90 | # compile_commands.json file to use that instead of 'flags'. See here for 91 | # more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html 92 | # 93 | # You can get CMake to generate this file for you by adding: 94 | # set( CMAKE_EXPORT_COMPILE_COMMANDS 1 ) 95 | # to your CMakeLists.txt file. 96 | # 97 | # Most projects will NOT need to set this to anything; you can just change the 98 | # 'flags' list of compilation flags. Notice that YCM itself uses that approach. 99 | compilation_database_folder = '' 100 | 101 | 102 | def IsHeaderFile( filename ): 103 | extension = p.splitext( filename )[ 1 ] 104 | return extension in [ '.h', '.hxx', '.hpp', '.hh' ] 105 | 106 | 107 | def FindCorrespondingSourceFile( filename ): 108 | if IsHeaderFile( filename ): 109 | basename = p.splitext( filename )[ 0 ] 110 | for extension in SOURCE_EXTENSIONS: 111 | replacement_file = basename + extension 112 | if p.exists( replacement_file ): 113 | return replacement_file 114 | return filename 115 | 116 | 117 | def PathToPythonUsedDuringBuild(): 118 | try: 119 | filepath = p.join( DIR_OF_THIS_SCRIPT, 'PYTHON_USED_DURING_BUILDING' ) 120 | with open( filepath ) as f: 121 | return f.read().strip() 122 | except OSError: 123 | return None 124 | 125 | 126 | def Settings( **kwargs ): 127 | # Do NOT import ycm_core at module scope. 128 | import ycm_core 129 | 130 | global database 131 | if database is None and p.exists( compilation_database_folder ): 132 | database = ycm_core.CompilationDatabase( compilation_database_folder ) 133 | 134 | language = kwargs[ 'language' ] 135 | 136 | if language == 'cfamily': 137 | # If the file is a header, try to find the corresponding source file and 138 | # retrieve its flags from the compilation database if using one. This is 139 | # necessary since compilation databases don't have entries for header files. 140 | # In addition, use this source file as the translation unit. This makes it 141 | # possible to jump from a declaration in the header file to its definition 142 | # in the corresponding source file. 143 | filename = FindCorrespondingSourceFile( kwargs[ 'filename' ] ) 144 | 145 | if not database: 146 | return { 147 | 'flags': flags, 148 | 'include_paths_relative_to_dir': DIR_OF_THIS_SCRIPT, 149 | 'override_filename': filename 150 | } 151 | 152 | compilation_info = database.GetCompilationInfoForFile( filename ) 153 | if not compilation_info.compiler_flags_: 154 | return {} 155 | 156 | # Bear in mind that compilation_info.compiler_flags_ does NOT return a 157 | # python list, but a "list-like" StringVec object. 158 | final_flags = list( compilation_info.compiler_flags_ ) 159 | 160 | # NOTE: This is just for YouCompleteMe; it's highly likely that your project 161 | # does NOT need to remove the stdlib flag. DO NOT USE THIS IN YOUR 162 | # ycm_extra_conf IF YOU'RE NOT 100% SURE YOU NEED IT. 163 | try: 164 | final_flags.remove( '-stdlib=libc++' ) 165 | except ValueError: 166 | pass 167 | 168 | return { 169 | 'flags': final_flags, 170 | 'include_paths_relative_to_dir': compilation_info.compiler_working_dir_, 171 | 'override_filename': filename 172 | } 173 | 174 | if language == 'python': 175 | return { 176 | 'interpreter_path': PathToPythonUsedDuringBuild() 177 | } 178 | 179 | return {} 180 | 181 | 182 | def PythonSysPath( **kwargs ): 183 | sys_path = kwargs[ 'sys_path' ] 184 | 185 | interpreter_path = kwargs[ 'interpreter_path' ] 186 | major_version = subprocess.check_output( [ 187 | interpreter_path, '-c', 'import sys; print( sys.version_info[ 0 ] )' ] 188 | ).rstrip().decode( 'utf8' ) 189 | 190 | sys_path[ 0:0 ] = [ p.join( DIR_OF_THIS_SCRIPT ), 191 | p.join( DIR_OF_THIRD_PARTY, 'bottle' ), 192 | p.join( DIR_OF_THIRD_PARTY, 'regex-build' ), 193 | p.join( DIR_OF_THIRD_PARTY, 'frozendict' ), 194 | p.join( DIR_OF_THIRD_PARTY, 'jedi_deps', 'jedi' ), 195 | p.join( DIR_OF_THIRD_PARTY, 'jedi_deps', 'parso' ), 196 | p.join( DIR_OF_WATCHDOG_DEPS, 'watchdog', 'build', 'lib3' ), 197 | p.join( DIR_OF_WATCHDOG_DEPS, 'pathtools' ), 198 | p.join( DIR_OF_THIRD_PARTY, 'waitress' ) ] 199 | 200 | sys_path.append( p.join( DIR_OF_THIRD_PARTY, 'jedi_deps', 'numpydoc' ) ) 201 | return sys_path 202 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, spriteray@gmail.com 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | # ------------------------------------------------------------------------------ 3 | OS = $(shell uname) 4 | 5 | APP = libevlite 6 | VERSION = 9.9.9 7 | PREFIX = /usr/local 8 | 9 | # 主版本号 10 | MAJORVER = $(firstword $(subst ., ,$(VERSION))) 11 | 12 | # ------------------------------------------------------------------------------ 13 | # FreeBSD采用clang做为编译器 14 | ifeq ($(OS),FreeBSD) 15 | CC = clang 16 | CXX = clang++ 17 | else 18 | CC = gcc 19 | CXX = g++ 20 | endif 21 | 22 | # 23 | # 编译选项 24 | # 25 | # USE_ATOMIC - 使用原子操作 26 | # USE_REUSESESSION - 重用会话(提高效率) 27 | # 28 | 29 | # 默认选项 30 | LFLAGS = -ggdb -lpthread 31 | CFLAGS = -Wall -Wformat=0 -Iinclude/ -Isrc/ -Itest/ -ggdb -fPIC -O2 -DNDEBUG -D__EVENT_VERSION__=\"$(REALNAME)\" -DUSE_ATOMIC #-DUSE_REUSESESSION 32 | CXXFLAGS = -Wall -Wformat=0 -Iinclude/ -Isrc/ -Itest/ -ggdb -fPIC -O2 -DNDEBUG -D__EVENT_VERSION__=\"$(REALNAME)\" -DUSE_ATOMIC #-DUSE_REUSESESSION 33 | 34 | # 动态库编译选项 35 | ifeq ($(OS),Darwin) 36 | LIBNAME = $(APP).dylib 37 | SONAME = $(APP).$(MAJORVER).dylib 38 | REALNAME= $(APP).$(VERSION).dylib 39 | SOFLAGS = -dynamiclib -Wl,-install_name,$(SONAME) -compatibility_version $(MAJORVER) -current_version $(VERSION) 40 | else 41 | LIBNAME = $(APP).so 42 | SONAME = $(LIBNAME).$(MAJORVER) 43 | REALNAME= $(LIBNAME).$(VERSION) 44 | SOFLAGS = -shared -Wl,-soname,$(SONAME) 45 | endif 46 | 47 | # Linux定制参数 48 | ifeq ($(OS),Linux) 49 | LFLAGS = -ggdb -pthread -lrt 50 | CFLAGS += -finline-limit=1000 51 | CXXFLAGS+= -finline-limit=1000 52 | endif 53 | 54 | # ------------------------------------------------------------------------------ 55 | # 安装目录lib, include 56 | LIBPATH = $(PREFIX)/lib 57 | INCLUDEPATH = $(PREFIX)/include 58 | 59 | # 60 | # 利用git tag发布软件版本 61 | # 62 | #APPNAME=`git describe | awk -F- '{print $$1}'` 63 | #VERSION=`git describe | awk -F- '{print $$2}'` 64 | #MAJORVER=`git describe | awk -F- '{print $$2}' | awk -F. '{print $$1}'` 65 | # 66 | #LIBNAME=$(APPNAME).so 67 | #SONAME=$(APPNAME).so.$(MAJORVER) 68 | #REALNAME=$(APPNAME).so.$(VERSION) 69 | # 70 | 71 | # ------------------------------------------------------------------------------ 72 | OBJS = ikcp.o driver.o utils.o ephashtable.o \ 73 | epoll.o kqueue.o timer.o \ 74 | event.o \ 75 | threads.o \ 76 | message.o channel.o session.o \ 77 | network.o 78 | 79 | # ------------------------------------------------------------------------------ 80 | all : $(REALNAME) 81 | 82 | install : all 83 | rm -rf $(INCLUDEPATH)/evlite 84 | cp -a include $(INCLUDEPATH)/evlite 85 | rm -rf $(LIBPATH)/$(REALNAME); cp $(REALNAME) $(LIBPATH) 86 | rm -rf $(LIBPATH)/$(SONAME); ln -s $(REALNAME) $(LIBPATH)/$(SONAME) 87 | rm -rf $(LIBPATH)/$(LIBNAME); ln -s $(REALNAME) $(LIBPATH)/$(LIBNAME) 88 | 89 | $(REALNAME) : $(OBJS) 90 | $(CC) $(SOFLAGS) $(LFLAGS) $^ -o $@ 91 | rm -rf $(SONAME); ln -s $@ $(SONAME) 92 | rm -rf $(LIBNAME); ln -s $@ $(LIBNAME) 93 | 94 | test : test_multicurl pingpong_client test_events test_addtimer test_queue test_sidlist echoserver-lock echoserver iothreads_dispatcher 95 | 96 | test_events : test_events.o $(OBJS) 97 | $(CC) $^ -o $@ $(LFLAGS) 98 | 99 | test_addtimer : test_addtimer.o $(OBJS) 100 | $(CC) $^ -o $@ $(LFLAGS) 101 | 102 | test_queue : test_queue.o 103 | $(CC) $^ -o $@ $(LFLAGS) 104 | 105 | test_sidlist : test_sidlist.o utils.o 106 | $(CC) $^ -o $@ $(LFLAGS) 107 | 108 | echoserver-lock : accept-lock-echoserver.o $(OBJS) 109 | $(CC) $^ -o $@ $(LFLAGS) 110 | 111 | echoclient : io.o echoclient.o $(OBJS) 112 | $(CXX) $^ -o $@ $(LFLAGS) 113 | 114 | echoserver : io.o echoserver.o $(OBJS) 115 | $(CXX) $^ -o $@ $(LFLAGS) 116 | 117 | raw_echoserver : raw_echoserver.o $(OBJS) 118 | $(CC) $^ -o $@ $(LFLAGS) 119 | 120 | pingpong : pingpong.o $(OBJS) 121 | $(CC) $^ -o $@ $(LFLAGS) 122 | 123 | echostress : 124 | $(CC) test/echostress.c -o $@ -I/usr/local/include -L/usr/local/lib -levent 125 | 126 | iothreads_dispatcher : test_iothreads.o $(OBJS) 127 | $(CC) $(LFLAGS) $^ -o $@ 128 | 129 | chatroom : chatroom_server chatroom_client 130 | 131 | chatroom_server: io.o chatroom_server.o $(OBJS) 132 | $(CXX) $^ -o $@ $(LFLAGS) 133 | 134 | chatroom_client: io.o chatroom_client.o $(OBJS) 135 | $(CXX) $^ -o $@ $(LFLAGS) 136 | 137 | redis_client : io.o redis.o helper.o $(OBJS) 138 | $(CXX) $^ -o $@ $(LFLAGS) -lhiredis 139 | 140 | pingpong_client : pingpongclient.o $(OBJS) 141 | $(CXX) $^ -o $@ $(LFLAGS) 142 | 143 | test_multicurl : xcurl.o test_multicurl.o $(OBJS) 144 | $(CXX) $^ -o $@ $(LFLAGS) -lcurl 145 | 146 | clean : 147 | rm -rf *.o 148 | rm -rf *.log 149 | rm -rf core 150 | rm -rf *.core 151 | rm -rf core.* 152 | rm -rf vgcore.* 153 | rm -rf $(SONAME) 154 | rm -rf $(LIBNAME) 155 | rm -rf $(REALNAME) 156 | rm -rf test_events event.fifo 157 | rm -rf test_queue test_sidlist 158 | rm -rf chatroom_client chatroom_server 159 | rm -rf test_multicurl test_addtimer echoclient echostress raw_echoserver echoserver pingpong echoserver-lock iothreads_dispatcher redis_client pingpong_client 160 | 161 | # -------------------------------------------------------- 162 | # 163 | # gmake的规则 164 | # 165 | %.o : %.c 166 | $(CC) $(CFLAGS) -Wno-unused-function -c $^ -o $@ 167 | 168 | %.o : %.cpp 169 | $(CXX) $(CXXFLAGS) -std=c++11 -Wno-unused-function -Itest/ -Iexamples/ -c $^ -o $@ 170 | 171 | VPATH = src:include:test:examples 172 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libevlite网络通信库(Linux, Darwin, \*BSD) 2 | 3 | ## 1. 基础事件模块( `include/event.h` ) 4 | ==支持的IO复用机制: epoll和kqueue== 5 | ### 1.1 事件类型说明 6 | - 读事件(`EV_READ`) 7 | - 写事件(`EV_WRITE`) 8 | - 超时事件(`EV_TIMEOUT`) 9 | - 在三种事件类型的基础上, 支持事件驻留在事件集中的永久模式(`EV_PERSIST`) 10 | 11 | ### 1.2 基于事件(`event_t`)的方法说明 12 | - 设置事件属性 `event_set()` 13 | - 设置事件回调函数 `event_set_callback()` 14 | 15 | ### 1.3 基于事件集(`evsets_t`)的方法说明 16 | - 向事件集中添加事件 `evsets_add()` 17 | - 从事件集中删除事件 `evsets_del()` 18 | - 分发并处理事件 `evsets_dispatch()` 19 | 20 | ## 2. 网络线程模块( `include/threads.h` ) 21 | 22 | ## 3. 通信模块( `include/networks.h` ) 23 | 24 | ### 3.1 创建网络通信层 `iolayer_create()` 25 | - nthreads: 指定网络线程的个数 26 | - nclients: 推荐连接数 27 | - precision: 事件集的时间精度(建议值8ms) 28 | - immediately: 数据是否会立刻推送到网络层,对实时性要求很高的场景, 建议设置为1 29 | 30 | ### 3.2 设置网络通信层的方法(仅在IO线程中才能使用) 31 | - 设置线程上下文: `iolayer_set_iocontext()` 32 | - 设置网络层数据改造方法: `iolayer_set_transform()` 33 | 34 | ### 3.3 监听端口/开启服务端 `iolayer_listen()` 35 | - type: 网络类型, 支持`TCP`, `UDP`和`KCP` 36 | - host: 绑定的地址 37 | - port: 监听的端口号 38 | - options: 服务器全局参数(当前主要是`KCP`的参数配置) 39 | - callback: 新会话创建成功的回调 40 | - context: 上下文参数 41 | 42 | ### 3.4 连接远程服务/开启客户端 `iolayer_connect()` 43 | 44 | ### 3.5 关联描述符的读写事件 `iolayer_associate()` 45 | 46 | ### 3.6 设置会话的方法(仅在IO线程中才能使用) 47 | - 设置会话的超时时间 `iolayer_set_timeout()` 48 | - 设置会话的保活时间 `iolayer_set_keepalive()` 49 | - 设置会话的IO服务逻辑 `iolayer_set_service()` 50 | - 设置会话的读事件常驻事件集 `iolayer_set_persist()` 51 | - 设置会话的发送队列长度限制 `iolayer_set_sndqlimit()` 52 | - 设置会话的最大传输单元(仅限`KCP`有效) `iolayer_set_mtu()` 53 | - 设置会话的最小重传时间(仅限`KCP`有效) `iolayer_set_minrto()` 54 | - 设置会话的发送接收窗口(仅限`KCP`有效) `iolayer_set_wndsize()` 55 | 56 | ### 3.7 发送数据 `iolayer_send()` 57 | 58 | ### 3.8 广播数据 `iolayer_broadcast()`, `iolayer_broadcast2()` 59 | 60 | ### 3.9 关闭会话 `iolayer_shutdown()`, `iolayer_shutdowns()` 61 | 62 | ### 3.10 提交任务到网络层 `iolayer_invoke()`, `iolayer_perform()` 63 | 64 | ### 3.11 停止服务 `iolayer_stop()` 65 | - 停止对外提供接入服务, 不再接受新的连接; 66 | - 停止所有连接的接收服务, 不再回调`ioservice_t::process()` 67 | 68 | ### 3.12 销毁网络层 `iolayer_destroy()` 69 | -------------------------------------------------------------------------------- /docs/INSTALL: -------------------------------------------------------------------------------- 1 | 2 | linux : 3 | make os=linux install 4 | 5 | freebsd : 6 | gmake os=bsd install(如果你安装了gmake) 7 | 或者直接make install -f Makefile.bsd 8 | -------------------------------------------------------------------------------- /docs/README: -------------------------------------------------------------------------------- 1 | 2 | event事件操作库 3 | 4 | include\ - 对外接口目录,提供给使用者的include文件 5 | src\ - 具体实现部分 6 | docs\ - 文档 7 | test\ - 测试目录 8 | Makefile.* - Makefile文件 9 | 10 | 二、基于FreeBSD的事件库([ FreeBSD7.4, Xeon E5410 * 1, 8G ]) 11 | 12 | 1. 测试结果:回显服务的压力测试,在没有超时参与的基础上, 性能比libevent高出10%左右。 13 | 14 | 2. 出现的问题: 15 | 1) 在1000个连接的情况下,总有10个左右的连接的close事件丢失了 16 | 所需要监控的描述符已经注册到kqueue中,基本定位到kevent()中;event事件库和libevent都存在。初步怀疑是测试环境有问题。 17 | 18 | 19 | 三、基于linux的事件库([ Centos 6.0(linux-2.6.32), Pentium(R) Dual-Core E6700 3.20GHz, 512M ]) 20 | 21 | 1. 测试结果: 22 | 1) 效率比内核2.6.18内核带来了20%的提升。 23 | 2) 回显服务的压力测试,在没有超时参与的基础上,性能比libevent高出17%左右. 24 | 3) 在超时时间设定为5s的情况下,性能比libevent高出160%左右。 25 | Connections 吞吐量[event] 吞吐量[libevent] 26 | 1000 113MBytes/s 41MBytes/s 27 | 1000 110MBytes/s 43MBytes/s 28 | 10000 119MBytes/s 20MBytes/s 29 | 10000 110MBytes/s 37MBytes/s 30 | 31 | 2. 出现的问题: 32 | 1) 1000个连接时候的上下文切换达到50K, 但20000个连接的切换才300多;[已解决, vmstat统计问题] 33 | Connections CS 吞吐量 34 | 1000 55000 110MBytes/s 35 | 2000 40000 109MBytes/s 36 | 3000 47000 108MBytes/s 37 | 4000 49000 104MBytes/s 38 | 5000 50000 109MBytes/s 39 | 6000 9000,[330~38000] 130MBytes/s 40 | 7000 6000,[300~19000] 131MBytes/s 41 | 8000 1000,[300~18000] 127MBytes/s 42 | 10000 350 130MBytes/s 43 | 44 | 2) 同样1000个连接的时候, IO线程等待时间为0时(IO线程真正分摊压力), 上下文切换大量降低,但吞吐量锐减; 45 | 46 | 四、Accept-Lock模型的回显服务器 47 | 48 | 1. 模型介绍: 49 | Accept-Lock模型,能在在多个IO线程中,高效的公平的分摊IO的压力,绝对比fd硬hash来的高效。 50 | 51 | 2. 测试结果: 52 | 1) accept事件总是在一个线程中发生, 并没有原先设计的效果。从压力测试的脚本来看,这样的结果也是可以理解的。 53 | 毕竟所有连接都成功后,才开始发送数据的,所以一开始的线程基本没有什么压力。 54 | 55 | 五、同样的开发环境中,centos6.0比freebsd8.2快了近30%。 56 | 57 | 六、吞吐量横向比较(muduo, Ubuntu 14.04.5, Intel(R) Xeon(R) CPU E5606 @ 2.13GHz, 8核8G) 58 | moduo 1000个连接,4个IO线程,4K的数据包,197.515234375 MiB/s throughput 59 | libevlite 1000个连接,4个IO线程,4K的数据包,245.58203125 MiB/s throughput 60 | 61 | -------------------------------------------------------------------------------- /docs/benchmark.md: -------------------------------------------------------------------------------- 1 | # libevlite性能测试 2 | 3 | 4 | 5 | ### 一、吞吐量测试结果 6 | 7 | ##### 1. 硬件配置 8 | 9 | 2CPU4Core 10 | 11 | CPU型号 Intel(R) Xeon(R) CPU E5606 @ 2.13GHz 12 | 13 | 8G内存 14 | 15 | Ubuntu 14.04.5 16 | 17 | ##### 2. 4K数据包,4个IO线程 18 | 19 | | 连接数 | 1000 | 2000 | 10000 | 20 | | --------- | ------- | ------- | ------- | 21 | | muduo | 197.515 | 232.825 | 244.675 | 22 | | libevlite | 245.582 | 238.293 | 244.478 | 23 | 24 | ##### 3. 16K数据包,4个IO线程 25 | 26 | | 连接数 | 1000 | 2000 | 10000 | 27 | | --------- | ------- | ------- | ------- | 28 | | muduo | 596.692 | 598.942 | 588.075 | 29 | | libevlite | 630.339 | 598.021 | 571.337 | -------------------------------------------------------------------------------- /examples/helper.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "helper.h" 5 | 6 | Slice RedisCommand::ping() 7 | { 8 | char * buffer = NULL; 9 | ssize_t length = redisFormatCommand( &buffer, "PING" ); 10 | 11 | if ( length < 0 ) 12 | { 13 | return Slice(); 14 | } 15 | 16 | return Slice( buffer, length ); 17 | } 18 | 19 | Slice RedisCommand::echo( const std::string & text ) 20 | { 21 | char * buffer = NULL; 22 | ssize_t length = redisFormatCommand( 23 | &buffer, "ECHO \"%s\"", text.c_str() ); 24 | 25 | if ( length < 0 ) 26 | { 27 | return Slice(); 28 | } 29 | 30 | return Slice( buffer, length ); 31 | } 32 | 33 | Slice RedisCommand::auth( const std::string & password ) 34 | { 35 | char * buffer = NULL; 36 | ssize_t length = redisFormatCommand( 37 | &buffer, "AUTH %s", password.c_str() ); 38 | 39 | if ( length < 0 ) 40 | { 41 | return Slice(); 42 | } 43 | 44 | return Slice( buffer, length ); 45 | } 46 | 47 | Slice RedisCommand::get( const std::string & key ) 48 | { 49 | char * buffer = NULL; 50 | ssize_t length = redisFormatCommand( 51 | &buffer, "GET %s", key.c_str() ); 52 | 53 | if ( length < 0 ) 54 | { 55 | return Slice(); 56 | } 57 | 58 | return Slice( buffer, length ); 59 | } 60 | 61 | Slice RedisCommand::mget( const std::vector & keys ) 62 | { 63 | size_t ncmds = keys.size() + 1; 64 | size_t argvlen[ ncmds ]; 65 | const char * argv[ ncmds ]; 66 | 67 | argv[0] = "MGET"; 68 | argvlen[0] = 4; 69 | 70 | for ( size_t i = 0; i < keys.size(); ++i ) 71 | { 72 | argv[i+1] = keys[i].c_str(); 73 | argvlen[i+1] = keys[i].size(); 74 | } 75 | 76 | char * buffer = NULL; 77 | ssize_t length = redisFormatCommandArgv( 78 | &buffer, 79 | keys.size()+1, argv, argvlen ); 80 | 81 | if ( length < 0 ) 82 | { 83 | return Slice(); 84 | } 85 | 86 | return Slice( buffer, length ); 87 | } 88 | 89 | Slice RedisCommand::set( const std::string & key, const std::string & value ) 90 | { 91 | char * buffer = NULL; 92 | ssize_t length = redisFormatCommand( 93 | &buffer, "SET %s %b", 94 | key.c_str(), value.data(), value.size() ); 95 | 96 | if ( length < 0 ) 97 | { 98 | return Slice(); 99 | } 100 | 101 | return Slice( buffer, length ); 102 | } 103 | 104 | Slice RedisCommand::incr( const std::string & key ) 105 | { 106 | char * buffer = NULL; 107 | ssize_t length = redisFormatCommand( 108 | &buffer, "INCR %s", key.c_str() ); 109 | 110 | if ( length < 0 ) 111 | { 112 | return Slice(); 113 | } 114 | 115 | return Slice( buffer, length ); 116 | } 117 | 118 | Slice RedisCommand::incrby( const std::string & key, int32_t value ) 119 | { 120 | char * buffer = NULL; 121 | ssize_t length = redisFormatCommand( 122 | &buffer, "INCRBY %s %d", key.c_str(), value ); 123 | 124 | if ( length < 0 ) 125 | { 126 | return Slice(); 127 | } 128 | 129 | return Slice( buffer, length ); 130 | } 131 | 132 | Slice RedisCommand::decr( const std::string & key ) 133 | { 134 | char * buffer = NULL; 135 | ssize_t length = redisFormatCommand( 136 | &buffer, "DECR %s", key.c_str() ); 137 | 138 | if ( length < 0 ) 139 | { 140 | return Slice(); 141 | } 142 | 143 | return Slice( buffer, length ); 144 | } 145 | 146 | Slice RedisCommand::decrby( const std::string & key, int32_t value ) 147 | { 148 | char * buffer = NULL; 149 | ssize_t length = redisFormatCommand( 150 | &buffer, "DECRBY %s %d", key.c_str(), value ); 151 | 152 | if ( length < 0 ) 153 | { 154 | return Slice(); 155 | } 156 | 157 | return Slice( buffer, length ); 158 | } 159 | 160 | Slice RedisCommand::lpush( const std::string & key, const std::vector & values ) 161 | { 162 | size_t ncmds = values.size() + 2; 163 | 164 | size_t argvlen[ ncmds ]; 165 | const char * argv[ ncmds ]; 166 | 167 | argv[0] = "LPUSH"; 168 | argvlen[0] = 5; 169 | argv[1] = key.c_str(); 170 | argvlen[1] = key.size(); 171 | 172 | for ( size_t i = 0; i < values.size(); ++i ) 173 | { 174 | argv[i+2] = values[i].c_str(); 175 | argvlen[i+2] = values[i].size(); 176 | } 177 | 178 | char * buffer = NULL; 179 | ssize_t length = redisFormatCommandArgv( 180 | &buffer, 181 | ncmds, argv, argvlen ); 182 | 183 | if ( length < 0 ) 184 | { 185 | return Slice(); 186 | } 187 | 188 | return Slice( buffer, length ); 189 | } 190 | 191 | Slice RedisCommand::rpush( const std::string & key, const std::vector & values ) 192 | { 193 | size_t ncmds = values.size() + 2; 194 | 195 | size_t argvlen[ ncmds ]; 196 | const char * argv[ ncmds ]; 197 | 198 | argv[0] = "RPUSH"; 199 | argvlen[0] = 5; 200 | argv[1] = key.c_str(); 201 | argvlen[1] = key.size(); 202 | 203 | for ( size_t i = 0; i < values.size(); ++i ) 204 | { 205 | argv[i+2] = values[i].c_str(); 206 | argvlen[i+2] = values[i].size(); 207 | } 208 | 209 | char * buffer = NULL; 210 | ssize_t length = redisFormatCommandArgv( 211 | &buffer, 212 | ncmds, argv, argvlen ); 213 | 214 | if ( length < 0 ) 215 | { 216 | return Slice(); 217 | } 218 | 219 | return Slice( buffer, length ); 220 | } 221 | 222 | Slice RedisCommand::lrange( const std::string & key, int32_t startidx, int32_t stopidx ) 223 | { 224 | char * buffer = NULL; 225 | ssize_t length = redisFormatCommand( 226 | &buffer, "LRANGE %s %d %d", key.c_str(), startidx, stopidx ); 227 | 228 | if ( length < 0 ) 229 | { 230 | return Slice(); 231 | } 232 | 233 | return Slice( buffer, length ); 234 | } 235 | 236 | Slice RedisCommand::subscribe( const std::string & channel ) 237 | { 238 | char * buffer = NULL; 239 | ssize_t length = redisFormatCommand( 240 | &buffer, "SUBSCRIBE %s", channel.c_str() ); 241 | 242 | if ( length < 0 ) 243 | { 244 | return Slice(); 245 | } 246 | 247 | return Slice( buffer, length ); 248 | } 249 | 250 | Slice RedisCommand::unsubscribe( const std::string & channel ) 251 | { 252 | char * buffer = NULL; 253 | ssize_t length = redisFormatCommand( 254 | &buffer, "UNSUBSCRIBE %s", channel.c_str() ); 255 | 256 | if ( length < 0 ) 257 | { 258 | return Slice(); 259 | } 260 | 261 | return Slice( buffer, length ); 262 | } 263 | 264 | Slice RedisCommand::publish( const std::string & channel, const std::string & message ) 265 | { 266 | char * buffer = NULL; 267 | ssize_t length = redisFormatCommand( 268 | &buffer, "PUBLISH %s \"%s\"", channel.c_str(), message.c_str() ); 269 | 270 | if ( length < 0 ) 271 | { 272 | return Slice(); 273 | } 274 | 275 | return Slice( buffer, length ); 276 | } 277 | 278 | template<> long long int ResultHelper::parse( redisReply * reply ) 279 | { 280 | assert( reply->type == REDIS_REPLY_INTEGER ); 281 | return reply->integer; 282 | } 283 | 284 | template<> std::string ResultHelper::parse( redisReply * reply ) 285 | { 286 | assert( reply->type == REDIS_REPLY_STRING ); 287 | return std::string( reply->str, reply->len ); 288 | } 289 | 290 | template<> RedisStrList ResultHelper::parse( redisReply * reply ) 291 | { 292 | RedisStrList values; 293 | 294 | assert( reply->type == REDIS_REPLY_ARRAY ); 295 | for ( size_t i = 0; i < reply->elements; ++i ) 296 | { 297 | if ( reply->element[i]->type != REDIS_REPLY_STRING ) 298 | { 299 | values.push_back( std::string() ); 300 | } 301 | else 302 | { 303 | values.push_back( std::string(reply->element[i]->str, reply->element[i]->len) ); 304 | } 305 | } 306 | 307 | return values; 308 | } 309 | -------------------------------------------------------------------------------- /examples/helper.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __SRC_REDIS_COMMAND_H__ 3 | #define __SRC_REDIS_COMMAND_H__ 4 | 5 | #include 6 | #include 7 | 8 | #include "slice.h" 9 | 10 | class RedisCommand 11 | { 12 | public : 13 | // PING 14 | static Slice ping(); 15 | // ECHO 16 | static Slice echo( const std::string & text ); 17 | // 验证命令 18 | static Slice auth( const std::string & password ); 19 | // 获取数据 20 | static Slice get( const std::string & key ); 21 | static Slice mget( const std::vector & keys ); 22 | // 更新命令 23 | static Slice set( 24 | const std::string & key, const std::string & value ); 25 | // 自增 26 | static Slice incr( const std::string & key ); 27 | static Slice incrby( const std::string & key, int32_t value ); 28 | // 自减 29 | static Slice decr( const std::string & key ); 30 | static Slice decrby( const std::string & key, int32_t value ); 31 | // list 32 | static Slice lpush( const std::string & key, const std::vector & values ); 33 | static Slice rpush( const std::string & key, const std::vector & values ); 34 | static Slice lrange( const std::string & key, int32_t startidx, int32_t stopidx ); 35 | // 订阅命令 36 | static Slice subscribe( const std::string & channel ); 37 | // 取消订阅命令 38 | static Slice unsubscribe( const std::string & channel ); 39 | // 发布命令 40 | static Slice publish( const std::string & channel, const std::string & message ); 41 | }; 42 | 43 | typedef std::vector RedisStrList; 44 | 45 | struct ResultHelper 46 | { 47 | template static T parse( redisReply * reply ) { return T(); } 48 | }; 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /examples/io.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __SRC_IO_IO_H__ 3 | #define __SRC_IO_IO_H__ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "event.h" 10 | #include "network.h" 11 | 12 | typedef std::vector sids_t; 13 | 14 | enum class NetType 15 | { 16 | TCP = NETWORK_TCP, 17 | UDP = NETWORK_UDP, 18 | KCP = NETWORK_KCP, 19 | }; 20 | 21 | // 22 | // 会话, 非线程安全的 23 | // 24 | 25 | class IIOService; 26 | 27 | class IIOSession 28 | { 29 | public : 30 | IIOSession(); 31 | virtual ~IIOSession(); 32 | 33 | public : 34 | // 35 | // 网络事件 36 | // 多个网络线程中被触发 37 | // 38 | 39 | virtual int32_t onStart() { return 0; } 40 | virtual ssize_t onProcess( const char * buffer, size_t nbytes ) { return 0; } 41 | virtual char * onTransform( const char * buffer, size_t & nbytes ) { return const_cast(buffer); } 42 | virtual int32_t onTimeout() { return 0; } 43 | virtual int32_t onKeepalive() { return 0; } 44 | virtual int32_t onError( int32_t result ) { return 0; } 45 | virtual int32_t onPerform( int32_t type, void * task, int32_t interval ) { return 0; } 46 | virtual void onShutdown( int32_t way ) {} 47 | 48 | public : 49 | // 50 | // 在网络线程中对会话的操作 51 | // 52 | 53 | // 获取会话ID 54 | sid_t id() const { return m_Sid; } 55 | 56 | // 获取主机端口 57 | uint16_t port() const { return m_Port; } 58 | // 获取主机地址 59 | const std::string & host() const { return m_Host; } 60 | 61 | // 获取线程上下文参数 62 | void * iocontext() const { return m_IOContext; } 63 | 64 | // 激活/关闭读事件永驻事件库 65 | // 激活后, 极端的情况下能提高IO性能40%左右 66 | void enablePersist(); 67 | void disablePersist(); 68 | // 设置超时/保活时间 69 | void setTimeout( int32_t seconds ); 70 | void setKeepalive( int32_t seconds ); 71 | // 设置host和port 72 | void setEndpoint( const std::string & host, uint16_t port ); 73 | // 设置发送队列长度 74 | void setSendqueueLimit( int32_t limit ); 75 | // 设置KCP的MTU 76 | void setMTU( int32_t mtu ); 77 | // 设置KCP的MinRTO 78 | void setMinRTO( int32_t minrto ); 79 | // 设置KCP的窗口大小 80 | void setWindowSize( int32_t sndwnd, int32_t rcvwnd ); 81 | 82 | // 发送数据 83 | int32_t send( const std::string & buffer ); 84 | int32_t send( const char * buffer, size_t nbytes, bool isfree = false ); 85 | 86 | // 关闭会话 87 | int32_t shutdown(); 88 | 89 | protected : 90 | friend class IIOService; 91 | 92 | // 初始化会话 93 | void init( sid_t id, 94 | void * context, iolayer_t layer, 95 | const std::string & host, uint16_t port ); 96 | 97 | // 内部回调函数 98 | static int32_t onStartSession( void * context ); 99 | static ssize_t onProcessSession( void * context, const char * buffer, size_t nbytes ); 100 | static char * onTransformSession( void * context, const char * buffer, size_t * nbytes ); 101 | static int32_t onTimeoutSession( void * context ); 102 | static int32_t onKeepaliveSession( void * context ); 103 | static int32_t onErrorSession( void * context, int32_t result ); 104 | static int32_t onPerformSession( void * context, int32_t type, void * task, int32_t interval ); 105 | static void onShutdownSession( void * context, int32_t way ); 106 | 107 | private : 108 | sid_t m_Sid; 109 | uint16_t m_Port; 110 | std::string m_Host; 111 | iolayer_t m_Layer; 112 | void * m_IOContext; 113 | }; 114 | 115 | // 116 | // 网络通信层 117 | // 118 | 119 | class IIOService 120 | { 121 | public : 122 | IIOService( uint8_t nthreads, 123 | uint32_t nclients, int32_t precision = 8, bool immediately = false, bool transform = false ); 124 | virtual ~IIOService(); 125 | 126 | public : 127 | // 初始化/销毁IO上下文 128 | virtual void * initIOContext() { return NULL; } 129 | virtual void finalIOContext( void * context ) { return; } 130 | 131 | // 数据改造 132 | virtual char * onTransform( const char * buffer, size_t & nbytes ) { return const_cast(buffer); } 133 | 134 | // 回调事件 135 | // 需要调用者自己实现 136 | // 有可能在IIOService的多个网络线程中被触发 137 | 138 | // 连接事件 139 | virtual bool onConnectFailed( int32_t result, const char * host, uint16_t port ) { return false; } 140 | virtual IIOSession * onConnectSucceed( sid_t id, const char * host, uint16_t port ) { return NULL; } 141 | // 接受事件 142 | virtual IIOSession * onAccept( sid_t id, NetType type, uint16_t listenport, const char * host, uint16_t port ) { return NULL; } 143 | 144 | public : 145 | // 146 | // 线程安全的API 147 | // 148 | 149 | // 开启/停止服务 150 | bool start(); 151 | void stop(); 152 | 153 | // 暂停对外服务(不可恢复) 154 | void halt(); 155 | 156 | // 获取版本号 157 | static const char * version(); 158 | 159 | // 获取IOLAYER 160 | iolayer_t iolayer() const { return m_IOLayer; } 161 | 162 | // 监听 163 | bool listen( NetType type, 164 | const char * host, uint16_t port, 165 | const options_t * options = nullptr ); 166 | 167 | // 是否正在异步连接 168 | bool isConnecting( const char * host, uint16_t port ); 169 | 170 | // 连接远程服务器 171 | // 参数: 172 | // host - 主机地址 173 | // port - 主机端口 174 | // seconds - 超时时间, <=0 非阻塞的连接; >0 带超时时间的阻塞连接 175 | // 176 | // 返回值: 177 | // -1 - 连接失败 178 | // 0 - 正在连接 179 | // >0 - 连接成功返回会话ID 180 | sid_t connect( const char * host, uint16_t port, int32_t seconds = 0 ); 181 | 182 | // 关联描述符 183 | // 参数: 184 | // fd - 关联的描述符 185 | // privdata- 关联的私有数据 186 | // reattach- 重新绑定新的描述符(相当于重连) 187 | // cb - 关联成功后的回调函数 188 | // context - 上下文参数 189 | // 190 | // 返回值: 191 | // -1 - 关联失败 192 | // 0 - 正在连接 193 | int32_t associate( int32_t fd, void * privdata, 194 | reattacher_t reattach, associator_t cb, void * context ); 195 | 196 | // 发送数据 197 | int32_t send( sid_t id, const std::string & buffer ); 198 | int32_t send( sid_t id, const char * buffer, size_t nbytes, bool isfree = false ); 199 | 200 | // 广播数据 201 | int32_t broadcast( const std::string & buffer ); 202 | int32_t broadcast( const char * buffer, size_t nbytes ); 203 | int32_t broadcast( const sids_t & ids, const std::string & buffer ); 204 | int32_t broadcast( const sids_t & ids, const char * buffer, size_t nbytes ); 205 | 206 | // 终止会话 207 | int32_t shutdown( sid_t id ); 208 | int32_t shutdown( const sids_t & ids ); 209 | 210 | // 提交任务到网络层 211 | int32_t invoke( void * task, taskcloner_t clone, taskexecutor_t execute ); 212 | int32_t perform( sid_t sid, int32_t type, void * task, taskrecycler_t recycle, int32_t interval = -1 ); 213 | 214 | private : 215 | // 监听上下文 216 | struct ListenContext 217 | { 218 | NetType type; 219 | uint16_t port; 220 | IIOService * service; 221 | 222 | ListenContext() 223 | : port( 0 ), 224 | service( NULL ) 225 | {} 226 | 227 | ListenContext( NetType t, uint16_t p, IIOService * s ) 228 | : type( t ), 229 | port( p ), 230 | service( s ) 231 | {} 232 | }; 233 | 234 | // 连接上下文 235 | struct ConnectContext 236 | { 237 | sid_t sid; 238 | uint16_t port; 239 | std::string host; 240 | IIOService * service; 241 | 242 | ConnectContext() 243 | : sid( 0 ), 244 | port( 0 ), 245 | service( NULL ) 246 | {} 247 | 248 | ConnectContext( const char * h, uint16_t p, IIOService * s ) 249 | : sid( 0 ), 250 | port( p ), 251 | host( h ), 252 | service( s ) 253 | {} 254 | }; 255 | 256 | typedef std::vector ListenContexts; 257 | typedef std::vector ConnectContexts; 258 | 259 | private : 260 | // 初始化会话 261 | // 在定制化非常强的场景下使用 262 | void initSession( sid_t id, 263 | IIOSession * session, void * iocontext, 264 | const std::string & host, uint16_t port ); 265 | 266 | // 通知连接结果 267 | void notifyConnectResult( 268 | ConnectContext * context, 269 | int32_t result, sid_t id, int32_t ack ); 270 | 271 | static char * onTransformService( void * context, const char * buffer, size_t * nbytes ); 272 | static int32_t onAcceptSession( void * context, void * iocontext, sid_t id, const char * host, uint16_t port ); 273 | static int32_t onConnectSession( void * context, void * iocontext, int32_t result, const char * host, uint16_t port, sid_t id ); 274 | 275 | private : 276 | iolayer_t m_IOLayer; 277 | bool m_Transform; 278 | bool m_Immediately; 279 | int32_t m_Precision; 280 | uint8_t m_ThreadsCount; 281 | uint32_t m_SessionsCount; 282 | void ** m_IOContextGroup; 283 | 284 | private : 285 | pthread_cond_t m_Cond; 286 | pthread_mutex_t m_Lock; 287 | ListenContexts m_ListenContexts; // 正在监听的会话 288 | ConnectContexts m_ConnectContexts; // 正在连接的会话 289 | }; 290 | 291 | #endif 292 | -------------------------------------------------------------------------------- /examples/redis.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __SRC_REDIS_REDIS_H__ 3 | #define __SRC_REDIS_REDIS_H__ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "io.h" 10 | #include "slice.h" 11 | 12 | class IRedisClient : public IIOService 13 | { 14 | public : 15 | // nthreads - 线程数 16 | // nconnections - 最大连接数 17 | IRedisClient( uint8_t nthreads, uint32_t nconnections ); 18 | virtual ~IRedisClient(); 19 | 20 | virtual void datasets( uint32_t ctxid, redisReply * response ) = 0; 21 | virtual void error( const Slice & request, uint32_t ctxid, const std::string & errstr ) = 0; 22 | 23 | public : 24 | // 初始化 25 | // nconnections - 默认初始化连接数 26 | bool initialize( 27 | const std::string & host, uint16_t port, 28 | uint32_t connections = 4, const std::string & token = "" ); 29 | // 销毁 30 | void finalize(); 31 | 32 | // 订阅 33 | int32_t subscribe( const std::string & channel ); 34 | int32_t unsubscribe( const std::string & channel ); 35 | // 提交 36 | int32_t submit( const Slice & cmd, uint32_t ctxid = 0 ); 37 | // 提交(支持pipeline) 38 | int32_t submit( const Slices & cmds, uint32_t ctxid = 0 ); 39 | 40 | public : 41 | // 绑定 42 | int32_t attach(); 43 | 44 | // 获取token 45 | const std::string & getToken() const { return m_Token; } 46 | 47 | private : 48 | // 初始化/销毁IO上下文 49 | virtual void * initIOContext(); 50 | virtual void finalIOContext( void * context ); 51 | 52 | static void * cloneTask( void * task ); 53 | static void performTask( void * iocontext, void * task ); 54 | static int32_t reattach( int32_t fd, void * privdata ); 55 | static int32_t associatecb( void * context, 56 | void * iocontext, int32_t result, int32_t fd, void * privdata, sid_t sid ); 57 | 58 | private : 59 | std::string m_Host; 60 | uint16_t m_Port; 61 | std::string m_Token; 62 | uint32_t m_Connections; 63 | }; 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /examples/xcurl.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __SRC_HTTP_XCURL_H__ 3 | #define __SRC_HTTP_XCURL_H__ 4 | 5 | // 6 | // https://curl.se/libcurl/c/hiperfifo.html 7 | // http://www.voidcn.com/article/p-nzggwldk-bpx.html 8 | // 9 | 10 | #include 11 | #include 12 | #include 13 | #include "event.h" 14 | #include 15 | #include 16 | 17 | enum HttpMethod 18 | { 19 | eHttpMethod_None = 0, 20 | eHttpMethod_Get = 1, 21 | eHttpMethod_Put = 2, 22 | eHttpMethod_Post = 3, 23 | }; 24 | 25 | class CurlRequest 26 | { 27 | public : 28 | CurlRequest( const std::string & url ); 29 | virtual ~CurlRequest(); 30 | 31 | virtual void onResponse() = 0; 32 | virtual void onError( const char * reason ) = 0; 33 | 34 | public : 35 | // 提交请求 36 | bool perform( HttpMethod method, int32_t timeout_ms = 0 ); 37 | 38 | // 获取URL 39 | CURL * getHandler() const { return m_Curl; } 40 | const std::string & getUrl() const { return m_Url; } 41 | // 获取响应 42 | long getCode() const { return m_Code; } 43 | const std::string & getResponse() const { return m_Response; } 44 | 45 | // 设置HTTPBody 46 | void setBody( const std::string & body ) { m_HttpBody = body; } 47 | // 添加HTTP头 48 | void addHead( const std::string & key, const std::string & value ); 49 | // 添加参数 50 | void addParam( const std::string & key, const std::string & value ); 51 | 52 | private : 53 | friend class CurlAgent; 54 | typedef std::map ParameterList; 55 | 56 | // 编码 57 | std::string encode(); 58 | // 准备 59 | void prepare( HttpMethod method ); 60 | // 设置CURL选项 61 | void initCurlOptions( int32_t timeout_ms, CURLSH * share = nullptr ); 62 | // 回调函数 63 | static int32_t writeCallback( void * buffer, size_t size, size_t nmemb, void * data ); 64 | static int32_t setSocketOption( void * clientp, curl_socket_t curlfd, curlsocktype purpose ); 65 | 66 | protected : 67 | std::string m_Url; 68 | long m_Code; 69 | std::string m_Response; 70 | 71 | private : 72 | CURL * m_Curl; 73 | struct curl_slist * m_Headers; 74 | std::string m_HttpBody; 75 | ParameterList m_Paramters; 76 | }; 77 | 78 | class CurlAgent 79 | { 80 | public : 81 | CurlAgent(); 82 | ~CurlAgent(); 83 | 84 | public : 85 | bool initialize(); 86 | void finalize(); 87 | 88 | CURLM * handler() const { return m_Handler; } 89 | evsets_t evsets() const { return m_EventBase; } 90 | 91 | // 分发任务 92 | int32_t dispatch(); 93 | // 分发任务 94 | bool perform( HttpMethod method, 95 | CurlRequest * request, int32_t timeout_ms = 0 ); 96 | 97 | private : 98 | friend struct CurlHelper; 99 | 100 | // 加锁/解锁 101 | void lock(); 102 | void unlock(); 103 | 104 | // 取消和定时 105 | void cancelTimer(); 106 | void scheduleTimer( int32_t timeout_ms ); 107 | // 处理请求 108 | int32_t processRequests( int32_t fd, int32_t action ); 109 | 110 | private : 111 | struct CurlTask 112 | { 113 | HttpMethod method; 114 | int32_t timeout; 115 | CurlRequest * request; 116 | 117 | CurlTask() 118 | : method(eHttpMethod_None), 119 | timeout(0), 120 | request(nullptr) 121 | {} 122 | 123 | CurlTask( HttpMethod method_, int32_t timeout_, CurlRequest * request_ ) 124 | : method(method_), 125 | timeout(timeout_), 126 | request(request_) 127 | {} 128 | }; 129 | 130 | event_t m_Timer; // 超时事件 131 | evsets_t m_EventBase; 132 | CURLM * m_Handler; 133 | CURLSH * m_ShareCache; // 共享的缓存 134 | pthread_mutex_t m_Lock; 135 | std::deque m_TaskQueue; 136 | }; 137 | 138 | #endif 139 | -------------------------------------------------------------------------------- /include/event.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (c) 2012, Raymond Zhang 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 10 | * * Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 13 | * * Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in 15 | * the documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 20 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 24 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 26 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef EVENT_H 31 | #define EVENT_H 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | #include 38 | 39 | // 40 | // 事件库所支持的事件类型 41 | // 42 | 43 | #define EV_READ 0x01 // 读事件 44 | #define EV_WRITE 0x02 // 写事件 45 | #define EV_TIMEOUT 0x04 // 超时事件 46 | #define EV_PERSIST 0x08 // 永久模式 47 | 48 | // 49 | // 事件的定义, 以及事件集的定义 50 | // 51 | 52 | typedef void * event_t; 53 | typedef void * evsets_t; 54 | typedef void ( *eventcb_t )( int32_t, int16_t, void * ); 55 | 56 | // 时间精度 57 | #define TIMER_PRECISION 8 // 时间精度 58 | 59 | // 60 | // 事件的方法 61 | // 62 | 63 | // 创建事件 64 | event_t event_create(); 65 | 66 | // 设置事件的一些基础属性 67 | // fd - 关注的描述符; 68 | // ev - 关注的事件,即以上定义的那四种 69 | void event_set( event_t self, int32_t fd, int16_t ev ); 70 | 71 | // 设置事件的回调函数 72 | // self - 73 | // cb - 回调函数 74 | // arg - 回调函数的参数 75 | void event_set_callback( event_t self, eventcb_t cb, void * arg ); 76 | 77 | // 获取事件关注的描述符FD 78 | int32_t event_get_fd( event_t self ); 79 | 80 | // 获取事件所属事件集 81 | evsets_t event_get_sets( event_t self ); 82 | 83 | // 重置事件 84 | void event_reset( event_t self ); 85 | 86 | // 销毁事件 87 | void event_destroy( event_t self ); 88 | 89 | // 90 | // 事件集的方法 91 | // 92 | 93 | // 创建事件集(TIMER_PRECISION) 94 | evsets_t evsets_create( int32_t precision ); 95 | 96 | // 事件库的版本 97 | const char * evsets_get_version(); 98 | 99 | // 向事件集中添加事件 100 | // self - 101 | // ev - 事件 102 | // tv - 超时时间(ms); -1:不超时; 0-立刻超时; >0指定的超时时间 103 | // 返回值定义: 104 | // 返回<0, 添加事件失败 105 | // 返回 1, 添加IO事件成功 106 | // 返回 2, 添加超时事件成功 107 | // 返回 3, IO事件和超时事件添加成功 108 | int32_t evsets_add( evsets_t self, event_t ev, int32_t tv ); 109 | 110 | // 从事件集中删除事件 111 | int32_t evsets_del( evsets_t self, event_t ev ); 112 | 113 | // 分发并处理事件 114 | // 返回激活的事件个数 115 | int32_t evsets_dispatch( evsets_t self ); 116 | 117 | // 销毁事件集 118 | void evsets_destroy( evsets_t self ); 119 | 120 | #ifdef __cplusplus 121 | } 122 | #endif 123 | 124 | #endif 125 | -------------------------------------------------------------------------------- /include/network.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (c) 2012, Raymond Zhang 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 10 | * * Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 13 | * * Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in 15 | * the documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 20 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 24 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 26 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef NETWORK_H 31 | #define NETWORK_H 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | #include 38 | #include 39 | 40 | // 41 | // 网络层 42 | // 43 | typedef uint64_t sid_t; 44 | typedef void * iolayer_t; 45 | 46 | // 网络类型 47 | #define NETWORK_TCP 1 // TCP 48 | #define NETWORK_UDP 2 // UDP, 特殊场景中使用 49 | #define NETWORK_KCP 3 // KCP 50 | 51 | // 服务器参数(当前是KCP的选项) 52 | typedef struct 53 | { 54 | int32_t mtu; // 最大传输单元, 默认值1400 55 | int32_t minrto; // 最小重传时间, 默认值30ms 56 | int32_t sndwnd; // 发送窗口, 默认值64 57 | int32_t rcvwnd; // 接收窗口, 默认值64 58 | int32_t stream; // kcp流模式, 默认值1(流模式) 59 | int32_t resend; // 快速重传, 默认值2 60 | int32_t deadlink; // 最大重传次数, 默认值50 61 | int32_t interval; // 内部处理时钟, 默认值40ms 62 | int32_t ntransfer; // 中转描述符个数, 默认值16 63 | } options_t; 64 | 65 | // IO服务 66 | // start() - 网络就绪的回调 67 | // process() - 收到数据包的回调 68 | // 返回值为处理掉的数据包, <0: 出错,关闭连接 69 | // transform() - 发送数据包前的回调 70 | // 返回需要发送的数据包 71 | // 如果改造了数据包,请务必确保1.禁止原地改造; 2.数据包是必须是malloc()分配出来的 72 | // keepalive() - 保活定时器超时的回调 73 | // timeout() - 超时的回调 74 | // error() - 出错的回调 75 | // 对于accept()出来的客户端, 直接回调shutdown(); 76 | // 对于connect()出去的客户端, ==0, 尝试重连, !=0, 直接回调shutdown(); 77 | // 对于assoicate()关联的客户端, 设置过reattach函数, 行为类似connect(), 未设置过的, 直接回调shutdown() . 78 | // perform() - 处理其他模块提交到网络层的任务, <0: 出错,关闭连接 79 | // type - 任务类型 80 | // task - 任务数据 81 | // interval - 定时任务 82 | // shutdown() - 会话终止时的回调, 不论返回值, 直接销毁会话 83 | // way - 0, 逻辑层主动终止会话的情况, 84 | // 1) 显式的iolayer_shutdown()或者iolayer_shutdowns() 85 | // 2) 显式的iolayer_perform()或者iolayer_performs() 86 | // 1, 逻辑层被动终止会话的情况, timeout(), error(), process()出错 87 | typedef struct 88 | { 89 | int32_t ( *start )( void * context ); 90 | ssize_t ( *process )( void * context, const char * buf, size_t nbytes ); 91 | char * ( *transform )( void * context, const char * buf, size_t * nbytes ); 92 | int32_t ( *keepalive )( void * context ); 93 | int32_t ( *timeout )( void * context ); 94 | int32_t ( *error )( void * context, int32_t result ); 95 | int32_t ( *perform )( void * context, int32_t type, void * task, int32_t interval ); 96 | void ( *shutdown )( void * context, int32_t way ); 97 | } ioservice_t; 98 | 99 | // 创建网络层 100 | // nthreads - 网络线程数 101 | // nclients - 网络层服务的连接数 102 | // precision - 事件集的时间精度(建议值为8ms) 103 | // immediately - 是否立刻提交网络层, 0:否; 1:是(用于对网络实时性比较高的场景) 104 | iolayer_t iolayer_create( uint8_t nthreads, uint32_t nclients, int32_t precision, uint8_t immediately ); 105 | 106 | // 网络层设置线程上下文参数(在listen(), connect(), associate()之前调用) 107 | // self - 108 | // contexts - 上下文参数数组, 每个网络线程设置上下文参数 109 | // count - 数组长度 110 | // 确保长度和网络线程个数相等, 毕竟多个网络线程是对等的 111 | int32_t iolayer_set_iocontext( iolayer_t self, void ** contexts, uint8_t count ); 112 | 113 | // 数据改造方法(不建议原地改造) 114 | // 参数1: 上下文参数 115 | // 参数2: 欲发送或者广播的消息内容 116 | // 参数3: 指向消息长度的指针, 返回改造后的数据包长度 117 | typedef char * ( *transformer_t )( void *, const char *, size_t * ); 118 | // 网络层设置数据包改造方法, 119 | // 该网络层的统一的数据包改造方法(在listen(), connect(), associate()之前调用) 120 | // self - 121 | // transform - 数据包改造方法(不建议原地改造) 122 | // context - 上下文参数 123 | int32_t iolayer_set_transform( iolayer_t self, transformer_t transform, void * context ); 124 | 125 | // 新会话创建成功后的回调 126 | // 参数1: 上下文参数 127 | // 参数2: 网络线程上下文参数 128 | // 参数3: 新会话ID 129 | // 参数4: 会话的IP地址 130 | // 参数5: 会话的端口号 131 | typedef int32_t ( *acceptor_t )( void *, void *, sid_t, const char *, uint16_t ); 132 | // 开启服务端 133 | // type - 网络类型: NETWORK_TCP or NETWORK_UDP or NETWORK_KCP 134 | // host - 绑定的地址 135 | // port - 监听的端口号 136 | // options - 服务器参数 137 | // callback - 新会话创建成功后的回调(参考acceptor_t的定义),会被多个网络线程调用 138 | // context - 上下文参数 139 | int32_t iolayer_listen( iolayer_t self, uint8_t type, 140 | const char * host, uint16_t port, const options_t * options, acceptor_t callback, void * context ); 141 | 142 | // 连接结果的回调 143 | // 参数1: 上下文参数 144 | // 参数2: 网络线程上下文参数 145 | // 参数3: 连接结果 146 | // 参数4: 连接的远程服务器的地址 147 | // 参数5: 连接的远程服务器的端口 148 | // 参数6: 连接成功后返回的会话ID 149 | typedef int32_t ( *connector_t )( void *, void *, int32_t, const char *, uint16_t, sid_t ); 150 | // 开启客户端 151 | // host - 远程服务器的地址 152 | // port - 远程服务器的端口 153 | // callback - 连接结果的回调(参考connector_t的定义) 154 | // context - 上下文参数 155 | int32_t iolayer_connect( iolayer_t self, 156 | const char * host, uint16_t port, connector_t callback, void * context ); 157 | 158 | // 重新关联函数,返回新的描述符 159 | // 参数1: 上次关联的描述符 160 | // 参数2: 描述符相关的私有数据 161 | typedef int32_t ( *reattacher_t )( int32_t, void * ); 162 | // 关联成功后的回调 163 | // 参数1: 上下文参数 164 | // 参数2: 网络线程上下文参数 165 | // 参数3: 关联结果 166 | // 参数3: 描述符 167 | // 参数4: 描述符相关私有数据 168 | // 参数5: 会话ID 169 | typedef int32_t ( *associator_t )( void *, void *, int32_t, int32_t, void *, sid_t ); 170 | // 描述符关联会话ID 171 | // fd - 描述符 172 | // privdata - 描述符相关的私有数据 173 | // reattach - 重新关联函数(参考reattacher_t定义),返回新的描述符 174 | // callback - 关联成功后的回调(参考associator_t的定义) 175 | // context - 上下文参数 176 | int32_t iolayer_associate( iolayer_t self, 177 | int32_t fd, void * privdata, reattacher_t reattach, associator_t callback, void * context ); 178 | 179 | // 会话参数的设置, 只能在ioservice_t中使用 180 | // 建议在ioservice_t::onStart()中调用 181 | int32_t iolayer_set_timeout( iolayer_t self, sid_t id, int32_t seconds ); 182 | int32_t iolayer_set_keepalive( iolayer_t self, sid_t id, int32_t seconds ); 183 | int32_t iolayer_set_service( iolayer_t self, sid_t id, ioservice_t * service, void * context ); 184 | // 设置读事件常驻事件库( 默认为0; 激活后, 极端的情况下能提高IO性能40%左右 ) 185 | int32_t iolayer_set_persist( iolayer_t self, sid_t id, int32_t onoff ); 186 | // 设置发送队列阈值, 超过阈值关闭连接( 默认为0: 不限制 ) 187 | int32_t iolayer_set_sndqlimit( iolayer_t self, sid_t id, int32_t queuelimit ); 188 | // 设置kcp的窗口, MTU, MINRTO 189 | int32_t iolayer_set_mtu( iolayer_t self, sid_t id, int32_t mtu ); 190 | int32_t iolayer_set_minrto( iolayer_t self, sid_t id, int32_t minrto ); 191 | int32_t iolayer_set_wndsize( iolayer_t self, sid_t id, int32_t sndwnd, int32_t rcvwnd ); 192 | 193 | // 发送数据到会话 194 | // id - 会话ID 195 | // buf - 要发送的缓冲区 196 | // nbytes - 要发送的长度 197 | // isfree - 1-由网络层释放缓冲区, 0-网络层需要Copy缓冲区 198 | int32_t iolayer_send( iolayer_t self, sid_t id, const char * buf, size_t nbytes, int32_t isfree ); 199 | 200 | // 广播数据到指定的会话 201 | int32_t iolayer_broadcast( iolayer_t self, sid_t * ids, uint32_t count, const char * buf, size_t nbytes ); 202 | 203 | // 广播数据到IO层的所有会话 204 | int32_t iolayer_broadcast2( iolayer_t self, const char * buf, size_t nbytes ); 205 | 206 | // 终止指定的会话 207 | // 此处需要注意, 主动终止会话的情况下,也会收到shutdown()的回调, 只是way==0 208 | int32_t iolayer_shutdown( iolayer_t self, sid_t id ); 209 | int32_t iolayer_shutdowns( iolayer_t self, sid_t * ids, uint32_t count ); 210 | 211 | // 任务复制函数 212 | // 参数: 任务 213 | typedef void * ( *taskcloner_t )( void * ); 214 | // 任务处理函数 215 | // 参数1: 网络线程上下文参数 216 | // 参数2: 任务 217 | typedef void ( *taskexecutor_t )( void *, void * ); 218 | // 提交任务到网络层 219 | // task - 任务 220 | // clone - 任务复制函数(参考taskcloner_t的定义), 如果为NULL, 随机选择一个网络线程投递 221 | // execute - 任务处理函数(参考taskexecutor_t的定义) 222 | int32_t iolayer_invoke( iolayer_t self, void * task, taskcloner_t clone, taskexecutor_t execute ); 223 | 224 | // 任务回收函数 225 | // 参数1: 类型 226 | // 参数2: 任务 227 | // 参数3: 时间 228 | typedef void ( *taskrecycler_t )( int32_t, void *, int32_t ); 229 | // 分派任务给指定的会话, 通过ioservice_t::perform()回调执行该任务 230 | // id - 会话ID 231 | // type - 任务类型 232 | // task - 任务数据 233 | // interval - 定时任务 234 | // recycle - 任务回收函数(参考taskrecycler_t的定义) 235 | int32_t iolayer_perform( iolayer_t self, sid_t id, int32_t type, void * task, int32_t interval, taskrecycler_t recycle ); 236 | 237 | // 停止网络服务 238 | // 行为定义: 239 | // 1. 停止对外提供接入服务, 不再接受新的连接; 240 | // 2. 停止已经接入连接的接收服务, 不再接收新的数据包(接收但不回调ioservice::process()) 241 | // 3. 一切发送行为都正常进行 242 | void iolayer_stop( iolayer_t self ); 243 | 244 | // 销毁网络层 245 | void iolayer_destroy( iolayer_t self ); 246 | 247 | #ifdef __cplusplus 248 | } 249 | #endif 250 | 251 | #endif 252 | -------------------------------------------------------------------------------- /include/threads.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (c) 2012, Raymond Zhang 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 10 | * * Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 13 | * * Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in 15 | * the documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 20 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 24 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 26 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef THREADS_H 31 | #define THREADS_H 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | #include 38 | #include 39 | 40 | #include "event.h" 41 | 42 | // 网络线程组 43 | typedef void * iothreads_t; 44 | typedef void ( *processor_t )( void *, uint8_t, int16_t, void * ); 45 | 46 | // 创建网络线程组 47 | // nthreads - 网络线程组中的线程数 48 | // precision - 事件集的精度 49 | // immediately - 是否立刻提交网络线程 50 | iothreads_t iothreads_start( uint8_t nthreads, int32_t precision, uint8_t immediately ); 51 | 52 | // 设置处理器 53 | void iothreads_set_processor( iothreads_t self, processor_t processor, void * context ); 54 | 55 | // 获取网络线程组中指定线程的ID 56 | pthread_t iothreads_get_id( iothreads_t self, uint8_t index ); 57 | 58 | // 获取网络线程组中指定线程的事件集 59 | evsets_t iothreads_get_sets( iothreads_t self, uint8_t index ); 60 | 61 | // 设置/获取网络线程上下文参数 62 | void * iothreads_get_context( iothreads_t self, uint8_t index ); 63 | void iothreads_set_context( iothreads_t self, uint8_t index, void * context ); 64 | 65 | // 向网络线程组中指定的线程提交任务 66 | // index - 指定网络线程的编号 67 | // type - 提交的任务类型, NOTE:0xff内置的任务类型 68 | // task - 提交的任务数据 69 | // size - 任务数据的长度, 默认设置为0 70 | int32_t iothreads_post( iothreads_t self, uint8_t index, int16_t type, void * task, uint8_t size ); 71 | 72 | // 网络线程组停止 73 | void iothreads_stop( iothreads_t self ); 74 | 75 | #ifdef __cplusplus 76 | } 77 | #endif 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /libevlite.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /libevlite.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /libevlite.xcodeproj/project.xcworkspace/xcshareddata/libevlite.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 57EFADE4-CBCB-4269-9C7F-D10DA0D9CF85 9 | IDESourceControlProjectName 10 | libevlite 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 43287AAFD8EE5E7E5655807BA84DE9315D588BD3 14 | bsdart.org:users/spriteray/libevlite.git 15 | 16 | IDESourceControlProjectPath 17 | libevlite.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 43287AAFD8EE5E7E5655807BA84DE9315D588BD3 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | bsdart.org:users/spriteray/libevlite.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | 43287AAFD8EE5E7E5655807BA84DE9315D588BD3 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 43287AAFD8EE5E7E5655807BA84DE9315D588BD3 36 | IDESourceControlWCCName 37 | libevlite 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /libevlite.xcodeproj/xcshareddata/xcschemes/echoserver.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /libevlite.xcodeproj/xcshareddata/xcschemes/pingpong.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 68 | 69 | 70 | 71 | 72 | 73 | 79 | 81 | 87 | 88 | 89 | 90 | 92 | 93 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /libevlite.xcodeproj/xcshareddata/xcschemes/pingpong_client.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /src/channel.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CHANNEL_H 3 | #define CHANNEL_H 4 | 5 | /* 6 | * channel 网络通道 7 | * 提供基于session的一切网络操作 8 | */ 9 | 10 | #include 11 | #include "session.h" 12 | 13 | // 14 | ssize_t channel_transmit( struct session * session ); 15 | ssize_t channel_receive( struct session * session ); 16 | ssize_t channel_send( struct session * session, char * buf, size_t nbytes ); 17 | 18 | // 会话出错 19 | // 丢弃发送队列中的数据 20 | int32_t channel_error( struct session * session, int32_t result ); 21 | 22 | // 会话关闭 23 | // 丢弃发送队列中的数据 24 | int32_t channel_shutdown( struct session * session ); 25 | 26 | // 处理UDP临时数据 27 | void channel_udpprocess( struct session * session, struct buffer * buffer ); 28 | 29 | // 事件的回调函数集合 30 | void channel_on_read( int32_t fd, int16_t ev, void * arg ); 31 | void channel_on_udpread( int32_t fd, int16_t ev, void * arg ); 32 | void channel_on_write( int32_t fd, int16_t ev, void * arg ); 33 | void channel_on_accept( int32_t fd, int16_t ev, void * arg ); 34 | void channel_on_keepalive( int32_t fd, int16_t ev, void * arg ); 35 | void channel_on_reconnect( int32_t fd, int16_t ev, void * arg ); 36 | void channel_on_connected( int32_t fd, int16_t ev, void * arg ); 37 | void channel_on_reconnected( int32_t fd, int16_t ev, void * arg ); 38 | void channel_on_associated( int32_t fd, int16_t ev, void * arg ); 39 | void channel_on_schedule( int32_t fd, int16_t ev, void * arg ); 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CONFIG_H 3 | #define CONFIG_H 4 | 5 | // 6 | // OS Feature(s) 7 | // 8 | 9 | // 10 | // OS 11 | // EVENT_OS_WIN32 12 | // EVENT_OS_UNKNOWN 13 | // EVENT_OS_LINUX, 14 | // EVENT_OS_BSD, EVENT_OS_MACOS 15 | // 16 | #if defined _WIN32 17 | #define EVENT_OS_WIN32 18 | #else 19 | // 除了windows平台外 20 | // 几乎都支持的头文件, 用于判断以下特性 21 | #include 22 | #include 23 | // 24 | #if defined __linux__ 25 | #define EVENT_OS_LINUX 26 | #include 27 | #include 28 | #elif defined __FreeBSD__ || defined __OpenBSD__ 29 | #define EVENT_OS_BSD 30 | #elif defined __APPLE__ || defined __darwin__ 31 | #define EVENT_OS_MACOS 32 | #else 33 | #define EVENT_OS_UNKNOWN 34 | #endif 35 | #endif 36 | 37 | // EVENT_HAVE_REUSEPORT 38 | #ifdef SO_REUSEPORT 39 | #ifdef EVENT_OS_LINUX 40 | // Faster SO_REUSEPORT for TCP ( since Linux 4.6.0 ) 41 | // faster lookup of a target socket when choosing a socket from the group of sockets, 42 | // and also expose the ability to use a BPF program when selecting a socket from a reuseport group 43 | // https://kernelnewbies.org/Linux_4.6#Networking 44 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,6,0) 45 | #define EVENT_HAVE_REUSEPORT 46 | #endif 47 | #else 48 | #define EVENT_HAVE_REUSEPORT 49 | #endif 50 | #endif 51 | 52 | // EVENT_USE_LOCALHOST 53 | #if defined EVENT_OS_WIN32 54 | #define EVENT_USE_LOCALHOST 55 | #elif defined EVENT_OS_MACOS 56 | #define EVENT_USE_LOCALHOST 57 | #elif defined EVENT_OS_LINUX 58 | #if LINUX_VERSION_CODE > KERNEL_VERSION(4,4,0) 59 | #define EVENT_USE_LOCALHOST 60 | #endif 61 | #endif 62 | 63 | // EVENT_HAVE_EVENTFD 64 | #if defined EVENT_OS_LINUX 65 | // eventfd() is available on Linux since kernel 2.6.22. 66 | // Working support is provided in glibc since version 2.8 67 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) 68 | #if defined __GLIBC__ && __GLIBC_PREREQ(2,8) 69 | #define EVENT_HAVE_EVENTFD 70 | #include 71 | #endif 72 | #endif 73 | #endif 74 | 75 | // EVENT_HAVE_EPOLLCREATE1 76 | #if defined EVENT_OS_LINUX 77 | // epoll_create1() was added to the kernel in version 2.6.27. 78 | // Library support is provided in glibc starting with version 2.9. 79 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) 80 | #if defined __GLIBC__ && __GLIBC_PREREQ(2,9) 81 | #define EVENT_HAVE_EPOLLCREATE1 82 | #endif 83 | #endif 84 | #endif 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /src/driver.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include "utils.h" 7 | #include "message.h" 8 | #include "session.h" 9 | #include "ephashtable.h" 10 | #include "network-internal.h" 11 | #include "event-internal.h" 12 | 13 | #include "driver.h" 14 | 15 | static inline int32_t _kcp_can_send( struct driver * self ); 16 | static inline uint32_t _kcp_milliseconds( struct driver * self ); 17 | static inline struct session * _get_session( struct driver * self ); 18 | static inline void _kcp_schedule( struct driver * self, int32_t timeout ); 19 | 20 | static void _kcp_refresh_state( struct driver * self ); 21 | static void _kcp_timer( int32_t fd, int16_t ev, void * arg ); 22 | static int32_t _kcp_output( const char * buf, int32_t len, ikcpcb * kcp, void * user ); 23 | 24 | struct driver * driver_create( struct session * s, struct buffer * buffer, const options_t * options ) 25 | { 26 | struct driver * self = (struct driver *)calloc( 1, sizeof( struct driver ) ); 27 | if ( self != NULL ) { 28 | uint32_t conv = ikcp_getconv( buffer_data( buffer ) ); 29 | 30 | // 创建定时器 31 | self->timer = event_create(); 32 | if ( self->timer == NULL ) { 33 | driver_destroy( self ); 34 | return NULL; 35 | } 36 | 37 | // 创建kcp对象 38 | self->kcp = ikcp_create( conv, s ); 39 | if ( self->kcp == NULL ) { 40 | driver_destroy( self ); 41 | return NULL; 42 | } 43 | 44 | // 设置时间戳 45 | self->epoch = milliseconds(); 46 | // 设置kcp的极速模式 47 | // 1 - 启动nodelay模式 48 | // 1 - 关闭流量控制 49 | ikcp_nodelay( self->kcp, 50 | 1, options->interval, options->resend, 1 ); // KCP极速模式 51 | ikcp_setmtu( self->kcp, options->mtu ); // 最大传输单元 52 | ikcp_wndsize( self->kcp, options->sndwnd, options->rcvwnd ); // 窗口大小 53 | self->kcp->stream = options->stream; // 流模式 54 | self->kcp->rx_minrto = options->minrto; // 最小重传超时时间 55 | self->kcp->dead_link = options->deadlink; // 最大重传次数 56 | ikcp_setoutput( self->kcp, _kcp_output ); 57 | } 58 | 59 | return self; 60 | } 61 | 62 | void driver_destroy( struct driver * self ) 63 | { 64 | struct session * session = _get_session( self ); 65 | 66 | if ( self->timer != NULL ) { 67 | if ( session != NULL 68 | && ( session->status & SESSION_SCHEDULING ) ) { 69 | evsets_del( session->evsets, self->timer ); 70 | session->status &= ~SESSION_SCHEDULING; 71 | } 72 | 73 | event_destroy( self->timer ); 74 | self->timer = NULL; 75 | } 76 | 77 | if ( self->entry != NULL ) { 78 | ephashtable_remove( 79 | self->entry->hashtable, session->host, session->port ); 80 | self->entry = NULL; 81 | } 82 | 83 | if ( self->kcp != NULL ) { 84 | ikcp_release( self->kcp ); 85 | self->kcp = NULL; 86 | } 87 | 88 | free( self ); 89 | self = NULL; 90 | } 91 | 92 | int32_t driver_set_endpoint( struct driver * self, struct ephashtable * table, int32_t family, const char * host, uint16_t port ) 93 | { 94 | // 添加到ep管理器中 95 | self->entry = (struct udpentry *)ephashtable_append( table, host, port ); 96 | if ( self->entry == NULL ) { 97 | return -1; 98 | } 99 | 100 | self->entry->hashtable = table; 101 | self->entry->session = _get_session( self ); 102 | convert_endpoint( &( self->addr ), family, host, port ); 103 | 104 | return 0; 105 | } 106 | 107 | ssize_t driver_input( struct driver * self, struct buffer * in, struct buffer * out ) 108 | { 109 | size_t len = 0; 110 | 111 | // 输入kcp 112 | ikcp_input( self->kcp, buffer_data( in ), buffer_length( in ) ); 113 | 114 | // 接收逻辑包到inbuffer中 115 | while ( 1 ) { 116 | ssize_t peeksize = ikcp_peeksize( self->kcp ); 117 | if ( peeksize <= 0 ) { 118 | break; 119 | } 120 | 121 | buffer_reserve( out, peeksize ); 122 | peeksize = ikcp_recv( self->kcp, 123 | out->buffer + out->length, (int32_t)peeksize ); 124 | if ( peeksize <= 0 ) { 125 | break; 126 | } 127 | 128 | len += peeksize; 129 | out->length += peeksize; 130 | // 检验self->buffer中的conv是否变化 131 | // 变化了直接替换kcp句柄中的conv 132 | // driver_set_conv( self, ikcp_getconv( buffer_data( in ) ) ); 133 | } 134 | 135 | // 读事件, 立刻update()不需要等下一轮 136 | _kcp_refresh_state( self ); 137 | 138 | return len; 139 | } 140 | 141 | ssize_t driver_transmit( struct session * session ) 142 | { 143 | int32_t rc = 0; 144 | ssize_t writen = 0; 145 | 146 | for ( ; session_sendqueue_count( session ) > 0; ) { 147 | if ( !_kcp_can_send( session->driver ) ) { 148 | rc = -3; 149 | break; 150 | } 151 | 152 | // 取出第一条消息 153 | struct message * message = NULL; 154 | QUEUE_TOP( sendqueue ) ( &session->sendqueue, &message ); 155 | // 发送数据给KCP 156 | rc = ikcp_send( session->driver->kcp, 157 | message_get_buffer( message ), (int32_t)message_get_length( message ) ); 158 | ikcp_flush( session->driver->kcp ); 159 | if ( rc < 0 ) { 160 | break; 161 | } 162 | 163 | writen += rc; 164 | QUEUE_POP( sendqueue ) ( &session->sendqueue, &message ); 165 | message_add_success( message ); 166 | if ( message_is_complete( message ) ) { 167 | message_destroy( message ); 168 | } 169 | } 170 | 171 | // 写事件, 立刻update()不需要等下一轮 172 | _kcp_refresh_state( session->driver ); 173 | 174 | if ( rc > 0 175 | && _kcp_can_send( session->driver ) 176 | && session_sendqueue_count( session ) > 0 ) { 177 | ssize_t againlen = driver_transmit( session ); 178 | if ( againlen > 0 ) { 179 | writen += againlen; 180 | } 181 | } 182 | 183 | return writen; 184 | } 185 | 186 | ssize_t driver_send( struct session * session, char * buf, size_t nbytes ) 187 | { 188 | int32_t rc = 0; 189 | struct driver * d = session->driver; 190 | 191 | if ( _kcp_can_send( d ) ) { 192 | // 发送 193 | rc = ikcp_send( 194 | d->kcp, buf, (int32_t)nbytes ); 195 | ikcp_flush( d->kcp ); 196 | } 197 | 198 | // 写事件, 立刻update()不需要等下一轮 199 | _kcp_refresh_state( d ); 200 | return rc < 0 ? 0 : rc; 201 | } 202 | 203 | void driver_set_mtu( struct driver * self, int32_t mtu ) 204 | { 205 | ikcp_setmtu( self->kcp, mtu ); 206 | } 207 | 208 | void driver_set_minrto( struct driver * self, int32_t minrto ) 209 | { 210 | self->kcp->rx_minrto = minrto; 211 | } 212 | 213 | void driver_set_wndsize( struct driver * self, int32_t sndwnd, int32_t rcvwnd ) 214 | { 215 | ikcp_wndsize( self->kcp, sndwnd, rcvwnd ); 216 | } 217 | 218 | void driver_set_conv( struct driver * self, uint32_t conv ) 219 | { 220 | self->kcp->conv = conv; 221 | } 222 | 223 | void _kcp_refresh_state( struct driver * self ) 224 | { 225 | // 刷新kcp的状态 226 | uint32_t current = _kcp_milliseconds( self ); 227 | ikcp_update( self->kcp, current ); 228 | ikcp_flush( self->kcp ); 229 | _kcp_schedule( self, ikcp_check( self->kcp, current ) - current ); 230 | } 231 | 232 | uint32_t _kcp_milliseconds( struct driver * self ) 233 | { 234 | return (uint32_t)( milliseconds() - self->epoch ); 235 | } 236 | 237 | void _kcp_timer( int32_t fd, int16_t ev, void * arg ) 238 | { 239 | struct session * session = (struct session *)arg; 240 | 241 | // 状态 242 | session->status &= ~SESSION_SCHEDULING; 243 | // 刷新kcp的状态 244 | _kcp_refresh_state( session->driver ); 245 | } 246 | 247 | struct session * _get_session( struct driver * self ) 248 | { 249 | if ( self->kcp != NULL ) { 250 | return (struct session *)self->kcp->user; 251 | } 252 | return NULL; 253 | } 254 | 255 | void _kcp_schedule( struct driver * self, int32_t timeout ) 256 | { 257 | struct session * session = _get_session( self ); 258 | 259 | if ( timeout < 0 ) { 260 | timeout = EVENTSET_PRECISION( session->evsets ); 261 | } 262 | 263 | // 还在定时器中, 移除 264 | if ( session->status & SESSION_SCHEDULING ) { 265 | evsets_del( session->evsets, self->timer ); 266 | session->status &= ~SESSION_SCHEDULING; 267 | } 268 | 269 | // 加入定时器 270 | event_set( self->timer, -1, 0 ); 271 | event_set_callback( self->timer, _kcp_timer, session ); 272 | evsets_add( session->evsets, self->timer, timeout ); 273 | session->status |= SESSION_SCHEDULING; 274 | } 275 | 276 | int32_t _kcp_output( const char * buf, int32_t len, ikcpcb * kcp, void * user ) 277 | { 278 | struct session * session = (struct session *)user; 279 | struct driver * driver = session->driver; 280 | 281 | // kcp出错的情况下,就重传呗, 没啥大不了的 282 | ssize_t writen = sendto( session->fd, buf, len, 283 | MSG_DONTWAIT, (struct sockaddr *)&driver->addr, sizeof( driver->addr ) ); 284 | if ( writen < 0 ) { 285 | if ( errno == EINTR 286 | || errno == EAGAIN || errno == EWOULDBLOCK ) { 287 | writen = 0; 288 | } 289 | } 290 | 291 | return (int32_t)writen; 292 | } 293 | 294 | int32_t _kcp_can_send( struct driver * self ) 295 | { 296 | return (uint32_t)ikcp_waitsnd( self->kcp ) < 4 * self->kcp->snd_wnd; 297 | } 298 | -------------------------------------------------------------------------------- /src/driver.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef DRIVER_H 3 | #define DRIVER_H 4 | 5 | #include "ikcp.h" 6 | #include "event.h" 7 | #include "message.h" 8 | #include "network.h" 9 | 10 | struct session; 11 | struct udpentry; 12 | struct ephashtable; 13 | 14 | struct driver 15 | { 16 | ikcpcb * kcp; // kcp对象 17 | event_t timer; // 定时器 18 | int64_t epoch; // 开始时间 19 | struct udpentry * entry; // UDP条目 20 | struct sockaddr_storage addr; // 对端地址 21 | }; 22 | 23 | // 创建驱动 24 | struct driver * driver_create( 25 | struct session * s, struct buffer * buffer, const options_t * option ); 26 | 27 | int32_t driver_set_endpoint( struct driver * self, 28 | struct ephashtable * table, int32_t family, const char * host, uint16_t port ); 29 | 30 | // 输入数据(KCP重组) 31 | ssize_t driver_input( struct driver * self, struct buffer * in, struct buffer * out ); 32 | 33 | // 设置窗口, MTU, MINRTO 34 | void driver_set_mtu( struct driver * self, int32_t mtu ); 35 | void driver_set_minrto( struct driver * self, int32_t minrto ); 36 | void driver_set_wndsize( struct driver * self, int32_t sndwnd, int32_t rcvwnd ); 37 | void driver_set_conv( struct driver * self, uint32_t conv ); 38 | 39 | // 发送/接收数据 40 | ssize_t driver_transmit( struct session * self ); 41 | ssize_t driver_send( struct session * self, char * buf, size_t nbytes ); 42 | 43 | // 销毁 44 | void driver_destroy( struct driver * self ); 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /src/ephashtable.c: -------------------------------------------------------------------------------- 1 | 2 | #include "utils.h" 3 | #include "ephashtable.h" 4 | 5 | #define INVOKE( self, method, entry ) \ 6 | if ( ( self )->helper != NULL ) { ( self )->helper( ( method ), ( entry ) ); } 7 | 8 | static inline uint32_t _hash_function( const void * key, int32_t len ) 9 | { 10 | /* 'm' and 'r' are mixing constants generated offline. 11 | * They're not really 'magic', they just happen to work well. */ 12 | const int r = 24; 13 | static uint32_t seed = 5381; 14 | const uint32_t m = 0x5bd1e995; 15 | 16 | /* Initialize the hash to a 'random' value */ 17 | uint32_t h = seed ^ len; 18 | 19 | /* Mix 4 bytes at a time into the hash */ 20 | const unsigned char * data = (const unsigned char *)key; 21 | 22 | while ( len >= 4 ) { 23 | uint32_t k = *(uint32_t *)data; 24 | 25 | k *= m; 26 | k ^= k >> r; 27 | k *= m; 28 | 29 | h *= m; 30 | h ^= k; 31 | 32 | data += 4; 33 | len -= 4; 34 | } 35 | 36 | /* Handle the last few bytes of the input array */ 37 | switch ( len ) { 38 | case 3 : h ^= data[2] << 16; 39 | case 2 : h ^= data[1] << 8; 40 | case 1 : h ^= data[0]; h *= m; 41 | }; 42 | 43 | /* Do a few final mixes of the hash to ensure the last few 44 | * * bytes are well-incorporated. */ 45 | h ^= h >> 13; 46 | h *= m; 47 | h ^= h >> 15; 48 | 49 | return h; 50 | } 51 | 52 | static uint32_t _get_hashkey( const char * host, uint16_t port ) 53 | { 54 | char key[128]; 55 | size_t len = snprintf( key, 56 | 127, "%s::%d", host, port ); 57 | return _hash_function( key, len ); 58 | } 59 | 60 | struct ephashtable * ephashtable_create( size_t count, size_t size, helper_t helper ) 61 | { 62 | struct ephashtable * q = NULL; 63 | 64 | q = (struct ephashtable *)calloc( 1, sizeof( struct ephashtable ) ); 65 | assert( q != NULL && "create struct ephashtable failed" ); 66 | 67 | q->size = nextpow2( count ); 68 | q->sizemask = q->size - 1; 69 | q->count = 0; 70 | q->objsize = size; 71 | q->helper = helper; 72 | q->table = (struct endpoint **)calloc( q->size, sizeof( struct endpoint * ) ); 73 | assert( q->table != NULL && "create struct udpentry * failed" ); 74 | 75 | return q; 76 | } 77 | 78 | void ephashtable_destroy( struct ephashtable * self ) 79 | { 80 | if ( self->table != NULL ) { 81 | for ( size_t i = 0; i < self->size; ++i ) { 82 | struct endpoint * next = NULL; 83 | struct endpoint * head = self->table[i]; 84 | 85 | while ( head != NULL ) { 86 | INVOKE( self, 3, head ); 87 | next = head->next; 88 | free( head ); head = next; 89 | --self->count; 90 | } 91 | } 92 | 93 | free( self->table ); 94 | self->table = NULL; 95 | } 96 | 97 | self->count = 0; 98 | self->size = 0; 99 | self->sizemask = 0; 100 | 101 | free( self ); 102 | self = NULL; 103 | } 104 | 105 | void * ephashtable_find( struct ephashtable * self, const char * host, uint16_t port ) 106 | { 107 | if ( self->count == 0 ) { 108 | return NULL; 109 | } 110 | 111 | struct endpoint * head = self->table[_get_hashkey(host, port) & self->sizemask]; 112 | while ( head != NULL ) { 113 | if ( port == head->port 114 | && strcmp( host, head->host ) == 0 ) { 115 | return head->value; 116 | } 117 | 118 | head = head->next; 119 | } 120 | 121 | return NULL; 122 | } 123 | 124 | void ephashtable_remove( struct ephashtable * self, const char * host, uint16_t port ) 125 | { 126 | if ( self->count == 0 ) { 127 | return; 128 | } 129 | 130 | uint32_t index = _get_hashkey( host, port ) & self->sizemask; 131 | 132 | struct endpoint * prev = NULL; 133 | struct endpoint * head = self->table[index]; 134 | while ( head != NULL ) { 135 | if ( port == head->port 136 | && strcmp( host, head->host ) == 0 ) { 137 | if ( prev != NULL ) { 138 | prev->next = head->next; 139 | } else { 140 | self->table[index] = head->next; 141 | } 142 | 143 | INVOKE( self, 2, head ); 144 | free( head ); --self->count; return; 145 | } 146 | 147 | prev = head; 148 | head = head->next; 149 | } 150 | } 151 | 152 | void * ephashtable_append( struct ephashtable * self, const char * host, uint16_t port ) 153 | { 154 | uint32_t index = _get_hashkey( host, port ) & self->sizemask; 155 | 156 | struct endpoint * entry = (struct endpoint *)calloc( 157 | 1, sizeof( struct endpoint ) + self->objsize ); 158 | assert( entry != NULL && "create struct udpentry failed" ); 159 | 160 | strcpy( entry->host, host ); 161 | if ( self->objsize > 0 ) { 162 | entry->value = entry + 1; 163 | } 164 | entry->port = port; 165 | entry->next = self->table[index]; 166 | ++self->count; 167 | self->table[index] = entry; 168 | INVOKE( self, 1, entry ); 169 | 170 | return entry->value; 171 | } 172 | -------------------------------------------------------------------------------- /src/ephashtable.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef EPHASHTABLE_H 3 | #define EPHASHTABLE_H 4 | 5 | #include 6 | #include 7 | 8 | struct endpoint { 9 | uint16_t port; 10 | char host[64]; 11 | void * value; 12 | struct endpoint * next; 13 | }; 14 | 15 | // 1 - 创建 16 | // 2 - 移除 17 | // 3 - 销毁 18 | typedef void ( *helper_t )( int, struct endpoint * ); 19 | 20 | // ep散列表 21 | struct ephashtable { 22 | struct endpoint ** table; 23 | size_t size; 24 | size_t sizemask; 25 | size_t count; 26 | size_t objsize; 27 | helper_t helper; 28 | }; 29 | 30 | // 创建散列表 31 | struct ephashtable * ephashtable_create( size_t count, size_t size, helper_t helper ); 32 | void ephashtable_destroy( struct ephashtable * self ); 33 | 34 | // 查找 35 | void * ephashtable_find( 36 | struct ephashtable * self, const char * host, uint16_t port ); 37 | 38 | // 删除 39 | void ephashtable_remove( 40 | struct ephashtable * self, const char * host, uint16_t port ); 41 | 42 | // 添加 43 | void * ephashtable_append( 44 | struct ephashtable * self, const char * host, uint16_t port ); 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /src/epoll.c: -------------------------------------------------------------------------------- 1 | 2 | #include "config.h" 3 | 4 | #if defined EVENT_OS_LINUX 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include "utils.h" 19 | #include "event-internal.h" 20 | 21 | // 22 | // epoll关注的是描述符, 而非读写事件 23 | // 为了模拟读写事件, 所以增加了一个事件对 24 | // 25 | struct eventpair { 26 | struct event * evread; 27 | struct event * evwrite; 28 | }; 29 | 30 | struct epoller { 31 | // 管理的所有事件对, 这个是基于描述符的 32 | int32_t npairs; 33 | struct eventpair * evpairs; 34 | 35 | // 管理固定数目的激活事件 36 | int32_t nevents; 37 | struct epoll_event * events; 38 | 39 | // epoll描述符 40 | int32_t epollfd; 41 | }; 42 | 43 | void * epoll_init(); 44 | int32_t epoll_add( void * arg, struct event * ev ); 45 | int32_t epoll_del( void * arg, struct event * ev ); 46 | int32_t epoll_dispatch( struct eventset * sets, void * arg, int32_t tv ); 47 | void epoll_final( void * arg ); 48 | 49 | int32_t epoll_expand( struct epoller * self ); 50 | int32_t epoll_insert( struct epoller * self, int32_t max ); 51 | 52 | const struct eventop epollops = { 53 | epoll_init, 54 | epoll_add, 55 | epoll_del, 56 | epoll_dispatch, 57 | epoll_final 58 | }; 59 | 60 | #define MAX_EPOLL_WAIT 35 * 60 * 1000 61 | 62 | void * epoll_init() 63 | { 64 | int32_t epollfd = -1; 65 | struct epoller * poller = NULL; 66 | 67 | #if defined EVENT_HAVE_EPOLLCREATE1 68 | epollfd = epoll_create1( EPOLL_CLOEXEC ); 69 | #endif 70 | if ( epollfd == -1 ) { 71 | epollfd = epoll_create( 64000 ); // 该参数在新内核中已经取消 72 | if ( epollfd == -1 ) { 73 | return NULL; 74 | } 75 | // CLOSEONEXEC 76 | set_cloexec( epollfd ); 77 | } 78 | 79 | poller = (struct epoller *)malloc( sizeof( struct epoller ) ); 80 | if ( poller == NULL ) { 81 | close( epollfd ); 82 | return NULL; 83 | } 84 | 85 | poller->epollfd = epollfd; 86 | 87 | poller->evpairs = (struct eventpair *)calloc( INIT_EVENTS, sizeof( struct eventpair ) ); 88 | poller->events = (struct epoll_event *)calloc( INIT_EVENTS, sizeof( struct epoll_event ) ); 89 | if ( poller->evpairs == NULL || poller->events == NULL ) { 90 | epoll_final( poller ); 91 | return NULL; 92 | } 93 | 94 | poller->npairs = INIT_EVENTS; 95 | poller->nevents = INIT_EVENTS; 96 | 97 | return poller; 98 | } 99 | 100 | int32_t epoll_expand( struct epoller * self ) 101 | { 102 | int32_t nevents = self->nevents; 103 | struct epoll_event * newevents = NULL; 104 | 105 | nevents <<= 1; 106 | 107 | newevents = (struct epoll_event*)realloc( 108 | self->events, nevents * sizeof( struct epoll_event ) ); 109 | if ( unlikely( newevents == NULL ) ) { 110 | return -1; 111 | } 112 | 113 | self->nevents = nevents; 114 | self->events = newevents; 115 | 116 | return 0; 117 | } 118 | 119 | int32_t epoll_insert( struct epoller * self, int32_t max ) 120 | { 121 | int32_t npairs = 0; 122 | struct eventpair * pairs = NULL; 123 | 124 | npairs = self->npairs; 125 | for ( ; npairs <= max; ) { 126 | npairs <<= 1; 127 | } 128 | 129 | pairs = (struct eventpair *)realloc( 130 | self->evpairs, npairs * sizeof( struct eventpair ) ); 131 | if ( unlikely( pairs == NULL ) ) { 132 | return -1; 133 | } 134 | 135 | self->evpairs = pairs; 136 | memset( pairs + self->npairs, 0, ( npairs - self->npairs ) * sizeof( struct eventpair ) ); 137 | self->npairs = npairs; 138 | 139 | return 0; 140 | } 141 | 142 | int32_t epoll_add( void * arg, struct event * ev ) 143 | { 144 | int32_t fd = 0, rc = -1; 145 | int32_t op = 0, events = 0; 146 | 147 | struct epoll_event epollevent; 148 | struct eventpair * eventpair = NULL; 149 | struct epoller * poller = (struct epoller *)arg; 150 | 151 | fd = event_get_fd( (event_t)ev ); 152 | if ( fd < 0 ) { 153 | return -3; 154 | } else if ( fd >= poller->npairs ) { 155 | if ( epoll_insert( poller, fd ) != 0 ) { 156 | return -1; 157 | } 158 | } 159 | 160 | events = 0; 161 | op = EPOLL_CTL_ADD; 162 | eventpair = &( poller->evpairs[fd] ); 163 | 164 | if ( eventpair->evread != NULL ) { 165 | events |= EPOLLIN; 166 | op = EPOLL_CTL_MOD; 167 | } 168 | if ( eventpair->evwrite != NULL ) { 169 | events |= EPOLLOUT; 170 | op = EPOLL_CTL_MOD; 171 | } 172 | 173 | if ( ev->events & EV_READ ) { 174 | events |= EPOLLIN; 175 | } 176 | if ( ev->events & EV_WRITE ) { 177 | events |= EPOLLOUT; 178 | } 179 | 180 | epollevent.data.u64 = 0; /* avoid valgrind warnning */ 181 | epollevent.data.fd = fd; 182 | epollevent.events = events; 183 | 184 | rc = epoll_ctl( poller->epollfd, op, fd, &epollevent ); 185 | if ( rc == -1 ) { 186 | if ( op == EPOLL_CTL_MOD && errno == ENOENT ) { 187 | // If a MOD operation fails with ENOENT, the fd was probably closed and re-opened. 188 | // We should retry the operation as an ADD. 189 | rc = epoll_ctl( poller->epollfd, EPOLL_CTL_ADD, fd, &epollevent ); 190 | } else if ( op == EPOLL_CTL_ADD && errno == EEXIST ) { 191 | // If an ADD operation fails with EEXIST, 192 | // either the operation was redundant (as with a precautionary add), 193 | // or we ran into a fun kernel bug where using dup*() to duplicate the same file into the same fd gives you the same epitem rather than a fresh one. 194 | // For the second case, we must retry with MOD. 195 | rc = epoll_ctl( poller->epollfd, EPOLL_CTL_MOD, fd, &epollevent ); 196 | } 197 | } 198 | 199 | if ( rc == -1 ) { 200 | return -2; 201 | } 202 | 203 | if ( ev->events & EV_READ ) { 204 | eventpair->evread = ev; 205 | } 206 | if ( ev->events & EV_WRITE ) { 207 | eventpair->evwrite = ev; 208 | } 209 | 210 | return 0; 211 | } 212 | 213 | int32_t epoll_del( void * arg, struct event * ev ) 214 | { 215 | int32_t fd = 0; 216 | int32_t op = 0, events = 0; 217 | int32_t delwrite = 1, delread = 1; 218 | 219 | struct epoll_event epollevent; 220 | struct eventpair * eventpair = NULL; 221 | struct epoller * poller = (struct epoller *)arg; 222 | 223 | fd = event_get_fd( (event_t)ev ); 224 | if ( fd >= poller->npairs ) { 225 | return -1; 226 | } 227 | 228 | // 简单的删除指定的事件 229 | events = 0; 230 | op = EPOLL_CTL_DEL; 231 | eventpair = &( poller->evpairs[fd] ); 232 | 233 | if ( ev->events & EV_READ ) { 234 | events |= EPOLLIN; 235 | } 236 | if ( ev->events & EV_WRITE ) { 237 | events |= EPOLLOUT; 238 | } 239 | 240 | // 删除的同时查看该描述符是否支持了其他事件 241 | // 如有, 则不能直接简单的删除 242 | if ( ( events & ( EPOLLIN | EPOLLOUT ) ) != ( EPOLLIN | EPOLLOUT ) ) { 243 | if ( ( events & EPOLLIN ) && eventpair->evwrite != NULL ) { 244 | // 删除的是读事件, 写事件仍然要监听 245 | // 并且还需要从写事件中查看是否支持了边缘触发模式 246 | delwrite = 0; 247 | events = EPOLLOUT; 248 | op = EPOLL_CTL_MOD; 249 | } else if ( ( events & EPOLLOUT ) && eventpair->evread != NULL ) { 250 | // 删除的是写事件, 读事件仍然要监听 251 | // 并且还需要从读事件中查看是否支持了边缘触发模式 252 | delread = 0; 253 | events = EPOLLIN; 254 | op = EPOLL_CTL_MOD; 255 | } 256 | } 257 | 258 | epollevent.data.u64 = 0; /* avoid valgrind warnning */ 259 | epollevent.data.fd = fd; 260 | epollevent.events = events; 261 | 262 | if ( delread ) { 263 | eventpair->evread = NULL; 264 | } 265 | if ( delwrite ) { 266 | eventpair->evwrite = NULL; 267 | } 268 | 269 | return epoll_ctl( poller->epollfd, op, fd, &epollevent ) == -1 ? -2 : 0; 270 | } 271 | 272 | int32_t epoll_dispatch( struct eventset * sets, void * arg, int32_t tv ) 273 | { 274 | int32_t i, res = -1; 275 | struct epoller * poller = (struct epoller *)arg; 276 | 277 | if ( unlikely( tv > MAX_EPOLL_WAIT ) ) { 278 | tv = MAX_EPOLL_WAIT; 279 | } 280 | 281 | res = epoll_wait( poller->epollfd, poller->events, poller->nevents, tv ); 282 | if ( res == -1 ) { 283 | if ( errno != EINTR ) { 284 | syslog( LOG_WARNING, "%s() epoll_wait() error <%d, %s>", __FUNCTION__, errno, strerror( errno ) ); 285 | return -1; 286 | } 287 | 288 | return 0; 289 | } 290 | 291 | for ( i = 0; i < res; ++i ) { 292 | int32_t fd = poller->events[i].data.fd; 293 | int32_t what = poller->events[i].events; 294 | 295 | struct eventpair * eventpair = NULL; 296 | struct event *evread = NULL, *evwrite = NULL; 297 | 298 | if ( fd < 0 || fd >= poller->npairs ) { 299 | continue; 300 | } 301 | 302 | eventpair = &( poller->evpairs[fd] ); 303 | 304 | if ( what & ( EPOLLHUP | EPOLLERR ) ) { 305 | evread = eventpair->evread; 306 | evwrite = eventpair->evwrite; 307 | } else { 308 | if ( what & ( EPOLLIN | EPOLLPRI ) ) { 309 | evread = eventpair->evread; 310 | } 311 | if ( what & EPOLLOUT ) { 312 | evwrite = eventpair->evwrite; 313 | } 314 | } 315 | 316 | if ( evread ) { 317 | event_active( evread, EV_READ ); 318 | } 319 | if ( evwrite ) { 320 | event_active( evwrite, EV_WRITE ); 321 | } 322 | } 323 | 324 | if ( res == poller->nevents ) { 325 | epoll_expand( poller ); 326 | } 327 | 328 | return res; 329 | } 330 | 331 | void epoll_final( void * arg ) 332 | { 333 | struct epoller * poller = (struct epoller *)arg; 334 | 335 | if ( poller->evpairs ) { 336 | free( poller->evpairs ); 337 | } 338 | if ( poller->events ) { 339 | free( poller->events ); 340 | } 341 | if ( poller->epollfd >= 0 ) { 342 | close( poller->epollfd ); 343 | } 344 | 345 | free( poller ); 346 | } 347 | 348 | #endif 349 | -------------------------------------------------------------------------------- /src/event-internal.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef EVENT_INTERNAL_H 3 | #define EVENT_INTERNAL_H 4 | 5 | #include "queue.h" 6 | #include "event.h" 7 | 8 | // 初始化的事件数, 该框架同样适用于内网服务器 9 | #define INIT_EVENTS 1024 10 | 11 | // 12 | // 事件的状态 13 | // 0000 0000 0000 0000 14 | // 保留 私有 系统 链表 15 | // 16 | // 链表: 1 - 在全局链表中 17 | // 2 - 在定时器中 18 | // 3 - 激活链表中 19 | // 4 - 保留 20 | // 21 | 22 | #define EVSTATUS_INSERTED 0x01 23 | #define EVSTATUS_TIMER 0x02 24 | #define EVSTATUS_ACTIVE 0x04 25 | #define EVSTATUS_INTERNAL 0x10 26 | #define EVSTATUS_INIT 0x20 27 | 28 | #define EVSTATUS_ALL ( 0x0f00 | 0x37 ) 29 | 30 | // 31 | // 32 | // 33 | struct event; 34 | struct eventset; 35 | struct eventop { 36 | void * ( *init )(); 37 | int32_t ( *add )( void *, struct event * ); 38 | int32_t ( *del )( void *, struct event * ); 39 | int32_t ( *dispatch )( struct eventset *, void *, int32_t ); 40 | void ( *final )( void * ); 41 | }; 42 | 43 | // 44 | // 事件 45 | // 46 | struct eventset; 47 | struct event { 48 | int32_t fd; 49 | int16_t events; 50 | 51 | int32_t status; 52 | int32_t results; 53 | 54 | // cb 一定要合法 55 | void * arg; 56 | eventcb_t cb; 57 | 58 | struct eventset * evsets; 59 | 60 | // 定时器的超时时间 61 | int32_t timer_msecs; 62 | 63 | // 事件在定时器数组中的索引 64 | // 删除时, 快速定位到某一个桶 65 | int32_t timer_index; 66 | 67 | // 事件的周期数 68 | int32_t timer_stepcnt; 69 | 70 | TAILQ_ENTRY( event ) timerlink; 71 | TAILQ_ENTRY( event ) eventlink; 72 | TAILQ_ENTRY( event ) activelink; 73 | }; 74 | 75 | TAILQ_HEAD( event_list, event ); 76 | 77 | #define EVENT_TIMEOUT( ev ) ( int32_t )( ( ev )->timer_msecs ) 78 | #define EVENT_TIMERINDEX( ev ) ( int32_t )( ( ev )->timer_index ) 79 | #define EVENT_TIMERSTEP( ev ) ( int32_t )( ( ev )->timer_stepcnt ) 80 | 81 | int32_t event_active( struct event * self, int16_t res ); 82 | 83 | // 84 | // event定时器模块 85 | // 86 | #define TIMER_BUCKET_COUNT 8192 // 必须是2的N次方 87 | 88 | struct evtimer { 89 | int32_t event_count; // 管理的事件个数 90 | int32_t bucket_count; // 桶的个数 91 | int32_t max_precision; // 最大精度, 精确到1毫秒 92 | 93 | int32_t dispatch_refer; // 94 | struct event_list * bucket_array; // 桶的数组 95 | }; 96 | 97 | #define EVTIMER_INDEX( t, c ) ( ( c ) & ( ( t )->bucket_count - 1 ) ) 98 | 99 | struct evtimer * evtimer_create( int32_t max_precision, int32_t bucket_count ); 100 | int32_t evtimer_append( struct evtimer * self, struct event * ev ); 101 | int32_t evtimer_remove( struct evtimer * self, struct event * ev ); 102 | int32_t evtimer_dispatch( struct evtimer * self ); 103 | int32_t evtimer_count( struct evtimer * self ); 104 | int32_t evtimer_clean( struct evtimer * self ); 105 | void evtimer_destroy( struct evtimer * self ); 106 | 107 | // 108 | // 事件集模块 109 | // 110 | 111 | struct eventset { 112 | int32_t timer_precision; 113 | 114 | int64_t expire_time; 115 | struct evtimer * core_timer; 116 | 117 | void * evsets; 118 | struct eventop * evselect; 119 | 120 | struct event_list eventlist; 121 | struct event_list activelist; 122 | }; 123 | 124 | #define EVENTSET_PRECISION( sets ) ( ( (struct eventset *)( sets ) )->timer_precision ) 125 | 126 | #endif 127 | -------------------------------------------------------------------------------- /src/kqueue.c: -------------------------------------------------------------------------------- 1 | 2 | #include "config.h" 3 | 4 | #if defined EVENT_OS_BSD || defined EVENT_OS_MACOS 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include "utils.h" 18 | #include "event-internal.h" 19 | 20 | struct kqueuer { 21 | int32_t nchanges; 22 | int32_t changessize; 23 | struct kevent * changes; 24 | 25 | int32_t nevents; 26 | struct kevent * events; 27 | 28 | int32_t kqueuefd; 29 | }; 30 | 31 | void * kqueue_init(); 32 | int32_t kqueue_add( void * arg, struct event * ev ); 33 | int32_t kqueue_del( void * arg, struct event * ev ); 34 | int32_t kqueue_dispatch( struct eventset * sets, void * arg, int32_t tv ); 35 | void kqueue_final( void * arg ); 36 | 37 | int32_t kqueue_expand( struct kqueuer * self ); 38 | int32_t kqueue_insert( struct kqueuer * self, struct kevent * ev ); 39 | 40 | const struct eventop kqueueops = { 41 | kqueue_init, 42 | kqueue_add, 43 | kqueue_del, 44 | kqueue_dispatch, 45 | kqueue_final 46 | }; 47 | 48 | #define EVSTATUS_X_KQINKERNEL 0x0100 49 | 50 | void * kqueue_init() 51 | { 52 | struct kqueuer * poller = NULL; 53 | 54 | poller = (struct kqueuer *)calloc( 1, sizeof( struct kqueuer ) ); 55 | if ( poller == NULL ) { 56 | return NULL; 57 | } 58 | 59 | poller->kqueuefd = kqueue(); 60 | if ( poller->kqueuefd == -1 ) { 61 | kqueue_final( poller ); 62 | return NULL; 63 | } 64 | 65 | poller->events = (struct kevent *)calloc( INIT_EVENTS, sizeof( struct kevent ) ); 66 | poller->changes = (struct kevent *)calloc( INIT_EVENTS, sizeof( struct kevent ) ); 67 | if ( poller->events == NULL || poller->changes == NULL ) { 68 | kqueue_final( poller ); 69 | return NULL; 70 | } 71 | 72 | poller->nchanges = 0; 73 | poller->nevents = INIT_EVENTS; 74 | poller->changessize = INIT_EVENTS; 75 | 76 | return poller; 77 | } 78 | 79 | int32_t kqueue_expand( struct kqueuer * self ) 80 | { 81 | int32_t nevents = self->nevents; 82 | struct kevent * newevents = NULL; 83 | 84 | nevents <<= 1; 85 | 86 | newevents = (struct kevent *)realloc( 87 | self->events, nevents * sizeof( struct kevent ) ); 88 | if ( unlikely( newevents == NULL ) ) { 89 | return -1; 90 | } 91 | 92 | self->nevents = nevents; 93 | self->events = newevents; 94 | 95 | return 0; 96 | } 97 | 98 | int32_t kqueue_insert( struct kqueuer * self, struct kevent * ev ) 99 | { 100 | int32_t changessize = self->changessize; 101 | 102 | if ( self->nchanges == changessize ) { 103 | struct kevent * newchanges = NULL; 104 | 105 | changessize <<= 1; 106 | 107 | newchanges = (struct kevent *)realloc( 108 | self->changes, changessize * sizeof( struct kevent ) ); 109 | if ( unlikely( newchanges == NULL ) ) { 110 | return -1; 111 | } 112 | 113 | self->changes = newchanges; 114 | self->changessize = changessize; 115 | } 116 | 117 | memcpy( &( self->changes[self->nchanges++] ), ev, sizeof( struct kevent ) ); 118 | 119 | return 0; 120 | } 121 | 122 | int32_t kqueue_add( void * arg, struct event * ev ) 123 | { 124 | struct kevent kev; 125 | struct kqueuer * poller = (struct kqueuer *)arg; 126 | 127 | bzero( &kev, sizeof( kev ) ); 128 | 129 | if ( ev->events & EV_READ ) { 130 | kev.flags = EV_ADD; 131 | kev.udata = (void *)ev; 132 | kev.filter = EVFILT_READ; 133 | kev.ident = event_get_fd( (event_t)ev ); 134 | #ifdef NOTE_EOF 135 | kev.fflags = NOTE_EOF; 136 | #endif 137 | if ( !( ev->events & EV_PERSIST ) ) { 138 | kev.flags |= EV_ONESHOT; 139 | } 140 | 141 | if ( kqueue_insert( poller, &kev ) != 0 ) { 142 | return -1; 143 | } 144 | 145 | ev->status |= EVSTATUS_X_KQINKERNEL; 146 | } 147 | 148 | if ( ev->events & EV_WRITE ) { 149 | kev.flags = EV_ADD; 150 | kev.udata = (void *)ev; 151 | kev.filter = EVFILT_WRITE; 152 | kev.ident = event_get_fd( (event_t)ev ); 153 | if ( !( ev->events & EV_PERSIST ) ) { 154 | kev.flags |= EV_ONESHOT; 155 | } 156 | 157 | if ( kqueue_insert( poller, &kev ) != 0 ) { 158 | return -2; 159 | } 160 | 161 | ev->status |= EVSTATUS_X_KQINKERNEL; 162 | } 163 | 164 | return 0; 165 | } 166 | 167 | int32_t kqueue_del( void * arg, struct event * ev ) 168 | { 169 | struct kevent kev; 170 | struct kqueuer * poller = (struct kqueuer *)arg; 171 | 172 | if ( !( ev->status & EVSTATUS_X_KQINKERNEL ) ) { 173 | // 174 | // 该事件已经不在KQUEUE中了, 直接返回成功 175 | // 176 | return 0; 177 | } 178 | 179 | bzero( &kev, sizeof( kev ) ); 180 | 181 | if ( ev->events & EV_READ ) { 182 | kev.flags = EV_DELETE; 183 | kev.filter = EVFILT_READ; 184 | kev.ident = event_get_fd( (event_t)ev ); 185 | 186 | if ( kqueue_insert( poller, &kev ) != 0 ) { 187 | return -2; 188 | } 189 | 190 | ev->status &= ~EVSTATUS_X_KQINKERNEL; 191 | } 192 | 193 | if ( ev->events & EV_WRITE ) { 194 | kev.flags = EV_DELETE; 195 | kev.filter = EVFILT_WRITE; 196 | kev.ident = event_get_fd( (event_t)ev ); 197 | 198 | if ( kqueue_insert( poller, &kev ) != 0 ) { 199 | return -3; 200 | } 201 | 202 | ev->status &= ~EVSTATUS_X_KQINKERNEL; 203 | } 204 | 205 | return 0; 206 | } 207 | 208 | int32_t kqueue_dispatch( struct eventset * sets, void * arg, int32_t tv ) 209 | { 210 | struct timespec tsp, *ptsp = NULL; 211 | 212 | int32_t res = -1, i = 0; 213 | struct kqueuer * poller = (struct kqueuer *)arg; 214 | 215 | if ( tv >= 0 ) { 216 | // tv - 单位为毫秒 217 | // 必须换算成秒,以及纳秒 218 | tsp.tv_sec = tv / 1000; 219 | tsp.tv_nsec = ( tv % 1000 ) * 1000000; 220 | ptsp = &tsp; 221 | } 222 | 223 | res = kevent( poller->kqueuefd, 224 | poller->changes, poller->nchanges, poller->events, poller->nevents, ptsp ); 225 | poller->nchanges = 0; 226 | 227 | if ( res == -1 ) { 228 | if ( errno != EINTR ) { 229 | return -1; 230 | } 231 | 232 | return 0; 233 | } 234 | 235 | for ( i = 0; i < res; ++i ) { 236 | int32_t which = 0; 237 | struct event * ev = NULL; 238 | 239 | if ( poller->events[i].flags & EV_ERROR ) { 240 | if ( poller->events[i].data == EBADF 241 | || poller->events[i].data == EINVAL 242 | || poller->events[i].data == ENOENT ) { 243 | continue; 244 | } 245 | 246 | errno = (int32_t)poller->events[i].data; 247 | return -2; 248 | } 249 | 250 | if ( poller->events[i].filter == EVFILT_READ ) { 251 | which |= EV_READ; 252 | } else if ( poller->events[i].filter == EVFILT_WRITE ) { 253 | which |= EV_WRITE; 254 | } 255 | 256 | if ( !which ) { 257 | continue; 258 | } 259 | 260 | ev = (struct event *)( poller->events[i].udata ); 261 | if ( !( ev->events & EV_PERSIST ) ) { 262 | ev->status &= ~EVSTATUS_X_KQINKERNEL; 263 | } 264 | 265 | event_active( ev, which ); 266 | } 267 | 268 | if ( res == poller->nevents ) { 269 | kqueue_expand( poller ); 270 | } 271 | 272 | return res; 273 | } 274 | 275 | void kqueue_final( void * arg ) 276 | { 277 | struct kqueuer * poller = (struct kqueuer *)arg; 278 | 279 | if ( poller->kqueuefd >= 0 ) { 280 | close( poller->kqueuefd ); 281 | } 282 | if ( poller->changes ) { 283 | free( poller->changes ); 284 | } 285 | if ( poller->events ) { 286 | free( poller->events ); 287 | } 288 | 289 | free( poller ); 290 | } 291 | 292 | #endif 293 | -------------------------------------------------------------------------------- /src/lock.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef LOCK_H 3 | #define LOCK_H 4 | 5 | #include 6 | 7 | #ifdef USE_ATOMIC 8 | 9 | struct evlock { 10 | int32_t lock; 11 | }; 12 | 13 | static inline void evlock_init( struct evlock * lock ) 14 | { 15 | lock->lock = 0; 16 | } 17 | 18 | static inline void evlock_lock( struct evlock * lock ) 19 | { 20 | for ( ; __sync_lock_test_and_set( &lock->lock, 1 ); ) usleep( 0 ); 21 | } 22 | 23 | static inline void evlock_unlock( struct evlock * lock ) 24 | { 25 | __sync_lock_release( &lock->lock ); 26 | } 27 | 28 | static inline void evlock_destroy( struct evlock * lock ) 29 | {} 30 | 31 | #else 32 | 33 | #include 34 | 35 | struct evlock { 36 | pthread_mutex_t lock; 37 | }; 38 | 39 | static inline void evlock_init( struct evlock * lock ) 40 | { 41 | pthread_mutex_init( &lock->lock, NULL ); 42 | } 43 | 44 | static inline void evlock_lock( struct evlock * lock ) 45 | { 46 | pthread_mutex_lock( &lock->lock ); 47 | } 48 | 49 | static inline void evlock_unlock( struct evlock * lock ) 50 | { 51 | pthread_mutex_unlock( &lock->lock ); 52 | } 53 | 54 | static inline void evlock_destroy( struct evlock * lock ) 55 | { 56 | pthread_mutex_destroy( &lock->lock ); 57 | } 58 | 59 | #endif 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /src/message.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "utils.h" 11 | #include "network-internal.h" 12 | #include "message.h" 13 | 14 | static inline void _align( struct buffer * self ); 15 | static inline size_t _offset( struct buffer * self ); 16 | static inline size_t _left( struct buffer * self ); 17 | static inline int32_t _expand( struct buffer * self, size_t length ); 18 | static inline ssize_t _read_withvector( struct buffer * self, int32_t fd ); 19 | static inline ssize_t _read_withsize( struct buffer * self, int32_t fd, ssize_t nbytes ); 20 | 21 | void _align( struct buffer * self ) 22 | { 23 | memmove( self->orignbuffer, self->buffer, self->length ); 24 | self->buffer = self->orignbuffer; 25 | } 26 | 27 | size_t _offset( struct buffer * self ) 28 | { 29 | return (size_t)( self->buffer - self->orignbuffer ); 30 | } 31 | 32 | size_t _left( struct buffer * self ) 33 | { 34 | return self->capacity - _offset( self ) - self->length; 35 | } 36 | 37 | int32_t _expand( struct buffer * self, size_t length ) 38 | { 39 | size_t offset = _offset( self ); 40 | size_t needlength = offset + self->length + length; 41 | 42 | if ( needlength <= self->capacity ) { 43 | return 0; 44 | } 45 | 46 | if ( self->capacity - self->length >= length ) { 47 | _align( self ); 48 | } else { 49 | void * newbuffer = NULL; 50 | size_t newcapacity = self->capacity; 51 | 52 | if ( newcapacity < MIN_BUFFER_LENGTH ) { 53 | newcapacity = MIN_BUFFER_LENGTH; 54 | } 55 | for ( ; newcapacity < needlength; ) { 56 | newcapacity <<= 1; 57 | } 58 | 59 | if ( self->orignbuffer != self->buffer ) { 60 | _align( self ); 61 | } 62 | 63 | newbuffer = (char *)realloc( self->orignbuffer, newcapacity ); 64 | if ( newbuffer == NULL ) { 65 | return -1; 66 | } 67 | 68 | self->capacity = newcapacity; 69 | self->orignbuffer = self->buffer = (char *)newbuffer; 70 | } 71 | 72 | return 0; 73 | } 74 | 75 | ssize_t _read_withvector( struct buffer * self, int32_t fd ) 76 | { 77 | struct iovec vec[2]; 78 | char extra[MAX_BUFFER_LENGTH]; 79 | 80 | ssize_t nread = 0; 81 | size_t left = _left( self ); 82 | 83 | vec[0].iov_base = self->buffer + self->length; 84 | vec[0].iov_len = left; 85 | vec[1].iov_base = extra; 86 | vec[1].iov_len = sizeof( extra ); 87 | 88 | nread = readv( fd, vec, left < sizeof( extra ) ? 2 : 1 ); 89 | if ( nread > (ssize_t)left ) { 90 | self->length += left; 91 | int32_t rc = buffer_append( self, extra, (size_t)( nread - left ) ); 92 | if ( rc != 0 ) { 93 | return -2; 94 | } 95 | } else if ( nread > 0 ) { 96 | self->length += nread; 97 | } 98 | 99 | return nread; 100 | } 101 | 102 | ssize_t _read_withsize( struct buffer * self, int32_t fd, ssize_t nbytes ) 103 | { 104 | ssize_t nread = -1; 105 | 106 | if ( nbytes == -1 ) { 107 | int32_t rc = ioctl( fd, FIONREAD, &nread ); 108 | if ( rc == 0 && nread > 0 ) { 109 | nbytes = nread; 110 | } else { 111 | nbytes = MAX_BUFFER_LENGTH; 112 | } 113 | } 114 | 115 | if ( _expand( self, nbytes ) != 0 ) { 116 | return -2; 117 | } 118 | 119 | nread = read( fd, self->buffer + self->length, nbytes ); 120 | if ( nread > 0 ) { 121 | self->length += nread; 122 | } 123 | 124 | return nread; 125 | } 126 | 127 | int32_t buffer_init( struct buffer * self ) 128 | { 129 | self->length = 0; 130 | self->capacity = 0; 131 | self->buffer = NULL; 132 | self->orignbuffer = NULL; 133 | return 0; 134 | } 135 | 136 | int32_t buffer_set( struct buffer * self, char * buf, size_t length ) 137 | { 138 | if ( self->orignbuffer ) { 139 | free( self->orignbuffer ); 140 | } 141 | 142 | self->buffer = self->orignbuffer = buf; 143 | self->length = self->capacity = length; 144 | 145 | return 0; 146 | } 147 | 148 | int32_t buffer_erase( struct buffer * self, size_t length ) 149 | { 150 | if ( self->length <= length ) { 151 | self->length = 0; 152 | self->buffer = self->orignbuffer; 153 | } else { 154 | self->buffer += length; 155 | self->length -= length; 156 | } 157 | 158 | return 0; 159 | } 160 | 161 | int32_t buffer_append( struct buffer * self, const char * buf, size_t length ) 162 | { 163 | size_t offset = _offset( self ); 164 | size_t needlength = offset + self->length + length; 165 | 166 | if ( needlength > self->capacity ) { 167 | if ( _expand( self, length ) == -1 ) { 168 | return -1; 169 | } 170 | } 171 | 172 | memcpy( self->buffer + self->length, buf, length ); 173 | self->length += length; 174 | 175 | return 0; 176 | } 177 | 178 | int32_t buffer_append2( struct buffer * self, struct buffer * buffer ) 179 | { 180 | return buffer_append( self, buffer_data( buffer ), buffer_length( buffer ) ); 181 | } 182 | 183 | size_t buffer_take( struct buffer * self, char * buf, size_t length ) 184 | { 185 | length = ( length > self->length ? self->length : length ); 186 | 187 | memcpy( buf, self->buffer, length ); 188 | buffer_erase( self, length ); 189 | 190 | return length; 191 | } 192 | 193 | int32_t buffer_reserve( struct buffer * self, size_t length ) 194 | { 195 | return _expand( self, length ); 196 | } 197 | 198 | void buffer_swap( struct buffer * buf1, struct buffer * buf2 ) 199 | { 200 | struct buffer tmpbuf = *buf1; 201 | 202 | *buf1 = *buf2; 203 | *buf2 = tmpbuf; 204 | } 205 | 206 | ssize_t buffer_read( struct buffer * self, int32_t fd, ssize_t nbytes ) 207 | { 208 | // 尽量读取SOCKET中的数据 209 | if ( likely( nbytes == 0 ) ) { 210 | return _read_withvector( self, fd ); 211 | } 212 | 213 | // -1和>0 都是读取定长的数据到BUFF中 214 | return _read_withsize( self, fd, nbytes ); 215 | } 216 | 217 | ssize_t buffer_receive( struct buffer * self, int32_t fd, struct sockaddr_storage * addr ) 218 | { 219 | ssize_t nread = MAX_BUFFER_LENGTH; 220 | socklen_t addrlen = sizeof( *addr ); 221 | 222 | // Using FIONREAD, it is impossible to distinguish the case 223 | // where no datagram is pending from the case where the next pending datagram contains zero bytes of data 224 | 225 | if ( _expand( self, nread ) != 0 ) { 226 | return -2; 227 | } 228 | 229 | bzero( addr, addrlen ); 230 | nread = recvfrom( fd, 231 | self->buffer + self->length, 232 | nread, MSG_DONTWAIT, (struct sockaddr *)addr, &addrlen ); 233 | if ( nread > 0 ) { 234 | self->length += nread; 235 | } 236 | 237 | return nread; 238 | } 239 | 240 | // ----------------------------------------------------------------------------- 241 | // ----------------------------------------------------------------------------- 242 | // ----------------------------------------------------------------------------- 243 | 244 | struct message * message_create() 245 | { 246 | struct message * self = (struct message *)malloc( sizeof( struct message ) ); 247 | if ( self != NULL ) { 248 | self->nfailure = 0; 249 | self->nsuccess = 0; 250 | self->tolist = NULL; 251 | buffer_init( &self->buffer ); 252 | } 253 | 254 | return self; 255 | } 256 | 257 | void message_destroy( struct message * self ) 258 | { 259 | if ( self->tolist ) { 260 | sidlist_destroy( self->tolist ); 261 | self->tolist = NULL; 262 | } 263 | 264 | // if ( self->failurelist ) 265 | // { 266 | // sidlist_destroy( self->failurelist ); 267 | // self->failurelist = NULL; 268 | // } 269 | 270 | buffer_clear( &self->buffer ); 271 | free( self ); 272 | } 273 | 274 | int32_t message_add_receiver( struct message * self, sid_t id ) 275 | { 276 | if ( self->tolist == NULL ) { 277 | self->tolist = sidlist_create( 8 ); 278 | if ( unlikely( self->tolist == NULL ) ) { 279 | return -1; 280 | } 281 | } 282 | 283 | return sidlist_add( self->tolist, id ); 284 | } 285 | 286 | int32_t message_add_receivers( struct message * self, sid_t * ids, uint32_t count ) 287 | { 288 | if ( self->tolist == NULL ) { 289 | self->tolist = sidlist_create( count ); 290 | if ( unlikely( self->tolist == NULL ) ) { 291 | return -1; 292 | } 293 | } 294 | 295 | return sidlist_adds( self->tolist, ids, count ); 296 | } 297 | 298 | int32_t message_set_receivers( struct message * self, struct sidlist * ids ) 299 | { 300 | if ( self->tolist ) { 301 | sidlist_destroy( self->tolist ); 302 | } 303 | 304 | self->tolist = ids; 305 | 306 | return 0; 307 | } 308 | 309 | int32_t message_reserve_receivers( struct message * self, uint32_t count ) 310 | { 311 | if ( self->tolist != NULL ) { 312 | count += sidlist_count( self->tolist ); 313 | } 314 | 315 | struct sidlist * tolist = sidlist_create( count ); 316 | if ( tolist == NULL ) { 317 | return -1; 318 | } 319 | 320 | if ( self->tolist != NULL ) { 321 | sidlist_append( tolist, self->tolist ); 322 | sidlist_destroy( self->tolist ); 323 | } 324 | 325 | self->tolist = tolist; 326 | return 0; 327 | } 328 | 329 | // int32_t message_add_failure( struct message * self, sid_t id ) 330 | // { 331 | // if ( self->failurelist == NULL ) 332 | // { 333 | // self->failurelist = sidlist_create(8); 334 | // if ( self->failurelist == NULL ) 335 | // { 336 | // return -1; 337 | // } 338 | // } 339 | // 340 | // return sidlist_add( self->failurelist, id ); 341 | // } 342 | // 343 | // int32_t message_is_complete( struct message * self ) 344 | // { 345 | // int32_t totalcount = self->tolist ? sidlist_count(self->tolist) : 0; 346 | // int32_t failurecount = self->failurelist ? sidlist_count( self->failurelist ) : 0; 347 | // 348 | // return (totalcount == self->nsuccess - failurecount); 349 | // } 350 | 351 | int32_t message_is_complete( struct message * self ) 352 | { 353 | int32_t totalcount = self->tolist ? sidlist_count( self->tolist ) : 0; 354 | return ( totalcount == self->nsuccess + self->nfailure ); 355 | } 356 | -------------------------------------------------------------------------------- /src/message.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef MESSAGE_H 3 | #define MESSAGE_H 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | #include 10 | 11 | #include "utils.h" 12 | 13 | #define MIN_BUFFER_LENGTH 128 14 | #define MAX_BUFFER_LENGTH 65536 15 | 16 | // 17 | // 缓冲区 18 | // 19 | struct buffer { 20 | size_t length; // 有效数据段的长度 21 | size_t capacity; // 内存块的总长度 22 | 23 | char * buffer; // 有效数据段 24 | char * orignbuffer; // 原始数据段 25 | }; 26 | 27 | int32_t buffer_init( struct buffer * self ); 28 | #define buffer_clear( self ) buffer_set( ( self ), NULL, 0 ) 29 | 30 | // 设置缓冲区(优先释放原先的内存) 31 | // 速度快, 不存在内存copy, buf一定是malloc()出来的内存地址 32 | int32_t buffer_set( struct buffer * self, char * buf, size_t length ); 33 | 34 | // 获取网络缓冲区得大小和数据 35 | #define buffer_data( self ) ( self )->buffer 36 | #define buffer_length( self ) ( self )->length 37 | 38 | // 39 | int32_t buffer_erase( struct buffer * self, size_t length ); 40 | int32_t buffer_append( struct buffer * self, const char * buf, size_t length ); 41 | int32_t buffer_append2( struct buffer * self, struct buffer * buffer ); 42 | size_t buffer_take( struct buffer * self, char * buf, size_t length ); 43 | int32_t buffer_reserve( struct buffer * self, size_t length ); 44 | 45 | // 两个缓冲区相互交换 46 | void buffer_swap( struct buffer * buf1, struct buffer * buf2 ); 47 | 48 | // nbytes = -1 : 将SOCKET的数据全部读取到BUFF中 49 | // nbytes = 0 : 尽量读取数据到BUFF中(最多一次性读取2*MAX_BUFFER_LENGTH-1) 50 | // nbytes > 0 : 读取指定长度的数据到BUFF中 51 | // -1, 系统调用read()返回出错; -2, 返回expand()失败 52 | ssize_t buffer_read( struct buffer * self, int32_t fd, ssize_t nbytes ); 53 | ssize_t buffer_receive( struct buffer * self, int32_t fd, struct sockaddr_storage * addr ); 54 | 55 | // 56 | // 缓冲区池 57 | // 58 | 59 | // TODO: 是否有必要写一个对象池来管理所有缓冲区的分配与释放 60 | 61 | // 62 | // 消息 63 | // 64 | struct message { 65 | int32_t nsuccess; 66 | int32_t nfailure; 67 | 68 | struct sidlist * tolist; 69 | // struct sidlist * failurelist; 70 | 71 | struct buffer buffer; 72 | }; 73 | 74 | // 创建/销毁 消息 75 | struct message * message_create(); 76 | void message_destroy( struct message * self ); 77 | 78 | // 增加消息的接收者 79 | // 设置消息的接收列表 80 | int32_t message_add_receiver( struct message * self, sid_t id ); 81 | int32_t message_add_receivers( struct message * self, sid_t * ids, uint32_t count ); 82 | int32_t message_set_receivers( struct message * self, struct sidlist * ids ); 83 | int32_t message_reserve_receivers( struct message * self, uint32_t count ); 84 | 85 | // 增加消息计数器 86 | // int32_t message_add_failure( struct message * self, sid_t id ); 87 | #define message_add_failure( self, id ) ++( ( self )->nfailure ) 88 | #define message_add_success( self ) ++( ( self )->nsuccess ) 89 | 90 | // 添加/设置 消息的数据 91 | #define message_set_buffer( self, buf, nbytes ) buffer_set( &( ( self )->buffer ), ( buf ), ( nbytes ) ) 92 | #define message_add_buffer( self, buf, nbytes ) buffer_append( &( ( self )->buffer ), ( buf ), ( nbytes ) ) 93 | 94 | // 消息是否完全发送 95 | int32_t message_is_complete( struct message * self ); 96 | 97 | // 获取消息数据的长度以及内容 98 | #define message_get_buffer( self ) buffer_data( &( ( self )->buffer ) ) 99 | #define message_get_length( self ) buffer_length( &( ( self )->buffer ) ) 100 | 101 | #ifdef __cplusplus 102 | } 103 | #endif 104 | 105 | #endif 106 | -------------------------------------------------------------------------------- /src/network-internal.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef NETWORK_INTERNAL_H 3 | #define NETWORK_INTERNAL_H 4 | 5 | #include 6 | #include "queue.h" 7 | #include "message.h" 8 | #include "threads.h" 9 | 10 | // 是否安全的终止会话 11 | #define SAFE_SHUTDOWN 1 12 | 13 | // 发送队列的默认大小 14 | #define DEFAULT_SENDQUEUE_SIZE 128 15 | 16 | // 关闭前最大等待时间,默认10s 17 | #define MAX_SECONDS_WAIT_FOR_SHUTDOWN ( 10 * 1000 ) 18 | 19 | // 尝试重连的间隔时间,默认为200ms 20 | #define TRY_RECONNECT_INTERVAL 200 21 | 22 | // UDP发送接收缓冲区设置 23 | #define SEND_BUFFER_SIZE 4194304 // 4M 24 | #define RECV_BUFFER_SIZE 4194304 // 4M 25 | 26 | enum { 27 | eIOStatus_Running = 1, // 运行 28 | eIOStatus_Stopped = 2, // 停止 29 | }; 30 | 31 | // 任务类型 32 | enum { 33 | eIOTaskType_Invalid = 0, 34 | eIOTaskType_Listen = 1, 35 | eIOTaskType_Assign = 2, 36 | eIOTaskType_Connect = 3, 37 | eIOTaskType_Associate = 4, 38 | eIOTaskType_Send = 5, 39 | eIOTaskType_Broadcast = 6, 40 | eIOTaskType_Shutdown = 7, 41 | eIOTaskType_Shutdowns = 8, 42 | eIOTaskType_Broadcast2 = 9, 43 | eIOTaskType_Invoke = 10, 44 | eIOTaskType_Perform = 11, 45 | }; 46 | 47 | // 网络服务错误码定义 48 | enum { 49 | eIOError_OutMemory = 0x00010001, 50 | eIOError_ConnectStatus = 0x00010002, // 非法的连接状态 51 | eIOError_ConflictSid = 0x00010003, // 冲突的SID 52 | eIOError_InBufferFull = 0x00010004, // 缓冲区满了 53 | eIOError_ReadFailure = 0x00010005, // read()失败 54 | eIOError_PeerShutdown = 0x00010006, // 对端关闭了连接 55 | eIOError_WriteFailure = 0x00010007, // write()失败 56 | eIOError_ConnectFailure = 0x00010008, // 连接失败 57 | eIOError_Timeout = 0x00010009, // 连接超时了 58 | eIOError_SocketInvalid = 0x0001000A, // read()失败, Socket非法 59 | eIOError_InBufferInvalid = 0x0001000B, // read()失败, 接收缓冲区非法 60 | eIOError_ReadIOError = 0x0001000C, // read()失败, IO错误 61 | eIOError_ReadInvalid = 0x0001000D, // read()失败, EINVAL 62 | eIOError_SendQueueLimit = 0x0001000E, // 发送队列过大 63 | }; 64 | 65 | // 网络层 66 | struct iolayer { 67 | // 网络层状态 68 | uint8_t status; 69 | // 基础配置 70 | uint8_t nthreads; 71 | uint32_t nclients; 72 | uint32_t roundrobin; // 轮询负载均衡 73 | 74 | // 网络线程组 75 | iothreads_t threads; 76 | // 会话管理器 77 | void ** managers; 78 | 79 | // 数据改造接口 80 | void * context; 81 | transformer_t transform; 82 | }; 83 | 84 | struct acceptor; 85 | struct transfer { 86 | int32_t fd; 87 | struct sockaddr_storage addr; 88 | event_t event; 89 | struct buffer buffer; 90 | struct acceptor * acceptor; 91 | }; 92 | 93 | // 接收器 94 | struct endpoint; 95 | struct ephashtable; 96 | struct acceptor { 97 | int32_t fd; 98 | uint8_t type; 99 | uint8_t index; 100 | options_t options; 101 | 102 | // 接收事件 103 | event_t event; 104 | evsets_t evsets; 105 | 106 | // 绑定的地址以及监听的端口号 107 | char * host; 108 | uint16_t port; 109 | 110 | // 逻辑 111 | acceptor_t cb; 112 | void * context; 113 | 114 | // 空闲描述符 115 | int32_t idlefd; 116 | // 通信层句柄 117 | struct iolayer * parent; 118 | 119 | // KCP管理器 120 | struct ephashtable * eptable; 121 | int32_t ntransfer; 122 | struct transfer * transferlist; 123 | 124 | // 便于回收资源 125 | STAILQ_ENTRY( acceptor ) linker; 126 | }; 127 | 128 | // 连接器 129 | struct connector { 130 | int32_t fd; 131 | uint8_t index; 132 | 133 | // 连接事件 134 | event_t event; 135 | evsets_t evsets; 136 | 137 | // 连接服务器的地址和端口号 138 | char * host; 139 | uint16_t port; 140 | 141 | // 逻辑 142 | connector_t cb; 143 | void * context; 144 | 145 | // 通信层句柄 146 | struct iolayer * parent; 147 | 148 | // 便于回收资源 149 | uint8_t state; 150 | STAILQ_ENTRY( connector ) linker; 151 | }; 152 | 153 | // 关联器 154 | struct associater { 155 | int32_t fd; 156 | uint8_t index; 157 | void * privdata; 158 | 159 | // 连接事件 160 | event_t event; 161 | evsets_t evsets; 162 | 163 | // 逻辑 164 | void * context; 165 | reattacher_t reattach; 166 | associator_t cb; 167 | 168 | // 通信句柄 169 | struct iolayer * parent; 170 | 171 | // 便于回收资源 172 | uint8_t state; 173 | STAILQ_ENTRY( associater ) linker; 174 | }; 175 | 176 | // udp入口 177 | struct session; 178 | struct udpentry { 179 | struct session * session; 180 | struct ephashtable * hashtable; 181 | }; 182 | 183 | // 184 | // NOTICE: 网络任务的最大长度不超过56 185 | // 186 | 187 | // NOTICE: task_assign长度已经达到40bytes 188 | struct task_assign { 189 | int32_t fd; 190 | uint8_t type; 191 | uint16_t port; 192 | char * host; 193 | struct acceptor * acceptor; 194 | struct transfer * transfer; 195 | }; 196 | 197 | struct task_send { 198 | sid_t id; // 8bytes 199 | char * buf; // 8bytes 200 | size_t nbytes; // 8bytes 201 | int32_t isfree; // 4bytes 202 | }; 203 | 204 | struct task_invoke { 205 | void * task; 206 | taskexecutor_t perform; 207 | }; 208 | 209 | struct task_perform { 210 | sid_t id; 211 | int32_t type; 212 | void * task; 213 | int32_t interval; 214 | taskrecycler_t recycle; 215 | }; 216 | 217 | // 描述符分发策略 218 | // 分发到IO线程后会分配到唯一的会话ID 219 | #define DISPATCH_POLICY( layer, seq ) ( ( seq ) % ( ( layer )->nthreads ) ) 220 | 221 | // socket选项 222 | int32_t iolayer_udp_option( int32_t fd ); 223 | int32_t iolayer_server_option( int32_t fd ); 224 | int32_t iolayer_client_option( int32_t fd ); 225 | 226 | // 分配一个会话 227 | struct session * iolayer_alloc_session( struct iolayer * self, int32_t key, uint8_t index ); 228 | 229 | // 销毁接收器 230 | void iolayer_free_acceptor( struct acceptor * acceptor ); 231 | // 销毁连接器 232 | void iolayer_free_connector( struct connector * connector ); 233 | // 销毁关联器 234 | void iolayer_free_associater( struct associater * associater ); 235 | 236 | // 处理EMFILE 237 | void iolayer_accept_fdlimits( struct acceptor * acceptor ); 238 | 239 | // 给当前线程分发一个会话 240 | int32_t iolayer_assign_session( struct iolayer * self, uint8_t acceptidx, uint8_t index, struct task_assign * task ); 241 | 242 | #endif 243 | -------------------------------------------------------------------------------- /src/session.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef SESSION_H 3 | #define SESSION_H 4 | 5 | /* 6 | * session 会话 7 | * 一个TCP连接的基本单元 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | #include "event.h" 14 | #include "network.h" 15 | 16 | #include "utils.h" 17 | #include "message.h" 18 | 19 | // 64位SID的构成 20 | // |XXXXXX |XX |XXXXXXXX | 21 | // |RES |INDEX |SEQ | 22 | // |24 |8 |32 | 23 | 24 | #define SID_MASK 0x000000ffffffffffULL 25 | #define SEQ_MASK 0x00000000ffffffffULL 26 | #define INDEX_MASK 0x000000ff00000000ULL 27 | 28 | #define SID_SEQ( sid ) ( (sid)&SEQ_MASK ) 29 | #define SID_INDEX( sid ) ( ( ( (sid)&INDEX_MASK ) >> 32 ) - 1 ) 30 | 31 | #define SESSION_READING 0x01 // 等待读事件 32 | #define SESSION_WRITING 0x02 // 等待写事件, [连接, 重连, 发送] 33 | #define SESSION_KEEPALIVING 0x04 // 等待保活事件 34 | #define SESSION_SHUTDOWNING 0x08 // 正在终止中..., 被逻辑层终止的会话 35 | #define SESSION_EXITING 0x10 // 等待退出, 数据全部发送完毕后, 即可终止 36 | #define SESSION_SCHEDULING 0x20 // UDP会话正在调度 37 | 38 | enum SessionType { 39 | eSessionType_Accept = 1, // Accept会话 40 | eSessionType_Connect = 2, // Connect会话 41 | eSessionType_Associate = 3, // Associate会话 42 | eSessionType_Shared = 4, // 共享会话 43 | }; 44 | 45 | struct session; 46 | struct session_setting { 47 | int32_t persist_mode; 48 | int32_t timeout_msecs; 49 | int32_t keepalive_msecs; 50 | int32_t max_inbuffer_len; 51 | int32_t sendqueue_limit; 52 | ssize_t ( *transmit )( struct session * s ); 53 | ssize_t ( *send )( struct session * s, char * buf, size_t nbytes ); 54 | }; 55 | 56 | // 定时任务 57 | struct schedule_task { 58 | int32_t type; 59 | void * task; 60 | int32_t interval; 61 | taskrecycler_t recycle; 62 | event_t evschedule; 63 | struct session * session; 64 | SLIST_ENTRY( schedule_task ) tasklink; 65 | }; 66 | SLIST_HEAD( tasklist, schedule_task ); 67 | 68 | struct driver; 69 | QUEUE_HEAD( sendqueue, struct message * ); 70 | QUEUE_PROTOTYPE( sendqueue, struct message * ) 71 | 72 | struct session { 73 | sid_t id; 74 | 75 | int32_t fd; 76 | int8_t type; 77 | int8_t status; 78 | char * host; 79 | uint16_t port; 80 | 81 | // 读写以及超时事件 82 | event_t evread; 83 | event_t evwrite; 84 | event_t evkeepalive; 85 | 86 | // 事件集和管理器 87 | evsets_t evsets; 88 | void * iolayer; 89 | void * context; 90 | ioservice_t service; 91 | struct session_manager * manager; 92 | 93 | // udp驱动 94 | struct driver * driver; 95 | 96 | // 关联的第三方会话 97 | void * privdata; // 私有数据 98 | reattacher_t reattach; // 99 | 100 | // 接收缓冲区 101 | struct buffer inbuffer; 102 | 103 | // 定时任务列表 104 | struct tasklist tasklist; 105 | 106 | // 发送队列以及消息偏移量 107 | size_t msgoffset; 108 | struct sendqueue sendqueue; 109 | 110 | // 会话的设置 111 | struct session_setting setting; 112 | 113 | // 回收链表 114 | STAILQ_ENTRY( session ) recyclelink; 115 | }; 116 | 117 | // 会话开始 118 | int32_t session_start( struct session * self, int8_t type, int32_t fd, evsets_t sets ); 119 | 120 | // 会话是否支持重连 121 | int8_t session_is_reattch( struct session * self ); 122 | 123 | // 124 | void session_set_iolayer( struct session * self, void * iolayer ); 125 | void session_set_driver( struct session * self, struct driver * driver ); 126 | void session_set_endpoint( struct session * self, char * host, uint16_t port ); 127 | void session_copy_endpoint( struct session * self, const char * host, uint16_t port ); 128 | // 设置第三方的重连函数 129 | void session_set_reattach( struct session * self, reattacher_t reattach, void * data ); 130 | 131 | // 发送队列的长度 132 | #define session_sendqueue_count( self ) QUEUE_COUNT( sendqueue )( &( ( self )->sendqueue ) ) 133 | #define session_sendqueue_append( self, msg ) QUEUE_PUSH( sendqueue )( &( ( self )->sendqueue ), &( msg ) ) 134 | #define session_sendqueue_shrink( self, size ) QUEUE_SHRINK( sendqueue )( &( ( self )->sendqueue ), ( size ) ) 135 | 136 | // 发送队列交换 137 | void session_sendqueue_take( struct session * self, struct sendqueue * q ); 138 | // 发送队列合并 139 | void session_sendqueue_merge( struct session * self, struct sendqueue * q ); 140 | 141 | // 发送数据 142 | ssize_t session_send( struct session * self, char * buf, size_t nbytes ); 143 | // 发送消息 144 | ssize_t session_sendmessage( struct session * self, struct message * message ); 145 | 146 | // 会话注册/反注册网络事件 147 | void session_add_event( struct session * self, int16_t ev ); 148 | void session_del_event( struct session * self, int16_t ev ); 149 | // 重新注册网络事件 150 | void session_readd_event( struct session * self, int16_t ev ); 151 | 152 | // 开始发送心跳 153 | int32_t session_start_keepalive( struct session * self ); 154 | 155 | // 重连远程服务器 156 | int32_t session_start_reconnect( struct session * self ); 157 | 158 | // 启动定时任务 159 | int32_t session_cancel_task( struct session * self, struct schedule_task * task ); 160 | int32_t session_schedule_task( struct session * self, int32_t type, void * t, int32_t interval, taskrecycler_t recycle ); 161 | 162 | // 设置被终止的标志 163 | #define session_close( self ) ( ( self )->status |= SESSION_SHUTDOWNING ) 164 | #define session_call_shutdown( self, way ) ( ( self )->service.shutdown( ( self )->context, ( way ) ) ) 165 | 166 | // 尝试终止会话 167 | // 该API会尽量把发送队列中的数据发送出去 168 | // libevlite安全终止会话的核心模块 169 | int32_t session_shutdown( struct session * self ); 170 | 171 | // 会话结束 172 | int32_t session_end( struct session * self, sid_t id, int8_t recycle ); 173 | 174 | // ----------------------------------------------------------------------------- 175 | // ----------------------------------------------------------------------------- 176 | // ----------------------------------------------------------------------------- 177 | 178 | struct hashtable; 179 | STAILQ_HEAD( sessionlist, session ); 180 | 181 | struct session_manager { 182 | uint8_t index; 183 | uint32_t autoseq; // 自增的序号 184 | 185 | struct hashtable * table; 186 | uint32_t recyclesize; // 回收个数 187 | struct sessionlist recyclelist; // 回收队列 188 | }; 189 | 190 | // 创建会话管理器 191 | // index - 会话管理器索引号 192 | // count - 会话管理器中管理多少个会话 193 | struct session_manager * session_manager_create( uint8_t index, uint32_t size ); 194 | 195 | // 获取会话个数 196 | uint32_t session_manager_count( struct session_manager * self ); 197 | 198 | // 分配一个会话 199 | struct session * session_manager_alloc( struct session_manager * self ); 200 | 201 | // 从会话管理器中取出一个会话 202 | struct session * session_manager_get( struct session_manager * self, sid_t id ); 203 | 204 | // 遍历 205 | int32_t session_manager_foreach( struct session_manager * self, 206 | int32_t ( *func )( void *, struct session * ), void * context ); 207 | 208 | // 从会话管理器中移出会话 209 | int32_t session_manager_remove( struct session_manager * self, struct session * session ); 210 | 211 | // 回收会话 212 | void session_manager_recycle( struct session_manager * self, struct session * session ); 213 | 214 | // 销毁会话管理器 215 | void session_manager_destroy( struct session_manager * self ); 216 | 217 | #endif 218 | -------------------------------------------------------------------------------- /src/threads-internal.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef THREADS_INTERNAL_H 3 | #define THREADS_INTERNAL_H 4 | 5 | #include "threads.h" 6 | #include "network-internal.h" 7 | 8 | // 队列默认大小 9 | // 这个选项需要测试期间不断调整以适应场景的需要 10 | #define MSGQUEUE_DEFAULT_SIZE 4096 11 | 12 | // 线程默认栈大小 13 | #define THREAD_DEFAULT_STACK_SIZE ( 8 * 1024 ) 14 | 15 | // 16 | // 网络线程 17 | // 18 | 19 | STAILQ_HEAD( acceptorlist, acceptor ); 20 | STAILQ_HEAD( connectorlist, connector ); 21 | STAILQ_HEAD( associaterlist, associater ); 22 | 23 | struct iothread { 24 | uint8_t index; 25 | pthread_t id; 26 | 27 | evsets_t sets; 28 | void * parent; 29 | void * context; // 上下文 30 | 31 | event_t cmdevent; 32 | struct msgqueue * queue; 33 | char padding[8]; 34 | 35 | // 回收列表 36 | struct acceptorlist acceptorlist; 37 | struct connectorlist connectorlist; 38 | struct associaterlist associaterlist; 39 | }; 40 | 41 | int32_t iothread_start( struct iothread * self, uint8_t index, iothreads_t parent ); 42 | int32_t iothread_post( struct iothread * self, 43 | int16_t type, int16_t utype, void * task, uint8_t size ); 44 | int32_t iothread_stop( struct iothread * self ); 45 | 46 | // 47 | // 网络线程组 48 | // 49 | 50 | struct iothreads { 51 | struct iothread * threads; 52 | 53 | // 任务处理器 54 | void * context; 55 | processor_t processor; 56 | 57 | uint8_t nthreads; 58 | uint8_t runflags; 59 | int32_t precision; // 时间精度 60 | uint8_t immediately; // 是否立刻通知IO线程 61 | 62 | uint8_t nrunthreads; 63 | pthread_cond_t cond; 64 | pthread_mutex_t lock; 65 | }; 66 | 67 | int8_t iothreads_get_index( iothreads_t self ); 68 | struct acceptorlist * iothreads_get_acceptlist( iothreads_t self, uint8_t index ); 69 | struct connectorlist * iothreads_get_connectlist( iothreads_t self, uint8_t index ); 70 | struct associaterlist * iothreads_get_associatelist( iothreads_t self, uint8_t index ); 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /src/timer.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include "event-internal.h" 7 | 8 | struct evtimer * evtimer_create( int32_t max_precision, int32_t bucket_count ) 9 | { 10 | struct evtimer * t = NULL; 11 | 12 | t = (struct evtimer *)malloc( sizeof( struct evtimer ) ); 13 | if ( t ) { 14 | t->event_count = 0; 15 | t->dispatch_refer = 0; 16 | t->bucket_count = bucket_count; 17 | t->max_precision = max_precision; 18 | 19 | t->bucket_array = (struct event_list *)calloc( 20 | bucket_count, sizeof( struct event_list ) ); 21 | if ( t->bucket_array == NULL ) { 22 | free( t ); 23 | t = NULL; 24 | } else { 25 | for ( int32_t i = 0; i < bucket_count; ++i ) { 26 | TAILQ_INIT( &( t->bucket_array[i] ) ); 27 | } 28 | } 29 | } 30 | 31 | return t; 32 | } 33 | 34 | int32_t evtimer_append( struct evtimer * self, struct event * ev ) 35 | { 36 | int32_t tv = EVENT_TIMEOUT( ev ); 37 | int32_t index = self->dispatch_refer - 1; 38 | 39 | if ( tv < 0 ) { 40 | return -1; 41 | } 42 | 43 | // tv至少比self->max_precision大 44 | tv = tv < self->max_precision ? self->max_precision : tv; 45 | 46 | // 把桶的索引号写入事件句柄中, 便于查找以及删除 47 | // 如果定时器超时时间过长, 设定其定时器周期数 48 | index += tv / self->max_precision; 49 | index = EVTIMER_INDEX( self, index ); 50 | 51 | // 52 | ev->timer_index = index; 53 | ev->timer_stepcnt = tv / ( self->max_precision * self->bucket_count ); 54 | if ( tv % ( self->max_precision * self->bucket_count ) != 0 ) { 55 | ev->timer_stepcnt += 1; 56 | } 57 | 58 | ++self->event_count; 59 | TAILQ_INSERT_TAIL( &( self->bucket_array[index] ), ev, timerlink ); 60 | 61 | return 0; 62 | } 63 | 64 | int32_t evtimer_remove( struct evtimer * self, struct event * ev ) 65 | { 66 | // 根据句柄中的索引号, 快速定位桶 67 | int32_t index = EVENT_TIMERINDEX( ev ); 68 | 69 | if ( index < 0 || index >= self->bucket_count ) { 70 | return -1; 71 | } 72 | 73 | ev->timer_index = -1; 74 | ev->timer_stepcnt = -1; 75 | 76 | --self->event_count; 77 | TAILQ_REMOVE( &( self->bucket_array[index] ), ev, timerlink ); 78 | 79 | return 0; 80 | } 81 | 82 | int32_t evtimer_dispatch( struct evtimer * self ) 83 | { 84 | int32_t index = 0; 85 | int32_t rc = 0, done = 0; 86 | 87 | struct event * laster = NULL; 88 | struct event_list * head = NULL; 89 | 90 | index = self->dispatch_refer++; 91 | head = &( self->bucket_array[EVTIMER_INDEX( self, index )] ); 92 | 93 | // 该桶中没有定时的事件 94 | if ( TAILQ_EMPTY( head ) ) { 95 | return 0; 96 | } 97 | 98 | // 遍历超时事件链表 99 | laster = TAILQ_LAST( head, event_list ); 100 | for ( ; !done; ) { 101 | int32_t step = 0; 102 | struct event * ev = TAILQ_FIRST( head ); 103 | 104 | // 由于某些事件的超时时间过长 105 | // 所以还是需要继续添加到事件链表中的 106 | // 必须判断当前节点是否是链表的尾部元素 107 | if ( ev == laster ) { 108 | done = 1; 109 | } 110 | 111 | // 获取步长 112 | --ev->timer_stepcnt; 113 | step = EVENT_TIMERSTEP( ev ); 114 | 115 | if ( step > 0 ) { 116 | // 未超时 117 | TAILQ_REMOVE( head, ev, timerlink ); 118 | TAILQ_INSERT_TAIL( head, ev, timerlink ); 119 | } else { 120 | // 超时 or 出错 121 | 122 | // 删除事件 123 | evtimer_remove( self, ev ); 124 | ev->status &= ~EVSTATUS_TIMER; 125 | 126 | // 超时了, 127 | // 从队列中删除, 并添加到激活队列中 128 | if ( step == 0 ) { 129 | // 计数器 130 | ++rc; 131 | event_active( ev, EV_TIMEOUT ); 132 | } else { 133 | // 出错, 暂且记日志吧 134 | syslog( LOG_WARNING, "%s() evtimer dispatch error <%p>", __FUNCTION__, ev ); 135 | } 136 | } 137 | } 138 | 139 | return rc; 140 | } 141 | 142 | int32_t evtimer_count( struct evtimer * self ) 143 | { 144 | return self->event_count; 145 | } 146 | 147 | int32_t evtimer_clean( struct evtimer * self ) 148 | { 149 | int32_t rc = 0; 150 | 151 | for ( int32_t i = 0; i < self->bucket_count; ++i ) { 152 | struct event * ev = NULL; 153 | struct event_list * head = &( self->bucket_array[i] ); 154 | 155 | for ( ev = TAILQ_FIRST( head ); ev; ) { 156 | struct event * next = TAILQ_NEXT( ev, timerlink ); 157 | 158 | evsets_del( event_get_sets( (event_t)ev ), (event_t)ev ); 159 | 160 | ++rc; 161 | ev = next; 162 | --self->event_count; 163 | } 164 | } 165 | 166 | self->dispatch_refer = 0; 167 | 168 | return rc; 169 | } 170 | 171 | void evtimer_destroy( struct evtimer * self ) 172 | { 173 | if ( self->bucket_array ) { 174 | free( self->bucket_array ); 175 | } 176 | 177 | free( self ); 178 | } 179 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef UTILS_H 3 | #define UTILS_H 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | /* 10 | * 工具算法模块 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "lock.h" 24 | #include "queue.h" 25 | #include "config.h" 26 | #include "network.h" 27 | 28 | // 分支预测 29 | #define likely( x ) __builtin_expect( ( x ), 1 ) 30 | #define unlikely( x ) __builtin_expect( ( x ), 0 ) 31 | 32 | #define MAX( a, b ) ( ( a ) > ( b ) ? ( a ) : ( b ) ) 33 | #define MIN( a, b ) ( ( a ) < ( b ) ? ( a ) : ( b ) ) 34 | 35 | // 36 | // 系统相关的操作 37 | // 38 | 39 | // 时间函数, 返回毫秒数/微妙数 40 | int64_t milliseconds(); 41 | int64_t microseconds(); 42 | 43 | // 获取线程ID 44 | #if defined EVENT_OS_LINUX 45 | pid_t threadid(); 46 | #endif 47 | 48 | // socket基本操作 49 | int32_t is_ipv6only( int32_t fd ); 50 | int32_t is_connected( int32_t fd ); 51 | int32_t get_domain( int32_t fd ); 52 | int32_t set_cloexec( int32_t fd ); 53 | int32_t set_non_block( int32_t fd ); 54 | int32_t unix_connect( const char * path, int32_t ( *options )( int32_t ) ); 55 | int32_t unix_listen( const char * path, int32_t ( *options )( int32_t ) ); 56 | int32_t tcp_accept( int32_t fd, char * remotehost, uint16_t * remoteport ); 57 | int32_t tcp_listen( const char * host, uint16_t port, int32_t ( *options )( int32_t ) ); 58 | int32_t tcp_connect( const char * host, uint16_t port, int32_t ( *options )( int32_t ) ); 59 | int32_t udp_bind( const char * host, uint16_t port, int32_t ( *options )( int32_t ), struct sockaddr_storage * addr ); 60 | int32_t udp_connect( struct sockaddr_storage * localaddr, 61 | struct sockaddr_storage * remoteaddr, int32_t ( *options )( int32_t ) ); 62 | int32_t parse_endpoint( struct sockaddr_storage * addr, char * host, uint16_t * port ); 63 | void convert_endpoint( struct sockaddr_storage * addr, int32_t type, const char * host, uint16_t port ); 64 | 65 | // 66 | // 基础算法类 67 | // 68 | 69 | uint32_t getpower( uint32_t size ); 70 | uint32_t nextpow2( uint32_t size ); 71 | 72 | // 73 | // sidlist 74 | // 75 | struct sidlist { 76 | uint32_t count; 77 | uint32_t size; 78 | 79 | sid_t * entries; 80 | }; 81 | 82 | struct sidlist * sidlist_create( uint32_t size ); 83 | #define sidlist_count( self ) ( ( self )->count ) 84 | sid_t sidlist_get( struct sidlist * self, int32_t index ); 85 | int32_t sidlist_add( struct sidlist * self, sid_t id ); 86 | int32_t sidlist_adds( struct sidlist * self, sid_t * ids, uint32_t count ); 87 | #define sidlist_append( self, list ) sidlist_adds( ( self ), ( list )->entries, ( list )->count ) 88 | sid_t sidlist_del( struct sidlist * self, int32_t index ); 89 | void sidlist_destroy( struct sidlist * self ); 90 | 91 | // 92 | // 消息队列 93 | // 94 | 95 | // 任务类型 96 | enum { 97 | eTaskType_Null = 0, // 空任务 98 | eTaskType_User = 1, // 用户任务 99 | eTaskType_Data = 2, // 数据任务 100 | }; 101 | 102 | // 任务填充长度 103 | #if __SIZEOF_POINTER__ == 4 104 | #define TASK_PADDING_SIZE 60 105 | #elif __SIZEOF_POINTER__ == 8 106 | #define TASK_PADDING_SIZE 56 107 | #else 108 | #error "No way to define bits" 109 | #endif 110 | 111 | // 任务数据 112 | struct task { 113 | int16_t type; // 2bytes 114 | int16_t utype; // 2bytes 115 | union { 116 | void * taskdata; 117 | char data[TASK_PADDING_SIZE]; 118 | }; 119 | }; 120 | 121 | QUEUE_PADDING_HEAD( taskqueue, struct task ); 122 | QUEUE_PROTOTYPE( taskqueue, struct task ) 123 | 124 | // 125 | // 消息队列 126 | // 线程安全的消息队列, 有通知的功能 127 | // 128 | struct msgqueue { 129 | struct taskqueue queue; 130 | int32_t popfd; 131 | int32_t pushfd; 132 | 133 | struct evlock lock; 134 | }; 135 | 136 | // 创建消息队列 137 | struct msgqueue * msgqueue_create( uint32_t size ); 138 | 139 | // 生产者发送任务 140 | // isnotify - 是否需要通知消费者 141 | int32_t msgqueue_push( struct msgqueue * self, struct task * task, uint8_t isnotify ); 142 | 143 | // 消费者从消息队列中取一定量的任务 144 | int32_t msgqueue_pop( struct msgqueue * self, struct task * task ); 145 | int32_t msgqueue_pops( struct msgqueue * self, struct task * tasks, uint32_t count ); 146 | 147 | // 交换 148 | int32_t msgqueue_swap( struct msgqueue * self, struct taskqueue * queue ); 149 | 150 | // 消息队列的长度 151 | uint32_t msgqueue_count( struct msgqueue * self ); 152 | 153 | // 消费者管道fd 154 | int32_t msgqueue_popfd( struct msgqueue * self ); 155 | 156 | // 销毁消息队列 157 | int32_t msgqueue_destroy( struct msgqueue * self ); 158 | 159 | #ifdef __cplusplus 160 | } 161 | #endif 162 | 163 | #endif 164 | -------------------------------------------------------------------------------- /test/chatroom.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CHATROOM_H 3 | #define CHATROOM_H 4 | 5 | #include 6 | 7 | #define CHATROOM_MESSAGE_SIZE 100 8 | 9 | #pragma pack(1) 10 | 11 | struct CSHead 12 | { 13 | uint16_t msgid; 14 | uint16_t length; 15 | }; 16 | 17 | #pragma pack() 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /test/chatroom_client.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "utils.h" 9 | #include "event.h" 10 | #include "message.h" 11 | 12 | #include "chatroom.h" 13 | 14 | bool g_Running; 15 | bool g_Waiting; 16 | 17 | char * g_Host; 18 | uint16_t g_Port; 19 | int32_t g_ClientsCount; 20 | 21 | class ChatRoomClient 22 | { 23 | public : 24 | ChatRoomClient() 25 | : m_Fd(0), 26 | m_EventSets(NULL), 27 | m_ReadEvent(NULL), 28 | m_TimerEvent(NULL), 29 | m_SendInterval(0), 30 | m_SendBytes(0), 31 | m_RecvBytes(0) 32 | { 33 | buffer_init( &m_InBuffer ); 34 | } 35 | 36 | ~ChatRoomClient() 37 | { 38 | buffer_clear( &m_InBuffer ); 39 | } 40 | 41 | enum 42 | { 43 | e_SendIntervalMicroSeconds = 400, // 每个100ms发送一个请求 44 | }; 45 | 46 | public : 47 | bool connect( const char * host, uint16_t port ) 48 | { 49 | struct sockaddr_in addr; 50 | 51 | memset( &addr, 0, sizeof(addr) ); 52 | addr.sin_family = AF_INET; 53 | addr.sin_addr.s_addr = inet_addr( host ); 54 | addr.sin_port = htons( port ); 55 | 56 | m_Fd = socket( AF_INET, SOCK_STREAM, 0 ); 57 | if ( m_Fd < 0 ) 58 | { 59 | return false; 60 | } 61 | if ( ::connect(m_Fd, (struct sockaddr *)&addr, sizeof(addr)) != 0 ) 62 | { 63 | close( m_Fd ); 64 | m_Fd = -1; 65 | return false; 66 | } 67 | set_non_block( m_Fd ); 68 | return true; 69 | } 70 | 71 | void start( evsets_t sets ) 72 | { 73 | m_EventSets = sets; 74 | 75 | m_ReadEvent = event_create(); 76 | m_TimerEvent = event_create(); 77 | 78 | event_set( m_ReadEvent, m_Fd, EV_READ|EV_PERSIST ); 79 | event_set_callback( m_ReadEvent, ChatRoomClient::onRead, this ); 80 | evsets_add( sets, m_ReadEvent, -1 ); 81 | 82 | addTimer(); 83 | } 84 | 85 | ssize_t recv() 86 | { 87 | return buffer_read( &m_InBuffer, m_Fd, -1 ); 88 | } 89 | 90 | ssize_t send() 91 | { 92 | uint16_t length = CHATROOM_MESSAGE_SIZE+sizeof(CSHead); 93 | std::string msg( length, 0 ); 94 | 95 | CSHead * head = (CSHead *)msg.data(); 96 | head->msgid = (rand()%1000) > 50 ? 1 : 2; 97 | head->length = length; 98 | msg.resize( length ); 99 | 100 | m_SendBytes += length; 101 | 102 | return ::write( m_Fd, msg.data(), msg.size() ); 103 | } 104 | 105 | void shutdown() 106 | { 107 | if ( m_Fd ) 108 | { 109 | close( m_Fd ); 110 | m_Fd = -1; 111 | } 112 | 113 | if ( m_ReadEvent ) 114 | { 115 | evsets_del( event_get_sets(m_ReadEvent), m_ReadEvent ); 116 | event_destroy( m_ReadEvent ); 117 | } 118 | 119 | if ( m_TimerEvent ) 120 | { 121 | evsets_del( event_get_sets(m_TimerEvent), m_TimerEvent ); 122 | event_destroy( m_TimerEvent ); 123 | } 124 | } 125 | 126 | void onProcess() 127 | { 128 | ssize_t nprocess = 0; 129 | 130 | const char * buf = buffer_data( &m_InBuffer ); 131 | size_t nbytes = buffer_length( &m_InBuffer ); 132 | 133 | while ( 1 ) 134 | { 135 | size_t nleft = nbytes - nprocess; 136 | const char * buffer = buf + nprocess; 137 | 138 | if ( nleft < sizeof(struct CSHead) ) 139 | { 140 | break; 141 | } 142 | 143 | CSHead * head = (CSHead *)buffer; 144 | 145 | if ( nleft < head->length ) 146 | { 147 | break; 148 | } 149 | 150 | assert( head->length == CHATROOM_MESSAGE_SIZE+sizeof(CSHead) ); 151 | assert( head->msgid == 1 || head->msgid == 2 ); 152 | 153 | nprocess += head->length; 154 | m_RecvBytes += head->length; 155 | } 156 | 157 | buffer_erase( &m_InBuffer, nprocess ); 158 | } 159 | 160 | void addTimer() 161 | { 162 | uint32_t seconds = e_SendIntervalMicroSeconds; 163 | 164 | event_set( m_TimerEvent, -1, 0 ); 165 | event_set_callback( m_TimerEvent, ChatRoomClient::onTimer, this ); 166 | evsets_add( m_EventSets, m_TimerEvent, seconds ); 167 | } 168 | 169 | uint64_t getSendBytes() const { return m_SendBytes; } 170 | uint64_t getRecvBytes() const { return m_RecvBytes; } 171 | 172 | public : 173 | static void onRead( int32_t fd, int16_t ev, void * arg ); 174 | static void onTimer( int32_t fd, int16_t ev, void * arg ); 175 | 176 | private : 177 | int32_t m_Fd; 178 | evsets_t m_EventSets; 179 | 180 | event_t m_ReadEvent; 181 | event_t m_TimerEvent; 182 | struct buffer m_InBuffer; 183 | 184 | uint32_t m_SendInterval; 185 | 186 | uint64_t m_SendBytes; 187 | uint64_t m_RecvBytes; 188 | }; 189 | 190 | void ChatRoomClient::onRead( int32_t fd, int16_t ev, void * arg ) 191 | { 192 | ChatRoomClient * client = (ChatRoomClient *)arg; 193 | 194 | if ( ev & EV_READ ) 195 | { 196 | ssize_t nread = client->recv(); 197 | if ( nread <= 0 ) 198 | { 199 | if ( nread == 0 200 | || ( nread < 0 && errno == EAGAIN ) ) 201 | { 202 | client->shutdown(); 203 | } 204 | return; 205 | } 206 | 207 | client->onProcess(); 208 | } 209 | } 210 | 211 | void ChatRoomClient::onTimer( int32_t fd, int16_t ev, void * arg ) 212 | { 213 | ChatRoomClient * client = (ChatRoomClient *)arg; 214 | client->send(); 215 | client->addTimer(); 216 | } 217 | 218 | // ------------------------------------------------------------------------------- 219 | // ------------------------------------------------------------------------------- 220 | // ------------------------------------------------------------------------------- 221 | 222 | void signal_handle( int32_t signo ) 223 | { 224 | g_Running = false; 225 | g_Waiting = false; 226 | } 227 | 228 | void start_clients( ChatRoomClient ** clients, evsets_t sets ) 229 | { 230 | for ( int32_t i = 0; i < g_ClientsCount; ++i ) 231 | { 232 | ChatRoomClient * client = new ChatRoomClient; 233 | clients[ i ] = client; 234 | 235 | if ( client ) 236 | { 237 | if ( !client->connect(g_Host, g_Port) ) 238 | { 239 | delete client; 240 | continue; 241 | } 242 | 243 | client->start( sets ); 244 | } 245 | } 246 | } 247 | 248 | void statics( ChatRoomClient ** clients, uint64_t mseconds ) 249 | { 250 | uint64_t total_recvbytes = 0; 251 | uint64_t total_sendbytes = 0; 252 | 253 | for ( int32_t i = 0; i < g_ClientsCount; ++i ) 254 | { 255 | total_recvbytes += clients[i]->getRecvBytes(); 256 | total_sendbytes += clients[i]->getSendBytes(); 257 | } 258 | 259 | printf("TotalRecvBytes: %lld, TotalSendBytes: %lld\n", total_recvbytes, total_sendbytes); 260 | printf("MSeconds: %lld, RecvSpeed: %6.3fKBytes/s, SendSpeed: %6.3fKBytes/s \n", mseconds, (double)total_recvbytes/(double)mseconds, (double)total_sendbytes/(double)mseconds ); 261 | } 262 | 263 | int main( int argc, char ** argv ) 264 | { 265 | if ( argc != 4 ) 266 | { 267 | printf("chatroom_client [host] [port] [clients] .\n"); 268 | return -1; 269 | } 270 | 271 | g_Host = strdup( argv[1] ); 272 | g_Port = atoi( argv[2] ); 273 | g_ClientsCount = atoi( argv[3] ); 274 | 275 | signal( SIGINT, signal_handle ); 276 | signal( SIGPIPE, SIG_IGN ); 277 | srand( (int32_t)time(NULL) ); 278 | 279 | // 280 | ChatRoomClient ** clients = (ChatRoomClient **)malloc( sizeof(ChatRoomClient *) * g_ClientsCount ); 281 | 282 | assert( clients != NULL && "malloc() failed " ); 283 | 284 | evsets_t sets = evsets_create(8); 285 | assert( sets != NULL && "evsets_create() failed" ); 286 | 287 | // 连接远程服务器 288 | start_clients( clients, sets ); 289 | 290 | // 运行 291 | g_Running = true; 292 | g_Waiting = true; 293 | uint64_t nStartTime, nEndTime; 294 | 295 | nStartTime = milliseconds(); 296 | while ( g_Running ) 297 | { 298 | evsets_dispatch( sets ); 299 | } 300 | nEndTime = milliseconds(); 301 | 302 | printf("chatroom_client stopped .\n"); 303 | g_Waiting = true; 304 | while ( g_Waiting ) 305 | { 306 | pause(); 307 | } 308 | 309 | // 统计 310 | statics( clients, nEndTime-nStartTime ); 311 | 312 | free( clients ); 313 | return 0; 314 | } 315 | -------------------------------------------------------------------------------- /test/chatroom_server.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "io.h" 15 | #include "chatroom.h" 16 | 17 | class TASK 18 | { 19 | public : 20 | TASK() {} 21 | ~TASK() {} 22 | 23 | static void * clone( void * t ) 24 | { 25 | TASK * task = (TASK *)t; 26 | TASK * newtask = new TASK(); 27 | newtask->data = task->data; 28 | 29 | return newtask; 30 | } 31 | 32 | static void perform( void * iocontext, void * t ) 33 | { 34 | TASK * task = (TASK *)t; 35 | printf( "%p : %d \n", iocontext, task->data ); 36 | delete task; 37 | } 38 | 39 | public : 40 | int32_t data; 41 | }; 42 | 43 | class CChatRoomService; 44 | class CChatRoomSession : public IIOSession 45 | { 46 | public: 47 | CChatRoomSession(); 48 | virtual ~CChatRoomSession(); 49 | 50 | public : 51 | virtual int32_t onStart(); 52 | virtual ssize_t onProcess( const char * buf, size_t nbytes ); 53 | virtual void onShutdown( int32_t way ); 54 | virtual int32_t onPerform( int32_t type, void * task ); 55 | 56 | public : 57 | void setService( CChatRoomService * s ); 58 | 59 | private : 60 | CChatRoomService * m_Service; 61 | }; 62 | 63 | class CChatRoomService : public IIOService 64 | { 65 | public : 66 | CChatRoomService( uint8_t nthreads, uint32_t nclients ); 67 | virtual ~CChatRoomService(); 68 | 69 | public : 70 | virtual IIOSession * onAccept( sid_t id, NetType type, uint16_t listenport, const char * host, uint16_t port ); 71 | 72 | public : 73 | bool init( const char * host, uint16_t port ); 74 | void run(); 75 | bool post( sid_t id, CSHead * header ); 76 | 77 | private : 78 | struct Task 79 | { 80 | uint16_t msgid; 81 | uint16_t length; 82 | sid_t sid; 83 | char * message; 84 | 85 | Task() 86 | { 87 | msgid = 0; 88 | length = 0; 89 | sid = 0; 90 | message = NULL; 91 | } 92 | }; 93 | 94 | uint32_t m_UniqueID; 95 | bool m_Perform; 96 | pthread_mutex_t m_TaskLock; 97 | std::deque m_TaskQueue; 98 | std::vector m_SessionMap; 99 | }; 100 | 101 | // ------------------------------------------------------------------------------- 102 | // ------------------------------------------------------------------------------- 103 | // ------------------------------------------------------------------------------- 104 | 105 | CChatRoomSession::CChatRoomSession() 106 | { 107 | } 108 | 109 | CChatRoomSession::~CChatRoomSession() 110 | { 111 | } 112 | 113 | int32_t CChatRoomSession::onStart() 114 | { 115 | CSHead head; 116 | head.msgid = 0; 117 | head.length = sizeof(head); 118 | m_Service->post( id(), &head ); 119 | 120 | enablePersist(); 121 | 122 | return 0; 123 | } 124 | 125 | ssize_t CChatRoomSession::onProcess( const char * buf, size_t nbytes ) 126 | { 127 | ssize_t nprocess = 0; 128 | 129 | while ( 1 ) 130 | { 131 | size_t nleft = nbytes - nprocess; 132 | const char * buffer = buf + nprocess; 133 | 134 | if ( nleft < sizeof(struct CSHead) ) 135 | { 136 | break; 137 | } 138 | 139 | CSHead * head = (CSHead *)buffer; 140 | 141 | assert( head->length == CHATROOM_MESSAGE_SIZE+sizeof(CSHead)); 142 | assert( head->msgid == 1 || head->msgid == 2 ); 143 | 144 | if ( nleft < head->length ) 145 | { 146 | break; 147 | } 148 | 149 | m_Service->post( id(), head ); 150 | nprocess += head->length; 151 | } 152 | 153 | return nprocess; 154 | } 155 | 156 | void CChatRoomSession::onShutdown( int32_t way ) 157 | { 158 | CSHead head; 159 | head.msgid = 3; 160 | head.length = sizeof(head); 161 | m_Service->post( id(), &head ); 162 | } 163 | 164 | int32_t CChatRoomSession::onPerform( int32_t type, void * task ) 165 | { 166 | printf( "Session:%lu, ID:%lu\n", id(), (uint64_t)(task) ); 167 | return 0; 168 | } 169 | 170 | void CChatRoomSession::setService( CChatRoomService * s ) 171 | { 172 | m_Service = s; 173 | } 174 | 175 | 176 | CChatRoomService::CChatRoomService( uint8_t nthreads, uint32_t nclients ) 177 | : IIOService( nthreads, nclients ), 178 | m_UniqueID( 0 ), 179 | m_Perform( false ) 180 | { 181 | m_SessionMap.reserve( nclients ); 182 | pthread_mutex_init( &m_TaskLock, NULL ); 183 | } 184 | 185 | CChatRoomService::~CChatRoomService() 186 | { 187 | pthread_mutex_destroy( &m_TaskLock ); 188 | } 189 | 190 | IIOSession * CChatRoomService::onAccept( sid_t id, NetType type, uint16_t listenport, const char * host, uint16_t port ) 191 | { 192 | CChatRoomSession * session = new CChatRoomSession; 193 | if ( session ) 194 | { 195 | session->setService( this ); 196 | } 197 | 198 | return session; 199 | } 200 | 201 | bool CChatRoomService::init( const char * host, uint16_t port ) 202 | { 203 | bool rc = false; 204 | 205 | start(); 206 | 207 | rc = listen( NetType::TCP, host, port ); 208 | if ( !rc ) 209 | { 210 | printf( "CChatRoomService::listen(%s::%d) failed .\n", host, port ); 211 | return false; 212 | } 213 | 214 | return true; 215 | } 216 | 217 | void CChatRoomService::run() 218 | { 219 | std::deque swapqueue; 220 | pthread_mutex_lock( &m_TaskLock ); 221 | std::swap( swapqueue, m_TaskQueue ); 222 | pthread_mutex_unlock( &m_TaskLock ); 223 | 224 | for ( std::deque::iterator it = swapqueue.begin(); it != swapqueue.end(); ++it ) 225 | { 226 | Task * task = &(*it); 227 | 228 | switch ( task->msgid ) 229 | { 230 | case 0 : 231 | { 232 | m_SessionMap.push_back( task->sid ); 233 | } 234 | break; 235 | 236 | case 1 : 237 | { 238 | uint16_t length = task->length+sizeof(CSHead); 239 | #if 0 240 | std::string buffer( length, 0 ); 241 | CSHead * head = (CSHead *)buffer.data(); 242 | head->msgid = task->msgid; 243 | head->length = length; 244 | memcpy( head+1, task->message, task->length ); 245 | buffer.resize( length ); 246 | 247 | send( task->sid, buffer ); 248 | #endif 249 | char * buffer = (char *)malloc( length ); 250 | CSHead * head = (CSHead *)buffer; 251 | head->msgid = task->msgid; 252 | head->length = length; 253 | memcpy( head+1, task->message, task->length ); 254 | send( task->sid, buffer, length, true ); 255 | 256 | free( task->message ); 257 | } 258 | break; 259 | 260 | case 2 : 261 | { 262 | uint16_t length = task->length+sizeof(CSHead); 263 | std::string buffer( length, 0 ); 264 | 265 | CSHead * head = (CSHead *)buffer.data(); 266 | head->msgid = task->msgid; 267 | head->length = length; 268 | memcpy( head+1, task->message, task->length ); 269 | buffer.resize( length ); 270 | 271 | broadcast( /*m_SessionMap, */buffer ); 272 | free( task->message ); 273 | } 274 | break; 275 | 276 | case 3 : 277 | { 278 | std::vector::iterator it; 279 | 280 | it = std::find( m_SessionMap.begin(), m_SessionMap.end(), task->sid ); 281 | if ( it != m_SessionMap.end() ) 282 | { 283 | m_SessionMap.erase( it ); 284 | } 285 | } 286 | break; 287 | } 288 | } 289 | } 290 | 291 | bool CChatRoomService::post( sid_t id, CSHead * header ) 292 | { 293 | Task task; 294 | task.sid = id; 295 | task.msgid = header->msgid; 296 | 297 | if ( header->length - sizeof(CSHead) > 0 ) 298 | { 299 | task.length = header->length - sizeof(CSHead); 300 | 301 | assert( task.length == CHATROOM_MESSAGE_SIZE ); 302 | 303 | task.message = (char *)malloc( task.length+1 ); 304 | memcpy( task.message, header+1, task.length ); 305 | task.message[task.length] = 0; 306 | } 307 | 308 | pthread_mutex_lock( &m_TaskLock ); 309 | m_TaskQueue.push_back( task ); 310 | pthread_mutex_unlock( &m_TaskLock ); 311 | 312 | return true; 313 | } 314 | 315 | // ------------------------------------------------------------------------------- 316 | // ------------------------------------------------------------------------------- 317 | // ------------------------------------------------------------------------------- 318 | 319 | bool g_Running; 320 | 321 | void signal_handle( int32_t signo ) 322 | { 323 | g_Running = false; 324 | } 325 | 326 | int main( int argc, char ** argv ) 327 | { 328 | if ( argc != 5 ) 329 | { 330 | printf("chatroom_server [host] [port] [threads] [clients] \n"); 331 | return -1; 332 | } 333 | 334 | const char * host = argv[1]; 335 | uint16_t port = atoi(argv[2]); 336 | uint8_t nthreads = atoi(argv[3]); 337 | uint32_t nclients = atoi(argv[4]); 338 | 339 | signal( SIGPIPE, SIG_IGN ); 340 | signal( SIGINT, signal_handle ); 341 | 342 | CChatRoomService service( nthreads, nclients ); 343 | 344 | if ( !service.init(host, port ) ) 345 | { 346 | return -2; 347 | } 348 | 349 | g_Running = true; 350 | while ( g_Running ) 351 | { 352 | service.run(); 353 | usleep(1000); 354 | } 355 | 356 | service.halt(); 357 | service.stop(); 358 | return 0; 359 | } 360 | -------------------------------------------------------------------------------- /test/echoclient.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "io.h" 8 | 9 | // 10 | // 回显服务实例 11 | // 12 | 13 | class CEchoSession : public IIOSession 14 | { 15 | public : 16 | CEchoSession() 17 | {} 18 | 19 | virtual ~CEchoSession() 20 | {} 21 | 22 | public : 23 | virtual int32_t onStart() { 24 | printf( "%lld, %s::%d .\n", id(), host().c_str(), port() ); 25 | return 0; 26 | } 27 | 28 | virtual ssize_t onProcess( const char * buf, size_t nbytes ) 29 | { 30 | std::string line( buf, buf+nbytes ); 31 | printf("%s", line.c_str() ); 32 | fflush(stdout); 33 | 34 | return nbytes; 35 | } 36 | 37 | virtual int32_t onError( int32_t result ) 38 | { 39 | printf("the Session (SID=%ld) : error, code=0x%08x \n", id(), result ); 40 | return 0; 41 | } 42 | 43 | virtual int32_t onShutdown() 44 | { 45 | return 0; 46 | } 47 | }; 48 | 49 | class CEchoService : public IIOService 50 | { 51 | public : 52 | 53 | CEchoService( uint8_t nthreads, uint32_t nclients ) 54 | : IIOService( nthreads, nclients ) 55 | { 56 | } 57 | 58 | virtual ~CEchoService() 59 | { 60 | } 61 | 62 | public : 63 | 64 | virtual IIOSession * onConnectSucceed( sid_t id, const char * host, uint16_t port ) 65 | { 66 | m_ClientSid = id; 67 | return new CEchoSession(); 68 | } 69 | 70 | public : 71 | 72 | int32_t send2( const std::string & buffer ) 73 | { 74 | return send( m_ClientSid, buffer ); 75 | } 76 | 77 | private : 78 | 79 | sid_t m_ClientSid; 80 | }; 81 | 82 | // ------------------------------------------------------------------------------- 83 | // ------------------------------------------------------------------------------- 84 | // ------------------------------------------------------------------------------- 85 | 86 | bool g_Running; 87 | 88 | void signal_handle( int32_t signo ) 89 | { 90 | g_Running = false; 91 | } 92 | 93 | int main( int argc, char ** argv ) 94 | { 95 | std::string line; 96 | CEchoService * service = NULL; 97 | 98 | if ( argc != 3 ) 99 | { 100 | printf("Usage: echoclient [host] [port] \n"); 101 | return -1; 102 | } 103 | 104 | signal( SIGPIPE, SIG_IGN ); 105 | signal( SIGINT, signal_handle ); 106 | 107 | service = new CEchoService( 1, 200 ); 108 | if ( service == NULL ) 109 | { 110 | return -1; 111 | } 112 | 113 | service->start(); 114 | 115 | if ( !service->connect( argv[1], atoi(argv[2]), 10 ) ) 116 | { 117 | printf("service start failed \n"); 118 | delete service; 119 | 120 | return -2; 121 | } 122 | 123 | printf( "echoclient connect succeed .\n" ); 124 | 125 | g_Running = true; 126 | 127 | while ( g_Running ) 128 | { 129 | int ch = getc(stdin); 130 | line.push_back( (char)ch ); 131 | 132 | if ( ch == '\n' ) 133 | { 134 | if ( strcmp( line.c_str(), "quit\n" ) == 0 ) 135 | { 136 | g_Running = false; 137 | continue; 138 | } 139 | 140 | service->send2( line ); 141 | line.clear(); 142 | } 143 | fflush(stdin); 144 | } 145 | 146 | printf("EchoClient stoping ...\n"); 147 | service->stop(); 148 | delete service; 149 | 150 | return 0; 151 | } 152 | -------------------------------------------------------------------------------- /test/echoserver.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include "io.h" 7 | 8 | #define DEBUG_OUTPUT 1 9 | 10 | 11 | // 12 | // 回显服务实例 13 | // 14 | 15 | class CEchoSession : public IIOSession 16 | { 17 | public : 18 | CEchoSession() 19 | {} 20 | 21 | virtual ~CEchoSession() 22 | {} 23 | 24 | public : 25 | 26 | virtual int32_t onStart() 27 | { 28 | #if DEBUG_OUTPUT 29 | printf("the Session (SID=%ld) Start (%s::%d) \n", id(), host().c_str(), port() ); 30 | #endif 31 | // setTimeout( 60 ); 32 | return 0; 33 | } 34 | 35 | virtual int32_t onProcess( const char * buf, uint32_t nbytes ) 36 | { 37 | send( buf, nbytes ); 38 | return nbytes; 39 | } 40 | 41 | virtual int32_t onTimeout() 42 | { 43 | #if DEBUG_OUTPUT 44 | printf("the Session (SID=%ld) : timeout \n", id() ); 45 | #endif 46 | return -1; 47 | } 48 | 49 | virtual int32_t onError( int32_t result ) 50 | { 51 | #if DEBUG_OUTPUT 52 | printf("the Session (SID=%ld) : error, code=0x%08x \n", id(), result ); 53 | #endif 54 | return -1; 55 | } 56 | 57 | virtual int32_t onShutdown() 58 | { 59 | return 0; 60 | } 61 | }; 62 | 63 | class CEchoService : public IIOService 64 | { 65 | public : 66 | 67 | CEchoService( uint8_t nthreads, uint32_t nclients ) 68 | : IIOService( nthreads, nclients ) 69 | { 70 | } 71 | 72 | virtual ~CEchoService() 73 | { 74 | } 75 | 76 | public : 77 | 78 | IIOSession * onAccept( sid_t id, uint16_t listenport, const char * host, uint16_t port ) 79 | { 80 | printf( "%lld, %s::%d .\n", id, host, port ); 81 | return new CEchoSession; 82 | } 83 | }; 84 | 85 | // ------------------------------------------------------------------------------- 86 | // ------------------------------------------------------------------------------- 87 | // ------------------------------------------------------------------------------- 88 | 89 | bool g_Running; 90 | 91 | void signal_handle( int32_t signo ) 92 | { 93 | g_Running = false; 94 | } 95 | 96 | int main( int argc, char ** argv ) 97 | { 98 | CEchoService * service = NULL; 99 | 100 | if ( argc != 3 ) 101 | { 102 | printf("Usage: echoserver [host] [port] \n"); 103 | return -1; 104 | } 105 | 106 | signal( SIGPIPE, SIG_IGN ); 107 | signal( SIGINT, signal_handle ); 108 | 109 | service = new CEchoService( 4, 200000 ); 110 | if ( service == NULL ) 111 | { 112 | return -1; 113 | } 114 | 115 | service->start(); 116 | 117 | if ( !service->listen( NetType::TCP, argv[1], atoi(argv[2]), nullptr ) ) 118 | { 119 | printf("service start failed \n"); 120 | delete service; 121 | 122 | return -2; 123 | } 124 | 125 | g_Running = true; 126 | 127 | while ( g_Running ) 128 | { 129 | sleep(1); 130 | } 131 | 132 | printf("EchoServer stoping ...\n"); 133 | service->stop(); 134 | delete service; 135 | 136 | return 0; 137 | } 138 | -------------------------------------------------------------------------------- /test/echostress.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | // -------------------------------------------------------------------------------------------- 19 | // -------------------------------------------------------------------------------------------- 20 | // -------------------------------------------------------------------------------------------- 21 | 22 | #define SENDBUFFER_SIZE 16384 23 | #define INBUFFER_SIZE 16384 24 | 25 | struct stress_client 26 | { 27 | int32_t fd; 28 | 29 | struct event evread; 30 | struct event evwrite; 31 | 32 | char inbuffer[INBUFFER_SIZE]; 33 | 34 | size_t send_nbytes; 35 | size_t recv_nbytes; 36 | }; 37 | 38 | static int8_t g_running; 39 | 40 | static char * g_host; 41 | static uint16_t g_port; 42 | static int32_t g_clients_count; 43 | 44 | static struct stress_client * g_clients; 45 | 46 | static struct timeval g_end; 47 | static struct timeval g_start; 48 | 49 | 50 | // -------------------------------------------------------------------------------------------- 51 | // -------------------------------------------------------------------------------------------- 52 | // -------------------------------------------------------------------------------------------- 53 | 54 | void stress_signal( int32_t signo ) 55 | { 56 | g_running = 0; 57 | } 58 | 59 | int32_t set_fd_nonblock( int32_t fd ) 60 | { 61 | int32_t flags ; 62 | int32_t retval = -1; 63 | 64 | flags = fcntl( fd, F_GETFL ); 65 | if ( flags >= 0 ) 66 | { 67 | flags |= O_NONBLOCK; 68 | 69 | if ( fcntl( fd, F_SETFL, flags ) >= 0 ) 70 | { 71 | retval = 0; 72 | } 73 | } 74 | 75 | return retval; 76 | } 77 | 78 | void stress_client_close( struct stress_client * client ) 79 | { 80 | event_del( &client->evread ); 81 | event_del( &client->evwrite ); 82 | 83 | if ( client->fd > 0 ) 84 | { 85 | close( client->fd ); 86 | client->fd = -1; 87 | } 88 | } 89 | 90 | void client_on_read( int32_t fd, int16_t ev, void * arg ) 91 | { 92 | struct stress_client * client = (struct stress_client *)arg; 93 | 94 | if ( ev & EV_READ ) 95 | { 96 | ssize_t nread = read( fd, client->inbuffer, INBUFFER_SIZE ); 97 | if ( nread <= 0 ) 98 | { 99 | if( nread < 0 && EAGAIN != errno ) 100 | { 101 | printf( "#%d client_on_read error, recvbytes %d, writebytes %d, errno %d, %s\n", 102 | fd, client->recv_nbytes, client->send_nbytes, errno, strerror( errno ) ); 103 | } 104 | 105 | stress_client_close( client ); 106 | return; 107 | } 108 | 109 | client->recv_nbytes += nread; 110 | 111 | if ( g_running == 0 112 | && client->recv_nbytes == client->send_nbytes ) 113 | { 114 | stress_client_close( client ); 115 | } 116 | } 117 | } 118 | 119 | void client_on_write( int32_t fd, int16_t ev, void * arg ) 120 | { 121 | struct stress_client * client = (struct stress_client *)arg; 122 | 123 | if ( ev & EV_WRITE ) 124 | { 125 | char buf[SENDBUFFER_SIZE] = {1}; 126 | 127 | ssize_t nwrite = write( client->fd, buf, SENDBUFFER_SIZE ); 128 | if ( nwrite == -1 ) 129 | { 130 | printf( "#%d client_on_write error, writebytes %lu, errno %d, %s\n", 131 | fd, client->send_nbytes, errno, strerror( errno ) ); 132 | return ; 133 | } 134 | 135 | client->send_nbytes += nwrite; 136 | 137 | if ( g_running == 0 ) 138 | { 139 | event_del( &client->evwrite ); 140 | } 141 | } 142 | } 143 | 144 | void start_clients( struct event_base * base ) 145 | { 146 | int32_t i = 0; 147 | struct sockaddr_in addr; 148 | 149 | double mseconds = 0; 150 | struct timeval connect_start; 151 | struct timeval connect_end; 152 | 153 | // 154 | memset( &addr, 0, sizeof(addr) ); 155 | addr.sin_family = AF_INET; 156 | addr.sin_addr.s_addr = inet_addr( g_host ); 157 | addr.sin_port = htons( g_port ); 158 | 159 | gettimeofday(&connect_start, NULL); 160 | 161 | // 162 | for ( i = 0; i < g_clients_count; ++i ) 163 | { 164 | struct stress_client * client = g_clients + i; 165 | 166 | client->fd = socket( AF_INET, SOCK_STREAM, 0 ); 167 | if( client->fd < 0 ) 168 | { 169 | printf("#%d, socket failed, errno %d, %s\n", i, errno, strerror( errno ) ); 170 | return ; 171 | } 172 | 173 | if( connect( client->fd, (struct sockaddr *)&addr, sizeof(addr) ) != 0) 174 | { 175 | printf("#%d, connect failed, errno %d, %s\n", i, errno, strerror( errno ) ); 176 | return ; 177 | } 178 | 179 | //set_fd_nonblock( client->fd ); 180 | 181 | event_set( &client->evread, client->fd, EV_READ | EV_PERSIST, client_on_read, client ); 182 | event_base_set( base, &client->evread ); 183 | event_add( &client->evread, NULL ); 184 | 185 | event_set( &client->evwrite, client->fd, EV_WRITE | EV_PERSIST, client_on_write, client ); 186 | event_base_set( base, &client->evwrite ); 187 | event_add( &client->evwrite, NULL ); 188 | 189 | if( 0 == ( i % 10 ) ) write( fileno( stdout ), ".", 1 ); 190 | } 191 | printf("\n"); 192 | 193 | gettimeofday(&connect_end, NULL); 194 | 195 | mseconds = (double) ( 196 | (connect_end.tv_sec-connect_start.tv_sec)*1000 + (connect_end.tv_usec-connect_start.tv_usec)/1000 ); 197 | printf("start_clients(CLIENTS=%d) ExecTimes: %.6f mseconds\n", g_clients_count, mseconds ); 198 | } 199 | 200 | int main( int argc, char ** argv ) 201 | { 202 | int32_t c, i ; 203 | extern char *optarg ; 204 | 205 | double total_time = 0; 206 | struct event_base * base = NULL; 207 | uint64_t total_sendlen = 0, total_recvlen = 0; 208 | 209 | while( ( c = getopt ( argc, argv, "h:p:c:v" )) != EOF ) 210 | { 211 | switch ( c ) 212 | { 213 | case 'h' : 214 | g_host = optarg; 215 | break; 216 | 217 | case 'p': 218 | g_port = (uint16_t)atoi( optarg ); 219 | break; 220 | 221 | case 'c' : 222 | g_clients_count = atoi ( optarg ); 223 | break; 224 | 225 | case 'v' : 226 | case '?' : 227 | return -1; 228 | } 229 | } 230 | 231 | // 232 | // 注册信号 233 | // 234 | signal( SIGPIPE, SIG_IGN ); 235 | signal( SIGINT, stress_signal ); 236 | signal( SIGINT, stress_signal ); 237 | 238 | // 239 | // 初始化事件集 240 | // 241 | base = event_base_new(); 242 | if ( base == NULL ) 243 | { 244 | printf("out of memory, allocate for 'base' failed .\n"); 245 | return -2; 246 | } 247 | 248 | // 249 | // 为所有的客户端分配内存 250 | // 251 | g_clients = calloc( g_clients_count, sizeof(struct stress_client) ); 252 | if ( g_clients == NULL ) 253 | { 254 | printf("out of memory, allocate for 'g_clients' failed .\n"); 255 | return -2; 256 | } 257 | 258 | // 259 | // 打开所有客户端 260 | // 261 | g_running = 1; 262 | start_clients( base ); 263 | printf("IO Test Begin, you can press Ctrl-C to break, and see the IOTestReport ... \n"); 264 | 265 | gettimeofday( &g_start, NULL ); 266 | while ( g_running ) 267 | { 268 | event_base_loop( base, EVLOOP_ONCE ); 269 | } 270 | gettimeofday( &g_end, NULL ); 271 | 272 | total_time = (double) ( 1000000*(g_end.tv_sec-g_start.tv_sec) + g_end.tv_usec-g_start.tv_usec ) / 1000000; 273 | 274 | // show result 275 | printf( "\n\nTest result :\n" ); 276 | printf( "Host %s, Port %d, Clients %d\n", g_host, g_port, g_clients_count ); 277 | printf( "ExecTimes: %.6f seconds\n\n", total_time ); 278 | 279 | printf( "client\t\t\tSend\t\t\tRecv\n" ); 280 | for( i = 0; i < g_clients_count; i++ ) 281 | { 282 | struct stress_client * client = g_clients + i; 283 | 284 | //printf( "client#%d : %d\t%d\n", i, client->mSendMsgs, client->mRecvMsgs ); 285 | 286 | total_sendlen += client->send_nbytes; 287 | total_recvlen += client->recv_nbytes; 288 | 289 | stress_client_close( client ); 290 | } 291 | 292 | printf( "total : \t\t%lld\t\t%lld\n", total_sendlen, total_recvlen ); 293 | printf( "average[KBytes/sec] : \t%.0f\t\t\t%.0f\n", total_sendlen / total_time / 1000.0f, total_recvlen / total_time / 1000.0f ); 294 | 295 | free( g_clients ); 296 | 297 | return 0; 298 | } 299 | -------------------------------------------------------------------------------- /test/pingpong.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "network.h" 12 | 13 | #define __DEBUG__ 14 | #define METHOD 1 15 | 16 | struct session 17 | { 18 | sid_t id; 19 | iolayer_t layer; 20 | char host[64]; 21 | uint16_t port; 22 | }; 23 | 24 | struct PacketHead 25 | { 26 | uint16_t cmd; 27 | uint16_t ext; 28 | uint32_t len; 29 | }; 30 | 31 | int32_t onStart( void * context ) 32 | { 33 | struct session * s = (struct session *)context; 34 | iolayer_set_persist( s->layer, s->id, 1 ); 35 | iolayer_set_wndsize( s->layer, s->id, 64, 64 ); 36 | #if defined __DEBUG__ 37 | printf( "START[%lu] : %lu -> %s:%d\n", time(NULL), s->id, s->host, s->port ); 38 | iolayer_set_timeout( s->layer, s->id, 60 ); 39 | #endif 40 | return 0; 41 | } 42 | 43 | ssize_t onProcess( void * context, const char * buf, size_t nbytes ) 44 | { 45 | ssize_t nprocess = 0; 46 | struct session * s = (struct session *)context; 47 | 48 | #if METHOD 49 | 50 | #if defined __DEBUG__ 51 | printf( "PROCESS[%lu] : %lu -> %ld\n", time(NULL), s->id, nbytes ); 52 | // iolayer_set_timeout( s->layer, s->id, 30 ); 53 | #endif 54 | iolayer_send( s->layer, s->id, buf, nbytes, 0 ); 55 | nprocess = nbytes; 56 | 57 | #else 58 | 59 | while ( 1 ) 60 | { 61 | size_t nleft = nbytes - nprocess; 62 | const char * buffer = buf + nprocess; 63 | 64 | if ( nleft < sizeof(struct PacketHead) ) 65 | { 66 | break; 67 | } 68 | 69 | struct PacketHead * head = (struct PacketHead *)buffer; 70 | size_t size = head->len+sizeof(struct PacketHead); 71 | 72 | if ( nleft < size ) 73 | { 74 | break; 75 | } 76 | 77 | iolayer_send( s->layer, s->id, buffer, size, 0 ); 78 | nprocess += size; 79 | } 80 | 81 | #endif 82 | 83 | return nprocess; 84 | } 85 | 86 | int32_t onTimeout( void * context ) 87 | { 88 | #if defined __DEBUG__ 89 | struct session * s = (struct session *)context; 90 | printf( "TIMEOUT[%lu] : %lu -> %s:%d\n", time(NULL), s->id, s->host, s->port ); 91 | #endif 92 | return -1; 93 | } 94 | 95 | int32_t onKeepalive( void * context ) 96 | { 97 | return 0; 98 | } 99 | 100 | int32_t onError( void * context, int32_t result ) 101 | { 102 | return 0; 103 | } 104 | 105 | void onShutdown( void * context, int32_t way ) 106 | { 107 | struct session * s = (struct session *)context; 108 | #if defined __DEBUG__ 109 | printf( "SHUTDOWN[%lu] : %lu -> %s:%d\n", time(NULL), s->id, s->host, s->port ); 110 | #endif 111 | free( s ); 112 | } 113 | 114 | int32_t onPerform( void * context, int32_t type, void * task, int32_t interval ) 115 | { 116 | return 0; 117 | } 118 | 119 | int32_t onLayerAccept( void * context, void * local, sid_t id, const char * host, uint16_t port ) 120 | { 121 | iolayer_t layer = (iolayer_t)context; 122 | struct session * session = malloc( sizeof(struct session) ); 123 | 124 | if ( session ) 125 | { 126 | session->id = id; 127 | session->layer = layer; 128 | session->port = port; 129 | strncpy( session->host, host, 63 ); 130 | 131 | ioservice_t ioservice; 132 | ioservice.start = onStart; 133 | ioservice.process = onProcess; 134 | ioservice.transform = NULL; 135 | ioservice.timeout = onTimeout; 136 | ioservice.keepalive = onKeepalive; 137 | ioservice.error = onError; 138 | ioservice.shutdown = onShutdown; 139 | ioservice.perform = onPerform; 140 | iolayer_set_service( layer, id, &ioservice, session ); 141 | } 142 | 143 | return 0; 144 | } 145 | 146 | 147 | int32_t g_Running; 148 | 149 | void signal_handle( int32_t signo ) 150 | { 151 | g_Running = 0; 152 | } 153 | 154 | int main( int32_t argc, char ** argv ) 155 | { 156 | if ( argc != 5 ) 157 | { 158 | printf("pingpong [type] [host] [port] [threads] \n"); 159 | return -1; 160 | } 161 | 162 | uint8_t type = atoi(argv[1]); 163 | char * host = argv[2]; 164 | uint16_t port = atoi(argv[3]); 165 | uint8_t nthreads = atoi(argv[4]); 166 | 167 | signal( SIGPIPE, SIG_IGN ); 168 | signal( SIGINT, signal_handle ); 169 | 170 | iolayer_t layer = iolayer_create( nthreads, 500, 8, 0 ); 171 | if ( layer == NULL ) 172 | { 173 | return -2; 174 | } 175 | 176 | options_t options = { .mtu=1400, .minrto=30, .sndwnd = 64, 177 | .rcvwnd = 64, .stream=1, .resend=2, .deadlink=50, .interval = 40, .ntransfer = 8 }; 178 | 179 | if ( iolayer_listen( layer, type, host, port, &options, onLayerAccept, layer ) < 0 ) 180 | { 181 | printf( "pingpong %s::%d failed .\n", host, port ); 182 | iolayer_destroy( layer ); 183 | return -3; 184 | } 185 | 186 | g_Running = 1; 187 | 188 | while ( g_Running ) 189 | { 190 | sleep(10); 191 | } 192 | 193 | iolayer_destroy( layer ); 194 | 195 | return 0; 196 | } 197 | -------------------------------------------------------------------------------- /test/pingpongclient.c: -------------------------------------------------------------------------------- 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 | 14 | #include "ikcp.h" 15 | #include "event.h" 16 | #include "utils.h" 17 | #include "message.h" 18 | 19 | int32_t g_IsInput; 20 | int32_t g_IsRunning; 21 | #define INBUFFER_LEN 65536 22 | 23 | typedef struct 24 | { 25 | int32_t fd; 26 | int32_t conv; 27 | uint8_t type; 28 | ikcpcb * kcp; 29 | event_t event; 30 | evsets_t evsets; 31 | struct sockaddr_in addr; 32 | }PingpongClient; 33 | 34 | #if ( 1>>1 == 0 ) 35 | #define bswap64(_x) \ 36 | (((_x) >> 56) | \ 37 | (((_x) >> 40) & (0xffUL << 8)) | \ 38 | (((_x) >> 24) & (0xffUL << 16)) | \ 39 | (((_x) >> 8) & (0xffUL << 24)) | \ 40 | (((_x) << 8) & (0xffUL << 32)) | \ 41 | (((_x) << 24) & (0xffUL << 40)) | \ 42 | (((_x) << 40) & (0xffUL << 48)) | \ 43 | ((_x) << 56)) 44 | #ifndef htobe64 45 | #define htobe64(x) bswap64((uint64_t)(x)) 46 | #endif 47 | #ifndef be64toh 48 | #define be64toh(x) bswap64((uint64_t)(x)) 49 | #endif 50 | #else 51 | #ifndef htobe64 52 | #define htobe64(x) ((uint64_t)(x)) 53 | #endif 54 | #ifndef be64toh 55 | #define be64toh(x) ((uint64_t)(x)) 56 | #endif 57 | #endif 58 | 59 | void signal_handler( int signo ) 60 | { 61 | g_IsRunning = 0; 62 | } 63 | 64 | int32_t kcp_output( const char * buf, int32_t len, ikcpcb * kcp, void * user ) 65 | { 66 | PingpongClient * cli = (PingpongClient *)user; 67 | 68 | if ( write(cli->fd, buf, len) < 0 ) 69 | { 70 | printf( "sendto(%s) error.\n", strerror(errno) ); 71 | } 72 | 73 | return 0; 74 | } 75 | 76 | PingpongClient * pingpongclient_init( uint8_t type ) 77 | { 78 | PingpongClient * client = (PingpongClient *)malloc( sizeof(PingpongClient) ); 79 | if ( client == NULL ) 80 | { 81 | return NULL; 82 | } 83 | 84 | client->type = type; 85 | client->conv = rand() % 0x7fffffff; 86 | client->kcp = ikcp_create( client->conv, client ); 87 | if ( client->kcp == NULL ) 88 | { 89 | return NULL; 90 | } 91 | 92 | client->evsets = evsets_create( 8 ); 93 | if ( client->evsets == NULL ) 94 | { 95 | return NULL; 96 | } 97 | 98 | client->event = event_create(); 99 | if ( client->event == NULL ) 100 | { 101 | return NULL; 102 | } 103 | 104 | client->kcp->stream = 1; 105 | ikcp_nodelay( client->kcp, 1, 10, 2, 1 ); 106 | ikcp_setoutput( client->kcp, kcp_output ); 107 | 108 | if ( type == NETWORK_TCP ) 109 | { 110 | client->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 111 | } 112 | else 113 | { 114 | client->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 115 | } 116 | if ( client->fd < 0 ) 117 | { 118 | return NULL; 119 | } 120 | 121 | set_non_block( client->fd ); 122 | 123 | struct sockaddr_in clientaddr; 124 | memset(&clientaddr, 0, sizeof(clientaddr)); 125 | clientaddr.sin_family = AF_INET; 126 | clientaddr.sin_port = 0; 127 | clientaddr.sin_addr.s_addr = htonl(INADDR_ANY); 128 | if ( bind( client->fd, (struct sockaddr *)&clientaddr, sizeof(clientaddr) ) < 0 ) 129 | { 130 | return NULL; 131 | } 132 | 133 | return client; 134 | } 135 | 136 | void pingpongclient_final( PingpongClient * cli ) 137 | { 138 | if ( cli->kcp != NULL ) 139 | { 140 | ikcp_flush( cli->kcp ); 141 | ikcp_release( cli->kcp ); 142 | } 143 | 144 | close( cli->fd ); 145 | 146 | evsets_del( cli->evsets, cli->event ); 147 | event_destroy( cli->event ); 148 | evsets_destroy( cli->evsets ); 149 | 150 | free( cli ); 151 | } 152 | 153 | int32_t pingpongclient_connect( PingpongClient * client, const char * host, uint16_t port ) 154 | { 155 | // 设置远程服务器地址 156 | memset( &client->addr, 0, sizeof(struct sockaddr_in) ); 157 | client->addr.sin_family = AF_INET; 158 | client->addr.sin_port = htons(port); 159 | inet_pton(AF_INET, host, &client->addr.sin_addr.s_addr); 160 | 161 | if ( connect( client->fd, (struct sockaddr*)&client->addr, sizeof(struct sockaddr) ) < 0) 162 | { 163 | if ( errno != EINPROGRESS ) 164 | { 165 | printf( "pingpongclient_connect(%s::%d) : %s .\n", host, port, strerror(errno) ); 166 | return -1; 167 | } 168 | } 169 | 170 | return 0; 171 | } 172 | 173 | void pingpongclient_on_read( int32_t fd, int16_t ev, void * arg ) 174 | { 175 | PingpongClient * cli = (PingpongClient *)arg; 176 | 177 | if ( ev & EV_READ ) 178 | { 179 | char buffer[ 65536 ]; 180 | ssize_t len = read( cli->fd, buffer, sizeof(buffer) ); 181 | 182 | if ( len > 0 ) 183 | { 184 | struct buffer inbuf; 185 | 186 | if ( cli->type == NETWORK_TCP ) 187 | { 188 | buffer_append( &inbuf, buffer, len ); 189 | } 190 | else if ( cli->type == NETWORK_KCP ) 191 | { 192 | ikcp_input( cli->kcp, buffer, len ); 193 | while ( 1 ) 194 | { 195 | ssize_t peeksize = ikcp_peeksize( cli->kcp ); 196 | if ( peeksize <= 0 ) 197 | { 198 | break; 199 | } 200 | 201 | buffer_reserve( &inbuf, peeksize ); 202 | peeksize = ikcp_recv( cli->kcp, inbuf.buffer+inbuf.length, peeksize ); 203 | if ( peeksize <= 0 ) 204 | { 205 | break; 206 | } 207 | inbuf.length += peeksize; 208 | } 209 | } 210 | if ( buffer_length(&inbuf) > 0 ) 211 | { 212 | char * xx = buffer_data( &inbuf ); 213 | size_t length = buffer_length( &inbuf ); 214 | int64_t timestamp = *(int64_t *)( xx - 8 + length ); 215 | 216 | xx[ length-8 ] = '\0'; 217 | timestamp = be64toh( timestamp ); 218 | printf( "%d> %s [-- %lu --]\n", cli->conv, xx, milliseconds()-timestamp ); 219 | g_IsInput = 1; fflush( stdin ); 220 | } 221 | 222 | buffer_clear( &inbuf ); 223 | } 224 | } 225 | } 226 | 227 | int main(int argc, char* argv[]) 228 | { 229 | if (argc < 4) 230 | { 231 | printf("usage: %s type ip port\n", argv[0]); 232 | exit(1); 233 | } 234 | 235 | int64_t epoch = milliseconds(); 236 | srand( time(NULL) ); 237 | signal( SIGPIPE, SIG_IGN ); 238 | signal( SIGINT, signal_handler ); 239 | signal( SIGTERM, signal_handler ); 240 | 241 | PingpongClient * cli = pingpongclient_init( atoi(argv[1]) ); 242 | if ( cli == NULL ) 243 | { 244 | printf( "kcp_init() failed\n" ); 245 | exit( -1 ); 246 | } 247 | 248 | if ( pingpongclient_connect( cli, argv[2], atoi(argv[3]) ) < 0 ) 249 | { 250 | printf( "kcp_connect() failed\n" ); 251 | exit( -2 ); 252 | } 253 | 254 | event_set( cli->event, cli->fd, EV_READ|EV_PERSIST ); 255 | event_set_callback( cli->event, pingpongclient_on_read, cli ); 256 | evsets_add( cli->evsets, cli->event, -1 ); 257 | 258 | // 初始状态 259 | g_IsInput = 1; g_IsRunning = 1; 260 | 261 | while ( g_IsRunning ) 262 | { 263 | if ( g_IsInput == 1 ) 264 | { 265 | size_t length = 0; 266 | char buffer[ INBUFFER_LEN ] = {0}; 267 | 268 | printf( "%d< ", cli->conv ); 269 | fflush( stdin ); 270 | fgets( buffer, INBUFFER_LEN, stdin ); 271 | 272 | if ( g_IsRunning != 0 ) 273 | { 274 | length = strlen( buffer ); 275 | if ( length != 0 ) 276 | { 277 | g_IsInput = 0; 278 | *(int64_t *)(buffer+length-1) = htobe64( milliseconds() ); 279 | 280 | if ( cli->type == 1 ) 281 | { 282 | write( cli->fd, buffer, length+7 ); 283 | } 284 | else 285 | { 286 | ikcp_send( cli->kcp, buffer, length+7 ); 287 | ikcp_flush( cli->kcp ); 288 | } 289 | } 290 | } 291 | } 292 | 293 | evsets_dispatch( cli->evsets ); 294 | if ( cli->type == NETWORK_KCP ) 295 | { 296 | ikcp_update( cli->kcp, milliseconds()-epoch ); 297 | } 298 | } 299 | 300 | pingpongclient_final( cli ); 301 | 302 | return 0; 303 | } 304 | -------------------------------------------------------------------------------- /test/raw_echoserver.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "event.h" 18 | #include "utils.h" 19 | 20 | int32_t isrunning, naccept; 21 | 22 | int32_t listenfd_options( int32_t fd ) 23 | { 24 | int32_t flags = 0; 25 | 26 | set_non_block( fd ); 27 | 28 | /* TCP Socket Option Settings */ 29 | flags = 1; 30 | setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags) ); 31 | 32 | flags = 1; 33 | setsockopt( fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags) ); 34 | 35 | flags = 1; 36 | setsockopt( fd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags) ); 37 | 38 | return 0; 39 | } 40 | 41 | void echoserver_signal_handler( int signo ) 42 | { 43 | isrunning = 0; 44 | } 45 | 46 | void * idlethread_main( void * arg ) 47 | { 48 | printf( "idlethread_main()\n" ); 49 | 50 | for ( ;; ) 51 | { 52 | sleep( 60 ); 53 | } 54 | 55 | return NULL; 56 | } 57 | 58 | void process_message( int32_t fd, int16_t ev, void * arg ) 59 | { 60 | static int32_t capacity = 16384; 61 | 62 | event_t event = arg; 63 | evsets_t evsets = event_get_sets( event ); 64 | 65 | if ( ev & EV_READ ) 66 | { 67 | ssize_t readn = -1; 68 | char buf[ capacity ]; 69 | 70 | readn = read( fd, buf, capacity ); 71 | if ( readn > 0 ) 72 | { 73 | write( fd, buf, readn ); 74 | } 75 | else if ( readn <= 0 76 | && (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) ) 77 | { 78 | evsets_del( evsets, event ); 79 | event_destroy( event ); 80 | close( fd ); 81 | } 82 | } 83 | } 84 | 85 | void accept_new_session( int32_t fd, int16_t ev, void * arg ) 86 | { 87 | evsets_t coreset = ( evsets_t )arg; 88 | 89 | if ( ev & EV_READ ) 90 | { 91 | char dsthost[20]; 92 | uint16_t dstport = 0; 93 | 94 | int32_t newfd = tcp_accept( fd, dsthost, &dstport ); 95 | if ( newfd > 0 ) 96 | { 97 | ++naccept; 98 | set_non_block( newfd ); 99 | 100 | event_t event = event_create(); 101 | if ( event == NULL ) 102 | { 103 | printf( "accept new fd failed .\n" ); 104 | return; 105 | } 106 | 107 | printf( "accept new fd:%d, %s::%d .\n", newfd, dsthost, dstport ); 108 | event_set( event, newfd, EV_READ|EV_PERSIST ); 109 | event_set_callback( event, process_message, event ); 110 | evsets_add( coreset, event, -1 ); 111 | } 112 | else 113 | { 114 | printf( "listenfd:%d, errno:%d:'%s'\n", fd, errno, strerror( errno ) ); 115 | } 116 | } 117 | } 118 | 119 | int main( int argc, char ** argv ) 120 | { 121 | char host[32]; 122 | uint16_t port = 0; 123 | 124 | if ( argc != 3 ) 125 | { 126 | printf("echoserver [host] [port] .\n"); 127 | return 0; 128 | } 129 | 130 | int32_t fd = 0; 131 | event_t evaccept = NULL; 132 | evsets_t coreset = NULL; 133 | 134 | signal( SIGPIPE, SIG_IGN ); 135 | signal( SIGINT, echoserver_signal_handler ); 136 | signal( SIGTERM, echoserver_signal_handler ); 137 | 138 | // host, listenport 139 | strcpy( host, argv[1] ); 140 | port = (uint16_t)atoi( argv[2] ); 141 | 142 | // core eventsets 143 | coreset = evsets_create(8); 144 | if ( coreset == NULL ) 145 | { 146 | printf( "create core event sets failed .\n" ); 147 | goto FINAL; 148 | } 149 | 150 | // listen port 151 | fd = tcp_listen( host, port, listenfd_options ); 152 | if ( fd < 0 ) 153 | { 154 | printf( "listen failed %d, %s::%d .\n", fd, host, port ); 155 | goto FINAL; 156 | } 157 | 158 | printf( "%s::%d\n", host, port ); 159 | 160 | evaccept = event_create(); 161 | if ( evaccept == NULL ) 162 | { 163 | printf( "create accept event failed .\n" ); 164 | goto FINAL; 165 | } 166 | event_set( evaccept, fd, EV_READ|EV_PERSIST ); 167 | event_set_callback( evaccept, accept_new_session, coreset ); 168 | evsets_add( coreset, evaccept, -1 ); 169 | 170 | // running ... 171 | naccept = 0; 172 | isrunning = 1; 173 | 174 | #if 1 175 | // Idle Thread 176 | pthread_t tid; 177 | pthread_create( &tid, NULL, idlethread_main, NULL ); 178 | #endif 179 | 180 | // loop 181 | while ( isrunning == 1 ) 182 | { 183 | evsets_dispatch( coreset ); 184 | } 185 | 186 | FINAL : 187 | if ( evaccept != NULL ) 188 | { 189 | event_destroy( evaccept ); 190 | } 191 | 192 | if ( fd > 0 ) 193 | { 194 | close( fd ); 195 | } 196 | 197 | if ( coreset != NULL ) 198 | { 199 | evsets_destroy( coreset ); 200 | } 201 | 202 | return 0; 203 | } 204 | -------------------------------------------------------------------------------- /test/slice.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __SRC_UTILS_SLICE_H__ 3 | #define __SRC_UTILS_SLICE_H__ 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | class Slice 13 | { 14 | public: 15 | // Create an empty slice. 16 | Slice() 17 | : m_Data(""), 18 | m_Size(0) 19 | {} 20 | 21 | // Create a slice that refers to d[0,n-1]. 22 | Slice( const char * d, size_t n ) 23 | : m_Data(d), 24 | m_Size(n) 25 | {} 26 | 27 | // Create a slice that refers to the contents of "s" 28 | Slice( const std::string & s ) 29 | : m_Data( s.data() ), 30 | m_Size( s.size() ) 31 | {} 32 | 33 | // Create a slice that refers to s[0,strlen(s)-1] 34 | Slice( const char * s ) 35 | : m_Data( s ), 36 | m_Size( std::strlen(s) ) 37 | {} 38 | 39 | // Return a pointer to the beginning of the referenced data 40 | const char * data() const { return m_Data; } 41 | 42 | // Return the length (in bytes) of the referenced data 43 | size_t size() const { return m_Size; } 44 | 45 | // Return true iff the length of the referenced data is zero 46 | bool empty() const { return m_Size == 0; } 47 | 48 | // Return the ith byte in the referenced data. 49 | // REQUIRES: n < size() 50 | char operator[](size_t n) const 51 | { 52 | assert( n < size() ); 53 | return m_Data[ n ]; 54 | } 55 | 56 | // Change this slice to refer to an empty array 57 | void clear() { m_Data = ""; m_Size = 0; } 58 | 59 | // Drop the first "n" bytes from this slice. 60 | void remove( size_t n ) 61 | { 62 | assert( n <= size() ); 63 | m_Data += n; 64 | m_Size -= n; 65 | } 66 | 67 | // Return a string that contains the copy of the referenced data. 68 | std::string ToString() const 69 | { 70 | return std::string( m_Data, m_Size ); 71 | } 72 | 73 | // Three-way comparison. Returns value: 74 | // < 0 iff "*this" < "b", 75 | // == 0 iff "*this" == "b", 76 | // > 0 iff "*this" > "b" 77 | int32_t compare( const Slice & b ) const; 78 | 79 | // Return true iff "x" is a prefix of "*this" 80 | bool startwith( const Slice & x ) const 81 | { 82 | return ( (m_Size >= x.m_Size) 83 | && (std::memcmp(m_Data, x.m_Data, x.m_Size) == 0) ); 84 | } 85 | 86 | private: 87 | const char * m_Data; 88 | size_t m_Size; 89 | }; 90 | 91 | inline bool operator== ( const Slice & x, const Slice & y ) 92 | { 93 | return ( (x.size() == y.size()) 94 | && (std::memcmp(x.data(), y.data(), x.size()) == 0) ); 95 | } 96 | 97 | inline bool operator!= ( const Slice & x, const Slice & y) 98 | { 99 | return !(x == y); 100 | } 101 | 102 | inline int32_t Slice::compare( const Slice & b ) const 103 | { 104 | const int32_t min_len = (m_Size < b.m_Size) ? m_Size : b.m_Size; 105 | int32_t r = std::memcmp(m_Data, b.m_Data, min_len); 106 | if ( r == 0 ) 107 | { 108 | if ( m_Size < b.m_Size ) 109 | { 110 | r = -1; 111 | } 112 | else if ( m_Size > b.m_Size ) 113 | { 114 | r = +1; 115 | } 116 | } 117 | 118 | return r; 119 | } 120 | 121 | typedef std::vector Slices; 122 | 123 | #endif 124 | -------------------------------------------------------------------------------- /test/test_addtimer.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 4 | // benchmark : 5 | // 6 | // FreeBSD 7.4 7 | // Xeon E5410 * 1, 8G 8 | // 9 | // evsets_add(1000000) : 0 secs, 63969 usecs . 10 | // evsets_del(1000000) : 0 secs, 57534 usecs . 11 | // -------------------------------------------------------- 12 | // libevent_add(1000000) : 0 secs, 310279 usecs . 13 | // libevent_del(1000000) : 0 secs, 490534 usecs . 14 | // 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "event.h" 22 | 23 | #define NTEST 1000000 24 | 25 | void ev_callback( int32_t fd, int16_t ev, void * arg ) 26 | {} 27 | 28 | void ev_timer_callback( int32_t fd, int16_t ev, void * arg ) 29 | { 30 | event_t e = (event_t)arg; 31 | evsets_t sets = event_get_sets( e ); 32 | 33 | printf("ev_timer_callback(handler=%p, fd=%d, ev=%d) : %d \n", e, fd, ev, time(NULL) ); 34 | 35 | evsets_add( sets, e, 2*1000 ); 36 | } 37 | 38 | int32_t test_addtimer( evsets_t sets, event_t * events ) 39 | { 40 | int32_t i = 0; 41 | struct timeval tv_start, tv_end; 42 | 43 | gettimeofday( &tv_start, NULL ); 44 | 45 | for ( i = 0; i < NTEST; ++i ) 46 | { 47 | evsets_add( sets, events[i], 10000 ); 48 | } 49 | 50 | gettimeofday( &tv_end, NULL ); 51 | 52 | printf("evsets_add(%d) : %ld secs, %ld usecs .\n", NTEST, 53 | tv_end.tv_sec - tv_start.tv_sec, tv_end.tv_usec - tv_start.tv_usec ); 54 | 55 | return 0; 56 | } 57 | 58 | int32_t test_deltimer( evsets_t sets, event_t * events ) 59 | { 60 | int32_t i = 0; 61 | struct timeval tv_start, tv_end; 62 | 63 | gettimeofday( &tv_start, NULL ); 64 | 65 | for ( i = 0; i < NTEST; ++i ) 66 | { 67 | evsets_del( sets, events[i] ); 68 | } 69 | 70 | gettimeofday( &tv_end, NULL ); 71 | 72 | printf("evsets_del(%d) : %ld secs, %ld usecs .\n", NTEST, 73 | tv_end.tv_sec - tv_start.tv_sec, tv_end.tv_usec - tv_start.tv_usec ); 74 | 75 | return 0; 76 | } 77 | 78 | int32_t test_operate_timer() 79 | { 80 | int32_t i = 0; 81 | 82 | evsets_t sets = NULL; 83 | event_t * events = NULL; 84 | 85 | sets = evsets_create( 8 ); 86 | events = malloc( sizeof(event_t) * NTEST ); 87 | 88 | for ( i = 0; i < NTEST; ++i ) 89 | { 90 | events[i] = event_create(); 91 | event_set_callback( events[i], ev_callback, NULL ); 92 | } 93 | 94 | test_addtimer( sets, events ); 95 | test_deltimer( sets, events ); 96 | 97 | for ( i = 0; i < NTEST; ++i ) 98 | { 99 | event_destroy( events[i] ); 100 | } 101 | free( events ); 102 | 103 | evsets_destroy( sets ); 104 | 105 | return 0; 106 | } 107 | 108 | int32_t test_evtimer() 109 | { 110 | evsets_t sets = NULL; 111 | event_t ev_timer = NULL; 112 | 113 | sets = evsets_create( 8 ); 114 | 115 | ev_timer = event_create(); 116 | event_set( ev_timer, -1, 0 ); 117 | event_set_callback( ev_timer, ev_timer_callback, ev_timer ); 118 | 119 | evsets_add( sets, ev_timer, 2*1000 ); 120 | 121 | while( 1 ) 122 | { 123 | evsets_dispatch( sets ); 124 | } 125 | 126 | event_destroy( ev_timer ); 127 | evsets_destroy( sets ); 128 | 129 | return 0; 130 | } 131 | 132 | int main() 133 | { 134 | test_operate_timer(); 135 | test_evtimer(); 136 | 137 | return 0; 138 | } 139 | -------------------------------------------------------------------------------- /test/test_events.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "event.h" 17 | 18 | int32_t done; 19 | 20 | void signal_handler( int32_t signo ) 21 | { 22 | done = 1; 23 | } 24 | 25 | void fifo_read( int32_t fd, int16_t ev, void * arg ) 26 | { 27 | char buf[255]; 28 | int32_t len = 0; 29 | int32_t rc = 0; 30 | 31 | event_t evfifo = (event_t)arg; 32 | evsets_t evsets = event_get_sets( evfifo ); 33 | 34 | rc = evsets_add( evsets, evfifo, -1 ); 35 | 36 | printf("fifo_read called with fd: %d, event: %d, arg: %p\n", fd, ev, arg); 37 | 38 | len = read(fd, buf, sizeof(buf)-1 ); 39 | if ( len == -1 ) 40 | { 41 | printf("read() error .\n"); 42 | return; 43 | } 44 | else if ( len == 0 ) 45 | { 46 | printf("this Connection is CLOSED .\n"); 47 | return; 48 | } 49 | 50 | buf[len] = '\0'; 51 | 52 | printf("Read: %s\n", buf); 53 | } 54 | 55 | int32_t main() 56 | { 57 | int32_t socketfd; 58 | 59 | event_t evfifo = NULL; 60 | evsets_t evsets = NULL; 61 | 62 | struct stat st; 63 | const char * fifo = "event.fifo"; 64 | 65 | done = 0; 66 | 67 | printf("VERSION : %s\n", evsets_get_version() ); 68 | 69 | signal( SIGINT, signal_handler ); 70 | signal( SIGTERM, signal_handler ); 71 | 72 | if ( lstat( fifo, &st ) == 0 ) 73 | { 74 | if ( (st.st_mode & S_IFMT) == S_IFREG ) 75 | { 76 | errno = EEXIST; 77 | printf("lstat() error .\n"); 78 | 79 | exit(1); 80 | } 81 | } 82 | 83 | unlink( fifo ); 84 | if ( mkfifo( fifo, 0600 ) == -1 ) 85 | { 86 | printf("mkfifo() error .\n"); 87 | exit(1); 88 | } 89 | 90 | #ifdef __linux__ 91 | socketfd = open( fifo, O_RDWR|O_NONBLOCK, 0 ); 92 | #else 93 | socketfd = open( fifo, O_RDONLY|O_NONBLOCK, 0 ); 94 | #endif 95 | 96 | if ( socketfd == -1 ) 97 | { 98 | printf("open() error .\n"); 99 | exit(1); 100 | } 101 | 102 | fprintf( stderr, "Write data to %s\n", fifo ); 103 | 104 | evsets = evsets_create( 8 ); 105 | if ( evsets == NULL ) 106 | { 107 | printf("evsets_create() error .\n"); 108 | exit(1); 109 | } 110 | 111 | evfifo = event_create(); 112 | if ( evfifo == NULL ) 113 | { 114 | printf("event_create() error .\n"); 115 | exit(1); 116 | } 117 | 118 | event_set( evfifo, socketfd, EV_READ ); 119 | event_set_callback( evfifo, fifo_read, evfifo ); 120 | printf("event_set() succeed, %p .\n", evfifo ); 121 | 122 | evsets_add( evsets, evfifo, -1 ); 123 | printf("evsets_add() succeed .\n"); 124 | 125 | while( !done ) 126 | { 127 | evsets_dispatch( evsets ); 128 | } 129 | 130 | evsets_destroy( evsets ); 131 | event_destroy( evfifo ); 132 | 133 | return 0; 134 | } 135 | -------------------------------------------------------------------------------- /test/test_iothreads.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "utils.h" 9 | #include "threads.h" 10 | 11 | uint8_t runflags; 12 | 13 | struct iothread_args 14 | { 15 | uint64_t taskcount; 16 | uint64_t padding[7]; 17 | }; 18 | 19 | void task_method( void * context, uint8_t index, int16_t type, void * task ) 20 | { 21 | struct iothread_args * arg = (struct iothread_args *)task; 22 | 23 | // TODO: 任务 24 | ++arg->taskcount; 25 | 26 | if ( rand()%10 == 0 ) 27 | { 28 | //usleep(2); 29 | } 30 | } 31 | 32 | void signal_handler( int32_t signo ) 33 | { 34 | printf("receive a quit signal \n"); 35 | runflags = 0; 36 | } 37 | 38 | int32_t main() 39 | { 40 | uint64_t index = 0; 41 | 42 | uint8_t i = 0; 43 | uint8_t nthreads = 4; 44 | 45 | iothreads_t threadgroup; 46 | uint64_t start_time = 0, end_time = 0; 47 | 48 | struct iothread_args * args = NULL; 49 | 50 | srand( (int32_t)time(NULL) ); 51 | signal( SIGINT, signal_handler ); 52 | 53 | // 54 | threadgroup = iothreads_start( nthreads, 0, task_method, NULL ); 55 | if ( threadgroup == NULL ) 56 | { 57 | printf("iothreads_start() failed \n"); 58 | return -1; 59 | } 60 | 61 | args = (struct iothread_args *)calloc( nthreads, sizeof(struct iothread_args) ); 62 | if ( args == NULL ) 63 | { 64 | printf("calloc for 'iothread_args' failed \n"); 65 | return -2; 66 | } 67 | 68 | // 69 | runflags = 1; 70 | 71 | // 72 | start_time = milliseconds(); 73 | while ( runflags ) 74 | { 75 | uint8_t _index = index&(nthreads-1); 76 | struct iothread_args * _args = args + _index; 77 | 78 | iothreads_post( threadgroup, _index, 0, _args, 0 ); 79 | ++index; 80 | } 81 | end_time = milliseconds(); 82 | 83 | // 84 | iothreads_stop( threadgroup ); 85 | 86 | // 87 | for ( i = 0; i < nthreads; ++i ) 88 | { 89 | struct iothread_args * _args = args + i; 90 | float rate = (float)(_args->taskcount)*1000.0f/(float)(end_time-start_time); 91 | 92 | printf("iothread[%d] process tasks: %ld, rate: %6.3f\n", i, _args->taskcount, rate ); 93 | } 94 | 95 | printf("dispatch tasks: %ld, rate: %6.3f\n", index, (float)(index)*1000.0f/(float)(end_time-start_time) ); 96 | 97 | return 0; 98 | } 99 | -------------------------------------------------------------------------------- /test/test_multicurl.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "utils.h" 4 | #include "xcurl.h" 5 | 6 | class TestRequest : public CurlRequest 7 | { 8 | public : 9 | TestRequest( const std::string & url ) 10 | : CurlRequest( url ), 11 | m_Timestamp( milliseconds() ) 12 | {} 13 | 14 | virtual ~TestRequest() 15 | {} 16 | 17 | virtual void onResponse() 18 | { 19 | printf( "onResponse(%lu, '%s', %lu)\n", milliseconds(), getResponse().c_str(), milliseconds()-m_Timestamp ); 20 | } 21 | 22 | virtual void onError( const char * reason ) 23 | { 24 | printf( "onError(%lu, '%s', %lu)\n", milliseconds(), reason, milliseconds()-m_Timestamp ); 25 | } 26 | 27 | private : 28 | int64_t m_Timestamp; 29 | }; 30 | 31 | void * iothread_main( void * arg ) 32 | { 33 | CurlAgent * agent = static_cast(arg); 34 | 35 | while ( 1 ) 36 | { 37 | agent->dispatch(); 38 | } 39 | 40 | return nullptr; 41 | } 42 | 43 | int main() 44 | { 45 | curl_global_init( CURL_GLOBAL_ALL ); 46 | 47 | CurlAgent * agent = new CurlAgent(); 48 | if ( agent == nullptr ) 49 | { 50 | return -1; 51 | } 52 | 53 | if ( !agent->initialize() ) 54 | { 55 | printf("CurlAgent::initialize() failed .\n"); 56 | delete agent; 57 | return -2; 58 | } 59 | 60 | pthread_t tid; 61 | void * status = nullptr; 62 | pthread_create( &tid, nullptr, iothread_main, agent ); 63 | 64 | for ( size_t i = 0; i < 10; ++i ) 65 | { 66 | agent->perform( 67 | eHttpMethod_Get, 68 | new TestRequest("http://ipinfo.io"), 1000 ); 69 | //agent->perform( 70 | // eHttpMethod_Get, 71 | // new TestRequest("http://172.21.161.138:3340/login"), 1000 ); 72 | sleep(1); 73 | } 74 | 75 | pthread_join( tid, &status ); 76 | 77 | agent->finalize(); 78 | delete agent; 79 | 80 | curl_global_cleanup(); 81 | 82 | return 0; 83 | } 84 | -------------------------------------------------------------------------------- /test/test_queue.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include "queue.h" 7 | 8 | struct task 9 | { 10 | int16_t type; 11 | int16_t utype; 12 | 13 | union 14 | { 15 | void * taskdata; 16 | char data[56]; 17 | }; 18 | }; 19 | 20 | QUEUE_PADDING_HEAD(taskqueue, struct task); 21 | QUEUE_GENERATE(taskqueue, struct task) 22 | 23 | int main() 24 | { 25 | uint32_t i = 0; 26 | struct taskqueue queue; 27 | 28 | srand( time(NULL) ); 29 | QUEUE_INIT(taskqueue)( &queue, 8192 ); 30 | 31 | for ( i = 0; i < 10000000; ++i ) 32 | { 33 | struct task * task = (struct task *)malloc( sizeof(struct task) ); 34 | QUEUE_PUSH( taskqueue )( &queue, task ); 35 | 36 | if ( rand()%10 == 0 ) 37 | { 38 | QUEUE_POP( taskqueue )( &queue, task ); 39 | } 40 | } 41 | 42 | QUEUE_CLEAR( taskqueue )( &queue ); 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /test/test_sidlist.c: -------------------------------------------------------------------------------- 1 | 2 | #include "utils.h" 3 | 4 | int main() 5 | { 6 | sid_t ids[3] = { 1234567, 8901234, 5678901 }; 7 | struct sidlist * list = sidlist_create( 8 ); 8 | 9 | sidlist_adds( list, &ids, 3 ); 10 | sidlist_adds( list, &ids, 3 ); 11 | 12 | sidlist_destroy( list ); 13 | 14 | return 0; 15 | } 16 | --------------------------------------------------------------------------------