├── .clang-format ├── .github └── workflows │ └── cmake.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── Common.cmake ├── LICENSE ├── Plugins └── OnlineSubsystemUTcp │ ├── Config │ └── DefaultOnlineSubsystemUTcp.ini │ ├── OnlineSubsystemUTcp.uplugin │ ├── README.md │ └── Source │ ├── OnlineSubsystemUTcp.Build.cs │ └── Private │ ├── BunchConvert.h │ ├── NetConnectionDebug.cpp │ ├── NetConnectionDebug.h │ ├── PrivatePCH.h │ ├── UTcpNetConnection.cpp │ ├── UTcpNetConnection.h │ ├── UTcpNetDriver.cpp │ └── UTcpNetDriver.h ├── README.md ├── abstract ├── CMakeLists.txt ├── utcp.cpp └── utcp.hpp ├── build.bat ├── build.sh ├── build_Xcode.sh ├── sample ├── CMakeLists.txt ├── ds_connection.cpp ├── ds_connection.h ├── echo_connection.cpp ├── echo_connection.h ├── main.cpp ├── sample_config.h ├── socket.h ├── udp_socket.cpp ├── udp_socket.h ├── utcp_listener.cpp └── utcp_listener.h ├── test ├── CMakeLists.txt └── test_case │ ├── CMakeLists.txt │ ├── bunch_test.cpp │ ├── channel_test.cpp │ ├── handshake_test.cpp │ ├── large_bunch_test.cpp │ ├── open_channel_test.cpp │ ├── packet_notify_test.cpp │ ├── packet_test.cpp │ ├── ringbuffer_test.cpp │ ├── sha1_test.cpp │ ├── test_utils.h │ └── xx_test.cpp.template └── utcp ├── 3rd ├── WjCryptLib_Sha1.c ├── WjCryptLib_Sha1.h ├── dl_list.h ├── lcg_random.c ├── ringbuffer.c └── ringbuffer.h ├── CMakeLists.txt ├── bit_buffer.c ├── bit_buffer.h ├── sha1.c ├── utcp.c ├── utcp.h ├── utcp_bunch.c ├── utcp_bunch.h ├── utcp_channel.c ├── utcp_channel.h ├── utcp_channel_def.h ├── utcp_channel_internal.h ├── utcp_def.h ├── utcp_def_internal.h ├── utcp_handshake.c ├── utcp_handshake.h ├── utcp_packet.c ├── utcp_packet.h ├── utcp_packet_notify.c ├── utcp_packet_notify.h ├── utcp_packet_notify_def.h ├── utcp_sequence_number.h └── utcp_utils.h /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Microsoft 2 | UseTab: Always 3 | IndentExternBlock: NoIndent 4 | IndentWidth: 4 5 | TabWidth: 4 6 | ColumnLimit: 180 7 | DerivePointerAlignment: false 8 | PointerAlignment: Left -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: CMake 2 | 3 | on: 4 | push: 5 | branches: [ "**" ] 6 | pull_request: 7 | branches: [ "**" ] 8 | 9 | env: 10 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 11 | BUILD_TYPE: Debug 12 | 13 | jobs: 14 | build: 15 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 16 | # You can convert this to a matrix build if you need cross-platform coverage. 17 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | with: 23 | submodules: 'true' 24 | 25 | - name: Configure CMake 26 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 27 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 28 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 29 | 30 | - name: Build 31 | # Build your program with the given configuration 32 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 33 | 34 | - name: Test 35 | working-directory: ${{github.workspace}}/build 36 | # Execute tests defined by the CMake configuration. 37 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 38 | run: ./test/test_case/test_case 39 | 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .idea/ 3 | .vscode/ 4 | cmake-build-debug/ 5 | 6 | # Prerequisites 7 | *.d 8 | 9 | # Object files 10 | *.o 11 | *.ko 12 | *.obj 13 | *.elf 14 | 15 | # Linker output 16 | *.ilk 17 | *.map 18 | *.exp 19 | 20 | # Precompiled Headers 21 | *.gch 22 | *.pch 23 | 24 | # Libraries 25 | *.lib 26 | *.a 27 | *.la 28 | *.lo 29 | 30 | # Shared objects (inc. Windows DLLs) 31 | *.dll 32 | *.so 33 | *.so.* 34 | *.dylib 35 | 36 | # Executables 37 | *.exe 38 | *.out 39 | *.app 40 | *.i*86 41 | *.x86_64 42 | *.hex 43 | 44 | # Debug files 45 | *.dSYM/ 46 | *.su 47 | *.idb 48 | *.pdb 49 | 50 | # Kernel Module Compile Results 51 | *.mod* 52 | *.cmd 53 | .tmp_versions/ 54 | modules.order 55 | Module.symvers 56 | Mkfile.old 57 | dkms.conf 58 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "test/googletest"] 2 | path = test/googletest 3 | url = https://github.com/google/googletest.git 4 | branch = v1.8.x 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | set(CMAKE_SUPPRESS_REGENERATION TRUE CACHE BOOL "Disable Zero Check Project") 3 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 4 | 5 | set(PROJ_NAME "utcp") 6 | project(${PROJ_NAME}) 7 | 8 | include(Common.cmake) 9 | 10 | add_subdirectory(utcp) 11 | add_subdirectory(abstract) 12 | add_subdirectory(sample) 13 | add_subdirectory(test) 14 | -------------------------------------------------------------------------------- /Common.cmake: -------------------------------------------------------------------------------- 1 | macro(use_atleast_cxx11) 2 | if(CMAKE_VERSION VERSION_LESS "3.1") 3 | if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 5 | endif() 6 | else() 7 | set(CMAKE_CXX_STANDARD 17) 8 | set(CMAKE_C_STANDARD 11) 9 | endif() 10 | endmacro(use_atleast_cxx11) 11 | 12 | use_atleast_cxx11() 13 | 14 | # WIN32 means windows (rue on windows systems, including win64.) 15 | # APPLE 16 | if(UNIX AND NOT APPLE AND NOT ANDROID) 17 | set(LINUX TRUE) 18 | endif() 19 | 20 | if(${CMAKE_BUILD_TYPE} MATCHES "Debug") 21 | add_definitions(-D_DEBUG) 22 | endif() 23 | 24 | if(MSVC) 25 | add_compile_options(/wd4200) 26 | add_definitions(-D_CRT_SECURE_NO_WARNINGS) 27 | if (CMAKE_CL_64) 28 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi /fp:fast /O2 /Oi /Ot") 29 | set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /debug") 30 | else() 31 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi /fp:fast /arch:SSE2 /O2 /Oi /Ot") 32 | set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /debug") 33 | endif() 34 | 35 | # set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") 36 | # set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 37 | endif(MSVC) 38 | 39 | if(LINUX) 40 | if(${CMAKE_BUILD_TYPE} MATCHES "Debug") 41 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") 42 | set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") 43 | endif() 44 | endif(LINUX) 45 | 46 | macro(source_group_by_dir source_files) 47 | if(MSVC) 48 | set(sgbd_cur_dir ${CMAKE_CURRENT_SOURCE_DIR}) 49 | foreach(sgbd_file ${${source_files}}) 50 | string(REGEX REPLACE ${sgbd_cur_dir}/\(.*\) \\1 sgbd_fpath ${sgbd_file}) 51 | string(REGEX REPLACE "\(.*\)/.*" \\1 sgbd_group_name ${sgbd_fpath}) 52 | string(COMPARE EQUAL ${sgbd_fpath} ${sgbd_group_name} sgbd_nogroup) 53 | string(REPLACE "/" "\\" sgbd_group_name ${sgbd_group_name}) 54 | if(sgbd_nogroup) 55 | set(sgbd_group_name "\\") 56 | endif(sgbd_nogroup) 57 | source_group(${sgbd_group_name} FILES ${sgbd_file}) 58 | endforeach(sgbd_file) 59 | endif(MSVC) 60 | endmacro(source_group_by_dir) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Archer 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 | -------------------------------------------------------------------------------- /Plugins/OnlineSubsystemUTcp/Config/DefaultOnlineSubsystemUTcp.ini: -------------------------------------------------------------------------------- 1 | [/Script/OnlineSubsystemUTcp.UTcpNetDriver] 2 | +AllowPeerConnections=False 3 | +AllowPeerVoice=False 4 | +ConnectionTimeout=60.0 5 | +InitialConnectTimeout=60.0 6 | +RecentlyDisconnectedTrackingTime=120 7 | +TimeoutMultiplierForUnoptimizedBuilds=1 8 | +KeepAliveTime=0.2 9 | +MaxClientRate=100000 10 | +MaxInternetClientRate=100000 11 | +RelevantTimeout=5.0 12 | +SpawnPrioritySeconds=1.0 13 | +ServerTravelPause=4.0 14 | +NetServerMaxTickRate=30 15 | +MaxNetTickRate=120 16 | +NetConnectionClassName="/Script/OnlineSubsystemUTcp.UTcpConnection" 17 | +MaxPortCountToTry=512 18 | +ResolutionConnectionTimeout=20.0 19 | !ChannelDefinitions=CLEAR_ARRAY 20 | +ChannelDefinitions=(ChannelName=Control, ClassName=/Script/OnlineSubsystemUTcp.UTcpControlChannel, StaticChannelIndex=0, bTickOnCreate=true, bServerOpen=false, bClientOpen=true, bInitialServer=false, bInitialClient=true) 21 | +ChannelDefinitions=(ChannelName=Voice, ClassName=/Script/Engine.VoiceChannel, StaticChannelIndex=1, bTickOnCreate=true, bServerOpen=true, bClientOpen=true, bInitialServer=true, bInitialClient=true) 22 | +ChannelDefinitions=(ChannelName=Actor, ClassName=/Script/OnlineSubsystemUTcp.UTcpActorChannel, StaticChannelIndex=-1, bTickOnCreate=false, bServerOpen=true, bClientOpen=false, bInitialServer=false, bInitialClient=false) 23 | -------------------------------------------------------------------------------- /Plugins/OnlineSubsystemUTcp/OnlineSubsystemUTcp.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion" : 3, 3 | 4 | "FriendlyName" : "OnlineSubsystemUTcp", 5 | "Version" : 1, 6 | "VersionName" : "1.0", 7 | "Description" : "UTcp NetDriver", 8 | "Category" : "Online Platform", 9 | "CreatedBy" : "", 10 | "CreatedByURL" : "", 11 | "EnabledByDefault" : true, 12 | 13 | "Modules": [ 14 | { 15 | "Name": "OnlineSubsystemUTcp", 16 | "Type": "Runtime", 17 | "LoadingPhase": "PostConfigInit" 18 | } 19 | ], 20 | "Plugins": [ 21 | { 22 | "Name": "OnlineSubsystem", 23 | "Enabled": true 24 | }, 25 | { 26 | "Name": "OnlineSubsystemUtils", 27 | "Enabled": true 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /Plugins/OnlineSubsystemUTcp/README.md: -------------------------------------------------------------------------------- 1 | # Plugins/OnlineSubsystemUTcp 2 | 3 | ``` 4 | mklink /D Plugins\OnlineSubsystemUTcp\Source\Private\utcp ${utcp dir}\utcp 5 | mklink /D Plugins\OnlineSubsystemUTcp\Source\Private\abstract ${utcp dir}\abstract 6 | ``` -------------------------------------------------------------------------------- /Plugins/OnlineSubsystemUTcp/Source/OnlineSubsystemUTcp.Build.cs: -------------------------------------------------------------------------------- 1 | using UnrealBuildTool; 2 | using System.IO; 3 | 4 | public class OnlineSubsystemUTcp : ModuleRules 5 | { 6 | public OnlineSubsystemUTcp(ReadOnlyTargetRules Target) : base(Target) 7 | { 8 | CStandard = CStandardVersion.Latest; 9 | PrivatePCHHeaderFile = "Private/PrivatePCH.h"; 10 | PublicDefinitions.Add("ONLINESUBSYSTEMUTILS_PACKAGE=1"); 11 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 12 | 13 | PublicDependencyModuleNames.AddRange( 14 | new string[] { 15 | } 16 | ); 17 | 18 | PrivateDependencyModuleNames.AddRange( 19 | new string[] { 20 | "Core", 21 | "CoreUObject", 22 | "NetCore", 23 | "Engine", 24 | "EngineSettings", 25 | "ImageCore", 26 | "Sockets", 27 | "Voice", 28 | "PacketHandler", 29 | "Json", 30 | "OnlineSubsystemUtils", 31 | "DeveloperSettings", 32 | "Networking", 33 | } 34 | ); 35 | 36 | if (Target.Type == TargetType.Server || Target.Type == TargetType.Editor) 37 | { 38 | PrivateDependencyModuleNames.Add("PerfCounters"); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Plugins/OnlineSubsystemUTcp/Source/Private/BunchConvert.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Net/DataBunch.h" 3 | #include "utcp/utcp_def.h" 4 | 5 | struct FConvert 6 | { 7 | static void To(const FOutBunch* InBunch, utcp_bunch* OutBunch) 8 | { 9 | memset(OutBunch, 0, sizeof(*OutBunch)); 10 | OutBunch->NameIndex = (uint32)(*(InBunch->ChName.ToEName())); 11 | OutBunch->ChIndex = InBunch->ChIndex; 12 | OutBunch->bOpen = InBunch->bOpen; 13 | OutBunch->bClose = InBunch->bClose; 14 | OutBunch->CloseReason = (uint8_t)InBunch->CloseReason; 15 | OutBunch->bIsReplicationPaused = InBunch->bIsReplicationPaused; 16 | OutBunch->bReliable = InBunch->bReliable; 17 | OutBunch->bHasPackageMapExports = InBunch->bHasPackageMapExports; 18 | OutBunch->bHasMustBeMappedGUIDs = InBunch->bHasMustBeMappedGUIDs; 19 | OutBunch->bPartial = InBunch->bPartial; 20 | OutBunch->bPartialInitial = InBunch->bPartialInitial; 21 | OutBunch->bPartialFinal = InBunch->bPartialFinal; 22 | OutBunch->DataBitsLen = InBunch->GetNumBits(); 23 | if (OutBunch->DataBitsLen > 0) 24 | { 25 | memcpy(OutBunch->Data, InBunch->GetData(), InBunch->GetNumBytes()); 26 | } 27 | } 28 | 29 | static void To(const utcp_bunch* InBunch, FInBunch* OutBunch) 30 | { 31 | OutBunch->ChName = EName(InBunch->NameIndex); 32 | OutBunch->PacketId = InBunch->PacketId; 33 | OutBunch->ChIndex = InBunch->ChIndex; 34 | OutBunch->ChSequence = InBunch->ChSequence; 35 | OutBunch->bOpen = InBunch->bOpen; 36 | OutBunch->bClose = InBunch->bClose; 37 | OutBunch->CloseReason = (EChannelCloseReason)InBunch->CloseReason; 38 | OutBunch->bIsReplicationPaused = InBunch->bIsReplicationPaused; 39 | OutBunch->bReliable = InBunch->bReliable; 40 | OutBunch->bHasPackageMapExports = InBunch->bHasPackageMapExports; 41 | OutBunch->bHasMustBeMappedGUIDs = InBunch->bHasMustBeMappedGUIDs; 42 | OutBunch->bPartial = InBunch->bPartial; 43 | OutBunch->bPartialInitial = InBunch->bPartialInitial; 44 | OutBunch->bPartialFinal = InBunch->bPartialFinal; 45 | OutBunch->SetData((uint8*)InBunch->Data, InBunch->DataBitsLen); 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /Plugins/OnlineSubsystemUTcp/Source/Private/NetConnectionDebug.cpp: -------------------------------------------------------------------------------- 1 | #include "NetConnectionDebug.h" 2 | #include "Net/DataBunch.h" 3 | #include "Misc/Paths.h" 4 | #include "Engine/NetConnection.h" 5 | #include "Engine/World.h" 6 | #include "Engine/NetDriver.h" 7 | #include "GameFramework/Actor.h" 8 | #include "HAL/PlatformFileManager.h" 9 | #include "HAL/PlatformStackWalk.h" 10 | 11 | constexpr int IGNORE_STACK_COUNT = 5; 12 | 13 | FNetConnectionDebug::FNetConnectionDebug() 14 | { 15 | bThreadExit.store(0); 16 | bEnable = false; 17 | bVerbose = false; 18 | WriteThread = std::thread([this]() { 19 | this->ThreadFunction(); 20 | }); 21 | } 22 | 23 | FNetConnectionDebug::~FNetConnectionDebug() 24 | { 25 | bThreadExit.store(1); 26 | WriteThreadNotify.notify_one(); 27 | WriteThread.join(); 28 | for (auto Item : WriteList) 29 | { 30 | delete Item; 31 | } 32 | WriteList.clear(); 33 | 34 | for (auto& Item : ConnName2FD) 35 | { 36 | if (Item.second) 37 | delete Item.second; 38 | } 39 | ConnName2FD.clear(); 40 | } 41 | 42 | void FNetConnectionDebug::Init(bool bInEnable, bool bInVerbose) 43 | { 44 | bEnable = bInEnable; 45 | bVerbose = bInVerbose; 46 | } 47 | 48 | void FNetConnectionDebug::OnSend(const UNetConnection* InConn, const FOutBunch* InBunch, int32 InPacketId) 49 | { 50 | if (!bEnable) 51 | return; 52 | 53 | if (!IsValid(InConn)) 54 | return; 55 | 56 | auto Info = new FWriteInfo; 57 | auto Driver = InConn->GetDriver(); 58 | Info->bIsServer = Driver->IsServer(); 59 | Info->bSendBunch = true; 60 | Info->ConnName = InConn->GetName(); 61 | 62 | Info->NumBits = InBunch->GetNumBits(); 63 | Info->ChIndex = InBunch->ChIndex; 64 | Info->PacketId = InPacketId; 65 | Info->bOpen = InBunch->bOpen; 66 | Info->bClose = InBunch->bClose; 67 | Info->bReliable = InBunch->bReliable; 68 | Info->bPartial = InBunch->bPartial; 69 | Info->bPartialInitial = InBunch->bPartialInitial; 70 | Info->bPartialFinal = InBunch->bPartialFinal; 71 | Info->bHasPackageMapExports = InBunch->bHasPackageMapExports; 72 | 73 | if (bVerbose) 74 | { 75 | Info->CRC = FCrc::MemCrc32(InBunch->GetData(), InBunch->GetNumBytes()); 76 | FPlatformStackWalk::CaptureStackBackTrace(Info->StackTrace, std::size(Info->StackTrace)); 77 | } 78 | CacheInfo(Info); 79 | } 80 | 81 | void FNetConnectionDebug::OnRecv(const UNetConnection* InConn, const FInBunch* InBunch) 82 | { 83 | if (!bEnable) 84 | return; 85 | 86 | if (!IsValid(InConn)) 87 | return; 88 | 89 | auto Info = new FWriteInfo; 90 | auto Driver = InConn->GetDriver(); 91 | Info->bIsServer = Driver->IsServer(); 92 | Info->bSendBunch = false; 93 | Info->ConnName = InConn->GetName(); 94 | 95 | Info->NumBits = InBunch->GetNumBits(); 96 | Info->ChIndex = InBunch->ChIndex; 97 | Info->PacketId = InBunch->PacketId; 98 | Info->bOpen = InBunch->bOpen; 99 | Info->bClose = InBunch->bClose; 100 | Info->bReliable = InBunch->bReliable; 101 | Info->bPartial = InBunch->bPartial; 102 | Info->bPartialInitial = InBunch->bPartialInitial; 103 | Info->bPartialFinal = InBunch->bPartialFinal; 104 | Info->bHasPackageMapExports = InBunch->bHasPackageMapExports; 105 | 106 | if (bVerbose) 107 | { 108 | Info->CRC = FCrc::MemCrc32(InBunch->GetData(), InBunch->GetNumBytes()); 109 | } 110 | 111 | CacheInfo(Info); 112 | } 113 | 114 | void FNetConnectionDebug::ThreadFunction() 115 | { 116 | while (bThreadExit.load() == 0) 117 | { 118 | FWriteInfo* Info = nullptr; 119 | 120 | { 121 | std::lock_guard Lock(WriteListMutex); 122 | if (!WriteList.empty()) 123 | { 124 | Info = WriteList.front(); 125 | WriteList.pop_front(); 126 | } 127 | } 128 | 129 | if (Info) 130 | { 131 | WriteInfo(Info); 132 | delete Info; 133 | } 134 | else 135 | { 136 | std::unique_lock Lock(WriteListMutex); 137 | WriteThreadNotify.wait(Lock); 138 | } 139 | } 140 | } 141 | 142 | void FNetConnectionDebug::WriteInfo(FWriteInfo* InInfo) 143 | { 144 | auto FileHandle = GetOrOpenFile(InInfo->ConnName, !!InInfo->bSendBunch, !!InInfo->bIsServer); 145 | if (!FileHandle) 146 | return; 147 | 148 | StringBuilder.Reset(); 149 | StringBuilder.Appendf( 150 | "ChIndex:%d, PacketId:%d, NumBits:%d, Open:%d, Close:%d, Reliable:%d, Partial:%d[%d, %d], HasPackageMapExports:%d\n", 151 | InInfo->ChIndex, 152 | InInfo->PacketId, 153 | InInfo->NumBits, 154 | InInfo->bOpen, 155 | InInfo->bClose, 156 | InInfo->bReliable, 157 | InInfo->bPartial, InInfo->bPartialInitial, InInfo->bPartialFinal, 158 | InInfo->bHasPackageMapExports); 159 | 160 | if (bVerbose) 161 | { 162 | StringBuilder.Appendf("\tCRC:%u\n", InInfo->CRC); 163 | } 164 | 165 | FileHandle->Write((uint8*)StringBuilder.GetData(), StringBuilder.Len()); 166 | WriteStackInfo(InInfo, FileHandle); 167 | } 168 | 169 | void FNetConnectionDebug::WriteStackInfo(FWriteInfo* InInfo, IFileHandle* InFileHandle) 170 | { 171 | if (!InInfo->bSendBunch || !bVerbose) 172 | return; 173 | 174 | StringBuilder.Reset(); 175 | for (int i = IGNORE_STACK_COUNT; i < std::size(InInfo->StackTrace); ++i) 176 | { 177 | auto Addr = InInfo->StackTrace[i]; 178 | if (!Addr) 179 | break; 180 | 181 | auto it = Addr2Line.find(Addr); 182 | if (it == Addr2Line.end()) 183 | { 184 | FProgramCounterSymbolInfo SymbolInfo; 185 | #if PLATFORM_WINDOWS 186 | __try 187 | { 188 | #endif 189 | FPlatformStackWalk::ProgramCounterToSymbolInfo(InInfo->StackTrace[i], SymbolInfo); 190 | #if PLATFORM_WINDOWS 191 | } 192 | __except (true) 193 | { 194 | } 195 | #endif 196 | char Buffer[4096]; 197 | snprintf(Buffer, sizeof(Buffer), "\t%s [%s:%d]\n", SymbolInfo.FunctionName, SymbolInfo.Filename, SymbolInfo.LineNumber); 198 | Buffer[sizeof(Buffer) - 1] = '\0'; 199 | auto itInsert = Addr2Line.insert(std::make_pair(Addr, Buffer)); 200 | it = itInsert.first; 201 | } 202 | StringBuilder.Append(it->second.c_str(), it->second.size()); 203 | } 204 | InFileHandle->Write((uint8*)StringBuilder.GetData(), StringBuilder.Len()); 205 | } 206 | 207 | void FNetConnectionDebug::CacheInfo(FWriteInfo* InInfo) 208 | { 209 | { 210 | std::lock_guard Lock(WriteListMutex); 211 | WriteList.push_back(InInfo); 212 | } 213 | WriteThreadNotify.notify_one(); 214 | } 215 | 216 | IFileHandle* FNetConnectionDebug::GetOrOpenFile(const FString& InConnName, bool bInSend, bool bIsServer) 217 | { 218 | FString FileName = InConnName; 219 | FileName.Append(bIsServer ? TEXT("_S_") : TEXT("_C_")); 220 | FileName.Append(bInSend ? TEXT("Send.txt") : TEXT("Recv.txt")); 221 | 222 | auto it = ConnName2FD.insert(std::make_pair(FileName, nullptr)); 223 | if (it.second) 224 | { 225 | auto FilePath = FPaths::Combine(FPaths::ProjectSavedDir(), FileName); 226 | auto& PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); 227 | while (PlatformFile.FileExists(*FilePath)) 228 | { 229 | FilePath.Append(".txt"); 230 | } 231 | auto File = PlatformFile.OpenWrite(*FilePath, false, false); 232 | it.first->second = File; 233 | } 234 | 235 | ensure(it.first->second); 236 | return it.first->second; 237 | } -------------------------------------------------------------------------------- /Plugins/OnlineSubsystemUTcp/Source/Private/NetConnectionDebug.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "CoreMinimal.h" 3 | #include "GenericPlatform/GenericPlatformFile.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class UNetConnection; 11 | class FOutBunch; 12 | class FInBunch; 13 | class AActor; 14 | class FNetConnectionDebug 15 | { 16 | public: 17 | FNetConnectionDebug(); 18 | ~FNetConnectionDebug(); 19 | 20 | void Init(bool bInEnable, bool bInVerbose); 21 | 22 | void OnSend(const UNetConnection* InConn, const FOutBunch* InBunch, int32 InPacketId); 23 | void OnRecv(const UNetConnection* InConn, const FInBunch* InBunch); 24 | 25 | private: 26 | struct FWriteInfo 27 | { 28 | FString ConnName; 29 | 30 | uint32 CRC; 31 | int32 NumBits; 32 | 33 | int32 ChIndex; 34 | int32 PacketId; 35 | 36 | uint8 bOpen : 1; 37 | uint8 bClose : 1; 38 | uint8 bReliable : 1; 39 | uint8 bPartial : 1; 40 | uint8 bPartialInitial : 1; 41 | uint8 bPartialFinal : 1; 42 | uint8 bHasPackageMapExports : 1; 43 | 44 | uint8 bSendBunch : 1; 45 | uint8 bIsServer : 1; 46 | 47 | uint64 StackTrace[16]; 48 | }; 49 | 50 | void ThreadFunction(); 51 | void WriteInfo(FWriteInfo* InInfo); 52 | void WriteStackInfo(FWriteInfo* InInfo, IFileHandle* InFileHandle); 53 | void CacheInfo(FWriteInfo* InInfo); 54 | IFileHandle* GetOrOpenFile(const FString& InConnName, bool bInSend, bool bIsServer); 55 | 56 | TAnsiStringBuilder<1024 * 1024> StringBuilder; 57 | std::list WriteList; 58 | std::mutex WriteListMutex; 59 | std::thread WriteThread; 60 | std::condition_variable WriteThreadNotify; 61 | std::map ConnName2FD; 62 | std::unordered_map Addr2Line; 63 | std::atomic bThreadExit; 64 | bool bVerbose; 65 | bool bEnable; 66 | }; 67 | -------------------------------------------------------------------------------- /Plugins/OnlineSubsystemUTcp/Source/Private/PrivatePCH.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "Logging/LogMacros.h" 5 | 6 | DECLARE_LOG_CATEGORY_EXTERN(LogUTcp, Log, All) -------------------------------------------------------------------------------- /Plugins/OnlineSubsystemUTcp/Source/Private/UTcpNetConnection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "IpConnection.h" 5 | #include "abstract/utcp.hpp" 6 | #include "Engine/ControlChannel.h" 7 | #include "Engine/ActorChannel.h" 8 | #include "Engine/NetConnection.h" 9 | #include "UTcpNetConnection.generated.h" 10 | 11 | UCLASS(transient, config = OnlineSubsystemUTcp) 12 | class UUTcpConnection : public UIpConnection, public utcp::conn 13 | { 14 | public: 15 | GENERATED_BODY() 16 | 17 | virtual void InitSequence(int32 IncomingSequence, int32 OutgoingSequence) override; 18 | virtual void InitRemoteConnection(UNetDriver* InDriver, class FSocket* InSocket, const FURL& InURL, const class FInternetAddr& InRemoteAddr, EConnectionState InState, int32 InMaxPacket = 0, int32 InPacketOverhead = 0) override; 19 | 20 | virtual void ReceivedRawPacket(void* Data, int32 Count) override; 21 | 22 | virtual void Tick(float DeltaSeconds) override; 23 | virtual void FlushNet(bool bIgnoreSimulation) override; 24 | virtual void LowLevelSend(void* Data, int32 CountBits, FOutPacketTraits& Traits) override; 25 | 26 | public: 27 | int32 InternalSendRawBunch(FOutBunch* InBunch, bool Merge); 28 | void InternalPostTickDispatch(); 29 | 30 | protected: 31 | virtual void on_disconnect(int close_reason) override; 32 | virtual void on_outgoing(const void* data, int len) override; 33 | virtual void on_recv_bunch(struct utcp_bunch* const bunches[], int count) override; 34 | virtual void on_delivery_status(int32_t packet_id, bool ack) override; 35 | 36 | private: 37 | void InternalAck(int32 AckPacketId, FChannelsToClose& OutChannelsToClose); 38 | void InternalNak(int32 NakPacketId); 39 | }; 40 | 41 | class UUTcpChannel : public UChannel 42 | { 43 | public: 44 | void UTcpTick(); 45 | FPacketIdRange UTcpSendBunch(FOutBunch* Bunch, bool Merge); 46 | void UTcpOnRecv(FInBunch& Bunch); 47 | 48 | private: 49 | FOutBunch* UTcpPrepBunch(FOutBunch* Bunch, FOutBunch* OutBunch, bool Merge); 50 | int32 UTcpSendRawBunch(FOutBunch* OutBunch, bool Merge); 51 | }; 52 | static_assert(sizeof(UUTcpChannel) == sizeof(UChannel)); 53 | 54 | UCLASS(transient, customConstructor) 55 | class UUTcpControlChannel : public UControlChannel 56 | { 57 | GENERATED_UCLASS_BODY() 58 | 59 | UUTcpControlChannel(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()) 60 | : UControlChannel(ObjectInitializer) 61 | { 62 | } 63 | 64 | virtual FPacketIdRange SendBunch(FOutBunch* Bunch, bool Merge) override 65 | { 66 | return ((UUTcpChannel*)this)->UTcpSendBunch(Bunch, Merge); 67 | } 68 | 69 | virtual void ReceivedBunch(FInBunch& Bunch) 70 | { 71 | ((UUTcpChannel*)this)->UTcpOnRecv(Bunch); 72 | Super::ReceivedBunch(Bunch); 73 | } 74 | }; 75 | 76 | UCLASS(transient, customConstructor) 77 | class UUTcpActorChannel : public UActorChannel 78 | { 79 | GENERATED_UCLASS_BODY() 80 | 81 | UUTcpActorChannel(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()) 82 | : UActorChannel(ObjectInitializer) 83 | { 84 | } 85 | 86 | virtual void Tick() override 87 | { 88 | Super::Tick(); 89 | if (Connection->Driver->IsServer()) 90 | ((UUTcpChannel*)this)->UTcpTick(); 91 | } 92 | 93 | virtual FPacketIdRange SendBunch(FOutBunch* Bunch, bool Merge) override 94 | { 95 | return ((UUTcpChannel*)this)->UTcpSendBunch(Bunch, Merge); 96 | } 97 | 98 | virtual void ReceivedBunch(FInBunch& Bunch) 99 | { 100 | ((UUTcpChannel*)this)->UTcpOnRecv(Bunch); 101 | Super::ReceivedBunch(Bunch); 102 | } 103 | }; 104 | -------------------------------------------------------------------------------- /Plugins/OnlineSubsystemUTcp/Source/Private/UTcpNetDriver.cpp: -------------------------------------------------------------------------------- 1 | #include "UTcpNetDriver.h" 2 | #include "EngineUtils.h" 3 | #include "Sockets.h" 4 | #include "Net/RepLayout.h" 5 | #include "GameFramework/PlayerController.h" 6 | #include "PacketHandlers/StatelessConnectHandlerComponent.h" 7 | #include "Modules/ModuleManager.h" 8 | 9 | class FOnlineSubsystemUTcpModule : public IModuleInterface 10 | { 11 | }; 12 | 13 | IMPLEMENT_MODULE(FOnlineSubsystemUTcpModule, OnlineSubsystemUTcp); 14 | DEFINE_LOG_CATEGORY(LogUTcp); 15 | 16 | static void LogWrapper(int InLevel, const char* InFormat, va_list InArgs) 17 | { 18 | char TempString[1024]; 19 | FCStringAnsi::GetVarArgs(TempString, UE_ARRAY_COUNT(TempString), InFormat, InArgs); 20 | UE_LOG(LogUTcp, Log, TEXT("[UTCP]%s"), UTF8_TO_TCHAR(TempString)); 21 | } 22 | 23 | bool UUTcpNetDriver::InitBase(bool bInitAsClient, FNetworkNotify* InNotify, const FURL& URL, bool bReuseAddressAndPort, 24 | FString& Error) 25 | { 26 | NetConnectionDebug.Init(true, false); 27 | return Super::InitBase(bInitAsClient, InNotify, URL, bReuseAddressAndPort, Error); 28 | } 29 | 30 | void UUTcpNetDriver::InitConnectionlessHandler() 31 | { 32 | check(!ConnectionlessHandler.IsValid()); 33 | auto CVarNetIpNetDriverUseReceiveThread = IConsoleManager::Get().FindConsoleVariable(TEXT("net.IpNetDriverUseReceiveThread")); 34 | check(CVarNetIpNetDriverUseReceiveThread->GetInt() == 0); 35 | 36 | config(LogWrapper); 37 | enbale_dump_data(true); 38 | } 39 | 40 | void UUTcpNetDriver::TickDispatch(float DeltaTime) 41 | { 42 | if (!IsServer()) 43 | { 44 | Super::TickDispatch(DeltaTime); 45 | return; 46 | } 47 | 48 | if (!LastRecvAddress.IsValid()) 49 | LastRecvAddress = GetSocketSubsystem()->CreateInternetAddr(); 50 | 51 | while (true) 52 | { 53 | int32 BytesRead = 0; 54 | bool bReceivedPacket = GetSocket()->RecvFrom(LastRecvData.GetData(), MAX_PACKET_SIZE, BytesRead, *LastRecvAddress); 55 | if (!bReceivedPacket) 56 | return; 57 | 58 | UNetConnection** Result = MappedClientConnections.Find(LastRecvAddress.ToSharedRef()); 59 | if (Result == nullptr || !IsValid(*Result)) 60 | { 61 | auto Str = LastRecvAddress->ToString(false); 62 | incoming(TCHAR_TO_UTF8(*Str), LastRecvData.GetData(), BytesRead); 63 | return; 64 | } 65 | 66 | auto Connection = Cast(*Result); 67 | check(Connection); 68 | Connection->ReceivedRawPacket(LastRecvData.GetData(), BytesRead); 69 | } 70 | } 71 | 72 | void UUTcpNetDriver::LowLevelSend(TSharedPtr Address, void* Data, int32 CountBits, FOutPacketTraits& Traits) 73 | { 74 | if (IsServer()) 75 | check(false); 76 | Super::LowLevelSend(Address, Data, CountBits, Traits); 77 | } 78 | 79 | void UUTcpNetDriver::Shutdown() 80 | { 81 | Super::Shutdown(); 82 | } 83 | 84 | void UUTcpNetDriver::PostTickDispatch() 85 | { 86 | Super::PostTickDispatch(); 87 | 88 | TArray ClientConnCopy = ClientConnections; 89 | for (auto CurConn : ClientConnCopy) 90 | { 91 | if (IsValid(CurConn)) 92 | { 93 | Cast(CurConn)->InternalPostTickDispatch(); 94 | } 95 | } 96 | } 97 | 98 | void UUTcpNetDriver::on_accept(bool reconnect) 99 | { 100 | UIpConnection* ReturnVal = nullptr; 101 | const TSharedPtr& Address = LastRecvAddress; 102 | 103 | if (reconnect) 104 | { 105 | UIpConnection* FoundConn = nullptr; 106 | for (UNetConnection* CurConn : ClientConnections) 107 | { 108 | auto Conn = Cast(CurConn); 109 | if (does_restarted_handshake_match(Conn)) 110 | { 111 | FoundConn = Cast(CurConn); 112 | break; 113 | } 114 | } 115 | 116 | if (FoundConn != nullptr) 117 | { 118 | UNetConnection* RemovedConn = nullptr; 119 | TSharedRef RemoteAddrRef = FoundConn->RemoteAddr.ToSharedRef(); 120 | 121 | verify(MappedClientConnections.RemoveAndCopyValue(RemoteAddrRef, RemovedConn) && RemovedConn == FoundConn); 122 | 123 | // @todo: There needs to be a proper/standardized copy API for this. Also in IpConnection.cpp 124 | bool bIsValid = false; 125 | const FString OldAddress = RemoteAddrRef->ToString(true); 126 | RemoteAddrRef->SetIp(*Address->ToString(false), bIsValid); 127 | RemoteAddrRef->SetPort(Address->GetPort()); 128 | 129 | MappedClientConnections.Add(RemoteAddrRef, FoundConn); 130 | ReturnVal = FoundConn; 131 | 132 | // We shouldn't need to log IncomingAddress, as the UNetConnection should dump it with it's description. 133 | UE_LOG(LogNet, Log, TEXT("Updated IP address for connection. Connection = %s, Old Address = %s"), *FoundConn->Describe(), *OldAddress); 134 | } 135 | else 136 | { 137 | UE_LOG(LogUTcp, Log, TEXT("Failed to find an existing connection with a matching cookie. Restarted Handshake failed.")); 138 | } 139 | } 140 | else 141 | { 142 | UE_LOG(LogNet, Log, TEXT("Server accepting post-challenge connection from: %s"), *Address->ToString(true)); 143 | 144 | ReturnVal = NewObject(GetTransientPackage(), NetConnectionClass); 145 | check(ReturnVal != nullptr); 146 | ReturnVal->InitRemoteConnection(this, GetSocket(), World ? World->URL : FURL(), *Address, USOCK_Open); 147 | AddClientConnection(ReturnVal); 148 | } 149 | 150 | if (ReturnVal) 151 | { 152 | auto UTcpConn = Cast(ReturnVal); 153 | UTcpConn->set_debug_name(TCHAR_TO_UTF8(*UTcpConn->GetName())); 154 | accept(UTcpConn, reconnect); 155 | } 156 | else 157 | { 158 | // UE_LOG(LogUTcp, Log, TEXT("")); 159 | } 160 | } 161 | 162 | void UUTcpNetDriver::on_outgoing(const void* data, int len) 163 | { 164 | int32 BytesSent = 0; 165 | GetSocket()->SendTo((const uint8*)data, len, BytesSent, *LastRecvAddress); 166 | } 167 | -------------------------------------------------------------------------------- /Plugins/OnlineSubsystemUTcp/Source/Private/UTcpNetDriver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "UObject/ObjectMacros.h" 5 | #include "UTcpNetConnection.h" 6 | #include "IpNetDriver.h" 7 | #include "NetConnectionDebug.h" 8 | #include "abstract/utcp.hpp" 9 | #include "UTcpNetDriver.generated.h" 10 | 11 | UCLASS(transient, config = OnlineSubsystemUTcp) 12 | class UUTcpNetDriver : public UIpNetDriver, public utcp::listener 13 | { 14 | GENERATED_BODY() 15 | 16 | public: 17 | virtual bool InitBase(bool bInitAsClient, FNetworkNotify* InNotify, const FURL& URL, bool bReuseAddressAndPort, FString& Error) override; 18 | virtual void InitConnectionlessHandler() override; 19 | virtual void TickDispatch(float DeltaTime) override; 20 | virtual void LowLevelSend(TSharedPtr Address, void* Data, int32 CountBits, FOutPacketTraits& Traits) override; 21 | virtual void Shutdown() override; 22 | virtual void PostTickDispatch() override; 23 | 24 | FNetConnectionDebug* GetNetConnectionDebug() { return &NetConnectionDebug; } 25 | 26 | protected: 27 | virtual void on_accept(bool reconnect) override; 28 | virtual void on_outgoing(const void* data, int len) override; 29 | 30 | private: 31 | FNetConnectionDebug NetConnectionDebug; 32 | TSharedPtr LastRecvAddress; 33 | TArray> LastRecvData; 34 | }; 35 | 36 | inline void DumpBin(const TCHAR* InPrefix, const void* InData, int32 InDataLen) 37 | { 38 | FString Str; 39 | 40 | for (auto i = 0; i < InDataLen; ++i) 41 | { 42 | if (i != 0) 43 | { 44 | Str.Append(TEXT(", ")); 45 | } 46 | 47 | Str.Appendf(TEXT("0x%hhX"), ((const uint8_t*)InData)[i]); 48 | } 49 | UE_LOG(LogUTcp, Log, TEXT("[DUMP]%s\t%d\t{%s}"), InPrefix, InDataLen, *Str); 50 | } 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # utcp 2 | rudp 3 | -------------------------------------------------------------------------------- /abstract/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | get_filename_component(CURRENT_SOURCE_DIR_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) 2 | set(LIB_NAME ${CURRENT_SOURCE_DIR_NAME}) # 工程名, 默认文件夹名 3 | 4 | include_directories(${CMAKE_SOURCE_DIR}) 5 | 6 | file(GLOB_RECURSE SOURCE_FILES 7 | *.h 8 | *.hpp 9 | *.cpp 10 | *.cc 11 | *.c 12 | ) 13 | add_library(${LIB_NAME} STATIC ${SOURCE_FILES}) 14 | target_link_libraries(${LIB_NAME} utcp) 15 | source_group_by_dir(SOURCE_FILES) 16 | set_property(TARGET ${LIB_NAME} PROPERTY FOLDER "utcp") -------------------------------------------------------------------------------- /abstract/utcp.cpp: -------------------------------------------------------------------------------- 1 | #include "utcp.hpp" 2 | 3 | extern "C" { 4 | #include "utcp/bit_buffer.h" 5 | #include "utcp/utcp_def_internal.h" 6 | #include "utcp/utcp_utils.h" 7 | } 8 | #include 9 | 10 | namespace utcp 11 | { 12 | void event_handler::add_elapsed_time(int64_t delta_time_ns) 13 | { 14 | utcp_add_elapsed_time(delta_time_ns); 15 | } 16 | 17 | void event_handler::config(decltype(utcp_config::on_log) log_fn) 18 | { 19 | auto config = utcp_get_config(); 20 | config->on_accept = [](struct utcp_listener* fd, void* userdata, bool reconnect) { 21 | auto handler = static_cast(userdata); 22 | handler->on_accept(reconnect); 23 | }; 24 | config->on_connect = [](struct utcp_connection* fd, void* userdata, bool reconnect) { 25 | auto handler = static_cast(userdata); 26 | handler->on_connect(reconnect); 27 | }; 28 | config->on_disconnect = [](struct utcp_connection* fd, void* userdata, int close_reason) { 29 | auto handler = static_cast(userdata); 30 | handler->on_disconnect(close_reason); 31 | }; 32 | 33 | config->on_outgoing = [](void* fd, void* userdata, const void* data, int len) { 34 | auto handler = static_cast(userdata); 35 | handler->on_outgoing(data, len); 36 | }; 37 | config->on_recv_bunch = [](struct utcp_connection* fd, void* userdata, struct utcp_bunch* const bunches[], int count) { 38 | auto handler = static_cast(userdata); 39 | handler->on_recv_bunch(bunches, count); 40 | }; 41 | config->on_delivery_status = [](struct utcp_connection* fd, void* userdata, int32_t packet_id, bool ack) { 42 | auto handler = static_cast(userdata); 43 | handler->on_delivery_status(packet_id, ack); 44 | }; 45 | config->on_log = log_fn; 46 | } 47 | 48 | void event_handler::enbale_dump_data(bool enable) 49 | { 50 | auto config = utcp_get_config(); 51 | config->EnableDump = enable; 52 | } 53 | 54 | event_handler::~event_handler() 55 | { 56 | } 57 | 58 | void event_handler::on_accept(bool reconnect) 59 | { 60 | throw; 61 | } 62 | 63 | void event_handler::on_connect(bool reconnect) 64 | { 65 | 66 | } 67 | 68 | void event_handler::on_disconnect(int close_reason) 69 | { 70 | 71 | } 72 | 73 | void event_handler::on_outgoing(const void* data, int len) 74 | { 75 | throw; 76 | } 77 | 78 | void event_handler::on_recv_bunch(struct utcp_bunch* const bunches[], int count) 79 | { 80 | throw; 81 | } 82 | 83 | void event_handler::on_delivery_status(int32_t packet_id, bool ack) 84 | { 85 | 86 | } 87 | 88 | large_bunch::large_bunch(const uint8_t* data, size_t data_bits_len) 89 | { 90 | memset(this, 0, sizeof(*this)); 91 | 92 | auto data_bytes_len = bits2bytes(data_bits_len); 93 | if (data_bytes_len > sizeof(ExtData)) 94 | throw; 95 | 96 | if (data_bits_len > MAX_PARTIAL_BUNCH_SIZE_BITS) 97 | { 98 | assert(data_bytes_len < sizeof(ExtData)); 99 | memcpy(ExtData, data, data_bytes_len); 100 | ExtDataBitsLen = (uint32_t)data_bits_len; 101 | } 102 | else 103 | { 104 | assert(data_bytes_len < sizeof(Data)); 105 | memcpy(Data, data, data_bytes_len); 106 | DataBitsLen = (uint32_t)data_bits_len; 107 | } 108 | } 109 | 110 | large_bunch::large_bunch(utcp_bunch* const bunches[], int count) 111 | { 112 | memcpy(this, bunches[0], sizeof(utcp_bunch)); 113 | ExtDataBitsLen = 0; 114 | if (count == 1) 115 | return; 116 | 117 | struct bitbuf buf; 118 | bitbuf_write_init(&buf, ExtData, sizeof(ExtData)); 119 | for (int i = 0; i < count; ++i) 120 | { 121 | auto bunch = bunches[i]; 122 | assert(bunch->bPartial); 123 | assert(bunch->bPartialInitial == ((i == 0) ? 1 : 0)); 124 | assert(bunch->bPartialFinal == ((i == (count - 1)) ? 1 : 0)); 125 | ExtDataBitsLen += bunch->DataBitsLen; 126 | bitbuf_write_bits(&buf, bunch->Data, bunch->DataBitsLen); 127 | } 128 | } 129 | 130 | large_bunch::large_bunch() 131 | { 132 | memset(this, 0, sizeof(*this)); 133 | } 134 | 135 | large_bunch::iterator large_bunch::begin() 136 | { 137 | large_bunch::iterator it; 138 | it.ref = this; 139 | it.pos = 0; 140 | return it; 141 | } 142 | 143 | large_bunch::iterator large_bunch::end() 144 | { 145 | large_bunch::iterator it; 146 | it.ref = this; 147 | it.pos = num(); 148 | return it; 149 | } 150 | 151 | utcp_bunch& large_bunch::sub_bunch(int pos) 152 | { 153 | if (ExtDataBitsLen == 0) 154 | { 155 | assert(pos == 0); 156 | return *this; 157 | } 158 | 159 | if (!bExtPartialSetFlag) 160 | { 161 | bExtPartialSetFlag = 1; 162 | bExtPartial = this->bPartial; 163 | bExtPartialInitial = this->bPartialInitial; 164 | bExtPartialFinal = this->bPartialFinal; 165 | } 166 | 167 | int last = ExtDataBitsLen / MAX_PARTIAL_BUNCH_SIZE_BITS; 168 | this->bPartial = last > 0; 169 | this->bPartialInitial = pos == 0; 170 | this->bPartialFinal = pos == last; 171 | 172 | if (bExtPartial) 173 | { 174 | if (this->bPartialInitial) 175 | this->bPartialInitial = bExtPartialInitial; 176 | if (this->bPartialFinal) 177 | this->bPartialFinal = bExtPartialFinal; 178 | } 179 | 180 | if (pos == last) 181 | { 182 | int offset = pos * MAX_SINGLE_BUNCH_SIZE_BYTES; 183 | int left = std::min(sizeof(this->ExtData) - offset, MAX_SINGLE_BUNCH_SIZE_BYTES); 184 | memcpy(this->Data, this->ExtData + offset, left); 185 | this->DataBitsLen = ExtDataBitsLen - MAX_PARTIAL_BUNCH_SIZE_BITS * pos; 186 | } 187 | else 188 | { 189 | int offset = pos * MAX_SINGLE_BUNCH_SIZE_BYTES; 190 | memcpy(this->Data, this->ExtData + offset, MAX_SINGLE_BUNCH_SIZE_BYTES); 191 | this->DataBitsLen = MAX_PARTIAL_BUNCH_SIZE_BITS; 192 | } 193 | return *this; 194 | } 195 | 196 | int large_bunch::num() 197 | { 198 | int cnt = 1; 199 | if (ExtDataBitsLen > 0) 200 | { 201 | auto bytes_len = (int)bits2bytes(ExtDataBitsLen); 202 | cnt = bytes_len / MAX_SINGLE_BUNCH_SIZE_BYTES + 1; 203 | } 204 | return cnt; 205 | } 206 | 207 | conn::conn() 208 | { 209 | _utcp_fd = utcp_connection_create(); 210 | utcp_init(_utcp_fd, this); 211 | } 212 | 213 | conn::~conn() 214 | { 215 | utcp_uninit(_utcp_fd); 216 | utcp_connection_destroy(_utcp_fd); 217 | } 218 | 219 | void conn::connect() 220 | { 221 | utcp_connect(_utcp_fd); 222 | } 223 | 224 | void conn::update() 225 | { 226 | utcp_update(_utcp_fd); 227 | } 228 | 229 | void conn::incoming(uint8_t* data, int count) 230 | { 231 | auto packet_id = utcp_peep_packet_id(_utcp_fd, data, count); 232 | if (packet_id <= 0) 233 | { 234 | utcp_incoming(_utcp_fd, data, count); 235 | return; 236 | } 237 | 238 | _packet_order_cache.emplace(packet_id, data, count); 239 | utcp_log(Verbose, "[%s]incoming _packet_order_cache:%d, count:%d, cache_size:%d", _utcp_fd->debug_name, packet_id, count, _packet_order_cache.size()); 240 | 241 | flush_packet_order_cache(false); 242 | } 243 | 244 | void conn::flush_incoming_cache() 245 | { 246 | flush_packet_order_cache(true); 247 | } 248 | 249 | packet_id_range conn::send_bunch(large_bunch* bunch) 250 | { 251 | packet_id_range range{packet_id_range::INDEX_NONE, packet_id_range::INDEX_NONE}; 252 | for (auto& sub : *bunch) 253 | { 254 | auto packet_id = utcp_send_bunch(_utcp_fd, &sub); 255 | if (range.first == packet_id_range::INDEX_NONE) 256 | range.first = packet_id; 257 | range.last = packet_id; 258 | } 259 | return range; 260 | } 261 | 262 | void conn::send_flush() 263 | { 264 | utcp_send_flush(_utcp_fd); 265 | } 266 | 267 | utcp_connection* conn::get_fd() 268 | { 269 | return _utcp_fd; 270 | } 271 | 272 | bool conn::is_closed() 273 | { 274 | return _utcp_fd->bClose; 275 | } 276 | 277 | void conn::flush_packet_order_cache(bool forced_flush) 278 | { 279 | while (!_packet_order_cache.empty()) 280 | { 281 | int packet_id = forced_flush ? -1 : utcp_expect_packet_id(_utcp_fd); 282 | auto& view = _packet_order_cache.top(); 283 | if (packet_id != -1 && view._packet_id > packet_id) 284 | break; 285 | 286 | utcp_incoming(_utcp_fd, const_cast(view._data), view._data_len); 287 | _packet_order_cache.pop(); 288 | } 289 | } 290 | 291 | void conn::set_debug_name(const char* debug_name) 292 | { 293 | if (debug_name) 294 | { 295 | strncpy(_utcp_fd->debug_name, debug_name, sizeof (_utcp_fd->debug_name)); 296 | _utcp_fd->debug_name[sizeof (_utcp_fd->debug_name)-1] = '\0'; 297 | } 298 | else 299 | { 300 | _utcp_fd->debug_name[0] = '\0'; 301 | } 302 | } 303 | const char* conn::debug_name() 304 | { 305 | return _utcp_fd->debug_name; 306 | } 307 | 308 | void bufconn::update() 309 | { 310 | try_send(); 311 | conn::update(); 312 | } 313 | 314 | utcp::packet_id_range bufconn::send_bunch(large_bunch* bunch) 315 | { 316 | try_send(); 317 | 318 | if (!_send_buffer.empty() || utcp_send_would_block(_utcp_fd, bunch->num())) 319 | { 320 | if (!bunch->bReliable) 321 | return {packet_id_range::INDEX_NONE, packet_id_range::INDEX_NONE}; 322 | } 323 | 324 | if (!_send_buffer.empty()) 325 | { 326 | for (auto& sub : *bunch) 327 | { 328 | _send_buffer.push_back(sub); 329 | } 330 | _send_buffer_packet_id--; 331 | return packet_id_range{_send_buffer_packet_id, _send_buffer_packet_id}; 332 | } 333 | 334 | packet_id_range range{packet_id_range::INDEX_NONE, packet_id_range::INDEX_NONE}; 335 | for (auto& sub : *bunch) 336 | { 337 | if (!utcp_send_would_block(_utcp_fd, 1)) 338 | { 339 | auto packet_id = utcp_send_bunch(_utcp_fd, &sub); 340 | if (range.first == packet_id_range::INDEX_NONE) 341 | range.first = packet_id; 342 | range.last = packet_id; 343 | } 344 | else 345 | { 346 | _send_buffer.push_back(sub); 347 | } 348 | } 349 | return range; 350 | } 351 | 352 | void bufconn::try_send() 353 | { 354 | while (!_send_buffer.empty()) 355 | { 356 | if (utcp_send_would_block(_utcp_fd, 1)) 357 | break; 358 | 359 | auto& bunch = _send_buffer.front(); 360 | utcp_send_bunch(_utcp_fd, &bunch); 361 | _send_buffer.pop_front(); 362 | } 363 | } 364 | 365 | listener::listener() 366 | { 367 | _utcp_fd = utcp_listener_create(); 368 | utcp_listener_init(_utcp_fd, this); 369 | } 370 | 371 | listener::~listener() 372 | { 373 | utcp_listener_destroy(_utcp_fd); 374 | } 375 | 376 | void listener::update_secret() 377 | { 378 | utcp_listener_update_secret(_utcp_fd, nullptr); 379 | } 380 | 381 | 382 | void listener::incoming(const char* address, uint8_t* data, int count) 383 | { 384 | utcp_listener_incoming(_utcp_fd, address, data, count); 385 | } 386 | 387 | void listener::accept(conn* c, bool reconnect) 388 | { 389 | utcp_listener_accept(_utcp_fd, c->get_fd(), reconnect); 390 | } 391 | 392 | // StatelessConnectHandlerComponent::DoesRestartedHandshakeMatch 393 | bool listener::does_restarted_handshake_match(conn* c) 394 | { 395 | return memcmp(_utcp_fd->AuthorisedCookie, c->get_fd()->AuthorisedCookie, sizeof(_utcp_fd->AuthorisedCookie)) == 0; 396 | } 397 | 398 | utcp_listener* listener::get_fd() 399 | { 400 | return _utcp_fd; 401 | } 402 | 403 | void listener::on_accept(bool reconnect) 404 | { 405 | if (reconnect) 406 | { 407 | // conn::same_auth_cookie(this->get_auth_cookie()); 408 | throw; 409 | } 410 | auto c = new conn; 411 | accept(c, reconnect); 412 | delete c; 413 | } 414 | } // namespace utcp 415 | -------------------------------------------------------------------------------- /abstract/utcp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "utcp/utcp.h" 3 | #include "utcp/utcp_def.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace utcp 11 | { 12 | // Fairly large number, and probably a bad idea to even have a bunch this size, but want to be safe for now and not throw out legitimate data 13 | constexpr int32_t NetMaxConstructedPartialBunchSizeBytes = 1024 * 64; 14 | constexpr int32_t MAX_SINGLE_BUNCH_SIZE_BITS = 7265; // Connection->GetMaxSingleBunchSizeBits(); 15 | constexpr int32_t MAX_SINGLE_BUNCH_SIZE_BYTES = MAX_SINGLE_BUNCH_SIZE_BITS / 8; 16 | constexpr int32_t MAX_PARTIAL_BUNCH_SIZE_BITS = MAX_SINGLE_BUNCH_SIZE_BYTES * 8; 17 | static_assert(UDP_MTU_SIZE > MAX_SINGLE_BUNCH_SIZE_BYTES); 18 | 19 | inline size_t bits2bytes(size_t bits_len) 20 | { 21 | return (bits_len + 7) >> 3; 22 | } 23 | 24 | class event_handler 25 | { 26 | public: 27 | static void add_elapsed_time(int64_t delta_time_ns); 28 | static void config(decltype(utcp_config::on_log) log_fn); 29 | static void enbale_dump_data(bool enable); 30 | 31 | virtual ~event_handler(); 32 | 33 | protected: 34 | virtual void on_accept(bool reconnect); 35 | virtual void on_connect(bool reconnect); 36 | virtual void on_disconnect(int close_reason); 37 | virtual void on_outgoing(const void* data, int len); 38 | virtual void on_recv_bunch(struct utcp_bunch* const bunches[], int count); 39 | virtual void on_delivery_status(int32_t packet_id, bool ack); 40 | }; 41 | 42 | struct packet_view 43 | { 44 | int32_t _packet_id; 45 | uint16_t _data_len; 46 | uint8_t _data[NetMaxConstructedPartialBunchSizeBytes]; 47 | 48 | packet_view(int32_t packet_id, uint8_t* data, int count) : _packet_id(packet_id), _data_len(count) 49 | { 50 | assert(count < sizeof(_data)); 51 | memcpy(_data, data, count); 52 | } 53 | 54 | friend bool operator<(const packet_view& l, const packet_view& r) 55 | { 56 | return l._packet_id > r._packet_id; 57 | } 58 | }; 59 | 60 | struct large_bunch : utcp_bunch 61 | { 62 | explicit large_bunch(const uint8_t* data, size_t data_bits_len); 63 | explicit large_bunch(utcp_bunch* const bunches[], int count); 64 | explicit large_bunch(); 65 | 66 | uint32_t ExtDataBitsLen : 28; 67 | uint32_t bExtPartialSetFlag : 1; 68 | uint32_t bExtPartial : 1; 69 | uint32_t bExtPartialInitial : 1; 70 | uint32_t bExtPartialFinal : 1; 71 | 72 | uint8_t ExtData[UDP_MTU_SIZE * 64]; 73 | 74 | #pragma region Range - based for loop 75 | struct iterator 76 | { 77 | large_bunch* ref; 78 | int pos; 79 | void operator++() 80 | { 81 | pos++; 82 | } 83 | bool operator!=(const iterator& rhs) 84 | { 85 | return pos != rhs.pos; 86 | } 87 | utcp_bunch& operator*() 88 | { 89 | return ref->sub_bunch(pos); 90 | } 91 | }; 92 | iterator begin(); 93 | iterator end(); 94 | utcp_bunch& sub_bunch(int pos); 95 | int num(); 96 | 97 | large_bunch(const large_bunch&) = delete; 98 | large_bunch(large_bunch&&) = delete; 99 | #pragma endregion 100 | }; 101 | 102 | struct packet_id_range 103 | { 104 | enum 105 | { 106 | INDEX_NONE = -1 107 | }; 108 | 109 | int32_t first; 110 | int32_t last; 111 | }; 112 | 113 | class conn : public event_handler 114 | { 115 | public: 116 | conn(); 117 | virtual ~conn() override; 118 | 119 | virtual void connect(); 120 | virtual void update(); 121 | virtual void incoming(uint8_t* data, int count); 122 | virtual void flush_incoming_cache(); 123 | virtual packet_id_range send_bunch(large_bunch* bunch); 124 | virtual void send_flush(); 125 | 126 | utcp_connection* get_fd(); 127 | bool is_closed(); 128 | void set_debug_name(const char* debug_name); 129 | const char* debug_name(); 130 | 131 | protected: 132 | void flush_packet_order_cache(bool forced_flush); 133 | 134 | protected: 135 | std::priority_queue _packet_order_cache; 136 | utcp_connection* _utcp_fd; 137 | }; 138 | 139 | class bufconn : public conn 140 | { 141 | public: 142 | virtual void update() override; 143 | virtual packet_id_range send_bunch(large_bunch* bunch) override; 144 | 145 | protected: 146 | void try_send(); 147 | 148 | std::list _send_buffer; 149 | int32_t _send_buffer_packet_id = packet_id_range::INDEX_NONE; 150 | }; 151 | 152 | class listener : public event_handler 153 | { 154 | public: 155 | listener(); 156 | virtual ~listener() override; 157 | 158 | void update_secret(); 159 | 160 | virtual void incoming(const char* address, uint8_t* data, int count); 161 | virtual void accept(conn* c, bool reconnect); 162 | virtual bool does_restarted_handshake_match(conn* c); 163 | 164 | utcp_listener* get_fd(); 165 | 166 | protected: 167 | virtual void on_accept(bool reconnect) override; 168 | 169 | protected: 170 | utcp_listener* _utcp_fd; 171 | }; 172 | } // namespace utcp 173 | -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | if not exist "build" ( 2 | mkdir build 3 | ) 4 | 5 | cd build 6 | cmake ../ -G"Visual Studio 17 2022" 7 | 8 | cd .. 9 | pause -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | #set -x 3 | set -e 4 | 5 | if [ ! -d build ];then 6 | mkdir build 7 | fi 8 | 9 | cd build 10 | 11 | cmake -DCMAKE_BUILD_TYPE=Debug .. 12 | 13 | make -j64 14 | 15 | cd - 16 | -------------------------------------------------------------------------------- /build_Xcode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | #set -x 3 | set -e 4 | 5 | if [ ! -d build ];then 6 | mkdir build 7 | fi 8 | 9 | cd build 10 | 11 | cmake -GXcode .. 12 | 13 | cd - 14 | -------------------------------------------------------------------------------- /sample/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | get_filename_component(CURRENT_SOURCE_DIR_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) 2 | set(APP_NAME ${CURRENT_SOURCE_DIR_NAME}) # 工程名, 默认文件夹名 3 | 4 | file(GLOB SOURCE_FILES 5 | *.h 6 | *.hpp 7 | *.cpp 8 | *.cc 9 | *.c 10 | ) 11 | 12 | if(WIN32) 13 | add_definitions(-D_WINSOCK_DEPRECATED_NO_WARNINGS) 14 | endif(WIN32) 15 | 16 | include_directories(${CMAKE_SOURCE_DIR}) 17 | 18 | add_executable(${APP_NAME} ${SOURCE_FILES}) 19 | target_link_libraries(${APP_NAME} abstract) 20 | source_group_by_dir(SOURCE_FILES) 21 | 22 | if(APPLE) 23 | target_link_libraries(${APP_NAME} "-framework CoreFoundation") 24 | endif(APPLE) 25 | 26 | if(WIN32) 27 | target_link_libraries(${APP_NAME} "ws2_32.lib") 28 | endif(WIN32) 29 | 30 | if(LINUX) 31 | target_link_libraries(${APP_NAME} "pthread") 32 | endif(LINUX) -------------------------------------------------------------------------------- /sample/ds_connection.cpp: -------------------------------------------------------------------------------- 1 | #include "ds_connection.h" 2 | #include "sample_config.h" 3 | extern "C" 4 | { 5 | #include "utcp/bit_buffer.h" 6 | } 7 | #include 8 | #include 9 | 10 | void ue_codec::reset(uint8_t* data, size_t len) 11 | { 12 | this->pos = data; 13 | this->end = data + len; 14 | } 15 | 16 | ue_codec& ue_codec::operator<<(const uint8_t value) 17 | { 18 | assert(pos + sizeof(value) < end); 19 | *pos = value; 20 | pos++; 21 | return *this; 22 | } 23 | 24 | ue_codec& ue_codec::operator<<(const uint32_t value) 25 | { 26 | assert(pos + sizeof(value) <= end); 27 | *((uint32_t*)pos) = value; 28 | pos += sizeof(value); 29 | return *this; 30 | } 31 | 32 | ue_codec& ue_codec::operator<<(const std::string& value) 33 | { 34 | auto size = (uint32_t)value.size(); 35 | *this << size; 36 | assert(pos + size <= end); 37 | memcpy(pos, value.c_str(), size); 38 | pos += size; 39 | return *this; 40 | } 41 | 42 | ue_codec& ue_codec::operator>>(uint8_t& value) 43 | { 44 | assert(pos + sizeof(value) < end); 45 | value = *pos; 46 | pos++; 47 | return *this; 48 | } 49 | 50 | ue_codec& ue_codec::operator>>(uint32_t& value) 51 | { 52 | assert(pos + sizeof(value) <= end); 53 | value = *((uint32_t*)pos); 54 | pos += sizeof(value); 55 | return *this; 56 | } 57 | 58 | ue_codec& ue_codec::operator>>(std::string& value) 59 | { 60 | uint32_t size; 61 | *this >> size; 62 | assert(pos + size <= end); 63 | value = std::string((char*)pos, size); 64 | pos += size; 65 | return *this; 66 | } 67 | 68 | void ds_connection::bind(socket_t fd, struct sockaddr_storage* addr, socklen_t addr_len) 69 | { 70 | memcpy(&dest_addr, addr, addr_len); 71 | dest_addr_len = addr_len; 72 | socket_fd = fd; 73 | set_debug_name("server"); 74 | } 75 | 76 | void ds_connection::update() 77 | { 78 | if (!disconnect) 79 | utcp::conn::update(); 80 | } 81 | 82 | void ds_connection::send_flush() 83 | { 84 | if (!disconnect) 85 | utcp::conn::send_flush(); 86 | } 87 | 88 | void ds_connection::on_disconnect(int close_reason) 89 | { 90 | disconnect = true; 91 | } 92 | 93 | void ds_connection::on_outgoing(const void* data, int len) 94 | { 95 | assert(dest_addr_len > 0); 96 | sendto(socket_fd, (const char*)data, len, 0, (sockaddr*)&dest_addr, dest_addr_len); 97 | } 98 | 99 | void ds_connection::on_recv_bunch(struct utcp_bunch* const bunches[], int count) 100 | { 101 | assert(count == 1); 102 | if (bunches[0]->DataBitsLen == 0) 103 | { 104 | assert(bunches[0]->bClose); 105 | log(log_level::Warning, "channel close"); 106 | return; 107 | } 108 | 109 | codec.reset(const_cast(bunches[0]->Data), bunches[0]->DataBitsLen / 8); 110 | 111 | uint8_t msg_type; 112 | codec >> msg_type; 113 | 114 | log(log_level::Verbose, "on_recv msg_type=%hhd\n", msg_type); 115 | switch (msg_type) 116 | { 117 | case 0: 118 | on_msg_hello(); 119 | break; 120 | 121 | case 4: 122 | on_msg_netspeed(); 123 | break; 124 | case 5: 125 | on_msg_login(); 126 | break; 127 | 128 | default: 129 | log(log_level::Warning, "on_recv unknown msg_type=%hhd\n", msg_type); 130 | } 131 | } 132 | 133 | void ds_connection::on_delivery_status(int32_t packet_id, bool ack) 134 | { 135 | log(log_level::Verbose, "on_delivery_status:%d %s\n", packet_id, ack ? "ACK" : "NAK"); 136 | } 137 | 138 | void ds_connection::send_data() 139 | { 140 | auto len = uint16_t(codec.pos - send_buffer); 141 | utcp::large_bunch bunch(send_buffer, len * 8); 142 | bunch.NameIndex = 255; 143 | bunch.ChIndex = 0; 144 | bunch.bReliable = 1; 145 | bunch.ExtDataBitsLen = 0; 146 | 147 | auto ret = send_bunch(&bunch); 148 | log(log_level::Verbose, "send bunch %d\n", ret.first); 149 | send_flush(); 150 | } 151 | 152 | // DEFINE_CONTROL_CHANNEL_MESSAGE(Hello, 0, uint8, uint32, FString); // initial client connection message 153 | // DEFINE_CONTROL_CHANNEL_MESSAGE(Challenge, 3, FString); // server sends client challenge string to verify integrity 154 | void ds_connection::on_msg_hello() 155 | { 156 | uint8_t IsLittleEndian; 157 | uint32_t RemoteNetworkVersion; 158 | std::string EncryptionToken; 159 | 160 | codec >> IsLittleEndian >> RemoteNetworkVersion >> EncryptionToken; 161 | 162 | assert(IsLittleEndian); 163 | if (EncryptionToken.size() == 0) 164 | { 165 | challenge = std::to_string((uint64_t)this); 166 | 167 | codec.reset(send_buffer, sizeof(send_buffer)); 168 | codec << (uint8_t)3 << challenge; 169 | send_data(); 170 | } 171 | } 172 | 173 | // DEFINE_CONTROL_CHANNEL_MESSAGE(Login, 5, FString, FString, FUniqueNetIdRepl, FString); // client requests to be admitted to the game 174 | // DEFINE_CONTROL_CHANNEL_MESSAGE(Welcome, 1, FString, FString, FString); // server tells client they're ok'ed to load the server's level 175 | void ds_connection::on_msg_login() 176 | { 177 | std::string ClientResponse; 178 | std::string RequestURL; 179 | 180 | codec >> ClientResponse >> RequestURL; 181 | 182 | codec.reset(send_buffer, sizeof(send_buffer)); 183 | codec << (uint8_t)1 << std::string("/Game/ThirdPerson/Maps/UEDPIE_0_ThirdPersonMap") << std::string("/Script/Example.ExampleGameMode") << std::string(); 184 | send_data(); 185 | } 186 | 187 | // DEFINE_CONTROL_CHANNEL_MESSAGE(Netspeed, 4, int32); // client sends requested transfer rate 188 | void ds_connection::on_msg_netspeed() 189 | { 190 | uint32_t Rate; 191 | codec >> Rate; 192 | } 193 | -------------------------------------------------------------------------------- /sample/ds_connection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "abstract/utcp.hpp" 3 | #include "socket.h" 4 | #include 5 | #include 6 | #include 7 | 8 | struct ue_codec 9 | { 10 | void reset(uint8_t* data, size_t len); 11 | 12 | ue_codec& operator<<(const uint8_t value); 13 | ue_codec& operator>>(uint8_t& value); 14 | 15 | ue_codec& operator<<(const uint32_t value); 16 | ue_codec& operator>>(uint32_t& value); 17 | 18 | ue_codec& operator<<(const std::string& value); 19 | ue_codec& operator>>(std::string& value); 20 | 21 | uint8_t* pos; 22 | uint8_t* end; 23 | }; 24 | 25 | class ds_connection : public utcp::conn 26 | { 27 | public: 28 | void bind(socket_t fd, struct sockaddr_storage* addr, socklen_t addr_len); 29 | virtual void update() override; 30 | virtual void send_flush() override; 31 | 32 | protected: 33 | virtual void on_disconnect(int close_reason) override; 34 | virtual void on_outgoing(const void* data, int len) override; 35 | virtual void on_recv_bunch(struct utcp_bunch* const bunches[], int count) override; 36 | virtual void on_delivery_status(int32_t packet_id, bool ack) override; 37 | 38 | private: 39 | void send_data(); 40 | void on_msg_hello(); 41 | void on_msg_login(); 42 | void on_msg_netspeed(); 43 | 44 | private: 45 | std::string challenge; 46 | ue_codec codec; 47 | socket_t socket_fd = INVALID_SOCKET; 48 | struct sockaddr_storage dest_addr; 49 | socklen_t dest_addr_len = 0; 50 | uint8_t send_buffer[UDP_MTU_SIZE]; 51 | bool disconnect = false; 52 | }; 53 | -------------------------------------------------------------------------------- /sample/echo_connection.cpp: -------------------------------------------------------------------------------- 1 | #include "echo_connection.h" 2 | #include "sample_config.h" 3 | #include 4 | #include 5 | 6 | void echo_connection::bind(socket_t fd, struct sockaddr_storage* addr, socklen_t addr_len) 7 | { 8 | memcpy(&socket.dest_addr, addr, addr_len); 9 | socket.dest_addr_len = addr_len; 10 | socket.socket_fd = fd; 11 | set_debug_name("server"); 12 | } 13 | 14 | bool echo_connection::async_connnect(const char* ip, int port) 15 | { 16 | if (!socket.connnect(ip, port)) 17 | return false; 18 | 19 | connect(); 20 | set_debug_name("client"); 21 | return true; 22 | } 23 | 24 | void echo_connection::send(int num) 25 | { 26 | utcp::large_bunch bunch((uint8_t*)&num, sizeof(num) * 8); 27 | bunch.NameIndex = 255; 28 | bunch.ChIndex = 0; 29 | bunch.bOpen = 1; 30 | bunch.bReliable = 1; 31 | bunch.ExtDataBitsLen = 0; 32 | bunch.bPartial = 1; 33 | bunch.bPartialInitial = 1; 34 | 35 | auto ret = send_bunch(&bunch); 36 | log(log_level::Log, "[%s]send%d\t%d", this->debug_name(), ret.first, num); 37 | send_flush(); 38 | 39 | bunch.bPartialInitial = 0; 40 | ret = send_bunch(&bunch); 41 | send_flush(); 42 | 43 | bunch.bPartialFinal = 1; 44 | ret = send_bunch(&bunch); 45 | send_flush(); 46 | } 47 | 48 | void echo_connection::update() 49 | { 50 | proc_recv_queue(); 51 | utcp::conn::update(); 52 | } 53 | 54 | void echo_connection::on_connect(bool reconnect) 55 | { 56 | send(1); 57 | } 58 | 59 | void echo_connection::on_disconnect(int close_reason) 60 | { 61 | } 62 | 63 | void echo_connection::on_outgoing(const void* data, int len) 64 | { 65 | int loss = rand() % 100; 66 | if (g_config->outgoing_loss > 0 && loss < g_config->outgoing_loss) 67 | { 68 | log(log_level::Log, "[%s]out loss", this->debug_name()); 69 | return; 70 | } 71 | 72 | assert(socket.dest_addr_len > 0); 73 | sendto(socket.socket_fd, (const char*)data, len, 0, (sockaddr*)&socket.dest_addr, socket.dest_addr_len); 74 | } 75 | 76 | void echo_connection::on_recv_bunch(struct utcp_bunch* const bunches[], int count) 77 | { 78 | int num; 79 | 80 | assert(count == 3); 81 | assert(bunches[0]->DataBitsLen == sizeof(num) * 8); 82 | memcpy(&num, bunches[0]->Data, sizeof(num)); 83 | send(num + 1); 84 | } 85 | 86 | void echo_connection::on_delivery_status(int32_t packet_id, bool ack) 87 | { 88 | log(log_level::Log, "[%s]delivery %d %s", this->debug_name(), packet_id, ack ? "ACK" : "NAK"); 89 | } 90 | 91 | void echo_connection::proc_recv_queue() 92 | { 93 | auto& proc_queue = socket.swap(); 94 | 95 | for (auto& datagram : proc_queue) 96 | { 97 | incoming(datagram.data, datagram.data_len); 98 | } 99 | 100 | proc_queue.clear(); 101 | } 102 | -------------------------------------------------------------------------------- /sample/echo_connection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "abstract/utcp.hpp" 3 | #include "udp_socket.h" 4 | #include 5 | #include 6 | #include 7 | 8 | class echo_connection : public utcp::conn 9 | { 10 | public: 11 | void bind(socket_t fd, struct sockaddr_storage* addr, socklen_t addr_len); 12 | bool async_connnect(const char* ip, int port); 13 | void send(int num); 14 | 15 | virtual void update() override; 16 | 17 | protected: 18 | virtual void on_connect(bool reconnect) override; 19 | virtual void on_disconnect(int close_reason) override; 20 | virtual void on_outgoing(const void* data, int len) override; 21 | virtual void on_recv_bunch(struct utcp_bunch* const bunches[], int count) override; 22 | virtual void on_delivery_status(int32_t packet_id, bool ack) override; 23 | 24 | void proc_recv_queue(); 25 | 26 | private: 27 | udp_socket socket; 28 | }; 29 | -------------------------------------------------------------------------------- /sample/main.cpp: -------------------------------------------------------------------------------- 1 | #include "ds_connection.h" 2 | #include "echo_connection.h" 3 | #include "sample_config.h" 4 | #include "utcp_listener.h" 5 | #include 6 | #include 7 | #include 8 | 9 | sample_config* g_config = nullptr; 10 | 11 | void config() 12 | { 13 | static sample_config config; 14 | g_config = &config; 15 | 16 | g_config->log_level_limit = log_level::Verbose; 17 | g_config->outgoing_loss = 0; 18 | } 19 | 20 | static void vlog(int level, const char* fmt, va_list marker) 21 | { 22 | if (level > (int)g_config->log_level_limit) 23 | return; 24 | 25 | static FILE* fd = nullptr; 26 | if (!fd) 27 | { 28 | fd = fopen("sample_server.log", "a+"); 29 | } 30 | 31 | if (fd) 32 | { 33 | vfprintf(fd, fmt, marker); 34 | fprintf(fd, "\n"); 35 | fflush(fd); 36 | } 37 | vfprintf(stdout, fmt, marker); 38 | fprintf(stdout, "\n"); 39 | } 40 | 41 | void log(log_level level, const char* fmt, ...) 42 | { 43 | va_list marker; 44 | va_start(marker, fmt); 45 | vlog((int)level, fmt, marker); 46 | va_end(marker); 47 | } 48 | 49 | struct sample_loop 50 | { 51 | std::chrono::time_point now; 52 | int64_t frame = 0; 53 | 54 | sample_loop() 55 | { 56 | now = std::chrono::high_resolution_clock::now(); 57 | } 58 | 59 | void tick() 60 | { 61 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); 62 | 63 | auto cur_now = std::chrono::high_resolution_clock::now(); 64 | utcp::event_handler::add_elapsed_time((cur_now - now).count()); 65 | now = cur_now; 66 | frame++; 67 | } 68 | 69 | bool need_post_tick() 70 | { 71 | return frame % 10 == 0; 72 | } 73 | }; 74 | 75 | void ds() 76 | { 77 | std::unique_ptr listener(new udp_utcp_listener_impl); 78 | auto now = std::chrono::high_resolution_clock::now(); 79 | sample_loop loop; 80 | 81 | listener->listen("127.0.0.1", 7777); 82 | 83 | while (true) 84 | { 85 | loop.tick(); 86 | listener->tick(); 87 | if (loop.need_post_tick()) 88 | { 89 | listener->post_tick(); 90 | } 91 | } 92 | } 93 | 94 | void echo() 95 | { 96 | std::unique_ptr listener(new udp_utcp_listener_impl); 97 | std::unique_ptr client(new echo_connection); 98 | sample_loop loop; 99 | 100 | listener->listen("127.0.0.1", 8241); 101 | client->async_connnect("127.0.0.1", 8241); 102 | 103 | while (loop.frame < 5000) 104 | { 105 | loop.tick(); 106 | 107 | listener->tick(); 108 | client->update(); 109 | 110 | if (loop.need_post_tick()) 111 | { 112 | listener->post_tick(); 113 | client->flush_incoming_cache(); 114 | client->send_flush(); 115 | log(log_level::Verbose, "post_tick"); 116 | } 117 | } 118 | } 119 | 120 | int main(int argc, const char* argv[]) 121 | { 122 | config(); 123 | log(log_level::Log, "server start"); 124 | 125 | utcp::event_handler::config(vlog); 126 | utcp::event_handler::enbale_dump_data(g_config->log_level_limit >= log_level::Verbose); 127 | 128 | ds(); 129 | // echo(); 130 | 131 | log(log_level::Log, "server stop"); 132 | return 0; 133 | } 134 | -------------------------------------------------------------------------------- /sample/sample_config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum class log_level 4 | { 5 | NoLogging = 0, 6 | Fatal, 7 | Error, 8 | Warning, 9 | Display, 10 | Log, 11 | Verbose, 12 | }; 13 | void log(log_level level, const char* fmt, ...); 14 | 15 | struct sample_config 16 | { 17 | log_level log_level_limit; 18 | int outgoing_loss; 19 | }; 20 | 21 | extern sample_config* g_config; 22 | -------------------------------------------------------------------------------- /sample/socket.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef WIN32 4 | #include 5 | #include 6 | #include 7 | 8 | using socket_t = SOCKET; 9 | inline int get_socket_error() 10 | { 11 | return WSAGetLastError(); 12 | } 13 | using ssize_t = int; 14 | #endif 15 | 16 | #if defined(__linux) || defined(__APPLE__) 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | using socket_t = int; 26 | constexpr socket_t INVALID_SOCKET = -1; 27 | constexpr int SOCKET_ERROR = -1; 28 | 29 | inline int get_socket_error() 30 | { 31 | return errno; 32 | } 33 | inline void closesocket(socket_t fd) 34 | { 35 | close(fd); 36 | } 37 | #endif -------------------------------------------------------------------------------- /sample/udp_socket.cpp: -------------------------------------------------------------------------------- 1 | #include "udp_socket.h" 2 | #include 3 | 4 | #ifdef _MSC_VER 5 | struct WSAGuard 6 | { 7 | WSAGuard() 8 | { 9 | WSADATA wsadata; 10 | WSAStartup(MAKEWORD(2, 2), &wsadata); 11 | } 12 | ~WSAGuard() 13 | { 14 | WSACleanup(); 15 | } 16 | }; 17 | static WSAGuard _WSAGuard; 18 | #endif 19 | 20 | udp_socket::udp_socket() 21 | { 22 | recv_queue.reserve(128); 23 | proc_queue.reserve(128); 24 | } 25 | 26 | udp_socket::~udp_socket() 27 | { 28 | recv_thread_exit_flag = true; 29 | recv_thread.join(); 30 | } 31 | 32 | bool udp_socket::listen(const char* ip, int port) 33 | { 34 | assert(socket_fd == INVALID_SOCKET); 35 | 36 | socket_fd = socket(AF_INET, SOCK_DGRAM, 0); 37 | if (socket_fd == INVALID_SOCKET) 38 | return false; 39 | 40 | int one = 1; 41 | if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) == SOCKET_ERROR) 42 | return false; 43 | 44 | memset(&dest_addr, 0, sizeof(dest_addr)); 45 | struct sockaddr_in* addripv4 = (struct sockaddr_in*)&dest_addr; 46 | addripv4->sin_family = AF_INET; 47 | addripv4->sin_addr.s_addr = inet_addr(ip); 48 | addripv4->sin_port = htons(port); 49 | dest_addr_len = sizeof(*addripv4); 50 | 51 | if (bind(socket_fd, (struct sockaddr*)&dest_addr, dest_addr_len) == SOCKET_ERROR) 52 | return false; 53 | 54 | memset(&dest_addr, 0, sizeof(dest_addr)); 55 | dest_addr_len = 0; 56 | 57 | create_recv_thread(); 58 | return true; 59 | } 60 | 61 | bool udp_socket::connnect(const char* ip, int port) 62 | { 63 | assert(socket_fd == INVALID_SOCKET); 64 | 65 | socket_fd = socket(AF_INET, SOCK_DGRAM, 0); 66 | if (socket_fd == INVALID_SOCKET) 67 | return false; 68 | 69 | memset(&dest_addr, 0, sizeof(dest_addr)); 70 | struct sockaddr_in* addripv4 = (struct sockaddr_in*)&dest_addr; 71 | addripv4->sin_family = AF_INET; 72 | addripv4->sin_addr.s_addr = inet_addr(ip); 73 | addripv4->sin_port = htons(port); 74 | dest_addr_len = sizeof(*addripv4); 75 | 76 | create_recv_thread(); 77 | return true; 78 | } 79 | 80 | std::vector& udp_socket::swap() 81 | { 82 | std::lock_guard lock(recv_queue_mutex); 83 | recv_queue.swap(proc_queue); 84 | return proc_queue; 85 | } 86 | 87 | void udp_socket::create_recv_thread() 88 | { 89 | recv_thread_exit_flag = false; 90 | recv_thread = std::thread([this]() { 91 | struct sockaddr_storage from_addr; 92 | uint8_t buffer[UDP_DATAGRAM_SIZE]; 93 | 94 | while (!this->recv_thread_exit_flag) 95 | { 96 | socklen_t addr_len = sizeof(from_addr); 97 | ssize_t ret = ::recvfrom(socket_fd, (char*)buffer, sizeof(buffer), 0, (struct sockaddr*)&from_addr, &addr_len); 98 | if (ret <= 0) 99 | continue; 100 | proc_recv(buffer, (int)ret, &from_addr, addr_len); 101 | } 102 | }); 103 | } 104 | 105 | void udp_socket::proc_recv(uint8_t* data, int data_len, struct sockaddr_storage* from_addr, socklen_t from_addr_len) 106 | { 107 | std::lock_guard lock(recv_queue_mutex); 108 | recv_queue.emplace_back(data, data_len, from_addr, from_addr_len); 109 | } 110 | -------------------------------------------------------------------------------- /sample/udp_socket.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "socket.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | constexpr int UDP_DATAGRAM_SIZE = 2000; 9 | 10 | struct udp_datagram 11 | { 12 | struct sockaddr_storage from_addr; 13 | socklen_t from_addr_len; 14 | uint16_t data_len; 15 | uint8_t data[UDP_DATAGRAM_SIZE]; 16 | 17 | udp_datagram(uint8_t* data, int data_len, struct sockaddr_storage* from_addr, socklen_t from_addr_len) 18 | { 19 | memcpy(this->data, data, data_len); 20 | this->data_len = data_len; 21 | memcpy(&this->from_addr, from_addr, from_addr_len); 22 | this->from_addr_len = from_addr_len; 23 | } 24 | }; 25 | 26 | struct udp_socket 27 | { 28 | udp_socket(); 29 | virtual ~udp_socket(); 30 | 31 | bool listen(const char* ip, int port); 32 | bool connnect(const char* ip, int port); 33 | 34 | std::vector& swap(); 35 | 36 | volatile int recv_thread_exit_flag = false; 37 | std::thread recv_thread; 38 | 39 | std::mutex recv_queue_mutex; 40 | std::vector recv_queue; 41 | std::vector proc_queue; 42 | 43 | socket_t socket_fd = INVALID_SOCKET; 44 | struct sockaddr_storage dest_addr; 45 | socklen_t dest_addr_len = 0; 46 | 47 | private: 48 | void create_recv_thread(); 49 | void proc_recv(uint8_t* data, int data_len, struct sockaddr_storage* from_addr, socklen_t from_addr_len); 50 | }; -------------------------------------------------------------------------------- /sample/utcp_listener.cpp: -------------------------------------------------------------------------------- 1 | #include "utcp_listener.h" 2 | #include 3 | #include 4 | 5 | static bool sockaddr2str(sockaddr_in* addr, char ipstr[], int size) 6 | { 7 | if (!inet_ntop(addr->sin_family, &addr->sin_addr, ipstr, size)) 8 | return false; 9 | auto len = strlen(ipstr); 10 | auto port = ntohs(addr->sin_port); 11 | snprintf(ipstr + len, size - len, ":%d", port); 12 | return true; 13 | } 14 | 15 | udp_utcp_listener::udp_utcp_listener() 16 | { 17 | } 18 | 19 | udp_utcp_listener::~udp_utcp_listener() 20 | { 21 | for (auto& it : clients) 22 | { 23 | delete it.second; 24 | } 25 | } 26 | 27 | bool udp_utcp_listener::listen(const char* ip, int port) 28 | { 29 | return socket.listen(ip, port); 30 | } 31 | 32 | void udp_utcp_listener::tick() 33 | { 34 | proc_recv_queue(); 35 | for (auto& it : clients) 36 | { 37 | it.second->update(); 38 | } 39 | } 40 | 41 | void udp_utcp_listener::post_tick() 42 | { 43 | proc_recv_queue(); 44 | for (auto& it : clients) 45 | { 46 | it.second->flush_incoming_cache(); 47 | it.second->send_flush(); 48 | } 49 | } 50 | 51 | void udp_utcp_listener::on_accept(bool reconnect) 52 | { 53 | utcp::conn* conn = nullptr; 54 | if (!reconnect) 55 | { 56 | conn = new_conn(); 57 | } 58 | else 59 | { 60 | for (auto it = clients.begin(); it != clients.end(); ++it) 61 | { 62 | if (does_restarted_handshake_match(it->second)) 63 | { 64 | conn = it->second; 65 | clients.erase(it); 66 | break; 67 | } 68 | } 69 | 70 | if (!conn) 71 | return; 72 | } 73 | 74 | auto it = clients.insert(std::make_pair(*(sockaddr_in*)&socket.dest_addr, conn)); 75 | assert(it.second); 76 | 77 | accept(conn, reconnect); 78 | } 79 | 80 | void udp_utcp_listener::on_outgoing(const void* data, int len) 81 | { 82 | assert(socket.dest_addr_len > 0); 83 | sendto(socket.socket_fd, (const char*)data, len, 0, (sockaddr*)&socket.dest_addr, socket.dest_addr_len); 84 | } 85 | 86 | void udp_utcp_listener::proc_recv_queue() 87 | { 88 | auto& proc_queue = socket.swap(); 89 | char ipstr[INET6_ADDRSTRLEN + 8]; 90 | 91 | for (auto& datagram : proc_queue) 92 | { 93 | assert(datagram.from_addr_len == sizeof(sockaddr_in)); 94 | auto it = clients.find(*(sockaddr_in*)&datagram.from_addr); 95 | if (it != clients.end()) 96 | { 97 | it->second->incoming(datagram.data, datagram.data_len); 98 | continue; 99 | } 100 | 101 | if (sockaddr2str((sockaddr_in*)&datagram.from_addr, ipstr, sizeof(ipstr))) 102 | { 103 | socket.dest_addr_len = datagram.from_addr_len; 104 | memcpy(&socket.dest_addr, &datagram.from_addr, socket.dest_addr_len); 105 | incoming(ipstr, datagram.data, datagram.data_len); 106 | } 107 | socket.dest_addr_len = 0; 108 | } 109 | 110 | proc_queue.clear(); 111 | } 112 | -------------------------------------------------------------------------------- /sample/utcp_listener.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "abstract/utcp.hpp" 3 | #include "udp_socket.h" 4 | #include 5 | #include 6 | 7 | struct sockaddr_in_Hash 8 | { 9 | std::size_t operator()(const sockaddr_in& in4) const 10 | { 11 | size_t out = in4.sin_port; 12 | out = out << 32; 13 | out += in4.sin_addr.s_addr; 14 | return out; 15 | } 16 | }; 17 | 18 | struct sockaddr_in_Equal 19 | { 20 | bool operator()(const sockaddr_in& lhs, const sockaddr_in& rhs) const 21 | { 22 | return lhs.sin_addr.s_addr == rhs.sin_addr.s_addr && lhs.sin_port == rhs.sin_port; 23 | } 24 | }; 25 | 26 | class udp_utcp_listener : public utcp::listener 27 | { 28 | public: 29 | udp_utcp_listener(); 30 | ~udp_utcp_listener(); 31 | 32 | bool listen(const char* ip, int port); 33 | 34 | void tick(); 35 | void post_tick(); 36 | 37 | protected: 38 | virtual void on_accept(bool reconnect) override; 39 | virtual void on_outgoing(const void* data, int len) override; 40 | virtual utcp::conn* new_conn() = 0; 41 | void proc_recv_queue(); 42 | 43 | protected: 44 | udp_socket socket; 45 | std::unordered_map clients; 46 | }; 47 | 48 | template class udp_utcp_listener_impl : public udp_utcp_listener 49 | { 50 | protected: 51 | virtual utcp::conn* new_conn() override 52 | { 53 | return new T; 54 | } 55 | 56 | virtual void accept(utcp::conn* c, bool reconnect) override 57 | { 58 | static_cast(c)->bind(socket.socket_fd, &socket.dest_addr, socket.dest_addr_len); 59 | udp_utcp_listener::accept(c, reconnect); 60 | } 61 | }; 62 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(GoogleTest) 2 | enable_testing() 3 | 4 | # googletest 5 | set(gtest_force_shared_crt ON CACHE BOOL "") 6 | add_subdirectory(googletest) 7 | set(GTEST_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/googletest/googletest/include) 8 | set(GTEST_LIBRARIES gtest gtest_main) 9 | set_property(TARGET gtest gtest_main gmock gmock_main PROPERTY FOLDER "googletest") 10 | 11 | add_subdirectory(test_case) -------------------------------------------------------------------------------- /test/test_case/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | get_filename_component(CURRENT_SOURCE_DIR_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) 2 | set(LIB_NAME ${CURRENT_SOURCE_DIR_NAME}) 3 | 4 | file(GLOB_RECURSE SOURCE_FILES 5 | *.h 6 | *.hpp 7 | *.cpp 8 | *.cc 9 | *.c 10 | ) 11 | 12 | include_directories(${GTEST_INCLUDE_DIRS}) 13 | 14 | include_directories(${CMAKE_SOURCE_DIR}) 15 | add_executable(${LIB_NAME} ${SOURCE_FILES}) 16 | target_link_libraries(${LIB_NAME} abstract) 17 | target_link_libraries(${LIB_NAME} ${GTEST_LIBRARIES}) 18 | gtest_add_tests(TARGET ${LIB_NAME} TEST_LIST utcp_test_suite) 19 | set_tests_properties(${utcp_test_suite} PROPERTIES RUN_SERIAL TRUE) -------------------------------------------------------------------------------- /test/test_case/bunch_test.cpp: -------------------------------------------------------------------------------- 1 | #include "utcp/utcp.h" 2 | extern "C" 3 | { 4 | #include "utcp/utcp_bunch.h" 5 | } 6 | #include "gtest/gtest.h" 7 | #include 8 | 9 | TEST(bunch, read_write) 10 | { 11 | struct utcp_bunch utcp_bunch1; 12 | memset(&utcp_bunch1, 0, sizeof(utcp_bunch1)); 13 | utcp_bunch1.NameIndex = 0; 14 | utcp_bunch1.ChSequence = 0; 15 | utcp_bunch1.ChIndex = 543; 16 | utcp_bunch1.DataBitsLen = 789; 17 | utcp_bunch1.bOpen = 0; 18 | utcp_bunch1.bClose = 1; 19 | utcp_bunch1.CloseReason = 9; 20 | utcp_bunch1.bIsReplicationPaused = 1; 21 | utcp_bunch1.bReliable = 0; 22 | utcp_bunch1.bHasPackageMapExports = 0; 23 | utcp_bunch1.bHasMustBeMappedGUIDs = 1; 24 | utcp_bunch1.bPartial = 1; 25 | utcp_bunch1.bPartialInitial = 0; 26 | utcp_bunch1.bPartialFinal = 0; 27 | 28 | uint8_t buffer[UDP_MTU_SIZE]; 29 | struct bitbuf bitbuf1; 30 | 31 | ASSERT_TRUE(bitbuf_write_init(&bitbuf1, buffer, sizeof(buffer))); 32 | ASSERT_TRUE(utcp_bunch_write_header(&utcp_bunch1, &bitbuf1)); 33 | ASSERT_TRUE(bitbuf_write_bits(&bitbuf1, utcp_bunch1.Data, utcp_bunch1.DataBitsLen)); 34 | bitbuf_write_end(&bitbuf1); 35 | 36 | struct utcp_bunch utcp_bunch2; 37 | memset(&utcp_bunch2, 0, sizeof(utcp_bunch2)); 38 | 39 | struct bitbuf bitbuf2; 40 | ASSERT_TRUE(bitbuf_read_init(&bitbuf2, buffer, bitbuf_num_bytes(&bitbuf1))); 41 | ASSERT_EQ(bitbuf1.num - 1, bitbuf2.size); 42 | ASSERT_TRUE(utcp_bunch_read(&utcp_bunch2, &bitbuf2)); 43 | ASSERT_EQ(0, memcmp(&utcp_bunch1, &utcp_bunch2, sizeof(utcp_bunch1))); 44 | } 45 | -------------------------------------------------------------------------------- /test/test_case/channel_test.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.h" 2 | #include "gtest/gtest.h" 3 | #include 4 | 5 | void reset_nodes(utcp_bunch_node_raii nodes[], size_t size) 6 | { 7 | for (int i = 0; i < size; ++i) 8 | { 9 | nodes[i].reset(); 10 | } 11 | } 12 | 13 | TEST(channel, same_incoming) 14 | { 15 | utcp_channel_rtti channel; 16 | utcp_bunch_node_raii nodes[1]; 17 | 18 | for (int i = 0; i < std::size(nodes); ++i) 19 | { 20 | auto node = &nodes[i]; 21 | node->utcp_bunch.ChSequence = i; 22 | node->utcp_bunch.bReliable = true; 23 | } 24 | 25 | ASSERT_TRUE(enqueue_incoming_data(&channel, &nodes[0])); 26 | ASSERT_FALSE(enqueue_incoming_data(&channel, &nodes[0])); 27 | 28 | for (int i = 0; i < std::size(nodes); ++i) 29 | { 30 | auto node = &nodes[i]; 31 | ASSERT_EQ(dequeue_incoming_data(&channel, i), node); 32 | } 33 | ASSERT_EQ((&channel)->NumInRec, 0); 34 | } 35 | 36 | TEST(channel, free_incoming) 37 | { 38 | utcp_channel_rtti channel; 39 | utcp_bunch_node_raii nodes[1]; 40 | 41 | for (int i = 0; i < std::size(nodes); ++i) 42 | { 43 | auto node = &nodes[i]; 44 | node->utcp_bunch.ChSequence = i; 45 | node->utcp_bunch.bReliable = true; 46 | } 47 | 48 | ASSERT_TRUE(enqueue_incoming_data(&channel, &nodes[0])); 49 | ASSERT_FALSE(enqueue_incoming_data(&channel, &nodes[0])); 50 | 51 | reset_nodes(nodes, std::size(nodes)); 52 | ASSERT_EQ((&channel)->NumInRec, 1); 53 | } 54 | 55 | TEST(channel, order_incoming) 56 | { 57 | utcp_channel_rtti channel; 58 | utcp_bunch_node_raii nodes[4]; 59 | for (int i = 0; i < std::size(nodes); ++i) 60 | { 61 | auto node = &nodes[i]; 62 | node->utcp_bunch.ChSequence = i; 63 | node->utcp_bunch.bReliable = true; 64 | } 65 | 66 | ASSERT_TRUE(enqueue_incoming_data(&channel, &nodes[2])); 67 | ASSERT_TRUE(enqueue_incoming_data(&channel, &nodes[0])); 68 | ASSERT_TRUE(enqueue_incoming_data(&channel, &nodes[1])); 69 | ASSERT_TRUE(enqueue_incoming_data(&channel, &nodes[3])); 70 | 71 | for (int i = 0; i < std::size(nodes); ++i) 72 | { 73 | auto node = &nodes[i]; 74 | ASSERT_EQ(dequeue_incoming_data(&channel, i), node); 75 | } 76 | ASSERT_EQ((&channel)->NumInRec, 0); 77 | } 78 | 79 | TEST(channel, ougoing) 80 | { 81 | utcp_channel_rtti channel; 82 | utcp_bunch_node_raii nodes[4]; 83 | for (int i = 0; i < std::size(nodes); ++i) 84 | { 85 | auto node = &nodes[i]; 86 | node->packet_id = i < 2 ? 1 : 2; 87 | 88 | add_ougoing_data(&channel, &nodes[i]); 89 | ASSERT_EQ((&channel)->NumOutRec, i + 1); 90 | } 91 | struct utcp_bunch_node* bunch_node[UTCP_RELIABLE_BUFFER]; 92 | int bunch_node_size; 93 | 94 | bunch_node_size = remove_ougoing_data(&channel, 1, bunch_node, (int)std::size(bunch_node)); 95 | ASSERT_EQ(bunch_node_size, 2); 96 | ASSERT_EQ((&channel)->NumOutRec, 2); 97 | 98 | bunch_node_size = remove_ougoing_data(&channel, 2, bunch_node, (int)std::size(bunch_node)); 99 | ASSERT_EQ(bunch_node_size, 2); 100 | ASSERT_EQ((&channel)->NumOutRec, 0); 101 | } 102 | 103 | TEST(channel, free_ougoing) 104 | { 105 | utcp_channel_rtti channel; 106 | utcp_bunch_node_raii nodes[1]; 107 | 108 | for (int i = 0; i < std::size(nodes); ++i) 109 | { 110 | auto node = &nodes[i]; 111 | node->packet_id = i < 2 ? 1 : 2; 112 | add_ougoing_data(&channel, node); 113 | ASSERT_EQ((&channel)->NumOutRec, i + 1); 114 | } 115 | 116 | reset_nodes(nodes, std::size(nodes)); 117 | ASSERT_EQ((&channel)->NumOutRec, 1); 118 | } 119 | 120 | TEST(channel, partial_reliable) 121 | { 122 | utcp_channel_rtti channel; 123 | utcp_bunch_node_raii nodes[4]; 124 | for (int i = 0; i < std::size(nodes); ++i) 125 | { 126 | auto node = &nodes[i]; 127 | node->utcp_bunch.ChSequence = i; 128 | node->utcp_bunch.bReliable = true; 129 | node->utcp_bunch.bPartial = true; 130 | node->utcp_bunch.bPartialInitial = i == 0; 131 | node->utcp_bunch.bPartialFinal = (i + 1 == std::size(nodes)); 132 | } 133 | 134 | for (int i = 0; i < std::size(nodes); ++i) 135 | { 136 | auto node = &nodes[i]; 137 | bool bOutSkipAck; 138 | int ret = merge_partial_data(&channel, node, &bOutSkipAck); 139 | if (i + 1 != std::size(nodes)) 140 | { 141 | ASSERT_EQ(ret, partial_merge_succeed); 142 | } 143 | else 144 | { 145 | ASSERT_EQ(ret, partial_available); 146 | } 147 | ASSERT_FALSE(bOutSkipAck); 148 | } 149 | 150 | struct utcp_bunch* handle_bunches[MaxSequenceHistoryLength]; 151 | ASSERT_EQ(get_partial_bunch(&channel, handle_bunches, (int)std::size(handle_bunches)), 4); 152 | 153 | reset_nodes(nodes, std::size(nodes)); 154 | clear_partial_data(&channel); 155 | } 156 | 157 | TEST(channel, partial_reliable_failed) 158 | { 159 | utcp_channel_rtti channel; 160 | utcp_bunch_node_raii nodes[4]; 161 | for (int i = 0; i < std::size(nodes); ++i) 162 | { 163 | auto node = &nodes[i]; 164 | node->utcp_bunch.ChSequence = i * 2; 165 | node->utcp_bunch.bReliable = true; 166 | node->utcp_bunch.bPartial = true; 167 | node->utcp_bunch.bPartialInitial = i == 0; 168 | node->utcp_bunch.bPartialFinal = (i + 1 == std::size(nodes)); 169 | } 170 | 171 | for (int i = 0; i < std::size(nodes); ++i) 172 | { 173 | auto node = &nodes[i]; 174 | bool bOutSkipAck; 175 | int ret = merge_partial_data(&channel, node, &bOutSkipAck); 176 | if (i == 0) 177 | { 178 | ASSERT_EQ(ret, partial_merge_succeed); 179 | ASSERT_FALSE(bOutSkipAck); 180 | nodes[i].reset(); 181 | } 182 | else 183 | { 184 | ASSERT_EQ(ret, partial_merge_fatal); 185 | ASSERT_TRUE(bOutSkipAck); 186 | } 187 | } 188 | clear_partial_data(&channel); 189 | } 190 | 191 | TEST(channel, partial_reliable_none_initial) 192 | { 193 | utcp_channel_rtti channel; 194 | utcp_bunch_node_raii nodes[2]; 195 | for (int i = 0; i < std::size(nodes); ++i) 196 | { 197 | auto node = &nodes[i]; 198 | node->utcp_bunch.ChSequence = i; 199 | node->utcp_bunch.bReliable = true; 200 | node->utcp_bunch.bPartial = true; 201 | node->utcp_bunch.bPartialInitial = 0; 202 | node->utcp_bunch.bPartialFinal = 0; 203 | } 204 | 205 | for (int i = 0; i < std::size(nodes); ++i) 206 | { 207 | auto node = &nodes[i]; 208 | bool bOutSkipAck; 209 | ASSERT_EQ(merge_partial_data(&channel, node, &bOutSkipAck), partial_merge_failed); 210 | } 211 | } 212 | 213 | TEST(channel, partial_unreliable) 214 | { 215 | utcp_channel_rtti channel; 216 | utcp_bunch_node_raii nodes[4]; 217 | for (int i = 0; i < std::size(nodes); ++i) 218 | { 219 | auto node = &nodes[i]; 220 | node->utcp_bunch.ChSequence = i; 221 | node->utcp_bunch.bReliable = false; 222 | node->utcp_bunch.bPartial = true; 223 | node->utcp_bunch.bPartialInitial = i == 0; 224 | node->utcp_bunch.bPartialFinal = (i + 1 == std::size(nodes)); 225 | } 226 | 227 | for (int i = 0; i < std::size(nodes); ++i) 228 | { 229 | auto node = &nodes[i]; 230 | bool bOutSkipAck; 231 | int ret = merge_partial_data(&channel, node, &bOutSkipAck); 232 | if (i + 1 != std::size(nodes)) 233 | ASSERT_EQ(ret, partial_merge_succeed); 234 | else 235 | ASSERT_EQ(ret, partial_available); 236 | ASSERT_FALSE(bOutSkipAck); 237 | } 238 | 239 | struct utcp_bunch* handle_bunches[MaxSequenceHistoryLength]; 240 | ASSERT_EQ(get_partial_bunch(&channel, handle_bunches, (int)std::size(handle_bunches)), std::size(nodes)); 241 | 242 | for (int i = 0; i < std::size(nodes); ++i) 243 | { 244 | ASSERT_EQ(handle_bunches[i]->ChSequence, i); 245 | } 246 | 247 | reset_nodes(nodes, std::size(nodes)); 248 | 249 | clear_partial_data(&channel); 250 | } 251 | 252 | TEST(channel, partial_unreliable_failed) 253 | { 254 | utcp_channel_rtti channel; 255 | utcp_bunch_node_raii nodes[4]; 256 | for (int i = 0; i < std::size(nodes); ++i) 257 | { 258 | auto node = &nodes[i]; 259 | node->utcp_bunch.ChSequence = i * 2; 260 | node->utcp_bunch.bReliable = false; 261 | node->utcp_bunch.bPartial = true; 262 | node->utcp_bunch.bPartialInitial = i == 0; 263 | node->utcp_bunch.bPartialFinal = (i + 1 == std::size(nodes)); 264 | } 265 | 266 | for (int i = 0; i < std::size(nodes); ++i) 267 | { 268 | auto node = &nodes[i]; 269 | bool bOutSkipAck; 270 | int ret = merge_partial_data(&channel, node, &bOutSkipAck); 271 | 272 | if (i == 0) 273 | { 274 | ASSERT_EQ(ret, partial_merge_succeed); 275 | ASSERT_FALSE(bOutSkipAck); 276 | nodes[i].reset(); 277 | } 278 | else 279 | { 280 | ASSERT_EQ(ret, partial_merge_failed); 281 | ASSERT_TRUE(bOutSkipAck); 282 | } 283 | } 284 | } 285 | 286 | TEST(channel, partial_unreliable_none_initial) 287 | { 288 | utcp_channel_rtti channel; 289 | utcp_bunch_node_raii nodes[2]; 290 | for (int i = 0; i < std::size(nodes); ++i) 291 | { 292 | auto node = &nodes[i]; 293 | node->utcp_bunch.ChSequence = i; 294 | node->utcp_bunch.bReliable = false; 295 | node->utcp_bunch.bPartial = true; 296 | node->utcp_bunch.bPartialInitial = 0; 297 | node->utcp_bunch.bPartialFinal = 0; 298 | } 299 | 300 | for (int i = 0; i < std::size(nodes); ++i) 301 | { 302 | auto node = &nodes[i]; 303 | bool bOutSkipAck; 304 | ASSERT_EQ(merge_partial_data(&channel, node, &bOutSkipAck), partial_merge_failed); 305 | } 306 | } 307 | 308 | TEST(channel, partial_unreliable_reliable) 309 | { 310 | utcp_channel_rtti channel; 311 | utcp_bunch_node_raii nodes1[2]; 312 | for (int i = 0; i < std::size(nodes1); ++i) 313 | { 314 | auto node = &nodes1[i]; 315 | node->utcp_bunch.ChSequence = i; 316 | node->utcp_bunch.bReliable = false; 317 | node->utcp_bunch.bPartial = true; 318 | node->utcp_bunch.bPartialInitial = i == 0; 319 | node->utcp_bunch.bPartialFinal = 0; 320 | } 321 | 322 | utcp_bunch_node_raii nodes2[2]; 323 | for (int i = 0; i < std::size(nodes1); ++i) 324 | { 325 | auto node = &nodes2[i]; 326 | node->utcp_bunch.ChSequence = i; 327 | node->utcp_bunch.bReliable = true; 328 | node->utcp_bunch.bPartial = true; 329 | node->utcp_bunch.bPartialInitial = i == 0; 330 | node->utcp_bunch.bPartialFinal = 0; 331 | } 332 | 333 | for (int i = 0; i < std::size(nodes1); ++i) 334 | { 335 | auto node = &nodes1[i]; 336 | bool bOutSkipAck; 337 | ASSERT_EQ(merge_partial_data(&channel, node, &bOutSkipAck), partial_merge_succeed); 338 | nodes1[i].reset(); 339 | } 340 | 341 | for (int i = 0; i < std::size(nodes2); ++i) 342 | { 343 | auto node = &nodes2[i]; 344 | bool bOutSkipAck; 345 | ASSERT_EQ(merge_partial_data(&channel, node, &bOutSkipAck), partial_merge_succeed); 346 | nodes2[i].reset(); 347 | } 348 | } 349 | 350 | TEST(channel, partial_reliable_unreliable) 351 | { 352 | utcp_channel_rtti channel; 353 | utcp_bunch_node_raii nodes1[2]; 354 | for (int i = 0; i < std::size(nodes1); ++i) 355 | { 356 | auto node = &nodes1[i]; 357 | node->utcp_bunch.ChSequence = i; 358 | node->utcp_bunch.bReliable = false; 359 | node->utcp_bunch.bPartial = true; 360 | node->utcp_bunch.bPartialInitial = i == 0; 361 | node->utcp_bunch.bPartialFinal = 0; 362 | } 363 | 364 | utcp_bunch_node_raii nodes2[2]; 365 | for (int i = 0; i < std::size(nodes1); ++i) 366 | { 367 | auto node = &nodes2[i]; 368 | node->utcp_bunch.ChSequence = i; 369 | node->utcp_bunch.bReliable = true; 370 | node->utcp_bunch.bPartial = true; 371 | node->utcp_bunch.bPartialInitial = i == 0; 372 | node->utcp_bunch.bPartialFinal = 0; 373 | } 374 | 375 | for (int i = 0; i < std::size(nodes2); ++i) 376 | { 377 | auto node = &nodes2[i]; 378 | bool bOutSkipAck; 379 | ASSERT_EQ(merge_partial_data(&channel, node, &bOutSkipAck), partial_merge_succeed); 380 | nodes2[i].reset(); 381 | } 382 | 383 | for (int i = 0; i < std::size(nodes1); ++i) 384 | { 385 | auto node = &nodes1[i]; 386 | bool bOutSkipAck; 387 | ASSERT_EQ(merge_partial_data(&channel, node, &bOutSkipAck), partial_merge_failed); 388 | } 389 | } 390 | -------------------------------------------------------------------------------- /test/test_case/handshake_test.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.h" 2 | #include "utcp/utcp.h" 3 | #include "utcp/utcp_def.h" 4 | #include "gtest/gtest.h" 5 | #include 6 | 7 | static uint8_t handshake_step1[] = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}; 8 | static std::vector last_send; 9 | static bool new_conn; 10 | 11 | struct handshake : public ::testing::Test 12 | { 13 | virtual void SetUp() override 14 | { 15 | new_conn = false; 16 | last_send.clear(); 17 | 18 | auto config = utcp_get_config(); 19 | config->on_outgoing = [](void* fd, void* userdata, const void* data, int len) { last_send.insert(last_send.end(), (const uint8_t*)data, (const uint8_t*)data + len); }; 20 | config->on_accept = [](struct utcp_listener* fd, void* userdata, bool reconnect) { 21 | assert(!reconnect); 22 | assert(!new_conn); 23 | new_conn = true; 24 | }; 25 | } 26 | 27 | virtual void TearDown() override 28 | { 29 | auto config = utcp_get_config(); 30 | config->on_outgoing = nullptr; 31 | config->on_accept = nullptr; 32 | } 33 | }; 34 | /* 35 | TEST_F(handshake, accept) 36 | { 37 | utcp_listener_rtti fd; 38 | 39 | int ret = utcp_listener_incoming(fd.get(), "127.0.0.1:12345", handshake_step1, sizeof(handshake_step1)); 40 | ASSERT_EQ(ret, 0); 41 | ASSERT_EQ(last_send.size(), 29); 42 | ASSERT_FALSE(new_conn); 43 | 44 | utcp_add_elapsed_time(10 * 1000 * 1000); 45 | 46 | std::vector send2 = last_send; 47 | last_send.clear(); 48 | ret = utcp_listener_incoming(fd.get(), "127.0.0.1:12345", send2.data(), (int)send2.size()); 49 | ASSERT_EQ(ret, 0); 50 | ASSERT_EQ(last_send.size(), 29); 51 | ASSERT_TRUE(new_conn); 52 | } 53 | 54 | TEST_F(handshake, client) 55 | { 56 | utcp_connection_rtti fd; 57 | utcp_connect(fd.get()); 58 | ASSERT_EQ(last_send.size(), sizeof(handshake_step1)); 59 | ASSERT_EQ(memcmp(handshake_step1, last_send.data(), last_send.size()), 0); 60 | } 61 | */ -------------------------------------------------------------------------------- /test/test_case/large_bunch_test.cpp: -------------------------------------------------------------------------------- 1 | #include "abstract/utcp.hpp" 2 | #include "gtest/gtest.h" 3 | 4 | class large_bunch_param_test : public ::testing::TestWithParam 5 | { 6 | }; 7 | 8 | TEST_P(large_bunch_param_test, test) 9 | { 10 | std::vector test_data; 11 | test_data.reserve(utcp::NetMaxConstructedPartialBunchSizeBytes); 12 | int test_count = GetParam(); 13 | assert(test_count <= utcp::NetMaxConstructedPartialBunchSizeBytes); 14 | 15 | for (int i = 0; i < test_count; ++i) 16 | { 17 | test_data.push_back(rand() % 256); 18 | } 19 | 20 | utcp::large_bunch large_bunch1(test_data.data(), test_count * 8); 21 | if (large_bunch1.ExtDataBitsLen == 0) 22 | { 23 | ASSERT_EQ(large_bunch1.DataBitsLen, test_count * 8); 24 | ASSERT_EQ(memcmp(large_bunch1.Data, test_data.data(), test_count), 0); 25 | } 26 | else 27 | { 28 | ASSERT_EQ(large_bunch1.ExtDataBitsLen, test_count * 8); 29 | ASSERT_EQ(memcmp(large_bunch1.ExtData, test_data.data(), test_count), 0); 30 | } 31 | 32 | std::vector bunches; 33 | std::vector ref_bunches; 34 | 35 | bunches.reserve(1024); 36 | ref_bunches.reserve(1024); 37 | 38 | for (auto& bunch : large_bunch1) 39 | { 40 | bunches.push_back(bunch); 41 | ref_bunches.push_back(&bunches[bunches.size() - 1]); 42 | } 43 | utcp::large_bunch large_bunch2(ref_bunches.data(), (int)ref_bunches.size()); 44 | 45 | if (large_bunch1.ExtDataBitsLen == 0) 46 | { 47 | ASSERT_EQ(large_bunch1.DataBitsLen, large_bunch2.DataBitsLen); 48 | ASSERT_EQ(memcmp(large_bunch1.Data, large_bunch2.Data, test_count), 0); 49 | } 50 | else 51 | { 52 | ASSERT_EQ(large_bunch1.ExtDataBitsLen, large_bunch2.ExtDataBitsLen); 53 | ASSERT_EQ(memcmp(large_bunch1.ExtData, large_bunch2.ExtData, test_count), 0); 54 | } 55 | } 56 | 57 | // INSTANTIATE_TEST_CASE_P(test_constructor, large_bunch_param_test, testing::Range(0, utcp::NetMaxConstructedPartialBunchSizeBytes + 1)); 58 | INSTANTIATE_TEST_CASE_P(test_constructor, large_bunch_param_test, 59 | testing::Values(0, utcp::MAX_SINGLE_BUNCH_SIZE_BYTES - 10, utcp::MAX_SINGLE_BUNCH_SIZE_BYTES, utcp::MAX_SINGLE_BUNCH_SIZE_BYTES + 10, 60 | utcp::NetMaxConstructedPartialBunchSizeBytes - 10, utcp::NetMaxConstructedPartialBunchSizeBytes)); 61 | -------------------------------------------------------------------------------- /test/test_case/open_channel_test.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utils.h" 2 | #include "gtest/gtest.h" 3 | 4 | TEST(open_channel, insert) 5 | { 6 | utcp_opened_channels_rtti open_channels; 7 | int all = 10; 8 | int cnt = 5; 9 | 10 | for (int i = 0; i < cnt; ++i) 11 | { 12 | opened_channels_add(&open_channels, i * 2); 13 | } 14 | 15 | for (int i = 0; i < all; ++i) 16 | { 17 | ASSERT_TRUE(opened_channels_add(&open_channels, i)); 18 | if (i % 2 != 0) 19 | cnt++; 20 | ASSERT_EQ(open_channels.get()->num, cnt); 21 | } 22 | 23 | for (int i = 0; i < all; ++i) 24 | { 25 | ASSERT_EQ(open_channels.get()->channels[i], i); 26 | } 27 | } 28 | 29 | TEST(open_channel, remove) 30 | { 31 | utcp_opened_channels_rtti open_channels; 32 | int all = 10; 33 | int cnt = 0; 34 | 35 | for (int i = 0; i < all; ++i) 36 | { 37 | opened_channels_add(&open_channels, i); 38 | cnt++; 39 | ASSERT_EQ(open_channels.get()->num, cnt); 40 | } 41 | 42 | for (int i = 0; i < all; ++i) 43 | { 44 | ASSERT_EQ(open_channels.get()->channels[i], i); 45 | } 46 | 47 | for (int i = 1; i < all; i += 2) 48 | { 49 | ASSERT_TRUE(opened_channels_remove(&open_channels, i)); 50 | cnt--; 51 | ASSERT_EQ(open_channels.get()->num, cnt); 52 | } 53 | ASSERT_EQ(cnt, 5); 54 | 55 | for (int i = 1; i < cnt; i++) 56 | { 57 | ASSERT_EQ(open_channels.get()->channels[i], i * 2); 58 | } 59 | 60 | for (int i = 1; i < all; i += 2) 61 | { 62 | ASSERT_FALSE(opened_channels_remove(&open_channels, i)); 63 | ASSERT_EQ(open_channels.get()->num, cnt); 64 | } 65 | 66 | for (int i = 0; i < all; i += 2) 67 | { 68 | ASSERT_TRUE(opened_channels_remove(&open_channels, i)); 69 | cnt--; 70 | ASSERT_EQ(open_channels.get()->num, cnt); 71 | } 72 | ASSERT_EQ(cnt, 0); 73 | } 74 | 75 | TEST(open_channel, resize) 76 | { 77 | utcp_opened_channels_rtti open_channels; 78 | for (int i = 0; i < 100; ++i) 79 | { 80 | opened_channels_add(&open_channels, i); 81 | ASSERT_EQ(open_channels.get()->num, i + 1); 82 | 83 | if (open_channels.get()->num <= 32) 84 | { 85 | ASSERT_EQ(open_channels.get()->cap, 32); 86 | } 87 | else if (open_channels.get()->num <= 64) 88 | { 89 | ASSERT_EQ(open_channels.get()->cap, 64); 90 | } 91 | else 92 | { 93 | ASSERT_EQ(open_channels.get()->cap, 128); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /test/test_case/packet_notify_test.cpp: -------------------------------------------------------------------------------- 1 | #include "utcp/utcp_sequence_number.h" 2 | #include "gtest/gtest.h" 3 | 4 | TEST(packet_notify, sequence_number) 5 | { 6 | uint16_t num1 = seq_num_init(SeqNumberMax - 5); 7 | ASSERT_EQ(num1, SeqNumberMax - 5); 8 | 9 | uint16_t num2 = seq_num_init(SeqNumberMax); 10 | ASSERT_EQ(num2, SeqNumberMax); 11 | 12 | uint16_t num3 = seq_num_init(SeqNumberMax + 5); 13 | ASSERT_EQ(num3, 4); 14 | 15 | ASSERT_TRUE(seq_num_greater_than(num2, num1)); 16 | ASSERT_TRUE(seq_num_greater_than(num3, num1)); 17 | ASSERT_TRUE(seq_num_greater_than(num3, num2)); 18 | 19 | ASSERT_EQ(seq_num_diff(num2, num1), 5); 20 | ASSERT_EQ(seq_num_diff(num3, num1), 10); 21 | ASSERT_EQ(seq_num_diff(num3, num2), 5); 22 | 23 | ASSERT_EQ(seq_num_diff(num1, num2), -5); 24 | ASSERT_EQ(seq_num_diff(num1, num3), -10); 25 | ASSERT_EQ(seq_num_diff(num2, num3), -5); 26 | } 27 | // FNetPacketNotifyTest::RunTest -------------------------------------------------------------------------------- /test/test_case/packet_test.cpp: -------------------------------------------------------------------------------- 1 |  2 | #include "test_utils.h" 3 | extern "C" 4 | { 5 | #include "utcp/utcp_packet.h" 6 | } 7 | #include "gtest/gtest.h" 8 | #include 9 | 10 | static std::vector last_send; 11 | static std::vector last_recv; 12 | 13 | struct packet : public ::testing::Test 14 | { 15 | virtual void SetUp() override 16 | { 17 | last_send.clear(); 18 | last_recv.clear(); 19 | 20 | auto config = utcp_get_config(); 21 | config->on_outgoing = [](void* fd, void* userdata, const void* data, int len) { last_send.insert(last_send.end(), (const char*)data, (const char*)data + len); }; 22 | config->on_recv_bunch = [](struct utcp_connection* fd, void* userdata, struct utcp_bunch* const bunches[], int count) { 23 | for (int i = 0; i < count; ++i) 24 | { 25 | last_recv.push_back(*bunches[i]); 26 | } 27 | }; 28 | } 29 | 30 | virtual void TearDown() override 31 | { 32 | auto config = utcp_get_config(); 33 | config->on_outgoing = nullptr; 34 | config->on_recv_bunch = nullptr; 35 | } 36 | }; 37 | 38 | #pragma pack(1) 39 | // DEFINE_CONTROL_CHANNEL_MESSAGE(Hello, 0, uint8, uint32, FString); 40 | struct Hello 41 | { 42 | uint8_t MsgType; 43 | uint8_t IsLittleEndian; 44 | uint32_t RemoteNetworkVersion; 45 | uint32_t EncryptionTokenStrLen; 46 | }; 47 | #pragma pack() 48 | /* 49 | TEST_F(packet, accept_hello) 50 | { 51 | uint8_t packet_hello[] = {0x80, 0x00, 0xb5, 0x78, 0x01, 0x00, 0x00, 0x00, 0xfe, 0x6f, 0x02, 0xe0, 0xe2, 0xff, 52 | 0x02, 0x50, 0x00, 0x20, 0x60, 0xa6, 0x0f, 0x93, 0x11, 0x00, 0x00, 0x00, 0x60}; 53 | 54 | utcp_connection_rtti fd; 55 | utcp_sequence_init(fd.get(), 12054, 10245); 56 | 57 | int32_t packet_id = utcp_peep_packet_id(fd.get(), packet_hello, sizeof(packet_hello)); 58 | ASSERT_EQ(packet_id, 12054); 59 | 60 | ASSERT_TRUE(utcp_incoming(fd.get(), packet_hello, sizeof(packet_hello))); 61 | 62 | ASSERT_EQ(last_recv.size(), 1); 63 | ASSERT_EQ(last_recv[0].bOpen, 1); 64 | ASSERT_EQ(last_recv[0].bClose, 0); 65 | ASSERT_EQ(last_recv[0].DataBitsLen, sizeof(Hello) * 8); 66 | 67 | auto hello = (Hello*)(last_recv[0].Data); 68 | ASSERT_EQ(hello->MsgType, 0); 69 | ASSERT_EQ(hello->IsLittleEndian, 1); 70 | ASSERT_EQ(hello->RemoteNetworkVersion, 2358803763); 71 | ASSERT_EQ(hello->EncryptionTokenStrLen, 0); 72 | 73 | ASSERT_FALSE(utcp_incoming(fd.get(), packet_hello, sizeof(packet_hello))); 74 | } 75 | 76 | TEST_F(packet, write_hello) 77 | { 78 | uint8_t packet_hello[] = {0x80, 0x00, 0xb5, 0x78, 0x01, 0x00, 0x00, 0x00, 0xfe, 0x6f, 0x02, 0xe0, 0xe2, 0xff, 79 | 0x02, 0x50, 0x00, 0x20, 0x60, 0xa6, 0x0f, 0x93, 0x11, 0x00, 0x00, 0x00, 0x60}; 80 | 81 | utcp_fd_rtti fd; 82 | utcp_init(fd.get(), nullptr); 83 | utcp_sequence_init(fd.get(), 10245, 12054); 84 | 85 | Hello hello; 86 | hello.MsgType = 0; 87 | hello.IsLittleEndian = 1; 88 | hello.RemoteNetworkVersion = 2358803763; 89 | hello.EncryptionTokenStrLen = 0; 90 | 91 | struct utcp_bunch bunch; 92 | memset(&bunch, 0, sizeof(bunch)); 93 | bunch.NameIndex = 255; 94 | bunch.ChIndex = 0; 95 | bunch.bReliable = 1; 96 | bunch.bOpen = 1; 97 | bunch.DataBitsLen = sizeof(hello) * 8; 98 | memcpy(bunch.Data, &hello, sizeof(hello)); 99 | utcp_send_bunch(fd.get(), &bunch); 100 | utcp_send_flush(fd.get()); 101 | for (int i = 0; i < last_send.size(); ++i) 102 | { 103 | int ii = last_send[i]; 104 | int j = packet_hello[i]; 105 | if (ii != j) 106 | { 107 | printf(""); 108 | } 109 | } 110 | ASSERT_EQ(sizeof(packet_hello), last_send.size()); 111 | ASSERT_EQ(0, memcmp(packet_hello, last_send.data(), sizeof(packet_hello))); 112 | } 113 | 114 | TEST_F(packet, accept_hello_login) 115 | { 116 | 117 | uint8_t packet_hello[] = {0x0, 0x40, 0x18, 0x20, 0x0, 0x0, 0x0, 0x0, 0xFE, 0x6F, 0x2, 0x80, 0x80, 0xFF, 0x2, 0x50, 0x0, 0x20, 0x60, 0xA6, 0xF, 0x93, 0x11, 0x0, 0x0, 0x0, 0x60}; 118 | uint8_t packet_challenge[] = {0x60, 0x80, 0x10, 0x10, 0x2, 0x0, 0x0, 0x0, 0x10, 0x0, 0x2, 0xFE, 0x17, 0x80, 0x4, 0x3, 0xD, 119 | 0x0, 0x0, 0x0, 0x32, 0x35, 0x34, 0x31, 0x39, 0x32, 0x31, 0x38, 0x31, 0x36, 0x36, 0x38, 0x38, 0x3}; 120 | uint8_t packet_login[] = {0x20, 0x40, 0x28, 0x20, 0x2, 0x0, 0x0, 0x0, 0x76, 0x87, 0x0, 0x28, 0xE0, 0xBF, 0x0, 0x2, 0x29, 0x10, 0x0, 0x0, 0x0, 0x80, 0x1, 0xB8, 0x1, 121 | 0x0, 0x0, 0xF8, 0x71, 0xA, 0x6B, 0x2B, 0xEB, 0x9, 0x93, 0x1B, 0x43, 0x2B, 0x93, 0x23, 0x1B, 0x43, 0x2B, 0x73, 0x6B, 0x81, 0x1A, 0x82, 0x69, 0xB1, 122 | 0x29, 0x9A, 0xC9, 0x29, 0xBA, 0xB9, 0xB1, 0xA1, 0xA1, 0x11, 0x92, 0x91, 0xB1, 0x91, 0xC9, 0x81, 0xA1, 0x91, 0x19, 0xBA, 0xB9, 0xC9, 0x99, 0xB1, 0x99, 123 | 0xC9, 0x11, 0x92, 0x19, 0x82, 0xB9, 0x1, 0x40, 0x88, 0x1, 0x0, 0x0, 0x8, 0x93, 0x1B, 0x43, 0x2B, 0x93, 0x23, 0x1B, 0x43, 0x2B, 0x73, 0x6B, 0x81, 124 | 0x1A, 0x82, 0x69, 0xB1, 0x29, 0x9A, 0xC9, 0x29, 0xBA, 0xB9, 0xB1, 0xA1, 0xA1, 0x11, 0x92, 0x91, 0xB1, 0x91, 0xC9, 0x81, 0xA1, 0x91, 0x19, 0xBA, 0xB9, 125 | 0xC9, 0x99, 0xB1, 0x99, 0xC9, 0x11, 0x92, 0x19, 0x82, 0xB9, 0x1, 0x28, 0x0, 0x0, 0x0, 0x70, 0xAA, 0x62, 0x62, 0x2, 0x18}; 126 | 127 | uint8_t packet_welcome[] = {0xA0, 0x80, 0x18, 0x10, 0xA, 0x0, 0x0, 0x0, 0x10, 0x0, 0x3, 0xFE, 0x17, 0x80, 0x16, 0x1, 0x2E, 0x0, 0x0, 0x0, 0x2F, 0x47, 128 | 0x61, 0x6D, 0x65, 0x2F, 0x54, 0x68, 0x69, 0x72, 0x64, 0x50, 0x65, 0x72, 0x73, 0x6F, 0x6E, 0x2F, 0x4D, 0x61, 0x70, 0x73, 0x2F, 0x55, 129 | 0x45, 0x44, 0x50, 0x49, 0x45, 0x5F, 0x30, 0x5F, 0x54, 0x68, 0x69, 0x72, 0x64, 0x50, 0x65, 0x72, 0x73, 0x6F, 0x6E, 0x4D, 0x61, 0x70, 130 | 0x1F, 0x0, 0x0, 0x0, 0x2F, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2F, 0x45, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x2E, 0x45, 0x78, 131 | 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x47, 0x61, 0x6D, 0x65, 0x4D, 0x6F, 0x64, 0x65, 0x0, 0x0, 0x0, 0x0, 0x3}; 132 | 133 | uint8_t packet_netspeed[] = {0x60, 0x40, 0x38, 0x20, 0xE, 0x0, 0x0, 0x0, 0x72, 0x88, 0x0, 0x30, 0xE0, 0xBF, 0x0, 0xA, 0x20, 0x0, 0x35, 0xC, 0x0, 0x18}; 134 | 135 | utcp_connection_rtti fd; 136 | utcp_sequence_init(fd.get(), 1027, 513); 137 | 138 | struct utcp_bunch bunch; 139 | memset(&bunch, 0, sizeof(bunch)); 140 | bunch.NameIndex = 255; 141 | bunch.ChIndex = 0; 142 | bunch.bReliable = 1; 143 | 144 | ASSERT_TRUE(utcp_incoming(fd.get(), packet_hello, sizeof(packet_hello))); 145 | ASSERT_EQ(last_recv.size(), 1); 146 | 147 | bunch.DataBitsLen = sizeof(packet_challenge) * 8; 148 | memcpy(bunch.Data, packet_challenge, sizeof(packet_challenge)); 149 | auto ret = utcp_send_bunch(fd.get(), &bunch); 150 | ASSERT_NE(ret, -1); 151 | 152 | utcp_send_flush(fd.get()); 153 | 154 | ASSERT_TRUE(utcp_incoming(fd.get(), packet_login, sizeof(packet_login))); 155 | ASSERT_EQ(last_recv.size(), 2); 156 | 157 | bunch.DataBitsLen = sizeof(packet_welcome) * 8; 158 | memcpy(bunch.Data, packet_welcome, sizeof(packet_welcome)); 159 | ret = utcp_send_bunch(fd.get(), &bunch); 160 | ASSERT_NE(ret, -1); 161 | 162 | utcp_send_flush(fd.get()); 163 | } 164 | */ -------------------------------------------------------------------------------- /test/test_case/ringbuffer_test.cpp: -------------------------------------------------------------------------------- 1 | #include "utcp/3rd/ringbuffer.h" 2 | #include "gtest/gtest.h" 3 | 4 | TEST(ring_buffer, size) 5 | { 6 | struct ring_buffer_t ring_buffer; 7 | ring_buffer_init(&ring_buffer); 8 | uint32_t start = 45678; 9 | uint32_t loop = 1000; 10 | for (uint32_t i = 0; i < 1000; ++i) 11 | { 12 | if (i < 256) 13 | ASSERT_EQ(ring_buffer_num_items(&ring_buffer), i); 14 | else 15 | ASSERT_EQ(ring_buffer_num_items(&ring_buffer), 255); 16 | ring_buffer_queue(&ring_buffer, start + i); 17 | } 18 | 19 | while (!ring_buffer_is_empty(&ring_buffer)) 20 | { 21 | uint32_t val = 0; 22 | auto ret = ring_buffer_dequeue(&ring_buffer, &val); 23 | ASSERT_EQ(ret, 1); 24 | uint32_t exp = start + loop - 1 - ring_buffer_num_items(&ring_buffer); 25 | ASSERT_EQ(val, exp); 26 | } 27 | } -------------------------------------------------------------------------------- /test/test_case/sha1_test.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | extern "C" void sha1_hmac_buffer(const void* Key, uint32_t KeySize, const void* Data, uint64_t DataSize, uint8_t* OutHash); 3 | 4 | TEST(sha1, test) 5 | { 6 | uint8_t result[][20] = { 7 | {0x53, 0x26, 0x9B, 0x98, 0x64, 0xC8, 0x65, 0x22, 0x4, 0x2C, 0x3E, 0x9B, 0x5A, 0xB7, 0x36, 0x28, 0xC5, 0xEE, 0xAD, 0x92}, 8 | {0xA, 0x13, 0xF3, 0x43, 0x1F, 0x85, 0xB1, 0x38, 0xCF, 0x6C, 0x31, 0x32, 0x56, 0x6F, 0x68, 0xDF, 0xE7, 0x57, 0xCD, 0x7C}, 9 | {0xA8, 0xB3, 0x5E, 0xF2, 0x7B, 0x8C, 0xC4, 0x8E, 0xC, 0x17, 0xB7, 0xB4, 0x47, 0xF4, 0x4, 0x13, 0xCB, 0xF4, 0x63, 0xA9}, 10 | {0xE, 0x5A, 0x17, 0xA0, 0x58, 0x4E, 0x50, 0xEB, 0xA, 0x5A, 0xF4, 0x20, 0x9E, 0x2A, 0x11, 0xCB, 0xE4, 0x2E, 0xAD, 0xA2}, 11 | {0xC8, 0xBF, 0xC5, 0x30, 0x45, 0xB3, 0x36, 0x86, 0xBD, 0x61, 0x25, 0x8D, 0xCF, 0x57, 0xA, 0xCD, 0xC3, 0x77, 0x28, 0x79}, 12 | {0xB, 0x31, 0xBE, 0xD0, 0x8B, 0x9C, 0xD4, 0x39, 0x74, 0x7B, 0x2C, 0x62, 0x1B, 0x55, 0x13, 0xAC, 0xDC, 0x91, 0x55, 0x2B}, 13 | {0x62, 0x5C, 0x63, 0xA, 0x2B, 0xC3, 0xC7, 0xF6, 0xEF, 0xD1, 0x85, 0xF3, 0xF7, 0x10, 0x1, 0xB7, 0x5F, 0x2B, 0x3E, 0xA8}, 14 | {0xC9, 0x33, 0x13, 0xCB, 0x49, 0x80, 0xF0, 0xDC, 0x8A, 0x84, 0xB7, 0x2A, 0x55, 0x88, 0xB2, 0x73, 0xC2, 0x8A, 0x82, 0x2A}, 15 | {0xCB, 0xC, 0xB6, 0xA, 0xC2, 0xB8, 0x43, 0x93, 0x55, 0x3A, 0x68, 0xC6, 0x6D, 0xC8, 0x4C, 0x79, 0xED, 0x67, 0x21, 0xDE}, 16 | {0xC6, 0x35, 0x2E, 0x11, 0xE9, 0x90, 0x97, 0xE0, 0x86, 0x98, 0xF2, 0xA1, 0x99, 0xD7, 0x9, 0xB0, 0x48, 0x13, 0x85, 0x79}, 17 | {0x8F, 0x1B, 0x67, 0x3E, 0x5F, 0x6C, 0x9C, 0xB0, 0x99, 0xF1, 0xEB, 0x8C, 0x31, 0xBC, 0x65, 0x60, 0x92, 0x78, 0x39, 0x69}, 18 | {0xE0, 0x3F, 0x27, 0x80, 0xD0, 0x86, 0x7F, 0xDF, 0x21, 0x60, 0x39, 0x46, 0x9E, 0x74, 0xCB, 0x6D, 0xED, 0x54, 0x21, 0xB2}, 19 | {0xCB, 0x4B, 0x7C, 0xAE, 0x7A, 0x27, 0xF7, 0x66, 0xAA, 0x9D, 0x68, 0x38, 0x43, 0xCD, 0x5B, 0x58, 0x2, 0xDD, 0xED, 0x6F}, 20 | {0x65, 0xFD, 0xA6, 0x56, 0xF4, 0x2E, 0x93, 0xEF, 0x11, 0xBD, 0x5D, 0xA2, 0xE9, 0xAE, 0xAF, 0x17, 0x34, 0xC6, 0x34, 0x5A}, 21 | {0x67, 0x23, 0x15, 0x22, 0xA0, 0xDC, 0xD9, 0xF, 0x36, 0x74, 0xC3, 0xA7, 0x55, 0xD2, 0xAE, 0xC7, 0xAD, 0x62, 0x6C, 0xFC}, 22 | {0x7A, 0x1E, 0x39, 0x6F, 0xC4, 0x4, 0x7D, 0x19, 0x1C, 0x18, 0x8A, 0xF3, 0x16, 0x8B, 0xED, 0x1E, 0xCF, 0x35, 0xF0, 0x4B}, 23 | }; 24 | 25 | uint8_t HandshakeSecret[64]; 26 | uint8_t CookieData[128]; 27 | uint8_t Cookie[20]; 28 | 29 | for (int i = 0; i < 16; ++i) 30 | { 31 | for (int j = 0; j < std::size(HandshakeSecret); ++j) 32 | { 33 | HandshakeSecret[j] = i + j * 2; 34 | } 35 | 36 | for (int j = 0; j < std::size(CookieData); ++j) 37 | { 38 | CookieData[j] = i + j; 39 | } 40 | 41 | sha1_hmac_buffer(HandshakeSecret, std::size(HandshakeSecret), CookieData, 100 + i, Cookie); 42 | ASSERT_EQ(memcmp(Cookie, result[i], std::size(Cookie)), 0); 43 | } 44 | } -------------------------------------------------------------------------------- /test/test_case/test_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | extern "C" { 3 | #include "utcp/utcp.h" 4 | #include "utcp/utcp_channel.h" 5 | #include "utcp/utcp_channel_internal.h" 6 | #include "utcp/utcp_def.h" 7 | } 8 | #include 9 | 10 | template struct utcp_raii 11 | { 12 | utcp_raii() 13 | { 14 | if constexpr (AllocFn != nullptr) 15 | p = AllocFn(); 16 | else 17 | p = new T; 18 | } 19 | ~utcp_raii() 20 | { 21 | if (!p) 22 | return; 23 | if constexpr (FreeFn != nullptr) 24 | FreeFn(p); 25 | else 26 | delete p; 27 | } 28 | 29 | T* operator&() 30 | { 31 | return p; 32 | } 33 | 34 | T* get() 35 | { 36 | return p; 37 | } 38 | 39 | void reset() 40 | { 41 | p = nullptr; 42 | } 43 | 44 | T* p; 45 | }; 46 | 47 | inline utcp_channel* alloc_utcp_channel_zero() 48 | { 49 | return alloc_utcp_channel(0, 0); 50 | } 51 | 52 | inline utcp_connection* new_utcp_connection() 53 | { 54 | auto conn = new utcp_connection; 55 | utcp_init(conn, nullptr); 56 | return conn; 57 | } 58 | 59 | inline void delete_utcp_connection(utcp_connection* fd) 60 | { 61 | utcp_uninit(fd); 62 | delete fd; 63 | } 64 | 65 | inline utcp_listener* new_utcp_listener() 66 | { 67 | auto listener = new utcp_listener; 68 | utcp_listener_init(listener, nullptr); 69 | return listener; 70 | } 71 | 72 | inline utcp_opened_channels* new_open_channels() 73 | { 74 | auto open_channels = new utcp_opened_channels; 75 | memset(open_channels, 0, sizeof(*open_channels)); 76 | return open_channels; 77 | } 78 | 79 | inline void delete_open_channels(utcp_opened_channels* open_channels) 80 | { 81 | opened_channels_uninit(open_channels); 82 | delete open_channels; 83 | } 84 | 85 | using utcp_bunch_node_raii = utcp_raii; 86 | using utcp_channel_rtti = utcp_raii; 87 | using utcp_connection_rtti = utcp_raii; 88 | using utcp_listener_rtti = utcp_raii; 89 | using utcp_opened_channels_rtti = utcp_raii; 90 | -------------------------------------------------------------------------------- /test/test_case/xx_test.cpp.template: -------------------------------------------------------------------------------- 1 | #include "utcp/3rd/ringbuffer.h" 2 | #include "gtest/gtest.h" 3 | 4 | TEST(xx, case) 5 | { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /utcp/3rd/WjCryptLib_Sha1.c: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 2 | // WjCryptLib_Sha1 3 | // 4 | // Implementation of SHA1 hash function. 5 | // Original author: Steve Reid 6 | // Contributions by: James H. Brown , Saul Kravitz , 7 | // and Ralph Giles 8 | // Modified by WaterJuice retaining Public Domain license. 9 | // 10 | // This is free and unencumbered software released into the public domain - June 2013 waterjuice.org 11 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 12 | 13 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 14 | // IMPORTS 15 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 16 | 17 | #include "WjCryptLib_Sha1.h" 18 | #include 19 | 20 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 21 | // DEFINES 22 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 23 | 24 | // Decide whether to use the Little-Endian shortcut. If the shortcut is not used then the code will work correctly 25 | // on either big or little endian, however if we do know it is a little endian architecture we can speed it up a bit. 26 | // Note, there are TWO places where USE_LITTLE_ENDIAN_SHORTCUT is used. They MUST be paired together. 27 | #if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && ( __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ ) 28 | // gcc defines __BYTE_ORDER__ so if it says its little endian we can use that. 29 | #define USE_LITTLE_ENDIAN_SHORTCUT 30 | #elif defined( _WIN32 ) 31 | // Windows is always little endian so we can use that. 32 | #define USE_LITTLE_ENDIAN_SHORTCUT 33 | #endif 34 | 35 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 36 | // TYPES 37 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 38 | 39 | typedef union 40 | { 41 | uint8_t c [64]; 42 | uint32_t l [16]; 43 | } CHAR64LONG16; 44 | 45 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 46 | // INTERNAL FUNCTIONS 47 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 48 | 49 | // Endian neutral macro for loading 32 bit value from 4 byte array (in big endian form). 50 | #define LOAD32H(x, y) \ 51 | { x = ((uint32_t)((y)[0] & 255)<<24) | \ 52 | ((uint32_t)((y)[1] & 255)<<16) | \ 53 | ((uint32_t)((y)[2] & 255)<<8) | \ 54 | ((uint32_t)((y)[3] & 255)); } 55 | 56 | #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) 57 | 58 | // blk0() and blk() perform the initial expand. 59 | #ifdef USE_LITTLE_ENDIAN_SHORTCUT 60 | #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) | (rol(block->l[i],8)&0x00FF00FF)) 61 | #else 62 | #define blk0(i) block->l[i] 63 | #endif 64 | 65 | #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15] ^ block->l[(i+8)&15] ^ block->l[(i+2)&15] ^ block->l[i&15],1)) 66 | 67 | // (R0+R1), R2, R3, R4 are the different operations used in SHA1 68 | #define R0(v,w,x,y,z,i) z += ((w&(x^y))^y) + blk0(i)+ 0x5A827999 + rol(v,5); w=rol(w,30); 69 | #define R1(v,w,x,y,z,i) z += ((w&(x^y))^y) + blk(i) + 0x5A827999 + rol(v,5); w=rol(w,30); 70 | #define R2(v,w,x,y,z,i) z += (w^x^y) + blk(i) + 0x6ED9EBA1 + rol(v,5); w=rol(w,30); 71 | #define R3(v,w,x,y,z,i) z += (((w|x)&y)|(w&x)) + blk(i) + 0x8F1BBCDC + rol(v,5); w=rol(w,30); 72 | #define R4(v,w,x,y,z,i) z += (w^x^y) + blk(i) + 0xCA62C1D6 + rol(v,5); w=rol(w,30); 73 | 74 | // Loads the 128 bits from ByteArray into WordArray, treating ByteArray as big endian data 75 | #ifdef USE_LITTLE_ENDIAN_SHORTCUT 76 | #define Load128BitsAsWords( WordArray, ByteArray ) \ 77 | memcpy( WordArray, ByteArray, 64 ) 78 | #else 79 | #define Load128BitsAsWords( WordArray, ByteArray ) \ 80 | { \ 81 | uint32_t i; \ 82 | for( i=0; i<16; i++ ) \ 83 | { \ 84 | LOAD32H( (WordArray)[i], (ByteArray)+(i*4) ); \ 85 | } \ 86 | } 87 | #endif 88 | 89 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 90 | // TransformFunction 91 | // 92 | // Hash a single 512-bit block. This is the core of the algorithm 93 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 94 | static 95 | void 96 | TransformFunction 97 | ( 98 | uint32_t state[5], 99 | uint8_t const buffer[64] 100 | ) 101 | { 102 | uint32_t a; 103 | uint32_t b; 104 | uint32_t c; 105 | uint32_t d; 106 | uint32_t e; 107 | uint8_t workspace[64]; 108 | CHAR64LONG16* block = (CHAR64LONG16*) workspace; 109 | 110 | Load128BitsAsWords( block->l, buffer ); 111 | 112 | // Copy context->state[] to working vars 113 | a = state[0]; 114 | b = state[1]; 115 | c = state[2]; 116 | d = state[3]; 117 | e = state[4]; 118 | 119 | // 4 rounds of 20 operations each. Loop unrolled. 120 | R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); 121 | R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); 122 | R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); 123 | R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); 124 | R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); 125 | R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); 126 | R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); 127 | R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); 128 | R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); 129 | R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); 130 | R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); 131 | R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); 132 | R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); 133 | R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); 134 | R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); 135 | R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); 136 | R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); 137 | R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); 138 | R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); 139 | R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); 140 | 141 | // Add the working vars back into context.state[] 142 | state[0] += a; 143 | state[1] += b; 144 | state[2] += c; 145 | state[3] += d; 146 | state[4] += e; 147 | } 148 | 149 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 150 | // PUBLIC FUNCTIONS 151 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 152 | 153 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 154 | // Sha1Initialise 155 | // 156 | // Initialises an SHA1 Context. Use this to initialise/reset a context. 157 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 158 | void 159 | Sha1Initialise 160 | ( 161 | Sha1Context* Context // [out] 162 | ) 163 | { 164 | // SHA1 initialisation constants 165 | Context->State[0] = 0x67452301; 166 | Context->State[1] = 0xEFCDAB89; 167 | Context->State[2] = 0x98BADCFE; 168 | Context->State[3] = 0x10325476; 169 | Context->State[4] = 0xC3D2E1F0; 170 | Context->Count[0] = 0; 171 | Context->Count[1] = 0; 172 | } 173 | 174 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 175 | // Sha1Update 176 | // 177 | // Adds data to the SHA1 context. This will process the data and update the internal state of the context. Keep on 178 | // calling this function until all the data has been added. Then call Sha1Finalise to calculate the hash. 179 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 180 | void 181 | Sha1Update 182 | ( 183 | Sha1Context* Context, // [in out] 184 | void const* Buffer, // [in] 185 | uint32_t BufferSize // [in] 186 | ) 187 | { 188 | uint32_t i; 189 | uint32_t j; 190 | 191 | j = (Context->Count[0] >> 3) & 63; 192 | if( (Context->Count[0] += BufferSize << 3) < (BufferSize << 3) ) 193 | { 194 | Context->Count[1]++; 195 | } 196 | 197 | Context->Count[1] += (BufferSize >> 29); 198 | if( (j + BufferSize) > 63 ) 199 | { 200 | i = 64 - j; 201 | memcpy( &Context->Buffer[j], Buffer, i ); 202 | TransformFunction(Context->State, Context->Buffer); 203 | for( ; i + 63 < BufferSize; i += 64 ) 204 | { 205 | TransformFunction(Context->State, (uint8_t*)Buffer + i); 206 | } 207 | j = 0; 208 | } 209 | else 210 | { 211 | i = 0; 212 | } 213 | 214 | memcpy( &Context->Buffer[j], &((uint8_t*)Buffer)[i], BufferSize - i ); 215 | } 216 | 217 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 218 | // Sha1Finalise 219 | // 220 | // Performs the final calculation of the hash and returns the digest (20 byte buffer containing 160bit hash). After 221 | // calling this, Sha1Initialised must be used to reuse the context. 222 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 223 | void 224 | Sha1Finalise 225 | ( 226 | Sha1Context* Context, // [in out] 227 | SHA1_HASH* Digest // [in] 228 | ) 229 | { 230 | uint32_t i; 231 | uint8_t finalcount[8]; 232 | 233 | for( i=0; i<8; i++ ) 234 | { 235 | finalcount[i] = (unsigned char)((Context->Count[(i >= 4 ? 0 : 1)] 236 | >> ((3-(i & 3)) * 8) ) & 255); // Endian independent 237 | } 238 | Sha1Update( Context, (uint8_t*)"\x80", 1 ); 239 | while( (Context->Count[0] & 504) != 448 ) 240 | { 241 | Sha1Update( Context, (uint8_t*)"\0", 1 ); 242 | } 243 | 244 | Sha1Update( Context, finalcount, 8 ); // Should cause a Sha1TransformFunction() 245 | for( i=0; ibytes[i] = (uint8_t)((Context->State[i>>2] >> ((3-(i & 3)) * 8) ) & 255); 248 | } 249 | } 250 | 251 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 252 | // Sha1Calculate 253 | // 254 | // Combines Sha1Initialise, Sha1Update, and Sha1Finalise into one function. Calculates the SHA1 hash of the buffer. 255 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 256 | void 257 | Sha1Calculate 258 | ( 259 | void const* Buffer, // [in] 260 | uint32_t BufferSize, // [in] 261 | SHA1_HASH* Digest // [in] 262 | ) 263 | { 264 | Sha1Context context; 265 | 266 | Sha1Initialise( &context ); 267 | Sha1Update( &context, Buffer, BufferSize ); 268 | Sha1Finalise( &context, Digest ); 269 | } 270 | -------------------------------------------------------------------------------- /utcp/3rd/WjCryptLib_Sha1.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 2 | // WjCryptLib_Sha1 3 | // 4 | // Implementation of SHA1 hash function. 5 | // Original author: Steve Reid 6 | // Contributions by: James H. Brown , Saul Kravitz , 7 | // and Ralph Giles 8 | // Modified by WaterJuice retaining Public Domain license. 9 | // 10 | // This is free and unencumbered software released into the public domain - June 2013 waterjuice.org 11 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 12 | 13 | #pragma once 14 | 15 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 16 | // IMPORTS 17 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 18 | 19 | #include 20 | #include 21 | 22 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 23 | // TYPES 24 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 25 | 26 | // Sha1Context - This must be initialised using Sha1Initialised. Do not modify the contents of this structure directly. 27 | typedef struct 28 | { 29 | uint32_t State[5]; 30 | uint32_t Count[2]; 31 | uint8_t Buffer[64]; 32 | } Sha1Context; 33 | 34 | #define SHA1_HASH_SIZE ( 160 / 8 ) 35 | 36 | typedef struct 37 | { 38 | uint8_t bytes [SHA1_HASH_SIZE]; 39 | } SHA1_HASH; 40 | 41 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 42 | // PUBLIC FUNCTIONS 43 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 44 | 45 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 46 | // Sha1Initialise 47 | // 48 | // Initialises an SHA1 Context. Use this to initialise/reset a context. 49 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 50 | void 51 | Sha1Initialise 52 | ( 53 | Sha1Context* Context // [out] 54 | ); 55 | 56 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 57 | // Sha1Update 58 | // 59 | // Adds data to the SHA1 context. This will process the data and update the internal state of the context. Keep on 60 | // calling this function until all the data has been added. Then call Sha1Finalise to calculate the hash. 61 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 62 | void 63 | Sha1Update 64 | ( 65 | Sha1Context* Context, // [in out] 66 | void const* Buffer, // [in] 67 | uint32_t BufferSize // [in] 68 | ); 69 | 70 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 71 | // Sha1Finalise 72 | // 73 | // Performs the final calculation of the hash and returns the digest (20 byte buffer containing 160bit hash). After 74 | // calling this, Sha1Initialised must be used to reuse the context. 75 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 76 | void 77 | Sha1Finalise 78 | ( 79 | Sha1Context* Context, // [in out] 80 | SHA1_HASH* Digest // [in] 81 | ); 82 | 83 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 84 | // Sha1Calculate 85 | // 86 | // Combines Sha1Initialise, Sha1Update, and Sha1Finalise into one function. Calculates the SHA1 hash of the buffer. 87 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 88 | void 89 | Sha1Calculate 90 | ( 91 | void const* Buffer, // [in] 92 | uint32_t BufferSize, // [in] 93 | SHA1_HASH* Digest // [in] 94 | ); 95 | -------------------------------------------------------------------------------- /utcp/3rd/dl_list.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2013 dpull.com 3 | http://www.dpull.com 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 18 | THE SOFTWARE. 19 | ****************************************************************************/ 20 | 21 | #pragma once 22 | #include 23 | #include 24 | #include 25 | 26 | #ifndef CONTAINING_RECORD 27 | // FAR * 28 | // CONTAINING_RECORD( 29 | // IN PVOID address, 30 | // IN type, 31 | // IN field 32 | // ); 33 | #define CONTAINING_RECORD(address, type, field) ((type*)((char*)(address) - (char*)(&((type*)0)->field))) 34 | 35 | #endif 36 | 37 | struct dl_list_node 38 | { 39 | struct dl_list_node* next; 40 | struct dl_list_node* prev; 41 | }; 42 | 43 | static inline int dl_list_init(struct dl_list_node* head_list) 44 | { 45 | assert(!head_list->next); 46 | assert(!head_list->prev); 47 | head_list->next = head_list->prev = head_list; 48 | return true; 49 | } 50 | 51 | static inline int dl_list_empty(struct dl_list_node* head_list) 52 | { 53 | return head_list->next == head_list; 54 | } 55 | 56 | static inline int dl_list_push_after(struct dl_list_node* head_list, struct dl_list_node* node) 57 | { 58 | assert(!node->next); 59 | assert(!node->prev); 60 | 61 | struct dl_list_node* list_head = head_list; 62 | struct dl_list_node* front = list_head->next; 63 | node->next = front; 64 | node->prev = list_head; 65 | front->prev = node; 66 | list_head->next = node; 67 | return true; 68 | } 69 | 70 | static inline int dl_list_push_before(struct dl_list_node* head_list, struct dl_list_node* node) 71 | { 72 | assert(!node->next); 73 | assert(!node->prev); 74 | 75 | struct dl_list_node* list_head = head_list; 76 | struct dl_list_node* back = list_head->prev; 77 | node->next = list_head; 78 | node->prev = back; 79 | back->next = node; 80 | list_head->prev = node; 81 | return true; 82 | } 83 | 84 | static inline int dl_list_erase(struct dl_list_node* node) 85 | { 86 | struct dl_list_node* next = node->next; 87 | struct dl_list_node* prev = node->prev; 88 | prev->next = next; 89 | next->prev = prev; 90 | 91 | node->next = NULL; 92 | node->prev = NULL; 93 | return true; 94 | } 95 | 96 | static inline struct dl_list_node* dl_list_pop_next(struct dl_list_node* head_list) 97 | { 98 | struct dl_list_node* node = head_list->next; 99 | dl_list_erase(node); 100 | return node; 101 | } 102 | 103 | static inline struct dl_list_node* dl_list_pop_prev(struct dl_list_node* head_list) 104 | { 105 | struct dl_list_node* node = head_list->prev; 106 | dl_list_erase(node); 107 | return node; 108 | } 109 | 110 | static inline int dl_list_swap(struct dl_list_node* head_list1, struct dl_list_node* head_list2) 111 | { 112 | struct dl_list_node* next1 = head_list1->next; 113 | struct dl_list_node* prev1 = head_list1->prev; 114 | struct dl_list_node* next2 = head_list2->next; 115 | struct dl_list_node* prev2 = head_list2->prev; 116 | 117 | if (next2 != head_list2) 118 | { // is not empty 119 | head_list1->next = next2; 120 | head_list1->prev = prev2; 121 | 122 | next2->prev = head_list1; 123 | prev2->next = head_list1; 124 | } 125 | else 126 | { // if empty 127 | dl_list_init(head_list1); 128 | } 129 | 130 | if (next1 != head_list1) 131 | { // is not empty 132 | head_list2->next = next1; 133 | head_list2->prev = prev1; 134 | 135 | next1->prev = head_list2; 136 | prev1->next = head_list2; 137 | } 138 | else 139 | { // if empty 140 | dl_list_init(head_list2); 141 | } 142 | 143 | return true; 144 | } 145 | -------------------------------------------------------------------------------- /utcp/3rd/lcg_random.c: -------------------------------------------------------------------------------- 1 | // Linear congruential generator 2 | // https://en.wikipedia.org/wiki/Linear_congruential_generator 3 | 4 | #define IA 3877 5 | #define IC 29573 6 | 7 | static unsigned int s_random_seed = 42; 8 | 9 | void lcg_set_random_seed(unsigned int seed) 10 | { 11 | s_random_seed = seed; 12 | } 13 | 14 | unsigned int lcg_get_random_seed(void) 15 | { 16 | return s_random_seed; 17 | } 18 | 19 | unsigned int lcg_random(void) 20 | { 21 | s_random_seed = s_random_seed * IA + IC; 22 | return s_random_seed; 23 | } 24 | -------------------------------------------------------------------------------- /utcp/3rd/ringbuffer.c: -------------------------------------------------------------------------------- 1 | #include "ringbuffer.h" 2 | 3 | /** 4 | * @file 5 | * Implementation of ring buffer functions. 6 | */ 7 | 8 | void ring_buffer_init(ring_buffer_t *buffer) { 9 | buffer->tail_index = 0; 10 | buffer->head_index = 0; 11 | } 12 | 13 | void ring_buffer_queue(ring_buffer_t *buffer, ring_buffer_elem_t data) { 14 | /* Is buffer full? */ 15 | if(ring_buffer_is_full(buffer)) { 16 | /* Is going to overwrite the oldest byte */ 17 | /* Increase tail index */ 18 | buffer->tail_index = ((buffer->tail_index + 1) & RING_BUFFER_MASK); 19 | } 20 | 21 | /* Place data in buffer */ 22 | buffer->buffer[buffer->head_index] = data; 23 | buffer->head_index = ((buffer->head_index + 1) & RING_BUFFER_MASK); 24 | } 25 | 26 | uint8_t ring_buffer_dequeue(ring_buffer_t *buffer, ring_buffer_elem_t *data) { 27 | if(ring_buffer_is_empty(buffer)) { 28 | /* No items */ 29 | return 0; 30 | } 31 | 32 | *data = buffer->buffer[buffer->tail_index]; 33 | buffer->tail_index = ((buffer->tail_index + 1) & RING_BUFFER_MASK); 34 | return 1; 35 | } 36 | 37 | extern inline uint8_t ring_buffer_is_empty(ring_buffer_t *buffer); 38 | extern inline uint8_t ring_buffer_is_full(ring_buffer_t *buffer); 39 | extern inline ring_buffer_size_t ring_buffer_num_items(ring_buffer_t *buffer); 40 | -------------------------------------------------------------------------------- /utcp/3rd/ringbuffer.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /** 4 | * @file 5 | * Prototypes and structures for the ring buffer module. 6 | */ 7 | 8 | #ifndef RINGBUFFER_H 9 | #define RINGBUFFER_H 10 | 11 | #ifdef __cplusplus 12 | extern "C" 13 | { 14 | #endif 15 | 16 | /** 17 | * The size of a ring buffer. 18 | * Due to the design only RING_BUFFER_SIZE-1 items 19 | * can be contained in the buffer. 20 | * The buffer size must be a power of two. 21 | */ 22 | #define RING_BUFFER_SIZE 256 23 | 24 | #if (RING_BUFFER_SIZE & (RING_BUFFER_SIZE - 1)) != 0 25 | #error "RING_BUFFER_SIZE must be a power of two" 26 | #endif 27 | 28 | /** 29 | * The type which is used to hold the size 30 | * and the indicies of the buffer. 31 | * Must be able to fit \c RING_BUFFER_SIZE . 32 | */ 33 | typedef uint16_t ring_buffer_size_t; 34 | typedef uint32_t ring_buffer_elem_t; 35 | 36 | /** 37 | * Used as a modulo operator 38 | * as a % b = (a & (b − 1)) 39 | * where \c a is a positive index in the buffer and 40 | * \c b is the (power of two) size of the buffer. 41 | */ 42 | #define RING_BUFFER_MASK (RING_BUFFER_SIZE-1) 43 | 44 | /** 45 | * Simplifies the use of struct ring_buffer_t. 46 | */ 47 | typedef struct ring_buffer_t ring_buffer_t; 48 | 49 | /** 50 | * Structure which holds a ring buffer. 51 | * The buffer contains a buffer array 52 | * as well as metadata for the ring buffer. 53 | */ 54 | struct ring_buffer_t { 55 | /** Buffer memory. */ 56 | ring_buffer_elem_t buffer[RING_BUFFER_SIZE]; 57 | /** Index of tail. */ 58 | ring_buffer_size_t tail_index; 59 | /** Index of head. */ 60 | ring_buffer_size_t head_index; 61 | }; 62 | 63 | /** 64 | * Initializes the ring buffer pointed to by buffer. 65 | * This function can also be used to empty/reset the buffer. 66 | * @param buffer The ring buffer to initialize. 67 | */ 68 | void ring_buffer_init(ring_buffer_t *buffer); 69 | 70 | /** 71 | * Adds a byte to a ring buffer. 72 | * @param buffer The buffer in which the data should be placed. 73 | * @param data The byte to place. 74 | */ 75 | void ring_buffer_queue(ring_buffer_t* buffer, ring_buffer_elem_t data); 76 | 77 | /** 78 | * Returns the oldest byte in a ring buffer. 79 | * @param buffer The buffer from which the data should be returned. 80 | * @param data A pointer to the location at which the data should be placed. 81 | * @return 1 if data was returned; 0 otherwise. 82 | */ 83 | uint8_t ring_buffer_dequeue(ring_buffer_t* buffer, ring_buffer_elem_t* data); 84 | 85 | /** 86 | * Returns whether a ring buffer is empty. 87 | * @param buffer The buffer for which it should be returned whether it is empty. 88 | * @return 1 if empty; 0 otherwise. 89 | */ 90 | inline uint8_t ring_buffer_is_empty(ring_buffer_t *buffer) { 91 | return (buffer->head_index == buffer->tail_index); 92 | } 93 | 94 | /** 95 | * Returns whether a ring buffer is full. 96 | * @param buffer The buffer for which it should be returned whether it is full. 97 | * @return 1 if full; 0 otherwise. 98 | */ 99 | inline uint8_t ring_buffer_is_full(ring_buffer_t *buffer) { 100 | return ((buffer->head_index - buffer->tail_index) & RING_BUFFER_MASK) == RING_BUFFER_MASK; 101 | } 102 | 103 | /** 104 | * Returns the number of items in a ring buffer. 105 | * @param buffer The buffer for which the number of items should be returned. 106 | * @return The number of items in the ring buffer. 107 | */ 108 | inline ring_buffer_size_t ring_buffer_num_items(ring_buffer_t *buffer) { 109 | return ((buffer->head_index - buffer->tail_index) & RING_BUFFER_MASK); 110 | } 111 | 112 | #ifdef __cplusplus 113 | } 114 | #endif 115 | 116 | #endif /* RINGBUFFER_H */ 117 | -------------------------------------------------------------------------------- /utcp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | get_filename_component(CURRENT_SOURCE_DIR_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) 2 | set(LIB_NAME ${CURRENT_SOURCE_DIR_NAME}) # 工程名, 默认文件夹名 3 | 4 | file(GLOB_RECURSE SOURCE_FILES 5 | *.h 6 | *.hpp 7 | *.cpp 8 | *.cc 9 | *.c 10 | ) 11 | add_library(${LIB_NAME} STATIC ${SOURCE_FILES}) 12 | source_group_by_dir(SOURCE_FILES) 13 | set_property(TARGET ${LIB_NAME} PROPERTY FOLDER "utcp") 14 | -------------------------------------------------------------------------------- /utcp/bit_buffer.c: -------------------------------------------------------------------------------- 1 | #include "bit_buffer.h" 2 | #include 3 | #include 4 | 5 | const uint8_t GShift[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; 6 | const uint8_t GMask[8] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f}; 7 | 8 | static inline uint8_t Shift(uint8_t Cnt) 9 | { 10 | return (uint8_t)(1 << Cnt); 11 | } 12 | 13 | static inline int MAX(int a, int b) 14 | { 15 | return a > b ? a : b; 16 | } 17 | 18 | static bool allow_opt(struct bitbuf* buff, size_t bits_length) 19 | { 20 | return buff->num + bits_length <= buff->size; 21 | } 22 | 23 | static uint32_t CeilLogTwo(uint32_t x) 24 | { 25 | static const uint8_t log_2[256] = {/* log_2[i] = ceil(log2(i - 1)) */ 26 | 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 27 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 28 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 29 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 30 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 31 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}; 32 | int l = 0; 33 | x--; 34 | while (x >= 256) 35 | { 36 | l += 8; 37 | x >>= 8; 38 | } 39 | return l + log_2[x]; 40 | } 41 | 42 | static void appBitsCpy(uint8_t* Dest, int32_t DestBit, const uint8_t* Src, int32_t SrcBit, int32_t BitCount) 43 | { 44 | if (BitCount == 0) 45 | return; 46 | 47 | // Special case - always at least one bit to copy, 48 | // a maximum of 2 bytes to read, 2 to write - only touch bytes that are actually used. 49 | if (BitCount <= 8) 50 | { 51 | uint32_t DestIndex = DestBit / 8; 52 | uint32_t SrcIndex = SrcBit / 8; 53 | uint32_t LastDest = (DestBit + BitCount - 1) / 8; 54 | uint32_t LastSrc = (SrcBit + BitCount - 1) / 8; 55 | uint32_t ShiftSrc = SrcBit & 7; 56 | uint32_t ShiftDest = DestBit & 7; 57 | uint32_t FirstMask = 0xFF << ShiftDest; 58 | uint32_t LastMask = 0xFE << ((DestBit + BitCount - 1) & 7); // Pre-shifted left by 1. 59 | uint32_t Accu; 60 | 61 | if (SrcIndex == LastSrc) 62 | Accu = (Src[SrcIndex] >> ShiftSrc); 63 | else 64 | Accu = ((Src[SrcIndex] >> ShiftSrc) | (Src[LastSrc] << (8 - ShiftSrc))); 65 | 66 | if (DestIndex == LastDest) 67 | { 68 | uint32_t MultiMask = FirstMask & ~LastMask; 69 | Dest[DestIndex] = ((Dest[DestIndex] & ~MultiMask) | ((Accu << ShiftDest) & MultiMask)); 70 | } 71 | else 72 | { 73 | Dest[DestIndex] = (uint8_t)((Dest[DestIndex] & ~FirstMask) | ((Accu << ShiftDest) & FirstMask)); 74 | Dest[LastDest] = (uint8_t)((Dest[LastDest] & LastMask) | ((Accu >> (8 - ShiftDest)) & ~LastMask)); 75 | } 76 | 77 | return; 78 | } 79 | 80 | // Main copier, uses byte sized shifting. Minimum size is 9 bits, so at least 2 reads and 2 writes. 81 | uint32_t DestIndex = DestBit / 8; 82 | uint32_t FirstSrcMask = 0xFF << (DestBit & 7); 83 | uint32_t LastDest = (DestBit + BitCount) / 8; 84 | uint32_t LastSrcMask = 0xFF << ((DestBit + BitCount) & 7); 85 | uint32_t SrcIndex = SrcBit / 8; 86 | uint32_t LastSrc = (SrcBit + BitCount) / 8; 87 | int32_t ShiftCount = (DestBit & 7) - (SrcBit & 7); 88 | int32_t DestLoop = LastDest - DestIndex; 89 | int32_t SrcLoop = LastSrc - SrcIndex; 90 | uint32_t FullLoop; 91 | uint32_t BitAccu; 92 | 93 | // Lead-in needs to read 1 or 2 source bytes depending on alignment. 94 | if (ShiftCount >= 0) 95 | { 96 | FullLoop = MAX(DestLoop, SrcLoop); 97 | BitAccu = Src[SrcIndex] << ShiftCount; 98 | ShiftCount += 8; // prepare for the inner loop. 99 | } 100 | else 101 | { 102 | ShiftCount += 8; // turn shifts -7..-1 into +1..+7 103 | FullLoop = MAX(DestLoop, SrcLoop - 1); 104 | BitAccu = Src[SrcIndex] << ShiftCount; 105 | SrcIndex++; 106 | ShiftCount += 8; // Prepare for inner loop. 107 | BitAccu = (((uint32_t)Src[SrcIndex] << ShiftCount) + (BitAccu)) >> 8; 108 | } 109 | 110 | // Lead-in - first copy. 111 | Dest[DestIndex] = (uint8_t)((BitAccu & FirstSrcMask) | (Dest[DestIndex] & ~FirstSrcMask)); 112 | SrcIndex++; 113 | DestIndex++; 114 | 115 | // Fast inner loop. 116 | for (; FullLoop > 1; FullLoop--) 117 | { // ShiftCount ranges from 8 to 15 - all reads are relevant. 118 | BitAccu = (((uint32_t)Src[SrcIndex] << ShiftCount) + (BitAccu)) >> 8; // Copy in the new, discard the old. 119 | SrcIndex++; 120 | Dest[DestIndex] = (uint8_t)BitAccu; // Copy low 8 bits. 121 | DestIndex++; 122 | } 123 | 124 | // Lead-out. 125 | if (LastSrcMask != 0xFF) 126 | { 127 | if ((uint32_t)(SrcBit + BitCount - 1) / 8 == SrcIndex) // Last legal byte ? 128 | { 129 | BitAccu = (((uint32_t)Src[SrcIndex] << ShiftCount) + (BitAccu)) >> 8; 130 | } 131 | else 132 | { 133 | BitAccu = BitAccu >> 8; 134 | } 135 | 136 | Dest[DestIndex] = (uint8_t)((Dest[DestIndex] & LastSrcMask) | (BitAccu & ~LastSrcMask)); 137 | } 138 | } 139 | 140 | size_t bitbuf_num_bytes(struct bitbuf* buff) 141 | { 142 | return (buff->num + 7) >> 3; 143 | } 144 | 145 | size_t bitbuf_left_bits(struct bitbuf* buff) 146 | { 147 | return buff->size - buff->num; 148 | } 149 | 150 | size_t bitbuf_left_bytes(struct bitbuf* buff) 151 | { 152 | return (bitbuf_left_bits(buff) + 7) >> 3; 153 | } 154 | 155 | bool bitbuf_write_init(struct bitbuf* buff, uint8_t* buffer, size_t size) 156 | { 157 | memset(buffer, 0, size); 158 | buff->buffer = buffer; 159 | buff->size = size * 8; 160 | buff->num = 0; 161 | return true; 162 | } 163 | 164 | void bitbuf_write_reuse(struct bitbuf* buff, uint8_t* buffer, size_t num_bits, size_t size) 165 | { 166 | buff->buffer = buffer; 167 | buff->size = size * 8; 168 | buff->num = num_bits; 169 | } 170 | 171 | bool bitbuf_write_end(struct bitbuf* buff) 172 | { 173 | return bitbuf_write_bit(buff, 1); 174 | } 175 | 176 | bool bitbuf_write_bit(struct bitbuf* buff, uint8_t value) 177 | { 178 | if (!allow_opt(buff, 1)) 179 | return false; 180 | if (value) 181 | buff->buffer[buff->num >> 3] |= GShift[buff->num & 7]; 182 | buff->num++; 183 | return true; 184 | } 185 | 186 | bool bitbuf_write_bits(struct bitbuf* buff, const void* data, size_t bits_size) 187 | { 188 | if (!allow_opt(buff, bits_size)) 189 | return false; 190 | 191 | if (bits_size == 1) 192 | { 193 | if (((uint8_t*)data)[0] & 0x01) 194 | buff->buffer[buff->num >> 3] |= GShift[buff->num & 7]; 195 | buff->num++; 196 | } 197 | else 198 | { 199 | appBitsCpy(buff->buffer, (int)buff->num, (const uint8_t*)data, 0, (int)bits_size); 200 | buff->num += bits_size; 201 | } 202 | return true; 203 | } 204 | 205 | bool bitbuf_write_bytes(struct bitbuf* buff, const void* data, size_t size) 206 | { 207 | size_t bits_size = size * 8; 208 | if (!allow_opt(buff, bits_size)) 209 | return false; 210 | appBitsCpy(buff->buffer, (int)buff->num, (const uint8_t*)data, 0, (int)bits_size); 211 | buff->num += bits_size; 212 | return true; 213 | } 214 | 215 | bool bitbuf_write_int(struct bitbuf* buff, uint32_t value, uint32_t value_max) 216 | { 217 | assert(value_max >= 2); 218 | 219 | const int32_t LengthBits = CeilLogTwo(value_max); 220 | uint32_t WriteValue = value; 221 | 222 | if (WriteValue >= value_max) 223 | return false; 224 | 225 | if (!allow_opt(buff, LengthBits)) 226 | return false; 227 | 228 | uint32_t NewValue = 0; 229 | size_t LocalNum = buff->num; // Use local var to avoid LHS 230 | 231 | for (uint32_t Mask = 1; (NewValue + Mask) < value_max && Mask; Mask *= 2, LocalNum++) 232 | { 233 | if (WriteValue & Mask) 234 | { 235 | buff->buffer[LocalNum >> 3] += GShift[LocalNum & 7]; 236 | NewValue += Mask; 237 | } 238 | } 239 | 240 | buff->num = LocalNum; 241 | return true; 242 | } 243 | 244 | bool bitbuf_write_int_packed(struct bitbuf* buff, uint32_t InValue) 245 | { 246 | uint32_t Value = InValue; 247 | uint32_t BytesAsWords[5]; 248 | uint32_t ByteCount = 0; 249 | for (unsigned It = 0; (It == 0) | (Value != 0); ++It, Value = Value >> 7U) 250 | { 251 | const uint32_t NextByteIndicator = (Value & ~0x7FU) != 0; 252 | const uint32_t ByteAsWord = ((Value & 0x7FU) << 1U) | NextByteIndicator; 253 | BytesAsWords[ByteCount++] = ByteAsWord; 254 | } 255 | 256 | const int64_t LengthBits = ByteCount * 8; 257 | if (!allow_opt(buff, LengthBits)) 258 | return false; 259 | 260 | const uint32_t BitCountUsedInByte = buff->num & 7; 261 | const uint32_t BitCountLeftInByte = 8 - (buff->num & 7); 262 | const uint8_t DestMaskByte0 = (uint8_t)((1U << BitCountUsedInByte) - 1U); 263 | const uint8_t DestMaskByte1 = 0xFFU ^ DestMaskByte0; 264 | const bool bStraddlesTwoBytes = (BitCountUsedInByte != 0); 265 | uint8_t* Dest = buff->buffer + (buff->num >> 3U); 266 | 267 | buff->num += LengthBits; 268 | for (uint32_t ByteIt = 0; ByteIt != ByteCount; ++ByteIt) 269 | { 270 | const uint32_t ByteAsWord = BytesAsWords[ByteIt]; 271 | 272 | *Dest = (*Dest & DestMaskByte0) | (uint8_t)(ByteAsWord << BitCountUsedInByte); 273 | ++Dest; 274 | if (bStraddlesTwoBytes) 275 | *Dest = (*Dest & DestMaskByte1) | (uint8_t)(ByteAsWord >> BitCountLeftInByte); 276 | } 277 | return true; 278 | } 279 | 280 | bool bitbuf_write_int_wrapped(struct bitbuf* buff, uint32_t value, uint32_t value_max) 281 | { 282 | assert(value_max >= 2); 283 | 284 | const int32_t LengthBits = CeilLogTwo(value_max); 285 | 286 | if (!allow_opt(buff, LengthBits)) 287 | return false; 288 | 289 | uint32_t NewValue = 0; 290 | 291 | for (uint32_t Mask = 1; NewValue + Mask < value_max && Mask; Mask *= 2, buff->num++) 292 | { 293 | if (value & Mask) 294 | { 295 | buff->buffer[buff->num >> 3] += GShift[buff->num & 7]; 296 | NewValue += Mask; 297 | } 298 | } 299 | return true; 300 | } 301 | 302 | bool bitbuf_write_int_byte_order(struct bitbuf* buff, uint32_t value) 303 | { 304 | return bitbuf_write_bytes(buff, &value, sizeof(value)); 305 | } 306 | 307 | bool bitbuf_read_init(struct bitbuf* buff, const uint8_t* data, size_t len) 308 | { 309 | if (len == 0) 310 | return false; 311 | 312 | uint8_t LastByte = data[len - 1]; 313 | if (LastByte == 0) 314 | return false; 315 | 316 | size_t CountBits = len * 8; 317 | CountBits--; 318 | while (!(LastByte & 0x80)) 319 | { 320 | LastByte *= 2; 321 | CountBits--; 322 | } 323 | 324 | buff->buffer = (uint8_t*)data; 325 | buff->size = CountBits; 326 | buff->num = 0; 327 | return true; 328 | } 329 | 330 | bool bitbuf_read_bit(struct bitbuf* buff, uint8_t* value) 331 | { 332 | if (!allow_opt(buff, 1)) 333 | return false; 334 | uint8_t Bit = !!(buff->buffer[(int32_t)(buff->num >> 3)] & Shift(buff->num & 7)); 335 | buff->num++; 336 | 337 | *value = Bit; 338 | return true; 339 | } 340 | 341 | bool bitbuf_read_bits(struct bitbuf* buff, void* buffer, size_t bits_size) 342 | { 343 | if (!allow_opt(buff, bits_size)) 344 | return false; 345 | 346 | if (bits_size == 1) 347 | { 348 | ((uint8_t*)buffer)[0] = 0; 349 | if (buff->buffer[(int)(buff->num >> 3)] & Shift(buff->num & 7)) 350 | ((uint8_t*)buffer)[0] |= 0x01; 351 | buff->num++; 352 | } 353 | else if (bits_size != 0) 354 | { 355 | ((uint8_t*)buffer)[((bits_size + 7) >> 3) - 1] = 0; 356 | appBitsCpy((uint8_t*)buffer, 0, buff->buffer, (int32_t)buff->num, (int32_t)bits_size); 357 | buff->num += bits_size; 358 | } 359 | return true; 360 | } 361 | 362 | bool bitbuf_read_bytes(struct bitbuf* buff, void* buffer, size_t size) 363 | { 364 | return bitbuf_read_bits(buff, buffer, size * 8); 365 | } 366 | 367 | // FBitReader::SerializeInt 368 | bool bitbuf_read_int(struct bitbuf* buff, uint32_t* value, uint32_t value_max) 369 | { 370 | // Use local variable to avoid Load-Hit-Store 371 | uint32_t Value = 0; 372 | size_t LocalPos = buff->num; 373 | const size_t LocalNum = buff->size; 374 | 375 | for (uint32_t Mask = 1; (Value + Mask) < value_max && Mask; Mask *= 2, LocalPos++) 376 | { 377 | if (LocalPos >= LocalNum) 378 | { 379 | return false; 380 | } 381 | 382 | if (buff->buffer[(int32_t)(LocalPos >> 3)] & Shift(LocalPos & 7)) 383 | { 384 | Value |= Mask; 385 | } 386 | } 387 | 388 | // Now write back 389 | buff->num = LocalPos; 390 | *value = Value; 391 | return true; 392 | } 393 | 394 | // FBitReader::SerializeIntPacked 395 | bool bitbuf_read_int_packed(struct bitbuf* buff, uint32_t* value) 396 | { 397 | const uint8_t* Src = buff->buffer + (buff->num >> 3U); 398 | const uint32_t BitCountUsedInByte = buff->num & 7; 399 | const uint32_t BitCountLeftInByte = 8 - (buff->num & 7); 400 | const uint8_t SrcMaskByte0 = (uint8_t)((1U << BitCountLeftInByte) - 1U); 401 | const uint8_t SrcMaskByte1 = (uint8_t)((1U << BitCountUsedInByte) - 1U); 402 | const uint32_t NextSrcIndex = (BitCountUsedInByte != 0); 403 | 404 | uint32_t Value = 0; 405 | for (unsigned It = 0, ShiftCount = 0; It < 5; ++It, ShiftCount += 7) 406 | { 407 | if (buff->num + 8 > buff->size) 408 | { 409 | return false; 410 | } 411 | 412 | buff->num += 8; 413 | 414 | const uint8_t Byte = ((Src[0] >> BitCountUsedInByte) & SrcMaskByte0) | ((Src[NextSrcIndex] & SrcMaskByte1) << (BitCountLeftInByte & 7)); 415 | const uint8_t NextByteIndicator = Byte & 1; 416 | const uint32_t ByteAsWord = Byte >> 1U; 417 | Value = (ByteAsWord << ShiftCount) | Value; 418 | ++Src; 419 | 420 | if (!NextByteIndicator) 421 | { 422 | break; 423 | } 424 | } 425 | 426 | *value = Value; 427 | return true; 428 | } 429 | 430 | bool bitbuf_read_int_byte_order(struct bitbuf* buff, uint32_t* value) 431 | { 432 | return bitbuf_read_bytes(buff, value, sizeof(*value)); 433 | } 434 | -------------------------------------------------------------------------------- /utcp/bit_buffer.h: -------------------------------------------------------------------------------- 1 | // Copyright DPULL, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | struct bitbuf 10 | { 11 | uint8_t* buffer; 12 | size_t size; 13 | size_t num; 14 | }; 15 | 16 | // TODO: bitbuf_write_int_byte_order & bitbuf_read_int_byte_order 大小端问题 17 | 18 | size_t bitbuf_num_bytes(struct bitbuf* buff); 19 | size_t bitbuf_left_bits(struct bitbuf* buff); 20 | size_t bitbuf_left_bytes(struct bitbuf* buff); 21 | 22 | bool bitbuf_write_init(struct bitbuf* buff, uint8_t* buffer, size_t size); 23 | void bitbuf_write_reuse(struct bitbuf* buff, uint8_t* buffer, size_t num_bits, size_t size); 24 | bool bitbuf_write_end(struct bitbuf* buff); 25 | bool bitbuf_write_bit(struct bitbuf* buff, uint8_t value); 26 | bool bitbuf_write_bits(struct bitbuf* buff, const void* data, size_t bits_size); 27 | bool bitbuf_write_bytes(struct bitbuf* buff, const void* data, size_t size); 28 | bool bitbuf_write_int(struct bitbuf* buff, uint32_t value, uint32_t value_max); 29 | bool bitbuf_write_int_packed(struct bitbuf* buff, uint32_t value); 30 | bool bitbuf_write_int_wrapped(struct bitbuf* buff, uint32_t value, uint32_t value_max); 31 | bool bitbuf_write_int_byte_order(struct bitbuf* buff, uint32_t value); 32 | 33 | bool bitbuf_read_init(struct bitbuf* buff, const uint8_t* data, size_t len); 34 | bool bitbuf_read_bit(struct bitbuf* buff, uint8_t* value); 35 | bool bitbuf_read_bits(struct bitbuf* buff, void* buffer, size_t bits_size); 36 | bool bitbuf_read_bytes(struct bitbuf* buff, void* buffer, size_t size); 37 | bool bitbuf_read_int(struct bitbuf* buff, uint32_t* value, uint32_t value_max); 38 | bool bitbuf_read_int_packed(struct bitbuf* buff, uint32_t* value); 39 | bool bitbuf_read_int_byte_order(struct bitbuf* buff, uint32_t* value); 40 | -------------------------------------------------------------------------------- /utcp/sha1.c: -------------------------------------------------------------------------------- 1 | #include "3rd/WjCryptLib_Sha1.h" 2 | #include "utcp_utils.h" 3 | #include 4 | #include 5 | 6 | /** 7 | * Calculate the hash on a single block and return it 8 | * 9 | * @param Data Input data to hash 10 | * @param DataSize Size of the Data block 11 | * @param OutHash Resulting hash value (20 byte buffer) 12 | */ 13 | // FSHA1::HashBuffer 14 | static void hash_buffer(const void* Data, uint64_t DataSize, uint8_t* OutHash) 15 | { 16 | // do an atomic hash operation 17 | Sha1Context sha1Context; 18 | SHA1_HASH* sha1Hash = (SHA1_HASH*)OutHash; 19 | 20 | Sha1Initialise(&sha1Context); 21 | Sha1Update(&sha1Context, Data, (uint32_t)DataSize); 22 | Sha1Finalise(&sha1Context, sha1Hash); 23 | } 24 | 25 | /** 26 | * Generate the HMAC (Hash-based Message Authentication Code) for a block of data. 27 | * https://en.wikipedia.org/wiki/Hash-based_message_authentication_code 28 | * 29 | * @param Key The secret key to be used when generating the HMAC 30 | * @param KeySize The size of the key 31 | * @param Data Input data to hash 32 | * @param DataSize Size of the Data block 33 | * @param OutHash Resulting hash value (20 byte buffer) 34 | */ 35 | 36 | // FSHA1::HMACBuffer 37 | void sha1_hmac_buffer(const void* Key, uint32_t KeySize, const void* Data, uint64_t DataSize, uint8_t* OutHash) 38 | { 39 | enum 40 | { 41 | BlockSize = 64, 42 | HashSize = 20, 43 | }; 44 | 45 | uint8_t FinalKey[BlockSize]; 46 | 47 | // Fit 'Key' into a BlockSize-aligned 'FinalKey' value 48 | if (KeySize > BlockSize) 49 | { 50 | hash_buffer(Key, KeySize, FinalKey); 51 | 52 | memset(FinalKey + HashSize, 0, BlockSize - HashSize); 53 | } 54 | else if (KeySize < BlockSize) 55 | { 56 | memcpy(FinalKey, Key, KeySize); 57 | memset(FinalKey + KeySize, 0, BlockSize - KeySize); 58 | } 59 | else 60 | { 61 | memcpy(FinalKey, Key, KeySize); 62 | } 63 | 64 | uint8_t OKeyPad[BlockSize]; 65 | uint8_t IKeyPad[BlockSize]; 66 | 67 | for (int32_t i = 0; i < BlockSize; i++) 68 | { 69 | OKeyPad[i] = 0x5C ^ FinalKey[i]; 70 | IKeyPad[i] = 0x36 ^ FinalKey[i]; 71 | } 72 | 73 | // Start concatenating/hashing the pads/data etc: Hash(OKeyPad + Hash(IKeyPad + Data)) 74 | //_countof(IKeyPad) + DataSize 75 | uint8_t IKeyPad_Data[1024]; 76 | assert(_countof(IKeyPad_Data) >= _countof(IKeyPad) + DataSize); 77 | 78 | memcpy(IKeyPad_Data, IKeyPad, _countof(IKeyPad)); 79 | memcpy(IKeyPad_Data + _countof(IKeyPad), Data, DataSize); 80 | 81 | uint8_t IKeyPad_Data_Hash[HashSize]; 82 | 83 | hash_buffer(IKeyPad_Data, _countof(IKeyPad) + DataSize, IKeyPad_Data_Hash); 84 | 85 | uint8_t OKeyPad_IHash[_countof(OKeyPad) + HashSize]; 86 | 87 | memcpy(OKeyPad_IHash, OKeyPad, _countof(OKeyPad)); 88 | memcpy(OKeyPad_IHash + _countof(OKeyPad), IKeyPad_Data_Hash, HashSize); 89 | 90 | // Output the final hash 91 | hash_buffer(OKeyPad_IHash, _countof(OKeyPad_IHash), OutHash); 92 | } 93 | -------------------------------------------------------------------------------- /utcp/utcp.c: -------------------------------------------------------------------------------- 1 | #include "utcp.h" 2 | #include "bit_buffer.h" 3 | #include "utcp_channel.h" 4 | #include "utcp_handshake.h" 5 | #include "utcp_packet.h" 6 | #include "utcp_packet_notify.h" 7 | #include "utcp_sequence_number.h" 8 | #include "utcp_utils.h" 9 | #include 10 | #include 11 | 12 | #define KeepAliveTime (int)(0.2 * 1000) 13 | 14 | static struct utcp_config utcp_config = {0}; 15 | 16 | struct utcp_config* utcp_get_config() 17 | { 18 | return &utcp_config; 19 | } 20 | 21 | void utcp_add_elapsed_time(int64_t delta_time_ns) 22 | { 23 | utcp_config.ElapsedTime += (delta_time_ns / 1000); 24 | } 25 | 26 | struct utcp_listener* utcp_listener_create() 27 | { 28 | return (struct utcp_listener*)utcp_realloc(NULL, sizeof(struct utcp_listener)); 29 | } 30 | 31 | void utcp_listener_destroy(struct utcp_listener* fd) 32 | { 33 | if (fd) 34 | { 35 | utcp_realloc(fd, 0); 36 | } 37 | } 38 | 39 | void utcp_listener_init(struct utcp_listener* fd, void* userdata) 40 | { 41 | memset(fd, 0, sizeof(*fd)); 42 | fd->userdata = userdata; 43 | fd->ActiveSecret = 255; 44 | 45 | utcp_listener_update_secret(fd, NULL); 46 | } 47 | 48 | // StatelessConnectHandlerComponent::UpdateSecret 49 | void utcp_listener_update_secret(struct utcp_listener* fd, uint8_t special_secret[64] /* = NULL*/) 50 | { 51 | static_assert(SECRET_BYTE_SIZE == 64, "SECRET_BYTE_SIZE == 64"); 52 | 53 | fd->LastSecretUpdateTimestamp = utcp_gettime(); 54 | 55 | // On first update, update both secrets 56 | if (fd->ActiveSecret == 255) 57 | { 58 | uint8_t* CurArray = fd->HandshakeSecret[1]; 59 | for (int i = 0; i < SECRET_BYTE_SIZE; i++) 60 | { 61 | CurArray[i] = utcp_rand() % 255; 62 | } 63 | 64 | fd->ActiveSecret = 0; 65 | } 66 | else 67 | { 68 | fd->ActiveSecret = (uint8_t)!fd->ActiveSecret; 69 | } 70 | 71 | uint8_t* CurArray = fd->HandshakeSecret[fd->ActiveSecret]; 72 | if (special_secret) 73 | { 74 | memcpy(CurArray, special_secret, SECRET_BYTE_SIZE); 75 | } 76 | else 77 | { 78 | for (int i = 0; i < SECRET_BYTE_SIZE; i++) 79 | { 80 | CurArray[i] = utcp_rand() % 255; 81 | } 82 | } 83 | } 84 | 85 | int utcp_listener_incoming(struct utcp_listener* fd, const char* address, const uint8_t* buffer, int len) 86 | { 87 | utcp_dump("listener", "incoming", buffer, len); 88 | return process_connectionless_packet(fd, address, buffer, len); 89 | } 90 | 91 | void utcp_listener_accept(struct utcp_listener* listener, struct utcp_connection* conn, bool reconnect) 92 | { 93 | conn->LastReceiveRealtime = utcp_gettime_ms(); 94 | conn->LastSendTime = utcp_gettime_ms(); 95 | if (!reconnect) 96 | { 97 | assert(conn->challenge_data == NULL); 98 | memcpy(conn->AuthorisedCookie, listener->AuthorisedCookie, sizeof(conn->AuthorisedCookie)); 99 | utcp_sequence_init(conn, listener->LastClientSequence, listener->LastServerSequence); 100 | } 101 | } 102 | 103 | struct utcp_connection* utcp_connection_create() 104 | { 105 | return (struct utcp_connection*)utcp_realloc(NULL, sizeof(struct utcp_connection)); 106 | } 107 | 108 | void utcp_connection_destroy(struct utcp_connection* fd) 109 | { 110 | if (fd) 111 | { 112 | utcp_realloc(fd, 0); 113 | } 114 | } 115 | 116 | void utcp_init(struct utcp_connection* fd, void* userdata) 117 | { 118 | memset(fd, 0, sizeof(*fd)); 119 | fd->userdata = userdata; 120 | } 121 | 122 | void utcp_uninit(struct utcp_connection* fd) 123 | { 124 | utcp_mark_close(fd, Cleanup); 125 | utcp_channels_uninit(&fd->channels); 126 | if (fd->challenge_data) 127 | { 128 | utcp_realloc(fd->challenge_data, 0); 129 | fd->challenge_data = NULL; 130 | } 131 | } 132 | 133 | void utcp_connect(struct utcp_connection* fd) 134 | { 135 | handshake_begin(fd); 136 | } 137 | 138 | // UNetConnection::ReceivedRawPacket 139 | bool utcp_incoming(struct utcp_connection* fd, uint8_t* buffer, int len) 140 | { 141 | utcp_dump(fd->debug_name, "incoming", buffer, len); 142 | 143 | struct bitbuf bitbuf; 144 | if (!bitbuf_read_init(&bitbuf, buffer, len)) 145 | { 146 | utcp_log(Warning, "[%s]Received packet with 0's in last byte of packet", fd->debug_name); 147 | utcp_mark_close(fd, ZeroLastByte); 148 | return false; 149 | } 150 | 151 | int ret = handshake_incoming(fd, &bitbuf); 152 | if (ret != 0) 153 | { 154 | if (!is_client(fd)) 155 | utcp_log(Warning, "[%s]handshake_incoming failed, ret=%d", fd->debug_name, ret); 156 | utcp_mark_close(fd, PacketHandlerIncomingError); 157 | return false; 158 | } 159 | 160 | size_t left_bits = bitbuf_left_bits(&bitbuf); 161 | if (left_bits == 0) 162 | return true; 163 | 164 | fd->LastReceiveRealtime = utcp_gettime_ms(); 165 | 166 | bitbuf.size--; 167 | ret = ReceivedPacket(fd, &bitbuf); 168 | if (!ret) 169 | return false; 170 | 171 | left_bits = bitbuf_left_bits(&bitbuf); 172 | return left_bits == 0; 173 | } 174 | 175 | int utcp_update(struct utcp_connection* fd) 176 | { 177 | if (is_connected(fd)) 178 | { 179 | int64_t now = utcp_gettime_ms(); 180 | if (now - fd->LastReceiveRealtime > UTCP_CONNECT_TIMEOUT) 181 | utcp_mark_close(fd, ConnectionTimeout); 182 | } 183 | else 184 | { 185 | handshake_update(fd); 186 | } 187 | 188 | utcp_delay_close_channel(&fd->channels); 189 | 190 | if (!fd->bClose) 191 | return 0; 192 | 193 | utcp_on_disconnect(fd, fd->CloseReason); 194 | return -1; 195 | } 196 | 197 | int32_t utcp_peep_packet_id(struct utcp_connection* fd, uint8_t* buffer, int len) 198 | { 199 | struct bitbuf bitbuf; 200 | if (!bitbuf_read_init(&bitbuf, buffer, len)) 201 | return -1; 202 | 203 | uint8_t SessionID, ClientID, bHandshakePacket; 204 | if (!read_packet_header(&bitbuf, LastRemoteHandshakeVersion(), &SessionID, &ClientID, &bHandshakePacket)) 205 | return -2; 206 | if (bHandshakePacket) 207 | return 0; 208 | return PeekPacketId(fd, &bitbuf); 209 | } 210 | 211 | int32_t utcp_expect_packet_id(struct utcp_connection* fd) 212 | { 213 | return fd->InPacketId + 1; 214 | } 215 | 216 | int32_t utcp_send_bunch(struct utcp_connection* fd, struct utcp_bunch* bunch) 217 | { 218 | int32_t packet_id = SendRawBunch(fd, bunch); 219 | if (packet_id >= 0) 220 | { 221 | utcp_log(Verbose, "[%s]send bunch, bOpen=%d, bClose=%d, NameIndex=%d, ChIndex=%d, NumBits=%d, PacketId=%d", fd->debug_name, bunch->bOpen, bunch->bClose, bunch->NameIndex, 222 | bunch->ChIndex, bunch->DataBitsLen, packet_id); 223 | return packet_id; 224 | } 225 | 226 | utcp_log(Warning, "[%s]send bunch failed:%d", packet_id); 227 | return PACKET_ID_INDEX_NONE; 228 | } 229 | 230 | // UNetConnection::FlushNet 231 | int utcp_send_flush(struct utcp_connection* fd) 232 | { 233 | if (!is_connected(fd)) 234 | return 0; 235 | 236 | int64_t now = utcp_gettime_ms(); 237 | if (fd->SendBufferBitsNum == 0 && !fd->HasDirtyAcks && (now - fd->LastSendTime) < KeepAliveTime) 238 | return 0; 239 | 240 | if (fd->SendBufferBitsNum == 0) 241 | WriteBitsToSendBuffer(fd, NULL, 0); 242 | 243 | struct bitbuf bitbuf; 244 | bitbuf_write_reuse(&bitbuf, fd->SendBuffer, fd->SendBufferBitsNum, sizeof(fd->SendBuffer)); 245 | 246 | // Write the UNetConnection-level termination bit 247 | bitbuf_write_end(&bitbuf); 248 | 249 | // if we update ack, we also update received ack associated with outgoing seq 250 | // so we know how many ack bits we need to write (which is updated in received packet) 251 | WritePacketHeader(fd, &bitbuf); 252 | 253 | bitbuf_write_end(&bitbuf); 254 | utcp_raw_send(fd, bitbuf.buffer, bitbuf_num_bytes(&bitbuf)); 255 | 256 | memset(fd->SendBuffer, 0, sizeof(fd->SendBuffer)); 257 | fd->SendBufferBitsNum = 0; 258 | 259 | packet_notify_commit_and_inc_outseq(&fd->packet_notify); 260 | fd->LastSendTime = now; 261 | fd->OutPacketId++; 262 | 263 | return 0; 264 | } 265 | 266 | bool utcp_send_would_block(struct utcp_connection* fd, int count) 267 | { 268 | return fd->OutPacketId - fd->OutAckPacketId + count >= (MaxSequenceHistoryLength - 2); 269 | } 270 | 271 | void utcp_mark_close(struct utcp_connection* fd, uint8_t close_reason) 272 | { 273 | if (fd->bClose) 274 | return; 275 | fd->bClose = true; 276 | fd->CloseReason = close_reason; 277 | utcp_log(Warning, "[%s]utcp_mark_close, type=%hhu", fd->debug_name, close_reason); 278 | } 279 | -------------------------------------------------------------------------------- /utcp/utcp.h: -------------------------------------------------------------------------------- 1 | // Copyright DPULL, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | #include "utcp_def.h" 5 | #include 6 | #include 7 | 8 | #ifdef __cplusplus 9 | extern "C" 10 | { 11 | #endif 12 | 13 | // global API 14 | struct utcp_config* utcp_get_config(); 15 | void utcp_add_elapsed_time(int64_t delta_time_ns); 16 | 17 | // listener API 18 | struct utcp_listener* utcp_listener_create(); 19 | void utcp_listener_destroy(struct utcp_listener* fd); 20 | 21 | void utcp_listener_init(struct utcp_listener* fd, void* userdata); 22 | void utcp_listener_update_secret(struct utcp_listener* fd, uint8_t special_secret[64] /* = NULL*/); 23 | int utcp_listener_incoming(struct utcp_listener* fd, const char* address, const uint8_t* buffer, int len); 24 | void utcp_listener_accept(struct utcp_listener* listener, struct utcp_connection* conn, bool reconnect); 25 | 26 | // connection API 27 | struct utcp_connection* utcp_connection_create(); 28 | void utcp_connection_destroy(struct utcp_connection* fd); 29 | 30 | void utcp_init(struct utcp_connection* fd, void* userdata); 31 | void utcp_uninit(struct utcp_connection* fd); 32 | 33 | void utcp_connect(struct utcp_connection* fd); 34 | 35 | bool utcp_incoming(struct utcp_connection* fd, uint8_t* buffer, int len); 36 | int utcp_update(struct utcp_connection* fd); 37 | 38 | int32_t utcp_peep_packet_id(struct utcp_connection* fd, uint8_t* buffer, int len); 39 | int32_t utcp_expect_packet_id(struct utcp_connection* fd); 40 | 41 | int32_t utcp_send_bunch(struct utcp_connection* fd, struct utcp_bunch* bunch); 42 | int utcp_send_flush(struct utcp_connection* fd); 43 | bool utcp_send_would_block(struct utcp_connection* fd, int count); 44 | 45 | void utcp_mark_close(struct utcp_connection* fd, uint8_t close_reason); 46 | 47 | #ifdef __cplusplus 48 | } 49 | #endif 50 | -------------------------------------------------------------------------------- /utcp/utcp_bunch.c: -------------------------------------------------------------------------------- 1 | #include "utcp_bunch.h" 2 | #include "bit_buffer.h" 3 | #include "utcp_def_internal.h" 4 | #include 5 | #include 6 | 7 | #define BITBUF_READ_BIT(VAR) \ 8 | do \ 9 | { \ 10 | uint8_t __VALUE; \ 11 | if (!bitbuf_read_bit(bitbuf, &__VALUE)) \ 12 | return false; \ 13 | VAR = __VALUE; \ 14 | } while (0); 15 | 16 | #define BITBUF_WRITE_BIT(VAR) \ 17 | do \ 18 | { \ 19 | if (!bitbuf_write_bit(bitbuf, VAR)) \ 20 | return false; \ 21 | } while (0); 22 | 23 | enum 24 | { 25 | EChannelCloseReasonMAX = 15 26 | }; 27 | 28 | // UNetConnection::ReceivedPacket 29 | bool utcp_bunch_read(struct utcp_bunch* utcp_bunch, struct bitbuf* bitbuf) 30 | { 31 | memset(utcp_bunch, 0, sizeof(*utcp_bunch)); 32 | uint8_t bControl; 33 | 34 | BITBUF_READ_BIT(bControl); 35 | 36 | if (bControl) 37 | { 38 | BITBUF_READ_BIT(utcp_bunch->bOpen); 39 | BITBUF_READ_BIT(utcp_bunch->bClose); 40 | } 41 | 42 | utcp_bunch->CloseReason = 0; 43 | if (utcp_bunch->bClose) 44 | { 45 | uint32_t CloseReason; 46 | if (!bitbuf_read_int(bitbuf, &CloseReason, EChannelCloseReasonMAX)) 47 | return false; 48 | utcp_bunch->CloseReason = CloseReason; 49 | } 50 | BITBUF_READ_BIT(utcp_bunch->bIsReplicationPaused); 51 | BITBUF_READ_BIT(utcp_bunch->bReliable); 52 | 53 | uint32_t ChIndex; 54 | if (!bitbuf_read_int_packed(bitbuf, &ChIndex)) 55 | return false; 56 | 57 | utcp_bunch->ChIndex = ChIndex; 58 | 59 | BITBUF_READ_BIT(utcp_bunch->bHasPackageMapExports); 60 | BITBUF_READ_BIT(utcp_bunch->bHasMustBeMappedGUIDs); 61 | BITBUF_READ_BIT(utcp_bunch->bPartial); 62 | 63 | utcp_bunch->ChSequence = 0; 64 | if (utcp_bunch->bReliable) 65 | { 66 | uint32_t ChSequence = 0; 67 | if (!bitbuf_read_int(bitbuf, (uint32_t*)&utcp_bunch->ChSequence, UTCP_MAX_CHSEQUENCE)) 68 | { 69 | return false; 70 | } 71 | } 72 | 73 | utcp_bunch->bPartialInitial = 0; 74 | utcp_bunch->bPartialFinal = 0; 75 | if (utcp_bunch->bPartial) 76 | { 77 | BITBUF_READ_BIT(utcp_bunch->bPartialInitial); 78 | BITBUF_READ_BIT(utcp_bunch->bPartialFinal); 79 | } 80 | 81 | if (utcp_bunch->bReliable || utcp_bunch->bOpen) 82 | { 83 | uint8_t bHardcoded; 84 | BITBUF_READ_BIT(bHardcoded); 85 | if (!bHardcoded) 86 | { 87 | // TODO 暂时不支持 88 | assert(false); 89 | return false; 90 | } 91 | 92 | uint32_t NameIndex = 0; 93 | if (!bitbuf_read_int_packed(bitbuf, &NameIndex)) 94 | return false; 95 | utcp_bunch->NameIndex = NameIndex; 96 | } 97 | 98 | uint32_t BunchDataBits; 99 | if (!bitbuf_read_int(bitbuf, &BunchDataBits, UTCP_MAX_PACKET * 8)) 100 | return false; 101 | utcp_bunch->DataBitsLen = BunchDataBits; 102 | if (!bitbuf_read_bits(bitbuf, utcp_bunch->Data, utcp_bunch->DataBitsLen)) 103 | return false; 104 | return true; 105 | } 106 | 107 | // UNetConnection::SendRawBunch 108 | bool utcp_bunch_write_header(const struct utcp_bunch* utcp_bunch, struct bitbuf* bitbuf) 109 | { 110 | const bool bIsOpenOrClose = utcp_bunch->bOpen || utcp_bunch->bClose; 111 | const bool bIsOpenOrReliable = utcp_bunch->bOpen || utcp_bunch->bReliable; 112 | 113 | BITBUF_WRITE_BIT(bIsOpenOrClose); 114 | if (bIsOpenOrClose) 115 | { 116 | BITBUF_WRITE_BIT(utcp_bunch->bOpen); 117 | BITBUF_WRITE_BIT(utcp_bunch->bClose); 118 | 119 | if (utcp_bunch->bClose) 120 | { 121 | if (!bitbuf_write_int(bitbuf, utcp_bunch->CloseReason, EChannelCloseReasonMAX)) 122 | return false; 123 | } 124 | } 125 | 126 | BITBUF_WRITE_BIT(utcp_bunch->bIsReplicationPaused); 127 | BITBUF_WRITE_BIT(utcp_bunch->bReliable); 128 | 129 | if (!bitbuf_write_int_packed(bitbuf, utcp_bunch->ChIndex)) 130 | return false; 131 | 132 | BITBUF_WRITE_BIT(utcp_bunch->bHasPackageMapExports); 133 | BITBUF_WRITE_BIT(utcp_bunch->bHasMustBeMappedGUIDs); 134 | BITBUF_WRITE_BIT(utcp_bunch->bPartial); 135 | 136 | if (utcp_bunch->bReliable) 137 | { 138 | if (!bitbuf_write_int_wrapped(bitbuf, utcp_bunch->ChSequence, UTCP_MAX_CHSEQUENCE)) 139 | return false; 140 | } 141 | 142 | if (utcp_bunch->bPartial) 143 | { 144 | BITBUF_WRITE_BIT(utcp_bunch->bPartialInitial); 145 | BITBUF_WRITE_BIT(utcp_bunch->bPartialFinal); 146 | } 147 | 148 | if (bIsOpenOrReliable) 149 | { 150 | BITBUF_WRITE_BIT(1); 151 | if (!bitbuf_write_int_packed(bitbuf, utcp_bunch->NameIndex)) 152 | return false; 153 | } 154 | 155 | if (!bitbuf_write_int_wrapped(bitbuf, utcp_bunch->DataBitsLen, UTCP_MAX_PACKET * 8)) 156 | return false; 157 | return true; 158 | } 159 | -------------------------------------------------------------------------------- /utcp/utcp_bunch.h: -------------------------------------------------------------------------------- 1 | // Copyright DPULL, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "bit_buffer.h" 6 | #include "utcp_def.h" 7 | #include 8 | #include 9 | #include 10 | 11 | bool utcp_bunch_read(struct utcp_bunch* utcp_bunch, struct bitbuf* bitbuf); 12 | bool utcp_bunch_write_header(const struct utcp_bunch* utcp_bunch, struct bitbuf* bitbuf); 13 | -------------------------------------------------------------------------------- /utcp/utcp_channel.h: -------------------------------------------------------------------------------- 1 | // Copyright DPULL, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "utcp_def_internal.h" 6 | #include 7 | #include 8 | #include 9 | 10 | struct utcp_bunch_node* alloc_utcp_bunch_node(); 11 | void free_utcp_bunch_node(struct utcp_bunch_node* utcp_bunch_node); 12 | 13 | bool enqueue_incoming_data(struct utcp_channel* utcp_channel, struct utcp_bunch_node* utcp_bunch_node); 14 | struct utcp_bunch_node* dequeue_incoming_data(struct utcp_channel* utcp_channel, int sequence); 15 | 16 | void add_ougoing_data(struct utcp_channel* utcp_channel, struct utcp_bunch_node* utcp_bunch_node); 17 | int remove_ougoing_data(struct utcp_channel* utcp_channel, int32_t packet_id, struct utcp_bunch_node* bunch_node[], int bunch_node_size); 18 | 19 | enum merge_partial_result 20 | { 21 | partial_merge_fatal = -2, // Close connection... 22 | partial_merge_failed = -1, 23 | partial_merge_succeed = 0, 24 | partial_available = 1, 25 | }; 26 | enum merge_partial_result merge_partial_data(struct utcp_channel* utcp_channel, struct utcp_bunch_node* utcp_bunch_node, bool* bOutSkipAck); 27 | void clear_partial_data(struct utcp_channel* utcp_channel); 28 | int get_partial_bunch(struct utcp_channel* utcp_channel, struct utcp_bunch* bunches[], int bunches_size); 29 | 30 | void utcp_channels_uninit(struct utcp_channels* utcp_channels); 31 | struct utcp_channel* utcp_channels_get_channel(struct utcp_channels* utcp_channels, struct utcp_bunch* utcp_bunch); 32 | void utcp_channels_on_ack(struct utcp_channels* utcp_channels, int32_t AckPacketId); 33 | typedef int (*write_bunch_fn)(struct utcp_connection* fd, const uint8_t* Bits, const int32_t SizeInBits); 34 | void utcp_channels_on_nak(struct utcp_channels* utcp_channels, int32_t NakPacketId, write_bunch_fn WriteBitsToSendBuffer, struct utcp_connection* fd); 35 | void utcp_delay_close_channel(struct utcp_channels* utcp_channels); 36 | -------------------------------------------------------------------------------- /utcp/utcp_channel_def.h: -------------------------------------------------------------------------------- 1 | // Copyright DPULL, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "3rd/dl_list.h" 6 | #include "utcp_def.h" 7 | #include 8 | #include 9 | 10 | #define UTCP_MAX_PACKET 1024 11 | #define DEFAULT_MAX_CHANNEL_SIZE 32767 12 | 13 | struct utcp_bunch_node 14 | { 15 | struct dl_list_node dl_list_node; 16 | 17 | union { 18 | struct utcp_bunch utcp_bunch; 19 | struct 20 | { 21 | int32_t packet_id; 22 | uint16_t bunch_data_len; 23 | uint8_t bunch_data[UDP_MTU_SIZE]; 24 | }; 25 | }; 26 | }; 27 | 28 | struct utcp_channel 29 | { 30 | struct dl_list_node InPartialBunch; 31 | 32 | struct dl_list_node InRec; 33 | struct dl_list_node OutRec; 34 | 35 | int32_t NumInRec; // Number of packets in InRec. 36 | int32_t NumOutRec; // Number of packets in OutRec. 37 | 38 | int32_t OutReliable; 39 | int32_t InReliable; 40 | 41 | uint8_t bClose : 1; 42 | uint8_t CloseReason : 4; 43 | }; 44 | 45 | struct utcp_opened_channels 46 | { 47 | int32_t cap; 48 | int32_t num; 49 | uint16_t* channels; 50 | }; 51 | 52 | struct utcp_channels 53 | { 54 | struct utcp_channel* Channels[DEFAULT_MAX_CHANNEL_SIZE]; 55 | struct utcp_opened_channels open_channels; 56 | int32_t InitOutReliable; 57 | int32_t InitInReliable; 58 | uint8_t bHasChannelClose; 59 | }; -------------------------------------------------------------------------------- /utcp/utcp_channel_internal.h: -------------------------------------------------------------------------------- 1 | // Copyright DPULL, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "utcp_def_internal.h" 6 | #include "utcp_utils.h" 7 | #include 8 | #include 9 | #include 10 | 11 | static inline struct utcp_channel* alloc_utcp_channel(int32_t InitInReliable, int32_t InitOutReliable) 12 | { 13 | struct utcp_channel* utcp_channel = (struct utcp_channel*)utcp_realloc(NULL, sizeof(*utcp_channel)); 14 | memset(utcp_channel, 0, sizeof(*utcp_channel)); 15 | 16 | utcp_channel->InReliable = InitInReliable; 17 | utcp_channel->OutReliable = InitOutReliable; 18 | dl_list_init(&utcp_channel->InRec); 19 | dl_list_init(&utcp_channel->OutRec); 20 | dl_list_init(&utcp_channel->InPartialBunch); 21 | 22 | return utcp_channel; 23 | } 24 | 25 | static inline void free_utcp_channel(struct utcp_channel* utcp_channel) 26 | { 27 | while (!dl_list_empty(&utcp_channel->InRec)) 28 | { 29 | struct dl_list_node* dl_list_node = dl_list_pop_next(&utcp_channel->InRec); 30 | struct utcp_bunch_node* cur_utcp_bunch_node = CONTAINING_RECORD(dl_list_node, struct utcp_bunch_node, dl_list_node); 31 | utcp_realloc(cur_utcp_bunch_node, 0); 32 | utcp_channel->NumInRec--; 33 | } 34 | assert(utcp_channel->NumInRec == 0); 35 | 36 | while (!dl_list_empty(&utcp_channel->OutRec)) 37 | { 38 | struct dl_list_node* dl_list_node = dl_list_pop_next(&utcp_channel->OutRec); 39 | struct utcp_bunch_node* cur_utcp_bunch_node = CONTAINING_RECORD(dl_list_node, struct utcp_bunch_node, dl_list_node); 40 | utcp_realloc(cur_utcp_bunch_node, 0); 41 | utcp_channel->NumOutRec--; 42 | } 43 | assert(utcp_channel->NumOutRec == 0); 44 | 45 | clear_partial_data(utcp_channel); 46 | 47 | utcp_realloc(utcp_channel, 0); 48 | } 49 | 50 | static inline void mark_channel_close(struct utcp_channel* utcp_channel, int8_t CloseReason) 51 | { 52 | if (!utcp_channel->bClose) 53 | { 54 | utcp_channel->bClose = true; 55 | utcp_channel->CloseReason = CloseReason; 56 | } 57 | } 58 | 59 | static inline void opened_channels_uninit(struct utcp_opened_channels* utcp_open_channels) 60 | { 61 | if (utcp_open_channels->channels) 62 | { 63 | utcp_realloc(utcp_open_channels->channels, 0); 64 | utcp_open_channels->channels = NULL; 65 | } 66 | } 67 | 68 | static inline int binary_search(const void* key, const void* base, size_t num, size_t element_size, int (*compar)(const void*, const void*)) 69 | { 70 | int lo = 0; 71 | int hi = (int)num - 1; 72 | 73 | while (lo <= hi) 74 | { 75 | // i might overflow if lo and hi are both large positive numbers. 76 | int i = lo + ((hi - lo) >> 1); 77 | 78 | int c = compar(key, (char*)base + i * element_size); 79 | if (c == 0) 80 | return i; 81 | if (c > 0) 82 | lo = i + 1; 83 | else 84 | hi = i - 1; 85 | } 86 | return ~lo; 87 | } 88 | 89 | static inline int uint16_less(const void* l, const void* r) 90 | { 91 | return *(uint16_t*)l - *(uint16_t*)r; 92 | } 93 | 94 | static bool opened_channels_resize(struct utcp_opened_channels* utcp_open_channels) 95 | { 96 | if (utcp_open_channels->num < utcp_open_channels->cap) 97 | return true; 98 | 99 | assert(utcp_open_channels->num == utcp_open_channels->cap); 100 | 101 | int cap = utcp_open_channels->cap; 102 | assert((!!cap) == (!!utcp_open_channels->channels)); 103 | 104 | cap = cap != 0 ? cap : 16; 105 | cap *= 2; 106 | assert(cap > 0 && cap < DEFAULT_MAX_CHANNEL_SIZE * 2); 107 | 108 | uint16_t* channels = (uint16_t*)utcp_realloc(utcp_open_channels->channels, cap * sizeof(uint16_t)); 109 | if (!channels) 110 | return false; 111 | utcp_open_channels->channels = channels; 112 | utcp_open_channels->cap = cap; 113 | return true; 114 | } 115 | 116 | static inline bool opened_channels_add(struct utcp_opened_channels* utcp_open_channels, uint16_t ChIndex) 117 | { 118 | int pos = binary_search(&ChIndex, utcp_open_channels->channels, utcp_open_channels->num, sizeof(uint16_t), uint16_less); 119 | if (pos >= 0) 120 | return true; 121 | 122 | if (!opened_channels_resize(utcp_open_channels)) 123 | return false; 124 | 125 | pos = ~pos; 126 | 127 | uint16_t* src = utcp_open_channels->channels + pos; 128 | uint16_t* dst = src + 1; 129 | int count = utcp_open_channels->num - pos; 130 | memmove(dst, src, count * sizeof(uint16_t)); 131 | utcp_open_channels->channels[pos] = ChIndex; 132 | utcp_open_channels->num++; 133 | return true; 134 | } 135 | 136 | static inline bool opened_channels_remove(struct utcp_opened_channels* utcp_open_channels, uint16_t ChIndex) 137 | { 138 | int pos = binary_search(&ChIndex, utcp_open_channels->channels, utcp_open_channels->num, sizeof(uint16_t), uint16_less); 139 | if (pos < 0) 140 | return false; 141 | uint16_t* dst = utcp_open_channels->channels + pos; 142 | uint16_t* src = dst + 1; 143 | int count = utcp_open_channels->num - pos - 1; 144 | memmove(dst, src, count * sizeof(uint16_t)); 145 | utcp_open_channels->num--; 146 | return true; 147 | } 148 | -------------------------------------------------------------------------------- /utcp/utcp_def.h: -------------------------------------------------------------------------------- 1 | // Copyright DPULL, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef __cplusplus 11 | extern "C" 12 | { 13 | #endif 14 | 15 | struct utcp_listener; 16 | struct utcp_connection; 17 | struct utcp_bunch; 18 | 19 | struct utcp_config 20 | { 21 | void (*on_accept)(struct utcp_listener* fd, void* userdata, bool reconnect); 22 | void (*on_connect)(struct utcp_connection* fd, void* userdata, bool reconnect); 23 | void (*on_disconnect)(struct utcp_connection* fd, void* userdata, int close_reason); 24 | void (*on_outgoing)(void* fd, void* userdata, const void* data, int len); // "void* fd" is "struct utcp_listener* fd" or "struct utcp_connection* fd" 25 | void (*on_recv_bunch)(struct utcp_connection* fd, void* userdata, struct utcp_bunch* const bunches[], int count); 26 | void (*on_delivery_status)(struct utcp_connection* fd, void* userdata, int32_t packet_id, bool ack); 27 | void (*on_log)(int level, const char* msg, va_list args); 28 | void* (*on_realloc)(void* ptr, size_t size); 29 | unsigned (*on_rand)(); 30 | 31 | int64_t ElapsedTime; 32 | uint32_t MagicHeader; 33 | uint8_t MagicHeaderBits; 34 | uint8_t EnableDump; 35 | 36 | uint32_t GlobalNetTravelCount; 37 | // LogNetVersion: Example 1.0.0.0, NetCL: 0, EngineNetVer: 30, GameNetVer: 0 (Checksum: 2743834095) 38 | uint32_t CachedNetworkChecksum; // 3713382154 39 | }; 40 | 41 | /* 42 | ------------------------------------------------------------------------------ 43 | |Ethernet | IPv4 |UDP | Data |Ethernet checksum| 44 | ------------------------------------------------------------------------------ 45 | 14 bytes 20 bytes 8 bytes x bytes 4 bytes 46 | \ (w/o options) / 47 | \___________________________________________/ 48 | | 49 | MTU 50 | The ethernet data payload is 1500 bytes. IPv4 requires a minimum of 20 bytes for its header. 51 | Alternatively IPv6 requires a minimum of 40 bytes. UDP requires 8 bytes for its header. 52 | That leaves 1472 bytes (ipv4) or 1452 (ipv6) for your data. 53 | */ 54 | #define UDP_MTU_SIZE (1452) 55 | 56 | struct utcp_bunch 57 | { 58 | int32_t ChSequence; // 内部赋值 59 | int32_t PacketId; // 内部赋值 60 | 61 | uint32_t NameIndex; 62 | uint16_t ChIndex; 63 | uint16_t DataBitsLen; 64 | 65 | uint8_t bOpen : 1; 66 | uint8_t bClose : 1; 67 | uint8_t CloseReason : 4; 68 | uint8_t bIsReplicationPaused : 1; 69 | uint8_t bReliable : 1; 70 | 71 | uint8_t bHasPackageMapExports : 1; 72 | uint8_t bHasMustBeMappedGUIDs : 1; 73 | uint8_t bPartial : 1; 74 | uint8_t bPartialInitial : 1; 75 | uint8_t bPartialFinal : 1; 76 | 77 | uint8_t Data[UDP_MTU_SIZE]; 78 | }; 79 | 80 | #ifdef __cplusplus 81 | } 82 | #endif 83 | -------------------------------------------------------------------------------- /utcp/utcp_def_internal.h: -------------------------------------------------------------------------------- 1 | // Copyright DPULL, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "utcp_channel_def.h" 10 | #include "utcp_def.h" 11 | #include "utcp_packet_notify_def.h" 12 | 13 | enum 14 | { 15 | PACKET_ID_INDEX_NONE = -1, 16 | HANDSHAKE_PACKET_SIZE_BITS = 291, 17 | RESTART_HANDSHAKE_PACKET_SIZE_BITS = 66, 18 | RESTART_RESPONSE_SIZE_BITS = 451, 19 | SECRET_BYTE_SIZE = 64, 20 | SECRET_COUNT = 2, 21 | COOKIE_BYTE_SIZE = 20, 22 | ADDRSTR_PORT_SIZE = 64, // INET6_ADDRSTRLEN + PORT_LEN 23 | DEBUG_NAME_MAX_SIZE = 32, 24 | }; 25 | 26 | #define SECRET_UPDATE_TIME 15.f 27 | #define SECRET_UPDATE_TIME_VARIANCE 5.f 28 | #define UTCP_CONNECT_TIMEOUT (120 * 1000) 29 | 30 | // The maximum allowed lifetime (in seconds) of any one handshake cookie 31 | #define MAX_COOKIE_LIFETIME ((SECRET_UPDATE_TIME + SECRET_UPDATE_TIME_VARIANCE) * (float)SECRET_COUNT) 32 | 33 | // The minimum amount of possible time a cookie may exist (for calculating when the clientside should timeout a challenge response) 34 | #define MIN_COOKIE_LIFETIME SECRET_UPDATE_TIME 35 | 36 | struct utcp_listener 37 | { 38 | void* userdata; 39 | 40 | /** Client: Whether or not we are in the middle of a restarted handshake. Server: Whether or not the last handshake was a restarted handshake. */ 41 | uint8_t bRestartedHandshake : 1; 42 | 43 | /** The serverside-only 'secret' value, used to help with generating cookies. */ 44 | uint8_t HandshakeSecret[SECRET_BYTE_SIZE][SECRET_COUNT]; 45 | 46 | /** Which of the two secret values above is active (values are changed frequently, to limit replay attacks) */ 47 | uint8_t ActiveSecret; 48 | 49 | /** The time of the last secret value update */ 50 | double LastSecretUpdateTimestamp; 51 | 52 | /** The local (client) time at which the challenge was last updated */ 53 | int64_t LastChallengeTimestamp; 54 | 55 | /** The cookie which completed the connection handshake. */ 56 | uint8_t AuthorisedCookie[COOKIE_BYTE_SIZE]; 57 | 58 | /** The initial server sequence value, from the last successful handshake */ 59 | int32_t LastServerSequence; 60 | 61 | /** The initial client sequence value, from the last successful handshake */ 62 | int32_t LastClientSequence; 63 | 64 | char LastChallengeSuccessAddress[ADDRSTR_PORT_SIZE]; 65 | }; 66 | 67 | struct utcp_challenge_data 68 | { 69 | uint8_t state : 2; 70 | uint8_t bLastChallengeSuccessAddress : 1; 71 | 72 | /** Whether or not component handshaking has begun */ 73 | uint8_t bBeganHandshaking : 1; 74 | 75 | /** Client: Whether or not we are in the middle of a restarted handshake. Server: Whether or not the last handshake was a restarted handshake. */ 76 | uint8_t bRestartedHandshake : 1; 77 | 78 | /** The local (client) time at which the challenge was last updated */ 79 | int64_t LastChallengeTimestamp; 80 | 81 | /** The last time a handshake packet was sent - used for detecting failed sends. */ 82 | int64_t LastClientSendTimestamp; 83 | 84 | /** The Timestamp value of the last challenge response sent */ 85 | double LastTimestamp; 86 | 87 | /** The SecretId value of the last challenge response sent */ 88 | uint8_t LastSecretId; 89 | 90 | /** The Cookie value of the last challenge response sent. Will differ from AuthorisedCookie, if a handshake retry is triggered. */ 91 | uint8_t LastCookie[COOKIE_BYTE_SIZE]; 92 | 93 | /** The number of sent handshake packets, added to packets to aid packet dump debugging (may roll over) */ 94 | uint8_t SentHandshakePacketCount; 95 | 96 | /** The local (client) time at which the last restart handshake request was received */ 97 | int64_t LastRestartPacketTimestamp; 98 | 99 | /** Cached version of the serverside UEngine.GlobalNetTravelCount value, for writing session id's */ 100 | uint32_t CachedGlobalNetTravelCount; 101 | 102 | /** Cached version of the clientside per-NetDriverDefinition 'ClientID' value, for writing client connection id's */ 103 | uint32_t CachedClientID; 104 | }; 105 | 106 | struct utcp_connection 107 | { 108 | void* userdata; 109 | char debug_name[DEBUG_NAME_MAX_SIZE]; 110 | 111 | uint8_t bClose : 1; 112 | uint8_t CloseReason : 7; 113 | 114 | /** The cookie which completed the connection handshake. */ 115 | uint8_t AuthorisedCookie[COOKIE_BYTE_SIZE]; 116 | 117 | int64_t LastReceiveRealtime; // Last time a packet was received, using real time seconds (FPlatformTime::Seconds) 118 | 119 | struct utcp_challenge_data* challenge_data; 120 | 121 | // packet相关 122 | int32_t InPacketId; // Full incoming packet index. 123 | int32_t OutPacketId; // Most recently sent packet. 124 | int32_t OutAckPacketId; // Most recently acked outgoing packet. 125 | /** Full PacketId of last sent packet that we have received notification for (i.e. we know if it was delivered or not). Related to OutAckPacketId which is 126 | * tha last successfully delivered PacketId */ 127 | int32_t LastNotifiedPacketId; 128 | 129 | struct packet_notify packet_notify; 130 | 131 | struct utcp_channels channels; 132 | 133 | /** Keep old behavior where we send a packet with only acks even if we have no other outgoing data if we got incoming data */ 134 | uint32_t HasDirtyAcks; // TODO 这个变量究竟做什么 135 | 136 | uint8_t SendBuffer[UTCP_MAX_PACKET + 32 /*MagicHeader*/ + 1 /*EndBits*/]; 137 | size_t SendBufferBitsNum; 138 | 139 | int64_t LastSendTime; // Last time a packet was sent, for keepalives. 140 | 141 | /** Stores the bit number where we wrote the dummy packet info in the packet header */ 142 | // size_t HeaderMarkForPacketInfo; 143 | 144 | uint8_t LastSessionID; 145 | uint8_t LastClientID; 146 | }; 147 | 148 | enum UTcpNetCloseResult 149 | { 150 | /** Control channel closing */ 151 | ControlChannelClose, 152 | 153 | /** Socket send failure */ 154 | SocketSendFailure, 155 | 156 | /** A connection to the net driver has been lost */ 157 | ConnectionLost, 158 | 159 | //////////////////////////////////////////////////////////////////////////////////// 160 | 161 | /** NetConnection Cleanup was triggered */ 162 | Cleanup, 163 | 164 | /** PacketHandler Error processing incoming packet */ 165 | PacketHandlerIncomingError, 166 | 167 | /** Attempted to send data before handshake is complete */ 168 | PrematureSend, 169 | 170 | /** A connection to the net driver has timed out */ 171 | ConnectionTimeout, 172 | 173 | /** Packet had zeros in the last byte */ 174 | ZeroLastByte, 175 | 176 | /** Zero size packet */ 177 | ZeroSize, 178 | 179 | /** Failed to read PacketHeader */ 180 | ReadHeaderFail, 181 | 182 | /** Failed to read extra PacketHeader information */ 183 | ReadHeaderExtraFail, 184 | 185 | /** Sequence mismatch while processing acks */ 186 | AckSequenceMismatch, 187 | 188 | /** Bunch channel index exceeds maximum channel limit */ 189 | BunchBadChannelIndex, 190 | 191 | /** Bunch header or data serialization overflowed */ 192 | BunchOverflow, 193 | 194 | /** Reliable buffer overflowed when attempting to send */ 195 | ReliableBufferOverflow, 196 | }; -------------------------------------------------------------------------------- /utcp/utcp_handshake.h: -------------------------------------------------------------------------------- 1 | // Copyright DPULL, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "bit_buffer.h" 6 | #include "utcp_def_internal.h" 7 | 8 | // The design of handshake: https://blog.dpull.com/post/2022-11-13-utcp_handshake 9 | 10 | int process_connectionless_packet(struct utcp_listener* fd, const char* address, const uint8_t* buffer, int len); 11 | 12 | void handshake_begin(struct utcp_connection* fd); 13 | int handshake_incoming(struct utcp_connection* fd, struct bitbuf* bitbuf); 14 | void handshake_update(struct utcp_connection* fd); 15 | 16 | bool is_client(struct utcp_connection* fd); 17 | bool is_connected(struct utcp_connection* fd); 18 | 19 | bool write_packet_header(struct bitbuf* bitbuf, uint8_t handshake_version, uint8_t session_id, uint8_t client_id, uint8_t is_handshake); 20 | bool read_packet_header(struct bitbuf* bitbuf, uint8_t handshake_version, uint8_t* session_id, uint8_t* client_id, uint8_t* is_handshake); 21 | 22 | uint8_t LastRemoteHandshakeVersion(); 23 | uint8_t CurrentHandshakeVersion(); 24 | uint8_t MinSupportedHandshakeVersion(); -------------------------------------------------------------------------------- /utcp/utcp_packet.h: -------------------------------------------------------------------------------- 1 | // Copyright DPULL, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "bit_buffer.h" 6 | #include "utcp_def_internal.h" 7 | 8 | void utcp_sequence_init(struct utcp_connection* fd, int32_t IncomingSequence, int32_t OutgoingSequence); 9 | bool ReceivedPacket(struct utcp_connection* fd, struct bitbuf* bitbuf); 10 | int32_t PeekPacketId(struct utcp_connection* fd, struct bitbuf* bitbuf); 11 | int WriteBitsToSendBuffer(struct utcp_connection* fd, const uint8_t* Bits, const int32_t SizeInBits); 12 | void WritePacketHeader(struct utcp_connection* fd, struct bitbuf* bitbuf); 13 | int32_t SendRawBunch(struct utcp_connection* fd, struct utcp_bunch* bunch); 14 | -------------------------------------------------------------------------------- /utcp/utcp_packet_notify.c: -------------------------------------------------------------------------------- 1 | #include "utcp_packet_notify.h" 2 | #include "bit_buffer.h" 3 | #include "utcp_def_internal.h" 4 | #include "utcp_packet.h" 5 | #include "utcp_sequence_number.h" 6 | #include "utcp_utils.h" 7 | #include 8 | #include 9 | 10 | static inline size_t MIN(size_t a, size_t b) 11 | { 12 | return a < b ? a : b; 13 | } 14 | static inline size_t ClAMP(const size_t X, const size_t Min, const size_t Max) 15 | { 16 | return (X < Min) ? Min : (X < Max) ? X : Max; 17 | } 18 | 19 | enum 20 | { 21 | HistoryWordCountBits = 4, 22 | HistoryWordCountMask = ((1 << HistoryWordCountBits) - 1), 23 | AckSeqShift = HistoryWordCountBits, 24 | SeqShift = AckSeqShift + SequenceNumberBits, 25 | }; 26 | 27 | // FPackedHeader::Pack 28 | static uint32_t PackedHeader_Pack(uint16_t Seq, uint16_t AckedSeq, size_t HistoryWordCount) 29 | { 30 | uint32_t Packed = 0u; 31 | 32 | Packed |= (uint32_t)Seq << SeqShift; 33 | Packed |= (uint32_t)AckedSeq << AckSeqShift; 34 | Packed |= HistoryWordCount & HistoryWordCountMask; 35 | 36 | return Packed; 37 | } 38 | 39 | // FPackedHeader::UnPack 40 | static void PackedHeader_UnPack(uint32_t Packed, uint16_t* Seq, uint16_t* AckedSeq, size_t* HistoryWordCount) 41 | { 42 | *Seq = (Packed >> SeqShift & SeqNumberMask); 43 | *AckedSeq = (Packed >> AckSeqShift & SeqNumberMask); 44 | *HistoryWordCount = (Packed & HistoryWordCountMask); 45 | } 46 | 47 | // FNetPacketNotify::UpdateInAckSeqAck 48 | static uint16_t UpdateInAckSeqAck(struct packet_notify* packet_notify, int32_t AckCount, uint16_t AckedSeq) 49 | { 50 | assert(AckCount > 0); 51 | if (AckCount <= ring_buffer_num_items(&packet_notify->AckRecord)) 52 | { 53 | union sent_ack_data AckData; 54 | AckData.Value = 0; 55 | 56 | while (AckCount > 0) 57 | { 58 | AckCount--; 59 | ring_buffer_dequeue(&packet_notify->AckRecord, &AckData.Value); 60 | } 61 | 62 | // verify that we have a matching sequence number 63 | if (AckData.OutSeq == AckedSeq) 64 | { 65 | return AckData.InAckSeq; 66 | } 67 | } 68 | 69 | // Pessimistic view, should never occur but we do want to know about it if it would 70 | // ensureMsgf(false, TEXT("FNetPacketNotify::UpdateInAckSeqAck - Failed to find matching AckRecord for %u"), AckedSeq.Get()); 71 | return AckedSeq - MaxSequenceHistoryLength; 72 | } 73 | 74 | // GetSequenceDelta 75 | int32_t packet_notify_delta_seq(struct packet_notify* packet_notify, struct notification_header* notification_header) 76 | { 77 | if (seq_num_greater_than(notification_header->Seq, packet_notify->InSeq) && seq_num_greater_equal(notification_header->AckedSeq, packet_notify->OutAckSeq) && 78 | seq_num_greater_than(packet_notify->OutSeq, notification_header->AckedSeq)) 79 | { 80 | return seq_num_diff(notification_header->Seq, packet_notify->InSeq); 81 | } 82 | else 83 | { 84 | return 0; 85 | } 86 | } 87 | 88 | // FNetPacketNotify::Init 89 | void packet_notify_init(struct packet_notify* packet_notify, uint16_t InitialInSeq, uint16_t InitialOutSeq) 90 | { 91 | memset(packet_notify->InSeqHistory, 0, sizeof(packet_notify->InSeqHistory)); 92 | packet_notify->InSeq = InitialInSeq; 93 | packet_notify->InAckSeq = InitialInSeq; 94 | packet_notify->InAckSeqAck = InitialInSeq; 95 | packet_notify->OutSeq = InitialOutSeq; 96 | packet_notify->OutAckSeq = seq_num_init(InitialOutSeq - 1); 97 | 98 | ring_buffer_init(&packet_notify->AckRecord); 99 | } 100 | 101 | // FNetPacketNotify::AckSeq 102 | void packet_notify_ack_seq(struct packet_notify* packet_notify, uint16_t AckedSeq, bool IsAck) 103 | { 104 | AckedSeq = seq_num_init(AckedSeq); 105 | assert(AckedSeq == packet_notify->InSeq); 106 | 107 | while (seq_num_greater_than(AckedSeq, packet_notify->InAckSeq)) 108 | { 109 | packet_notify->InAckSeq = seq_num_inc(packet_notify->InAckSeq, 1); 110 | 111 | const bool bReportAcked = packet_notify->InAckSeq == AckedSeq ? IsAck : false; 112 | 113 | utcp_log(Verbose, "packet_notify_ack_seq:%hd, %s", packet_notify->InAckSeq, bReportAcked ? "ACK" : "NAK"); 114 | 115 | // TSequenceHistory::AddDeliveryStatus 116 | { 117 | SequenceHistoryWord Carry = bReportAcked ? 1u : 0u; 118 | const SequenceHistoryWord ValueMask = 1u << (SequenceHistoryBitsPerWord - 1); 119 | 120 | for (size_t CurrentWordIt = 0; CurrentWordIt < SequenceHistoryWordCount; ++CurrentWordIt) 121 | { 122 | const SequenceHistoryWord OldValue = Carry; 123 | 124 | // carry over highest bit in each word to the next word 125 | Carry = (packet_notify->InSeqHistory[CurrentWordIt] & ValueMask) >> (SequenceHistoryBitsPerWord - 1); 126 | packet_notify->InSeqHistory[CurrentWordIt] = (packet_notify->InSeqHistory[CurrentWordIt] << 1u) | OldValue; 127 | } 128 | } 129 | } 130 | } 131 | 132 | // FNetPacketNotify::Update 133 | int32_t packet_notify_update(handle_notify_fn handle, void* fd, struct packet_notify* packet_notify, struct notification_header* notification_header) 134 | { 135 | const int32_t InSeqDelta = packet_notify_delta_seq(packet_notify, notification_header); 136 | if (InSeqDelta > 0) 137 | { 138 | // ProcessReceivedAcks(NotificationData, InFunc); 139 | // FNetPacketNotify::ProcessReceivedAcks 140 | if (seq_num_greater_than(notification_header->AckedSeq, packet_notify->OutAckSeq)) 141 | { 142 | int32_t AckCount = seq_num_diff(notification_header->AckedSeq, packet_notify->OutAckSeq); 143 | 144 | // Update InAckSeqAck used to track the needed number of bits to transmit our ack history 145 | packet_notify->InAckSeqAck = UpdateInAckSeqAck(packet_notify, AckCount, notification_header->AckedSeq); 146 | 147 | // ExpectedAck = OutAckSeq + 1 148 | uint16_t CurrentAck = packet_notify->OutAckSeq; 149 | CurrentAck = seq_num_inc(CurrentAck, 1); 150 | 151 | // Warn if the received sequence number is greater than our history buffer, since if that is the case we have to treat the data as lost. 152 | if (AckCount > MaxSequenceHistoryLength) 153 | { 154 | /* 155 | UE_LOG_PACKET_NOTIFY_WARNING(TEXT("Notification::ProcessReceivedAcks - Missed Acks: AckedSeq: %u, OutAckSeq: %u, FirstMissingSeq: %u Count: 156 | %u"), NotificationData.AckedSeq.Get(), OutAckSeq.Get(), CurrentAck.Get(), AckCount - (SequenceNumberT::DifferenceT)(SequenceHistoryT::Size)); 157 | */ 158 | } 159 | 160 | while (AckCount > MaxSequenceHistoryLength) 161 | { 162 | --AckCount; 163 | handle(fd, CurrentAck, false); 164 | CurrentAck = seq_num_inc(CurrentAck, 1); 165 | } 166 | 167 | // For sequence numbers contained in the history we lookup the delivery status from the history 168 | while (AckCount > 0) 169 | { 170 | --AckCount; 171 | 172 | // TSequenceHistory::IsDelivered 173 | bool IsDelivered; 174 | { 175 | size_t Index = AckCount; 176 | assert(Index < MaxSequenceHistoryLength); 177 | 178 | const size_t WordIndex = Index / SequenceHistoryBitsPerWord; 179 | const SequenceHistoryWord WordMask = ((SequenceHistoryWord)(1) << (Index & (SequenceHistoryBitsPerWord - 1))); 180 | 181 | IsDelivered = (notification_header->History[WordIndex] & WordMask) != 0u; 182 | } 183 | 184 | // UE_LOG_PACKET_NOTIFY(TEXT("Notification::ProcessReceivedAcks Seq: %u - IsAck: %u HistoryIndex: %u"), CurrentAck.Get(), 185 | // NotificationData.History.IsDelivered(AckCount) ? 1u : 0u, AckCount); 186 | handle(fd, CurrentAck, IsDelivered); 187 | CurrentAck = seq_num_inc(CurrentAck, 1); 188 | } 189 | packet_notify->OutAckSeq = notification_header->AckedSeq; 190 | } 191 | 192 | // accept sequence 193 | packet_notify->InSeq = notification_header->Seq; 194 | return InSeqDelta; 195 | } 196 | else 197 | { 198 | return 0; 199 | } 200 | } 201 | 202 | // FNetPacketNotify::CommitAndIncrementOutSeq 203 | uint16_t packet_notify_commit_and_inc_outseq(struct packet_notify* packet_notify) 204 | { 205 | // we have not written a header...this is a fail. 206 | assert(packet_notify->WrittenHistoryWordCount != 0); 207 | 208 | // Add entry to the ack-record so that we can update the InAckSeqAck when we received the ack for this OutSeq. 209 | union sent_ack_data AckData; 210 | AckData.InAckSeq = packet_notify->WrittenInAckSeq; 211 | AckData.OutSeq = packet_notify->OutSeq; 212 | ring_buffer_queue(&packet_notify->AckRecord, AckData.Value); 213 | packet_notify->WrittenHistoryWordCount = 0u; 214 | packet_notify->OutSeq = seq_num_inc(packet_notify->OutSeq, 1); 215 | return packet_notify->OutSeq; 216 | } 217 | 218 | // FNetPacketNotify::GetCurrentSequenceHistoryLength 219 | static size_t packet_notify_GetCurrentSequenceHistoryLength(struct packet_notify* packet_notify) 220 | { 221 | if (seq_num_greater_equal(packet_notify->InAckSeq, packet_notify->InAckSeqAck)) 222 | { 223 | return seq_num_diff(packet_notify->InAckSeq, packet_notify->InAckSeqAck); 224 | } 225 | else 226 | { 227 | // Worst case send full history 228 | return MaxSequenceHistoryLength; 229 | } 230 | } 231 | 232 | // FNetPacketNotify::ReadHeader 233 | int packet_notify_read_header(struct bitbuf* bitbuf, struct notification_header* notification_header) 234 | { 235 | // Read packed header 236 | uint32_t PackedHeader = 0; 237 | if (!bitbuf_read_int_byte_order(bitbuf, &PackedHeader)) 238 | { 239 | return -1; 240 | } 241 | 242 | memset(notification_header, 0, sizeof(*notification_header)); 243 | 244 | // unpack 245 | PackedHeader_UnPack(PackedHeader, ¬ification_header->Seq, ¬ification_header->AckedSeq, ¬ification_header->HistoryWordCount); 246 | notification_header->HistoryWordCount += 1; 247 | notification_header->HistoryWordCount = MIN(_countof(notification_header->History), notification_header->HistoryWordCount); 248 | 249 | for (int i = 0; i < notification_header->HistoryWordCount; ++i) 250 | { 251 | if (!bitbuf_read_int_byte_order(bitbuf, ¬ification_header->History[i])) 252 | return -2; 253 | } 254 | 255 | return 0; 256 | } 257 | 258 | int packet_header_read(struct packet_header* packet_header, struct bitbuf* bitbuf) 259 | { 260 | int ret = packet_notify_read_header(bitbuf, &packet_header->notification_header); 261 | if (ret != 0) 262 | { 263 | utcp_log(Warning, "Failed to read PacketHeader.%d", ret); 264 | return ReadHeaderFail; 265 | } 266 | 267 | if (!bitbuf_read_bit(bitbuf, &packet_header->bHasPacketInfoPayload)) 268 | { 269 | utcp_log(Warning, "Failed to read extra PacketHeader information.%d", 1); 270 | return ReadHeaderExtraFail; 271 | } 272 | 273 | if (packet_header->bHasPacketInfoPayload) 274 | { 275 | if (!bitbuf_read_int(bitbuf, &packet_header->PacketJitterClockTimeMS, 1 << NumBitsForJitterClockTimeInHeader)) 276 | { 277 | utcp_log(Warning, "Failed to read extra PacketHeader information.%d", 2); 278 | return ReadHeaderExtraFail; 279 | } 280 | 281 | // UNetConnection::ReadPacketInfo 282 | if (!bitbuf_read_bit(bitbuf, &packet_header->bHasServerFrameTime)) 283 | { 284 | utcp_log(Warning, "Failed to read extra PacketHeader information.%d", 3); 285 | return ReadHeaderExtraFail; 286 | } 287 | 288 | if (packet_header->bHasServerFrameTime) 289 | { 290 | if (!bitbuf_read_bytes(bitbuf, &packet_header->FrameTimeByte, 1)) 291 | { 292 | utcp_log(Warning, "Failed to read extra PacketHeader information.%d", 3); 293 | return ReadHeaderExtraFail; 294 | } 295 | } 296 | } 297 | return 0; 298 | } 299 | 300 | // FNetPacketNotify::WriteHeader 301 | bool packet_notify_fill_notification_header(struct packet_notify* packet_notify, struct notification_header* notification_header, bool bRefresh) 302 | { 303 | // we always write at least 1 word 304 | size_t CurrentHistoryWordCount = 305 | ClAMP((packet_notify_GetCurrentSequenceHistoryLength(packet_notify) + SequenceHistoryBitsPerWord - 1u) / SequenceHistoryBitsPerWord, 1u, SequenceHistoryWordCount); 306 | 307 | // We can only do a refresh if we do not need more space for the history 308 | if (bRefresh && (CurrentHistoryWordCount > packet_notify->WrittenHistoryWordCount)) 309 | return false; 310 | 311 | // How many words of ack data should we write? If this is a refresh we must write the same size as the original header 312 | packet_notify->WrittenHistoryWordCount = bRefresh ? packet_notify->WrittenHistoryWordCount : CurrentHistoryWordCount; 313 | 314 | // This is the last InAck we have acknowledged at this time 315 | packet_notify->WrittenInAckSeq = packet_notify->InAckSeq; 316 | 317 | notification_header->Seq = packet_notify->OutSeq; 318 | notification_header->AckedSeq = packet_notify->WrittenInAckSeq; 319 | notification_header->HistoryWordCount = packet_notify->WrittenHistoryWordCount; 320 | 321 | // Write ack history 322 | // TSequenceHistory::Write 323 | { 324 | size_t NumWords = MIN(packet_notify->WrittenHistoryWordCount, SequenceHistoryWordCount); 325 | for (size_t i = 0; i < NumWords; ++i) 326 | { 327 | notification_header->History[i] = packet_notify->InSeqHistory[i]; 328 | } 329 | } 330 | return true; 331 | } 332 | 333 | static int packet_notify_WriteHeader(struct bitbuf* bitbuf, struct notification_header* notification_header) 334 | { 335 | // Pack data into a uint 336 | uint32_t PackedHeader = PackedHeader_Pack(notification_header->Seq, notification_header->AckedSeq, notification_header->HistoryWordCount - 1); 337 | 338 | // Write packed header 339 | if (!bitbuf_write_int_byte_order(bitbuf, PackedHeader)) 340 | return false; 341 | 342 | // Write ack history 343 | // TSequenceHistory::Write 344 | { 345 | size_t NumWords = MIN(notification_header->HistoryWordCount, SequenceHistoryWordCount); 346 | for (size_t i = 0; i < NumWords; ++i) 347 | { 348 | if (!bitbuf_write_int_byte_order(bitbuf, notification_header->History[i])) 349 | return false; 350 | } 351 | } 352 | 353 | // TODO log 354 | // UE_LOG_PACKET_NOTIFY(TEXT("FNetPacketNotify::WriteHeader - Seq %u, AckedSeq %u bReFresh %u HistorySizeInWords %u"), Seq, AckedSeq, bRefresh ? 1u : 0u, 355 | // WrittenHistoryWordCount); 356 | return true; 357 | } 358 | 359 | // FNetPacketNotify::WriteHeader 360 | bool packet_header_write(struct packet_header* packet_header, struct bitbuf* bitbuf) 361 | { 362 | if (!packet_notify_WriteHeader(bitbuf, &packet_header->notification_header)) 363 | return false; 364 | 365 | // UNetConnection::WriteDummyPacketInfo 366 | if (!bitbuf_write_bit(bitbuf, packet_header->bHasPacketInfoPayload)) 367 | return false; 368 | 369 | if (packet_header->bHasPacketInfoPayload) 370 | { 371 | // UNetConnection::WriteFinalPacketInfo 372 | if (!bitbuf_write_int(bitbuf, packet_header->PacketJitterClockTimeMS, 1 << NumBitsForJitterClockTimeInHeader)) 373 | return false; 374 | 375 | if (!bitbuf_write_bit(bitbuf, packet_header->bHasServerFrameTime)) 376 | return false; 377 | 378 | if (packet_header->bHasServerFrameTime) 379 | { 380 | if (!bitbuf_write_bytes(bitbuf, &packet_header->FrameTimeByte, 1)) 381 | return false; 382 | } 383 | } 384 | return true; 385 | } 386 | -------------------------------------------------------------------------------- /utcp/utcp_packet_notify.h: -------------------------------------------------------------------------------- 1 | // Copyright DPULL, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "bit_buffer.h" 6 | #include "utcp_def_internal.h" 7 | #include "utcp_packet_notify_def.h" 8 | #include 9 | #include 10 | 11 | enum 12 | { 13 | NumBitsForJitterClockTimeInHeader = 10, 14 | }; 15 | 16 | struct packet_header 17 | { 18 | struct notification_header notification_header; 19 | 20 | uint8_t bHasPacketInfoPayload; 21 | uint32_t PacketJitterClockTimeMS; 22 | uint8_t bHasServerFrameTime; 23 | uint8_t FrameTimeByte; 24 | }; 25 | 26 | void packet_notify_init(struct packet_notify* packet_notify, uint16_t InitialInSeq, uint16_t InitialOutSeq); 27 | 28 | int packet_notify_read_header(struct bitbuf* bitbuf, struct notification_header* notification_header); 29 | int32_t packet_notify_delta_seq(struct packet_notify* packet_notify, struct notification_header* notification_header); 30 | 31 | typedef void (*handle_notify_fn)(void* fd, uint16_t AckedSequence, bool bDelivered); 32 | int32_t packet_notify_update(handle_notify_fn handle, void* fd, struct packet_notify* packet_notify, struct notification_header* notification_header); 33 | 34 | void packet_notify_ack_seq(struct packet_notify* packet_notify, uint16_t AckedSeq, bool IsAck); 35 | uint16_t packet_notify_commit_and_inc_outseq(struct packet_notify* packet_notify); 36 | bool packet_notify_fill_notification_header(struct packet_notify* packet_notify, struct notification_header* notification_header, bool bRefresh); 37 | 38 | int packet_header_read(struct packet_header* packet_header, struct bitbuf* bitbuf); 39 | bool packet_header_write(struct packet_header* packet_header, struct bitbuf* bitbuf); 40 | -------------------------------------------------------------------------------- /utcp/utcp_packet_notify_def.h: -------------------------------------------------------------------------------- 1 | // Copyright DPULL, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "3rd/ringbuffer.h" 6 | #include 7 | #include 8 | #include 9 | 10 | typedef uint32_t SequenceHistoryWord; 11 | enum 12 | { 13 | UTCP_RELIABLE_BUFFER = 256, // Power of 2 >= 1. 14 | UTCP_MAX_CHSEQUENCE = 1024, // Power of 2 >RELIABLE_BUFFER, covering loss/misorder time. 15 | MaxSequenceHistoryLength = 256, 16 | SequenceHistoryBitsPerWord = (sizeof(SequenceHistoryWord) * 8), 17 | SequenceHistoryWordCount = (MaxSequenceHistoryLength / SequenceHistoryBitsPerWord), 18 | }; 19 | 20 | union sent_ack_data { 21 | struct 22 | { 23 | uint16_t OutSeq; // Not needed... just to verify that things work as expected 24 | uint16_t InAckSeq; 25 | }; 26 | uint32_t Value; 27 | }; 28 | 29 | struct packet_notify 30 | { 31 | // Track incoming sequence data 32 | SequenceHistoryWord InSeqHistory[SequenceHistoryWordCount]; // BitBuffer containing a bitfield describing the history of received packets 33 | uint16_t InSeq; // Last sequence number received and accepted from remote 34 | uint16_t InAckSeq; // Last sequence number received from remote that we have acknowledged, this is needed since we support accepting a packet but explicitly 35 | // not acknowledge it as received. 36 | uint16_t InAckSeqAck; // Last sequence number received from remote that we have acknowledged and also knows that the remote has received the ack, used to 37 | // calculate how big our history must be 38 | 39 | // Track outgoing sequence data 40 | uint16_t OutSeq; // Outgoing sequence number 41 | uint16_t OutAckSeq; // Last sequence number that we know that the remote side have received. 42 | 43 | size_t WrittenHistoryWordCount; // Bookkeeping to track if we can update data 44 | uint16_t WrittenInAckSeq; // When we call CommitAndIncrementOutSequence this will be committed along with the current outgoing sequence number for bookkeeping 45 | 46 | struct ring_buffer_t AckRecord; 47 | }; 48 | 49 | struct notification_header 50 | { 51 | uint16_t Seq; 52 | uint16_t AckedSeq; 53 | size_t HistoryWordCount; 54 | SequenceHistoryWord History[SequenceHistoryWordCount]; // typedef uint32 WordT; 55 | }; 56 | -------------------------------------------------------------------------------- /utcp/utcp_sequence_number.h: -------------------------------------------------------------------------------- 1 | // Copyright DPULL, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | #include 5 | #include 6 | 7 | #ifndef SequenceNumberBits 8 | #define SequenceNumberBits 14 9 | #endif 10 | 11 | #ifndef SequenceType 12 | #define SequenceType uint16_t 13 | #endif 14 | 15 | #ifndef DifferenceType 16 | #define DifferenceType int32_t 17 | #endif 18 | 19 | enum 20 | { 21 | SeqNumberCount = ((SequenceType)1) << SequenceNumberBits, 22 | SeqNumberHalf = ((SequenceType)1) << (SequenceNumberBits - 1), 23 | SeqNumberMax = SeqNumberCount - 1u, 24 | SeqNumberMask = SeqNumberMax, 25 | }; 26 | 27 | static inline SequenceType seq_num_init(SequenceType value) 28 | { 29 | return value & SeqNumberMask; 30 | } 31 | 32 | static inline bool seq_num_greater_than(SequenceType l, SequenceType r) 33 | { 34 | return (l != r) && (((l - r) & SeqNumberMask) < SeqNumberHalf); 35 | } 36 | 37 | static inline bool seq_num_greater_equal(SequenceType l, SequenceType r) 38 | { 39 | return ((l - r) & SeqNumberMask) < SeqNumberHalf; 40 | } 41 | 42 | static inline SequenceType seq_num_inc(SequenceType value, SequenceType i) 43 | { 44 | return seq_num_init(value + i); 45 | } 46 | 47 | static inline DifferenceType seq_num_diff(SequenceType a, SequenceType b) 48 | { 49 | const size_t ShiftValue = sizeof(DifferenceType) * 8 - SequenceNumberBits; 50 | return (DifferenceType)((a - b) << ShiftValue) >> ShiftValue; 51 | } -------------------------------------------------------------------------------- /utcp/utcp_utils.h: -------------------------------------------------------------------------------- 1 | // Copyright DPULL, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | #include "bit_buffer.h" 5 | #include "utcp_def_internal.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | extern struct utcp_config* utcp_get_config(); 13 | 14 | #if defined(__linux) || defined(__APPLE__) 15 | #define _countof(array) (sizeof(array) / sizeof(array[0])) 16 | #endif 17 | 18 | enum log_level 19 | { 20 | NoLogging = 0, 21 | Fatal, 22 | Error, 23 | Warning, 24 | Display, 25 | Log, 26 | Verbose, 27 | }; 28 | 29 | static inline void utcp_log(enum log_level level, const char* fmt, ...) 30 | { 31 | struct utcp_config* utcp_config = utcp_get_config(); 32 | if (utcp_config->on_log) 33 | { 34 | va_list marker; 35 | va_start(marker, fmt); 36 | utcp_config->on_log(level, fmt, marker); 37 | va_end(marker); 38 | } 39 | } 40 | 41 | static inline void* utcp_realloc(void* ptr, size_t size) 42 | { 43 | struct utcp_config* utcp_config = utcp_get_config(); 44 | if (!utcp_config->on_realloc) 45 | { 46 | if (size > 0) 47 | return realloc(ptr, size); 48 | // if new_size is zero, the behavior is undefined. (since C23) 49 | free(ptr); 50 | return NULL; 51 | } 52 | else 53 | { 54 | return utcp_config->on_realloc(ptr, size); 55 | } 56 | } 57 | 58 | static inline unsigned utcp_rand() 59 | { 60 | struct utcp_config* utcp_config = utcp_get_config(); 61 | if (!utcp_config->on_rand) 62 | { 63 | unsigned int lcg_random(void); 64 | return lcg_random(); 65 | } 66 | else 67 | { 68 | return utcp_config->on_rand(); 69 | } 70 | } 71 | 72 | static inline void utcp_dump(const char* debug_name, const char* type, const void* data, int len) 73 | { 74 | struct utcp_config* utcp_config = utcp_get_config(); 75 | if (!utcp_config->EnableDump) 76 | return; 77 | 78 | char str[UTCP_MAX_PACKET * 8]; 79 | int size = 0; 80 | 81 | for (int i = 0; i < len; ++i) 82 | { 83 | if (i != 0) 84 | { 85 | int ret = snprintf(str + size, sizeof(str) - size, ", "); 86 | if (ret < 0) 87 | break; 88 | size += ret; 89 | } 90 | 91 | int ret = snprintf(str + size, sizeof(str) - size, "0x%hhX", ((const uint8_t*)data)[i]); 92 | if (ret < 0) 93 | break; 94 | size += ret; 95 | } 96 | str[size] = '\0'; 97 | utcp_log(Verbose, "[%s][DUMP]%s\t%d\t{%s}", debug_name, type, len, str); 98 | } 99 | 100 | static inline int64_t utcp_gettime_ms(void) 101 | { 102 | struct utcp_config* utcp_config = utcp_get_config(); 103 | return utcp_config->ElapsedTime / 1000 + 1000; 104 | } 105 | 106 | static inline double utcp_gettime(void) 107 | { 108 | struct utcp_config* utcp_config = utcp_get_config(); 109 | return ((double)utcp_config->ElapsedTime) / 1000 / 1000 / 1000 + 1; 110 | } 111 | 112 | static inline void utcp_listener_outgoing(struct utcp_listener* fd, const void* buffer, size_t len) 113 | { 114 | utcp_dump("listener", "outgoing", buffer, (int)len); 115 | struct utcp_config* utcp_config = utcp_get_config(); 116 | if (utcp_config->on_outgoing) 117 | { 118 | utcp_config->on_outgoing(fd, fd->userdata, buffer, (int)len); 119 | } 120 | } 121 | 122 | static inline void utcp_connection_outgoing(struct utcp_connection* fd, const void* buffer, size_t len) 123 | { 124 | utcp_dump(fd->debug_name, "outgoing", buffer, (int)len); 125 | struct utcp_config* utcp_config = utcp_get_config(); 126 | if (utcp_config->on_outgoing) 127 | { 128 | utcp_config->on_outgoing(fd, fd->userdata, buffer, (int)len); 129 | } 130 | } 131 | 132 | #define utcp_raw_send(fd, buffer, len) _Generic((fd), struct utcp_listener *: utcp_listener_outgoing, struct utcp_connection *: utcp_connection_outgoing)(fd, buffer, len) 133 | 134 | static inline void utcp_on_accept(struct utcp_listener* fd, bool reconnect) 135 | { 136 | utcp_log(Log, "accept:%s, reconnect=%d", fd->LastChallengeSuccessAddress, reconnect); 137 | struct utcp_config* utcp_config = utcp_get_config(); 138 | if (utcp_config->on_accept) 139 | { 140 | utcp_config->on_accept(fd, fd->userdata, reconnect); 141 | } 142 | } 143 | 144 | static inline void utcp_on_connect(struct utcp_connection* fd, bool reconnect) 145 | { 146 | utcp_log(Log, "[%s]connected, reconnect=%d", fd->debug_name, reconnect); 147 | struct utcp_config* utcp_config = utcp_get_config(); 148 | if (utcp_config->on_connect) 149 | { 150 | utcp_config->on_connect(fd, fd->userdata, reconnect); 151 | } 152 | } 153 | 154 | static inline void utcp_on_disconnect(struct utcp_connection* fd, int close_reason) 155 | { 156 | struct utcp_config* utcp_config = utcp_get_config(); 157 | if (utcp_config->on_disconnect) 158 | { 159 | utcp_config->on_disconnect(fd, fd->userdata, close_reason); 160 | } 161 | } 162 | 163 | static inline void utcp_recv_bunch(struct utcp_connection* fd, struct utcp_bunch* bunches[], int bunches_count) 164 | { 165 | assert(bunches_count > 0); 166 | struct utcp_config* utcp_config = utcp_get_config(); 167 | if (utcp_config->on_recv_bunch) 168 | { 169 | utcp_config->on_recv_bunch(fd, fd->userdata, bunches, bunches_count); 170 | } 171 | } 172 | 173 | static inline void utcp_delivery_status(struct utcp_connection* fd, int32_t packet_id, bool ack) 174 | { 175 | struct utcp_config* utcp_config = utcp_get_config(); 176 | if (utcp_config->on_delivery_status) 177 | { 178 | utcp_config->on_delivery_status(fd, fd->userdata, packet_id, ack); 179 | } 180 | } 181 | --------------------------------------------------------------------------------