├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── appveyor.yml ├── build.ps1 ├── build_package.ps1 ├── build_third_party.sh ├── internal_utils.cmake ├── src ├── dbg.h ├── ikcp.c ├── ikcp.h ├── kcpev.c ├── kcpev.h ├── kcpev_ringbuf.c ├── kcpev_ringbuf.h ├── myuuid.c ├── myuuid.h ├── portable_endian.h └── utils.h ├── tests ├── client_kcp_simple_test.c ├── client_kcp_test.c ├── client_udp_test.c ├── kcpev_client_pressure_test.c ├── kcpev_client_test.c ├── kcpev_echo_client_test.c ├── kcpev_echo_server_test.c ├── kcpev_heartbeat_test.cpp ├── kcpev_package_test.cpp ├── kcpev_remote_package_test.cpp ├── kcpev_send_test.cpp ├── kcpev_server_test.c ├── libev_udp_echo_client.c ├── libev_udp_echo_server.c ├── pressure_test.sh ├── ringbuf_test.cpp ├── server_kcp_simple_test.c ├── server_kcp_test.c ├── server_kcp_test2.c ├── server_udp_test.c ├── test.cc └── test.h ├── travis.sh └── utMakefile /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | *.smod 19 | 20 | # Compiled Static libraries 21 | *.lai 22 | *.la 23 | *.a 24 | *.lib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/uthash"] 2 | path = third_party/uthash 3 | url = https://github.com/troydhanson/uthash.git 4 | [submodule "third_party/googletest"] 5 | path = third_party/googletest 6 | url = https://github.com/google/googletest.git 7 | [submodule "third_party/libev-win"] 8 | path = third_party/libev-win 9 | url = https://github.com/disenone/libev-win.git 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Build matrix / environment variable are explained on: 2 | # http://about.travis-ci.org/docs/user/build-configuration/ 3 | # This file can be validated on: 4 | # http://lint.travis-ci.org/ 5 | 6 | before_install: 7 | - brew update || true 8 | - brew install libev || true 9 | - brew install ossp-uuid || true 10 | 11 | install: 12 | # /usr/bin/gcc is 4.6 always, but gcc-X.Y is available. 13 | - if [ "$CXX" = "g++" ]; then export CXX="g++-4.9" CC="gcc-4.9"; fi 14 | # /usr/bin/clang is 3.4, lets override with modern one. 15 | - if [ "$CXX" = "clang++" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then export CXX="clang++-3.7" CC="clang-3.7"; fi 16 | - echo ${PATH} 17 | - echo ${CXX} 18 | - ${CXX} --version 19 | - ${CXX} -v 20 | addons: 21 | apt: 22 | # List of whitelisted in travis packages for ubuntu-precise can be found here: 23 | # https://github.com/travis-ci/apt-package-whitelist/blob/master/ubuntu-precise 24 | # List of whitelisted in travis apt-sources: 25 | # https://github.com/travis-ci/apt-source-whitelist/blob/master/ubuntu.json 26 | sources: 27 | - ubuntu-toolchain-r-test 28 | - llvm-toolchain-precise-3.7 29 | packages: 30 | - gcc-4.9 31 | - g++-4.9 32 | - clang-3.7 33 | - valgrind 34 | - libev-dev 35 | - uuid-dev 36 | os: 37 | - linux 38 | - osx 39 | language: cpp 40 | compiler: 41 | - gcc 42 | - clang 43 | script: ./travis.sh 44 | notifications: 45 | email: false 46 | sudo: false 47 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6.2) 2 | project(kcpev CXX C) 3 | set(CMAKE_VERBOSE_MAKEFILE ON) 4 | 5 | include(internal_utils.cmake) 6 | config_compiler_and_linker() 7 | 8 | message("cxx flags ${CMAKE_CXX_FLAGS}") 9 | message("c flags ${CMAKE_C_FLAGS}") 10 | 11 | set(KCPEV_DIR ${PROJECT_SOURCE_DIR}/src) 12 | set(TEST_DIR ${PROJECT_SOURCE_DIR}/tests) 13 | 14 | # sources 15 | aux_source_directory(${KCPEV_DIR} KCPEV_SOURCES) 16 | aux_source_directory(${TEST_DIR} KCPEV_TEST_SOURCES) 17 | 18 | # third party 19 | include_directories( 20 | "${PROJECT_SOURCE_DIR}/third_party/uthash/src/" 21 | "${PROJECT_SOURCE_DIR}/third_party/googletest/googletest/include/" 22 | ) 23 | 24 | link_directories("${PROJECT_SOURCE_DIR}/third_party/googletest/build/googlemock/gtest") 25 | 26 | if(MSVC) 27 | include_directories( 28 | "${PROJECT_SOURCE_DIR}/third_party/libev-win") 29 | link_directories( 30 | "${PROJECT_SOURCE_DIR}/third_party/googletest/build/googlemock/gtest/$(ConfigurationName)/" 31 | "${PROJECT_SOURCE_DIR}/third_party/libev-win/build/$(ConfigurationName)/" 32 | "${PROJECT_SOURCE_DIR}/build/$(ConfigurationName)/" 33 | ) 34 | endif() 35 | 36 | # build libkcpev 37 | add_library(kcpev_static STATIC ${KCPEV_SOURCES}) 38 | add_library(kcpev SHARED ${KCPEV_SOURCES}) 39 | 40 | if(MSVC) 41 | target_link_libraries(kcpev_static libev_static ws2_32) 42 | target_link_libraries(kcpev libev_static ws2_32) 43 | else() 44 | target_link_libraries(kcpev_static ev uuid) 45 | target_link_libraries(kcpev ev uuid) 46 | endif() 47 | 48 | list(REMOVE_ITEM KCPEV_TEST_SOURCES "${PROJECT_SOURCE_DIR}/tests/test.cc") 49 | 50 | if(MSVC) 51 | list(REMOVE_ITEM KCPEV_TEST_SOURCES 52 | "${PROJECT_SOURCE_DIR}/tests/kcpev_send_test.cpp" 53 | "${PROJECT_SOURCE_DIR}/tests/kcpev_package_test.cpp" 54 | "${PROJECT_SOURCE_DIR}/tests/kcpev_heartbeat_test.cpp") 55 | STRING(REGEX REPLACE "/" "\\\\" PROJECT_SOURCE_DIR_SLASH ${PROJECT_SOURCE_DIR}) 56 | add_custom_command(TARGET kcpev POST_BUILD 57 | COMMAND xcopy /C /Y "${PROJECT_SOURCE_DIR_SLASH}\\third_party\\googletest\\build\\googlemock\\gtest\\$(ConfigurationName)\\gtest.dll" "${PROJECT_SOURCE_DIR_SLASH}\\build\\$(ConfigurationName)" 58 | ) 59 | endif() 60 | 61 | # build tests 62 | include_directories( ${TEST_DIR} ) 63 | include_directories( ${KCPEV_DIR} ) 64 | foreach(testfile ${KCPEV_TEST_SOURCES}) 65 | get_filename_component(testname ${testfile} NAME_WE) 66 | add_executable(${testname} ${testfile} "${PROJECT_SOURCE_DIR}/tests/test.cc") 67 | if(MSVC) 68 | target_link_libraries(${testname} kcpev_static libev_static gtest ws2_32) 69 | else() 70 | target_link_libraries(${testname} kcpev_static ev gtest) 71 | endif() 72 | endforeach(testfile) 73 | 74 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 disenone 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kcpev 2 | [![Build Status](https://travis-ci.org/disenone/kcpev.svg?branch=master) 3 | ](https://travis-ci.org/disenone/kcpev) 4 | [![Build status](https://ci.appveyor.com/api/projects/status/gv4uqlvaoh46ect0?svg=true)](https://ci.appveyor.com/project/disenone/kcpev) 5 | 6 | 设计: 7 | TCP 和 UDP 同时使用,结构参考[同现有 TCP 服务器整合](https://github.com/skywind3000/kcp/wiki/Cooperate-With-Tcp-Server) 8 | 9 | ========== 10 | ###Feature: 11 | * Linux 和 Windows 下都能编译运行,但 Windows 下只支持客户端的运行,服务端 udp 不能正确接收数据 12 | * tcp 和 udp 收发,默认使用 udp;udp 不可用时切换为 tcp,也可以强行使用 tcp 13 | * tcp 包使用 ringbuf 来重组,保证完整 14 | * udp 使用 kcp 来保证收发可靠 15 | * udp 心跳包 16 | * 收发正确性测试 17 | - tests/kcpev_package_test 18 | - tests/echo_server + tests/kcpev_remote_package_test 19 | * 压测 20 | - pressure_test.sh 21 | 22 | ========== 23 | ###TODO: 24 | * 心跳包测试 25 | * 断线重连 26 | 27 | ========== 28 | ###Dependency: 29 | * [libev](http://software.schmorp.de/pkg/libev.html) 30 | * [libuuid](https://github.com/karelzak/util-linux/tree/master/libuuid) 31 | * [uthash](https://github.com/troydhanson/uthash) 32 | * [googletest](https://github.com/google/googletest.git) 33 | 34 | ========== 35 | ###Build: 36 | * Linux 37 | - apt-get install libev-dev uuid-dev 38 | - ./travis.sh 39 | 40 | * Windows 41 | - install cmake 42 | - powershell 43 | - Set-ExecutionPolicy RemoteSigned 44 | - ./build.ps1 45 | 46 | * Mac 47 | - brew install libev 48 | - ./travis.sh 49 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | 3 | os: Visual Studio 2015 4 | 5 | environment: 6 | matrix: 7 | - Toolset: v140 8 | - Toolset: v120 9 | 10 | platform: 11 | - Win32 12 | - x64 13 | 14 | configuration: 15 | - Release 16 | - Debug 17 | 18 | build: 19 | verbosity: detailed 20 | 21 | before_build: 22 | - ps: | 23 | Write-Output "Configuration: $env:CONFIGURATION" 24 | Write-Output "Platform: $env:PLATFORM" 25 | $generator = switch ($env:TOOLSET) 26 | { 27 | "v140" {"Visual Studio 14 2015"} 28 | "v120" {"Visual Studio 12 2013"} 29 | "v110" {"Visual Studio 11 2012"} 30 | "v100" {"Visual Studio 10 2010"} 31 | } 32 | if ($env:PLATFORM -eq "x64") 33 | { 34 | $generator = "$generator Win64" 35 | } 36 | 37 | build_script: 38 | - ps: | 39 | ./build.ps1 $generator $env:CONFIGURATION 40 | 41 | -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | # build in windows 2 | 3 | param($gen, $config) 4 | $root = (Get-Item -Path ".\" -Verbose).FullName 5 | 6 | git submodule update --init --recursive 7 | 8 | ./build_package.ps1 third_party/googletest $gen $config -extra_cmake "-DBUILD_SHARED_LIBS=ON" 9 | cd $root 10 | 11 | ./build_package.ps1 third_party/libev-win $gen $config 12 | cd $root 13 | 14 | ./build_package.ps1 "" $gen $config 15 | 16 | -------------------------------------------------------------------------------- /build_package.ps1: -------------------------------------------------------------------------------- 1 | param($folder, $gen, $config, $extra_cmake) 2 | 3 | cd $folder 4 | md build -Force | Out-Null 5 | cd build 6 | if ($gen){ 7 | & cmake $extra_cmake -G "$gen" .. 8 | } 9 | else{ 10 | & cmake $extra_cmake .. 11 | } 12 | if ($LastExitCode -ne 0) { 13 | throw "Exec: $ErrorMessage" 14 | } 15 | if ($config){ 16 | & cmake --build . --config $config 17 | } 18 | else{ 19 | & cmake --build . 20 | } 21 | if ($LastExitCode -ne 0) { 22 | throw "Exec: $ErrorMessage" 23 | } 24 | 25 | -------------------------------------------------------------------------------- /build_third_party.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ROOT=$(pwd) 4 | 5 | git submodule update --init --recursive 6 | 7 | # build googletest 8 | GTEST_FILE="third_party/googletest/build/googlemock/gtest/libgtest.a" 9 | if [ ! -f "$GTEST_FILE" ] 10 | then 11 | cd third_party/googletest 12 | mkdir build || true 13 | cd build 14 | cmake .. 15 | make 16 | cd $ROOT 17 | fi 18 | 19 | # build libut 20 | #UT_FILE="third_party/uthash/libut/libut.so" 21 | #if [ ! -f "$UT_FILE" ] 22 | #then 23 | #cp utMakefile third_party/uthash/libut/Makefile 24 | #cd third_party/uthash/libut 25 | #make clean 26 | #make 27 | #git checkout Makefile 28 | #cd $ROOT 29 | #fi 30 | 31 | -------------------------------------------------------------------------------- /internal_utils.cmake: -------------------------------------------------------------------------------- 1 | macro(fix_default_compiler_settings_) 2 | if (MSVC) 3 | foreach (flag_var 4 | CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE 5 | CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) 6 | # We prefer more strict warning checking for building Google Test. 7 | # Replaces /W3 with /W4 in defaults. 8 | string(REPLACE "/W3" "/W4" ${flag_var} "${${flag_var}}") 9 | endforeach() 10 | endif() 11 | endmacro() 12 | 13 | # Defines the compiler/linker flags used to build 14 | macro(config_compiler_and_linker) 15 | # Defines CMAKE_USE_PTHREADS_INIT and CMAKE_THREAD_LIBS_INIT. 16 | find_package(Threads) 17 | fix_default_compiler_settings_() 18 | 19 | if (MSVC) 20 | set(cxx_base_flags "-GS -W4 -wd4251 -wd4275 -nologo -J -Zi -DWIN32") 21 | if (MSVC_VERSION LESS 1400) # 1400 is Visual Studio 2005 22 | # Suppress spurious warnings MSVC 7.1 sometimes issues. 23 | # Forcing value to bool. 24 | set(cxx_base_flags "${cxx_base_flags} -wd4800") 25 | # Copy constructor and assignment operator could not be generated. 26 | set(cxx_base_flags "${cxx_base_flags} -wd4511 -wd4512") 27 | # Compatibility warnings not applicable to Google Test. 28 | # Resolved overload was found by argument-dependent lookup. 29 | set(cxx_base_flags "${cxx_base_flags} -wd4675") 30 | endif() 31 | if (MSVC_VERSION LESS 1500) # 1500 is Visual Studio 2008 32 | # Conditional expression is constant. 33 | # When compiling with /W4, we get several instances of C4127 34 | # (Conditional expression is constant). In our code, we disable that 35 | # warning on a case-by-case basis. However, on Visual Studio 2005, 36 | # the warning fires on std::list. Therefore on that compiler and earlier, 37 | # we disable the warning project-wide. 38 | set(cxx_base_flags "${cxx_base_flags} -wd4127") 39 | endif() 40 | if (NOT (MSVC_VERSION LESS 1700)) # 1700 is Visual Studio 2012. 41 | # Suppress "unreachable code" warning on VS 2012 and later. 42 | # http://stackoverflow.com/questions/3232669 explains the issue. 43 | set(cxx_base_flags "${cxx_base_flags} -wd4702") 44 | endif() 45 | # if (NOT (MSVC_VERSION GREATER 1900)) # 1900 is Visual Studio 2015 46 | # # BigObj required for tests. 47 | # set(cxx_base_flags "${cxx_base_flags} -bigobj") 48 | # endif() 49 | 50 | set(cxx_flags "${cxx_base_flags} -D_UNICODE -DUNICODE -DWIN32 -D_WIN32") 51 | set(cxx_flags "${cxx_base_flags} -DSTRICT -DWIN32_LEAN_AND_MEAN") 52 | set(c_flags "${cxx_flags}") 53 | set(CMAKE_CXX_FLAGS "${cxx_flags}") 54 | set(CMAKE_C_FLAGS "${c_flags}") 55 | 56 | elseif(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang" OR ${CMAKE_CXX_COMPILER_ID} MATCHES "GNU") 57 | set(base_flags "-fpic -pthread -O2 -g -fno-strict-aliasing -fwrapv -Wall -Wextra") 58 | set(base_flags "${base_flags} -Wno-unused-parameter -Wno-unused-variable -Wno-unused-but-set-variable -Wno-unused-function") 59 | set(cxx_flags "-std=c++11 ${base_flags}") 60 | set(c_flags "-std=gnu11 ${base_flags}") 61 | set(CMAKE_CXX_FLAGS "${cxx_flags}") 62 | set(CMAKE_C_FLAGS "${c_flags}") 63 | endif() 64 | endmacro() 65 | 66 | # Defines the libraries. 67 | function(cxx_library_with_type name type cxx_flags) 68 | # type can be either STATIC or SHARED to denote a static or shared library. 69 | # ARGN refers to additional arguments after 'cxx_flags'. 70 | add_library(${name} ${type} ${ARGN}) 71 | set_target_properties(${name} 72 | PROPERTIES 73 | COMPILE_FLAGS "${cxx_flags}") 74 | if (CMAKE_USE_PTHREADS_INIT) 75 | target_link_libraries(${name} ${CMAKE_THREAD_LIBS_INIT}) 76 | endif() 77 | endfunction() 78 | 79 | ######################################################################## 80 | # 81 | # Helper functions for creating build targets. 82 | 83 | function(cxx_shared_library name cxx_flags) 84 | cxx_library_with_type(${name} SHARED "${cxx_flags}" ${ARGN}) 85 | endfunction() 86 | 87 | function(cxx_library name cxx_flags) 88 | cxx_library_with_type(${name} "" "${cxx_flags}" ${ARGN}) 89 | endfunction() 90 | 91 | # cxx_executable_with_flags(name cxx_flags libs srcs...) 92 | # 93 | # creates a named C++ executable that depends on the given libraries and 94 | # is built from the given source files with the given compiler flags. 95 | function(cxx_executable_with_flags name cxx_flags libs) 96 | add_executable(${name} ${ARGN}) 97 | if (cxx_flags) 98 | set_target_properties(${name} 99 | PROPERTIES 100 | COMPILE_FLAGS "${cxx_flags}") 101 | endif() 102 | if (BUILD_SHARED_LIBS) 103 | set_target_properties(${name} 104 | PROPERTIES 105 | COMPILE_DEFINITIONS "GTEST_LINKED_AS_SHARED_LIBRARY=1") 106 | endif() 107 | # To support mixing linking in static and dynamic libraries, link each 108 | # library in with an extra call to target_link_libraries. 109 | foreach (lib "${libs}") 110 | target_link_libraries(${name} ${lib}) 111 | endforeach() 112 | endfunction() 113 | 114 | # cxx_executable(name dir lib srcs...) 115 | # 116 | # creates a named target that depends on the given libs and is built 117 | # from the given source files. dir/name.cc is implicitly included in 118 | # the source file list. 119 | function(cxx_executable name dir libs) 120 | cxx_executable_with_flags( 121 | ${name} "${cxx_default}" "${libs}" "${dir}/${name}.cc" ${ARGN}) 122 | endfunction() 123 | 124 | -------------------------------------------------------------------------------- /src/dbg.h: -------------------------------------------------------------------------------- 1 | #ifndef __dbg_h__ 2 | #define __dbg_h__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define INFO_STR "%s (%d): %s: " 9 | 10 | #ifdef NDEBUG 11 | #define debug(M, ...) 12 | #else 13 | #define debug(M, ...) fprintf(stderr, "[DEBUG] " INFO_STR M "\n", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__) 14 | #endif 15 | 16 | #define clean_errno() (errno == 0 ? "None" : strerror(errno)) 17 | 18 | #define log_err(M, ...) fprintf(stderr, "[ERROR] (" INFO_STR "errno: %s) " M "\n", __FILE__, __LINE__, __FUNCTION__, clean_errno(), ##__VA_ARGS__) 19 | 20 | #define log_warn(M, ...) fprintf(stderr, "[WARN] (" INFO_STR "errno: %s) " M "\n", __FILE__, __LINE__, __FUNCTION__, clean_errno(), ##__VA_ARGS__) 21 | 22 | #define log_info(M, ...) fprintf(stderr, "[INFO] (" INFO_STR ") " M "\n", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__) 23 | 24 | #define check(A, M, ...) do {if(!(A)) { log_err(M, ##__VA_ARGS__); errno=0; goto error; }} while (0) 25 | 26 | #define sentinel(M, ...) do { log_err(M, ##__VA_ARGS__); errno=0; goto error; } while (0) 27 | 28 | #define check_mem(A) check((A), "Out of memory.") 29 | 30 | #define check_debug(A, M, ...) do {if(!(A)) { debug(M, ##__VA_ARGS__); errno=0; goto error; }} while (0) 31 | 32 | #define check_silently(A) do {if(!(A)) { goto error; }} while (0) 33 | 34 | #define check_goto(A, M, G, ...) do {if(!(A)) { log_err(M, ##__VA_ARGS__); errno=0; goto G; }} while (0) 35 | 36 | #define check_log(A, M, ...) do {if(!(A)) { log_err(M, ##__VA_ARGS__); errno=0; }} while (0) 37 | 38 | #endif 39 | 40 | -------------------------------------------------------------------------------- /src/ikcp.c: -------------------------------------------------------------------------------- 1 | //===================================================================== 2 | // 3 | // KCP - A Better ARQ Protocol Implementation 4 | // skywind3000 (at) gmail.com, 2010-2011 5 | // 6 | // Features: 7 | // + Average RTT reduce 30% - 40% vs traditional ARQ like tcp. 8 | // + Maximum RTT reduce three times vs tcp. 9 | // + Lightweight, distributed as a single source file. 10 | // 11 | //===================================================================== 12 | #include "ikcp.h" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | 21 | //===================================================================== 22 | // KCP BASIC 23 | //===================================================================== 24 | const IUINT32 IKCP_RTO_NDL = 30; // no delay min rto 25 | const IUINT32 IKCP_RTO_MIN = 100; // normal min rto 26 | const IUINT32 IKCP_RTO_DEF = 200; 27 | const IUINT32 IKCP_RTO_MAX = 60000; 28 | const IUINT32 IKCP_CMD_PUSH = 81; // cmd: push data 29 | const IUINT32 IKCP_CMD_ACK = 82; // cmd: ack 30 | const IUINT32 IKCP_CMD_WASK = 83; // cmd: window probe (ask) 31 | const IUINT32 IKCP_CMD_WINS = 84; // cmd: window size (tell) 32 | const IUINT32 IKCP_ASK_SEND = 1; // need to send IKCP_CMD_WASK 33 | const IUINT32 IKCP_ASK_TELL = 2; // need to send IKCP_CMD_WINS 34 | const IUINT32 IKCP_WND_SND = 32; 35 | const IUINT32 IKCP_WND_RCV = 32; 36 | const IUINT32 IKCP_MTU_DEF = 1400; 37 | const IUINT32 IKCP_ACK_FAST = 3; 38 | const IUINT32 IKCP_INTERVAL = 100; 39 | const IUINT32 IKCP_OVERHEAD = 24; 40 | const IUINT32 IKCP_DEADLINK = 20; 41 | const IUINT32 IKCP_THRESH_INIT = 2; 42 | const IUINT32 IKCP_THRESH_MIN = 2; 43 | const IUINT32 IKCP_PROBE_INIT = 7000; // 7 secs to probe window size 44 | const IUINT32 IKCP_PROBE_LIMIT = 120000; // up to 120 secs to probe window 45 | 46 | 47 | //--------------------------------------------------------------------- 48 | // encode / decode 49 | //--------------------------------------------------------------------- 50 | 51 | /* encode 8 bits unsigned int */ 52 | static inline char *ikcp_encode8u(char *p, unsigned char c) 53 | { 54 | *(unsigned char*)p++ = c; 55 | return p; 56 | } 57 | 58 | /* decode 8 bits unsigned int */ 59 | static inline const char *ikcp_decode8u(const char *p, unsigned char *c) 60 | { 61 | *c = *(unsigned char*)p++; 62 | return p; 63 | } 64 | 65 | /* encode 16 bits unsigned int (lsb) */ 66 | static inline char *ikcp_encode16u(char *p, unsigned short w) 67 | { 68 | #if IWORDS_BIG_ENDIAN 69 | *(unsigned char*)(p + 0) = (w & 255); 70 | *(unsigned char*)(p + 1) = (w >> 8); 71 | #else 72 | *(unsigned short*)(p) = w; 73 | #endif 74 | p += 2; 75 | return p; 76 | } 77 | 78 | /* decode 16 bits unsigned int (lsb) */ 79 | static inline const char *ikcp_decode16u(const char *p, unsigned short *w) 80 | { 81 | #if IWORDS_BIG_ENDIAN 82 | *w = *(const unsigned char*)(p + 1); 83 | *w = *(const unsigned char*)(p + 0) + (*w << 8); 84 | #else 85 | *w = *(const unsigned short*)p; 86 | #endif 87 | p += 2; 88 | return p; 89 | } 90 | 91 | /* encode 32 bits unsigned int (lsb) */ 92 | static inline char *ikcp_encode32u(char *p, IUINT32 l) 93 | { 94 | #if IWORDS_BIG_ENDIAN 95 | *(unsigned char*)(p + 0) = (unsigned char)((l >> 0) & 0xff); 96 | *(unsigned char*)(p + 1) = (unsigned char)((l >> 8) & 0xff); 97 | *(unsigned char*)(p + 2) = (unsigned char)((l >> 16) & 0xff); 98 | *(unsigned char*)(p + 3) = (unsigned char)((l >> 24) & 0xff); 99 | #else 100 | *(IUINT32*)p = l; 101 | #endif 102 | p += 4; 103 | return p; 104 | } 105 | 106 | /* decode 32 bits unsigned int (lsb) */ 107 | static inline const char *ikcp_decode32u(const char *p, IUINT32 *l) 108 | { 109 | #if IWORDS_BIG_ENDIAN 110 | *l = *(const unsigned char*)(p + 3); 111 | *l = *(const unsigned char*)(p + 2) + (*l << 8); 112 | *l = *(const unsigned char*)(p + 1) + (*l << 8); 113 | *l = *(const unsigned char*)(p + 0) + (*l << 8); 114 | #else 115 | *l = *(const IUINT32*)p; 116 | #endif 117 | p += 4; 118 | return p; 119 | } 120 | 121 | static inline IUINT32 _imin_(IUINT32 a, IUINT32 b) { 122 | return a <= b ? a : b; 123 | } 124 | 125 | static inline IUINT32 _imax_(IUINT32 a, IUINT32 b) { 126 | return a >= b ? a : b; 127 | } 128 | 129 | static inline IUINT32 _ibound_(IUINT32 lower, IUINT32 middle, IUINT32 upper) 130 | { 131 | return _imin_(_imax_(lower, middle), upper); 132 | } 133 | 134 | static inline long _itimediff(IUINT32 later, IUINT32 earlier) 135 | { 136 | return ((IINT32)(later - earlier)); 137 | } 138 | 139 | //--------------------------------------------------------------------- 140 | // manage segment 141 | //--------------------------------------------------------------------- 142 | typedef struct IKCPSEG IKCPSEG; 143 | 144 | static void* (*ikcp_malloc_hook)(size_t) = NULL; 145 | static void (*ikcp_free_hook)(void *) = NULL; 146 | 147 | // internal malloc 148 | static void* ikcp_malloc(size_t size) { 149 | if (ikcp_malloc_hook) 150 | return ikcp_malloc_hook(size); 151 | return malloc(size); 152 | } 153 | 154 | // internal free 155 | static void ikcp_free(void *ptr) { 156 | if (ikcp_free_hook) { 157 | ikcp_free_hook(ptr); 158 | } else { 159 | free(ptr); 160 | } 161 | } 162 | 163 | // redefine allocator 164 | void ikcp_allocator(void* (*new_malloc)(size_t), void (*new_free)(void*)) 165 | { 166 | ikcp_malloc_hook = new_malloc; 167 | ikcp_free_hook = new_free; 168 | } 169 | 170 | // allocate a new kcp segment 171 | static IKCPSEG* ikcp_segment_new(ikcpcb *kcp, int size) 172 | { 173 | return (IKCPSEG*)ikcp_malloc(sizeof(IKCPSEG) + size); 174 | } 175 | 176 | // delete a segment 177 | static void ikcp_segment_delete(ikcpcb *kcp, IKCPSEG *seg) 178 | { 179 | ikcp_free(seg); 180 | } 181 | 182 | // write log 183 | void ikcp_log(ikcpcb *kcp, int mask, const char *fmt, ...) 184 | { 185 | char buffer[1024]; 186 | va_list argptr; 187 | if ((mask & kcp->logmask) == 0 || kcp->writelog == 0) return; 188 | va_start(argptr, fmt); 189 | vsprintf(buffer, fmt, argptr); 190 | va_end(argptr); 191 | kcp->writelog(buffer, kcp, kcp->user); 192 | } 193 | 194 | // check log mask 195 | static int ikcp_canlog(const ikcpcb *kcp, int mask) 196 | { 197 | if ((mask & kcp->logmask) == 0 || kcp->writelog == NULL) return 0; 198 | return 1; 199 | } 200 | 201 | // output segment 202 | static int ikcp_output(ikcpcb *kcp, const void *data, int size) 203 | { 204 | assert(kcp); 205 | assert(kcp->output); 206 | if (ikcp_canlog(kcp, IKCP_LOG_OUTPUT)) { 207 | ikcp_log(kcp, IKCP_LOG_OUTPUT, "[RO] %ld bytes", (long)size); 208 | } 209 | if (size == 0) return 0; 210 | return kcp->output((const char*)data, size, kcp, kcp->user); 211 | } 212 | 213 | // output queue 214 | void ikcp_qprint(const char *name, const struct IQUEUEHEAD *head) 215 | { 216 | #if 0 217 | const struct IQUEUEHEAD *p; 218 | printf("<%s>: [", name); 219 | for (p = head->next; p != head; p = p->next) { 220 | const IKCPSEG *seg = iqueue_entry(p, const IKCPSEG, node); 221 | printf("(%lu %d)", (unsigned long)seg->sn, (int)(seg->ts % 10000)); 222 | if (p->next != head) printf(","); 223 | } 224 | printf("]\n"); 225 | #endif 226 | } 227 | 228 | 229 | //--------------------------------------------------------------------- 230 | // create a new kcpcb 231 | //--------------------------------------------------------------------- 232 | ikcpcb* ikcp_create(IUINT32 conv, void *user) 233 | { 234 | ikcpcb *kcp = (ikcpcb*)ikcp_malloc(sizeof(struct IKCPCB)); 235 | if (kcp == NULL) return NULL; 236 | kcp->conv = conv; 237 | kcp->user = user; 238 | kcp->snd_una = 0; 239 | kcp->snd_nxt = 0; 240 | kcp->rcv_nxt = 0; 241 | kcp->ts_recent = 0; 242 | kcp->ts_lastack = 0; 243 | kcp->ts_probe = 0; 244 | kcp->probe_wait = 0; 245 | kcp->snd_wnd = IKCP_WND_SND; 246 | kcp->rcv_wnd = IKCP_WND_RCV; 247 | kcp->rmt_wnd = IKCP_WND_RCV; 248 | kcp->cwnd = 0; 249 | kcp->incr = 0; 250 | kcp->probe = 0; 251 | kcp->mtu = IKCP_MTU_DEF; 252 | kcp->mss = kcp->mtu - IKCP_OVERHEAD; 253 | 254 | kcp->buffer = (char*)ikcp_malloc((kcp->mtu + IKCP_OVERHEAD) * 3); 255 | if (kcp->buffer == NULL) { 256 | ikcp_free(kcp); 257 | return NULL; 258 | } 259 | 260 | iqueue_init(&kcp->snd_queue); 261 | iqueue_init(&kcp->rcv_queue); 262 | iqueue_init(&kcp->snd_buf); 263 | iqueue_init(&kcp->rcv_buf); 264 | kcp->nrcv_buf = 0; 265 | kcp->nsnd_buf = 0; 266 | kcp->nrcv_que = 0; 267 | kcp->nsnd_que = 0; 268 | kcp->state = 0; 269 | kcp->acklist = NULL; 270 | kcp->ackblock = 0; 271 | kcp->ackcount = 0; 272 | kcp->rx_srtt = 0; 273 | kcp->rx_rttval = 0; 274 | kcp->rx_rto = IKCP_RTO_DEF; 275 | kcp->rx_minrto = IKCP_RTO_MIN; 276 | kcp->current = 0; 277 | kcp->interval = IKCP_INTERVAL; 278 | kcp->ts_flush = IKCP_INTERVAL; 279 | kcp->nodelay = 0; 280 | kcp->updated = 0; 281 | kcp->logmask = 0; 282 | kcp->ssthresh = IKCP_THRESH_INIT; 283 | kcp->fastresend = 0; 284 | kcp->nocwnd = 0; 285 | kcp->xmit = 0; 286 | kcp->dead_link = IKCP_DEADLINK; 287 | kcp->output = NULL; 288 | kcp->writelog = NULL; 289 | 290 | return kcp; 291 | } 292 | 293 | 294 | //--------------------------------------------------------------------- 295 | // release a new kcpcb 296 | //--------------------------------------------------------------------- 297 | void ikcp_release(ikcpcb *kcp) 298 | { 299 | assert(kcp); 300 | if (kcp) { 301 | IKCPSEG *seg; 302 | while (!iqueue_is_empty(&kcp->snd_buf)) { 303 | seg = iqueue_entry(kcp->snd_buf.next, IKCPSEG, node); 304 | iqueue_del(&seg->node); 305 | ikcp_segment_delete(kcp, seg); 306 | } 307 | while (!iqueue_is_empty(&kcp->rcv_buf)) { 308 | seg = iqueue_entry(kcp->rcv_buf.next, IKCPSEG, node); 309 | iqueue_del(&seg->node); 310 | ikcp_segment_delete(kcp, seg); 311 | } 312 | while (!iqueue_is_empty(&kcp->snd_queue)) { 313 | seg = iqueue_entry(kcp->snd_queue.next, IKCPSEG, node); 314 | iqueue_del(&seg->node); 315 | ikcp_segment_delete(kcp, seg); 316 | } 317 | while (!iqueue_is_empty(&kcp->rcv_queue)) { 318 | seg = iqueue_entry(kcp->rcv_queue.next, IKCPSEG, node); 319 | iqueue_del(&seg->node); 320 | ikcp_segment_delete(kcp, seg); 321 | } 322 | if (kcp->buffer) { 323 | ikcp_free(kcp->buffer); 324 | } 325 | if (kcp->acklist) { 326 | ikcp_free(kcp->acklist); 327 | } 328 | 329 | kcp->nrcv_buf = 0; 330 | kcp->nsnd_buf = 0; 331 | kcp->nrcv_que = 0; 332 | kcp->nsnd_que = 0; 333 | kcp->ackcount = 0; 334 | kcp->buffer = NULL; 335 | kcp->acklist = NULL; 336 | ikcp_free(kcp); 337 | } 338 | } 339 | 340 | 341 | 342 | //--------------------------------------------------------------------- 343 | // user/upper level recv: returns size, returns below zero for EAGAIN 344 | //--------------------------------------------------------------------- 345 | int ikcp_recv(ikcpcb *kcp, char *buffer, int len) 346 | { 347 | struct IQUEUEHEAD *p; 348 | int ispeek = (len < 0)? 1 : 0; 349 | int peeksize; 350 | int recover = 0; 351 | IKCPSEG *seg; 352 | assert(kcp); 353 | 354 | if (iqueue_is_empty(&kcp->rcv_queue)) 355 | return -1; 356 | 357 | if (len < 0) len = -len; 358 | 359 | peeksize = ikcp_peeksize(kcp); 360 | 361 | if (peeksize < 0) 362 | return -2; 363 | 364 | if (peeksize > len) 365 | return -3; 366 | 367 | if (kcp->nrcv_que >= kcp->rcv_wnd) 368 | recover = 1; 369 | 370 | // merge fragment 371 | for (len = 0, p = kcp->rcv_queue.next; p != &kcp->rcv_queue; ) { 372 | int fragment; 373 | seg = iqueue_entry(p, IKCPSEG, node); 374 | p = p->next; 375 | 376 | if (buffer) { 377 | memcpy(buffer, seg->data, seg->len); 378 | buffer += seg->len; 379 | } 380 | 381 | len += seg->len; 382 | fragment = seg->frg; 383 | 384 | if (ikcp_canlog(kcp, IKCP_LOG_RECV)) { 385 | ikcp_log(kcp, IKCP_LOG_RECV, "recv sn=%lu", seg->sn); 386 | } 387 | 388 | if (ispeek == 0) { 389 | iqueue_del(&seg->node); 390 | ikcp_segment_delete(kcp, seg); 391 | kcp->nrcv_que--; 392 | } 393 | 394 | if (fragment == 0) 395 | break; 396 | } 397 | 398 | assert(len == peeksize); 399 | 400 | // move available data from rcv_buf -> rcv_queue 401 | while (! iqueue_is_empty(&kcp->rcv_buf)) { 402 | IKCPSEG *seg = iqueue_entry(kcp->rcv_buf.next, IKCPSEG, node); 403 | if (seg->sn == kcp->rcv_nxt && kcp->nrcv_que < kcp->rcv_wnd) { 404 | iqueue_del(&seg->node); 405 | kcp->nrcv_buf--; 406 | iqueue_add_tail(&seg->node, &kcp->rcv_queue); 407 | kcp->nrcv_que++; 408 | kcp->rcv_nxt++; 409 | } else { 410 | break; 411 | } 412 | } 413 | 414 | // fast recover 415 | if (kcp->nrcv_que < kcp->rcv_wnd && recover) { 416 | // ready to send back IKCP_CMD_WINS in ikcp_flush 417 | // tell remote my window size 418 | kcp->probe |= IKCP_ASK_TELL; 419 | } 420 | 421 | return len; 422 | } 423 | 424 | 425 | //--------------------------------------------------------------------- 426 | // peek data size 427 | //--------------------------------------------------------------------- 428 | int ikcp_peeksize(const ikcpcb *kcp) 429 | { 430 | struct IQUEUEHEAD *p; 431 | IKCPSEG *seg; 432 | int length = 0; 433 | 434 | assert(kcp); 435 | 436 | if (iqueue_is_empty(&kcp->rcv_queue)) return -1; 437 | 438 | seg = iqueue_entry(kcp->rcv_queue.next, IKCPSEG, node); 439 | if (seg->frg == 0) return seg->len; 440 | 441 | if (kcp->nrcv_que < seg->frg + 1) return -1; 442 | 443 | for (p = kcp->rcv_queue.next; p != &kcp->rcv_queue; p = p->next) { 444 | seg = iqueue_entry(p, IKCPSEG, node); 445 | length += seg->len; 446 | if (seg->frg == 0) break; 447 | } 448 | 449 | return length; 450 | } 451 | 452 | 453 | //--------------------------------------------------------------------- 454 | // user/upper level send, returns below zero for error 455 | //--------------------------------------------------------------------- 456 | int ikcp_send(ikcpcb *kcp, const char *buffer, int len) 457 | { 458 | IKCPSEG *seg; 459 | int count, i; 460 | 461 | assert(kcp->mss > 0); 462 | if (len < 0) return -1; 463 | 464 | if (len <= (int)kcp->mss) count = 1; 465 | else count = (len + kcp->mss - 1) / kcp->mss; 466 | 467 | if (count > 255) return -2; 468 | 469 | if (count == 0) count = 1; 470 | 471 | // fragment 472 | for (i = 0; i < count; i++) { 473 | int size = len > (int)kcp->mss ? (int)kcp->mss : len; 474 | seg = ikcp_segment_new(kcp, size); 475 | assert(seg); 476 | if (seg == NULL) { 477 | return -2; 478 | } 479 | if (buffer && len > 0) { 480 | memcpy(seg->data, buffer, size); 481 | } 482 | seg->len = size; 483 | seg->frg = count - i - 1; 484 | iqueue_init(&seg->node); 485 | iqueue_add_tail(&seg->node, &kcp->snd_queue); 486 | kcp->nsnd_que++; 487 | if (buffer) { 488 | buffer += size; 489 | } 490 | len -= size; 491 | } 492 | 493 | return 0; 494 | } 495 | 496 | 497 | //--------------------------------------------------------------------- 498 | // parse ack 499 | //--------------------------------------------------------------------- 500 | static void ikcp_update_ack(ikcpcb *kcp, IINT32 rtt) 501 | { 502 | IINT32 rto = 0; 503 | if (kcp->rx_srtt == 0) { 504 | kcp->rx_srtt = rtt; 505 | kcp->rx_rttval = rtt / 2; 506 | } else { 507 | long delta = rtt - kcp->rx_srtt; 508 | if (delta < 0) delta = -delta; 509 | kcp->rx_rttval = (3 * kcp->rx_rttval + delta) / 4; 510 | kcp->rx_srtt = (7 * kcp->rx_srtt + rtt) / 8; 511 | if (kcp->rx_srtt < 1) kcp->rx_srtt = 1; 512 | } 513 | rto = kcp->rx_srtt + _imax_(1, 4 * kcp->rx_rttval); 514 | kcp->rx_rto = _ibound_(kcp->rx_minrto, rto, IKCP_RTO_MAX); 515 | } 516 | 517 | static void ikcp_shrink_buf(ikcpcb *kcp) 518 | { 519 | struct IQUEUEHEAD *p = kcp->snd_buf.next; 520 | if (p != &kcp->snd_buf) { 521 | IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node); 522 | kcp->snd_una = seg->sn; 523 | } else { 524 | kcp->snd_una = kcp->snd_nxt; 525 | } 526 | } 527 | 528 | static void ikcp_parse_ack(ikcpcb *kcp, IUINT32 sn) 529 | { 530 | struct IQUEUEHEAD *p, *next; 531 | 532 | if (_itimediff(sn, kcp->snd_una) < 0 || _itimediff(sn, kcp->snd_nxt) >= 0) 533 | return; 534 | 535 | for (p = kcp->snd_buf.next; p != &kcp->snd_buf; p = next) { 536 | IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node); 537 | next = p->next; 538 | if (sn == seg->sn) { 539 | iqueue_del(p); 540 | ikcp_segment_delete(kcp, seg); 541 | kcp->nsnd_buf--; 542 | break; 543 | } 544 | if (_itimediff(sn, seg->sn) < 0) { 545 | break; 546 | } 547 | } 548 | } 549 | 550 | static void ikcp_parse_una(ikcpcb *kcp, IUINT32 una) 551 | { 552 | struct IQUEUEHEAD *p, *next; 553 | for (p = kcp->snd_buf.next; p != &kcp->snd_buf; p = next) { 554 | IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node); 555 | next = p->next; 556 | if (_itimediff(una, seg->sn) > 0) { 557 | iqueue_del(p); 558 | ikcp_segment_delete(kcp, seg); 559 | kcp->nsnd_buf--; 560 | } else { 561 | break; 562 | } 563 | } 564 | } 565 | 566 | static void ikcp_parse_fastack(ikcpcb *kcp, IUINT32 sn) 567 | { 568 | struct IQUEUEHEAD *p, *next; 569 | 570 | if (_itimediff(sn, kcp->snd_una) < 0 || _itimediff(sn, kcp->snd_nxt) >= 0) 571 | return; 572 | 573 | for (p = kcp->snd_buf.next; p != &kcp->snd_buf; p = next) { 574 | IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node); 575 | next = p->next; 576 | if (_itimediff(sn, seg->sn) < 0) { 577 | break; 578 | } 579 | else if (sn != seg->sn) { 580 | seg->fastack++; 581 | } 582 | } 583 | } 584 | 585 | 586 | //--------------------------------------------------------------------- 587 | // ack append 588 | //--------------------------------------------------------------------- 589 | static void ikcp_ack_push(ikcpcb *kcp, IUINT32 sn, IUINT32 ts) 590 | { 591 | size_t newsize = kcp->ackcount + 1; 592 | IUINT32 *ptr; 593 | 594 | if (newsize > kcp->ackblock) { 595 | IUINT32 *acklist; 596 | size_t newblock; 597 | 598 | for (newblock = 8; newblock < newsize; newblock <<= 1); 599 | acklist = (IUINT32*)ikcp_malloc(newblock * sizeof(IUINT32) * 2); 600 | 601 | if (acklist == NULL) { 602 | assert(acklist != NULL); 603 | abort(); 604 | } 605 | 606 | if (kcp->acklist != NULL) { 607 | size_t x; 608 | for (x = 0; x < kcp->ackcount; x++) { 609 | acklist[x * 2 + 0] = kcp->acklist[x * 2 + 0]; 610 | acklist[x * 2 + 1] = kcp->acklist[x * 2 + 1]; 611 | } 612 | ikcp_free(kcp->acklist); 613 | } 614 | 615 | kcp->acklist = acklist; 616 | kcp->ackblock = newblock; 617 | } 618 | 619 | ptr = &kcp->acklist[kcp->ackcount * 2]; 620 | ptr[0] = sn; 621 | ptr[1] = ts; 622 | kcp->ackcount++; 623 | } 624 | 625 | static void ikcp_ack_get(const ikcpcb *kcp, int p, IUINT32 *sn, IUINT32 *ts) 626 | { 627 | if (sn) sn[0] = kcp->acklist[p * 2 + 0]; 628 | if (ts) ts[0] = kcp->acklist[p * 2 + 1]; 629 | } 630 | 631 | 632 | //--------------------------------------------------------------------- 633 | // parse data 634 | //--------------------------------------------------------------------- 635 | void ikcp_parse_data(ikcpcb *kcp, IKCPSEG *newseg) 636 | { 637 | struct IQUEUEHEAD *p, *prev; 638 | IUINT32 sn = newseg->sn; 639 | int repeat = 0; 640 | 641 | if (_itimediff(sn, kcp->rcv_nxt + kcp->rcv_wnd) >= 0 || 642 | _itimediff(sn, kcp->rcv_nxt) < 0) { 643 | ikcp_segment_delete(kcp, newseg); 644 | return; 645 | } 646 | 647 | for (p = kcp->rcv_buf.prev; p != &kcp->rcv_buf; p = prev) { 648 | IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node); 649 | prev = p->prev; 650 | if (seg->sn == sn) { 651 | repeat = 1; 652 | break; 653 | } 654 | if (_itimediff(sn, seg->sn) > 0) { 655 | break; 656 | } 657 | } 658 | 659 | if (repeat == 0) { 660 | iqueue_init(&newseg->node); 661 | iqueue_add(&newseg->node, p); 662 | kcp->nrcv_buf++; 663 | } else { 664 | ikcp_segment_delete(kcp, newseg); 665 | } 666 | 667 | #if 0 668 | ikcp_qprint("rcvbuf", &kcp->rcv_buf); 669 | printf("rcv_nxt=%lu\n", kcp->rcv_nxt); 670 | #endif 671 | 672 | // move available data from rcv_buf -> rcv_queue 673 | while (! iqueue_is_empty(&kcp->rcv_buf)) { 674 | IKCPSEG *seg = iqueue_entry(kcp->rcv_buf.next, IKCPSEG, node); 675 | if (seg->sn == kcp->rcv_nxt && kcp->nrcv_que < kcp->rcv_wnd) { 676 | iqueue_del(&seg->node); 677 | kcp->nrcv_buf--; 678 | iqueue_add_tail(&seg->node, &kcp->rcv_queue); 679 | kcp->nrcv_que++; 680 | kcp->rcv_nxt++; 681 | } else { 682 | break; 683 | } 684 | } 685 | 686 | #if 0 687 | ikcp_qprint("queue", &kcp->rcv_queue); 688 | printf("rcv_nxt=%lu\n", kcp->rcv_nxt); 689 | #endif 690 | 691 | #if 1 692 | // printf("snd(buf=%d, queue=%d)\n", kcp->nsnd_buf, kcp->nsnd_que); 693 | // printf("rcv(buf=%d, queue=%d)\n", kcp->nrcv_buf, kcp->nrcv_que); 694 | #endif 695 | } 696 | 697 | 698 | //--------------------------------------------------------------------- 699 | // input data 700 | //--------------------------------------------------------------------- 701 | int ikcp_input(ikcpcb *kcp, const char *data, long size) 702 | { 703 | IUINT32 una = kcp->snd_una; 704 | IUINT32 maxack = 0; 705 | int flag = 0; 706 | 707 | if (ikcp_canlog(kcp, IKCP_LOG_INPUT)) { 708 | ikcp_log(kcp, IKCP_LOG_INPUT, "[RI] %d bytes", size); 709 | } 710 | 711 | if (data == NULL || size < 24) return -1; 712 | 713 | while (1) { 714 | IUINT32 ts, sn, len, una, conv; 715 | IUINT16 wnd; 716 | IUINT8 cmd, frg; 717 | IKCPSEG *seg; 718 | 719 | if (size < (int)IKCP_OVERHEAD) break; 720 | 721 | data = ikcp_decode32u(data, &conv); 722 | if (conv != kcp->conv) return -1; 723 | 724 | data = ikcp_decode8u(data, &cmd); 725 | data = ikcp_decode8u(data, &frg); 726 | data = ikcp_decode16u(data, &wnd); 727 | data = ikcp_decode32u(data, &ts); 728 | data = ikcp_decode32u(data, &sn); 729 | data = ikcp_decode32u(data, &una); 730 | data = ikcp_decode32u(data, &len); 731 | 732 | size -= IKCP_OVERHEAD; 733 | 734 | if ((long)size < (long)len) return -2; 735 | 736 | if (cmd != IKCP_CMD_PUSH && cmd != IKCP_CMD_ACK && 737 | cmd != IKCP_CMD_WASK && cmd != IKCP_CMD_WINS) 738 | return -3; 739 | 740 | kcp->rmt_wnd = wnd; 741 | ikcp_parse_una(kcp, una); 742 | ikcp_shrink_buf(kcp); 743 | 744 | if (cmd == IKCP_CMD_ACK) { 745 | if (_itimediff(kcp->current, ts) >= 0) { 746 | ikcp_update_ack(kcp, _itimediff(kcp->current, ts)); 747 | } 748 | ikcp_parse_ack(kcp, sn); 749 | ikcp_shrink_buf(kcp); 750 | if (flag == 0) { 751 | flag = 1; 752 | maxack = sn; 753 | } else { 754 | if (_itimediff(sn, maxack) > 0) { 755 | maxack = sn; 756 | } 757 | } 758 | if (ikcp_canlog(kcp, IKCP_LOG_IN_ACK)) { 759 | ikcp_log(kcp, IKCP_LOG_IN_DATA, 760 | "input ack: sn=%lu rtt=%ld rto=%ld", sn, 761 | (long)_itimediff(kcp->current, ts), 762 | (long)kcp->rx_rto); 763 | } 764 | } 765 | else if (cmd == IKCP_CMD_PUSH) { 766 | if (ikcp_canlog(kcp, IKCP_LOG_IN_DATA)) { 767 | ikcp_log(kcp, IKCP_LOG_IN_DATA, 768 | "input psh: sn=%lu ts=%lu", sn, ts); 769 | } 770 | if (_itimediff(sn, kcp->rcv_nxt + kcp->rcv_wnd) < 0) { 771 | ikcp_ack_push(kcp, sn, ts); 772 | if (_itimediff(sn, kcp->rcv_nxt) >= 0) { 773 | seg = ikcp_segment_new(kcp, len); 774 | seg->conv = conv; 775 | seg->cmd = cmd; 776 | seg->frg = frg; 777 | seg->wnd = wnd; 778 | seg->ts = ts; 779 | seg->sn = sn; 780 | seg->una = una; 781 | seg->len = len; 782 | 783 | if (len > 0) { 784 | memcpy(seg->data, data, len); 785 | } 786 | 787 | ikcp_parse_data(kcp, seg); 788 | } 789 | } 790 | } 791 | else if (cmd == IKCP_CMD_WASK) { 792 | // ready to send back IKCP_CMD_WINS in ikcp_flush 793 | // tell remote my window size 794 | kcp->probe |= IKCP_ASK_TELL; 795 | if (ikcp_canlog(kcp, IKCP_LOG_IN_PROBE)) { 796 | ikcp_log(kcp, IKCP_LOG_IN_PROBE, "input probe"); 797 | } 798 | } 799 | else if (cmd == IKCP_CMD_WINS) { 800 | // do nothing 801 | if (ikcp_canlog(kcp, IKCP_LOG_IN_WINS)) { 802 | ikcp_log(kcp, IKCP_LOG_IN_WINS, 803 | "input wins: %lu", (IUINT32)(wnd)); 804 | } 805 | } 806 | else { 807 | return -3; 808 | } 809 | 810 | data += len; 811 | size -= len; 812 | } 813 | 814 | if (flag != 0) { 815 | ikcp_parse_fastack(kcp, maxack); 816 | } 817 | 818 | if (_itimediff(kcp->snd_una, una) > 0) { 819 | if (kcp->cwnd < kcp->rmt_wnd) { 820 | IUINT32 mss = kcp->mss; 821 | if (kcp->cwnd < kcp->ssthresh) { 822 | kcp->cwnd++; 823 | kcp->incr += mss; 824 | } else { 825 | if (kcp->incr < mss) kcp->incr = mss; 826 | kcp->incr += (mss * mss) / kcp->incr + (mss / 16); 827 | if ((kcp->cwnd + 1) * mss <= kcp->incr) { 828 | kcp->cwnd++; 829 | } 830 | } 831 | if (kcp->cwnd > kcp->rmt_wnd) { 832 | kcp->cwnd = kcp->rmt_wnd; 833 | kcp->incr = kcp->rmt_wnd * mss; 834 | } 835 | } 836 | } 837 | 838 | return 0; 839 | } 840 | 841 | 842 | //--------------------------------------------------------------------- 843 | // ikcp_encode_seg 844 | //--------------------------------------------------------------------- 845 | static char *ikcp_encode_seg(char *ptr, const IKCPSEG *seg) 846 | { 847 | ptr = ikcp_encode32u(ptr, seg->conv); 848 | ptr = ikcp_encode8u(ptr, (IUINT8)seg->cmd); 849 | ptr = ikcp_encode8u(ptr, (IUINT8)seg->frg); 850 | ptr = ikcp_encode16u(ptr, (IUINT16)seg->wnd); 851 | ptr = ikcp_encode32u(ptr, seg->ts); 852 | ptr = ikcp_encode32u(ptr, seg->sn); 853 | ptr = ikcp_encode32u(ptr, seg->una); 854 | ptr = ikcp_encode32u(ptr, seg->len); 855 | return ptr; 856 | } 857 | 858 | static int ikcp_wnd_unused(const ikcpcb *kcp) 859 | { 860 | if (kcp->nrcv_que < kcp->rcv_wnd) { 861 | return kcp->rcv_wnd - kcp->nrcv_que; 862 | } 863 | return 0; 864 | } 865 | 866 | 867 | //--------------------------------------------------------------------- 868 | // ikcp_flush 869 | //--------------------------------------------------------------------- 870 | void ikcp_flush(ikcpcb *kcp) 871 | { 872 | IUINT32 current = kcp->current; 873 | char *buffer = kcp->buffer; 874 | char *ptr = buffer; 875 | int count, size, i; 876 | IUINT32 resent, cwnd; 877 | IUINT32 rtomin; 878 | struct IQUEUEHEAD *p; 879 | int change = 0; 880 | int lost = 0; 881 | IKCPSEG seg; 882 | 883 | // 'ikcp_update' haven't been called. 884 | if (kcp->updated == 0) return; 885 | 886 | seg.conv = kcp->conv; 887 | seg.cmd = IKCP_CMD_ACK; 888 | seg.frg = 0; 889 | seg.wnd = ikcp_wnd_unused(kcp); 890 | seg.una = kcp->rcv_nxt; 891 | seg.len = 0; 892 | seg.sn = 0; 893 | seg.ts = 0; 894 | 895 | // flush acknowledges 896 | count = kcp->ackcount; 897 | for (i = 0; i < count; i++) { 898 | size = (int)(ptr - buffer); 899 | if (size + (int)IKCP_OVERHEAD > (int)kcp->mtu) { 900 | ikcp_output(kcp, buffer, size); 901 | ptr = buffer; 902 | } 903 | ikcp_ack_get(kcp, i, &seg.sn, &seg.ts); 904 | ptr = ikcp_encode_seg(ptr, &seg); 905 | } 906 | 907 | kcp->ackcount = 0; 908 | 909 | // probe window size (if remote window size equals zero) 910 | if (kcp->rmt_wnd == 0) { 911 | if (kcp->probe_wait == 0) { 912 | kcp->probe_wait = IKCP_PROBE_INIT; 913 | kcp->ts_probe = kcp->current + kcp->probe_wait; 914 | } 915 | else { 916 | if (_itimediff(kcp->current, kcp->ts_probe) >= 0) { 917 | if (kcp->probe_wait < IKCP_PROBE_INIT) 918 | kcp->probe_wait = IKCP_PROBE_INIT; 919 | kcp->probe_wait += kcp->probe_wait / 2; 920 | if (kcp->probe_wait > IKCP_PROBE_LIMIT) 921 | kcp->probe_wait = IKCP_PROBE_LIMIT; 922 | kcp->ts_probe = kcp->current + kcp->probe_wait; 923 | kcp->probe |= IKCP_ASK_SEND; 924 | } 925 | } 926 | } else { 927 | kcp->ts_probe = 0; 928 | kcp->probe_wait = 0; 929 | } 930 | 931 | // flush window probing commands 932 | if (kcp->probe & IKCP_ASK_SEND) { 933 | seg.cmd = IKCP_CMD_WASK; 934 | size = (int)(ptr - buffer); 935 | if (size + (int)IKCP_OVERHEAD > (int)kcp->mtu) { 936 | ikcp_output(kcp, buffer, size); 937 | ptr = buffer; 938 | } 939 | ptr = ikcp_encode_seg(ptr, &seg); 940 | } 941 | 942 | // flush window probing commands 943 | if (kcp->probe & IKCP_ASK_TELL) { 944 | seg.cmd = IKCP_CMD_WINS; 945 | size = (int)(ptr - buffer); 946 | if (size + (int)IKCP_OVERHEAD > (int)kcp->mtu) { 947 | ikcp_output(kcp, buffer, size); 948 | ptr = buffer; 949 | } 950 | ptr = ikcp_encode_seg(ptr, &seg); 951 | } 952 | 953 | kcp->probe = 0; 954 | 955 | // calculate window size 956 | cwnd = _imin_(kcp->snd_wnd, kcp->rmt_wnd); 957 | if (kcp->nocwnd == 0) cwnd = _imin_(kcp->cwnd, cwnd); 958 | 959 | // move data from snd_queue to snd_buf 960 | while (_itimediff(kcp->snd_nxt, kcp->snd_una + cwnd) < 0) { 961 | IKCPSEG *newseg; 962 | if (iqueue_is_empty(&kcp->snd_queue)) break; 963 | 964 | newseg = iqueue_entry(kcp->snd_queue.next, IKCPSEG, node); 965 | 966 | iqueue_del(&newseg->node); 967 | iqueue_add_tail(&newseg->node, &kcp->snd_buf); 968 | kcp->nsnd_que--; 969 | kcp->nsnd_buf++; 970 | 971 | newseg->conv = kcp->conv; 972 | newseg->cmd = IKCP_CMD_PUSH; 973 | newseg->wnd = seg.wnd; 974 | newseg->ts = current; 975 | newseg->sn = kcp->snd_nxt++; 976 | newseg->una = kcp->rcv_nxt; 977 | newseg->resendts = current; 978 | newseg->rto = kcp->rx_rto; 979 | newseg->fastack = 0; 980 | newseg->xmit = 0; 981 | } 982 | 983 | // calculate resent 984 | resent = (kcp->fastresend > 0)? (IUINT32)kcp->fastresend : 0xffffffff; 985 | rtomin = (kcp->nodelay == 0)? (kcp->rx_rto >> 3) : 0; 986 | 987 | // flush data segments 988 | for (p = kcp->snd_buf.next; p != &kcp->snd_buf; p = p->next) { 989 | IKCPSEG *segment = iqueue_entry(p, IKCPSEG, node); 990 | int needsend = 0; 991 | if (segment->xmit == 0) { 992 | needsend = 1; 993 | segment->xmit++; 994 | segment->rto = kcp->rx_rto; 995 | segment->resendts = current + segment->rto + rtomin; 996 | } 997 | else if (_itimediff(current, segment->resendts) >= 0) { 998 | needsend = 1; 999 | segment->xmit++; 1000 | kcp->xmit++; 1001 | if (kcp->nodelay == 0) { 1002 | segment->rto += kcp->rx_rto; 1003 | } else { 1004 | segment->rto += kcp->rx_rto / 2; 1005 | } 1006 | segment->resendts = current + segment->rto; 1007 | lost = 1; 1008 | } 1009 | else if (segment->fastack >= resent) { 1010 | needsend = 1; 1011 | segment->xmit++; 1012 | segment->fastack = 0; 1013 | segment->resendts = current + segment->rto; 1014 | change++; 1015 | } 1016 | 1017 | if (needsend) { 1018 | int size, need; 1019 | segment->ts = current; 1020 | segment->wnd = seg.wnd; 1021 | segment->una = kcp->rcv_nxt; 1022 | 1023 | size = (int)(ptr - buffer); 1024 | need = IKCP_OVERHEAD + segment->len; 1025 | 1026 | if (size + need > (int)kcp->mtu) { 1027 | ikcp_output(kcp, buffer, size); 1028 | ptr = buffer; 1029 | } 1030 | 1031 | ptr = ikcp_encode_seg(ptr, segment); 1032 | 1033 | if (segment->len > 0) { 1034 | memcpy(ptr, segment->data, segment->len); 1035 | ptr += segment->len; 1036 | } 1037 | 1038 | if (segment->xmit >= kcp->dead_link) { 1039 | kcp->state = -1; 1040 | } 1041 | } 1042 | } 1043 | 1044 | // flash remain segments 1045 | size = (int)(ptr - buffer); 1046 | if (size > 0) { 1047 | ikcp_output(kcp, buffer, size); 1048 | } 1049 | 1050 | // update ssthresh 1051 | if (change) { 1052 | IUINT32 inflight = kcp->snd_nxt - kcp->snd_una; 1053 | kcp->ssthresh = inflight / 2; 1054 | if (kcp->ssthresh < IKCP_THRESH_MIN) 1055 | kcp->ssthresh = IKCP_THRESH_MIN; 1056 | kcp->cwnd = kcp->ssthresh + resent; 1057 | kcp->incr = kcp->cwnd * kcp->mss; 1058 | } 1059 | 1060 | if (lost) { 1061 | kcp->ssthresh = cwnd / 2; 1062 | if (kcp->ssthresh < IKCP_THRESH_MIN) 1063 | kcp->ssthresh = IKCP_THRESH_MIN; 1064 | kcp->cwnd = 1; 1065 | kcp->incr = kcp->mss; 1066 | } 1067 | 1068 | if (kcp->cwnd < 1) { 1069 | kcp->cwnd = 1; 1070 | kcp->incr = kcp->mss; 1071 | } 1072 | } 1073 | 1074 | 1075 | //--------------------------------------------------------------------- 1076 | // update state (call it repeatedly, every 10ms-100ms), or you can ask 1077 | // ikcp_check when to call it again (without ikcp_input/_send calling). 1078 | // 'current' - current timestamp in millisec. 1079 | //--------------------------------------------------------------------- 1080 | void ikcp_update(ikcpcb *kcp, IUINT32 current) 1081 | { 1082 | IINT32 slap; 1083 | 1084 | kcp->current = current; 1085 | 1086 | if (kcp->updated == 0) { 1087 | kcp->updated = 1; 1088 | kcp->ts_flush = kcp->current; 1089 | } 1090 | 1091 | slap = _itimediff(kcp->current, kcp->ts_flush); 1092 | 1093 | if (slap >= 10000 || slap < -10000) { 1094 | kcp->ts_flush = kcp->current; 1095 | slap = 0; 1096 | } 1097 | 1098 | if (slap >= 0) { 1099 | kcp->ts_flush += kcp->interval; 1100 | if (_itimediff(kcp->current, kcp->ts_flush) >= 0) { 1101 | kcp->ts_flush = kcp->current + kcp->interval; 1102 | } 1103 | ikcp_flush(kcp); 1104 | } 1105 | } 1106 | 1107 | 1108 | //--------------------------------------------------------------------- 1109 | // Determine when should you invoke ikcp_update: 1110 | // returns when you should invoke ikcp_update in millisec, if there 1111 | // is no ikcp_input/_send calling. you can call ikcp_update in that 1112 | // time, instead of call update repeatly. 1113 | // Important to reduce unnacessary ikcp_update invoking. use it to 1114 | // schedule ikcp_update (eg. implementing an epoll-like mechanism, 1115 | // or optimize ikcp_update when handling massive kcp connections) 1116 | //--------------------------------------------------------------------- 1117 | IUINT32 ikcp_check(const ikcpcb *kcp, IUINT32 current) 1118 | { 1119 | IUINT32 ts_flush = kcp->ts_flush; 1120 | IINT32 tm_flush = 0x7fffffff; 1121 | IINT32 tm_packet = 0x7fffffff; 1122 | IUINT32 minimal = 0; 1123 | struct IQUEUEHEAD *p; 1124 | 1125 | if (kcp->updated == 0) { 1126 | return current; 1127 | } 1128 | 1129 | if (_itimediff(current, ts_flush) >= 10000 || 1130 | _itimediff(current, ts_flush) < -10000) { 1131 | ts_flush = current; 1132 | } 1133 | 1134 | if (_itimediff(current, ts_flush) >= 0) { 1135 | return current; 1136 | } 1137 | 1138 | tm_flush = _itimediff(ts_flush, current); 1139 | 1140 | for (p = kcp->snd_buf.next; p != &kcp->snd_buf; p = p->next) { 1141 | const IKCPSEG *seg = iqueue_entry(p, const IKCPSEG, node); 1142 | IINT32 diff = _itimediff(seg->resendts, current); 1143 | if (diff <= 0) { 1144 | return current; 1145 | } 1146 | if (diff < tm_packet) tm_packet = diff; 1147 | } 1148 | 1149 | minimal = (IUINT32)(tm_packet < tm_flush ? tm_packet : tm_flush); 1150 | if (minimal >= kcp->interval) minimal = kcp->interval; 1151 | 1152 | return current + minimal; 1153 | } 1154 | 1155 | 1156 | 1157 | int ikcp_setmtu(ikcpcb *kcp, int mtu) 1158 | { 1159 | char *buffer; 1160 | if (mtu < 50 || mtu < (int)IKCP_OVERHEAD) 1161 | return -1; 1162 | buffer = (char*)ikcp_malloc((mtu + IKCP_OVERHEAD) * 3); 1163 | if (buffer == NULL) 1164 | return -2; 1165 | kcp->mtu = mtu; 1166 | kcp->mss = kcp->mtu - IKCP_OVERHEAD; 1167 | ikcp_free(kcp->buffer); 1168 | kcp->buffer = buffer; 1169 | return 0; 1170 | } 1171 | 1172 | int ikcp_interval(ikcpcb *kcp, int interval) 1173 | { 1174 | if (interval > 5000) interval = 5000; 1175 | else if (interval < 10) interval = 10; 1176 | kcp->interval = interval; 1177 | return 0; 1178 | } 1179 | 1180 | int ikcp_nodelay(ikcpcb *kcp, int nodelay, int interval, int resend, int nc) 1181 | { 1182 | if (nodelay >= 0) { 1183 | kcp->nodelay = nodelay; 1184 | if (nodelay) { 1185 | kcp->rx_minrto = IKCP_RTO_NDL; 1186 | } 1187 | else { 1188 | kcp->rx_minrto = IKCP_RTO_MIN; 1189 | } 1190 | } 1191 | if (interval >= 0) { 1192 | if (interval > 5000) interval = 5000; 1193 | else if (interval < 10) interval = 10; 1194 | kcp->interval = interval; 1195 | } 1196 | if (resend >= 0) { 1197 | kcp->fastresend = resend; 1198 | } 1199 | if (nc >= 0) { 1200 | kcp->nocwnd = nc; 1201 | } 1202 | return 0; 1203 | } 1204 | 1205 | 1206 | int ikcp_wndsize(ikcpcb *kcp, int sndwnd, int rcvwnd) 1207 | { 1208 | if (kcp) { 1209 | if (sndwnd > 0) { 1210 | kcp->snd_wnd = sndwnd; 1211 | } 1212 | if (rcvwnd > 0) { 1213 | kcp->rcv_wnd = rcvwnd; 1214 | } 1215 | } 1216 | return 0; 1217 | } 1218 | 1219 | int ikcp_waitsnd(const ikcpcb *kcp) 1220 | { 1221 | return kcp->nsnd_buf + kcp->nsnd_que; 1222 | } 1223 | 1224 | 1225 | -------------------------------------------------------------------------------- /src/ikcp.h: -------------------------------------------------------------------------------- 1 | //===================================================================== 2 | // 3 | // KCP - A Better ARQ Protocol Implementation 4 | // skywind3000 (at) gmail.com, 2010-2011 5 | // 6 | // Features: 7 | // + Average RTT reduce 30% - 40% vs traditional ARQ like tcp. 8 | // + Maximum RTT reduce three times vs tcp. 9 | // + Lightweight, distributed as a single source file. 10 | // 11 | //===================================================================== 12 | #ifndef __IKCP_H__ 13 | #define __IKCP_H__ 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | 20 | //===================================================================== 21 | // 32BIT INTEGER DEFINITION 22 | //===================================================================== 23 | #ifndef __INTEGER_32_BITS__ 24 | #define __INTEGER_32_BITS__ 25 | #if defined(_WIN64) || defined(WIN64) || defined(__amd64__) || \ 26 | defined(__x86_64) || defined(__x86_64__) || defined(_M_IA64) || \ 27 | defined(_M_AMD64) 28 | typedef unsigned int ISTDUINT32; 29 | typedef int ISTDINT32; 30 | #elif defined(_WIN32) || defined(WIN32) || defined(__i386__) || \ 31 | defined(__i386) || defined(_M_X86) 32 | typedef unsigned long ISTDUINT32; 33 | typedef long ISTDINT32; 34 | #elif defined(__MACOS__) 35 | typedef UInt32 ISTDUINT32; 36 | typedef SInt32 ISTDINT32; 37 | #elif defined(__APPLE__) && defined(__MACH__) 38 | #include 39 | typedef u_int32_t ISTDUINT32; 40 | typedef int32_t ISTDINT32; 41 | #elif defined(__BEOS__) 42 | #include 43 | typedef u_int32_t ISTDUINT32; 44 | typedef int32_t ISTDINT32; 45 | #elif (defined(_MSC_VER) || defined(__BORLANDC__)) && (!defined(__MSDOS__)) 46 | typedef unsigned __int32 ISTDUINT32; 47 | typedef __int32 ISTDINT32; 48 | #elif defined(__GNUC__) 49 | #include 50 | typedef uint32_t ISTDUINT32; 51 | typedef int32_t ISTDINT32; 52 | #else 53 | typedef unsigned long ISTDUINT32; 54 | typedef long ISTDINT32; 55 | #endif 56 | #endif 57 | 58 | 59 | //===================================================================== 60 | // Integer Definition 61 | //===================================================================== 62 | #ifndef __IINT8_DEFINED 63 | #define __IINT8_DEFINED 64 | typedef char IINT8; 65 | #endif 66 | 67 | #ifndef __IUINT8_DEFINED 68 | #define __IUINT8_DEFINED 69 | typedef unsigned char IUINT8; 70 | #endif 71 | 72 | #ifndef __IUINT16_DEFINED 73 | #define __IUINT16_DEFINED 74 | typedef unsigned short IUINT16; 75 | #endif 76 | 77 | #ifndef __IINT16_DEFINED 78 | #define __IINT16_DEFINED 79 | typedef short IINT16; 80 | #endif 81 | 82 | #ifndef __IINT32_DEFINED 83 | #define __IINT32_DEFINED 84 | typedef ISTDINT32 IINT32; 85 | #endif 86 | 87 | #ifndef __IUINT32_DEFINED 88 | #define __IUINT32_DEFINED 89 | typedef ISTDUINT32 IUINT32; 90 | #endif 91 | 92 | #ifndef __IINT64_DEFINED 93 | #define __IINT64_DEFINED 94 | #if defined(_MSC_VER) || defined(__BORLANDC__) 95 | typedef __int64 IINT64; 96 | #else 97 | typedef long long IINT64; 98 | #endif 99 | #endif 100 | 101 | #ifndef __IUINT64_DEFINED 102 | #define __IUINT64_DEFINED 103 | #if defined(_MSC_VER) || defined(__BORLANDC__) 104 | typedef unsigned __int64 IUINT64; 105 | #else 106 | typedef unsigned long long IUINT64; 107 | #endif 108 | #endif 109 | 110 | #ifndef INLINE 111 | #if defined(__GNUC__) 112 | 113 | #if (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1)) 114 | #define INLINE __inline__ __attribute__((always_inline)) 115 | #else 116 | #define INLINE __inline__ 117 | #endif 118 | 119 | #elif (defined(_MSC_VER) || defined(__BORLANDC__) || defined(__WATCOMC__)) 120 | #define INLINE __inline 121 | #else 122 | #define INLINE 123 | #endif 124 | #endif 125 | 126 | #if (!defined(__cplusplus)) && (!defined(inline)) 127 | #define inline INLINE 128 | #endif 129 | 130 | 131 | //===================================================================== 132 | // QUEUE DEFINITION 133 | //===================================================================== 134 | #ifndef __IQUEUE_DEF__ 135 | #define __IQUEUE_DEF__ 136 | 137 | struct IQUEUEHEAD { 138 | struct IQUEUEHEAD *next, *prev; 139 | }; 140 | 141 | typedef struct IQUEUEHEAD iqueue_head; 142 | 143 | 144 | //--------------------------------------------------------------------- 145 | // queue init 146 | //--------------------------------------------------------------------- 147 | #define IQUEUE_HEAD_INIT(name) { &(name), &(name) } 148 | #define IQUEUE_HEAD(name) \ 149 | struct IQUEUEHEAD name = IQUEUE_HEAD_INIT(name) 150 | 151 | #define IQUEUE_INIT(ptr) ( \ 152 | (ptr)->next = (ptr), (ptr)->prev = (ptr)) 153 | 154 | #define IOFFSETOF(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 155 | 156 | #define ICONTAINEROF(ptr, type, member) ( \ 157 | (type*)( ((char*)((type*)ptr)) - IOFFSETOF(type, member)) ) 158 | 159 | #define IQUEUE_ENTRY(ptr, type, member) ICONTAINEROF(ptr, type, member) 160 | 161 | 162 | //--------------------------------------------------------------------- 163 | // queue operation 164 | //--------------------------------------------------------------------- 165 | #define IQUEUE_ADD(node, head) ( \ 166 | (node)->prev = (head), (node)->next = (head)->next, \ 167 | (head)->next->prev = (node), (head)->next = (node)) 168 | 169 | #define IQUEUE_ADD_TAIL(node, head) ( \ 170 | (node)->prev = (head)->prev, (node)->next = (head), \ 171 | (head)->prev->next = (node), (head)->prev = (node)) 172 | 173 | #define IQUEUE_DEL_BETWEEN(p, n) ((n)->prev = (p), (p)->next = (n)) 174 | 175 | #define IQUEUE_DEL(entry) (\ 176 | (entry)->next->prev = (entry)->prev, \ 177 | (entry)->prev->next = (entry)->next, \ 178 | (entry)->next = 0, (entry)->prev = 0) 179 | 180 | #define IQUEUE_DEL_INIT(entry) do { \ 181 | IQUEUE_DEL(entry); IQUEUE_INIT(entry); } while (0) 182 | 183 | #define IQUEUE_IS_EMPTY(entry) ((entry) == (entry)->next) 184 | 185 | #define iqueue_init IQUEUE_INIT 186 | #define iqueue_entry IQUEUE_ENTRY 187 | #define iqueue_add IQUEUE_ADD 188 | #define iqueue_add_tail IQUEUE_ADD_TAIL 189 | #define iqueue_del IQUEUE_DEL 190 | #define iqueue_del_init IQUEUE_DEL_INIT 191 | #define iqueue_is_empty IQUEUE_IS_EMPTY 192 | 193 | #define IQUEUE_FOREACH(iterator, head, TYPE, MEMBER) \ 194 | for ((iterator) = iqueue_entry((head)->next, TYPE, MEMBER); \ 195 | &((iterator)->MEMBER) != (head); \ 196 | (iterator) = iqueue_entry((iterator)->MEMBER.next, TYPE, MEMBER)) 197 | 198 | #define iqueue_foreach(iterator, head, TYPE, MEMBER) \ 199 | IQUEUE_FOREACH(iterator, head, TYPE, MEMBER) 200 | 201 | #define iqueue_foreach_entry(pos, head) \ 202 | for( (pos) = (head)->next; (pos) != (head) ; (pos) = (pos)->next ) 203 | 204 | 205 | #define __iqueue_splice(list, head) do { \ 206 | iqueue_head *first = (list)->next, *last = (list)->prev; \ 207 | iqueue_head *at = (head)->next; \ 208 | (first)->prev = (head), (head)->next = (first); \ 209 | (last)->next = (at), (at)->prev = (last); } while (0) 210 | 211 | #define iqueue_splice(list, head) do { \ 212 | if (!iqueue_is_empty(list)) __iqueue_splice(list, head); } while (0) 213 | 214 | #define iqueue_splice_init(list, head) do { \ 215 | iqueue_splice(list, head); iqueue_init(list); } while (0) 216 | 217 | 218 | #ifdef _MSC_VER 219 | #pragma warning(disable:4311) 220 | #pragma warning(disable:4312) 221 | #pragma warning(disable:4996) 222 | #endif 223 | 224 | #endif 225 | 226 | 227 | //--------------------------------------------------------------------- 228 | // WORD ORDER 229 | //--------------------------------------------------------------------- 230 | #ifndef IWORDS_BIG_ENDIAN 231 | #ifdef _BIG_ENDIAN_ 232 | #if _BIG_ENDIAN_ 233 | #define IWORDS_BIG_ENDIAN 1 234 | #endif 235 | #endif 236 | #ifndef IWORDS_BIG_ENDIAN 237 | #if defined(__hppa__) || \ 238 | defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \ 239 | (defined(__MIPS__) && defined(__MISPEB__)) || \ 240 | defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \ 241 | defined(__sparc__) || defined(__powerpc__) || \ 242 | defined(__mc68000__) || defined(__s390x__) || defined(__s390__) 243 | #define IWORDS_BIG_ENDIAN 1 244 | #endif 245 | #endif 246 | #ifndef IWORDS_BIG_ENDIAN 247 | #define IWORDS_BIG_ENDIAN 0 248 | #endif 249 | #endif 250 | 251 | 252 | 253 | //===================================================================== 254 | // SEGMENT 255 | //===================================================================== 256 | struct IKCPSEG 257 | { 258 | struct IQUEUEHEAD node; 259 | IUINT32 conv; 260 | IUINT32 cmd; 261 | IUINT32 frg; 262 | IUINT32 wnd; 263 | IUINT32 ts; 264 | IUINT32 sn; 265 | IUINT32 una; 266 | IUINT32 len; 267 | IUINT32 resendts; 268 | IUINT32 rto; 269 | IUINT32 fastack; 270 | IUINT32 xmit; 271 | char data[1]; 272 | }; 273 | 274 | 275 | //--------------------------------------------------------------------- 276 | // IKCPCB 277 | //--------------------------------------------------------------------- 278 | struct IKCPCB 279 | { 280 | IUINT32 conv, mtu, mss, state; 281 | IUINT32 snd_una, snd_nxt, rcv_nxt; 282 | IUINT32 ts_recent, ts_lastack, ssthresh; 283 | IINT32 rx_rttval, rx_srtt, rx_rto, rx_minrto; 284 | IUINT32 snd_wnd, rcv_wnd, rmt_wnd, cwnd, probe; 285 | IUINT32 current, interval, ts_flush, xmit; 286 | IUINT32 nrcv_buf, nsnd_buf; 287 | IUINT32 nrcv_que, nsnd_que; 288 | IUINT32 nodelay, updated; 289 | IUINT32 ts_probe, probe_wait; 290 | IUINT32 dead_link, incr; 291 | struct IQUEUEHEAD snd_queue; 292 | struct IQUEUEHEAD rcv_queue; 293 | struct IQUEUEHEAD snd_buf; 294 | struct IQUEUEHEAD rcv_buf; 295 | IUINT32 *acklist; 296 | IUINT32 ackcount; 297 | IUINT32 ackblock; 298 | void *user; 299 | char *buffer; 300 | int fastresend; 301 | int nocwnd; 302 | int logmask; 303 | int (*output)(const char *buf, int len, struct IKCPCB *kcp, void *user); 304 | void (*writelog)(const char *log, struct IKCPCB *kcp, void *user); 305 | }; 306 | 307 | 308 | typedef struct IKCPCB ikcpcb; 309 | 310 | #define IKCP_LOG_OUTPUT 1 311 | #define IKCP_LOG_INPUT 2 312 | #define IKCP_LOG_SEND 4 313 | #define IKCP_LOG_RECV 8 314 | #define IKCP_LOG_IN_DATA 16 315 | #define IKCP_LOG_IN_ACK 32 316 | #define IKCP_LOG_IN_PROBE 64 317 | #define IKCP_LOG_IN_WINS 128 318 | #define IKCP_LOG_OUT_DATA 256 319 | #define IKCP_LOG_OUT_ACK 512 320 | #define IKCP_LOG_OUT_PROBE 1024 321 | #define IKCP_LOG_OUT_WINS 2048 322 | 323 | #ifdef __cplusplus 324 | extern "C" { 325 | #endif 326 | 327 | //--------------------------------------------------------------------- 328 | // interface 329 | //--------------------------------------------------------------------- 330 | 331 | // create a new kcp control object, 'conv' must equal in two endpoint 332 | // from the same connection. 'user' will be passed to the output callback 333 | // output callback can be setup like this: 'kcp->output = my_udp_output' 334 | ikcpcb* ikcp_create(IUINT32 conv, void *user); 335 | 336 | // release kcp control object 337 | void ikcp_release(ikcpcb *kcp); 338 | 339 | // user/upper level recv: returns size, returns below zero for EAGAIN 340 | int ikcp_recv(ikcpcb *kcp, char *buffer, int len); 341 | 342 | // user/upper level send, returns below zero for error 343 | int ikcp_send(ikcpcb *kcp, const char *buffer, int len); 344 | 345 | // update state (call it repeatedly, every 10ms-100ms), or you can ask 346 | // ikcp_check when to call it again (without ikcp_input/_send calling). 347 | // 'current' - current timestamp in millisec. 348 | void ikcp_update(ikcpcb *kcp, IUINT32 current); 349 | 350 | // Determine when should you invoke ikcp_update: 351 | // returns when you should invoke ikcp_update in millisec, if there 352 | // is no ikcp_input/_send calling. you can call ikcp_update in that 353 | // time, instead of call update repeatly. 354 | // Important to reduce unnacessary ikcp_update invoking. use it to 355 | // schedule ikcp_update (eg. implementing an epoll-like mechanism, 356 | // or optimize ikcp_update when handling massive kcp connections) 357 | IUINT32 ikcp_check(const ikcpcb *kcp, IUINT32 current); 358 | 359 | // when you received a low level packet (eg. UDP packet), call it 360 | int ikcp_input(ikcpcb *kcp, const char *data, long size); 361 | 362 | // flush pending data 363 | void ikcp_flush(ikcpcb *kcp); 364 | 365 | // check the size of next message in the recv queue 366 | int ikcp_peeksize(const ikcpcb *kcp); 367 | 368 | // change MTU size, default is 1400 369 | int ikcp_setmtu(ikcpcb *kcp, int mtu); 370 | 371 | // set maximum window size: sndwnd=32, rcvwnd=32 by default 372 | int ikcp_wndsize(ikcpcb *kcp, int sndwnd, int rcvwnd); 373 | 374 | // get how many packet is waiting to be sent 375 | int ikcp_waitsnd(const ikcpcb *kcp); 376 | 377 | // fastest: ikcp_nodelay(kcp, 1, 20, 2, 1) 378 | // nodelay: 0:disable(default), 1:enable 379 | // interval: internal update timer interval in millisec, default is 100ms 380 | // resend: 0:disable fast resend(default), 1:enable fast resend 381 | // nc: 0:normal congestion control(default), 1:disable congestion control 382 | int ikcp_nodelay(ikcpcb *kcp, int nodelay, int interval, int resend, int nc); 383 | 384 | int ikcp_rcvbuf_count(const ikcpcb *kcp); 385 | int ikcp_sndbuf_count(const ikcpcb *kcp); 386 | 387 | void ikcp_log(ikcpcb *kcp, int mask, const char *fmt, ...); 388 | 389 | // setup allocator 390 | void ikcp_allocator(void* (*new_malloc)(size_t), void (*new_free)(void*)); 391 | 392 | 393 | #ifdef __cplusplus 394 | } 395 | #endif 396 | 397 | #endif 398 | 399 | 400 | -------------------------------------------------------------------------------- /src/kcpev.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifdef _WIN32 4 | # include 5 | # include 6 | # include 7 | #else 8 | # include 9 | # include 10 | # include 11 | #endif 12 | #include 13 | #include "dbg.h" 14 | #include "ikcp.h" 15 | #include "kcpev.h" 16 | #include "portable_endian.h" 17 | 18 | void server_udp_recv(EV_P_ struct ev_io *w, int revents); 19 | int kcp_send_command(Kcpev *kcpev, uint8_t command, const char *msg, size_t len); 20 | int check_create_kcp_timer(Kcpev *kcpev, timer_cb hcb); 21 | int kcpev_set_ev(struct ev_loop *loop, void *data, KcpevSock *evs, ev_io_callback cb); 22 | int kcpev_create_kcp(KcpevUdp *udp, int conv, int kcp_mode); 23 | void on_client_heartbeat_timer(EV_P_ ev_timer *w, int revents); 24 | void set_kcp_invalid(Kcpev *kcpev); 25 | void on_client_heartbeat_timer(EV_P_ ev_timer *w, int revents); 26 | void on_server_heartbeat_timer(EV_P_ ev_timer *w, int revents); 27 | 28 | static void* (*kcpev_malloc_hook)(size_t) = NULL; 29 | static void (*kcpev_free_hook)(void *) = NULL; 30 | 31 | static inline void *kcpev_malloc(size_t size) 32 | { 33 | if(kcpev_malloc_hook) 34 | { 35 | void *p = kcpev_malloc_hook(size); 36 | memset(p, 0, size); 37 | return p; 38 | } 39 | 40 | return calloc(1, size); 41 | } 42 | 43 | static inline void kcpev_free(void *p) 44 | { 45 | if(kcpev_free_hook) 46 | kcpev_free_hook(p); 47 | else 48 | free(p); 49 | } 50 | 51 | Kcpev *kcpev_create() 52 | { 53 | Kcpev *kcpev = kcpev_malloc(sizeof(Kcpev)); 54 | check_mem(kcpev); 55 | 56 | return kcpev; 57 | 58 | error: 59 | free(kcpev); 60 | return NULL; 61 | } 62 | 63 | 64 | void KcpevSock_destroy(struct ev_loop *loop, KcpevSock *evs) 65 | { 66 | if(evs->sock) 67 | { 68 | #ifdef _WIN32 69 | closesocket(evs->sock); 70 | #else 71 | close(evs->sock); 72 | #endif 73 | evs->sock = 0; 74 | } 75 | 76 | if(evs->evio) 77 | { 78 | ev_io_stop(loop, evs->evio); 79 | kcpev_free(evs->evio); 80 | evs->evio = NULL; 81 | } 82 | } 83 | 84 | void KcpevTcp_destroy(struct ev_loop *loop, KcpevTcp *evs) 85 | { 86 | 87 | KcpevSock_destroy(loop, (KcpevSock *)evs); 88 | if(evs->rb) 89 | { 90 | ringbuf_free(evs->rb); 91 | evs->rb = NULL; 92 | } 93 | } 94 | 95 | void KcpevUdp_destroy(struct ev_loop *loop, KcpevUdp *evs) 96 | { 97 | KcpevSock_destroy(loop, (KcpevSock *)evs); 98 | if(evs->evt) 99 | { 100 | ev_timer_stop(loop, evs->evt); 101 | kcpev_free(evs->evt); 102 | evs->evt = NULL; 103 | } 104 | if(evs->evh) 105 | { 106 | ev_timer_stop(loop, evs->evh); 107 | kcpev_free(evs->evh); 108 | evs->evh = NULL; 109 | } 110 | if(evs->kcp) 111 | { 112 | ikcp_release(evs->kcp); 113 | evs->kcp = NULL; 114 | } 115 | } 116 | 117 | void kcpev_destroy(Kcpev *kcpev) 118 | { 119 | KcpevTcp_destroy(kcpev->loop, &kcpev->tcp); 120 | KcpevUdp_destroy(kcpev->loop, &kcpev->udp); 121 | kcpev_free(kcpev); 122 | } 123 | 124 | KcpevServer *kcpev_server_create() 125 | { 126 | KcpevServer *kcpev = kcpev_malloc(sizeof(KcpevServer)); 127 | check_mem(kcpev); 128 | 129 | return kcpev; 130 | 131 | error: 132 | free(kcpev); 133 | return NULL; 134 | 135 | } 136 | 137 | void delete_hash(KcpevServer *kcpev) 138 | { 139 | Kcpev *current, *tmp; 140 | 141 | HASH_ITER(hh, kcpev->hash, current, tmp) { 142 | HASH_DEL(kcpev->hash, current); 143 | kcpev_destroy(current); 144 | } 145 | } 146 | 147 | void kcpev_server_destroy(KcpevServer *kcpev) 148 | { 149 | KcpevTcp_destroy(kcpev->loop, &kcpev->tcp); 150 | KcpevUdp_destroy(kcpev->loop, &kcpev->udp); 151 | delete_hash(kcpev); 152 | kcpev_free(kcpev); 153 | } 154 | 155 | // create ipv4 or ipv6 socket 156 | // sock_type = SOCK_STREAM or SOCK_DGRAM 157 | // ifaddress = INADDR_ANY, will auto pick an usable address 158 | // if port = KCPEV_INPORT_ANY, will auto pick an usable port 159 | // family = AF_UNSPEC or AF_INET or AF_INET6 160 | int kcpev_bind_socket(KcpevSock *sock, int sock_type, const char *port, int family, int reuse) 161 | { 162 | struct addrinfo hints, *addr; 163 | memset(&hints, 0, sizeof(hints)); 164 | hints.ai_family = family; 165 | hints.ai_socktype = sock_type; 166 | hints.ai_flags = AI_PASSIVE; 167 | 168 | int ret = getaddrinfo(NULL, port, &hints, &addr); 169 | check(ret == 0, "getaddrinfo ERROR: %s", gai_strerror(ret)); 170 | 171 | sock->sock = 0; 172 | 173 | struct addrinfo *p; 174 | for(p = addr; p != NULL; p = p->ai_next) 175 | { 176 | int s = socket(p->ai_family, p->ai_socktype, p->ai_protocol); 177 | check_goto(s >= 0, "socket", sock_error); 178 | 179 | if(reuse) 180 | { 181 | int opt = 1; 182 | 183 | #ifdef SO_REUSEPORT 184 | int optname = SO_REUSEPORT; 185 | #else 186 | int optname = SO_REUSEADDR; 187 | #endif 188 | ret = setsockopt(s, SOL_SOCKET, optname, &opt, sizeof(opt)); 189 | check(ret >= 0, "set reuse addr"); 190 | } 191 | 192 | ret = bind(s, p->ai_addr, p->ai_addrlen); 193 | check_goto(ret >= 0, "bind", sock_error); 194 | 195 | /*ret = setnonblocking(s);*/ 196 | /*check_goto(ret >= 0, "setnonblocking", sock_error);*/ 197 | 198 | sock->sock = s; 199 | break; 200 | 201 | sock_error: 202 | if(s >= 0) 203 | close(s); 204 | } 205 | 206 | freeaddrinfo(addr); 207 | 208 | check(sock->sock, "failed to create socket!"); 209 | 210 | return 0; 211 | 212 | error: 213 | return -1; 214 | } 215 | 216 | inline int kcpev_bind_tcp(KcpevTcp *sock, const char *port, int family, int reuse) 217 | { 218 | return kcpev_bind_socket((KcpevSock *)sock, SOCK_STREAM, port, family, reuse); 219 | } 220 | 221 | inline int kcpev_bind_udp(KcpevUdp *sock, const char *port, int family, int reuse) 222 | { 223 | return kcpev_bind_socket((KcpevSock *)sock, SOCK_DGRAM, port, family, reuse); 224 | } 225 | 226 | // create socket and bind it 227 | int kcpev_bind(Kcpev *kcpev, const char *port, int family, int reuse) 228 | { 229 | int ret = 0; 230 | 231 | // tcp连接是必须的 232 | ret = kcpev_bind_tcp(&kcpev->tcp, port, family, reuse); 233 | check(ret >= 0, "kcpev_bind_tcp"); 234 | 235 | struct sockaddr_storage client_addr; 236 | socklen_t addr_size = sizeof(client_addr); 237 | char hbuf[KCPEV_NI_MAXHOST], sbuf[KCPEV_NI_MAXSERV]; 238 | ret = getsockname(kcpev->tcp.sock, (struct sockaddr*)&client_addr, &addr_size); 239 | check(ret >= 0, "getpeername"); 240 | ret = getnameinfo((struct sockaddr *)&client_addr, addr_size, hbuf, sizeof(hbuf), \ 241 | sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV); 242 | check(ret >= 0, "getnameinfo"); 243 | debug("local tcp port[%s : %s]", hbuf, sbuf); 244 | 245 | // 如果udp创建失败,会退化到用tcp来通信 246 | ret = kcpev_bind_udp(&kcpev->udp, port, family, reuse); 247 | getsockname(kcpev->udp.sock, (struct sockaddr*)&client_addr, &addr_size); 248 | getnameinfo((struct sockaddr *)&client_addr, addr_size, hbuf, sizeof(hbuf), \ 249 | sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV); 250 | debug("local udp port[%s : %s]", hbuf, sbuf); 251 | if(ret < 0) 252 | { 253 | debug("failed to create udp socket\n"); 254 | } 255 | 256 | return 0; 257 | error: 258 | return -1; 259 | } 260 | 261 | int kcpev_connect_socket(KcpevSock *sock, int sock_type, const char *address, const char *port) 262 | { 263 | if(!sock->sock) 264 | return -1; 265 | 266 | struct addrinfo hints, *addr; 267 | memset(&hints, 0, sizeof(hints)); 268 | hints.ai_family = AF_UNSPEC; 269 | hints.ai_socktype = sock_type; 270 | hints.ai_flags = AI_PASSIVE; 271 | 272 | int ret = getaddrinfo(address, port, &hints, &addr); 273 | check(ret == 0, "getaddrinfo ERROR: %s", gai_strerror(ret)); 274 | 275 | struct addrinfo *p; 276 | int success = -1; 277 | for(p = addr; p != NULL; p = p->ai_next) 278 | { 279 | ret = connect(sock->sock, p->ai_addr, p->ai_addrlen); 280 | if(ret < 0) 281 | { 282 | log_err("connect"); 283 | continue; 284 | } 285 | success = 0; 286 | break; 287 | } 288 | 289 | check(success == 0, "connect"); 290 | 291 | return success; 292 | 293 | error: 294 | return -1; 295 | } 296 | 297 | inline int kcpev_connect_tcp(KcpevTcp *sock, const char *address, const char *port) 298 | { 299 | return kcpev_connect_socket((KcpevSock *)sock, SOCK_STREAM, address, port); 300 | } 301 | 302 | inline int kcpev_connect_udp(KcpevUdp *sock, const char *address, const char *port) 303 | { 304 | return kcpev_connect_socket((KcpevSock *)sock, SOCK_DGRAM, address, port); 305 | } 306 | 307 | int kcpev_connect(Kcpev *kcpev, const char *address, const char *port) 308 | { 309 | int ret = 0; 310 | 311 | // tcp连接是必须的 312 | ret = kcpev_connect_tcp(&kcpev->tcp, address, port); 313 | check(ret >= 0, "kcpev_connect_tcp"); 314 | 315 | // 如果udp连接失败,会退化到用tcp来通信 316 | ret = kcpev_connect_udp(&kcpev->udp, address, port); 317 | if(ret < 0) 318 | { 319 | debug("failed to connect udp socket\n"); 320 | if(kcpev->udp.sock) 321 | { 322 | close(kcpev->udp.sock); 323 | kcpev->udp.sock = 0; 324 | } 325 | 326 | } 327 | else if(kcpev->udp.kcp) 328 | { 329 | kcpev->udp.kcp->user = (void *)&kcpev->udp; 330 | 331 | } 332 | 333 | return 0; 334 | 335 | error: 336 | return -1; 337 | } 338 | 339 | int kcpev_listen(Kcpev *kcpev, int backlog) 340 | { 341 | int ret; 342 | ret = listen(kcpev->tcp.sock, backlog); 343 | check(ret >=0, "tcp listen"); 344 | 345 | return 0; 346 | error: 347 | return -1; 348 | } 349 | 350 | int kcpev_init_ev(Kcpev *kcpev, struct ev_loop *loop, void *data, ev_io_callback tcp_cb, ev_io_callback udp_cb) 351 | { 352 | check(kcpev, "kcpev is NULL"); 353 | 354 | kcpev->loop = loop; 355 | int ret = -1; 356 | 357 | if(tcp_cb) 358 | { 359 | ret = kcpev_set_ev(loop, data, (KcpevSock *)&kcpev->tcp, tcp_cb); 360 | check(ret >= 0, "set tcp ev"); 361 | } 362 | 363 | if(udp_cb) 364 | { 365 | ret = kcpev_set_ev(loop, data, (KcpevSock *)&kcpev->udp, udp_cb); 366 | check_log(ret >= 0, "set udp ev"); 367 | } 368 | 369 | return 0; 370 | error: 371 | return -1; 372 | } 373 | 374 | // try create udp connection 375 | int connect_client_udp(Kcpev *client, char *port, const struct sockaddr *addr, socklen_t addr_size, \ 376 | struct ev_loop *loop) 377 | { 378 | int ret = kcpev_bind_udp(&client->udp, port, addr->sa_family, 1); 379 | check(ret >= 0, "server udp bind"); 380 | 381 | ret = connect(client->udp.sock, addr, addr_size); 382 | check(ret >= 0, "connect client udp"); 383 | 384 | ret = kcpev_create_kcp(&client->udp, client->key.split_key.conv, KCPEV_KCP_MODE); 385 | check(ret >= 0, "client udp create kcp"); 386 | 387 | ret = kcpev_init_ev(client, loop, client, NULL, server_udp_recv); 388 | check(ret >= 0, "client init ev"); 389 | 390 | return 0; 391 | 392 | error: 393 | KcpevUdp_destroy(loop, &client->udp); 394 | return -1; 395 | } 396 | 397 | int udp_output(const char *buf, int len, ikcpcb *kcp, void *user) 398 | { 399 | KcpevUdp *client = user; 400 | int ret = send(client->sock, buf, len, 0); 401 | check(ret == len, "send"); 402 | 403 | error: 404 | return 0; 405 | } 406 | 407 | // 创建kcp, 会使用kcpev里面的conv属性和udp套接口, 408 | // 如果是空的,后面创建socket或者客户端连接服务端时会重新填写这两个属性 409 | // kcp_mode: 0 默认模式, 1 普通模式, 2 极速模式 410 | int kcpev_create_kcp(KcpevUdp *udp, int conv, int kcp_mode) 411 | { 412 | ikcpcb *kcp = ikcp_create(conv, udp); 413 | check_mem(kcp); 414 | 415 | ikcp_wndsize(kcp, 128, 128); 416 | 417 | switch(kcp_mode) 418 | { 419 | case 0: 420 | ikcp_nodelay(kcp, 0, 10, 0, 0); 421 | break; 422 | case 1: 423 | ikcp_nodelay(kcp, 0, 10, 0, 1); 424 | break; 425 | case 2: 426 | ikcp_nodelay(kcp, 1, 10, 2, 1); 427 | break; 428 | default: 429 | sentinel("unrecognized kcp_mode"); 430 | } 431 | 432 | kcp->output = udp_output; 433 | 434 | udp->kcp = kcp; 435 | return 0; 436 | 437 | error: 438 | if(kcp) 439 | ikcp_release(kcp); 440 | return -1; 441 | } 442 | 443 | // buf = uint32_t(len) + uint8_t(command) + data 444 | int on_client_recv(Kcpev *client, const char *buf, size_t len) 445 | { 446 | KcpevHeader header; 447 | const size_t header_size = sizeof(KcpevHeader); 448 | int ret = -1; 449 | 450 | check(len >= header_size, "recv data len < %zu", header_size); 451 | 452 | ret = header_from_net(&header, buf, len); 453 | check(ret == 0, "header_from_net"); 454 | 455 | check(header.size == len, "recv data len error, len = %zu, real_len = %u", len, header.size); 456 | 457 | const char *data = buf + header_size; 458 | size_t data_len = len - header_size; 459 | 460 | switch(header.command) 461 | { 462 | case COMMAND_DATA: 463 | client->recv_cb(client, data, data_len); 464 | break; 465 | 466 | case COMMAND_SHAKE_HAND1: 467 | if(!client->udp.sock) 468 | break; 469 | 470 | check(data_len == sizeof(KcpevKey), "set key data len error"); 471 | 472 | memcpy(client->key.uuid, data, sizeof(KcpevKey)); 473 | 474 | char uuids[KCPEV_UUID_PARSE_SIZE]; 475 | uuid_unparse(client->key.uuid, uuids); 476 | debug("set key successfully [%s]!", uuids); 477 | 478 | // udp shake hand 479 | ret = sock_send_command(client->udp.sock, COMMAND_SHAKE_HAND1, data, data_len); 480 | check(ret == 0, "send shake hand1 to server [%d]", ret); 481 | 482 | client->udp.status = UDP_SHAKING_HAND; 483 | break; 484 | 485 | case COMMAND_SHAKE_HAND2: 486 | if(!client->udp.sock) 487 | break; 488 | 489 | check(data_len == sizeof(KcpevKey), "set key data len error"); 490 | 491 | check(client->udp.status == UDP_SHAKING_HAND, 492 | "recv server shake hand2 msg at invalid status [%d]", client->udp.status); 493 | 494 | check(memcmp(data, &client->key, sizeof(KcpevKey)) == 0, 495 | "recv server shake hand2 key not match hand1 key"); 496 | 497 | ret = sock_send_command(client->udp.sock, COMMAND_SHAKE_HAND2, data, data_len); 498 | check(ret == 0, "send shake hand2 to server"); 499 | 500 | ret = check_create_kcp_timer(client, on_client_heartbeat_timer); 501 | check(ret >= 0, "check_create_kcp_timer"); 502 | 503 | client->udp.kcp->conv = client->key.split_key.conv; 504 | client->udp.status = UDP_READY; 505 | 506 | debug("shake hand successfully conv: %d.", client->udp.kcp->conv); 507 | break; 508 | 509 | case COMMAND_HEARTBEAT1: 510 | if(!is_kcp_valid(client)) 511 | break; 512 | 513 | check(data_len == sizeof(KcpevKey) + sizeof(uint64_t), "set key data len error"); 514 | 515 | check(client->udp.status == UDP_READY, 516 | "recv server shake hand2 msg at invalid status [%d]", client->udp.status); 517 | 518 | check(memcmp(data, &client->key, sizeof(KcpevKey)) == 0, 519 | "recv server shake hand2 key not match hand1 key"); 520 | 521 | ret = kcpev_send_command(client, COMMAND_HEARTBEAT2, data, data_len); 522 | check(ret == 0, "send shake hand"); 523 | 524 | client->udp.heart = ev_now(client->loop); 525 | debug("end heart beat"); 526 | break; 527 | 528 | case COMMAND_UDP_INVALID: 529 | if(!is_kcp_valid(client)) 530 | break; 531 | 532 | check(data_len == sizeof(KcpevKey), "set key data len error"); 533 | 534 | check(memcmp(data, &client->key, sizeof(KcpevKey)) == 0, 535 | "recv server shake hand2 key not match hand1 key"); 536 | 537 | set_kcp_invalid(client); 538 | 539 | break; 540 | 541 | default: 542 | sentinel("unrecognized command from server"); 543 | } 544 | 545 | return 0; 546 | 547 | error: 548 | return -1; 549 | } 550 | 551 | // buf = uint32_t(len) + uint8_t(command) + data 552 | int on_server_recv(KcpevServer *server, Kcpev *client, const char *buf, size_t len, \ 553 | const struct sockaddr *client_addr, int addr_size) 554 | { 555 | KcpevHeader header; 556 | int ret = -1; 557 | 558 | const size_t header_size = sizeof(KcpevHeader); 559 | check(len >= header_size, "recv data len < %zu", header_size); 560 | 561 | ret = header_from_net(&header, buf, len); 562 | check(ret == 0, "header_from_net"); 563 | check(header.size == len, "recv data len error len = %zu, real_len = %u", len, header.size); 564 | 565 | const char *data = buf + header_size; 566 | size_t data_len = len - header_size; 567 | 568 | char hbuf[KCPEV_NI_MAXHOST], sbuf[KCPEV_NI_MAXSERV]; 569 | 570 | ret = getnameinfo(client_addr, addr_size, hbuf, sizeof(hbuf), \ 571 | sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV); 572 | check(ret == 0, ""); 573 | 574 | KcpevKey *key; 575 | 576 | switch(header.command) 577 | { 578 | case COMMAND_DATA: 579 | 580 | // debug("recv client data: [%s : %s], [%d]", hbuf, sbuf, data_len); 581 | server->recv_cb(server, client, data, data_len); 582 | break; 583 | 584 | case COMMAND_SHAKE_HAND1: 585 | check(data_len == sizeof(KcpevKey), "udp shake len error [%s : %s]", hbuf, sbuf); 586 | 587 | key = (KcpevKey *)data; 588 | client = NULL; 589 | 590 | HASH_FIND(hh, server->hash, key, sizeof(KcpevKey), client); 591 | check(client, "udp shake client key not find [%s : %s]", hbuf, sbuf); 592 | 593 | // udp may failed, if so, use tcp then 594 | ret = connect_client_udp(client, server->port, client_addr, \ 595 | addr_size, server->loop); 596 | check(ret >= 0, "connect_client_udp"); 597 | client->udp.status = UDP_SHAKING_HAND; 598 | 599 | ret = sock_send_command(client->udp.sock, COMMAND_SHAKE_HAND2, (char *)&client->key, sizeof(KcpevKey)); 600 | 601 | if(ret == 0) 602 | debug("recv client shake hand1 msg"); 603 | else 604 | log_err("send client shake hand2 msg failed"); 605 | break; 606 | 607 | case COMMAND_SHAKE_HAND2: 608 | check(data_len == sizeof(KcpevKey), "udp shake len error [%s : %s]", hbuf, sbuf); 609 | 610 | key = (KcpevKey *)data; 611 | client = NULL; 612 | 613 | HASH_FIND(hh, server->hash, key, sizeof(KcpevKey), client); 614 | check(client, "udp shake client key not find [%s : %s]", hbuf, sbuf); 615 | check(client->udp.status == UDP_SHAKING_HAND, 616 | "recv client shake hand2 msg at invalid status [%d]", client->udp.status); 617 | 618 | ret = check_create_kcp_timer(client, on_server_heartbeat_timer); 619 | check(ret >= 0, "check_create_kcp_timer"); 620 | 621 | client->udp.status = UDP_READY; 622 | 623 | char uuids[KCPEV_UUID_PARSE_SIZE]; 624 | uuid_unparse(client->key.uuid, uuids); 625 | debug("successfully shake hand with client [%s]", uuids); 626 | break; 627 | 628 | case COMMAND_HEARTBEAT1: 629 | check(client, "client is NULL"); 630 | 631 | if(!is_kcp_valid(client)) 632 | break; 633 | 634 | check(data_len == sizeof(KcpevKey), "udp shake len error [%s : %s]", hbuf, sbuf); 635 | 636 | check(memcmp(data, &client->key, sizeof(KcpevKey)) == 0, 637 | "recv client heart beat1 key not match"); 638 | 639 | char send_buf[sizeof(KcpevKey) + sizeof(uint64_t)]; 640 | memcpy(send_buf, (char *)&client->key, sizeof(KcpevKey)); 641 | *(uint64_t *)(send_buf + sizeof(KcpevKey)) = htobe64(*(uint64_t *)&client->udp.heart); 642 | 643 | int ret = kcp_send_command(client, COMMAND_HEARTBEAT1, send_buf, sizeof(send_buf)); 644 | check(ret == 0, ""); 645 | 646 | debug("begin heart beat [%lu : %lu]", *(uint64_t *)&client->udp.heart, *(uint64_t *)(send_buf + sizeof(KcpevKey))); 647 | break; 648 | 649 | case COMMAND_HEARTBEAT2: 650 | check(client, "client is NULL"); 651 | 652 | if(!is_kcp_valid(client)) 653 | break; 654 | 655 | check(data_len == sizeof(KcpevKey) + sizeof(uint64_t), "udp shake len error [%s : %s]", hbuf, sbuf); 656 | check(memcmp(data, &client->key, sizeof(KcpevKey)) == 0, 657 | "recv client heart beat1 key not match"); 658 | 659 | uint64_t time64 = be64toh(*(uint64_t *)(data + sizeof(KcpevKey))); 660 | if(time64 != *(uint64_t *)&client->udp.heart) 661 | { 662 | log_err("recv client heart not match [%lu : %lu].", *(uint64_t *)(data + sizeof(KcpevKey)), time64); 663 | break; 664 | } 665 | else 666 | { 667 | client->udp.heart = ev_now(client->loop); 668 | debug("end heart beat"); 669 | } 670 | 671 | break; 672 | 673 | default: 674 | sentinel("unrecognized command from server"); 675 | } 676 | 677 | return 0; 678 | 679 | error: 680 | return -1; 681 | } 682 | 683 | size_t on_tcp_recv(Kcpev *kcpev, char* data, size_t len) 684 | { 685 | KcpevTcp *tcp = &kcpev->tcp; 686 | KcpevHeader header; 687 | int ret = -1; 688 | ringbuf *rb = tcp->rb; 689 | ringbuf *new_rb; 690 | 691 | // no pending data 692 | if(!rb || ringbuf_get_pending_size(rb) == 0) 693 | { 694 | if(len >= sizeof(KcpevHeader)) 695 | { 696 | ret = header_from_net(&header, data, len); 697 | check(ret == 0, "header_from_net"); 698 | 699 | if(header.size == len) 700 | { 701 | return len; 702 | } 703 | } 704 | } 705 | 706 | if(!rb) 707 | { 708 | rb = ringbuf_new(KCPEV_BUFFER_SIZE); 709 | check_mem(rb); 710 | tcp->rb = rb; 711 | } 712 | 713 | ret = ringbuf_put(rb, data, len); 714 | if(ret == -1) 715 | { 716 | ringbuf *new_rb = ringbuf_new(rb->n + KCPEV_BUFFER_SIZE); 717 | 718 | check_mem(new_rb); 719 | 720 | char* chunk; 721 | int size; 722 | while((size = ringbuf_get_next_chunk(rb, &chunk))) 723 | { 724 | ret = ringbuf_put(new_rb, chunk, size); 725 | if(ret == -1) 726 | { 727 | ringbuf_free(new_rb); 728 | goto error; 729 | } 730 | 731 | ringbuf_mark_consumed(rb, size); 732 | } 733 | } 734 | 735 | return 0; 736 | 737 | error: 738 | return -1; 739 | } 740 | 741 | size_t get_tcp_buf_chunk(Kcpev *kcpev, char *ret_data, size_t len) 742 | { 743 | KcpevHeader header; 744 | ringbuf *rb = kcpev->tcp.rb; 745 | char *data; 746 | int ret = -1; 747 | size_t size; 748 | 749 | check(len >= sizeof(KcpevHeader), "ret buf len too small"); 750 | 751 | check_silently(rb); 752 | 753 | size = ringbuf_get_pending_size(rb); 754 | check_silently(size >= sizeof(KcpevHeader)); 755 | 756 | ret = ringbuf_copy_data(rb, ret_data, sizeof(KcpevHeader)); 757 | check(ret == 0, "ringbuf_copy_data"); 758 | 759 | header_from_net(&header, ret_data, len); 760 | 761 | if(header.size <= size) 762 | { 763 | ret = ringbuf_copy_data(rb, ret_data, header.size); 764 | check(ret == 0, "ringbuf_copy_data"); 765 | ringbuf_mark_consumed(rb, header.size); 766 | return header.size; 767 | } 768 | 769 | error: 770 | return 0; 771 | } 772 | 773 | void client_tcp_recv(EV_P_ struct ev_io *w, int revents) 774 | { 775 | char buf[KCPEV_BUFFER_SIZE]; 776 | uint8_t command; 777 | int ret = -1; 778 | 779 | Kcpev *client = w->data; 780 | 781 | int len = recv(KCPEV_FD_TO_HANDLE(w->fd), buf, sizeof(buf), 0); 782 | check(len > 0, ""); 783 | 784 | len = on_tcp_recv(client, buf, len); 785 | 786 | if(len > 0) 787 | { 788 | ret = on_client_recv(client, buf, len); 789 | check(ret == 0, ""); 790 | } 791 | 792 | while((len = get_tcp_buf_chunk(client, buf, sizeof(buf)))) 793 | { 794 | ret = on_client_recv(client, buf, len); 795 | check(ret == 0, ""); 796 | } 797 | 798 | return; 799 | 800 | error: 801 | debug("tcp recv error"); 802 | exit(1); 803 | return; 804 | } 805 | 806 | int try_kcp_recv(Kcpev *kcpev) 807 | { 808 | ikcpcb *kcp = kcpev->udp.kcp; 809 | 810 | char buf[KCPEV_BUFFER_SIZE]; 811 | int result = -1; 812 | for(;;) 813 | { 814 | int len = ikcp_recv(kcp, buf, sizeof(buf)); 815 | check_silently(len > 0); 816 | buf[len] = '\0'; 817 | 818 | // debug("kcp recv client [%d]\n", len); 819 | if(kcpev->server) 820 | { 821 | struct sockaddr_storage client_addr; 822 | socklen_t addr_size = sizeof(client_addr); 823 | result = getpeername(kcpev->udp.sock, (struct sockaddr*)&client_addr, &addr_size); 824 | check(result == 0, "getpeername"); 825 | result = on_server_recv(kcpev->server, kcpev, buf, len, (struct sockaddr*)&client_addr, addr_size); 826 | check(result == 0, ""); 827 | } 828 | else 829 | { 830 | result = on_client_recv(kcpev, buf, len); 831 | check(result == 0, ""); 832 | } 833 | } 834 | 835 | error: 836 | return result; 837 | } 838 | 839 | void kcpev_timer_repeat(Kcpev *kcpev) 840 | { 841 | if(!kcpev) 842 | return; 843 | 844 | struct ev_loop *loop = kcpev->loop; 845 | ev_timer *evt = kcpev->udp.evt; 846 | ikcpcb *kcp = kcpev->udp.kcp; 847 | 848 | if(!kcp) 849 | return; 850 | 851 | uint64_t now64 = ev_now(EV_A) * 1000; 852 | uint32_t now = now64 & 0xfffffffful; 853 | 854 | // use ikcp_check to decide if really need ikcp_update 855 | uint32_t next = ikcp_check(kcp, now); 856 | 857 | if (next <= now) 858 | { 859 | ikcp_update(kcp, now); 860 | next = ikcp_check(kcp, now); 861 | } 862 | 863 | if (next <= now) 864 | evt->repeat = 0.01; 865 | else 866 | evt->repeat = (next - now) / 1000.0; 867 | //debug("%zu, %zu, %lf", now, next, evt->repeat); 868 | ev_timer_again(EV_A_ evt); 869 | 870 | } 871 | 872 | void kcpev_on_timer(EV_P_ ev_timer *w, int revents) 873 | { 874 | kcpev_timer_repeat((Kcpev *)w->data); 875 | try_kcp_recv(w->data); 876 | } 877 | 878 | void on_client_heartbeat_timer(EV_P_ ev_timer *w, int revents) 879 | { 880 | debug("client heart beat timer"); 881 | Kcpev *client = (Kcpev *)w->data; 882 | 883 | if(!is_kcp_valid(client)) 884 | return; 885 | 886 | if(ev_now(EV_A) - client->udp.heart > KCPEV_HEARTBEAT_TIMEOUT) 887 | { 888 | set_kcp_invalid(client); 889 | return; 890 | } 891 | 892 | // 由客户端发起心跳包 893 | int ret = kcp_send_command(client, COMMAND_HEARTBEAT1, (char *)&client->key, sizeof(KcpevKey)); 894 | check(ret == 0, ""); 895 | 896 | debug("begin heart beat"); 897 | error: 898 | return; 899 | } 900 | 901 | void on_server_heartbeat_timer(EV_P_ ev_timer *w, int revents) 902 | { 903 | debug("server heart beat timer"); 904 | 905 | Kcpev *client = (Kcpev *)w->data; 906 | 907 | if(!is_kcp_valid(client)) 908 | return; 909 | 910 | if(ev_now(EV_A) - client->udp.heart > KCPEV_HEARTBEAT_TIMEOUT) 911 | { 912 | debug("server heart beat set invalid"); 913 | 914 | set_kcp_invalid(client); 915 | sock_send_command(client->tcp.sock, COMMAND_UDP_INVALID, (char *)&client->key, sizeof(KcpevKey)); 916 | return; 917 | } 918 | } 919 | 920 | // create kcp timer here 921 | int check_create_kcp_timer(Kcpev *kcpev, timer_cb hcb) 922 | { 923 | if(kcpev->udp.evt) 924 | return -1; 925 | 926 | ev_timer *evt = kcpev_malloc(sizeof(ev_timer)); 927 | check_mem(evt); 928 | 929 | kcpev->udp.evt = evt; 930 | evt->data = kcpev; 931 | ev_timer_init(evt, kcpev_on_timer, 0.01, 0.01); 932 | ev_timer_start(kcpev->loop, evt); 933 | 934 | ev_timer *evh = kcpev_malloc(sizeof(ev_timer)); 935 | check_mem(evh); 936 | 937 | kcpev->udp.evh = evh; 938 | evh->data = kcpev; 939 | ev_timer_init(evh, hcb, 10, 10); 940 | ev_timer_start(kcpev->loop, evh); 941 | 942 | kcpev->udp.heart = ev_now(kcpev->loop); 943 | 944 | return 0; 945 | 946 | error: 947 | if(evt) 948 | kcpev_free(evt); 949 | kcpev->udp.evt = NULL; 950 | 951 | if(evh) 952 | kcpev_free(evh); 953 | kcpev->udp.evh = NULL; 954 | return -1; 955 | } 956 | 957 | // 如果 kcpev.conv 没有设置,说明还没有握手成功,来到这里收到服务端的udp包,直接设置kcp.conv 958 | // 否则说明握手成功,用 kcp 接收数据 959 | void client_udp_recv(EV_P_ struct ev_io *w, int revents) 960 | { 961 | char buf[KCPEV_BUFFER_SIZE]; 962 | int len = recv(KCPEV_FD_TO_HANDLE(w->fd), buf, sizeof(buf), 0); 963 | check(len > 0, ""); 964 | Kcpev *client = w->data; 965 | int ret = -1; 966 | 967 | //debug("client recv udp raw: [%d]", len); 968 | 969 | if(!is_kcp_valid(client)) 970 | { 971 | ret = on_client_recv(client, buf, len); 972 | check(ret == 0, ""); 973 | } 974 | else 975 | { 976 | ikcp_input(client->udp.kcp, buf, len); 977 | ret = try_kcp_recv(client); 978 | } 979 | error: 980 | return; 981 | } 982 | 983 | void close_client(Kcpev *client) 984 | { 985 | if(!client) 986 | return; 987 | 988 | KcpevServer *server = client->server; 989 | HASH_DEL(server->hash, client); 990 | kcpev_destroy(client); 991 | } 992 | 993 | void server_tcp_recv(EV_P_ struct ev_io *w, int revents) 994 | { 995 | struct sockaddr_storage client_addr; 996 | 997 | char buf[KCPEV_BUFFER_SIZE]; 998 | Kcpev *client = w->data; 999 | int ret = -1; 1000 | 1001 | int len = recv(KCPEV_FD_TO_HANDLE(w->fd), buf, sizeof(buf), 0); 1002 | check(len > 0, "client tcp closed"); 1003 | 1004 | char uuids[KCPEV_UUID_PARSE_SIZE]; 1005 | uuid_unparse(client->key.uuid, uuids); 1006 | 1007 | socklen_t addr_size = sizeof(client_addr); 1008 | getpeername(client->tcp.sock, (struct sockaddr*)&client_addr, &addr_size); 1009 | 1010 | len = on_tcp_recv(client, buf, len); 1011 | 1012 | //debug("tcp recv from client[%s]: [%d]", uuids, len); 1013 | 1014 | if(len > 0) 1015 | { 1016 | ret = on_server_recv(client->server, client, buf, len, (struct sockaddr*)&client_addr, addr_size); 1017 | check(ret == 0, ""); 1018 | } 1019 | 1020 | while((len = get_tcp_buf_chunk(client, buf, sizeof(buf)))) 1021 | { 1022 | ret = on_server_recv(client->server, client, buf, len, (struct sockaddr*)&client_addr, addr_size); 1023 | check(ret == 0, ""); 1024 | } 1025 | return; 1026 | 1027 | error: 1028 | close_client(client); 1029 | return; 1030 | } 1031 | 1032 | void server_udp_recv(EV_P_ struct ev_io *w, int revents) 1033 | { 1034 | char buf[KCPEV_BUFFER_SIZE]; 1035 | Kcpev *client = w->data; 1036 | 1037 | int len = recv(KCPEV_FD_TO_HANDLE(w->fd), buf, sizeof(buf), 0); 1038 | check(len > 0, ""); 1039 | 1040 | char uuids[KCPEV_UUID_PARSE_SIZE]; 1041 | uuid_unparse(client->key.uuid, uuids); 1042 | 1043 | //debug("udp recv from client[%s : %d]: [%d]", uuids, client->udp.kcp->conv, len); 1044 | 1045 | int ret = -1; 1046 | if(!is_kcp_valid(client)) 1047 | { 1048 | struct sockaddr_storage client_addr; 1049 | socklen_t addr_size = sizeof(client_addr); 1050 | ret = getpeername(client->udp.sock, (struct sockaddr*)&client_addr, &addr_size); 1051 | check(ret == 0, "getpeername"); 1052 | 1053 | ret = on_server_recv(client->server, client, buf, len, (struct sockaddr*)&client_addr, addr_size); 1054 | check(ret == 0, ""); 1055 | } 1056 | else 1057 | { 1058 | ikcp_input(client->udp.kcp, buf, len); 1059 | 1060 | //kcpev_timer_repeat(client); 1061 | 1062 | int ret = try_kcp_recv(client); 1063 | 1064 | } 1065 | error: 1066 | return; 1067 | } 1068 | 1069 | // 接受新的客户端,并发送key 1070 | void tcp_accept(EV_P_ struct ev_io *w, int revents) 1071 | { 1072 | Kcpev *client = NULL; 1073 | 1074 | struct sockaddr_storage client_addr; 1075 | socklen_t addr_size = sizeof(client_addr); 1076 | int client_sock = accept(KCPEV_FD_TO_HANDLE(w->fd), (struct sockaddr *)&client_addr, &addr_size); 1077 | check(client_sock > 0, "accept"); 1078 | 1079 | char hbuf[KCPEV_NI_MAXHOST], sbuf[KCPEV_NI_MAXSERV]; 1080 | int ret = getnameinfo((struct sockaddr *)&client_addr, addr_size, hbuf, sizeof(hbuf), \ 1081 | sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV); 1082 | check(ret == 0, "getnameinfo"); 1083 | 1084 | KcpevServer *server = w->data; 1085 | 1086 | // create new client structure and add to server 1087 | client = kcpev_create(); 1088 | check_mem(client); 1089 | 1090 | client->server = server; 1091 | client->tcp.sock = client_sock; 1092 | uuid_generate(client->key.uuid); 1093 | 1094 | // create hashtable for client kcpev 1095 | HASH_ADD(hh, server->hash, key, sizeof(KcpevKey), client); 1096 | 1097 | char uuids[KCPEV_UUID_PARSE_SIZE]; 1098 | uuid_unparse(client->key.uuid, uuids); 1099 | debug("accept client [%s : %s : %s]\n", hbuf, sbuf, uuids); 1100 | 1101 | // set up tcp ev 1102 | ret = kcpev_init_ev(client, loop, client, server_tcp_recv, NULL); 1103 | check(ret >= 0, "client init ev"); 1104 | 1105 | // shake hand 1106 | ret = sock_send_command(client_sock, COMMAND_SHAKE_HAND1, (char *)&client->key, sizeof(KcpevKey)); 1107 | check(ret == 0, "send client shake hand1"); 1108 | 1109 | return; 1110 | 1111 | error: 1112 | if(client) 1113 | kcpev_destroy(client); 1114 | else if(client_sock) 1115 | close(client_sock); 1116 | return; 1117 | } 1118 | 1119 | // 接收客户端的udp信息 1120 | void server_udp_recv_all(EV_P_ struct ev_io *w, int revents) 1121 | { 1122 | struct sockaddr_storage client_addr; 1123 | socklen_t addr_size = sizeof(client_addr); 1124 | char buf[KCPEV_BUFFER_SIZE]; 1125 | int ret = -1; 1126 | 1127 | int len = recvfrom(KCPEV_FD_TO_HANDLE(w->fd), buf, sizeof(buf) - 1, 0, (struct sockaddr *)&client_addr, &addr_size); 1128 | check(len > 0, "server recvfrom"); 1129 | 1130 | KcpevServer *server = w->data; 1131 | 1132 | ret = on_server_recv(server, NULL, buf, len, (struct sockaddr *)&client_addr, addr_size); 1133 | check(ret == 0, ""); 1134 | 1135 | debug("udp_all recv client: [%d]\n", len); 1136 | 1137 | error: 1138 | return; 1139 | } 1140 | 1141 | int kcpev_set_ev(struct ev_loop *loop, void *data, KcpevSock *evs, ev_io_callback cb) 1142 | { 1143 | if(!evs->sock) 1144 | return 0; 1145 | 1146 | ev_io * ev_one = kcpev_malloc(sizeof(ev_io)); 1147 | check(ev_one != NULL, "kcpev_malloc"); 1148 | 1149 | evs->evio = ev_one; 1150 | 1151 | ev_one->data = data; 1152 | ev_io_init(ev_one, cb, KCPEV_HANDLE_TO_FD(evs->sock), EV_READ); 1153 | ev_io_start(loop, ev_one); 1154 | 1155 | return 0; 1156 | 1157 | error: 1158 | return -1; 1159 | } 1160 | 1161 | static inline int kcpev_startup() 1162 | { 1163 | #ifdef _WIN32 1164 | static int is_init = 0; 1165 | 1166 | if(is_init) 1167 | return 1; 1168 | 1169 | WSADATA wsa_data; 1170 | int ret = WSAStartup(MAKEWORD(2, 2), &wsa_data); 1171 | check(ret == 0, "WSAStartup"); 1172 | is_init = 1; 1173 | return 1; 1174 | 1175 | error: 1176 | return -1; 1177 | #else 1178 | return 1; 1179 | #endif 1180 | } 1181 | 1182 | Kcpev *kcpev_create_client(struct ev_loop *loop, const char *port, int family) 1183 | { 1184 | int ret; 1185 | 1186 | ret = kcpev_startup(); 1187 | check(ret == 1, ""); 1188 | 1189 | Kcpev *kcpev = kcpev_create(); 1190 | check(kcpev, "kcpev_create"); 1191 | 1192 | ret = kcpev_bind(kcpev, port, family, 0); 1193 | check(ret >= 0, "kcpev_bind"); 1194 | 1195 | ret = kcpev_create_kcp(&kcpev->udp, kcpev->key.split_key.conv, KCPEV_KCP_MODE); 1196 | check(ret >= 0, "create kcp"); 1197 | 1198 | ret = kcpev_init_ev(kcpev, loop, kcpev, client_tcp_recv, client_udp_recv); 1199 | check(ret >= 0, "init ev"); 1200 | 1201 | return kcpev; 1202 | 1203 | error: 1204 | if(kcpev) 1205 | { 1206 | kcpev_destroy(kcpev); 1207 | kcpev = NULL; 1208 | } 1209 | return NULL; 1210 | } 1211 | 1212 | KcpevServer *kcpev_create_server(struct ev_loop *loop, const char *port, int family, int backlog) 1213 | { 1214 | int ret; 1215 | 1216 | ret = kcpev_startup(); 1217 | check(ret == 1, ""); 1218 | 1219 | KcpevServer *kcpev = kcpev_server_create(); 1220 | check(kcpev, "kcpev_create"); 1221 | 1222 | strncpy(kcpev->port, port, sizeof(kcpev->port)); 1223 | 1224 | int reuse = 1; 1225 | ret = kcpev_bind((Kcpev *)kcpev, port, family, reuse); 1226 | check(ret >= 0, "kcpev_server_bind"); 1227 | 1228 | ret = kcpev_init_ev((Kcpev *)kcpev, loop, kcpev, tcp_accept, server_udp_recv_all); 1229 | check(ret >= 0, "init ev"); 1230 | 1231 | ret = kcpev_listen((Kcpev *)kcpev, backlog); 1232 | check(ret >= 0, "listen"); 1233 | 1234 | return kcpev; 1235 | 1236 | error: 1237 | if(kcpev) 1238 | { 1239 | kcpev_server_destroy(kcpev); 1240 | kcpev = NULL; 1241 | } 1242 | return NULL; 1243 | } 1244 | 1245 | void set_kcp_invalid(Kcpev *kcpev) 1246 | { 1247 | if(kcpev->udp.status == UDP_INVALID) 1248 | return; 1249 | 1250 | kcpev->udp.status = UDP_INVALID; 1251 | } 1252 | 1253 | int is_kcp_valid(Kcpev *kcpev) 1254 | { 1255 | if(!kcpev->udp.sock) 1256 | return 0; 1257 | 1258 | if(!kcpev->udp.kcp) 1259 | return 0; 1260 | 1261 | if(kcpev->udp.status != UDP_READY && kcpev->udp.status != UDP_HEARTBEAT) 1262 | return 0; 1263 | 1264 | if(!kcpev->udp.evt) 1265 | return 0; 1266 | 1267 | return 1; 1268 | } 1269 | 1270 | size_t pack_send_buf(char *buf, uint32_t buf_size, uint8_t command, const char *msg, size_t len) 1271 | { 1272 | KcpevHeader header; 1273 | int ret = -1; 1274 | const uint32_t header_size = sizeof(KcpevHeader); 1275 | const uint32_t real_size = len + header_size; 1276 | 1277 | check(buf_size >= real_size, "buf exceed max size, buf size: %zu, max size: %d", \ 1278 | len, buf_size - header_size); 1279 | 1280 | header.size = real_size; 1281 | header.command = command; 1282 | ret = header_to_net(&header, buf, buf_size); 1283 | check(ret == 0, "header_to_net"); 1284 | 1285 | memcpy(buf + header_size, msg, len); 1286 | return real_size; 1287 | 1288 | error: 1289 | return 0; 1290 | } 1291 | 1292 | int sock_send_command(int sock, uint8_t command, const char *msg, size_t len) 1293 | { 1294 | char buf[KCPEV_BUFFER_SIZE]; 1295 | int real_size = pack_send_buf(buf, sizeof(buf), command, msg, len); 1296 | check(real_size > 0, "pack_send_buf"); 1297 | 1298 | int ret = send(sock, buf, real_size, 0); 1299 | check(ret == real_size, ""); 1300 | return 0; 1301 | 1302 | error: 1303 | return -1; 1304 | } 1305 | 1306 | int kcp_send_command(Kcpev *kcpev, uint8_t command, const char *msg, size_t len) 1307 | { 1308 | char buf[KCPEV_BUFFER_SIZE]; 1309 | size_t real_size = pack_send_buf(buf, sizeof(buf), command, msg, len); 1310 | int ret = -1; 1311 | check(real_size > 0, "pack_send_buf"); 1312 | 1313 | if(is_kcp_valid(kcpev)) 1314 | { 1315 | ret = ikcp_send(kcpev->udp.kcp, buf, real_size); 1316 | } 1317 | 1318 | error: 1319 | return ret; 1320 | } 1321 | 1322 | int kcpev_send_command(Kcpev *kcpev, uint8_t command, const char *msg, size_t len) 1323 | { 1324 | char buf[KCPEV_BUFFER_SIZE]; 1325 | size_t real_size = pack_send_buf(buf, sizeof(buf), command, msg, len); 1326 | check(real_size > 0, "pack_send_buf"); 1327 | 1328 | int ret = -1; 1329 | // 如果kcp能用,就用kcp来发消息,否则用tcp 1330 | if(is_kcp_valid(kcpev)) 1331 | { 1332 | ret = ikcp_send(kcpev->udp.kcp, buf, real_size); 1333 | return ret; 1334 | } 1335 | else 1336 | { 1337 | ret = send(kcpev->tcp.sock, buf, real_size, 0); 1338 | check(ret == real_size, ""); 1339 | return 0; 1340 | } 1341 | 1342 | error: 1343 | return -1; 1344 | } 1345 | 1346 | int kcpev_send(Kcpev *kcpev, const char *msg, size_t len) 1347 | { 1348 | return kcpev_send_command(kcpev, COMMAND_DATA, msg, len); 1349 | } 1350 | 1351 | int kcpev_send_tcp(Kcpev *kcpev, const char *msg, size_t len) 1352 | { 1353 | return sock_send_command(kcpev->tcp.sock, COMMAND_DATA, msg, len); 1354 | } 1355 | 1356 | void kcpev_set_cb(Kcpev *kcpev, kcpev_recv_cb recv_cb, kcpev_disconnect_cb disconnect_cb) 1357 | { 1358 | kcpev->recv_cb = recv_cb; 1359 | } 1360 | 1361 | void kcpev_server_set_cb(KcpevServer *kcpev, kcpev_server_recv_cb recv_cb, kcpev_server_disconnect_cb disconnect_cb) 1362 | { 1363 | kcpev->recv_cb = recv_cb; 1364 | } 1365 | 1366 | int header_to_net(KcpevHeader *header, char *buf, size_t len) 1367 | { 1368 | if(len < sizeof(KcpevHeader)) 1369 | return -1; 1370 | 1371 | *(KCPEV_HEADER_SIZE_TYPE *)(buf) = htobe32(header->size); 1372 | *(KCPEV_HEADER_COMMAND_TYPE *)(buf + sizeof(KCPEV_HEADER_SIZE_TYPE)) = header->command; 1373 | return 0; 1374 | } 1375 | 1376 | int header_from_net(KcpevHeader *header, const char *buf, size_t len) 1377 | { 1378 | if(len < sizeof(KcpevHeader)) 1379 | return -1; 1380 | 1381 | header->size = be32toh(*(const KCPEV_HEADER_SIZE_TYPE *)buf); 1382 | header->command = *(KCPEV_HEADER_COMMAND_TYPE *)(buf + sizeof(KCPEV_HEADER_SIZE_TYPE)); 1383 | return 0; 1384 | } 1385 | 1386 | 1387 | -------------------------------------------------------------------------------- /src/kcpev.h: -------------------------------------------------------------------------------- 1 | #ifndef __KCPEV_H__ 2 | #define __KCPEV_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "kcpev_ringbuf.h" 8 | #include "ikcp.h" 9 | #include "utils.h" 10 | 11 | #define KCPEV_INPORT_ANY "0" 12 | #define KCPEV_BUFFER_SIZE 65536 13 | #define KCPEV_NI_MAXHOST 1025 14 | #define KCPEV_NI_MAXSERV 32 15 | #define KCPEV_UUID_PARSE_SIZE 37 16 | 17 | #define KCPEV_HEADER_SIZE_TYPE uint32_t 18 | #define KCPEV_HEADER_COMMAND_TYPE uint8_t 19 | 20 | #define KCPEV_KCP_MODE 2 21 | #define KCPEV_HEARTBEAT_TIMEOUT 30 22 | 23 | #ifdef _WIN32 24 | #define KCPEV_HANDLE_TO_FD(handle) (_open_osfhandle (handle, 0)) 25 | #define KCPEV_FD_TO_HANDLE(fd) (_get_osfhandle(fd)) 26 | #else 27 | #define KCPEV_HANDLE_TO_FD(handle) handle 28 | #define KCPEV_FD_TO_HANDLE(fd) fd 29 | #endif 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | typedef double KcpevTimestamp; 36 | 37 | enum Command 38 | { 39 | COMMAND_DATA = 1, // 普通数据 40 | COMMAND_SHAKE_HAND1, // 握手第一次 41 | COMMAND_SHAKE_HAND2, // 握手第二次 42 | COMMAND_HEARTBEAT1, // 心跳 43 | COMMAND_HEARTBEAT2, // 心跳 44 | COMMAND_UDP_INVALID, // 设置udp无效 45 | }; 46 | 47 | enum UdpStatus 48 | { 49 | UDP_INVALID = 0, // udp不可用 50 | UDP_SHAKING_HAND, // udp握手中 51 | UDP_READY, // udp可用 52 | UDP_HEARTBEAT, // 心跳包 53 | }; 54 | 55 | typedef struct 56 | { 57 | uint32_t r1; 58 | uint32_t r2; 59 | uint32_t r3; 60 | uint16_t r4; 61 | uint8_t r5; 62 | uint8_t conv; 63 | } KcpevSkey; 64 | 65 | typedef union 66 | { 67 | KcpevSkey split_key; 68 | myuuid_t uuid; 69 | } KcpevKey; 70 | 71 | // 存ev_io接口和socket 72 | #define KCPEV_SOCK \ 73 | int sock; \ 74 | ev_io *evio 75 | 76 | typedef struct 77 | { 78 | KCPEV_SOCK; 79 | } KcpevSock; 80 | 81 | typedef struct 82 | { 83 | KCPEV_SOCK; 84 | ringbuf *rb; 85 | } KcpevTcp; 86 | 87 | typedef struct 88 | { 89 | KCPEV_SOCK; 90 | ev_timer *evt; // kcp timer 91 | ev_timer *evh; // heartbeat timer 92 | ikcpcb *kcp; 93 | uint8_t status; // UdpStatus 94 | KcpevTimestamp heart; 95 | } KcpevUdp; 96 | 97 | struct _Kcpev; 98 | struct _KcpevServer; 99 | 100 | typedef void (*kcpev_recv_cb)(struct _Kcpev *kcpev, const char *buf, size_t len); 101 | typedef void (*kcpev_server_recv_cb)(struct _KcpevServer *kcpev_server, struct _Kcpev *kcpev, const char *buf, size_t len); 102 | 103 | typedef void (*kcpev_disconnect_cb)(struct _Kcpev *kcpev); 104 | typedef void (*kcpev_server_disconnect_cb)(struct _KcpevServer *server, struct _Kcpev *kcpev); 105 | 106 | typedef void (*timer_cb)(EV_P_ ev_timer *w, int revents); 107 | 108 | #define KCPEV_BASE \ 109 | KcpevTcp tcp; \ 110 | KcpevUdp udp; \ 111 | struct ev_loop *loop \ 112 | 113 | typedef struct _Kcpev 114 | { 115 | KCPEV_BASE; 116 | KcpevKey key; 117 | struct _KcpevServer *server; 118 | kcpev_recv_cb recv_cb; 119 | void *data; 120 | ev_tstamp heartbeat; 121 | UT_hash_handle hh; 122 | }Kcpev; 123 | 124 | typedef struct _KcpevServer 125 | { 126 | KCPEV_BASE; 127 | kcpev_server_recv_cb recv_cb; 128 | char port[10]; 129 | Kcpev *hash; 130 | } KcpevServer; 131 | 132 | typedef struct 133 | { 134 | KCPEV_HEADER_SIZE_TYPE size; 135 | KCPEV_HEADER_COMMAND_TYPE command; 136 | } KcpevHeader; 137 | 138 | typedef void (*ev_io_callback)(EV_P_ ev_io *w, int revents); 139 | 140 | // interface 141 | 142 | Kcpev *kcpev_create_client(struct ev_loop *loop, const char *port, int family); 143 | KcpevServer *kcpev_create_server(struct ev_loop *loop, const char *port, int family, int backlog); 144 | 145 | int kcpev_connect(Kcpev *kcpev, const char *addr, const char *port); 146 | 147 | int sock_send_command(int sock, uint8_t command, const char *msg, size_t len); 148 | int kcpev_send_command(Kcpev *kcpev, uint8_t command, const char *msg, size_t len); 149 | int kcpev_send(Kcpev *kcpev, const char *msg, size_t len); 150 | int kcpev_send_tcp(Kcpev *kcpev, const char *msg, size_t len); 151 | 152 | void kcpev_set_cb(Kcpev *kcpev, kcpev_recv_cb recv_cb, kcpev_disconnect_cb disconnect_cb); 153 | 154 | void kcpev_server_set_cb(KcpevServer *kcpev, kcpev_server_recv_cb recv_cb, kcpev_server_disconnect_cb disconnect_cb); 155 | 156 | int header_to_net(KcpevHeader *header, char *buf, size_t len); 157 | int header_from_net(KcpevHeader *header, const char *buf, size_t len); 158 | int is_kcp_valid(Kcpev *kcpev); 159 | 160 | #ifdef __cplusplus 161 | } 162 | #endif 163 | #endif 164 | 165 | -------------------------------------------------------------------------------- /src/kcpev_ringbuf.c: -------------------------------------------------------------------------------- 1 | /*********************************************************** 2 | Copyright (c) 2005-2014, Troy D. Hanson http://troydhanson.github.com/uthash/ 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 12 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 13 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 14 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 15 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 16 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 17 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 18 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 19 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | *************************************************************/ 23 | 24 | #include "kcpev_ringbuf.h" 25 | 26 | ringbuf *ringbuf_new(size_t sz) { 27 | ringbuf *r = malloc(sizeof(*r) + sz); 28 | if (r == NULL) { 29 | fprintf(stderr,"out of memory\n"); 30 | goto done; 31 | } 32 | 33 | r->u = r->i = r->o = 0; 34 | r->n = sz; 35 | 36 | done: 37 | return r; 38 | } 39 | 40 | void ringbuf_free(ringbuf* r) { 41 | free(r); 42 | } 43 | 44 | /* copy data in. fails if ringbuf has insuff space. */ 45 | #define MIN(a,b) (((a) < (b)) ? (a) : (b)) 46 | int ringbuf_put(ringbuf *r, const void *_data, size_t len) { 47 | char *data = (char*)_data; 48 | size_t a,b,c; 49 | if (r->i < r->o) { // available space is a contiguous buffer 50 | a = r->o - r->i; 51 | assert(a == r->n - r->u); 52 | if (len > a) return -1; 53 | memcpy(&r->d[r->i], data, len); 54 | } else { // available space wraps; it's two buffers 55 | b = r->n - r->i; // in-head to eob (receives leading input) 56 | c = r->o; // out-head to in-head (receives trailing input) 57 | a = b + c; // available space 58 | // the only ambiguous case is i==o, that's why u is needed 59 | if (r->i == r->o) a = r->n - r->u; 60 | assert(a == r->n - r->u); 61 | if (len > a) return -1; 62 | memcpy(&r->d[r->i], data, MIN(b, len)); 63 | if (len > b) memcpy(r->d, &data[b], len-b); 64 | } 65 | r->i = (r->i + len) % r->n; 66 | r->u += len; 67 | return 0; 68 | } 69 | 70 | size_t ringbuf_get_pending_size(ringbuf *r) { 71 | return r->u; 72 | } 73 | 74 | size_t ringbuf_get_next_chunk(ringbuf *r, char **data) { 75 | // in this case the next chunk is the whole pending buffer 76 | if (r->o < r->i) { 77 | assert(r->u == r->i - r->o); 78 | *data = &r->d[r->o]; 79 | return r->u; 80 | } 81 | // in this case (i==o) either the buffer is empty of full. 82 | // r->u tells distinguishes these cases. 83 | if ((r->o == r->i) && (r->u == 0)) { *data=NULL; return 0; } 84 | // if we're here, that means r->o > r->i. the pending 85 | // output is wrapped around the buffer. this function 86 | // returns the chunk prior to eob. the next call gets 87 | // the next chunk that's wrapped around the buffer. 88 | size_t b,c; 89 | b = r->n - r->o; // length of the part we're returning 90 | c = r->i; // wrapped part length- a sanity check 91 | assert(r->u == b + c); 92 | *data = &r->d[r->o]; 93 | return b; 94 | } 95 | 96 | void ringbuf_mark_consumed(ringbuf *r, size_t len) { 97 | assert(len <= r->u); 98 | r->o = (r->o + len ) % r->n; 99 | r->u -= len; 100 | } 101 | 102 | void ringbuf_clear(ringbuf *r) { 103 | r->u = r->i = r->o = 0; 104 | } 105 | 106 | // not consuming the data, just copy len data 107 | int ringbuf_copy_data(ringbuf *r, void *data, size_t len) 108 | { 109 | if(r->u < len) 110 | return -1; 111 | 112 | // continuous buf 113 | size_t b = r->n - r->o; 114 | if(r->o < r->i || b >= len) { 115 | memcpy(data, &r->d[r->o], len); 116 | } 117 | else { 118 | memcpy(data, &r->d[r->o], b); 119 | memcpy((char *)data + b, r->d, len - b); 120 | } 121 | 122 | return 0; 123 | } 124 | 125 | -------------------------------------------------------------------------------- /src/kcpev_ringbuf.h: -------------------------------------------------------------------------------- 1 | /*********************************************************** 2 | Copyright (c) 2005-2014, Troy D. Hanson http://troydhanson.github.com/uthash/ 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 12 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 13 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 14 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 15 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 16 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 17 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 18 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 19 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | *************************************************************/ 23 | 24 | #ifndef _RINGBUF_H_ 25 | #define _RINGBUF_H_ 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | /* simple ring buffer */ 36 | 37 | typedef struct _ringbuf { 38 | size_t n; /* allocd size */ 39 | size_t u; /* used space */ 40 | size_t i; /* input pos */ 41 | size_t o; /* output pos */ 42 | char d[]; /* C99 flexible array member */ 43 | } ringbuf; 44 | 45 | ringbuf *ringbuf_new(size_t sz); 46 | int ringbuf_put(ringbuf *r, const void *data, size_t len); 47 | size_t ringbuf_get_pending_size(ringbuf *r); 48 | size_t ringbuf_get_next_chunk(ringbuf *r, char **data); 49 | void ringbuf_mark_consumed(ringbuf *r, size_t len); 50 | void ringbuf_free(ringbuf *r); 51 | void ringbuf_clear(ringbuf *r); 52 | int ringbuf_copy_data(ringbuf *r, void *data, size_t len); 53 | #ifdef __cplusplus 54 | } 55 | #endif 56 | #endif /* _RINGBUF_H_ */ 57 | -------------------------------------------------------------------------------- /src/myuuid.c: -------------------------------------------------------------------------------- 1 | #include "myuuid.h" 2 | 3 | #ifdef WIN32 4 | #include 5 | #include 6 | #include 7 | 8 | struct uuid { 9 | uint32_t time_low; 10 | uint16_t time_mid; 11 | uint16_t time_hi_and_version; 12 | uint16_t clock_seq; 13 | uint8_t node[6]; 14 | }; 15 | 16 | static const char *fmt_lower = 17 | "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x"; 18 | 19 | static const char *fmt_upper = 20 | "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X"; 21 | 22 | #ifdef UUID_UNPARSE_DEFAULT_UPPER 23 | #define FMT_DEFAULT fmt_upper 24 | #else 25 | #define FMT_DEFAULT fmt_lower 26 | #endif 27 | 28 | 29 | void uuid_unpack(const myuuid_t in, struct uuid *uu) 30 | { 31 | const uint8_t *ptr = in; 32 | uint32_t tmp; 33 | 34 | tmp = *ptr++; 35 | tmp = (tmp << 8) | *ptr++; 36 | tmp = (tmp << 8) | *ptr++; 37 | tmp = (tmp << 8) | *ptr++; 38 | uu->time_low = tmp; 39 | 40 | tmp = *ptr++; 41 | tmp = (tmp << 8) | *ptr++; 42 | uu->time_mid = tmp; 43 | 44 | tmp = *ptr++; 45 | tmp = (tmp << 8) | *ptr++; 46 | uu->time_hi_and_version = tmp; 47 | 48 | tmp = *ptr++; 49 | tmp = (tmp << 8) | *ptr++; 50 | uu->clock_seq = tmp; 51 | 52 | memcpy(uu->node, ptr, 6); 53 | } 54 | 55 | 56 | static void uuid_unparse_x(const myuuid_t uu, char *out, const char *fmt) 57 | { 58 | struct uuid uuid; 59 | 60 | uuid_unpack(uu, &uuid); 61 | sprintf(out, fmt, 62 | uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, 63 | uuid.clock_seq >> 8, uuid.clock_seq & 0xFF, 64 | uuid.node[0], uuid.node[1], uuid.node[2], 65 | uuid.node[3], uuid.node[4], uuid.node[5]); 66 | } 67 | 68 | void uuid_unparse_lower(const myuuid_t uu, char *out) 69 | { 70 | uuid_unparse_x(uu, out, fmt_lower); 71 | } 72 | 73 | void uuid_unparse_upper(const myuuid_t uu, char *out) 74 | { 75 | uuid_unparse_x(uu, out, fmt_upper); 76 | } 77 | 78 | void uuid_unparse(const myuuid_t uu, char *out) 79 | { 80 | uuid_unparse_x(uu, out, FMT_DEFAULT); 81 | } 82 | 83 | void uuid_generate(myuuid_t out) 84 | { 85 | CoCreateGuid((GUID *)out); 86 | } 87 | 88 | #endif // define WIN32 89 | -------------------------------------------------------------------------------- /src/myuuid.h: -------------------------------------------------------------------------------- 1 | #ifndef __MYUUID_H__ 2 | #define __MYUUID_H__ 3 | 4 | #ifdef WIN32 5 | typedef unsigned char myuuid_t[16]; 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | void uuid_generate(myuuid_t out); 11 | void uuid_unparse(const myuuid_t uu, char *out); 12 | 13 | #ifdef __cplusplus 14 | } 15 | #endif 16 | 17 | #else // unix and apple 18 | 19 | #include 20 | typedef uuid_t myuuid_t; 21 | 22 | #endif 23 | 24 | #endif 25 | 26 | -------------------------------------------------------------------------------- /src/portable_endian.h: -------------------------------------------------------------------------------- 1 | // "License": Public Domain 2 | // I, Mathias Panzenböck, place this file hereby into the public domain. Use it at your own risk for whatever you like. 3 | // In case there are jurisdictions that don't support putting things in the public domain you can also consider it to 4 | // be "dual licensed" under the BSD, MIT and Apache licenses, if you want to. This code is trivial anyway. Consider it 5 | // an example on how to get the endian conversion functions on different platforms. 6 | 7 | #ifndef PORTABLE_ENDIAN_H__ 8 | #define PORTABLE_ENDIAN_H__ 9 | 10 | #if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__) 11 | 12 | # define __WINDOWS__ 13 | 14 | #endif 15 | 16 | #if defined(__linux__) || defined(__CYGWIN__) 17 | 18 | # include 19 | 20 | #elif defined(__APPLE__) 21 | 22 | # include 23 | 24 | # define htobe16(x) OSSwapHostToBigInt16(x) 25 | # define htole16(x) OSSwapHostToLittleInt16(x) 26 | # define be16toh(x) OSSwapBigToHostInt16(x) 27 | # define le16toh(x) OSSwapLittleToHostInt16(x) 28 | 29 | # define htobe32(x) OSSwapHostToBigInt32(x) 30 | # define htole32(x) OSSwapHostToLittleInt32(x) 31 | # define be32toh(x) OSSwapBigToHostInt32(x) 32 | # define le32toh(x) OSSwapLittleToHostInt32(x) 33 | 34 | # define htobe64(x) OSSwapHostToBigInt64(x) 35 | # define htole64(x) OSSwapHostToLittleInt64(x) 36 | # define be64toh(x) OSSwapBigToHostInt64(x) 37 | # define le64toh(x) OSSwapLittleToHostInt64(x) 38 | 39 | # define __BYTE_ORDER BYTE_ORDER 40 | # define __BIG_ENDIAN BIG_ENDIAN 41 | # define __LITTLE_ENDIAN LITTLE_ENDIAN 42 | # define __PDP_ENDIAN PDP_ENDIAN 43 | 44 | #elif defined(__OpenBSD__) 45 | 46 | # include 47 | 48 | #elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) 49 | 50 | # include 51 | 52 | # define be16toh(x) betoh16(x) 53 | # define le16toh(x) letoh16(x) 54 | 55 | # define be32toh(x) betoh32(x) 56 | # define le32toh(x) letoh32(x) 57 | 58 | # define be64toh(x) betoh64(x) 59 | # define le64toh(x) letoh64(x) 60 | 61 | #elif defined(__WINDOWS__) 62 | 63 | # include 64 | 65 | # if BYTE_ORDER == LITTLE_ENDIAN 66 | 67 | # define htobe16(x) htons(x) 68 | # define htole16(x) (x) 69 | # define be16toh(x) ntohs(x) 70 | # define le16toh(x) (x) 71 | 72 | # define htobe32(x) htonl(x) 73 | # define htole32(x) (x) 74 | # define be32toh(x) ntohl(x) 75 | # define le32toh(x) (x) 76 | 77 | # define htobe64(x) htonll(x) 78 | # define htole64(x) (x) 79 | # define be64toh(x) ntohll(x) 80 | # define le64toh(x) (x) 81 | 82 | # elif BYTE_ORDER == BIG_ENDIAN 83 | 84 | /* that would be xbox 360 */ 85 | # define htobe16(x) (x) 86 | # define htole16(x) __builtin_bswap16(x) 87 | # define be16toh(x) (x) 88 | # define le16toh(x) __builtin_bswap16(x) 89 | 90 | # define htobe32(x) (x) 91 | # define htole32(x) __builtin_bswap32(x) 92 | # define be32toh(x) (x) 93 | # define le32toh(x) __builtin_bswap32(x) 94 | 95 | # define htobe64(x) (x) 96 | # define htole64(x) __builtin_bswap64(x) 97 | # define be64toh(x) (x) 98 | # define le64toh(x) __builtin_bswap64(x) 99 | 100 | # else 101 | 102 | # error byte order not supported 103 | 104 | # endif 105 | 106 | # define __BYTE_ORDER BYTE_ORDER 107 | # define __BIG_ENDIAN BIG_ENDIAN 108 | # define __LITTLE_ENDIAN LITTLE_ENDIAN 109 | # define __PDP_ENDIAN PDP_ENDIAN 110 | 111 | #else 112 | 113 | # error platform not supported 114 | 115 | #endif 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef __UTILS_H__ 2 | #define __UTILS_H__ 3 | 4 | #include "myuuid.h" 5 | 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /tests/client_kcp_simple_test.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/disenone/kcpev/5211adaf074bc59b26fbf2a4d4760dad1f113e50/tests/client_kcp_simple_test.c -------------------------------------------------------------------------------- /tests/client_kcp_test.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/disenone/kcpev/5211adaf074bc59b26fbf2a4d4760dad1f113e50/tests/client_kcp_test.c -------------------------------------------------------------------------------- /tests/client_udp_test.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/disenone/kcpev/5211adaf074bc59b26fbf2a4d4760dad1f113e50/tests/client_udp_test.c -------------------------------------------------------------------------------- /tests/kcpev_client_pressure_test.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/disenone/kcpev/5211adaf074bc59b26fbf2a4d4760dad1f113e50/tests/kcpev_client_pressure_test.c -------------------------------------------------------------------------------- /tests/kcpev_client_test.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/disenone/kcpev/5211adaf074bc59b26fbf2a4d4760dad1f113e50/tests/kcpev_client_test.c -------------------------------------------------------------------------------- /tests/kcpev_echo_client_test.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/disenone/kcpev/5211adaf074bc59b26fbf2a4d4760dad1f113e50/tests/kcpev_echo_client_test.c -------------------------------------------------------------------------------- /tests/kcpev_echo_server_test.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/disenone/kcpev/5211adaf074bc59b26fbf2a4d4760dad1f113e50/tests/kcpev_echo_server_test.c -------------------------------------------------------------------------------- /tests/kcpev_heartbeat_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifdef _WIN32 4 | # include 5 | # include 6 | # include 7 | #else 8 | # include 9 | # include 10 | # include 11 | #endif 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "test.h" 17 | 18 | using namespace std; 19 | 20 | void on_stop(EV_P_ struct ev_timer *w, int revents) 21 | { 22 | ev_break(EV_A_ EVBREAK_ALL); 23 | } 24 | 25 | void on_stop_heartbeat(EV_P_ struct ev_timer *w, int revents) 26 | { 27 | Kcpev *client = (Kcpev *)w->data; 28 | 29 | ev_timer_stop(client->loop, client->udp.evh); 30 | } 31 | 32 | void client_recv_cb(Kcpev* kcpev, const char* buf, size_t len) 33 | { 34 | } 35 | 36 | Kcpev* create_client() 37 | { 38 | Kcpev *kcpev = NULL; 39 | struct ev_loop *loop = EV_DEFAULT; 40 | int ret = 0; 41 | ev_timer *evt = NULL; 42 | 43 | kcpev = kcpev_create_client(loop, "0", AF_INET); 44 | check(kcpev, "init client"); 45 | 46 | ret = kcpev_connect(kcpev, "127.0.0.1", "33333"); 47 | check(ret >= 0, "connect"); 48 | 49 | kcpev_set_cb(kcpev, client_recv_cb, NULL); 50 | 51 | return kcpev; 52 | 53 | error: 54 | return NULL; 55 | } 56 | 57 | 58 | std::string get_server_full_path_name() 59 | { 60 | string exe_path(1024 * 10, '\0'); 61 | 62 | readlink("/proc/self/exe", &exe_path[0], exe_path.capacity()); 63 | 64 | int pos = exe_path.find_last_of('/'); 65 | 66 | string server_path = exe_path.substr(0, pos + 1); 67 | 68 | return server_path; 69 | } 70 | 71 | TEST(KcpevTest, PackageTest) 72 | { 73 | signal(SIGCHLD, SIG_IGN); 74 | 75 | const std::string server_full_name = get_server_full_path_name() + "kcpev_echo_server_test"; 76 | 77 | pid_t child_pid = 0; 78 | // fork a child process for create a server. 79 | pid_t pid = fork(); 80 | if (pid == 0) // child exec server 81 | { 82 | int ret_exec = execl(server_full_name.c_str(), "kcpev_echo_server_test", NULL); 83 | if (ret_exec < 0) 84 | std::cerr << "execl error with errno: " << errno << " " << strerror(errno) << std::endl; 85 | return; 86 | } 87 | 88 | sleep(1); 89 | 90 | // parent 91 | child_pid = pid; 92 | 93 | Kcpev *client = create_client(); 94 | EXPECT_NE(client, (Kcpev *)NULL); 95 | 96 | ev_timer evs; 97 | ev_timer_init(&evs, on_stop, 60, 0); 98 | ev_timer_start(client->loop, &evs); 99 | 100 | ev_timer evh; 101 | ev_timer_init(&evh, on_stop_heartbeat, 15, 0); 102 | ev_timer_start(client->loop, &evh); 103 | evh.data = client; 104 | 105 | ev_run(client->loop, 0); 106 | 107 | EXPECT_EQ(client->udp.status, UDP_INVALID); 108 | 109 | int ret_kill = ::kill(child_pid, SIGINT); 110 | if (ret_kill < 0) 111 | std::cerr << "kill error with errno: " << errno << " " << strerror(errno) << std::endl; 112 | } 113 | 114 | int main(int argc, char* argv[]) 115 | { 116 | testing::InitGoogleTest(&argc, argv); 117 | return RUN_ALL_TESTS(); 118 | } 119 | 120 | -------------------------------------------------------------------------------- /tests/kcpev_package_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifdef _WIN32 4 | # include 5 | # include 6 | # include 7 | #else 8 | # include 9 | # include 10 | # include 11 | #endif 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "test.h" 18 | 19 | using namespace std; 20 | 21 | void client_recv_cb(Kcpev* kcpev, const char* buf, size_t len) 22 | { 23 | unordered_map> *package_info = static_cast> *>(kcpev->data); 24 | int key = *(int *)buf; 25 | int ret = 0; 26 | vector* origin = NULL; 27 | 28 | auto search = package_info->find(key); 29 | check(search != package_info->end(), "package not found, key: %d", key); 30 | 31 | origin = &package_info->at(key); 32 | check(origin->size() == len, "package len not match, key: %d, origin: %d, recv: %d", key, origin->size(), len); 33 | 34 | ret = memcmp(origin->data(), buf, len); 35 | check(ret == 0, "package data not equal, key: %d, origin: %d, recv: %d", key, origin->size(), len); 36 | 37 | package_info->at(key) = vector(); 38 | 39 | error: 40 | return; 41 | } 42 | 43 | void on_timer(EV_P_ struct ev_timer *w, int revents) 44 | { 45 | static int odd = 0; 46 | 47 | Kcpev *kcpev = (Kcpev *)w->data; 48 | unordered_map> *package_info = static_cast> *>(kcpev->data); 49 | if(package_info->size() > 1000) 50 | return; 51 | 52 | char buf[KCPEV_BUFFER_SIZE]; 53 | int len = create_rand_str(buf, 4, sizeof(buf)); 54 | 55 | *(int *)buf = package_info->size(); 56 | package_info->emplace(package_info->size(), vector(buf, buf + len)); 57 | 58 | odd = (odd + 1) % 2; 59 | 60 | if(odd) 61 | kcpev_send(kcpev, buf, len); 62 | else 63 | kcpev_send_tcp(kcpev, buf, len); 64 | } 65 | 66 | Kcpev* create_client() 67 | { 68 | Kcpev *kcpev = NULL; 69 | struct ev_loop *loop = EV_DEFAULT; 70 | int ret = 0; 71 | ev_timer *evt = NULL; 72 | unordered_map> *package_info = NULL; 73 | 74 | kcpev = kcpev_create_client(loop, "0", AF_INET); 75 | check(kcpev, "init client"); 76 | 77 | ret = kcpev_connect(kcpev, "127.0.0.1", "33333"); 78 | check(ret >= 0, "connect"); 79 | 80 | kcpev_set_cb(kcpev, client_recv_cb, NULL); 81 | 82 | package_info = new unordered_map>; 83 | kcpev->data = package_info; 84 | 85 | evt = new ev_timer; 86 | evt->data = kcpev; 87 | ev_timer_init(evt, on_timer, 1, 0.01); 88 | ev_timer_start(loop, evt); 89 | 90 | return kcpev; 91 | 92 | error: 93 | return NULL; 94 | } 95 | 96 | std::string get_server_full_path_name() 97 | { 98 | string exe_path(1024 * 10, '\0'); 99 | 100 | readlink("/proc/self/exe", &exe_path[0], exe_path.capacity()); 101 | 102 | int pos = exe_path.find_last_of('/'); 103 | 104 | string server_path = exe_path.substr(0, pos + 1); 105 | 106 | return server_path; 107 | } 108 | 109 | void on_stop(EV_P_ struct ev_timer *w, int revents) 110 | { 111 | ev_break(EV_A_ EVBREAK_ALL); 112 | } 113 | 114 | TEST(KcpevTest, PackageTest) 115 | { 116 | signal(SIGCHLD, SIG_IGN); 117 | 118 | const std::string server_full_name = get_server_full_path_name() + "kcpev_echo_server_test"; 119 | 120 | pid_t child_pid = 0; 121 | // fork a child process for create a server. 122 | pid_t pid = fork(); 123 | if (pid == 0) // child exec server 124 | { 125 | int ret_exec = execl(server_full_name.c_str(), "kcpev_echo_server_test", NULL); 126 | if (ret_exec < 0) 127 | std::cerr << "execl error with errno: " << errno << " " << strerror(errno) << std::endl; 128 | return; 129 | } 130 | 131 | sleep(1); 132 | 133 | // parent 134 | child_pid = pid; 135 | 136 | // 创建两个客户端,分别给服务端发送数据 137 | Kcpev *kcpev1 = create_client(); 138 | EXPECT_NE(kcpev1, (Kcpev *)NULL); 139 | 140 | Kcpev *kcpev2 = create_client(); 141 | EXPECT_NE(kcpev2, (Kcpev *)NULL); 142 | 143 | ev_timer evs; 144 | ev_timer_init(&evs, on_stop, 20, 0); 145 | ev_timer_start(kcpev1->loop, &evs); 146 | 147 | ev_run(kcpev1->loop, 0); 148 | 149 | EXPECT_TRUE(is_kcp_valid(kcpev1)); 150 | EXPECT_TRUE(is_kcp_valid(kcpev2)); 151 | 152 | for(const auto &pair: *static_cast> *>(kcpev1->data)) 153 | { 154 | EXPECT_EQ(pair.second.size(), 0) << "package error found, key: " << pair.first; 155 | } 156 | 157 | for(const auto &pair: *static_cast> *>(kcpev2->data)) 158 | { 159 | EXPECT_EQ(pair.second.size(), 0) << "package error found, key: " << pair.first; 160 | } 161 | 162 | int ret_kill = ::kill(child_pid, SIGINT); 163 | if (ret_kill < 0) 164 | std::cerr << "kill error with errno: " << errno << " " << strerror(errno) << std::endl; 165 | } 166 | 167 | int main(int argc, char* argv[]) 168 | { 169 | testing::InitGoogleTest(&argc, argv); 170 | return RUN_ALL_TESTS(); 171 | } 172 | 173 | -------------------------------------------------------------------------------- /tests/kcpev_remote_package_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifdef _WIN32 4 | # include 5 | # include 6 | # include 7 | #else 8 | # include 9 | # include 10 | # include 11 | #endif 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "test.h" 18 | 19 | using namespace std; 20 | 21 | const char *server_ip = NULL; 22 | const char *server_port = NULL; 23 | 24 | void client_recv_cb(Kcpev* kcpev, const char* buf, size_t len) 25 | { 26 | unordered_map> *package_info = static_cast> *>(kcpev->data); 27 | int key = *(int *)buf; 28 | int ret = 0; 29 | vector* origin = NULL; 30 | 31 | auto search = package_info->find(key); 32 | check(search != package_info->end(), "package not found, key: %d", key); 33 | 34 | origin = &package_info->at(key); 35 | check(origin->size() == len, "package len not match, key: %d, origin: %d, recv: %d", key, origin->size(), len); 36 | 37 | ret = memcmp(origin->data(), buf, len); 38 | check(ret == 0, "package data not equal, key: %d, origin: %d, recv: %d", key, origin->size(), len); 39 | 40 | package_info->at(key) = vector(); 41 | 42 | error: 43 | return; 44 | } 45 | 46 | void on_timer(EV_P_ struct ev_timer *w, int revents) 47 | { 48 | Kcpev *kcpev = (Kcpev *)w->data; 49 | unordered_map> *package_info = static_cast> *>(kcpev->data); 50 | if(package_info->size() > 1000) 51 | return; 52 | 53 | char buf[KCPEV_BUFFER_SIZE]; 54 | int len = create_rand_str(buf, 4, sizeof(buf)); 55 | 56 | *(int *)buf = package_info->size(); 57 | package_info->emplace(package_info->size(), vector(buf, buf + len)); 58 | 59 | kcpev_send(kcpev, buf, len); 60 | } 61 | 62 | Kcpev* create_client() 63 | { 64 | Kcpev *kcpev = NULL; 65 | struct ev_loop *loop = EV_DEFAULT; 66 | int ret = 0; 67 | ev_timer *evt = NULL; 68 | unordered_map> *package_info = NULL; 69 | 70 | kcpev = kcpev_create_client(loop, "0", AF_INET); 71 | check(kcpev, "init client"); 72 | 73 | ret = kcpev_connect(kcpev, server_ip, server_port); 74 | check(ret >= 0, "connect"); 75 | 76 | kcpev_set_cb(kcpev, client_recv_cb, NULL); 77 | 78 | package_info = new unordered_map>; 79 | kcpev->data = package_info; 80 | 81 | evt = new ev_timer; 82 | evt->data = kcpev; 83 | ev_timer_init(evt, on_timer, 1, 0.01); 84 | ev_timer_start(loop, evt); 85 | 86 | return kcpev; 87 | 88 | error: 89 | return NULL; 90 | } 91 | 92 | void on_stop(EV_P_ struct ev_timer *w, int revents) 93 | { 94 | ev_break(EV_A_ EVBREAK_ALL); 95 | } 96 | 97 | TEST(KcpevTest, PackageTest) 98 | { 99 | // 创建两个客户端,分别给服务端发送数据 100 | Kcpev *kcpev1 = create_client(); 101 | EXPECT_NE(kcpev1, (Kcpev *)NULL); 102 | 103 | Kcpev *kcpev2 = create_client(); 104 | EXPECT_NE(kcpev2, (Kcpev *)NULL); 105 | 106 | ev_timer evs; 107 | ev_timer_init(&evs, on_stop, 20, 0); 108 | ev_timer_start(kcpev1->loop, &evs); 109 | 110 | ev_run(kcpev1->loop, 0); 111 | 112 | for(const auto &pair: *static_cast> *>(kcpev1->data)) 113 | { 114 | EXPECT_EQ(pair.second.size(), 0) << "package error found, key: " << pair.first; 115 | } 116 | 117 | for(const auto &pair: *static_cast> *>(kcpev2->data)) 118 | { 119 | EXPECT_EQ(pair.second.size(), 0) << "package error found, key: " << pair.first; 120 | } 121 | } 122 | 123 | int main(int argc, char* argv[]) 124 | { 125 | if(argc != 3) 126 | { 127 | printf("usage:kcpev_remote_package_test server_ip server_port\n"); 128 | return 0; 129 | } 130 | 131 | server_ip = argv[1]; 132 | server_port = argv[2]; 133 | 134 | int ac = 1; 135 | testing::InitGoogleTest(&ac, argv); 136 | return RUN_ALL_TESTS(); 137 | } 138 | 139 | -------------------------------------------------------------------------------- /tests/kcpev_send_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifdef _WIN32 4 | # include 5 | # include 6 | # include 7 | #else 8 | # include 9 | # include 10 | # include 11 | # include 12 | #endif 13 | #include 14 | #include 15 | #include 16 | #include "test.h" 17 | 18 | using namespace std; 19 | 20 | void client_recv_cb(Kcpev* kcpev, const char* buf, size_t len) 21 | { 22 | char *data = new char[len + 1]; 23 | memcpy(data, buf, len); 24 | data[len] = '\0'; 25 | debug("%s", data); 26 | printf(">> "); 27 | fflush(stdout); 28 | delete data; 29 | } 30 | 31 | void on_stdin_read(EV_P_ struct ev_watcher *w, int revents, const char *buf, size_t len) 32 | { 33 | static int odd = 0; 34 | int ret = -1; 35 | //odd = (odd + 1) % 2; 36 | Kcpev *kcpev = (Kcpev *)w->data; 37 | if(odd) 38 | ret = kcpev_send(kcpev, buf, strlen(buf)); 39 | else 40 | ret = kcpev_send_tcp(kcpev, buf, strlen(buf)); 41 | 42 | check(ret >= 0, ""); 43 | error: 44 | return; 45 | } 46 | 47 | Kcpev* create_client() 48 | { 49 | Kcpev *kcpev = NULL; 50 | struct ev_loop *loop = EV_DEFAULT; 51 | int ret = 0; 52 | 53 | kcpev = kcpev_create_client(loop, "0", AF_INET); 54 | check(kcpev, "init client"); 55 | 56 | ret = kcpev_connect(kcpev, "127.0.0.1", "33333"); 57 | check(ret >= 0, "connect"); 58 | 59 | kcpev_set_cb(kcpev, client_recv_cb, NULL); 60 | return kcpev; 61 | 62 | error: 63 | return NULL; 64 | } 65 | 66 | std::string get_server_full_path_name() 67 | { 68 | string exe_path(1024 * 10, '\0'); 69 | 70 | readlink("/proc/self/exe", &exe_path[0], exe_path.capacity()); 71 | 72 | int pos = exe_path.find_last_of('/'); 73 | 74 | string server_path = exe_path.substr(0, pos + 1); 75 | 76 | return server_path; 77 | } 78 | 79 | int main(int argc, char* argv[]) 80 | { 81 | signal(SIGCHLD, SIG_IGN); 82 | 83 | const std::string server_full_name = get_server_full_path_name() + "kcpev_echo_server_test"; 84 | 85 | pid_t child_pid = 0; 86 | // fork a child process for create a server. 87 | pid_t pid = fork(); 88 | if (pid == 0) // child exec server 89 | { 90 | int ret_exec = execl(server_full_name.c_str(), "kcpev_echo_server_test", NULL); 91 | if (ret_exec < 0) 92 | std::cerr << "execl error with errno: " << errno << " " << strerror(errno) << std::endl; 93 | return 0; 94 | } 95 | 96 | sleep(1); 97 | 98 | // parent 99 | child_pid = pid; 100 | 101 | Kcpev *kcpev = create_client(); 102 | 103 | setup_stdin(kcpev->loop, kcpev, on_stdin_read); 104 | 105 | ev_run(kcpev->loop, 0); 106 | return 0; 107 | } 108 | 109 | -------------------------------------------------------------------------------- /tests/kcpev_server_test.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/disenone/kcpev/5211adaf074bc59b26fbf2a4d4760dad1f113e50/tests/kcpev_server_test.c -------------------------------------------------------------------------------- /tests/libev_udp_echo_client.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/disenone/kcpev/5211adaf074bc59b26fbf2a4d4760dad1f113e50/tests/libev_udp_echo_client.c -------------------------------------------------------------------------------- /tests/libev_udp_echo_server.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/disenone/kcpev/5211adaf074bc59b26fbf2a4d4760dad1f113e50/tests/libev_udp_echo_server.c -------------------------------------------------------------------------------- /tests/pressure_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ $# != 2 ] 3 | then 4 | echo "usage: pressure_test ip port" 5 | exit 1 6 | fi 7 | 8 | echo "Running pressure tests" 9 | 10 | for i in {1..100} 11 | do 12 | echo $i 13 | (./kcpev_client_pressure_test $1 $2 &) 14 | sleep 0.001 15 | done 16 | -------------------------------------------------------------------------------- /tests/ringbuf_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | TEST(RingbufTest, baseTest) 9 | { 10 | ringbuf *rb = ringbuf_new(5); 11 | 12 | EXPECT_EQ(ringbuf_put(rb, "aaaa", 4), 0); 13 | 14 | EXPECT_EQ(ringbuf_get_pending_size(rb), 4); 15 | 16 | char *data, *data1; 17 | EXPECT_EQ(ringbuf_get_next_chunk(rb, &data), 4); 18 | EXPECT_EQ(ringbuf_get_next_chunk(rb, &data1), 4); 19 | EXPECT_EQ(data, data1); 20 | 21 | ringbuf_mark_consumed(rb, 3); 22 | EXPECT_EQ(ringbuf_get_pending_size(rb), 1); 23 | 24 | EXPECT_EQ(ringbuf_get_pending_size(rb), 1); 25 | 26 | EXPECT_EQ(ringbuf_get_next_chunk(rb, &data), 1); 27 | EXPECT_EQ(data, data1 + 3); 28 | 29 | EXPECT_EQ(ringbuf_put(rb, "bbbb", 4), 0); 30 | EXPECT_EQ(ringbuf_get_pending_size(rb), 5); 31 | 32 | EXPECT_EQ(ringbuf_get_pending_size(rb), 5); 33 | 34 | EXPECT_EQ(ringbuf_get_next_chunk(rb, &data), 2); 35 | 36 | char ret[6]; 37 | ret[5] = '\0'; 38 | 39 | EXPECT_EQ(ringbuf_copy_data(rb, ret, 6), -1); 40 | EXPECT_EQ(ringbuf_copy_data(rb, ret, 5), 0); 41 | EXPECT_STREQ(ret, "abbbb"); 42 | EXPECT_EQ(ringbuf_copy_data(rb, ret, 3), 0); 43 | ret[3] = '\0'; 44 | EXPECT_STREQ(ret, "abb"); 45 | 46 | memcpy(ret, data, 2); 47 | ringbuf_mark_consumed(rb, 2); 48 | EXPECT_EQ(ringbuf_get_pending_size(rb), 3); 49 | 50 | EXPECT_EQ(ringbuf_get_next_chunk(rb, &data), 3); 51 | EXPECT_EQ(data, data1); 52 | 53 | memcpy(ret + 2, data, 3); 54 | ringbuf_mark_consumed(rb, 3); 55 | EXPECT_EQ(ringbuf_get_pending_size(rb), 0); 56 | 57 | EXPECT_EQ(ringbuf_get_next_chunk(rb, &data), 0); 58 | EXPECT_EQ(data, (char *)NULL); 59 | 60 | ret[5] = '\0'; 61 | EXPECT_STREQ(ret, "abbbb"); 62 | } 63 | 64 | int main(int argc, char* argv[]) 65 | { 66 | testing::InitGoogleTest(&argc, argv); 67 | return RUN_ALL_TESTS(); 68 | } 69 | 70 | -------------------------------------------------------------------------------- /tests/server_kcp_simple_test.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/disenone/kcpev/5211adaf074bc59b26fbf2a4d4760dad1f113e50/tests/server_kcp_simple_test.c -------------------------------------------------------------------------------- /tests/server_kcp_test.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/disenone/kcpev/5211adaf074bc59b26fbf2a4d4760dad1f113e50/tests/server_kcp_test.c -------------------------------------------------------------------------------- /tests/server_kcp_test2.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/disenone/kcpev/5211adaf074bc59b26fbf2a4d4760dad1f113e50/tests/server_kcp_test2.c -------------------------------------------------------------------------------- /tests/server_udp_test.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/disenone/kcpev/5211adaf074bc59b26fbf2a4d4760dad1f113e50/tests/server_udp_test.c -------------------------------------------------------------------------------- /tests/test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #ifdef _WIN32 6 | # include 7 | # include 8 | #else 9 | # include 10 | # include 11 | #endif 12 | #include 13 | #include 14 | #include "test.h" 15 | #include "dbg.h" 16 | 17 | void itimeofday(long *sec, long *usec) 18 | { 19 | #if defined(__unix) || defined(__APPLE__) 20 | struct timeval time; 21 | gettimeofday(&time, NULL); 22 | if (sec) *sec = time.tv_sec; 23 | if (usec) *usec = time.tv_usec; 24 | #else 25 | static long mode = 0, addsec = 0; 26 | BOOL retval; 27 | static int64_t freq = 1; 28 | int64_t qpc; 29 | if (mode == 0) { 30 | retval = QueryPerformanceFrequency((LARGE_INTEGER*)&freq); 31 | freq = (freq == 0)? 1 : freq; 32 | retval = QueryPerformanceCounter((LARGE_INTEGER*)&qpc); 33 | addsec = (long)time(NULL); 34 | addsec = addsec - (long)((qpc / freq) & 0x7fffffff); 35 | mode = 1; 36 | } 37 | retval = QueryPerformanceCounter((LARGE_INTEGER*)&qpc); 38 | retval = retval * 2; 39 | if (sec) *sec = (long)(qpc / freq) + addsec; 40 | if (usec) *usec = (long)((qpc % freq) * 1000000 / freq); 41 | #endif 42 | } 43 | 44 | int64_t iclock64(void) 45 | { 46 | long s, u; 47 | int64_t value; 48 | itimeofday(&s, &u); 49 | value = ((int64_t)s) * 1000 + (u / 1000); 50 | return value; 51 | } 52 | 53 | uint32_t iclock() 54 | { 55 | return (uint32_t)(iclock64() & 0xfffffffful); 56 | } 57 | 58 | void isleep(unsigned long millisecond) 59 | { 60 | #ifdef __unix /* usleep( time * 1000 ); */ 61 | struct timespec ts; 62 | ts.tv_sec = (time_t)(millisecond / 1000); 63 | ts.tv_nsec = (long)((millisecond % 1000) * 1000000); 64 | /*nanosleep(&ts, NULL);*/ 65 | usleep((millisecond << 10) - (millisecond << 4) - (millisecond << 3)); 66 | #elif defined(_WIN32) 67 | Sleep(millisecond); 68 | #endif 69 | } 70 | 71 | int setnonblocking(int fd) 72 | { 73 | #ifdef _WIN32 74 | return 0; 75 | #else 76 | int flag = fcntl(fd, F_GETFL, 0); 77 | if (flag < 0) 78 | { 79 | return -1; 80 | } 81 | if (fcntl(fd, F_SETFL, flag | O_NONBLOCK) < 0) 82 | { 83 | return -1; 84 | } 85 | 86 | return 0; 87 | #endif 88 | } 89 | 90 | int create_rand_str(char* ptr, int min_len, int max_len) 91 | { 92 | int len = rand() % (max_len - min_len) + min_len; 93 | for(int i = 0; i < len; ++i) 94 | { 95 | ptr[i] = rand() % 126 + 1; 96 | } 97 | ptr[len] = '\0'; 98 | return len; 99 | } 100 | 101 | 102 | static stdin_callback stdin_cb = NULL; 103 | 104 | #ifdef _WIN32 105 | static void stdin_read(EV_P_ struct ev_watcher *w, int revents) 106 | { 107 | static HANDLE stdinHandle; 108 | static int index = 0; 109 | static char buf[KCPEV_BUFFER_SIZE]; 110 | // Get the IO handles 111 | stdinHandle = GetStdHandle(STD_INPUT_HANDLE); 112 | 113 | switch (WaitForSingleObject(stdinHandle, 10)) 114 | { 115 | case(WAIT_TIMEOUT) : 116 | break; // return from this function to allow thread to terminate 117 | case(WAIT_OBJECT_0) : 118 | if (_kbhit()) // _kbhit() always returns immediately 119 | { 120 | int i = _getch(); 121 | printf("%c", (char)i); 122 | buf[index++] = (char)i; 123 | if (i == 13) 124 | { 125 | buf[index] = '\0'; 126 | if(stdin_cb) 127 | stdin_cb(EV_A_ w, revents, buf, index); 128 | index = 0; 129 | printf("\n>> "); 130 | fflush(stdout); 131 | } 132 | } 133 | else // some sort of other events , we need to clear it from the queue 134 | { 135 | // clear events 136 | INPUT_RECORD r[512]; 137 | DWORD read; 138 | ReadConsoleInput(stdinHandle, r, 512, &read); 139 | } 140 | break; 141 | case(WAIT_FAILED) : 142 | break; 143 | case(WAIT_ABANDONED) : 144 | break; 145 | default: 146 | break; 147 | } 148 | 149 | } 150 | 151 | #else 152 | static void stdin_read(EV_P_ struct ev_watcher *w, int revents) 153 | { 154 | 155 | char buf[KCPEV_BUFFER_SIZE]; 156 | char *buf_in; 157 | buf_in = fgets(buf, sizeof(buf), stdin); 158 | check(buf_in != NULL, "get stdin"); 159 | 160 | if(stdin_cb) 161 | stdin_cb(EV_A_ w, revents, buf, strlen(buf)); 162 | 163 | printf("\n>> "); 164 | fflush(stdout); 165 | error: 166 | return; 167 | } 168 | 169 | #endif 170 | 171 | ev_watcher* setup_stdin(EV_P_ void *data, stdin_callback cb) 172 | { 173 | stdin_cb = cb; 174 | #ifdef _WIN32 175 | ev_timer *evt = (ev_timer *)calloc(1, sizeof(ev_timer)); 176 | evt->data = data; 177 | ev_timer_init(evt, (ev_timer_callback)stdin_read, 1, 0.05); 178 | ev_timer_start(EV_A_ evt); 179 | return (ev_watcher *)evt; 180 | #else 181 | ev_io *ev_stdin = (ev_io *)calloc(1, sizeof(ev_io)); 182 | ev_stdin->data = data; 183 | ev_io_init(ev_stdin, (ev_io_callback)stdin_read, STDIN_FILENO, EV_READ); 184 | ev_io_start(EV_A_ ev_stdin); 185 | return (ev_watcher *)ev_stdin; 186 | #endif // _WIN32 187 | } 188 | 189 | -------------------------------------------------------------------------------- /tests/test.h: -------------------------------------------------------------------------------- 1 | #ifndef __TEST_H__ 2 | #define __TEST_H__ 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | /* get system time */ 12 | void itimeofday(long *sec, long *usec); 13 | 14 | /* get clock in millisecond 64 */ 15 | int64_t iclock64(void); 16 | 17 | uint32_t iclock(); 18 | 19 | /* sleep in millisecond */ 20 | void isleep(unsigned long millisecond); 21 | 22 | int setnonblocking(int fd); 23 | 24 | int create_rand_str(char* ptr, int min_len, int max_len); 25 | 26 | typedef void (*stdin_callback)(EV_P_ struct ev_watcher *w, int revents, const char *buf, size_t len); 27 | typedef void (*ev_io_callback)(EV_P_ struct ev_io *w, int revents); 28 | typedef void (*ev_timer_callback)(EV_P_ struct ev_timer *w, int revents); 29 | 30 | ev_watcher* setup_stdin(EV_P_ void *data, stdin_callback cb); 31 | 32 | #ifdef __cplusplus 33 | } 34 | #endif 35 | #endif 36 | -------------------------------------------------------------------------------- /travis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # build third_party 4 | ./build_third_party.sh 5 | 6 | mkdir build || true 7 | cd build 8 | cmake .. 9 | make 10 | 11 | -------------------------------------------------------------------------------- /utMakefile: -------------------------------------------------------------------------------- 1 | OBJS=libut.a libut.so 2 | all: $(OBJS) 3 | INCDIR=./include 4 | CFLAGS+=-I$(INCDIR) 5 | CFLAGS+=-Wall -Wextra 6 | CFLAGS+=-g 7 | CFLAGS+=-fpic 8 | 9 | libut.so: libut.o utvector.o utmm.o ringbuf.o 10 | $(CC) -shared -o $@ $^ 11 | 12 | libut.a: libut.o utvector.o utmm.o ringbuf.o 13 | ar r $@ $^ 14 | 15 | libut.o: src/libut.c $(INCDIR)/libut.h 16 | $(CC) $(CFLAGS) $(CPPFLAGS) -c $< 17 | 18 | utvector.o: src/utvector.c $(INCDIR)/utvector.h 19 | $(CC) $(CFLAGS) $(CPPFLAGS) -c $< 20 | 21 | utmm.o: src/utmm.c $(INCDIR)/utmm.h 22 | $(CC) $(CFLAGS) $(CPPFLAGS) -c $< 23 | 24 | ringbuf.o: src/ringbuf.c $(INCDIR)/ringbuf.h 25 | $(CC) $(CFLAGS) $(CPPFLAGS) -c $< 26 | 27 | .PHONY: clean tests install 28 | 29 | clean: 30 | rm -f $(OBJS) *.o 31 | make -C tests clean 32 | 33 | tests: libut.a 34 | make -C tests 35 | 36 | install: libut.a 37 | cp $< /usr/local/lib 38 | --------------------------------------------------------------------------------