From adda4f64e3810b2a331ef459ab9f4137ee4719a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E5=B0=8F=E4=BA=91?= Date: Wed, 14 Jan 2026 15:38:20 +0800 Subject: [PATCH] first commit --- .clang-format | 89 + .gitattributes | 2 + .github/ISSUE_TEMPLATE/bug.md | 95 + .github/ISSUE_TEMPLATE/compile.md | 57 + .github/ISSUE_TEMPLATE/config.yml | 6 + .github/ISSUE_TEMPLATE/feature.md | 14 + .github/workflows/android.yml | 59 + .github/workflows/codeql.yml | 62 + .github/workflows/docker.yml | 88 + .github/workflows/issue_lint.yml | 58 + .github/workflows/linux.yml | 135 + .github/workflows/macos.yml | 71 + .github/workflows/style.yml | 27 + .github/workflows/windows.yml | 68 + .gitignore | 51 + .gitmodules_github | 12 + 3rdpart/CMakeLists.txt | 123 + 3rdpart/ZLToolKit/.clang-format | 89 + 3rdpart/ZLToolKit/.github/workflows/linux.yml | 46 + 3rdpart/ZLToolKit/.github/workflows/macos.yml | 40 + 3rdpart/ZLToolKit/.github/workflows/style.yml | 27 + .../ZLToolKit/.github/workflows/windows.yml | 30 + 3rdpart/ZLToolKit/.gitignore | 34 + 3rdpart/ZLToolKit/.travis.yml | 13 + 3rdpart/ZLToolKit/AUTHORS | 5 + 3rdpart/ZLToolKit/CMakeLists.txt | 145 + 3rdpart/ZLToolKit/LICENSE | 21 + 3rdpart/ZLToolKit/README.md | 138 + 3rdpart/ZLToolKit/cmake/FindMYSQL.cmake | 132 + 3rdpart/ZLToolKit/src/Network/Buffer.cpp | 35 + 3rdpart/ZLToolKit/src/Network/Buffer.h | 491 + 3rdpart/ZLToolKit/src/Network/BufferSock.cpp | 622 + 3rdpart/ZLToolKit/src/Network/BufferSock.h | 87 + 3rdpart/ZLToolKit/src/Network/Kcp.cpp | 911 ++ 3rdpart/ZLToolKit/src/Network/Kcp.h | 385 + 3rdpart/ZLToolKit/src/Network/Server.cpp | 87 + 3rdpart/ZLToolKit/src/Network/Server.h | 93 + 3rdpart/ZLToolKit/src/Network/Session.cpp | 40 + 3rdpart/ZLToolKit/src/Network/Session.h | 127 + 3rdpart/ZLToolKit/src/Network/Socket.cpp | 1181 ++ 3rdpart/ZLToolKit/src/Network/Socket.h | 1067 ++ 3rdpart/ZLToolKit/src/Network/Socket_ios.mm | 86 + 3rdpart/ZLToolKit/src/Network/TcpClient.cpp | 179 + 3rdpart/ZLToolKit/src/Network/TcpClient.h | 229 + 3rdpart/ZLToolKit/src/Network/TcpServer.cpp | 306 + 3rdpart/ZLToolKit/src/Network/TcpServer.h | 136 + 3rdpart/ZLToolKit/src/Network/UdpClient.cpp | 130 + 3rdpart/ZLToolKit/src/Network/UdpClient.h | 194 + 3rdpart/ZLToolKit/src/Network/UdpServer.cpp | 406 + 3rdpart/ZLToolKit/src/Network/UdpServer.h | 201 + 3rdpart/ZLToolKit/src/Network/sockutil.cpp | 1197 ++ 3rdpart/ZLToolKit/src/Network/sockutil.h | 587 + 3rdpart/ZLToolKit/src/Poller/EventPoller.cpp | 646 + 3rdpart/ZLToolKit/src/Poller/EventPoller.h | 422 + 3rdpart/ZLToolKit/src/Poller/Pipe.cpp | 62 + 3rdpart/ZLToolKit/src/Poller/Pipe.h | 35 + 3rdpart/ZLToolKit/src/Poller/PipeWrap.cpp | 96 + 3rdpart/ZLToolKit/src/Poller/PipeWrap.h | 35 + 3rdpart/ZLToolKit/src/Poller/SelectWrap.cpp | 52 + 3rdpart/ZLToolKit/src/Poller/SelectWrap.h | 32 + 3rdpart/ZLToolKit/src/Poller/Timer.cpp | 44 + 3rdpart/ZLToolKit/src/Poller/Timer.h | 46 + 3rdpart/ZLToolKit/src/README.md | 64 + 3rdpart/ZLToolKit/src/Thread/TaskExecutor.cpp | 256 + 3rdpart/ZLToolKit/src/Thread/TaskExecutor.h | 308 + 3rdpart/ZLToolKit/src/Thread/TaskQueue.h | 76 + 3rdpart/ZLToolKit/src/Thread/ThreadPool.h | 173 + .../ZLToolKit/src/Thread/WorkThreadPool.cpp | 43 + 3rdpart/ZLToolKit/src/Thread/WorkThreadPool.h | 82 + 3rdpart/ZLToolKit/src/Thread/semaphore.h | 121 + 3rdpart/ZLToolKit/src/Thread/threadgroup.h | 85 + 3rdpart/ZLToolKit/src/Util/Byte.hpp | 175 + 3rdpart/ZLToolKit/src/Util/CMD.cpp | 124 + 3rdpart/ZLToolKit/src/Util/CMD.h | 384 + 3rdpart/ZLToolKit/src/Util/File.cpp | 384 + 3rdpart/ZLToolKit/src/Util/File.h | 206 + 3rdpart/ZLToolKit/src/Util/List.h | 218 + 3rdpart/ZLToolKit/src/Util/MD5.cpp | 372 + 3rdpart/ZLToolKit/src/Util/MD5.h | 97 + 3rdpart/ZLToolKit/src/Util/NoticeCenter.cpp | 19 + 3rdpart/ZLToolKit/src/Util/NoticeCenter.h | 206 + 3rdpart/ZLToolKit/src/Util/ResourcePool.h | 227 + 3rdpart/ZLToolKit/src/Util/RingBuffer.h | 515 + 3rdpart/ZLToolKit/src/Util/SHA1.cpp | 341 + 3rdpart/ZLToolKit/src/Util/SHA1.h | 47 + 3rdpart/ZLToolKit/src/Util/SSLBox.cpp | 550 + 3rdpart/ZLToolKit/src/Util/SSLBox.h | 290 + 3rdpart/ZLToolKit/src/Util/SSLUtil.cpp | 399 + 3rdpart/ZLToolKit/src/Util/SSLUtil.h | 193 + 3rdpart/ZLToolKit/src/Util/SpeedStatistic.h | 79 + 3rdpart/ZLToolKit/src/Util/SqlConnection.h | 287 + 3rdpart/ZLToolKit/src/Util/SqlPool.cpp | 27 + 3rdpart/ZLToolKit/src/Util/SqlPool.h | 383 + 3rdpart/ZLToolKit/src/Util/TimeTicker.h | 172 + 3rdpart/ZLToolKit/src/Util/base64.cpp | 202 + 3rdpart/ZLToolKit/src/Util/base64.h | 81 + 3rdpart/ZLToolKit/src/Util/function_traits.h | 58 + 3rdpart/ZLToolKit/src/Util/local_time.cpp | 174 + 3rdpart/ZLToolKit/src/Util/local_time.h | 15 + 3rdpart/ZLToolKit/src/Util/logger.cpp | 654 + 3rdpart/ZLToolKit/src/Util/logger.h | 564 + 3rdpart/ZLToolKit/src/Util/mini.cpp | 59 + 3rdpart/ZLToolKit/src/Util/mini.h | 247 + 3rdpart/ZLToolKit/src/Util/onceToken.h | 51 + 3rdpart/ZLToolKit/src/Util/strptime_win.cpp | 366 + 3rdpart/ZLToolKit/src/Util/strptime_win.h | 12 + 3rdpart/ZLToolKit/src/Util/util.cpp | 728 + 3rdpart/ZLToolKit/src/Util/util.h | 621 + 3rdpart/ZLToolKit/src/Util/uv_errno.cpp | 189 + 3rdpart/ZLToolKit/src/Util/uv_errno.h | 526 + 3rdpart/ZLToolKit/src/win32/LICENSE | 28 + 3rdpart/ZLToolKit/src/win32/README.md | 202 + 3rdpart/ZLToolKit/src/win32/getopt.c | 761 + 3rdpart/ZLToolKit/src/win32/getopt.h | 128 + 3rdpart/ZLToolKit/src/win32/sys/epoll.h | 19 + 3rdpart/ZLToolKit/src/win32/tailor.h | 329 + 3rdpart/ZLToolKit/src/win32/wepoll.c | 2060 +++ 3rdpart/ZLToolKit/src/win32/wepoll.h | 107 + 3rdpart/ZLToolKit/tests/CMakeLists.txt | 10 + 3rdpart/ZLToolKit/tests/ssl.p12 | Bin 0 -> 2417 bytes 3rdpart/ZLToolKit/tests/test_creator.cpp | 193 + 3rdpart/ZLToolKit/tests/test_delayTask.cpp | 74 + 3rdpart/ZLToolKit/tests/test_eventPoller.cpp | 67 + 3rdpart/ZLToolKit/tests/test_ini.cpp | 66 + 3rdpart/ZLToolKit/tests/test_kcpClient.cpp | 71 + .../ZLToolKit/tests/test_kcpEchoServer.cpp | 114 + .../tests/test_kcpLantencyClient.cpp | 129 + 3rdpart/ZLToolKit/tests/test_logger.cpp | 98 + 3rdpart/ZLToolKit/tests/test_noticeCenter.cpp | 80 + 3rdpart/ZLToolKit/tests/test_pingpong.cpp | 168 + 3rdpart/ZLToolKit/tests/test_pipe.cpp | 69 + 3rdpart/ZLToolKit/tests/test_resourcePool.cpp | 162 + 3rdpart/ZLToolKit/tests/test_ringBuffer.cpp | 123 + 3rdpart/ZLToolKit/tests/test_semaphore.cpp | 92 + 3rdpart/ZLToolKit/tests/test_shell.cpp | 191 + 3rdpart/ZLToolKit/tests/test_sql.cpp | 118 + 3rdpart/ZLToolKit/tests/test_ssl.cpp | 82 + 3rdpart/ZLToolKit/tests/test_tcpClient.cpp | 83 + .../ZLToolKit/tests/test_tcpEchoServer.cpp | 82 + .../tests/test_tcpLantencyClient.cpp | 118 + 3rdpart/ZLToolKit/tests/test_threadPool.cpp | 65 + .../tests/test_threadPoolBenchmark.cpp | 56 + 3rdpart/ZLToolKit/tests/test_timer.cpp | 53 + 3rdpart/ZLToolKit/tests/test_udpClient.cpp | 71 + .../ZLToolKit/tests/test_udpEchoServer.cpp | 73 + .../tests/test_udpLantencyClient.cpp | 114 + 3rdpart/ZLToolKit/tests/test_udpSock.cpp | 72 + 3rdpart/ZLToolKit/tests/test_variant.cpp | 33 + 3rdpart/assert.h | 30 + 3rdpart/jsoncpp/.clang-format | 4 + 3rdpart/jsoncpp/.clang-tidy | 11 + 3rdpart/jsoncpp/.gitattributes | 11 + .../.github/ISSUE_TEMPLATE/bug_report.md | 26 + .../.github/ISSUE_TEMPLATE/feature_request.md | 20 + .../.github/workflows/clang-format.yml | 20 + 3rdpart/jsoncpp/.github/workflows/cmake.yml | 18 + 3rdpart/jsoncpp/.github/workflows/meson.yml | 65 + 3rdpart/jsoncpp/.gitignore | 61 + 3rdpart/jsoncpp/AUTHORS | 115 + 3rdpart/jsoncpp/BUILD.bazel | 37 + 3rdpart/jsoncpp/CMakeLists.txt | 214 + 3rdpart/jsoncpp/CONTRIBUTING.md | 152 + 3rdpart/jsoncpp/CTestConfig.cmake | 15 + 3rdpart/jsoncpp/LICENSE | 55 + 3rdpart/jsoncpp/MODULE.bazel | 14 + 3rdpart/jsoncpp/README.md | 67 + 3rdpart/jsoncpp/SECURITY.md | 17 + 3rdpart/jsoncpp/amalgamate.py | 161 + 3rdpart/jsoncpp/appveyor.yml | 37 + 3rdpart/jsoncpp/cmake/JoinPaths.cmake | 23 + 3rdpart/jsoncpp/dev.makefile | 37 + 3rdpart/jsoncpp/devtools/__init__.py | 6 + 3rdpart/jsoncpp/devtools/agent_vmw7.json | 33 + 3rdpart/jsoncpp/devtools/agent_vmxp.json | 26 + 3rdpart/jsoncpp/devtools/antglob.py | 205 + 3rdpart/jsoncpp/devtools/batchbuild.py | 278 + 3rdpart/jsoncpp/devtools/fixeol.py | 70 + 3rdpart/jsoncpp/devtools/licenseupdater.py | 94 + 3rdpart/jsoncpp/devtools/tarball.py | 52 + 3rdpart/jsoncpp/doc/doxyfile.in | 2302 +++ 3rdpart/jsoncpp/doc/footer.html | 21 + 3rdpart/jsoncpp/doc/header.html | 64 + 3rdpart/jsoncpp/doc/jsoncpp.dox | 164 + 3rdpart/jsoncpp/doc/readme.txt | 1 + 3rdpart/jsoncpp/doc/roadmap.dox | 3 + 3rdpart/jsoncpp/doc/web_doxyfile.in | 2290 +++ 3rdpart/jsoncpp/doxybuild.py | 189 + 3rdpart/jsoncpp/example/BUILD.bazel | 33 + 3rdpart/jsoncpp/example/CMakeLists.txt | 27 + 3rdpart/jsoncpp/example/README.md | 13 + .../example/readFromStream/errorFormat.json | 3 + .../example/readFromStream/readFromStream.cpp | 30 + .../example/readFromStream/withComment.json | 6 + .../example/readFromString/readFromString.cpp | 38 + .../example/streamWrite/streamWrite.cpp | 23 + .../example/stringWrite/stringWrite.cpp | 33 + 3rdpart/jsoncpp/get_version.pl | 5 + 3rdpart/jsoncpp/include/CMakeLists.txt | 5 + .../include/PreventInBuildInstalls.cmake | 9 + .../include/PreventInSourceBuilds.cmake | 45 + 3rdpart/jsoncpp/include/json/allocator.h | 100 + 3rdpart/jsoncpp/include/json/assertions.h | 61 + 3rdpart/jsoncpp/include/json/config.h | 150 + 3rdpart/jsoncpp/include/json/forwards.h | 43 + 3rdpart/jsoncpp/include/json/json.h | 15 + 3rdpart/jsoncpp/include/json/json_features.h | 62 + 3rdpart/jsoncpp/include/json/reader.h | 437 + 3rdpart/jsoncpp/include/json/value.h | 1037 ++ 3rdpart/jsoncpp/include/json/version.h | 28 + 3rdpart/jsoncpp/include/json/writer.h | 368 + .../jsoncpp/jsoncpp-namespaced-targets.cmake | 9 + 3rdpart/jsoncpp/jsoncppConfig.cmake.in | 11 + 3rdpart/jsoncpp/jsoncppConfig.cmake.meson.in | 6 + 3rdpart/jsoncpp/meson.build | 157 + 3rdpart/jsoncpp/meson_options.txt | 5 + 3rdpart/jsoncpp/pkg-config/jsoncpp.pc.in | 11 + 3rdpart/jsoncpp/reformat.sh | 1 + 3rdpart/jsoncpp/src/CMakeLists.txt | 5 + .../jsoncpp/src/jsontestrunner/CMakeLists.txt | 51 + 3rdpart/jsoncpp/src/jsontestrunner/main.cpp | 346 + 3rdpart/jsoncpp/src/lib_json/CMakeLists.txt | 205 + 3rdpart/jsoncpp/src/lib_json/json_reader.cpp | 2005 +++ 3rdpart/jsoncpp/src/lib_json/json_tool.h | 138 + 3rdpart/jsoncpp/src/lib_json/json_value.cpp | 1760 +++ .../src/lib_json/json_valueiterator.inl | 156 + 3rdpart/jsoncpp/src/lib_json/json_writer.cpp | 1201 ++ .../jsoncpp/src/test_lib_json/CMakeLists.txt | 39 + 3rdpart/jsoncpp/src/test_lib_json/fuzz.cpp | 54 + 3rdpart/jsoncpp/src/test_lib_json/fuzz.dict | 54 + 3rdpart/jsoncpp/src/test_lib_json/fuzz.h | 14 + .../jsoncpp/src/test_lib_json/jsontest.cpp | 430 + 3rdpart/jsoncpp/src/test_lib_json/jsontest.h | 288 + 3rdpart/jsoncpp/src/test_lib_json/main.cpp | 4106 +++++ 3rdpart/jsoncpp/test/cleantests.py | 16 + .../jsoncpp/test/data/fail_invalid_quote.json | 1 + .../test/data/fail_strict_comment_01.json | 4 + .../test/data/fail_strict_comment_02.json | 4 + .../test/data/fail_strict_comment_03.json | 3 + .../jsoncpp/test/data/fail_test_array_01.json | 1 + .../jsoncpp/test/data/fail_test_array_02.json | 1 + .../test/data/fail_test_object_01.json | 1 + .../test/data/fail_test_object_02.json | 1 + .../test/data/fail_test_stack_limit.json | 1 + .../test/data/legacy_test_array_01.expected | 1 + .../test/data/legacy_test_array_01.json | 1 + .../test/data/legacy_test_array_02.expected | 2 + .../test/data/legacy_test_array_02.json | 1 + .../test/data/legacy_test_array_03.expected | 6 + .../test/data/legacy_test_array_03.json | 1 + .../test/data/legacy_test_array_04.expected | 5 + .../test/data/legacy_test_array_04.json | 1 + .../test/data/legacy_test_array_05.expected | 100 + .../test/data/legacy_test_array_05.json | 1 + .../test/data/legacy_test_array_06.expected | 5 + .../test/data/legacy_test_array_06.json | 4 + .../test/data/legacy_test_array_07.expected | 2122 +++ .../test/data/legacy_test_array_07.json | 2 + .../test/data/legacy_test_basic_01.expected | 1 + .../test/data/legacy_test_basic_01.json | 1 + .../test/data/legacy_test_basic_02.expected | 1 + .../test/data/legacy_test_basic_02.json | 1 + .../test/data/legacy_test_basic_03.expected | 3 + .../test/data/legacy_test_basic_03.json | 3 + .../test/data/legacy_test_basic_04.expected | 2 + .../test/data/legacy_test_basic_04.json | 2 + .../test/data/legacy_test_basic_05.expected | 2 + .../test/data/legacy_test_basic_05.json | 2 + .../test/data/legacy_test_basic_06.expected | 2 + .../test/data/legacy_test_basic_06.json | 2 + .../test/data/legacy_test_basic_07.expected | 2 + .../test/data/legacy_test_basic_07.json | 2 + .../test/data/legacy_test_basic_08.expected | 3 + .../test/data/legacy_test_basic_08.json | 3 + .../test/data/legacy_test_basic_09.expected | 4 + .../test/data/legacy_test_basic_09.json | 4 + .../test/data/legacy_test_comment_00.expected | 4 + .../test/data/legacy_test_comment_00.json | 5 + .../test/data/legacy_test_comment_01.expected | 10 + .../test/data/legacy_test_comment_01.json | 10 + .../test/data/legacy_test_comment_02.expected | 23 + .../test/data/legacy_test_comment_02.json | 26 + .../test/data/legacy_test_complex_01.expected | 20 + .../test/data/legacy_test_complex_01.json | 17 + .../test/data/legacy_test_integer_01.expected | 2 + .../test/data/legacy_test_integer_01.json | 2 + .../test/data/legacy_test_integer_02.expected | 2 + .../test/data/legacy_test_integer_02.json | 2 + .../test/data/legacy_test_integer_03.expected | 2 + .../test/data/legacy_test_integer_03.json | 2 + .../test/data/legacy_test_integer_04.expected | 3 + .../test/data/legacy_test_integer_04.json | 3 + .../test/data/legacy_test_integer_05.expected | 2 + .../test/data/legacy_test_integer_05.json | 2 + .../legacy_test_integer_06_64bits.expected | 1 + .../data/legacy_test_integer_06_64bits.json | 2 + .../legacy_test_integer_07_64bits.expected | 1 + .../data/legacy_test_integer_07_64bits.json | 2 + .../legacy_test_integer_08_64bits.expected | 1 + .../data/legacy_test_integer_08_64bits.json | 2 + .../test/data/legacy_test_large_01.expected | 2122 +++ .../test/data/legacy_test_large_01.json | 2 + .../test/data/legacy_test_object_01.expected | 1 + .../test/data/legacy_test_object_01.json | 1 + .../test/data/legacy_test_object_02.expected | 2 + .../test/data/legacy_test_object_02.json | 1 + .../test/data/legacy_test_object_03.expected | 4 + .../test/data/legacy_test_object_03.json | 5 + .../test/data/legacy_test_object_04.expected | 2 + .../test/data/legacy_test_object_04.json | 3 + .../legacy_test_preserve_comment_01.expected | 11 + .../data/legacy_test_preserve_comment_01.json | 14 + .../test/data/legacy_test_real_01.expected | 3 + .../test/data/legacy_test_real_01.json | 3 + .../test/data/legacy_test_real_02.expected | 3 + .../test/data/legacy_test_real_02.json | 3 + .../test/data/legacy_test_real_03.expected | 3 + .../test/data/legacy_test_real_03.json | 3 + .../test/data/legacy_test_real_04.expected | 3 + .../test/data/legacy_test_real_04.json | 3 + .../test/data/legacy_test_real_05.expected | 4 + .../test/data/legacy_test_real_05.json | 3 + .../test/data/legacy_test_real_06.expected | 4 + .../test/data/legacy_test_real_06.json | 3 + .../test/data/legacy_test_real_07.expected | 4 + .../test/data/legacy_test_real_07.json | 3 + .../test/data/legacy_test_real_08.expected | 4 + .../test/data/legacy_test_real_08.json | 4 + .../test/data/legacy_test_real_09.expected | 4 + .../test/data/legacy_test_real_09.json | 4 + .../test/data/legacy_test_real_10.expected | 4 + .../test/data/legacy_test_real_10.json | 4 + .../test/data/legacy_test_real_11.expected | 4 + .../test/data/legacy_test_real_11.json | 4 + .../test/data/legacy_test_real_12.expected | 2 + .../test/data/legacy_test_real_12.json | 2 + .../test/data/legacy_test_real_13.expected | 3 + .../test/data/legacy_test_real_13.json | 1 + .../test/data/legacy_test_string_01.expected | 1 + .../test/data/legacy_test_string_01.json | 1 + .../test/data/legacy_test_string_02.expected | 1 + .../test/data/legacy_test_string_02.json | 1 + .../test/data/legacy_test_string_03.expected | 1 + .../test/data/legacy_test_string_03.json | 1 + .../test/data/legacy_test_string_04.expected | 2 + .../test/data/legacy_test_string_04.json | 2 + .../test/data/legacy_test_string_05.expected | 2 + .../test/data/legacy_test_string_05.json | 2 + .../legacy_test_string_unicode_01.expected | 1 + .../data/legacy_test_string_unicode_01.json | 1 + .../legacy_test_string_unicode_02.expected | 1 + .../data/legacy_test_string_unicode_02.json | 1 + .../legacy_test_string_unicode_03.expected | 1 + .../data/legacy_test_string_unicode_03.json | 1 + .../legacy_test_string_unicode_04.expected | 1 + .../data/legacy_test_string_unicode_04.json | 1 + .../legacy_test_string_unicode_05.expected | 2 + .../data/legacy_test_string_unicode_05.json | 1 + .../jsoncpp/test/data/test_array_08.expected | 2 + 3rdpart/jsoncpp/test/data/test_array_08.json | 1 + .../jsoncpp/test/data/test_object_05.expected | 2 + 3rdpart/jsoncpp/test/data/test_object_05.json | 1 + 3rdpart/jsoncpp/test/generate_expected.py | 17 + 3rdpart/jsoncpp/test/jsonchecker/fail1.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail10.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail11.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail12.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail13.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail14.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail15.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail16.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail17.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail18.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail19.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail2.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail20.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail21.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail22.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail23.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail24.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail25.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail26.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail27.json | 2 + 3rdpart/jsoncpp/test/jsonchecker/fail28.json | 2 + 3rdpart/jsoncpp/test/jsonchecker/fail29.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail3.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail30.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail31.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail32.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail33.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail4.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail5.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail6.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail7.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail8.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/fail9.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/pass1.json | 58 + 3rdpart/jsoncpp/test/jsonchecker/pass2.json | 1 + 3rdpart/jsoncpp/test/jsonchecker/pass3.json | 6 + 3rdpart/jsoncpp/test/jsonchecker/readme.txt | 3 + 3rdpart/jsoncpp/test/pyjsontestrunner.py | 71 + 3rdpart/jsoncpp/test/runjsontests.py | 194 + 3rdpart/jsoncpp/test/rununittests.py | 84 + 3rdpart/jsoncpp/version.in | 1 + .../media-server/.github/workflows/ccpp.yml | 33 + .../.github/workflows/codeql-analysis.yml | 84 + .../.github/workflows/sonarcloud.yml | 68 + 3rdpart/media-server/.gitignore | 11 + 3rdpart/media-server/.travis.yml | 32 + 3rdpart/media-server/Android.mk | 72 + 3rdpart/media-server/Application.mk | 4 + 3rdpart/media-server/LICENSE | 21 + 3rdpart/media-server/Makefile | 57 + 3rdpart/media-server/README.md | 68 + 3rdpart/media-server/SECURITY.md | 13 + 3rdpart/media-server/compile.cn.md | 69 + 3rdpart/media-server/gcc.mk | 154 + 3rdpart/media-server/libdash/Android.mk | 16 + 3rdpart/media-server/libdash/Makefile | 46 + .../media-server/libdash/include/dash-mpd.h | 31 + .../libdash/include/dash-parser.h | 483 + .../media-server/libdash/include/dash-proto.h | 10 + .../libdash/include/xs-datatype.h | 19 + 3rdpart/media-server/libdash/libdash.vcxproj | 151 + .../libdash/libdash.vcxproj.filters | 54 + .../libdash/libdash.xcodeproj/project.pbxproj | 330 + .../libdash/src/dash-adaptation.c | 77 + 3rdpart/media-server/libdash/src/dash-mpd.c | 552 + .../media-server/libdash/src/dash-parser.c | 1537 ++ .../media-server/libdash/src/dash-period.c | 182 + .../libdash/src/dash-representation.c | 189 + .../media-server/libdash/src/dash-segment.c | 210 + 3rdpart/media-server/libdash/src/list.h | 61 + .../media-server/libdash/src/xs-duration.c | 160 + .../libdash/test/dash-dynamic-test.cpp | 249 + .../libdash/test/dash-static-test.cpp | 85 + 3rdpart/media-server/libflv/Android.mk | 14 + 3rdpart/media-server/libflv/Makefile | 44 + 3rdpart/media-server/libflv/include/amf0.h | 70 + 3rdpart/media-server/libflv/include/amf3.h | 56 + 3rdpart/media-server/libflv/include/aom-av1.h | 54 + .../media-server/libflv/include/avswg-avs3.h | 37 + .../media-server/libflv/include/flv-demuxer.h | 38 + .../media-server/libflv/include/flv-header.h | 125 + .../media-server/libflv/include/flv-muxer.h | 79 + .../media-server/libflv/include/flv-parser.h | 53 + .../media-server/libflv/include/flv-proto.h | 136 + .../media-server/libflv/include/flv-reader.h | 26 + .../media-server/libflv/include/flv-writer.h | 41 + .../media-server/libflv/include/mp3-header.h | 109 + .../media-server/libflv/include/mpeg4-aac.h | 156 + .../media-server/libflv/include/mpeg4-avc.h | 78 + .../media-server/libflv/include/mpeg4-bits.h | 201 + .../media-server/libflv/include/mpeg4-hevc.h | 68 + .../media-server/libflv/include/mpeg4-vvc.h | 73 + .../media-server/libflv/include/opus-head.h | 40 + .../media-server/libflv/include/riff-acm.h | 43 + .../media-server/libflv/include/webm-vpx.h | 35 + .../media-server/libflv/include/xiph-flac.h | 32 + 3rdpart/media-server/libflv/libflv.vcxproj | 188 + .../libflv/libflv.vcxproj.filters | 165 + .../libflv/libflv.xcodeproj/project.pbxproj | 396 + 3rdpart/media-server/libflv/source/amf0.c | 600 + 3rdpart/media-server/libflv/source/amf3.c | 95 + 3rdpart/media-server/libflv/source/aom-av1.c | 607 + .../media-server/libflv/source/avswg-avs3.c | 90 + .../libflv/source/flv-demuxer-script.c | 84 + .../media-server/libflv/source/flv-demuxer.c | 361 + .../media-server/libflv/source/flv-header.c | 474 + .../media-server/libflv/source/flv-muxer.c | 622 + .../media-server/libflv/source/flv-parser.c | 261 + .../media-server/libflv/source/flv-reader.c | 135 + .../media-server/libflv/source/flv-writer.c | 162 + .../libflv/source/hevc-annexbtomp4.c | 474 + .../libflv/source/hevc-mp4toannexb.c | 134 + .../media-server/libflv/source/mp3-header.c | 233 + .../libflv/source/mpeg4-aac-asc.c | 664 + .../media-server/libflv/source/mpeg4-aac.c | 364 + .../libflv/source/mpeg4-annexbtomp4.c | 518 + .../media-server/libflv/source/mpeg4-avc.c | 277 + .../media-server/libflv/source/mpeg4-hevc.c | 329 + .../libflv/source/mpeg4-mp4toannexb.c | 170 + .../media-server/libflv/source/mpeg4-vvc.c | 430 + .../media-server/libflv/source/opus-head.c | 420 + 3rdpart/media-server/libflv/source/riff-acm.c | 51 + .../libflv/source/vvc-annexbtomp4.c | 433 + .../libflv/source/vvc-mp4toannexb.c | 138 + 3rdpart/media-server/libflv/source/webm-vpx.c | 171 + .../media-server/libflv/source/xiph-flac.c | 157 + 3rdpart/media-server/libflv/test/amf0-test.c | 51 + .../media-server/libflv/test/av1-flv-test.cpp | 50 + .../libflv/test/flv-parser-test.cpp | 86 + .../libflv/test/flv-read-write-test.cpp | 85 + .../libflv/test/flv-reader-test.cpp | 132 + .../media-server/libflv/test/flv2ts-test.cpp | 157 + .../libflv/test/h264-flv-test.cpp | 60 + .../libflv/test/h265-flv-test.cpp | 61 + .../libflv/test/http-flv-live.cpp | 228 + .../libflv/test/rtmp.onStatus.amf0 | Bin 0 -> 364 bytes .../media-server/libflv/test/ts2flv-test.cpp | 80 + 3rdpart/media-server/libflv/version.ver | 2 + 3rdpart/media-server/libhls/Android.mk | 16 + 3rdpart/media-server/libhls/Makefile | 45 + 3rdpart/media-server/libhls/demo/Makefile | 62 + .../media-server/libhls/demo/alternate.m3u8 | 17 + .../media-server/libhls/demo/alternate1.m3u8 | 16 + .../libhls/demo/hls-segmenter-flv.cpp | 89 + .../libhls/demo/hls-segmenter-mp4.cpp | 157 + .../media-server/libhls/demo/hls-server.cpp | 307 + 3rdpart/media-server/libhls/demo/master.m3u8 | 16 + .../media-server/libhls/demo/playlist.m3u8 | 20 + 3rdpart/media-server/libhls/demo/说明.txt | 9 + .../media-server/libhls/include/hls-fmp4.h | 51 + .../media-server/libhls/include/hls-m3u8.h | 44 + .../media-server/libhls/include/hls-media.h | 48 + .../media-server/libhls/include/hls-param.h | 80 + .../media-server/libhls/include/hls-parser.h | 201 + .../media-server/libhls/include/hls-string.h | 27 + 3rdpart/media-server/libhls/libhls.vcxproj | 155 + .../libhls/libhls.vcxproj.filters | 63 + .../libhls/libhls.xcodeproj/project.pbxproj | 338 + 3rdpart/media-server/libhls/source/hls-fmp4.c | 242 + 3rdpart/media-server/libhls/source/hls-h264.h | 66 + 3rdpart/media-server/libhls/source/hls-m3u8.c | 207 + .../media-server/libhls/source/hls-master.c | 78 + .../media-server/libhls/source/hls-media.c | 198 + .../media-server/libhls/source/hls-parser.c | 819 + .../media-server/libhls/source/hls-playlist.c | 15 + .../media-server/libhls/source/hls-string.c | 84 + 3rdpart/media-server/libhls/source/list.h | 61 + 3rdpart/media-server/libhls/version.ver | 2 + 3rdpart/media-server/libmkv/Android.mk | 14 + 3rdpart/media-server/libmkv/Makefile | 42 + 3rdpart/media-server/libmkv/include/ebml.h | 66 + .../media-server/libmkv/include/mkv-buffer.h | 54 + .../media-server/libmkv/include/mkv-format.h | 232 + .../media-server/libmkv/include/mkv-reader.h | 55 + .../media-server/libmkv/include/mkv-writer.h | 41 + 3rdpart/media-server/libmkv/libmkv.vcxproj | 181 + .../libmkv/libmkv.vcxproj.filters | 72 + .../libmkv/libmkv.xcodeproj/project.pbxproj | 336 + .../contents.xcworkspacedata | 7 + 3rdpart/media-server/libmkv/src/ebml.c | 91 + .../media-server/libmkv/src/mkv-attachment.c | 1 + 3rdpart/media-server/libmkv/src/mkv-chapter.c | 1 + 3rdpart/media-server/libmkv/src/mkv-cluster.c | 241 + 3rdpart/media-server/libmkv/src/mkv-codec.c | 124 + 3rdpart/media-server/libmkv/src/mkv-cue.c | 55 + .../media-server/libmkv/src/mkv-internal.h | 248 + 3rdpart/media-server/libmkv/src/mkv-ioutil.c | 111 + 3rdpart/media-server/libmkv/src/mkv-ioutil.h | 178 + 3rdpart/media-server/libmkv/src/mkv-reader.c | 1142 ++ 3rdpart/media-server/libmkv/src/mkv-track.c | 169 + 3rdpart/media-server/libmkv/src/mkv-writer.c | 411 + .../libmkv/test/mkv-2-mp4-test.cpp | 125 + .../libmkv/test/mkv-file-buffer.c | 156 + .../libmkv/test/mkv-reader-test.cpp | 225 + .../libmkv/test/mkv-writer-test.cpp | 120 + .../libmkv/test/mkv-writer-test2.cpp | 58 + .../libmkv/test/mvk-writer-audio.cpp | 112 + 3rdpart/media-server/libmov/Android.mk | 14 + 3rdpart/media-server/libmov/Makefile | 42 + .../media-server/libmov/include/fmp4-writer.h | 49 + .../media-server/libmov/include/mov-atom.h | 182 + 3rdpart/media-server/libmov/include/mov-box.h | 25 + .../media-server/libmov/include/mov-buffer.h | 33 + .../media-server/libmov/include/mov-format.h | 54 + .../libmov/include/mov-memory-buffer.h | 89 + .../media-server/libmov/include/mov-reader.h | 54 + .../media-server/libmov/include/mov-udta.h | 53 + .../media-server/libmov/include/mov-writer.h | 40 + .../media-server/libmov/include/mp4-writer.h | 131 + 3rdpart/media-server/libmov/libmov.vcxproj | 188 + .../libmov/libmov.vcxproj.filters | 165 + .../libmov/libmov.xcodeproj/project.pbxproj | 462 + .../media-server/libmov/source/fmp4-reader.c | 119 + .../media-server/libmov/source/fmp4-writer.c | 545 + 3rdpart/media-server/libmov/source/mov-avc1.c | 76 + 3rdpart/media-server/libmov/source/mov-dinf.c | 32 + 3rdpart/media-server/libmov/source/mov-elst.c | 136 + 3rdpart/media-server/libmov/source/mov-esds.c | 391 + 3rdpart/media-server/libmov/source/mov-ftyp.c | 54 + 3rdpart/media-server/libmov/source/mov-hdlr.c | 43 + 3rdpart/media-server/libmov/source/mov-hdr.c | 37 + .../media-server/libmov/source/mov-internal.h | 335 + 3rdpart/media-server/libmov/source/mov-iods.c | 90 + .../media-server/libmov/source/mov-ioutil.h | 123 + 3rdpart/media-server/libmov/source/mov-leva.c | 35 + 3rdpart/media-server/libmov/source/mov-mdhd.c | 76 + 3rdpart/media-server/libmov/source/mov-mehd.c | 33 + 3rdpart/media-server/libmov/source/mov-mfhd.c | 22 + 3rdpart/media-server/libmov/source/mov-minf.c | 166 + 3rdpart/media-server/libmov/source/mov-mvhd.c | 125 + 3rdpart/media-server/libmov/source/mov-opus.c | 77 + .../media-server/libmov/source/mov-reader.c | 687 + 3rdpart/media-server/libmov/source/mov-sidx.c | 73 + 3rdpart/media-server/libmov/source/mov-stco.c | 174 + 3rdpart/media-server/libmov/source/mov-stsc.c | 86 + 3rdpart/media-server/libmov/source/mov-stsd.c | 576 + 3rdpart/media-server/libmov/source/mov-stss.c | 79 + 3rdpart/media-server/libmov/source/mov-stsz.c | 131 + 3rdpart/media-server/libmov/source/mov-stts.c | 243 + 3rdpart/media-server/libmov/source/mov-tag.c | 63 + 3rdpart/media-server/libmov/source/mov-tfdt.c | 48 + 3rdpart/media-server/libmov/source/mov-tfhd.c | 98 + 3rdpart/media-server/libmov/source/mov-tfra.c | 90 + 3rdpart/media-server/libmov/source/mov-tkhd.c | 135 + .../media-server/libmov/source/mov-track.c | 385 + 3rdpart/media-server/libmov/source/mov-trex.c | 37 + 3rdpart/media-server/libmov/source/mov-trun.c | 163 + 3rdpart/media-server/libmov/source/mov-tx3g.c | 115 + 3rdpart/media-server/libmov/source/mov-udta.c | 148 + 3rdpart/media-server/libmov/source/mov-vpcc.c | 40 + .../media-server/libmov/source/mov-writer.c | 343 + .../libmov/test/fmp4-writer-test.cpp | 110 + .../libmov/test/fmp4-writer-test2.cpp | 56 + .../media-server/libmov/test/mov-2-flv.cpp | 141 + .../libmov/test/mov-file-buffer.c | 161 + .../libmov/test/mov-file-buffer.h | 27 + .../libmov/test/mov-reader-test.cpp | 271 + .../libmov/test/mov-writer-adts.cpp | 95 + .../libmov/test/mov-writer-audio.cpp | 100 + .../libmov/test/mov-writer-av1.cpp | 138 + .../libmov/test/mov-writer-h264.cpp | 128 + .../libmov/test/mov-writer-h265.cpp | 136 + .../libmov/test/mov-writer-subtitle.cpp | 76 + .../libmov/test/mov-writer-test.cpp | 160 + .../libmov/test/mov-writer-vp9.cpp | 119 + 3rdpart/media-server/libmpeg/Android.mk | 14 + 3rdpart/media-server/libmpeg/Makefile | 42 + 3rdpart/media-server/libmpeg/README.md | 18 + .../libmpeg/include/mpeg-element-descriptor.h | 289 + .../media-server/libmpeg/include/mpeg-muxer.h | 63 + .../libmpeg/include/mpeg-pes-proto.h | 6 + .../media-server/libmpeg/include/mpeg-proto.h | 157 + .../libmpeg/include/mpeg-ps-proto.h | 6 + .../media-server/libmpeg/include/mpeg-ps.h | 112 + .../libmpeg/include/mpeg-ts-proto.h | 6 + .../media-server/libmpeg/include/mpeg-ts.h | 102 + .../media-server/libmpeg/include/mpeg-types.h | 12 + .../media-server/libmpeg/include/mpeg-util.h | 179 + 3rdpart/media-server/libmpeg/libmpeg.vcxproj | 174 + .../libmpeg/libmpeg.vcxproj.filters | 117 + .../libmpeg/libmpeg.xcodeproj/project.pbxproj | 384 + .../media-server/libmpeg/source/mpeg-crc32.c | 57 + .../libmpeg/source/mpeg-element-descriptor.c | 719 + .../media-server/libmpeg/source/mpeg-muxer.c | 105 + .../libmpeg/source/mpeg-pack-header.c | 137 + .../media-server/libmpeg/source/mpeg-packet.c | 243 + .../media-server/libmpeg/source/mpeg-pat.c | 204 + .../libmpeg/source/mpeg-pes-internal.h | 99 + .../media-server/libmpeg/source/mpeg-pes.c | 323 + .../media-server/libmpeg/source/mpeg-pmt.c | 368 + .../media-server/libmpeg/source/mpeg-ps-dec.c | 519 + .../media-server/libmpeg/source/mpeg-ps-enc.c | 295 + .../libmpeg/source/mpeg-ps-internal.h | 87 + .../media-server/libmpeg/source/mpeg-psd.c | 158 + .../media-server/libmpeg/source/mpeg-psm.c | 273 + .../media-server/libmpeg/source/mpeg-sdt.c | 180 + .../libmpeg/source/mpeg-system-header.c | 164 + .../media-server/libmpeg/source/mpeg-ts-dec.c | 357 + .../media-server/libmpeg/source/mpeg-ts-enc.c | 590 + .../libmpeg/source/mpeg-ts-h264.c | 223 + .../libmpeg/source/mpeg-ts-h265.c | 134 + .../libmpeg/source/mpeg-ts-h266.c | 98 + .../libmpeg/source/mpeg-ts-internal.h | 103 + .../libmpeg/source/mpeg-ts-opus.h | 63 + .../media-server/libmpeg/source/mpeg-util.c | 64 + .../libmpeg/test/fileSequence0.ts | Bin 0 -> 250228 bytes .../libmpeg/test/flv-2-mpeg-ps-test.cpp | 119 + .../libmpeg/test/mov-2-mpeg-ps-test.cpp | 165 + .../libmpeg/test/mpeg-ps-2-flv-test.cpp | 82 + .../libmpeg/test/mpeg-ps-dec-test.cpp | 100 + .../libmpeg/test/mpeg-ps-test.cpp | 108 + .../libmpeg/test/mpeg-ts-dec-test.cpp | 101 + .../libmpeg/test/mpeg-ts-encrypt-test.cpp | 59 + .../test/mpeg-ts-multi-program-test.cpp | 157 + .../libmpeg/test/mpeg-ts-test.cpp | 91 + 3rdpart/media-server/libmpeg/version.ver | 2 + 3rdpart/media-server/librtmp/Android.mk | 15 + 3rdpart/media-server/librtmp/Makefile | 52 + .../librtmp/aio/aio-rtmp-client.c | 179 + .../librtmp/aio/aio-rtmp-client.h | 52 + .../librtmp/aio/aio-rtmp-server.c | 296 + .../librtmp/aio/aio-rtmp-server.h | 61 + .../librtmp/aio/aio-rtmp-transport.c | 198 + .../librtmp/aio/aio-rtmp-transport.h | 50 + .../librtmp/include/rtmp-chunk-header.h | 38 + .../librtmp/include/rtmp-client.h | 67 + .../librtmp/include/rtmp-control-message.h | 41 + .../media-server/librtmp/include/rtmp-event.h | 48 + .../librtmp/include/rtmp-handshake.h | 29 + .../librtmp/include/rtmp-internal.h | 176 + .../librtmp/include/rtmp-msgtypeid.h | 35 + .../librtmp/include/rtmp-netconnection.h | 84 + .../librtmp/include/rtmp-netstream.h | 30 + .../librtmp/include/rtmp-server.h | 82 + .../media-server/librtmp/include/rtmp-url.h | 168 + .../media-server/librtmp/include/rtmp-util.h | 55 + 3rdpart/media-server/librtmp/librtmp.vcxproj | 166 + .../librtmp/librtmp.vcxproj.filters | 99 + .../librtmp/librtmp.xcodeproj/project.pbxproj | 354 + .../librtmp/source/rtmp-chunk-header.c | 146 + .../librtmp/source/rtmp-chunk-read.c | 257 + .../librtmp/source/rtmp-chunk-write.c | 143 + .../source/rtmp-client-invoke-handler.h | 254 + .../media-server/librtmp/source/rtmp-client.c | 601 + .../librtmp/source/rtmp-control-handler.c | 118 + .../librtmp/source/rtmp-control-message.c | 79 + .../media-server/librtmp/source/rtmp-event.c | 143 + .../librtmp/source/rtmp-handler.c | 128 + .../librtmp/source/rtmp-handshake.c | 286 + .../librtmp/source/rtmp-invoke-handler.c | 281 + .../librtmp/source/rtmp-netconnection.c | 117 + .../librtmp/source/rtmp-netstream.c | 280 + .../media-server/librtmp/source/rtmp-server.c | 628 + 3rdpart/media-server/librtmp/test/RTMPUrl.h | 49 + .../librtmp/test/rtmp-chunk-test.cpp | 110 + .../librtmp/test/rtmp-input-test.cpp | 67 + .../librtmp/test/rtmp-play-aio-test.cpp | 76 + .../librtmp/test/rtmp-play-test.cpp | 66 + .../librtmp/test/rtmp-publish-aio-test.cpp | 127 + .../librtmp/test/rtmp-publish-test.cpp | 197 + .../test/rtmp-server-forward-aio-test.cpp | 250 + .../librtmp/test/rtmp-server-input-test.cpp | 64 + .../test/rtmp-server-publish-aio-test.cpp | 64 + .../test/rtmp-server-publish-benchmark.cpp | 142 + .../librtmp/test/rtmp-server-publish-test.cpp | 79 + .../librtmp/test/rtmp-server-vod-aio-test.cpp | 171 + .../librtmp/test/rtmp-server-vod-test.cpp | 132 + 3rdpart/media-server/librtmp/version.ver | 2 + 3rdpart/media-server/librtp/Android.mk | 18 + 3rdpart/media-server/librtp/Makefile | 42 + .../media-server/librtp/include/rtcp-header.h | 444 + .../media-server/librtp/include/rtp-demuxer.h | 43 + 3rdpart/media-server/librtp/include/rtp-ext.h | 354 + .../librtp/include/rtp-header-extension.h | 311 + .../media-server/librtp/include/rtp-header.h | 29 + .../librtp/include/rtp-internal.h | 66 + .../librtp/include/rtp-member-list.h | 17 + .../media-server/librtp/include/rtp-member.h | 43 + .../media-server/librtp/include/rtp-packet.h | 25 + .../media-server/librtp/include/rtp-param.h | 15 + .../media-server/librtp/include/rtp-payload.h | 74 + .../media-server/librtp/include/rtp-profile.h | 134 + .../media-server/librtp/include/rtp-queue.h | 36 + .../media-server/librtp/include/rtp-util.h | 67 + 3rdpart/media-server/librtp/include/rtp.h | 138 + 3rdpart/media-server/librtp/librtp.vcxproj | 223 + .../librtp/librtp.vcxproj.filters | 270 + .../librtp/librtp.xcodeproj/project.pbxproj | 576 + .../librtp/payload/rtp-av1-pack.c | 339 + .../librtp/payload/rtp-av1-unpack.c | 390 + .../librtp/payload/rtp-h264-bitstream.c | 140 + .../librtp/payload/rtp-h264-pack.c | 198 + .../librtp/payload/rtp-h264-unpack.c | 343 + .../librtp/payload/rtp-h265-pack.c | 191 + .../librtp/payload/rtp-h265-unpack.c | 294 + .../librtp/payload/rtp-h266-pack.c | 193 + .../librtp/payload/rtp-h266-unpack.c | 292 + .../librtp/payload/rtp-mp4a-latm-pack.c | 136 + .../librtp/payload/rtp-mp4a-latm-unpack.c | 93 + .../librtp/payload/rtp-mp4v-es-pack.c | 121 + .../librtp/payload/rtp-mp4v-es-unpack.c | 52 + .../librtp/payload/rtp-mpeg1or2es-pack.c | 339 + .../librtp/payload/rtp-mpeg1or2es-unpack.c | 67 + .../librtp/payload/rtp-mpeg4-generic-pack.c | 143 + .../librtp/payload/rtp-mpeg4-generic-unpack.c | 100 + .../media-server/librtp/payload/rtp-pack.c | 104 + .../librtp/payload/rtp-payload-helper.c | 134 + .../librtp/payload/rtp-payload-helper.h | 30 + .../librtp/payload/rtp-payload-internal.h | 77 + .../media-server/librtp/payload/rtp-payload.c | 249 + .../librtp/payload/rtp-ps-unpack.c | 69 + .../media-server/librtp/payload/rtp-ts-pack.c | 128 + .../librtp/payload/rtp-ts-unpack.c | 52 + .../media-server/librtp/payload/rtp-unpack.c | 38 + .../librtp/payload/rtp-vp8-pack.c | 113 + .../librtp/payload/rtp-vp8-unpack.c | 170 + .../librtp/payload/rtp-vp9-pack.c | 118 + .../librtp/payload/rtp-vp9-unpack.c | 217 + .../librtp/rtpext/rtp-ext-abs-send-time.c | 40 + .../rtpext/rtp-ext-absolute-capture-time.c | 89 + .../librtp/rtpext/rtp-ext-color-space.c | 124 + .../librtp/rtpext/rtp-ext-csrc-audio-level.c | 57 + .../librtp/rtpext/rtp-ext-frame-marking.c | 68 + .../librtp/rtpext/rtp-ext-inband-cn.c | 67 + .../librtp/rtpext/rtp-ext-playout-delay.c | 73 + .../media-server/librtp/rtpext/rtp-ext-sdes.c | 64 + .../librtp/rtpext/rtp-ext-ssrc-audio-level.c | 52 + .../librtp/rtpext/rtp-ext-toffset.c | 35 + .../librtp/rtpext/rtp-ext-transport-wide-cc.c | 66 + .../rtpext/rtp-ext-video-content-type.c | 40 + .../rtpext/rtp-ext-video-frame-tracking-id.c | 32 + .../rtpext/rtp-ext-video-layers-allocation.c | 74 + .../librtp/rtpext/rtp-ext-video-orientation.c | 90 + .../librtp/rtpext/rtp-ext-video-timing.c | 76 + 3rdpart/media-server/librtp/rtpext/rtp-ext.c | 270 + 3rdpart/media-server/librtp/source/rtcp-app.c | 56 + 3rdpart/media-server/librtp/source/rtcp-bye.c | 69 + .../librtp/source/rtcp-interval.c | 100 + .../media-server/librtp/source/rtcp-psfb.c | 851 + 3rdpart/media-server/librtp/source/rtcp-rr.c | 91 + .../media-server/librtp/source/rtcp-rtpfb.c | 1186 ++ .../media-server/librtp/source/rtcp-sdec.c | 128 + 3rdpart/media-server/librtp/source/rtcp-sr.c | 172 + 3rdpart/media-server/librtp/source/rtcp-xr.c | 775 + 3rdpart/media-server/librtp/source/rtcp.c | 258 + .../media-server/librtp/source/rtp-demuxer.c | 253 + .../librtp/source/rtp-member-list.c | 143 + .../media-server/librtp/source/rtp-member.c | 72 + .../media-server/librtp/source/rtp-packet.c | 150 + .../media-server/librtp/source/rtp-profile.c | 67 + .../media-server/librtp/source/rtp-queue.c | 518 + 3rdpart/media-server/librtp/source/rtp-ssrc.c | 54 + 3rdpart/media-server/librtp/source/rtp-time.c | 87 + 3rdpart/media-server/librtp/source/rtp.c | 185 + .../media-server/librtp/test/av1-rtp-test.cpp | 146 + .../media-server/librtp/test/mov-rtp-test.cpp | 254 + .../librtp/test/rtp-dump-replay.cpp | 55 + .../librtp/test/rtp-dump-test.cpp | 136 + 3rdpart/media-server/librtp/test/rtp-dump.c | 100 + 3rdpart/media-server/librtp/test/rtp-dump.h | 21 + .../librtp/test/rtp-header-ext-test.cpp | 141 + .../librtp/test/rtp-payload-test.cpp | 215 + .../librtp/test/rtp-queue-test.cpp | 164 + .../librtp/test/rtp-receiver-test.c | 276 + .../librtp/test/rtp-sender-test.cpp | 37 + 3rdpart/media-server/librtsp/Android.mk | 30 + 3rdpart/media-server/librtsp/Makefile | 57 + 3rdpart/media-server/librtsp/TODO.txt | 2 + .../librtsp/include/rtp-over-rtsp.h | 33 + .../media-server/librtsp/include/rtp-sender.h | 42 + .../librtsp/include/rtsp-client.h | 134 + .../librtsp/include/rtsp-demuxer.h | 48 + .../librtsp/include/rtsp-header-range.h | 48 + .../librtsp/include/rtsp-header-rtp-info.h | 29 + .../librtsp/include/rtsp-header-session.h | 12 + .../librtsp/include/rtsp-header-transport.h | 75 + .../media-server/librtsp/include/rtsp-media.h | 126 + .../media-server/librtsp/include/rtsp-muxer.h | 48 + .../librtsp/include/rtsp-payloads.h | 162 + .../librtsp/include/rtsp-reason.h | 6 + .../librtsp/include/rtsp-server-aio.h | 29 + .../librtsp/include/rtsp-server.h | 201 + .../media-server/librtsp/include/sdp-a-fmtp.h | 160 + .../librtsp/include/sdp-a-rtpmap.h | 15 + .../librtsp/include/sdp-a-webrtc.h | 202 + .../librtsp/include/sdp-options.h | 98 + .../librtsp/include/sdp-payload.h | 33 + 3rdpart/media-server/librtsp/include/sdp.h | 130 + 3rdpart/media-server/librtsp/librtsp.vcxproj | 198 + .../librtsp/librtsp.vcxproj.filters | 194 + .../librtsp/librtsp.xcodeproj/project.pbxproj | 522 + .../librtsp/source/client/rtp-over-rtsp.c | 140 + .../source/client/rtsp-client-announce.c | 83 + .../librtsp/source/client/rtsp-client-auth.c | 112 + .../source/client/rtsp-client-describe.c | 103 + .../source/client/rtsp-client-get-parameter.c | 76 + .../source/client/rtsp-client-internal.h | 105 + .../source/client/rtsp-client-options.c | 58 + .../librtsp/source/client/rtsp-client-pause.c | 120 + .../librtsp/source/client/rtsp-client-play.c | 219 + .../source/client/rtsp-client-record.c | 179 + .../source/client/rtsp-client-set-parameter.c | 75 + .../librtsp/source/client/rtsp-client-setup.c | 224 + .../source/client/rtsp-client-teardown.c | 108 + .../librtsp/source/client/rtsp-client.c | 189 + .../librtsp/source/rtsp-header-range.c | 466 + .../librtsp/source/rtsp-header-rtp-info.c | 76 + .../librtsp/source/rtsp-header-session.c | 70 + .../librtsp/source/rtsp-header-transport.c | 238 + .../media-server/librtsp/source/rtsp-media.c | 457 + .../librtsp/source/rtsp-multicast.c | 47 + .../media-server/librtsp/source/rtsp-reason.c | 33 + .../media-server/librtsp/source/sdp-a-fmtp.c | 755 + .../librtsp/source/sdp-a-rtpmap.c | 89 + .../librtsp/source/sdp-a-webrtc.c | 824 + .../media-server/librtsp/source/sdp-options.c | 118 + 3rdpart/media-server/librtsp/source/sdp.c | 1822 +++ .../media-server/librtsp/source/sdp/sdp-aac.c | 158 + .../media-server/librtsp/source/sdp/sdp-av1.c | 39 + .../librtsp/source/sdp/sdp-fmtp-load.c | 127 + .../librtsp/source/sdp/sdp-g7xx.c | 20 + .../librtsp/source/sdp/sdp-h264.c | 100 + .../librtsp/source/sdp/sdp-h265.c | 109 + .../librtsp/source/sdp/sdp-h266.c | 95 + .../librtsp/source/sdp/sdp-mpeg2.c | 27 + .../librtsp/source/sdp/sdp-mpeg4.c | 36 + .../librtsp/source/sdp/sdp-opus.c | 29 + .../librtsp/source/sdp/sdp-payload.c | 65 + .../media-server/librtsp/source/sdp/sdp-vpx.c | 30 + .../source/server/aio/rtsp-server-listen.c | 69 + .../source/server/aio/rtsp-server-tcp.c | 176 + .../source/server/aio/rtsp-server-udp.c | 183 + .../source/server/rtsp-server-announce.c | 42 + .../source/server/rtsp-server-describe.c | 33 + .../source/server/rtsp-server-handler.c | 93 + .../source/server/rtsp-server-internal.h | 49 + .../source/server/rtsp-server-options.c | 20 + .../source/server/rtsp-server-parameter.c | 50 + .../librtsp/source/server/rtsp-server-pause.c | 39 + .../librtsp/source/server/rtsp-server-play.c | 61 + .../source/server/rtsp-server-record.c | 50 + .../librtsp/source/server/rtsp-server-setup.c | 70 + .../source/server/rtsp-server-teardown.c | 18 + .../librtsp/source/server/rtsp-server.c | 110 + .../librtsp/source/utils/rtp-sender.c | 182 + .../librtsp/source/utils/rtsp-demuxer.c | 614 + .../librtsp/source/utils/rtsp-muxer.c | 447 + 3rdpart/media-server/librtsp/test/Makefile | 71 + .../librtsp/test/media/avpacket-queue.cpp | 128 + .../librtsp/test/media/avpacket-queue.h | 43 + .../librtsp/test/media/ffmpeg-file-source.cpp | 553 + .../librtsp/test/media/ffmpeg-file-source.h | 81 + .../librtsp/test/media/ffmpeg-live-source.cpp | 759 + .../librtsp/test/media/ffmpeg-live-source.h | 92 + .../librtsp/test/media/h264-file-reader.cpp | 166 + .../librtsp/test/media/h264-file-reader.h | 51 + .../librtsp/test/media/h264-file-source.cpp | 207 + .../librtsp/test/media/h264-file-source.h | 52 + .../librtsp/test/media/h265-file-reader.cpp | 146 + .../librtsp/test/media/h265-file-reader.h | 51 + .../librtsp/test/media/h265-file-source.cpp | 216 + .../librtsp/test/media/h265-file-source.h | 52 + .../librtsp/test/media/media-source.h | 30 + .../librtsp/test/media/mp4-file-reader.cpp | 112 + .../librtsp/test/media/mp4-file-reader.h | 42 + .../librtsp/test/media/mp4-file-source.cpp | 428 + .../librtsp/test/media/mp4-file-source.h | 79 + .../librtsp/test/media/pcm-file-source.cpp | 188 + .../librtsp/test/media/pcm-file-source.h | 51 + .../librtsp/test/media/ps-file-reader.cpp | 203 + .../librtsp/test/media/ps-file-reader.h | 47 + .../librtsp/test/media/ps-file-source.cpp | 213 + .../librtsp/test/media/ps-file-source.h | 62 + .../librtsp/test/media/vod-file-source.cpp | 117 + .../librtsp/test/media/vod-file-source.h | 69 + .../librtsp/test/rtp-streaming-test.cpp | 276 + .../librtsp/test/rtp-tcp-transport.h | 35 + .../librtsp/test/rtp-udp-transport.cpp | 50 + .../librtsp/test/rtp-udp-transport.h | 26 + .../librtsp/test/rtsp-client-input-test.cpp | 202 + .../librtsp/test/rtsp-client-push-test.cpp | 299 + .../librtsp/test/rtsp-client-test.c | 240 + .../librtsp/test/rtsp-client-test2.c | 467 + .../librtsp/test/rtsp-demuxer-test.cpp | 92 + .../librtsp/test/rtsp-push-server.cpp | 356 + .../librtsp/test/rtsp-server-test.cpp | 669 + .../librtsp/test/sdp-receiver-test.cpp | 391 + .../media-server/librtsp/test/sdp-test.cpp | 71 + 3rdpart/media-server/librtsp/test/sdp1.txt | 15 + 3rdpart/media-server/librtsp/test/sdp2.txt | 12 + 3rdpart/media-server/libsip/Android.mk | 23 + 3rdpart/media-server/libsip/Makefile | 50 + .../media-server/libsip/include/sip-agent.h | 67 + .../media-server/libsip/include/sip-dialog.h | 61 + .../media-server/libsip/include/sip-header.h | 236 + .../media-server/libsip/include/sip-message.h | 150 + .../libsip/include/sip-subscribe.h | 54 + .../media-server/libsip/include/sip-timer.h | 34 + .../libsip/include/sip-transport.h | 55 + 3rdpart/media-server/libsip/include/sip-uac.h | 83 + 3rdpart/media-server/libsip/include/sip-uas.h | 26 + 3rdpart/media-server/libsip/libsip.vcxproj | 194 + .../libsip/libsip.vcxproj.filters | 183 + .../libsip/libsip.xcodeproj/project.pbxproj | 480 + .../libsip/src/header/sip-header-contact.c | 326 + .../libsip/src/header/sip-header-cseq.c | 39 + .../libsip/src/header/sip-header-event.c | 132 + .../libsip/src/header/sip-header-param.c | 227 + .../libsip/src/header/sip-header-route.c | 140 + .../libsip/src/header/sip-header-substate.c | 97 + .../libsip/src/header/sip-header-uri.c | 460 + .../libsip/src/header/sip-header-via.c | 212 + .../libsip/src/header/sip-header.c | 42 + 3rdpart/media-server/libsip/src/sip-agent.c | 56 + 3rdpart/media-server/libsip/src/sip-dialog.c | 208 + .../media-server/libsip/src/sip-internal.h | 53 + 3rdpart/media-server/libsip/src/sip-message.c | 890 ++ 3rdpart/media-server/libsip/src/sip-reason.c | 59 + .../media-server/libsip/src/sip-subscribe.c | 187 + .../media-server/libsip/src/uac/sip-uac-ack.c | 258 + .../media-server/libsip/src/uac/sip-uac-bye.c | 11 + .../libsip/src/uac/sip-uac-cancel.c | 37 + .../libsip/src/uac/sip-uac-info.c | 46 + .../libsip/src/uac/sip-uac-invite.c | 42 + .../libsip/src/uac/sip-uac-options.c | 37 + .../libsip/src/uac/sip-uac-register.c | 30 + .../libsip/src/uac/sip-uac-subscribe.c | 159 + .../src/uac/sip-uac-transaction-invite.c | 290 + .../src/uac/sip-uac-transaction-noninvite.c | 148 + .../libsip/src/uac/sip-uac-transaction.c | 215 + .../libsip/src/uac/sip-uac-transaction.h | 89 + 3rdpart/media-server/libsip/src/uac/sip-uac.c | 322 + .../media-server/libsip/src/uas/sip-uas-bye.c | 21 + .../libsip/src/uas/sip-uas-cancel.c | 42 + .../libsip/src/uas/sip-uas-info.c | 22 + .../libsip/src/uas/sip-uas-options.c | 65 + .../libsip/src/uas/sip-uas-prack.c | 29 + .../libsip/src/uas/sip-uas-refer.c | 16 + .../libsip/src/uas/sip-uas-register.c | 174 + .../libsip/src/uas/sip-uas-subscribe.c | 92 + .../src/uas/sip-uas-transaction-invite.c | 295 + .../src/uas/sip-uas-transaction-noninvite.c | 140 + .../libsip/src/uas/sip-uas-transaction.c | 261 + .../libsip/src/uas/sip-uas-transaction.h | 92 + 3rdpart/media-server/libsip/src/uas/sip-uas.c | 346 + .../libsip/test/sip-agent-test.cpp | 515 + .../libsip/test/sip-header-test.c | 24 + .../libsip/test/sip-message-test.cpp | 774 + 3rdpart/media-server/libsip/test/sip-timer.c | 63 + .../libsip/test/sip-uac-message-test.cpp | 401 + .../media-server/libsip/test/sip-uac-test.cpp | 259 + .../libsip/test/sip-uac-test2.cpp | 1198 ++ .../libsip/test/sip-uas-message-test.cpp | 300 + .../media-server/libsip/test/sip-uas-test.cpp | 147 + .../libsip/test/sip-uas-test2.cpp | 206 + .../media-server/libsip/test/transport-tcp.c | 0 .../media-server/libsip/test/transport-udp.c | 0 3rdpart/media-server/media-server.sln | 207 + .../contents.xcworkspacedata | 69 + 3rdpart/media-server/test/BinaryDiff.cpp | 37 + 3rdpart/media-server/test/Makefile | 90 + 3rdpart/media-server/test/Reflector.h | 50 + 3rdpart/media-server/test/test.cpp | 290 + 3rdpart/media-server/test/test.vcxproj | 382 + .../media-server/test/test.vcxproj.filters | 547 + .../test/test.xcodeproj/project.pbxproj | 1000 ++ AUTHORS | 128 + Android/.gitignore | 10 + Android/app/.gitignore | 1 + Android/app/build.gradle | 45 + Android/app/proguard-rules.pro | 21 + .../demo/ExampleInstrumentedTest.java | 26 + Android/app/src/main/AndroidManifest.xml | 25 + Android/app/src/main/cpp/CMakeLists.txt | 40 + Android/app/src/main/cpp/native-lib.cpp | 268 + .../com/zlmediakit/demo/MainActivity.java | 74 + .../java/com/zlmediakit/jni/ZLMediaKit.java | 93 + .../drawable-v24/ic_launcher_foreground.xml | 34 + .../res/drawable/ic_launcher_background.xml | 170 + .../app/src/main/res/layout/activity_main.xml | 19 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 2963 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 4905 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2060 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2783 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4490 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 6895 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 6387 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 10413 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 9128 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 15132 bytes Android/app/src/main/res/values/colors.xml | 6 + Android/app/src/main/res/values/strings.xml | 3 + Android/app/src/main/res/values/styles.xml | 11 + .../com/zlmediakit/demo/ExampleUnitTest.java | 17 + Android/build.gradle | 27 + Android/gradle.properties | 15 + Android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54329 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + Android/gradlew | 172 + Android/gradlew.bat | 84 + Android/settings.gradle | 1 + CMakeLists.txt | 613 + DejaVuSans.ttf | Bin 0 -> 757076 bytes LICENSE | 29 + README.md | 440 + README_en.md | 588 + ZLMVersion.h.ini | 10 + api/CMakeLists.txt | 112 + api/include/mk_common.h | 312 + api/include/mk_events.h | 348 + api/include/mk_events_objects.h | 578 + api/include/mk_frame.h | 384 + api/include/mk_h264_splitter.h | 83 + api/include/mk_httpclient.h | 248 + api/include/mk_media.h | 502 + api/include/mk_mediakit.h | 32 + api/include/mk_player.h | 208 + api/include/mk_proxyplayer.h | 217 + api/include/mk_pusher.h | 148 + api/include/mk_recorder.h | 159 + api/include/mk_rtp_server.h | 164 + api/include/mk_tcp.h | 399 + api/include/mk_thread.h | 255 + api/include/mk_track.h | 246 + api/include/mk_transcode.h | 285 + api/include/mk_util.h | 256 + api/include/mk_webrtc.h | 111 + api/source/mk_common.cpp | 383 + api/source/mk_events.cpp | 219 + api/source/mk_events_objects.cpp | 629 + api/source/mk_frame.cpp | 259 + api/source/mk_h264_splitter.cpp | 104 + api/source/mk_httpclient.cpp | 154 + api/source/mk_media.cpp | 375 + api/source/mk_player.cpp | 230 + api/source/mk_proxyplayer.cpp | 111 + api/source/mk_pusher.cpp | 87 + api/source/mk_recorder.cpp | 140 + api/source/mk_rtp_server.cpp | 142 + api/source/mk_tcp.cpp | 395 + api/source/mk_tcp_private.h | 44 + api/source/mk_thread.cpp | 179 + api/source/mk_track.cpp | 243 + api/source/mk_transcode.cpp | 194 + api/source/mk_util.cpp | 269 + api/source/mk_webrtc.cpp | 190 + api/tests/CMakeLists.sample | 6 + api/tests/CMakeLists.txt | 45 + api/tests/h264_media_server.c | 224 + api/tests/h264_pusher.c | 159 + api/tests/httpclient.c | 62 + api/tests/player_opencv.c | 118 + api/tests/pusher.c | 157 + api/tests/server.c | 621 + api/tests/websocket.c | 255 + build_docker_images.sh | 95 + cmake/AndroidNdkGdb.cmake | 96 + cmake/AndroidNdkModules.cmake | 58 + cmake/FindAVCODEC.cmake | 16 + cmake/FindAVFILTER.cmake | 16 + cmake/FindAVFORMAT.cmake | 16 + cmake/FindAVUTIL.cmake | 16 + cmake/FindFAAC.cmake | 16 + cmake/FindJEMALLOC.cmake | 49 + cmake/FindMP4V2.cmake | 14 + cmake/FindMYSQL.cmake | 128 + cmake/FindPCAP.cmake | 23 + cmake/FindSCTP.cmake | 24 + cmake/FindSDL2.cmake | 15 + cmake/FindSRTP.cmake | 55 + cmake/FindSWRESAMPLE.cmake | 16 + cmake/FindSWSCALE.cmake | 16 + cmake/FindTCMALLOC.cmake | 16 + cmake/FindX264.cmake | 57 + cmake/Jemalloc.cmake | 63 + cmake/android.toolchain.cmake | 1693 ++ cmake/checks/atomic_check.cpp | 12 + cmake/ios.toolchain.cmake | 674 + conf/config.ini | 472 + conf/novideo.yuv | 1 + conf/readme.md | 24 + default.pem | 89 + docker/build_docker_images.sh | 7 + docker/centos7/Dockerfile.runtime | 131 + docker/ubuntu16.04/Dockerfile.devel | 44 + docker/ubuntu16.04/Dockerfile.runtime | 63 + docker/ubuntu18.04/Dockerfile.devel | 45 + docker/ubuntu18.04/Dockerfile.runtime | 63 + dockerfile | 85 + ext-codec/AAC.cpp | 449 + ext-codec/AAC.h | 64 + ext-codec/AACRtmp.cpp | 66 + ext-codec/AACRtmp.h | 91 + ext-codec/AACRtp.cpp | 142 + ext-codec/AACRtp.h | 80 + ext-codec/AV1.cpp | 95 + ext-codec/AV1.h | 65 + ext-codec/AV1Rtp.cpp | 582 + ext-codec/AV1Rtp.h | 95 + ext-codec/CMakeLists.txt | 48 + ext-codec/G711.cpp | 161 + ext-codec/G711.h | 34 + ext-codec/G711Rtp.cpp | 49 + ext-codec/G711Rtp.h | 66 + ext-codec/H264.cpp | 428 + ext-codec/H264.h | 174 + ext-codec/H264Rtmp.cpp | 111 + ext-codec/H264Rtmp.h | 106 + ext-codec/H264Rtp.cpp | 374 + ext-codec/H264Rtp.h | 113 + ext-codec/H265.cpp | 433 + ext-codec/H265.h | 190 + ext-codec/H265Rtmp.cpp | 178 + ext-codec/H265Rtmp.h | 109 + ext-codec/H265Rtp.cpp | 395 + ext-codec/H265Rtp.h | 111 + ext-codec/JPEG.cpp | 85 + ext-codec/JPEG.h | 69 + ext-codec/JPEGRtp.cpp | 847 + ext-codec/JPEGRtp.h | 59 + ext-codec/L16.cpp | 78 + ext-codec/L16.h | 37 + ext-codec/MP3.cpp | 77 + ext-codec/MP3.h | 33 + ext-codec/MP3Rtp.cpp | 83 + ext-codec/MP3Rtp.h | 57 + ext-codec/Opus.cpp | 87 + ext-codec/Opus.h | 39 + ext-codec/OpusRtmp.cpp | 113 + ext-codec/OpusRtmp.h | 51 + ext-codec/SPSParser.c | 2321 +++ ext-codec/SPSParser.h | 526 + ext-codec/VP8.cpp | 79 + ext-codec/VP8.h | 49 + ext-codec/VP8Rtp.cpp | 356 + ext-codec/VP8Rtp.h | 63 + ext-codec/VP9.cpp | 76 + ext-codec/VP9.h | 49 + ext-codec/VP9Rtp.cpp | 320 + ext-codec/VP9Rtp.h | 64 + ext-codec/VpxRtmp.cpp | 153 + ext-codec/VpxRtmp.h | 54 + golang/.idea/.gitignore | 8 + golang/go.sum | 10 + golang/tester/mk_common_test.go | 62 + golang/zlmediakit/mk_common.go | 255 + k8s_readme.md | 31 + package/rpm/ZLMediaKit.spec | 168 + player/AudioSRC.cpp | 136 + player/AudioSRC.h | 83 + player/CMakeLists.txt | 71 + player/SDLAudioDevice.cpp | 100 + player/SDLAudioDevice.h | 50 + player/YuvDisplayer.h | 212 + player/test_player.cpp | 133 + postman/127.0.0.1.postman_environment.json | 24 + postman/ZLMediaKit.postman_collection.json | 2931 ++++ postman/readme.md | 1 + resource.rc | 48 + server/CMakeLists.txt | 92 + server/FFmpegSource.cpp | 456 + server/FFmpegSource.h | 133 + server/Process.cpp | 379 + server/Process.h | 43 + server/ShellParser.h | 207 + server/System.cpp | 266 + server/System.h | 23 + server/VideoStack.cpp | 620 + server/VideoStack.h | 206 + server/WebApi.cpp | 2449 +++ server/WebApi.h | 364 + server/WebHook.cpp | 798 + server/WebHook.h | 51 + server/main.cpp | 517 + sources.list | 11 + src/CMakeLists.txt | 67 + src/Codec/AACEncoder.cpp | 111 + src/Codec/AACEncoder.h | 38 + src/Codec/H264Encoder.cpp | 554 + src/Codec/H264Encoder.h | 50 + src/Codec/Transcode.cpp | 889 ++ src/Codec/Transcode.h | 186 + src/Common/Device.cpp | 168 + src/Common/Device.h | 190 + src/Common/JemallocUtil.cpp | 76 + src/Common/JemallocUtil.h | 28 + src/Common/MediaSink.cpp | 430 + src/Common/MediaSink.h | 300 + src/Common/MediaSource.cpp | 901 ++ src/Common/MediaSource.h | 577 + src/Common/MultiMediaSourceMuxer.cpp | 896 ++ src/Common/MultiMediaSourceMuxer.h | 258 + src/Common/PacketCache.h | 101 + src/Common/Parser.cpp | 358 + src/Common/Parser.h | 159 + src/Common/Stamp.cpp | 434 + src/Common/Stamp.h | 198 + src/Common/config.cpp | 695 + src/Common/config.h | 638 + src/Common/macros.cpp | 38 + src/Common/macros.h | 90 + src/Common/strCoding.cpp | 246 + src/Common/strCoding.h | 38 + src/Extension/CommonRtmp.cpp | 43 + src/Extension/CommonRtmp.h | 74 + src/Extension/CommonRtp.cpp | 98 + src/Extension/CommonRtp.h | 86 + src/Extension/Factory.cpp | 234 + src/Extension/Factory.h | 167 + src/Extension/Frame.cpp | 358 + src/Extension/Frame.h | 828 + src/Extension/Track.cpp | 26 + src/Extension/Track.h | 374 + src/FMP4/FMP4MediaSource.h | 175 + src/FMP4/FMP4MediaSourceMuxer.h | 95 + src/Http/HlsParser.cpp | 139 + src/Http/HlsParser.h | 143 + src/Http/HlsPlayer.cpp | 551 + src/Http/HlsPlayer.h | 166 + src/Http/HttpBody.cpp | 403 + src/Http/HttpBody.h | 220 + src/Http/HttpChunkedSplitter.cpp | 41 + src/Http/HttpChunkedSplitter.h | 50 + src/Http/HttpClient.cpp | 502 + src/Http/HttpClient.h | 321 + src/Http/HttpClientImp.cpp | 46 + src/Http/HttpClientImp.h | 29 + src/Http/HttpConst.cpp | 219 + src/Http/HttpConst.h | 51 + src/Http/HttpCookie.cpp | 134 + src/Http/HttpCookie.h | 75 + src/Http/HttpCookieManager.cpp | 320 + src/Http/HttpCookieManager.h | 368 + src/Http/HttpDownloader.cpp | 77 + src/Http/HttpDownloader.h | 63 + src/Http/HttpFileManager.cpp | 817 + src/Http/HttpFileManager.h | 97 + src/Http/HttpRequestSplitter.cpp | 181 + src/Http/HttpRequestSplitter.h | 139 + src/Http/HttpRequester.cpp | 291 + src/Http/HttpRequester.h | 46 + src/Http/HttpSession.cpp | 918 ++ src/Http/HttpSession.h | 187 + src/Http/HttpTSPlayer.cpp | 60 + src/Http/HttpTSPlayer.h | 61 + src/Http/TsPlayer.cpp | 68 + src/Http/TsPlayer.h | 53 + src/Http/TsPlayerImp.h | 48 + src/Http/TsplayerImp.cpp | 87 + src/Http/WebSocketClient.h | 534 + src/Http/WebSocketSession.h | 304 + src/Http/WebSocketSplitter.cpp | 208 + src/Http/WebSocketSplitter.h | 181 + src/Onvif/Onvif.cpp | 183 + src/Onvif/Onvif.h | 50 + src/Onvif/SoapUtil.cpp | 491 + src/Onvif/SoapUtil.h | 177 + src/Onvif/pugiconfig.hpp | 77 + src/Onvif/pugixml.cpp | 13027 ++++++++++++++++ src/Onvif/pugixml.hpp | 1501 ++ src/Player/MediaPlayer.cpp | 61 + src/Player/MediaPlayer.h | 38 + src/Player/PlayerBase.cpp | 107 + src/Player/PlayerBase.h | 318 + src/Player/PlayerProxy.cpp | 371 + src/Player/PlayerProxy.h | 187 + src/Pusher/MediaPusher.cpp | 61 + src/Pusher/MediaPusher.h | 48 + src/Pusher/PusherBase.cpp | 112 + src/Pusher/PusherBase.h | 169 + src/Pusher/PusherProxy.cpp | 145 + src/Pusher/PusherProxy.h | 86 + src/Record/HlsMaker.cpp | 209 + src/Record/HlsMaker.h | 215 + src/Record/HlsMakerImp.cpp | 269 + src/Record/HlsMakerImp.h | 87 + src/Record/HlsMediaSource.cpp | 145 + src/Record/HlsMediaSource.h | 115 + src/Record/HlsRecorder.h | 142 + src/Record/MP4.cpp | 187 + src/Record/MP4.h | 182 + src/Record/MP4Demuxer.cpp | 285 + src/Record/MP4Demuxer.h | 162 + src/Record/MP4Muxer.cpp | 261 + src/Record/MP4Muxer.h | 239 + src/Record/MP4Reader.cpp | 291 + src/Record/MP4Reader.h | 110 + src/Record/MP4Recorder.cpp | 162 + src/Record/MP4Recorder.h | 85 + src/Record/MPEG.cpp | 161 + src/Record/MPEG.h | 131 + src/Record/Recorder.cpp | 130 + src/Record/Recorder.h | 117 + src/Rtcp/Rtcp.cpp | 883 ++ src/Rtcp/Rtcp.h | 918 ++ src/Rtcp/RtcpContext.cpp | 284 + src/Rtcp/RtcpContext.h | 211 + src/Rtcp/RtcpFCI.cpp | 599 + src/Rtcp/RtcpFCI.h | 389 + src/Rtmp/FlvMuxer.cpp | 214 + src/Rtmp/FlvMuxer.h | 65 + src/Rtmp/FlvPlayer.cpp | 88 + src/Rtmp/FlvPlayer.h | 50 + src/Rtmp/FlvSplitter.cpp | 136 + src/Rtmp/FlvSplitter.h | 38 + src/Rtmp/Rtmp.cpp | 356 + src/Rtmp/Rtmp.h | 431 + src/Rtmp/RtmpCodec.h | 73 + src/Rtmp/RtmpDemuxer.cpp | 194 + src/Rtmp/RtmpDemuxer.h | 69 + src/Rtmp/RtmpMediaSource.h | 198 + src/Rtmp/RtmpMediaSourceImp.cpp | 192 + src/Rtmp/RtmpMediaSourceImp.h | 131 + src/Rtmp/RtmpMediaSourceMuxer.h | 93 + src/Rtmp/RtmpMuxer.cpp | 89 + src/Rtmp/RtmpMuxer.h | 107 + src/Rtmp/RtmpPlayer.cpp | 463 + src/Rtmp/RtmpPlayer.h | 119 + src/Rtmp/RtmpPlayerImp.h | 136 + src/Rtmp/RtmpProtocol.cpp | 958 ++ src/Rtmp/RtmpProtocol.h | 115 + src/Rtmp/RtmpPusher.cpp | 343 + src/Rtmp/RtmpPusher.h | 85 + src/Rtmp/RtmpSession.cpp | 639 + src/Rtmp/RtmpSession.h | 124 + src/Rtmp/amf.cpp | 692 + src/Rtmp/amf.h | 126 + src/Rtmp/utils.cpp | 77 + src/Rtmp/utils.h | 27 + src/Rtp/Decoder.cpp | 171 + src/Rtp/Decoder.h | 74 + src/Rtp/GB28181Process.cpp | 209 + src/Rtp/GB28181Process.h | 71 + src/Rtp/PSDecoder.cpp | 79 + src/Rtp/PSDecoder.h | 42 + src/Rtp/PSEncoder.cpp | 54 + src/Rtp/PSEncoder.h | 55 + src/Rtp/ProcessInterface.h | 52 + src/Rtp/RawEncoder.cpp | 87 + src/Rtp/RawEncoder.h | 68 + src/Rtp/RtpCache.cpp | 55 + src/Rtp/RtpCache.h | 69 + src/Rtp/RtpProcess.cpp | 352 + src/Rtp/RtpProcess.h | 155 + src/Rtp/RtpSender.cpp | 526 + src/Rtp/RtpSender.h | 134 + src/Rtp/RtpServer.cpp | 314 + src/Rtp/RtpServer.h | 124 + src/Rtp/RtpSession.cpp | 285 + src/Rtp/RtpSession.h | 69 + src/Rtp/RtpSplitter.cpp | 103 + src/Rtp/RtpSplitter.h | 48 + src/Rtp/TSDecoder.cpp | 109 + src/Rtp/TSDecoder.h | 57 + src/Rtsp/RtpCodec.cpp | 56 + src/Rtsp/RtpCodec.h | 132 + src/Rtsp/RtpMultiCaster.cpp | 222 + src/Rtsp/RtpMultiCaster.h | 66 + src/Rtsp/RtpReceiver.cpp | 155 + src/Rtsp/RtpReceiver.h | 404 + src/Rtsp/Rtsp.cpp | 768 + src/Rtsp/Rtsp.h | 403 + src/Rtsp/RtspDemuxer.cpp | 137 + src/Rtsp/RtspDemuxer.h | 70 + src/Rtsp/RtspMediaSource.h | 212 + src/Rtsp/RtspMediaSourceImp.cpp | 154 + src/Rtsp/RtspMediaSourceImp.h | 151 + src/Rtsp/RtspMediaSourceMuxer.h | 92 + src/Rtsp/RtspMuxer.cpp | 179 + src/Rtsp/RtspMuxer.h | 137 + src/Rtsp/RtspPlayer.cpp | 934 ++ src/Rtsp/RtspPlayer.h | 215 + src/Rtsp/RtspPlayerImp.h | 82 + src/Rtsp/RtspPusher.cpp | 628 + src/Rtsp/RtspPusher.h | 112 + src/Rtsp/RtspSession.cpp | 1296 ++ src/Rtsp/RtspSession.h | 285 + src/Rtsp/RtspSplitter.cpp | 120 + src/Rtsp/RtspSplitter.h | 82 + src/Rtsp/UDPServer.cpp | 101 + src/Rtsp/UDPServer.h | 45 + src/Shell/ShellCMD.h | 67 + src/Shell/ShellSession.cpp | 149 + src/Shell/ShellSession.h | 42 + src/TS/TSMediaSource.h | 150 + src/TS/TSMediaSourceMuxer.h | 89 + srt/Ack.cpp | 85 + srt/Ack.hpp | 90 + srt/CMakeLists.txt | 44 + srt/Common.hpp | 120 + srt/Crypto.cpp | 507 + srt/Crypto.hpp | 102 + srt/HSExt.cpp | 294 + srt/HSExt.hpp | 242 + srt/NackContext.cpp | 109 + srt/NackContext.hpp | 30 + srt/Packet.cpp | 669 + srt/Packet.hpp | 459 + srt/PacketQueue.cpp | 538 + srt/PacketQueue.hpp | 105 + srt/PacketSendQueue.cpp | 84 + srt/PacketSendQueue.hpp | 38 + srt/SrtCaller.cpp | 1066 ++ srt/SrtCaller.h | 205 + srt/SrtPlayer.cpp | 184 + srt/SrtPlayer.h | 67 + srt/SrtPlayerImp.h | 51 + srt/SrtPusher.cpp | 132 + srt/SrtPusher.h | 62 + srt/SrtSession.cpp | 86 + srt/SrtSession.hpp | 29 + srt/SrtTransport.cpp | 1001 ++ srt/SrtTransport.hpp | 200 + srt/SrtTransportImp.cpp | 381 + srt/SrtTransportImp.hpp | 94 + srt/Statistic.cpp | 228 + srt/Statistic.hpp | 63 + srt/srt.md | 46 + srt/srt_en.md | 48 + tests/CMakeLists.txt | 68 + tests/DeviceHK/DeviceHK.cpp | 251 + tests/DeviceHK/DeviceHK.h | 118 + tests/README.md | 41 + tests/bom.cpp | 157 + tests/tab.cpp | 160 + tests/test_bench_forward.cpp | 290 + tests/test_bench_proxy.cpp | 163 + tests/test_bench_pull.cpp | 208 + tests/test_bench_push.cpp | 264 + tests/test_flv.cpp | 114 + tests/test_httpApi.cpp | 136 + tests/test_httpClient.cpp | 203 + tests/test_http_head.cpp | 27 + tests/test_ps.cpp | 118 + tests/test_pusher.cpp | 136 + tests/test_pusherMp4.cpp | 147 + tests/test_rtcp_nack.cpp | 54 + tests/test_rtp.cpp | 130 + tests/test_rtp_pcap.cpp | 250 + tests/test_server.cpp | 390 + tests/test_sortor.cpp | 296 + tests/test_wsClient.cpp | 76 + tests/test_wsServer.cpp | 143 + tools/openapi/generates.py | 163 + tools/openapi/options.json | 40 + webrtc/CMakeLists.txt | 80 + webrtc/DtlsTransport.cpp | 1445 ++ webrtc/DtlsTransport.hpp | 254 + webrtc/IceSession.cpp | 139 + webrtc/IceSession.hpp | 70 + webrtc/IceTransport.cpp | 2065 +++ webrtc/IceTransport.hpp | 758 + webrtc/Nack.cpp | 384 + webrtc/Nack.h | 91 + webrtc/RtpExt.cpp | 659 + webrtc/RtpExt.h | 144 + webrtc/RtpMap.h | 210 + webrtc/SctpAssociation.cpp | 1007 ++ webrtc/SctpAssociation.hpp | 142 + webrtc/Sdp.cpp | 2079 +++ webrtc/Sdp.h | 779 + webrtc/SrtpSession.cpp | 291 + webrtc/SrtpSession.hpp | 65 + webrtc/StunPacket.cpp | 906 ++ webrtc/StunPacket.hpp | 689 + webrtc/TwccContext.cpp | 134 + webrtc/TwccContext.h | 49 + webrtc/USAGE.md | 256 + webrtc/WebRtcClient.cpp | 304 + webrtc/WebRtcClient.h | 92 + webrtc/WebRtcEchoTest.cpp | 58 + webrtc/WebRtcEchoTest.h | 38 + webrtc/WebRtcPlayer.cpp | 338 + webrtc/WebRtcPlayer.h | 165 + webrtc/WebRtcProxyPlayer.cpp | 126 + webrtc/WebRtcProxyPlayer.h | 66 + webrtc/WebRtcProxyPlayerImp.h | 43 + webrtc/WebRtcProxyPusher.cpp | 92 + webrtc/WebRtcProxyPusher.h | 54 + webrtc/WebRtcPusher.cpp | 232 + webrtc/WebRtcPusher.h | 113 + webrtc/WebRtcSession.cpp | 164 + webrtc/WebRtcSession.h | 56 + webrtc/WebRtcSignalingMsg.cpp | 49 + webrtc/WebRtcSignalingMsg.h | 58 + webrtc/WebRtcSignalingPeer.cpp | 687 + webrtc/WebRtcSignalingPeer.h | 141 + webrtc/WebRtcSignalingSession.cpp | 488 + webrtc/WebRtcSignalingSession.h | 85 + webrtc/WebRtcTalk.cpp | 171 + webrtc/WebRtcTalk.h | 56 + webrtc/WebRtcTransport.cpp | 1870 +++ webrtc/WebRtcTransport.h | 453 + webrtc/answer.sdp | 50 + webrtc/janus_answer.sdp | 69 + webrtc/janus_offer.sdp | 83 + webrtc/logger.h | 14 + webrtc/offer-simulcast.sdp | 164 + webrtc/offer.sdp | 169 + webrtc/readme.md | 25 + webrtc/webrtcSignal.txt | 132 + www/ZLMRTCClient.js | 9472 +++++++++++ www/ZLMRTCClient.js.map | 1 + www/logo.ico | Bin 0 -> 1435 bytes www/logo.png | Bin 0 -> 47255 bytes www/readme/index.html | 8 + www/swagger/favicon-16x16.png | Bin 0 -> 665 bytes www/swagger/favicon-32x32.png | Bin 0 -> 628 bytes www/swagger/index.css | 16 + www/swagger/index.html | 19 + www/swagger/oauth2-redirect.html | 79 + www/swagger/openapi.json | 4357 ++++++ www/swagger/swagger-initializer.js | 20 + www/swagger/swagger-ui-bundle.js | 3 + www/swagger/swagger-ui-bundle.js.map | 1 + www/swagger/swagger-ui-es-bundle-core.js | 3 + www/swagger/swagger-ui-es-bundle-core.js.map | 1 + www/swagger/swagger-ui-es-bundle.js | 3 + www/swagger/swagger-ui-es-bundle.js.map | 1 + www/swagger/swagger-ui-standalone-preset.js | 3 + .../swagger-ui-standalone-preset.js.map | 1 + www/swagger/swagger-ui.css | 3 + www/swagger/swagger-ui.css.map | 1 + www/swagger/swagger-ui.js | 2 + www/swagger/swagger-ui.js.map | 1 + www/webassist/LICENSE | 21 + www/webassist/README.md | 120 + www/webassist/README_EN.md | 14 + .../assets/elementui/fonts/element-icons.woff | Bin 0 -> 28200 bytes www/webassist/assets/elementui/index.css | 1 + www/webassist/assets/elementui/index.js | 1 + www/webassist/assets/favicon.ico | Bin 0 -> 137750 bytes www/webassist/assets/logo.png | Bin 0 -> 59464 bytes www/webassist/index.html | 2989 ++++ www/webassist/js/ZLMRTCClient.js | 8284 ++++++++++ www/webassist/js/ZLMRTCClient.js.map | 1 + www/webassist/js/axios.min.js | 2 + www/webassist/js/echarts.min.js | 45 + www/webassist/js/qs.js | 1904 +++ www/webassist/js/utils.js | 70 + www/webassist/js/vue.min.js | 11 + www/webrtc/ZLMRTCClient.js | 9472 +++++++++++ www/webrtc/ZLMRTCClient.js.map | 1 + www/webrtc/index.html | 454 + www/webrtc/readme.txt | 2 + 1609 files changed, 326006 insertions(+) create mode 100644 .clang-format create mode 100644 .gitattributes create mode 100644 .github/ISSUE_TEMPLATE/bug.md create mode 100644 .github/ISSUE_TEMPLATE/compile.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature.md create mode 100644 .github/workflows/android.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/docker.yml create mode 100644 .github/workflows/issue_lint.yml create mode 100644 .github/workflows/linux.yml create mode 100644 .github/workflows/macos.yml create mode 100644 .github/workflows/style.yml create mode 100644 .github/workflows/windows.yml create mode 100644 .gitignore create mode 100644 .gitmodules_github create mode 100644 3rdpart/CMakeLists.txt create mode 100644 3rdpart/ZLToolKit/.clang-format create mode 100644 3rdpart/ZLToolKit/.github/workflows/linux.yml create mode 100644 3rdpart/ZLToolKit/.github/workflows/macos.yml create mode 100644 3rdpart/ZLToolKit/.github/workflows/style.yml create mode 100644 3rdpart/ZLToolKit/.github/workflows/windows.yml create mode 100644 3rdpart/ZLToolKit/.gitignore create mode 100644 3rdpart/ZLToolKit/.travis.yml create mode 100644 3rdpart/ZLToolKit/AUTHORS create mode 100644 3rdpart/ZLToolKit/CMakeLists.txt create mode 100644 3rdpart/ZLToolKit/LICENSE create mode 100644 3rdpart/ZLToolKit/README.md create mode 100644 3rdpart/ZLToolKit/cmake/FindMYSQL.cmake create mode 100644 3rdpart/ZLToolKit/src/Network/Buffer.cpp create mode 100644 3rdpart/ZLToolKit/src/Network/Buffer.h create mode 100644 3rdpart/ZLToolKit/src/Network/BufferSock.cpp create mode 100644 3rdpart/ZLToolKit/src/Network/BufferSock.h create mode 100644 3rdpart/ZLToolKit/src/Network/Kcp.cpp create mode 100644 3rdpart/ZLToolKit/src/Network/Kcp.h create mode 100644 3rdpart/ZLToolKit/src/Network/Server.cpp create mode 100644 3rdpart/ZLToolKit/src/Network/Server.h create mode 100644 3rdpart/ZLToolKit/src/Network/Session.cpp create mode 100644 3rdpart/ZLToolKit/src/Network/Session.h create mode 100644 3rdpart/ZLToolKit/src/Network/Socket.cpp create mode 100644 3rdpart/ZLToolKit/src/Network/Socket.h create mode 100644 3rdpart/ZLToolKit/src/Network/Socket_ios.mm create mode 100644 3rdpart/ZLToolKit/src/Network/TcpClient.cpp create mode 100644 3rdpart/ZLToolKit/src/Network/TcpClient.h create mode 100644 3rdpart/ZLToolKit/src/Network/TcpServer.cpp create mode 100644 3rdpart/ZLToolKit/src/Network/TcpServer.h create mode 100644 3rdpart/ZLToolKit/src/Network/UdpClient.cpp create mode 100644 3rdpart/ZLToolKit/src/Network/UdpClient.h create mode 100644 3rdpart/ZLToolKit/src/Network/UdpServer.cpp create mode 100644 3rdpart/ZLToolKit/src/Network/UdpServer.h create mode 100644 3rdpart/ZLToolKit/src/Network/sockutil.cpp create mode 100644 3rdpart/ZLToolKit/src/Network/sockutil.h create mode 100644 3rdpart/ZLToolKit/src/Poller/EventPoller.cpp create mode 100644 3rdpart/ZLToolKit/src/Poller/EventPoller.h create mode 100644 3rdpart/ZLToolKit/src/Poller/Pipe.cpp create mode 100644 3rdpart/ZLToolKit/src/Poller/Pipe.h create mode 100644 3rdpart/ZLToolKit/src/Poller/PipeWrap.cpp create mode 100644 3rdpart/ZLToolKit/src/Poller/PipeWrap.h create mode 100644 3rdpart/ZLToolKit/src/Poller/SelectWrap.cpp create mode 100644 3rdpart/ZLToolKit/src/Poller/SelectWrap.h create mode 100644 3rdpart/ZLToolKit/src/Poller/Timer.cpp create mode 100644 3rdpart/ZLToolKit/src/Poller/Timer.h create mode 100644 3rdpart/ZLToolKit/src/README.md create mode 100644 3rdpart/ZLToolKit/src/Thread/TaskExecutor.cpp create mode 100644 3rdpart/ZLToolKit/src/Thread/TaskExecutor.h create mode 100644 3rdpart/ZLToolKit/src/Thread/TaskQueue.h create mode 100644 3rdpart/ZLToolKit/src/Thread/ThreadPool.h create mode 100644 3rdpart/ZLToolKit/src/Thread/WorkThreadPool.cpp create mode 100644 3rdpart/ZLToolKit/src/Thread/WorkThreadPool.h create mode 100644 3rdpart/ZLToolKit/src/Thread/semaphore.h create mode 100644 3rdpart/ZLToolKit/src/Thread/threadgroup.h create mode 100644 3rdpart/ZLToolKit/src/Util/Byte.hpp create mode 100644 3rdpart/ZLToolKit/src/Util/CMD.cpp create mode 100644 3rdpart/ZLToolKit/src/Util/CMD.h create mode 100755 3rdpart/ZLToolKit/src/Util/File.cpp create mode 100644 3rdpart/ZLToolKit/src/Util/File.h create mode 100644 3rdpart/ZLToolKit/src/Util/List.h create mode 100644 3rdpart/ZLToolKit/src/Util/MD5.cpp create mode 100644 3rdpart/ZLToolKit/src/Util/MD5.h create mode 100644 3rdpart/ZLToolKit/src/Util/NoticeCenter.cpp create mode 100644 3rdpart/ZLToolKit/src/Util/NoticeCenter.h create mode 100644 3rdpart/ZLToolKit/src/Util/ResourcePool.h create mode 100644 3rdpart/ZLToolKit/src/Util/RingBuffer.h create mode 100644 3rdpart/ZLToolKit/src/Util/SHA1.cpp create mode 100644 3rdpart/ZLToolKit/src/Util/SHA1.h create mode 100644 3rdpart/ZLToolKit/src/Util/SSLBox.cpp create mode 100644 3rdpart/ZLToolKit/src/Util/SSLBox.h create mode 100644 3rdpart/ZLToolKit/src/Util/SSLUtil.cpp create mode 100644 3rdpart/ZLToolKit/src/Util/SSLUtil.h create mode 100644 3rdpart/ZLToolKit/src/Util/SpeedStatistic.h create mode 100644 3rdpart/ZLToolKit/src/Util/SqlConnection.h create mode 100644 3rdpart/ZLToolKit/src/Util/SqlPool.cpp create mode 100644 3rdpart/ZLToolKit/src/Util/SqlPool.h create mode 100644 3rdpart/ZLToolKit/src/Util/TimeTicker.h create mode 100644 3rdpart/ZLToolKit/src/Util/base64.cpp create mode 100644 3rdpart/ZLToolKit/src/Util/base64.h create mode 100644 3rdpart/ZLToolKit/src/Util/function_traits.h create mode 100644 3rdpart/ZLToolKit/src/Util/local_time.cpp create mode 100644 3rdpart/ZLToolKit/src/Util/local_time.h create mode 100644 3rdpart/ZLToolKit/src/Util/logger.cpp create mode 100644 3rdpart/ZLToolKit/src/Util/logger.h create mode 100644 3rdpart/ZLToolKit/src/Util/mini.cpp create mode 100644 3rdpart/ZLToolKit/src/Util/mini.h create mode 100644 3rdpart/ZLToolKit/src/Util/onceToken.h create mode 100644 3rdpart/ZLToolKit/src/Util/strptime_win.cpp create mode 100644 3rdpart/ZLToolKit/src/Util/strptime_win.h create mode 100644 3rdpart/ZLToolKit/src/Util/util.cpp create mode 100644 3rdpart/ZLToolKit/src/Util/util.h create mode 100644 3rdpart/ZLToolKit/src/Util/uv_errno.cpp create mode 100644 3rdpart/ZLToolKit/src/Util/uv_errno.h create mode 100644 3rdpart/ZLToolKit/src/win32/LICENSE create mode 100644 3rdpart/ZLToolKit/src/win32/README.md create mode 100644 3rdpart/ZLToolKit/src/win32/getopt.c create mode 100644 3rdpart/ZLToolKit/src/win32/getopt.h create mode 100644 3rdpart/ZLToolKit/src/win32/sys/epoll.h create mode 100644 3rdpart/ZLToolKit/src/win32/tailor.h create mode 100644 3rdpart/ZLToolKit/src/win32/wepoll.c create mode 100644 3rdpart/ZLToolKit/src/win32/wepoll.h create mode 100644 3rdpart/ZLToolKit/tests/CMakeLists.txt create mode 100644 3rdpart/ZLToolKit/tests/ssl.p12 create mode 100644 3rdpart/ZLToolKit/tests/test_creator.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_delayTask.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_eventPoller.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_ini.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_kcpClient.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_kcpEchoServer.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_kcpLantencyClient.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_logger.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_noticeCenter.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_pingpong.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_pipe.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_resourcePool.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_ringBuffer.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_semaphore.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_shell.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_sql.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_ssl.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_tcpClient.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_tcpEchoServer.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_tcpLantencyClient.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_threadPool.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_threadPoolBenchmark.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_timer.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_udpClient.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_udpEchoServer.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_udpLantencyClient.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_udpSock.cpp create mode 100644 3rdpart/ZLToolKit/tests/test_variant.cpp create mode 100644 3rdpart/assert.h create mode 100644 3rdpart/jsoncpp/.clang-format create mode 100644 3rdpart/jsoncpp/.clang-tidy create mode 100644 3rdpart/jsoncpp/.gitattributes create mode 100644 3rdpart/jsoncpp/.github/ISSUE_TEMPLATE/bug_report.md create mode 100644 3rdpart/jsoncpp/.github/ISSUE_TEMPLATE/feature_request.md create mode 100644 3rdpart/jsoncpp/.github/workflows/clang-format.yml create mode 100644 3rdpart/jsoncpp/.github/workflows/cmake.yml create mode 100644 3rdpart/jsoncpp/.github/workflows/meson.yml create mode 100644 3rdpart/jsoncpp/.gitignore create mode 100644 3rdpart/jsoncpp/AUTHORS create mode 100644 3rdpart/jsoncpp/BUILD.bazel create mode 100644 3rdpart/jsoncpp/CMakeLists.txt create mode 100644 3rdpart/jsoncpp/CONTRIBUTING.md create mode 100644 3rdpart/jsoncpp/CTestConfig.cmake create mode 100644 3rdpart/jsoncpp/LICENSE create mode 100644 3rdpart/jsoncpp/MODULE.bazel create mode 100644 3rdpart/jsoncpp/README.md create mode 100644 3rdpart/jsoncpp/SECURITY.md create mode 100755 3rdpart/jsoncpp/amalgamate.py create mode 100644 3rdpart/jsoncpp/appveyor.yml create mode 100644 3rdpart/jsoncpp/cmake/JoinPaths.cmake create mode 100644 3rdpart/jsoncpp/dev.makefile create mode 100644 3rdpart/jsoncpp/devtools/__init__.py create mode 100644 3rdpart/jsoncpp/devtools/agent_vmw7.json create mode 100644 3rdpart/jsoncpp/devtools/agent_vmxp.json create mode 100644 3rdpart/jsoncpp/devtools/antglob.py create mode 100644 3rdpart/jsoncpp/devtools/batchbuild.py create mode 100644 3rdpart/jsoncpp/devtools/fixeol.py create mode 100644 3rdpart/jsoncpp/devtools/licenseupdater.py create mode 100644 3rdpart/jsoncpp/devtools/tarball.py create mode 100644 3rdpart/jsoncpp/doc/doxyfile.in create mode 100644 3rdpart/jsoncpp/doc/footer.html create mode 100644 3rdpart/jsoncpp/doc/header.html create mode 100644 3rdpart/jsoncpp/doc/jsoncpp.dox create mode 100644 3rdpart/jsoncpp/doc/readme.txt create mode 100644 3rdpart/jsoncpp/doc/roadmap.dox create mode 100644 3rdpart/jsoncpp/doc/web_doxyfile.in create mode 100644 3rdpart/jsoncpp/doxybuild.py create mode 100644 3rdpart/jsoncpp/example/BUILD.bazel create mode 100644 3rdpart/jsoncpp/example/CMakeLists.txt create mode 100644 3rdpart/jsoncpp/example/README.md create mode 100644 3rdpart/jsoncpp/example/readFromStream/errorFormat.json create mode 100644 3rdpart/jsoncpp/example/readFromStream/readFromStream.cpp create mode 100644 3rdpart/jsoncpp/example/readFromStream/withComment.json create mode 100644 3rdpart/jsoncpp/example/readFromString/readFromString.cpp create mode 100644 3rdpart/jsoncpp/example/streamWrite/streamWrite.cpp create mode 100644 3rdpart/jsoncpp/example/stringWrite/stringWrite.cpp create mode 100644 3rdpart/jsoncpp/get_version.pl create mode 100644 3rdpart/jsoncpp/include/CMakeLists.txt create mode 100644 3rdpart/jsoncpp/include/PreventInBuildInstalls.cmake create mode 100644 3rdpart/jsoncpp/include/PreventInSourceBuilds.cmake create mode 100644 3rdpart/jsoncpp/include/json/allocator.h create mode 100644 3rdpart/jsoncpp/include/json/assertions.h create mode 100644 3rdpart/jsoncpp/include/json/config.h create mode 100644 3rdpart/jsoncpp/include/json/forwards.h create mode 100644 3rdpart/jsoncpp/include/json/json.h create mode 100644 3rdpart/jsoncpp/include/json/json_features.h create mode 100644 3rdpart/jsoncpp/include/json/reader.h create mode 100644 3rdpart/jsoncpp/include/json/value.h create mode 100644 3rdpart/jsoncpp/include/json/version.h create mode 100644 3rdpart/jsoncpp/include/json/writer.h create mode 100644 3rdpart/jsoncpp/jsoncpp-namespaced-targets.cmake create mode 100644 3rdpart/jsoncpp/jsoncppConfig.cmake.in create mode 100644 3rdpart/jsoncpp/jsoncppConfig.cmake.meson.in create mode 100644 3rdpart/jsoncpp/meson.build create mode 100644 3rdpart/jsoncpp/meson_options.txt create mode 100644 3rdpart/jsoncpp/pkg-config/jsoncpp.pc.in create mode 100755 3rdpart/jsoncpp/reformat.sh create mode 100644 3rdpart/jsoncpp/src/CMakeLists.txt create mode 100644 3rdpart/jsoncpp/src/jsontestrunner/CMakeLists.txt create mode 100644 3rdpart/jsoncpp/src/jsontestrunner/main.cpp create mode 100644 3rdpart/jsoncpp/src/lib_json/CMakeLists.txt create mode 100644 3rdpart/jsoncpp/src/lib_json/json_reader.cpp create mode 100644 3rdpart/jsoncpp/src/lib_json/json_tool.h create mode 100644 3rdpart/jsoncpp/src/lib_json/json_value.cpp create mode 100644 3rdpart/jsoncpp/src/lib_json/json_valueiterator.inl create mode 100644 3rdpart/jsoncpp/src/lib_json/json_writer.cpp create mode 100644 3rdpart/jsoncpp/src/test_lib_json/CMakeLists.txt create mode 100644 3rdpart/jsoncpp/src/test_lib_json/fuzz.cpp create mode 100644 3rdpart/jsoncpp/src/test_lib_json/fuzz.dict create mode 100644 3rdpart/jsoncpp/src/test_lib_json/fuzz.h create mode 100644 3rdpart/jsoncpp/src/test_lib_json/jsontest.cpp create mode 100644 3rdpart/jsoncpp/src/test_lib_json/jsontest.h create mode 100644 3rdpart/jsoncpp/src/test_lib_json/main.cpp create mode 100644 3rdpart/jsoncpp/test/cleantests.py create mode 100644 3rdpart/jsoncpp/test/data/fail_invalid_quote.json create mode 100644 3rdpart/jsoncpp/test/data/fail_strict_comment_01.json create mode 100644 3rdpart/jsoncpp/test/data/fail_strict_comment_02.json create mode 100644 3rdpart/jsoncpp/test/data/fail_strict_comment_03.json create mode 100644 3rdpart/jsoncpp/test/data/fail_test_array_01.json create mode 100644 3rdpart/jsoncpp/test/data/fail_test_array_02.json create mode 100644 3rdpart/jsoncpp/test/data/fail_test_object_01.json create mode 100644 3rdpart/jsoncpp/test/data/fail_test_object_02.json create mode 100644 3rdpart/jsoncpp/test/data/fail_test_stack_limit.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_array_01.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_array_01.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_array_02.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_array_02.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_array_03.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_array_03.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_array_04.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_array_04.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_array_05.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_array_05.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_array_06.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_array_06.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_array_07.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_array_07.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_basic_01.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_basic_01.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_basic_02.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_basic_02.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_basic_03.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_basic_03.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_basic_04.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_basic_04.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_basic_05.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_basic_05.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_basic_06.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_basic_06.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_basic_07.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_basic_07.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_basic_08.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_basic_08.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_basic_09.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_basic_09.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_comment_00.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_comment_00.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_comment_01.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_comment_01.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_comment_02.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_comment_02.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_complex_01.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_complex_01.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_integer_01.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_integer_01.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_integer_02.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_integer_02.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_integer_03.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_integer_03.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_integer_04.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_integer_04.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_integer_05.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_integer_05.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_integer_06_64bits.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_integer_06_64bits.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_integer_07_64bits.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_integer_07_64bits.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_integer_08_64bits.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_integer_08_64bits.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_large_01.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_large_01.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_object_01.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_object_01.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_object_02.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_object_02.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_object_03.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_object_03.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_object_04.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_object_04.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_preserve_comment_01.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_preserve_comment_01.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_01.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_01.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_02.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_02.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_03.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_03.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_04.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_04.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_05.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_05.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_06.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_06.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_07.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_07.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_08.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_08.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_09.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_09.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_10.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_10.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_11.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_11.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_12.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_12.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_13.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_real_13.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_string_01.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_string_01.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_string_02.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_string_02.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_string_03.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_string_03.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_string_04.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_string_04.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_string_05.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_string_05.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_string_unicode_01.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_string_unicode_01.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_string_unicode_02.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_string_unicode_02.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_string_unicode_03.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_string_unicode_03.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_string_unicode_04.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_string_unicode_04.json create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_string_unicode_05.expected create mode 100644 3rdpart/jsoncpp/test/data/legacy_test_string_unicode_05.json create mode 100644 3rdpart/jsoncpp/test/data/test_array_08.expected create mode 100644 3rdpart/jsoncpp/test/data/test_array_08.json create mode 100644 3rdpart/jsoncpp/test/data/test_object_05.expected create mode 100644 3rdpart/jsoncpp/test/data/test_object_05.json create mode 100644 3rdpart/jsoncpp/test/generate_expected.py create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail1.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail10.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail11.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail12.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail13.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail14.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail15.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail16.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail17.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail18.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail19.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail2.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail20.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail21.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail22.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail23.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail24.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail25.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail26.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail27.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail28.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail29.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail3.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail30.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail31.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail32.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail33.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail4.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail5.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail6.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail7.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail8.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/fail9.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/pass1.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/pass2.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/pass3.json create mode 100644 3rdpart/jsoncpp/test/jsonchecker/readme.txt create mode 100644 3rdpart/jsoncpp/test/pyjsontestrunner.py create mode 100644 3rdpart/jsoncpp/test/runjsontests.py create mode 100644 3rdpart/jsoncpp/test/rununittests.py create mode 100644 3rdpart/jsoncpp/version.in create mode 100644 3rdpart/media-server/.github/workflows/ccpp.yml create mode 100644 3rdpart/media-server/.github/workflows/codeql-analysis.yml create mode 100644 3rdpart/media-server/.github/workflows/sonarcloud.yml create mode 100644 3rdpart/media-server/.gitignore create mode 100644 3rdpart/media-server/.travis.yml create mode 100644 3rdpart/media-server/Android.mk create mode 100644 3rdpart/media-server/Application.mk create mode 100644 3rdpart/media-server/LICENSE create mode 100644 3rdpart/media-server/Makefile create mode 100644 3rdpart/media-server/README.md create mode 100644 3rdpart/media-server/SECURITY.md create mode 100644 3rdpart/media-server/compile.cn.md create mode 100644 3rdpart/media-server/gcc.mk create mode 100644 3rdpart/media-server/libdash/Android.mk create mode 100644 3rdpart/media-server/libdash/Makefile create mode 100644 3rdpart/media-server/libdash/include/dash-mpd.h create mode 100644 3rdpart/media-server/libdash/include/dash-parser.h create mode 100644 3rdpart/media-server/libdash/include/dash-proto.h create mode 100644 3rdpart/media-server/libdash/include/xs-datatype.h create mode 100644 3rdpart/media-server/libdash/libdash.vcxproj create mode 100644 3rdpart/media-server/libdash/libdash.vcxproj.filters create mode 100644 3rdpart/media-server/libdash/libdash.xcodeproj/project.pbxproj create mode 100644 3rdpart/media-server/libdash/src/dash-adaptation.c create mode 100644 3rdpart/media-server/libdash/src/dash-mpd.c create mode 100644 3rdpart/media-server/libdash/src/dash-parser.c create mode 100644 3rdpart/media-server/libdash/src/dash-period.c create mode 100644 3rdpart/media-server/libdash/src/dash-representation.c create mode 100644 3rdpart/media-server/libdash/src/dash-segment.c create mode 100644 3rdpart/media-server/libdash/src/list.h create mode 100644 3rdpart/media-server/libdash/src/xs-duration.c create mode 100644 3rdpart/media-server/libdash/test/dash-dynamic-test.cpp create mode 100644 3rdpart/media-server/libdash/test/dash-static-test.cpp create mode 100644 3rdpart/media-server/libflv/Android.mk create mode 100644 3rdpart/media-server/libflv/Makefile create mode 100644 3rdpart/media-server/libflv/include/amf0.h create mode 100644 3rdpart/media-server/libflv/include/amf3.h create mode 100644 3rdpart/media-server/libflv/include/aom-av1.h create mode 100644 3rdpart/media-server/libflv/include/avswg-avs3.h create mode 100644 3rdpart/media-server/libflv/include/flv-demuxer.h create mode 100644 3rdpart/media-server/libflv/include/flv-header.h create mode 100644 3rdpart/media-server/libflv/include/flv-muxer.h create mode 100644 3rdpart/media-server/libflv/include/flv-parser.h create mode 100644 3rdpart/media-server/libflv/include/flv-proto.h create mode 100644 3rdpart/media-server/libflv/include/flv-reader.h create mode 100644 3rdpart/media-server/libflv/include/flv-writer.h create mode 100644 3rdpart/media-server/libflv/include/mp3-header.h create mode 100644 3rdpart/media-server/libflv/include/mpeg4-aac.h create mode 100644 3rdpart/media-server/libflv/include/mpeg4-avc.h create mode 100644 3rdpart/media-server/libflv/include/mpeg4-bits.h create mode 100644 3rdpart/media-server/libflv/include/mpeg4-hevc.h create mode 100644 3rdpart/media-server/libflv/include/mpeg4-vvc.h create mode 100644 3rdpart/media-server/libflv/include/opus-head.h create mode 100644 3rdpart/media-server/libflv/include/riff-acm.h create mode 100644 3rdpart/media-server/libflv/include/webm-vpx.h create mode 100644 3rdpart/media-server/libflv/include/xiph-flac.h create mode 100644 3rdpart/media-server/libflv/libflv.vcxproj create mode 100644 3rdpart/media-server/libflv/libflv.vcxproj.filters create mode 100644 3rdpart/media-server/libflv/libflv.xcodeproj/project.pbxproj create mode 100644 3rdpart/media-server/libflv/source/amf0.c create mode 100644 3rdpart/media-server/libflv/source/amf3.c create mode 100644 3rdpart/media-server/libflv/source/aom-av1.c create mode 100644 3rdpart/media-server/libflv/source/avswg-avs3.c create mode 100644 3rdpart/media-server/libflv/source/flv-demuxer-script.c create mode 100644 3rdpart/media-server/libflv/source/flv-demuxer.c create mode 100644 3rdpart/media-server/libflv/source/flv-header.c create mode 100644 3rdpart/media-server/libflv/source/flv-muxer.c create mode 100644 3rdpart/media-server/libflv/source/flv-parser.c create mode 100644 3rdpart/media-server/libflv/source/flv-reader.c create mode 100644 3rdpart/media-server/libflv/source/flv-writer.c create mode 100644 3rdpart/media-server/libflv/source/hevc-annexbtomp4.c create mode 100644 3rdpart/media-server/libflv/source/hevc-mp4toannexb.c create mode 100644 3rdpart/media-server/libflv/source/mp3-header.c create mode 100644 3rdpart/media-server/libflv/source/mpeg4-aac-asc.c create mode 100644 3rdpart/media-server/libflv/source/mpeg4-aac.c create mode 100644 3rdpart/media-server/libflv/source/mpeg4-annexbtomp4.c create mode 100644 3rdpart/media-server/libflv/source/mpeg4-avc.c create mode 100644 3rdpart/media-server/libflv/source/mpeg4-hevc.c create mode 100644 3rdpart/media-server/libflv/source/mpeg4-mp4toannexb.c create mode 100644 3rdpart/media-server/libflv/source/mpeg4-vvc.c create mode 100644 3rdpart/media-server/libflv/source/opus-head.c create mode 100644 3rdpart/media-server/libflv/source/riff-acm.c create mode 100644 3rdpart/media-server/libflv/source/vvc-annexbtomp4.c create mode 100644 3rdpart/media-server/libflv/source/vvc-mp4toannexb.c create mode 100644 3rdpart/media-server/libflv/source/webm-vpx.c create mode 100644 3rdpart/media-server/libflv/source/xiph-flac.c create mode 100644 3rdpart/media-server/libflv/test/amf0-test.c create mode 100644 3rdpart/media-server/libflv/test/av1-flv-test.cpp create mode 100644 3rdpart/media-server/libflv/test/flv-parser-test.cpp create mode 100644 3rdpart/media-server/libflv/test/flv-read-write-test.cpp create mode 100644 3rdpart/media-server/libflv/test/flv-reader-test.cpp create mode 100644 3rdpart/media-server/libflv/test/flv2ts-test.cpp create mode 100644 3rdpart/media-server/libflv/test/h264-flv-test.cpp create mode 100644 3rdpart/media-server/libflv/test/h265-flv-test.cpp create mode 100644 3rdpart/media-server/libflv/test/http-flv-live.cpp create mode 100644 3rdpart/media-server/libflv/test/rtmp.onStatus.amf0 create mode 100644 3rdpart/media-server/libflv/test/ts2flv-test.cpp create mode 100644 3rdpart/media-server/libflv/version.ver create mode 100644 3rdpart/media-server/libhls/Android.mk create mode 100644 3rdpart/media-server/libhls/Makefile create mode 100644 3rdpart/media-server/libhls/demo/Makefile create mode 100644 3rdpart/media-server/libhls/demo/alternate.m3u8 create mode 100644 3rdpart/media-server/libhls/demo/alternate1.m3u8 create mode 100644 3rdpart/media-server/libhls/demo/hls-segmenter-flv.cpp create mode 100644 3rdpart/media-server/libhls/demo/hls-segmenter-mp4.cpp create mode 100644 3rdpart/media-server/libhls/demo/hls-server.cpp create mode 100644 3rdpart/media-server/libhls/demo/master.m3u8 create mode 100644 3rdpart/media-server/libhls/demo/playlist.m3u8 create mode 100644 3rdpart/media-server/libhls/demo/说明.txt create mode 100644 3rdpart/media-server/libhls/include/hls-fmp4.h create mode 100644 3rdpart/media-server/libhls/include/hls-m3u8.h create mode 100644 3rdpart/media-server/libhls/include/hls-media.h create mode 100644 3rdpart/media-server/libhls/include/hls-param.h create mode 100644 3rdpart/media-server/libhls/include/hls-parser.h create mode 100644 3rdpart/media-server/libhls/include/hls-string.h create mode 100644 3rdpart/media-server/libhls/libhls.vcxproj create mode 100644 3rdpart/media-server/libhls/libhls.vcxproj.filters create mode 100644 3rdpart/media-server/libhls/libhls.xcodeproj/project.pbxproj create mode 100644 3rdpart/media-server/libhls/source/hls-fmp4.c create mode 100644 3rdpart/media-server/libhls/source/hls-h264.h create mode 100644 3rdpart/media-server/libhls/source/hls-m3u8.c create mode 100644 3rdpart/media-server/libhls/source/hls-master.c create mode 100644 3rdpart/media-server/libhls/source/hls-media.c create mode 100644 3rdpart/media-server/libhls/source/hls-parser.c create mode 100644 3rdpart/media-server/libhls/source/hls-playlist.c create mode 100644 3rdpart/media-server/libhls/source/hls-string.c create mode 100644 3rdpart/media-server/libhls/source/list.h create mode 100644 3rdpart/media-server/libhls/version.ver create mode 100644 3rdpart/media-server/libmkv/Android.mk create mode 100644 3rdpart/media-server/libmkv/Makefile create mode 100644 3rdpart/media-server/libmkv/include/ebml.h create mode 100644 3rdpart/media-server/libmkv/include/mkv-buffer.h create mode 100644 3rdpart/media-server/libmkv/include/mkv-format.h create mode 100644 3rdpart/media-server/libmkv/include/mkv-reader.h create mode 100644 3rdpart/media-server/libmkv/include/mkv-writer.h create mode 100644 3rdpart/media-server/libmkv/libmkv.vcxproj create mode 100644 3rdpart/media-server/libmkv/libmkv.vcxproj.filters create mode 100644 3rdpart/media-server/libmkv/libmkv.xcodeproj/project.pbxproj create mode 100644 3rdpart/media-server/libmkv/libmkv.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 3rdpart/media-server/libmkv/src/ebml.c create mode 100644 3rdpart/media-server/libmkv/src/mkv-attachment.c create mode 100644 3rdpart/media-server/libmkv/src/mkv-chapter.c create mode 100644 3rdpart/media-server/libmkv/src/mkv-cluster.c create mode 100644 3rdpart/media-server/libmkv/src/mkv-codec.c create mode 100644 3rdpart/media-server/libmkv/src/mkv-cue.c create mode 100644 3rdpart/media-server/libmkv/src/mkv-internal.h create mode 100644 3rdpart/media-server/libmkv/src/mkv-ioutil.c create mode 100644 3rdpart/media-server/libmkv/src/mkv-ioutil.h create mode 100644 3rdpart/media-server/libmkv/src/mkv-reader.c create mode 100644 3rdpart/media-server/libmkv/src/mkv-track.c create mode 100644 3rdpart/media-server/libmkv/src/mkv-writer.c create mode 100644 3rdpart/media-server/libmkv/test/mkv-2-mp4-test.cpp create mode 100644 3rdpart/media-server/libmkv/test/mkv-file-buffer.c create mode 100644 3rdpart/media-server/libmkv/test/mkv-reader-test.cpp create mode 100644 3rdpart/media-server/libmkv/test/mkv-writer-test.cpp create mode 100644 3rdpart/media-server/libmkv/test/mkv-writer-test2.cpp create mode 100644 3rdpart/media-server/libmkv/test/mvk-writer-audio.cpp create mode 100644 3rdpart/media-server/libmov/Android.mk create mode 100644 3rdpart/media-server/libmov/Makefile create mode 100644 3rdpart/media-server/libmov/include/fmp4-writer.h create mode 100644 3rdpart/media-server/libmov/include/mov-atom.h create mode 100644 3rdpart/media-server/libmov/include/mov-box.h create mode 100644 3rdpart/media-server/libmov/include/mov-buffer.h create mode 100644 3rdpart/media-server/libmov/include/mov-format.h create mode 100644 3rdpart/media-server/libmov/include/mov-memory-buffer.h create mode 100644 3rdpart/media-server/libmov/include/mov-reader.h create mode 100644 3rdpart/media-server/libmov/include/mov-udta.h create mode 100644 3rdpart/media-server/libmov/include/mov-writer.h create mode 100644 3rdpart/media-server/libmov/include/mp4-writer.h create mode 100644 3rdpart/media-server/libmov/libmov.vcxproj create mode 100644 3rdpart/media-server/libmov/libmov.vcxproj.filters create mode 100644 3rdpart/media-server/libmov/libmov.xcodeproj/project.pbxproj create mode 100644 3rdpart/media-server/libmov/source/fmp4-reader.c create mode 100644 3rdpart/media-server/libmov/source/fmp4-writer.c create mode 100644 3rdpart/media-server/libmov/source/mov-avc1.c create mode 100644 3rdpart/media-server/libmov/source/mov-dinf.c create mode 100644 3rdpart/media-server/libmov/source/mov-elst.c create mode 100644 3rdpart/media-server/libmov/source/mov-esds.c create mode 100644 3rdpart/media-server/libmov/source/mov-ftyp.c create mode 100644 3rdpart/media-server/libmov/source/mov-hdlr.c create mode 100644 3rdpart/media-server/libmov/source/mov-hdr.c create mode 100644 3rdpart/media-server/libmov/source/mov-internal.h create mode 100644 3rdpart/media-server/libmov/source/mov-iods.c create mode 100644 3rdpart/media-server/libmov/source/mov-ioutil.h create mode 100644 3rdpart/media-server/libmov/source/mov-leva.c create mode 100644 3rdpart/media-server/libmov/source/mov-mdhd.c create mode 100644 3rdpart/media-server/libmov/source/mov-mehd.c create mode 100644 3rdpart/media-server/libmov/source/mov-mfhd.c create mode 100644 3rdpart/media-server/libmov/source/mov-minf.c create mode 100644 3rdpart/media-server/libmov/source/mov-mvhd.c create mode 100644 3rdpart/media-server/libmov/source/mov-opus.c create mode 100755 3rdpart/media-server/libmov/source/mov-reader.c create mode 100644 3rdpart/media-server/libmov/source/mov-sidx.c create mode 100644 3rdpart/media-server/libmov/source/mov-stco.c create mode 100644 3rdpart/media-server/libmov/source/mov-stsc.c create mode 100644 3rdpart/media-server/libmov/source/mov-stsd.c create mode 100644 3rdpart/media-server/libmov/source/mov-stss.c create mode 100644 3rdpart/media-server/libmov/source/mov-stsz.c create mode 100644 3rdpart/media-server/libmov/source/mov-stts.c create mode 100644 3rdpart/media-server/libmov/source/mov-tag.c create mode 100644 3rdpart/media-server/libmov/source/mov-tfdt.c create mode 100644 3rdpart/media-server/libmov/source/mov-tfhd.c create mode 100644 3rdpart/media-server/libmov/source/mov-tfra.c create mode 100644 3rdpart/media-server/libmov/source/mov-tkhd.c create mode 100644 3rdpart/media-server/libmov/source/mov-track.c create mode 100644 3rdpart/media-server/libmov/source/mov-trex.c create mode 100644 3rdpart/media-server/libmov/source/mov-trun.c create mode 100644 3rdpart/media-server/libmov/source/mov-tx3g.c create mode 100644 3rdpart/media-server/libmov/source/mov-udta.c create mode 100644 3rdpart/media-server/libmov/source/mov-vpcc.c create mode 100644 3rdpart/media-server/libmov/source/mov-writer.c create mode 100644 3rdpart/media-server/libmov/test/fmp4-writer-test.cpp create mode 100644 3rdpart/media-server/libmov/test/fmp4-writer-test2.cpp create mode 100644 3rdpart/media-server/libmov/test/mov-2-flv.cpp create mode 100644 3rdpart/media-server/libmov/test/mov-file-buffer.c create mode 100644 3rdpart/media-server/libmov/test/mov-file-buffer.h create mode 100644 3rdpart/media-server/libmov/test/mov-reader-test.cpp create mode 100644 3rdpart/media-server/libmov/test/mov-writer-adts.cpp create mode 100644 3rdpart/media-server/libmov/test/mov-writer-audio.cpp create mode 100644 3rdpart/media-server/libmov/test/mov-writer-av1.cpp create mode 100644 3rdpart/media-server/libmov/test/mov-writer-h264.cpp create mode 100644 3rdpart/media-server/libmov/test/mov-writer-h265.cpp create mode 100644 3rdpart/media-server/libmov/test/mov-writer-subtitle.cpp create mode 100644 3rdpart/media-server/libmov/test/mov-writer-test.cpp create mode 100644 3rdpart/media-server/libmov/test/mov-writer-vp9.cpp create mode 100644 3rdpart/media-server/libmpeg/Android.mk create mode 100644 3rdpart/media-server/libmpeg/Makefile create mode 100644 3rdpart/media-server/libmpeg/README.md create mode 100644 3rdpart/media-server/libmpeg/include/mpeg-element-descriptor.h create mode 100644 3rdpart/media-server/libmpeg/include/mpeg-muxer.h create mode 100644 3rdpart/media-server/libmpeg/include/mpeg-pes-proto.h create mode 100644 3rdpart/media-server/libmpeg/include/mpeg-proto.h create mode 100644 3rdpart/media-server/libmpeg/include/mpeg-ps-proto.h create mode 100644 3rdpart/media-server/libmpeg/include/mpeg-ps.h create mode 100644 3rdpart/media-server/libmpeg/include/mpeg-ts-proto.h create mode 100644 3rdpart/media-server/libmpeg/include/mpeg-ts.h create mode 100644 3rdpart/media-server/libmpeg/include/mpeg-types.h create mode 100644 3rdpart/media-server/libmpeg/include/mpeg-util.h create mode 100644 3rdpart/media-server/libmpeg/libmpeg.vcxproj create mode 100644 3rdpart/media-server/libmpeg/libmpeg.vcxproj.filters create mode 100644 3rdpart/media-server/libmpeg/libmpeg.xcodeproj/project.pbxproj create mode 100644 3rdpart/media-server/libmpeg/source/mpeg-crc32.c create mode 100644 3rdpart/media-server/libmpeg/source/mpeg-element-descriptor.c create mode 100644 3rdpart/media-server/libmpeg/source/mpeg-muxer.c create mode 100644 3rdpart/media-server/libmpeg/source/mpeg-pack-header.c create mode 100644 3rdpart/media-server/libmpeg/source/mpeg-packet.c create mode 100644 3rdpart/media-server/libmpeg/source/mpeg-pat.c create mode 100644 3rdpart/media-server/libmpeg/source/mpeg-pes-internal.h create mode 100644 3rdpart/media-server/libmpeg/source/mpeg-pes.c create mode 100644 3rdpart/media-server/libmpeg/source/mpeg-pmt.c create mode 100644 3rdpart/media-server/libmpeg/source/mpeg-ps-dec.c create mode 100644 3rdpart/media-server/libmpeg/source/mpeg-ps-enc.c create mode 100644 3rdpart/media-server/libmpeg/source/mpeg-ps-internal.h create mode 100644 3rdpart/media-server/libmpeg/source/mpeg-psd.c create mode 100644 3rdpart/media-server/libmpeg/source/mpeg-psm.c create mode 100644 3rdpart/media-server/libmpeg/source/mpeg-sdt.c create mode 100644 3rdpart/media-server/libmpeg/source/mpeg-system-header.c create mode 100644 3rdpart/media-server/libmpeg/source/mpeg-ts-dec.c create mode 100644 3rdpart/media-server/libmpeg/source/mpeg-ts-enc.c create mode 100644 3rdpart/media-server/libmpeg/source/mpeg-ts-h264.c create mode 100644 3rdpart/media-server/libmpeg/source/mpeg-ts-h265.c create mode 100644 3rdpart/media-server/libmpeg/source/mpeg-ts-h266.c create mode 100644 3rdpart/media-server/libmpeg/source/mpeg-ts-internal.h create mode 100644 3rdpart/media-server/libmpeg/source/mpeg-ts-opus.h create mode 100644 3rdpart/media-server/libmpeg/source/mpeg-util.c create mode 100644 3rdpart/media-server/libmpeg/test/fileSequence0.ts create mode 100644 3rdpart/media-server/libmpeg/test/flv-2-mpeg-ps-test.cpp create mode 100644 3rdpart/media-server/libmpeg/test/mov-2-mpeg-ps-test.cpp create mode 100644 3rdpart/media-server/libmpeg/test/mpeg-ps-2-flv-test.cpp create mode 100644 3rdpart/media-server/libmpeg/test/mpeg-ps-dec-test.cpp create mode 100644 3rdpart/media-server/libmpeg/test/mpeg-ps-test.cpp create mode 100644 3rdpart/media-server/libmpeg/test/mpeg-ts-dec-test.cpp create mode 100644 3rdpart/media-server/libmpeg/test/mpeg-ts-encrypt-test.cpp create mode 100644 3rdpart/media-server/libmpeg/test/mpeg-ts-multi-program-test.cpp create mode 100644 3rdpart/media-server/libmpeg/test/mpeg-ts-test.cpp create mode 100644 3rdpart/media-server/libmpeg/version.ver create mode 100644 3rdpart/media-server/librtmp/Android.mk create mode 100644 3rdpart/media-server/librtmp/Makefile create mode 100644 3rdpart/media-server/librtmp/aio/aio-rtmp-client.c create mode 100644 3rdpart/media-server/librtmp/aio/aio-rtmp-client.h create mode 100644 3rdpart/media-server/librtmp/aio/aio-rtmp-server.c create mode 100644 3rdpart/media-server/librtmp/aio/aio-rtmp-server.h create mode 100644 3rdpart/media-server/librtmp/aio/aio-rtmp-transport.c create mode 100644 3rdpart/media-server/librtmp/aio/aio-rtmp-transport.h create mode 100644 3rdpart/media-server/librtmp/include/rtmp-chunk-header.h create mode 100644 3rdpart/media-server/librtmp/include/rtmp-client.h create mode 100644 3rdpart/media-server/librtmp/include/rtmp-control-message.h create mode 100644 3rdpart/media-server/librtmp/include/rtmp-event.h create mode 100644 3rdpart/media-server/librtmp/include/rtmp-handshake.h create mode 100644 3rdpart/media-server/librtmp/include/rtmp-internal.h create mode 100644 3rdpart/media-server/librtmp/include/rtmp-msgtypeid.h create mode 100644 3rdpart/media-server/librtmp/include/rtmp-netconnection.h create mode 100644 3rdpart/media-server/librtmp/include/rtmp-netstream.h create mode 100644 3rdpart/media-server/librtmp/include/rtmp-server.h create mode 100644 3rdpart/media-server/librtmp/include/rtmp-url.h create mode 100644 3rdpart/media-server/librtmp/include/rtmp-util.h create mode 100644 3rdpart/media-server/librtmp/librtmp.vcxproj create mode 100644 3rdpart/media-server/librtmp/librtmp.vcxproj.filters create mode 100644 3rdpart/media-server/librtmp/librtmp.xcodeproj/project.pbxproj create mode 100644 3rdpart/media-server/librtmp/source/rtmp-chunk-header.c create mode 100644 3rdpart/media-server/librtmp/source/rtmp-chunk-read.c create mode 100644 3rdpart/media-server/librtmp/source/rtmp-chunk-write.c create mode 100644 3rdpart/media-server/librtmp/source/rtmp-client-invoke-handler.h create mode 100644 3rdpart/media-server/librtmp/source/rtmp-client.c create mode 100644 3rdpart/media-server/librtmp/source/rtmp-control-handler.c create mode 100644 3rdpart/media-server/librtmp/source/rtmp-control-message.c create mode 100644 3rdpart/media-server/librtmp/source/rtmp-event.c create mode 100644 3rdpart/media-server/librtmp/source/rtmp-handler.c create mode 100644 3rdpart/media-server/librtmp/source/rtmp-handshake.c create mode 100644 3rdpart/media-server/librtmp/source/rtmp-invoke-handler.c create mode 100644 3rdpart/media-server/librtmp/source/rtmp-netconnection.c create mode 100644 3rdpart/media-server/librtmp/source/rtmp-netstream.c create mode 100644 3rdpart/media-server/librtmp/source/rtmp-server.c create mode 100644 3rdpart/media-server/librtmp/test/RTMPUrl.h create mode 100644 3rdpart/media-server/librtmp/test/rtmp-chunk-test.cpp create mode 100644 3rdpart/media-server/librtmp/test/rtmp-input-test.cpp create mode 100644 3rdpart/media-server/librtmp/test/rtmp-play-aio-test.cpp create mode 100644 3rdpart/media-server/librtmp/test/rtmp-play-test.cpp create mode 100644 3rdpart/media-server/librtmp/test/rtmp-publish-aio-test.cpp create mode 100644 3rdpart/media-server/librtmp/test/rtmp-publish-test.cpp create mode 100644 3rdpart/media-server/librtmp/test/rtmp-server-forward-aio-test.cpp create mode 100644 3rdpart/media-server/librtmp/test/rtmp-server-input-test.cpp create mode 100644 3rdpart/media-server/librtmp/test/rtmp-server-publish-aio-test.cpp create mode 100644 3rdpart/media-server/librtmp/test/rtmp-server-publish-benchmark.cpp create mode 100644 3rdpart/media-server/librtmp/test/rtmp-server-publish-test.cpp create mode 100644 3rdpart/media-server/librtmp/test/rtmp-server-vod-aio-test.cpp create mode 100644 3rdpart/media-server/librtmp/test/rtmp-server-vod-test.cpp create mode 100644 3rdpart/media-server/librtmp/version.ver create mode 100644 3rdpart/media-server/librtp/Android.mk create mode 100644 3rdpart/media-server/librtp/Makefile create mode 100644 3rdpart/media-server/librtp/include/rtcp-header.h create mode 100644 3rdpart/media-server/librtp/include/rtp-demuxer.h create mode 100644 3rdpart/media-server/librtp/include/rtp-ext.h create mode 100644 3rdpart/media-server/librtp/include/rtp-header-extension.h create mode 100644 3rdpart/media-server/librtp/include/rtp-header.h create mode 100644 3rdpart/media-server/librtp/include/rtp-internal.h create mode 100644 3rdpart/media-server/librtp/include/rtp-member-list.h create mode 100644 3rdpart/media-server/librtp/include/rtp-member.h create mode 100644 3rdpart/media-server/librtp/include/rtp-packet.h create mode 100644 3rdpart/media-server/librtp/include/rtp-param.h create mode 100644 3rdpart/media-server/librtp/include/rtp-payload.h create mode 100644 3rdpart/media-server/librtp/include/rtp-profile.h create mode 100644 3rdpart/media-server/librtp/include/rtp-queue.h create mode 100644 3rdpart/media-server/librtp/include/rtp-util.h create mode 100644 3rdpart/media-server/librtp/include/rtp.h create mode 100644 3rdpart/media-server/librtp/librtp.vcxproj create mode 100644 3rdpart/media-server/librtp/librtp.vcxproj.filters create mode 100644 3rdpart/media-server/librtp/librtp.xcodeproj/project.pbxproj create mode 100644 3rdpart/media-server/librtp/payload/rtp-av1-pack.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-av1-unpack.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-h264-bitstream.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-h264-pack.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-h264-unpack.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-h265-pack.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-h265-unpack.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-h266-pack.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-h266-unpack.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-mp4a-latm-pack.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-mp4a-latm-unpack.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-mp4v-es-pack.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-mp4v-es-unpack.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-mpeg1or2es-pack.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-mpeg1or2es-unpack.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-mpeg4-generic-pack.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-mpeg4-generic-unpack.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-pack.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-payload-helper.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-payload-helper.h create mode 100644 3rdpart/media-server/librtp/payload/rtp-payload-internal.h create mode 100644 3rdpart/media-server/librtp/payload/rtp-payload.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-ps-unpack.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-ts-pack.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-ts-unpack.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-unpack.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-vp8-pack.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-vp8-unpack.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-vp9-pack.c create mode 100644 3rdpart/media-server/librtp/payload/rtp-vp9-unpack.c create mode 100644 3rdpart/media-server/librtp/rtpext/rtp-ext-abs-send-time.c create mode 100644 3rdpart/media-server/librtp/rtpext/rtp-ext-absolute-capture-time.c create mode 100644 3rdpart/media-server/librtp/rtpext/rtp-ext-color-space.c create mode 100644 3rdpart/media-server/librtp/rtpext/rtp-ext-csrc-audio-level.c create mode 100644 3rdpart/media-server/librtp/rtpext/rtp-ext-frame-marking.c create mode 100644 3rdpart/media-server/librtp/rtpext/rtp-ext-inband-cn.c create mode 100644 3rdpart/media-server/librtp/rtpext/rtp-ext-playout-delay.c create mode 100644 3rdpart/media-server/librtp/rtpext/rtp-ext-sdes.c create mode 100644 3rdpart/media-server/librtp/rtpext/rtp-ext-ssrc-audio-level.c create mode 100644 3rdpart/media-server/librtp/rtpext/rtp-ext-toffset.c create mode 100644 3rdpart/media-server/librtp/rtpext/rtp-ext-transport-wide-cc.c create mode 100644 3rdpart/media-server/librtp/rtpext/rtp-ext-video-content-type.c create mode 100644 3rdpart/media-server/librtp/rtpext/rtp-ext-video-frame-tracking-id.c create mode 100644 3rdpart/media-server/librtp/rtpext/rtp-ext-video-layers-allocation.c create mode 100644 3rdpart/media-server/librtp/rtpext/rtp-ext-video-orientation.c create mode 100644 3rdpart/media-server/librtp/rtpext/rtp-ext-video-timing.c create mode 100644 3rdpart/media-server/librtp/rtpext/rtp-ext.c create mode 100644 3rdpart/media-server/librtp/source/rtcp-app.c create mode 100644 3rdpart/media-server/librtp/source/rtcp-bye.c create mode 100644 3rdpart/media-server/librtp/source/rtcp-interval.c create mode 100644 3rdpart/media-server/librtp/source/rtcp-psfb.c create mode 100644 3rdpart/media-server/librtp/source/rtcp-rr.c create mode 100644 3rdpart/media-server/librtp/source/rtcp-rtpfb.c create mode 100644 3rdpart/media-server/librtp/source/rtcp-sdec.c create mode 100644 3rdpart/media-server/librtp/source/rtcp-sr.c create mode 100644 3rdpart/media-server/librtp/source/rtcp-xr.c create mode 100644 3rdpart/media-server/librtp/source/rtcp.c create mode 100644 3rdpart/media-server/librtp/source/rtp-demuxer.c create mode 100644 3rdpart/media-server/librtp/source/rtp-member-list.c create mode 100644 3rdpart/media-server/librtp/source/rtp-member.c create mode 100644 3rdpart/media-server/librtp/source/rtp-packet.c create mode 100644 3rdpart/media-server/librtp/source/rtp-profile.c create mode 100644 3rdpart/media-server/librtp/source/rtp-queue.c create mode 100644 3rdpart/media-server/librtp/source/rtp-ssrc.c create mode 100644 3rdpart/media-server/librtp/source/rtp-time.c create mode 100644 3rdpart/media-server/librtp/source/rtp.c create mode 100644 3rdpart/media-server/librtp/test/av1-rtp-test.cpp create mode 100644 3rdpart/media-server/librtp/test/mov-rtp-test.cpp create mode 100644 3rdpart/media-server/librtp/test/rtp-dump-replay.cpp create mode 100644 3rdpart/media-server/librtp/test/rtp-dump-test.cpp create mode 100644 3rdpart/media-server/librtp/test/rtp-dump.c create mode 100644 3rdpart/media-server/librtp/test/rtp-dump.h create mode 100644 3rdpart/media-server/librtp/test/rtp-header-ext-test.cpp create mode 100644 3rdpart/media-server/librtp/test/rtp-payload-test.cpp create mode 100644 3rdpart/media-server/librtp/test/rtp-queue-test.cpp create mode 100644 3rdpart/media-server/librtp/test/rtp-receiver-test.c create mode 100644 3rdpart/media-server/librtp/test/rtp-sender-test.cpp create mode 100644 3rdpart/media-server/librtsp/Android.mk create mode 100644 3rdpart/media-server/librtsp/Makefile create mode 100644 3rdpart/media-server/librtsp/TODO.txt create mode 100644 3rdpart/media-server/librtsp/include/rtp-over-rtsp.h create mode 100644 3rdpart/media-server/librtsp/include/rtp-sender.h create mode 100644 3rdpart/media-server/librtsp/include/rtsp-client.h create mode 100644 3rdpart/media-server/librtsp/include/rtsp-demuxer.h create mode 100644 3rdpart/media-server/librtsp/include/rtsp-header-range.h create mode 100644 3rdpart/media-server/librtsp/include/rtsp-header-rtp-info.h create mode 100644 3rdpart/media-server/librtsp/include/rtsp-header-session.h create mode 100644 3rdpart/media-server/librtsp/include/rtsp-header-transport.h create mode 100644 3rdpart/media-server/librtsp/include/rtsp-media.h create mode 100644 3rdpart/media-server/librtsp/include/rtsp-muxer.h create mode 100644 3rdpart/media-server/librtsp/include/rtsp-payloads.h create mode 100644 3rdpart/media-server/librtsp/include/rtsp-reason.h create mode 100644 3rdpart/media-server/librtsp/include/rtsp-server-aio.h create mode 100644 3rdpart/media-server/librtsp/include/rtsp-server.h create mode 100644 3rdpart/media-server/librtsp/include/sdp-a-fmtp.h create mode 100644 3rdpart/media-server/librtsp/include/sdp-a-rtpmap.h create mode 100644 3rdpart/media-server/librtsp/include/sdp-a-webrtc.h create mode 100644 3rdpart/media-server/librtsp/include/sdp-options.h create mode 100644 3rdpart/media-server/librtsp/include/sdp-payload.h create mode 100644 3rdpart/media-server/librtsp/include/sdp.h create mode 100644 3rdpart/media-server/librtsp/librtsp.vcxproj create mode 100644 3rdpart/media-server/librtsp/librtsp.vcxproj.filters create mode 100644 3rdpart/media-server/librtsp/librtsp.xcodeproj/project.pbxproj create mode 100644 3rdpart/media-server/librtsp/source/client/rtp-over-rtsp.c create mode 100644 3rdpart/media-server/librtsp/source/client/rtsp-client-announce.c create mode 100644 3rdpart/media-server/librtsp/source/client/rtsp-client-auth.c create mode 100644 3rdpart/media-server/librtsp/source/client/rtsp-client-describe.c create mode 100644 3rdpart/media-server/librtsp/source/client/rtsp-client-get-parameter.c create mode 100644 3rdpart/media-server/librtsp/source/client/rtsp-client-internal.h create mode 100644 3rdpart/media-server/librtsp/source/client/rtsp-client-options.c create mode 100644 3rdpart/media-server/librtsp/source/client/rtsp-client-pause.c create mode 100644 3rdpart/media-server/librtsp/source/client/rtsp-client-play.c create mode 100644 3rdpart/media-server/librtsp/source/client/rtsp-client-record.c create mode 100644 3rdpart/media-server/librtsp/source/client/rtsp-client-set-parameter.c create mode 100644 3rdpart/media-server/librtsp/source/client/rtsp-client-setup.c create mode 100644 3rdpart/media-server/librtsp/source/client/rtsp-client-teardown.c create mode 100644 3rdpart/media-server/librtsp/source/client/rtsp-client.c create mode 100644 3rdpart/media-server/librtsp/source/rtsp-header-range.c create mode 100644 3rdpart/media-server/librtsp/source/rtsp-header-rtp-info.c create mode 100644 3rdpart/media-server/librtsp/source/rtsp-header-session.c create mode 100644 3rdpart/media-server/librtsp/source/rtsp-header-transport.c create mode 100644 3rdpart/media-server/librtsp/source/rtsp-media.c create mode 100644 3rdpart/media-server/librtsp/source/rtsp-multicast.c create mode 100644 3rdpart/media-server/librtsp/source/rtsp-reason.c create mode 100644 3rdpart/media-server/librtsp/source/sdp-a-fmtp.c create mode 100644 3rdpart/media-server/librtsp/source/sdp-a-rtpmap.c create mode 100644 3rdpart/media-server/librtsp/source/sdp-a-webrtc.c create mode 100644 3rdpart/media-server/librtsp/source/sdp-options.c create mode 100644 3rdpart/media-server/librtsp/source/sdp.c create mode 100644 3rdpart/media-server/librtsp/source/sdp/sdp-aac.c create mode 100644 3rdpart/media-server/librtsp/source/sdp/sdp-av1.c create mode 100644 3rdpart/media-server/librtsp/source/sdp/sdp-fmtp-load.c create mode 100644 3rdpart/media-server/librtsp/source/sdp/sdp-g7xx.c create mode 100644 3rdpart/media-server/librtsp/source/sdp/sdp-h264.c create mode 100644 3rdpart/media-server/librtsp/source/sdp/sdp-h265.c create mode 100644 3rdpart/media-server/librtsp/source/sdp/sdp-h266.c create mode 100644 3rdpart/media-server/librtsp/source/sdp/sdp-mpeg2.c create mode 100644 3rdpart/media-server/librtsp/source/sdp/sdp-mpeg4.c create mode 100644 3rdpart/media-server/librtsp/source/sdp/sdp-opus.c create mode 100644 3rdpart/media-server/librtsp/source/sdp/sdp-payload.c create mode 100644 3rdpart/media-server/librtsp/source/sdp/sdp-vpx.c create mode 100644 3rdpart/media-server/librtsp/source/server/aio/rtsp-server-listen.c create mode 100644 3rdpart/media-server/librtsp/source/server/aio/rtsp-server-tcp.c create mode 100644 3rdpart/media-server/librtsp/source/server/aio/rtsp-server-udp.c create mode 100644 3rdpart/media-server/librtsp/source/server/rtsp-server-announce.c create mode 100644 3rdpart/media-server/librtsp/source/server/rtsp-server-describe.c create mode 100644 3rdpart/media-server/librtsp/source/server/rtsp-server-handler.c create mode 100644 3rdpart/media-server/librtsp/source/server/rtsp-server-internal.h create mode 100644 3rdpart/media-server/librtsp/source/server/rtsp-server-options.c create mode 100644 3rdpart/media-server/librtsp/source/server/rtsp-server-parameter.c create mode 100644 3rdpart/media-server/librtsp/source/server/rtsp-server-pause.c create mode 100644 3rdpart/media-server/librtsp/source/server/rtsp-server-play.c create mode 100644 3rdpart/media-server/librtsp/source/server/rtsp-server-record.c create mode 100644 3rdpart/media-server/librtsp/source/server/rtsp-server-setup.c create mode 100644 3rdpart/media-server/librtsp/source/server/rtsp-server-teardown.c create mode 100644 3rdpart/media-server/librtsp/source/server/rtsp-server.c create mode 100644 3rdpart/media-server/librtsp/source/utils/rtp-sender.c create mode 100644 3rdpart/media-server/librtsp/source/utils/rtsp-demuxer.c create mode 100644 3rdpart/media-server/librtsp/source/utils/rtsp-muxer.c create mode 100644 3rdpart/media-server/librtsp/test/Makefile create mode 100644 3rdpart/media-server/librtsp/test/media/avpacket-queue.cpp create mode 100644 3rdpart/media-server/librtsp/test/media/avpacket-queue.h create mode 100644 3rdpart/media-server/librtsp/test/media/ffmpeg-file-source.cpp create mode 100644 3rdpart/media-server/librtsp/test/media/ffmpeg-file-source.h create mode 100644 3rdpart/media-server/librtsp/test/media/ffmpeg-live-source.cpp create mode 100644 3rdpart/media-server/librtsp/test/media/ffmpeg-live-source.h create mode 100644 3rdpart/media-server/librtsp/test/media/h264-file-reader.cpp create mode 100644 3rdpart/media-server/librtsp/test/media/h264-file-reader.h create mode 100644 3rdpart/media-server/librtsp/test/media/h264-file-source.cpp create mode 100644 3rdpart/media-server/librtsp/test/media/h264-file-source.h create mode 100644 3rdpart/media-server/librtsp/test/media/h265-file-reader.cpp create mode 100644 3rdpart/media-server/librtsp/test/media/h265-file-reader.h create mode 100644 3rdpart/media-server/librtsp/test/media/h265-file-source.cpp create mode 100644 3rdpart/media-server/librtsp/test/media/h265-file-source.h create mode 100644 3rdpart/media-server/librtsp/test/media/media-source.h create mode 100644 3rdpart/media-server/librtsp/test/media/mp4-file-reader.cpp create mode 100644 3rdpart/media-server/librtsp/test/media/mp4-file-reader.h create mode 100644 3rdpart/media-server/librtsp/test/media/mp4-file-source.cpp create mode 100644 3rdpart/media-server/librtsp/test/media/mp4-file-source.h create mode 100644 3rdpart/media-server/librtsp/test/media/pcm-file-source.cpp create mode 100644 3rdpart/media-server/librtsp/test/media/pcm-file-source.h create mode 100644 3rdpart/media-server/librtsp/test/media/ps-file-reader.cpp create mode 100644 3rdpart/media-server/librtsp/test/media/ps-file-reader.h create mode 100644 3rdpart/media-server/librtsp/test/media/ps-file-source.cpp create mode 100644 3rdpart/media-server/librtsp/test/media/ps-file-source.h create mode 100644 3rdpart/media-server/librtsp/test/media/vod-file-source.cpp create mode 100644 3rdpart/media-server/librtsp/test/media/vod-file-source.h create mode 100644 3rdpart/media-server/librtsp/test/rtp-streaming-test.cpp create mode 100644 3rdpart/media-server/librtsp/test/rtp-tcp-transport.h create mode 100644 3rdpart/media-server/librtsp/test/rtp-udp-transport.cpp create mode 100644 3rdpart/media-server/librtsp/test/rtp-udp-transport.h create mode 100644 3rdpart/media-server/librtsp/test/rtsp-client-input-test.cpp create mode 100644 3rdpart/media-server/librtsp/test/rtsp-client-push-test.cpp create mode 100644 3rdpart/media-server/librtsp/test/rtsp-client-test.c create mode 100644 3rdpart/media-server/librtsp/test/rtsp-client-test2.c create mode 100644 3rdpart/media-server/librtsp/test/rtsp-demuxer-test.cpp create mode 100644 3rdpart/media-server/librtsp/test/rtsp-push-server.cpp create mode 100644 3rdpart/media-server/librtsp/test/rtsp-server-test.cpp create mode 100644 3rdpart/media-server/librtsp/test/sdp-receiver-test.cpp create mode 100644 3rdpart/media-server/librtsp/test/sdp-test.cpp create mode 100644 3rdpart/media-server/librtsp/test/sdp1.txt create mode 100644 3rdpart/media-server/librtsp/test/sdp2.txt create mode 100644 3rdpart/media-server/libsip/Android.mk create mode 100644 3rdpart/media-server/libsip/Makefile create mode 100644 3rdpart/media-server/libsip/include/sip-agent.h create mode 100644 3rdpart/media-server/libsip/include/sip-dialog.h create mode 100644 3rdpart/media-server/libsip/include/sip-header.h create mode 100644 3rdpart/media-server/libsip/include/sip-message.h create mode 100644 3rdpart/media-server/libsip/include/sip-subscribe.h create mode 100644 3rdpart/media-server/libsip/include/sip-timer.h create mode 100644 3rdpart/media-server/libsip/include/sip-transport.h create mode 100644 3rdpart/media-server/libsip/include/sip-uac.h create mode 100644 3rdpart/media-server/libsip/include/sip-uas.h create mode 100644 3rdpart/media-server/libsip/libsip.vcxproj create mode 100644 3rdpart/media-server/libsip/libsip.vcxproj.filters create mode 100644 3rdpart/media-server/libsip/libsip.xcodeproj/project.pbxproj create mode 100644 3rdpart/media-server/libsip/src/header/sip-header-contact.c create mode 100644 3rdpart/media-server/libsip/src/header/sip-header-cseq.c create mode 100644 3rdpart/media-server/libsip/src/header/sip-header-event.c create mode 100644 3rdpart/media-server/libsip/src/header/sip-header-param.c create mode 100644 3rdpart/media-server/libsip/src/header/sip-header-route.c create mode 100644 3rdpart/media-server/libsip/src/header/sip-header-substate.c create mode 100644 3rdpart/media-server/libsip/src/header/sip-header-uri.c create mode 100644 3rdpart/media-server/libsip/src/header/sip-header-via.c create mode 100644 3rdpart/media-server/libsip/src/header/sip-header.c create mode 100644 3rdpart/media-server/libsip/src/sip-agent.c create mode 100644 3rdpart/media-server/libsip/src/sip-dialog.c create mode 100644 3rdpart/media-server/libsip/src/sip-internal.h create mode 100644 3rdpart/media-server/libsip/src/sip-message.c create mode 100644 3rdpart/media-server/libsip/src/sip-reason.c create mode 100644 3rdpart/media-server/libsip/src/sip-subscribe.c create mode 100644 3rdpart/media-server/libsip/src/uac/sip-uac-ack.c create mode 100644 3rdpart/media-server/libsip/src/uac/sip-uac-bye.c create mode 100644 3rdpart/media-server/libsip/src/uac/sip-uac-cancel.c create mode 100644 3rdpart/media-server/libsip/src/uac/sip-uac-info.c create mode 100644 3rdpart/media-server/libsip/src/uac/sip-uac-invite.c create mode 100644 3rdpart/media-server/libsip/src/uac/sip-uac-options.c create mode 100644 3rdpart/media-server/libsip/src/uac/sip-uac-register.c create mode 100644 3rdpart/media-server/libsip/src/uac/sip-uac-subscribe.c create mode 100644 3rdpart/media-server/libsip/src/uac/sip-uac-transaction-invite.c create mode 100644 3rdpart/media-server/libsip/src/uac/sip-uac-transaction-noninvite.c create mode 100644 3rdpart/media-server/libsip/src/uac/sip-uac-transaction.c create mode 100644 3rdpart/media-server/libsip/src/uac/sip-uac-transaction.h create mode 100644 3rdpart/media-server/libsip/src/uac/sip-uac.c create mode 100644 3rdpart/media-server/libsip/src/uas/sip-uas-bye.c create mode 100644 3rdpart/media-server/libsip/src/uas/sip-uas-cancel.c create mode 100644 3rdpart/media-server/libsip/src/uas/sip-uas-info.c create mode 100644 3rdpart/media-server/libsip/src/uas/sip-uas-options.c create mode 100644 3rdpart/media-server/libsip/src/uas/sip-uas-prack.c create mode 100644 3rdpart/media-server/libsip/src/uas/sip-uas-refer.c create mode 100644 3rdpart/media-server/libsip/src/uas/sip-uas-register.c create mode 100644 3rdpart/media-server/libsip/src/uas/sip-uas-subscribe.c create mode 100644 3rdpart/media-server/libsip/src/uas/sip-uas-transaction-invite.c create mode 100644 3rdpart/media-server/libsip/src/uas/sip-uas-transaction-noninvite.c create mode 100644 3rdpart/media-server/libsip/src/uas/sip-uas-transaction.c create mode 100644 3rdpart/media-server/libsip/src/uas/sip-uas-transaction.h create mode 100644 3rdpart/media-server/libsip/src/uas/sip-uas.c create mode 100644 3rdpart/media-server/libsip/test/sip-agent-test.cpp create mode 100644 3rdpart/media-server/libsip/test/sip-header-test.c create mode 100644 3rdpart/media-server/libsip/test/sip-message-test.cpp create mode 100644 3rdpart/media-server/libsip/test/sip-timer.c create mode 100644 3rdpart/media-server/libsip/test/sip-uac-message-test.cpp create mode 100644 3rdpart/media-server/libsip/test/sip-uac-test.cpp create mode 100644 3rdpart/media-server/libsip/test/sip-uac-test2.cpp create mode 100644 3rdpart/media-server/libsip/test/sip-uas-message-test.cpp create mode 100644 3rdpart/media-server/libsip/test/sip-uas-test.cpp create mode 100644 3rdpart/media-server/libsip/test/sip-uas-test2.cpp create mode 100644 3rdpart/media-server/libsip/test/transport-tcp.c create mode 100644 3rdpart/media-server/libsip/test/transport-udp.c create mode 100644 3rdpart/media-server/media-server.sln create mode 100644 3rdpart/media-server/media-server.xcworkspace/contents.xcworkspacedata create mode 100644 3rdpart/media-server/test/BinaryDiff.cpp create mode 100755 3rdpart/media-server/test/Makefile create mode 100755 3rdpart/media-server/test/Reflector.h create mode 100755 3rdpart/media-server/test/test.cpp create mode 100644 3rdpart/media-server/test/test.vcxproj create mode 100644 3rdpart/media-server/test/test.vcxproj.filters create mode 100644 3rdpart/media-server/test/test.xcodeproj/project.pbxproj create mode 100644 AUTHORS create mode 100644 Android/.gitignore create mode 100644 Android/app/.gitignore create mode 100644 Android/app/build.gradle create mode 100644 Android/app/proguard-rules.pro create mode 100644 Android/app/src/androidTest/java/com/zlmediakit/demo/ExampleInstrumentedTest.java create mode 100644 Android/app/src/main/AndroidManifest.xml create mode 100644 Android/app/src/main/cpp/CMakeLists.txt create mode 100644 Android/app/src/main/cpp/native-lib.cpp create mode 100644 Android/app/src/main/java/com/zlmediakit/demo/MainActivity.java create mode 100644 Android/app/src/main/java/com/zlmediakit/jni/ZLMediaKit.java create mode 100644 Android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 Android/app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 Android/app/src/main/res/layout/activity_main.xml create mode 100644 Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 Android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 Android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 Android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 Android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 Android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 Android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 Android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 Android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 Android/app/src/main/res/values/colors.xml create mode 100644 Android/app/src/main/res/values/strings.xml create mode 100644 Android/app/src/main/res/values/styles.xml create mode 100644 Android/app/src/test/java/com/zlmediakit/demo/ExampleUnitTest.java create mode 100644 Android/build.gradle create mode 100644 Android/gradle.properties create mode 100644 Android/gradle/wrapper/gradle-wrapper.jar create mode 100644 Android/gradle/wrapper/gradle-wrapper.properties create mode 100755 Android/gradlew create mode 100644 Android/gradlew.bat create mode 100644 Android/settings.gradle create mode 100644 CMakeLists.txt create mode 100644 DejaVuSans.ttf create mode 100644 LICENSE create mode 100644 README.md create mode 100644 README_en.md create mode 100644 ZLMVersion.h.ini create mode 100644 api/CMakeLists.txt create mode 100755 api/include/mk_common.h create mode 100644 api/include/mk_events.h create mode 100644 api/include/mk_events_objects.h create mode 100644 api/include/mk_frame.h create mode 100644 api/include/mk_h264_splitter.h create mode 100755 api/include/mk_httpclient.h create mode 100755 api/include/mk_media.h create mode 100755 api/include/mk_mediakit.h create mode 100755 api/include/mk_player.h create mode 100644 api/include/mk_proxyplayer.h create mode 100644 api/include/mk_pusher.h create mode 100644 api/include/mk_recorder.h create mode 100644 api/include/mk_rtp_server.h create mode 100644 api/include/mk_tcp.h create mode 100644 api/include/mk_thread.h create mode 100644 api/include/mk_track.h create mode 100644 api/include/mk_transcode.h create mode 100644 api/include/mk_util.h create mode 100644 api/include/mk_webrtc.h create mode 100644 api/source/mk_common.cpp create mode 100644 api/source/mk_events.cpp create mode 100644 api/source/mk_events_objects.cpp create mode 100644 api/source/mk_frame.cpp create mode 100644 api/source/mk_h264_splitter.cpp create mode 100755 api/source/mk_httpclient.cpp create mode 100755 api/source/mk_media.cpp create mode 100755 api/source/mk_player.cpp create mode 100644 api/source/mk_proxyplayer.cpp create mode 100644 api/source/mk_pusher.cpp create mode 100644 api/source/mk_recorder.cpp create mode 100644 api/source/mk_rtp_server.cpp create mode 100644 api/source/mk_tcp.cpp create mode 100644 api/source/mk_tcp_private.h create mode 100644 api/source/mk_thread.cpp create mode 100644 api/source/mk_track.cpp create mode 100644 api/source/mk_transcode.cpp create mode 100644 api/source/mk_util.cpp create mode 100644 api/source/mk_webrtc.cpp create mode 100644 api/tests/CMakeLists.sample create mode 100644 api/tests/CMakeLists.txt create mode 100644 api/tests/h264_media_server.c create mode 100644 api/tests/h264_pusher.c create mode 100644 api/tests/httpclient.c create mode 100644 api/tests/player_opencv.c create mode 100644 api/tests/pusher.c create mode 100644 api/tests/server.c create mode 100644 api/tests/websocket.c create mode 100644 build_docker_images.sh create mode 100755 cmake/AndroidNdkGdb.cmake create mode 100755 cmake/AndroidNdkModules.cmake create mode 100644 cmake/FindAVCODEC.cmake create mode 100644 cmake/FindAVFILTER.cmake create mode 100644 cmake/FindAVFORMAT.cmake create mode 100644 cmake/FindAVUTIL.cmake create mode 100644 cmake/FindFAAC.cmake create mode 100644 cmake/FindJEMALLOC.cmake create mode 100644 cmake/FindMP4V2.cmake create mode 100644 cmake/FindMYSQL.cmake create mode 100644 cmake/FindPCAP.cmake create mode 100644 cmake/FindSCTP.cmake create mode 100644 cmake/FindSDL2.cmake create mode 100644 cmake/FindSRTP.cmake create mode 100644 cmake/FindSWRESAMPLE.cmake create mode 100644 cmake/FindSWSCALE.cmake create mode 100644 cmake/FindTCMALLOC.cmake create mode 100644 cmake/FindX264.cmake create mode 100644 cmake/Jemalloc.cmake create mode 100755 cmake/android.toolchain.cmake create mode 100644 cmake/checks/atomic_check.cpp create mode 100644 cmake/ios.toolchain.cmake create mode 100644 conf/config.ini create mode 100644 conf/novideo.yuv create mode 100644 conf/readme.md create mode 100644 default.pem create mode 100644 docker/build_docker_images.sh create mode 100644 docker/centos7/Dockerfile.runtime create mode 100644 docker/ubuntu16.04/Dockerfile.devel create mode 100644 docker/ubuntu16.04/Dockerfile.runtime create mode 100644 docker/ubuntu18.04/Dockerfile.devel create mode 100644 docker/ubuntu18.04/Dockerfile.runtime create mode 100644 dockerfile create mode 100644 ext-codec/AAC.cpp create mode 100644 ext-codec/AAC.h create mode 100644 ext-codec/AACRtmp.cpp create mode 100644 ext-codec/AACRtmp.h create mode 100644 ext-codec/AACRtp.cpp create mode 100644 ext-codec/AACRtp.h create mode 100644 ext-codec/AV1.cpp create mode 100644 ext-codec/AV1.h create mode 100644 ext-codec/AV1Rtp.cpp create mode 100644 ext-codec/AV1Rtp.h create mode 100644 ext-codec/CMakeLists.txt create mode 100644 ext-codec/G711.cpp create mode 100644 ext-codec/G711.h create mode 100644 ext-codec/G711Rtp.cpp create mode 100644 ext-codec/G711Rtp.h create mode 100644 ext-codec/H264.cpp create mode 100644 ext-codec/H264.h create mode 100644 ext-codec/H264Rtmp.cpp create mode 100644 ext-codec/H264Rtmp.h create mode 100644 ext-codec/H264Rtp.cpp create mode 100644 ext-codec/H264Rtp.h create mode 100644 ext-codec/H265.cpp create mode 100644 ext-codec/H265.h create mode 100644 ext-codec/H265Rtmp.cpp create mode 100644 ext-codec/H265Rtmp.h create mode 100644 ext-codec/H265Rtp.cpp create mode 100644 ext-codec/H265Rtp.h create mode 100644 ext-codec/JPEG.cpp create mode 100644 ext-codec/JPEG.h create mode 100644 ext-codec/JPEGRtp.cpp create mode 100644 ext-codec/JPEGRtp.h create mode 100644 ext-codec/L16.cpp create mode 100644 ext-codec/L16.h create mode 100644 ext-codec/MP3.cpp create mode 100644 ext-codec/MP3.h create mode 100644 ext-codec/MP3Rtp.cpp create mode 100644 ext-codec/MP3Rtp.h create mode 100644 ext-codec/Opus.cpp create mode 100644 ext-codec/Opus.h create mode 100644 ext-codec/OpusRtmp.cpp create mode 100644 ext-codec/OpusRtmp.h create mode 100644 ext-codec/SPSParser.c create mode 100644 ext-codec/SPSParser.h create mode 100644 ext-codec/VP8.cpp create mode 100644 ext-codec/VP8.h create mode 100644 ext-codec/VP8Rtp.cpp create mode 100644 ext-codec/VP8Rtp.h create mode 100644 ext-codec/VP9.cpp create mode 100644 ext-codec/VP9.h create mode 100644 ext-codec/VP9Rtp.cpp create mode 100644 ext-codec/VP9Rtp.h create mode 100644 ext-codec/VpxRtmp.cpp create mode 100644 ext-codec/VpxRtmp.h create mode 100644 golang/.idea/.gitignore create mode 100644 golang/go.sum create mode 100644 golang/tester/mk_common_test.go create mode 100644 golang/zlmediakit/mk_common.go create mode 100644 k8s_readme.md create mode 100644 package/rpm/ZLMediaKit.spec create mode 100644 player/AudioSRC.cpp create mode 100644 player/AudioSRC.h create mode 100644 player/CMakeLists.txt create mode 100644 player/SDLAudioDevice.cpp create mode 100644 player/SDLAudioDevice.h create mode 100644 player/YuvDisplayer.h create mode 100644 player/test_player.cpp create mode 100644 postman/127.0.0.1.postman_environment.json create mode 100644 postman/ZLMediaKit.postman_collection.json create mode 100644 postman/readme.md create mode 100644 resource.rc create mode 100644 server/CMakeLists.txt create mode 100644 server/FFmpegSource.cpp create mode 100644 server/FFmpegSource.h create mode 100644 server/Process.cpp create mode 100644 server/Process.h create mode 100644 server/ShellParser.h create mode 100644 server/System.cpp create mode 100644 server/System.h create mode 100644 server/VideoStack.cpp create mode 100644 server/VideoStack.h create mode 100755 server/WebApi.cpp create mode 100755 server/WebApi.h create mode 100755 server/WebHook.cpp create mode 100755 server/WebHook.h create mode 100644 server/main.cpp create mode 100644 sources.list create mode 100644 src/CMakeLists.txt create mode 100644 src/Codec/AACEncoder.cpp create mode 100644 src/Codec/AACEncoder.h create mode 100644 src/Codec/H264Encoder.cpp create mode 100644 src/Codec/H264Encoder.h create mode 100644 src/Codec/Transcode.cpp create mode 100644 src/Codec/Transcode.h create mode 100644 src/Common/Device.cpp create mode 100644 src/Common/Device.h create mode 100644 src/Common/JemallocUtil.cpp create mode 100644 src/Common/JemallocUtil.h create mode 100644 src/Common/MediaSink.cpp create mode 100644 src/Common/MediaSink.h create mode 100644 src/Common/MediaSource.cpp create mode 100644 src/Common/MediaSource.h create mode 100644 src/Common/MultiMediaSourceMuxer.cpp create mode 100644 src/Common/MultiMediaSourceMuxer.h create mode 100644 src/Common/PacketCache.h create mode 100644 src/Common/Parser.cpp create mode 100644 src/Common/Parser.h create mode 100644 src/Common/Stamp.cpp create mode 100644 src/Common/Stamp.h create mode 100644 src/Common/config.cpp create mode 100644 src/Common/config.h create mode 100644 src/Common/macros.cpp create mode 100644 src/Common/macros.h create mode 100644 src/Common/strCoding.cpp create mode 100644 src/Common/strCoding.h create mode 100644 src/Extension/CommonRtmp.cpp create mode 100644 src/Extension/CommonRtmp.h create mode 100644 src/Extension/CommonRtp.cpp create mode 100644 src/Extension/CommonRtp.h create mode 100644 src/Extension/Factory.cpp create mode 100644 src/Extension/Factory.h create mode 100644 src/Extension/Frame.cpp create mode 100644 src/Extension/Frame.h create mode 100644 src/Extension/Track.cpp create mode 100644 src/Extension/Track.h create mode 100644 src/FMP4/FMP4MediaSource.h create mode 100644 src/FMP4/FMP4MediaSourceMuxer.h create mode 100644 src/Http/HlsParser.cpp create mode 100644 src/Http/HlsParser.h create mode 100644 src/Http/HlsPlayer.cpp create mode 100644 src/Http/HlsPlayer.h create mode 100644 src/Http/HttpBody.cpp create mode 100644 src/Http/HttpBody.h create mode 100644 src/Http/HttpChunkedSplitter.cpp create mode 100644 src/Http/HttpChunkedSplitter.h create mode 100644 src/Http/HttpClient.cpp create mode 100644 src/Http/HttpClient.h create mode 100644 src/Http/HttpClientImp.cpp create mode 100644 src/Http/HttpClientImp.h create mode 100644 src/Http/HttpConst.cpp create mode 100644 src/Http/HttpConst.h create mode 100644 src/Http/HttpCookie.cpp create mode 100644 src/Http/HttpCookie.h create mode 100644 src/Http/HttpCookieManager.cpp create mode 100644 src/Http/HttpCookieManager.h create mode 100644 src/Http/HttpDownloader.cpp create mode 100644 src/Http/HttpDownloader.h create mode 100644 src/Http/HttpFileManager.cpp create mode 100644 src/Http/HttpFileManager.h create mode 100644 src/Http/HttpRequestSplitter.cpp create mode 100644 src/Http/HttpRequestSplitter.h create mode 100644 src/Http/HttpRequester.cpp create mode 100644 src/Http/HttpRequester.h create mode 100644 src/Http/HttpSession.cpp create mode 100644 src/Http/HttpSession.h create mode 100644 src/Http/HttpTSPlayer.cpp create mode 100644 src/Http/HttpTSPlayer.h create mode 100644 src/Http/TsPlayer.cpp create mode 100644 src/Http/TsPlayer.h create mode 100644 src/Http/TsPlayerImp.h create mode 100644 src/Http/TsplayerImp.cpp create mode 100644 src/Http/WebSocketClient.h create mode 100644 src/Http/WebSocketSession.h create mode 100644 src/Http/WebSocketSplitter.cpp create mode 100644 src/Http/WebSocketSplitter.h create mode 100644 src/Onvif/Onvif.cpp create mode 100644 src/Onvif/Onvif.h create mode 100644 src/Onvif/SoapUtil.cpp create mode 100644 src/Onvif/SoapUtil.h create mode 100644 src/Onvif/pugiconfig.hpp create mode 100644 src/Onvif/pugixml.cpp create mode 100644 src/Onvif/pugixml.hpp create mode 100644 src/Player/MediaPlayer.cpp create mode 100644 src/Player/MediaPlayer.h create mode 100644 src/Player/PlayerBase.cpp create mode 100644 src/Player/PlayerBase.h create mode 100644 src/Player/PlayerProxy.cpp create mode 100644 src/Player/PlayerProxy.h create mode 100644 src/Pusher/MediaPusher.cpp create mode 100644 src/Pusher/MediaPusher.h create mode 100644 src/Pusher/PusherBase.cpp create mode 100644 src/Pusher/PusherBase.h create mode 100644 src/Pusher/PusherProxy.cpp create mode 100644 src/Pusher/PusherProxy.h create mode 100644 src/Record/HlsMaker.cpp create mode 100644 src/Record/HlsMaker.h create mode 100644 src/Record/HlsMakerImp.cpp create mode 100644 src/Record/HlsMakerImp.h create mode 100644 src/Record/HlsMediaSource.cpp create mode 100644 src/Record/HlsMediaSource.h create mode 100644 src/Record/HlsRecorder.h create mode 100644 src/Record/MP4.cpp create mode 100644 src/Record/MP4.h create mode 100644 src/Record/MP4Demuxer.cpp create mode 100644 src/Record/MP4Demuxer.h create mode 100644 src/Record/MP4Muxer.cpp create mode 100644 src/Record/MP4Muxer.h create mode 100644 src/Record/MP4Reader.cpp create mode 100644 src/Record/MP4Reader.h create mode 100644 src/Record/MP4Recorder.cpp create mode 100644 src/Record/MP4Recorder.h create mode 100644 src/Record/MPEG.cpp create mode 100644 src/Record/MPEG.h create mode 100644 src/Record/Recorder.cpp create mode 100644 src/Record/Recorder.h create mode 100644 src/Rtcp/Rtcp.cpp create mode 100644 src/Rtcp/Rtcp.h create mode 100644 src/Rtcp/RtcpContext.cpp create mode 100644 src/Rtcp/RtcpContext.h create mode 100644 src/Rtcp/RtcpFCI.cpp create mode 100644 src/Rtcp/RtcpFCI.h create mode 100644 src/Rtmp/FlvMuxer.cpp create mode 100644 src/Rtmp/FlvMuxer.h create mode 100644 src/Rtmp/FlvPlayer.cpp create mode 100644 src/Rtmp/FlvPlayer.h create mode 100644 src/Rtmp/FlvSplitter.cpp create mode 100644 src/Rtmp/FlvSplitter.h create mode 100644 src/Rtmp/Rtmp.cpp create mode 100644 src/Rtmp/Rtmp.h create mode 100644 src/Rtmp/RtmpCodec.h create mode 100644 src/Rtmp/RtmpDemuxer.cpp create mode 100644 src/Rtmp/RtmpDemuxer.h create mode 100644 src/Rtmp/RtmpMediaSource.h create mode 100644 src/Rtmp/RtmpMediaSourceImp.cpp create mode 100644 src/Rtmp/RtmpMediaSourceImp.h create mode 100644 src/Rtmp/RtmpMediaSourceMuxer.h create mode 100644 src/Rtmp/RtmpMuxer.cpp create mode 100644 src/Rtmp/RtmpMuxer.h create mode 100644 src/Rtmp/RtmpPlayer.cpp create mode 100644 src/Rtmp/RtmpPlayer.h create mode 100644 src/Rtmp/RtmpPlayerImp.h create mode 100644 src/Rtmp/RtmpProtocol.cpp create mode 100644 src/Rtmp/RtmpProtocol.h create mode 100644 src/Rtmp/RtmpPusher.cpp create mode 100644 src/Rtmp/RtmpPusher.h create mode 100644 src/Rtmp/RtmpSession.cpp create mode 100644 src/Rtmp/RtmpSession.h create mode 100644 src/Rtmp/amf.cpp create mode 100644 src/Rtmp/amf.h create mode 100644 src/Rtmp/utils.cpp create mode 100644 src/Rtmp/utils.h create mode 100644 src/Rtp/Decoder.cpp create mode 100644 src/Rtp/Decoder.h create mode 100644 src/Rtp/GB28181Process.cpp create mode 100644 src/Rtp/GB28181Process.h create mode 100644 src/Rtp/PSDecoder.cpp create mode 100644 src/Rtp/PSDecoder.h create mode 100644 src/Rtp/PSEncoder.cpp create mode 100644 src/Rtp/PSEncoder.h create mode 100644 src/Rtp/ProcessInterface.h create mode 100644 src/Rtp/RawEncoder.cpp create mode 100644 src/Rtp/RawEncoder.h create mode 100644 src/Rtp/RtpCache.cpp create mode 100644 src/Rtp/RtpCache.h create mode 100644 src/Rtp/RtpProcess.cpp create mode 100644 src/Rtp/RtpProcess.h create mode 100644 src/Rtp/RtpSender.cpp create mode 100644 src/Rtp/RtpSender.h create mode 100644 src/Rtp/RtpServer.cpp create mode 100644 src/Rtp/RtpServer.h create mode 100644 src/Rtp/RtpSession.cpp create mode 100644 src/Rtp/RtpSession.h create mode 100644 src/Rtp/RtpSplitter.cpp create mode 100644 src/Rtp/RtpSplitter.h create mode 100644 src/Rtp/TSDecoder.cpp create mode 100644 src/Rtp/TSDecoder.h create mode 100644 src/Rtsp/RtpCodec.cpp create mode 100644 src/Rtsp/RtpCodec.h create mode 100644 src/Rtsp/RtpMultiCaster.cpp create mode 100644 src/Rtsp/RtpMultiCaster.h create mode 100644 src/Rtsp/RtpReceiver.cpp create mode 100644 src/Rtsp/RtpReceiver.h create mode 100644 src/Rtsp/Rtsp.cpp create mode 100644 src/Rtsp/Rtsp.h create mode 100644 src/Rtsp/RtspDemuxer.cpp create mode 100644 src/Rtsp/RtspDemuxer.h create mode 100644 src/Rtsp/RtspMediaSource.h create mode 100644 src/Rtsp/RtspMediaSourceImp.cpp create mode 100644 src/Rtsp/RtspMediaSourceImp.h create mode 100644 src/Rtsp/RtspMediaSourceMuxer.h create mode 100644 src/Rtsp/RtspMuxer.cpp create mode 100644 src/Rtsp/RtspMuxer.h create mode 100644 src/Rtsp/RtspPlayer.cpp create mode 100644 src/Rtsp/RtspPlayer.h create mode 100644 src/Rtsp/RtspPlayerImp.h create mode 100644 src/Rtsp/RtspPusher.cpp create mode 100644 src/Rtsp/RtspPusher.h create mode 100644 src/Rtsp/RtspSession.cpp create mode 100644 src/Rtsp/RtspSession.h create mode 100644 src/Rtsp/RtspSplitter.cpp create mode 100644 src/Rtsp/RtspSplitter.h create mode 100644 src/Rtsp/UDPServer.cpp create mode 100644 src/Rtsp/UDPServer.h create mode 100644 src/Shell/ShellCMD.h create mode 100644 src/Shell/ShellSession.cpp create mode 100644 src/Shell/ShellSession.h create mode 100644 src/TS/TSMediaSource.h create mode 100644 src/TS/TSMediaSourceMuxer.h create mode 100644 srt/Ack.cpp create mode 100644 srt/Ack.hpp create mode 100644 srt/CMakeLists.txt create mode 100644 srt/Common.hpp create mode 100644 srt/Crypto.cpp create mode 100644 srt/Crypto.hpp create mode 100644 srt/HSExt.cpp create mode 100644 srt/HSExt.hpp create mode 100644 srt/NackContext.cpp create mode 100644 srt/NackContext.hpp create mode 100644 srt/Packet.cpp create mode 100644 srt/Packet.hpp create mode 100644 srt/PacketQueue.cpp create mode 100644 srt/PacketQueue.hpp create mode 100644 srt/PacketSendQueue.cpp create mode 100644 srt/PacketSendQueue.hpp create mode 100644 srt/SrtCaller.cpp create mode 100644 srt/SrtCaller.h create mode 100644 srt/SrtPlayer.cpp create mode 100644 srt/SrtPlayer.h create mode 100644 srt/SrtPlayerImp.h create mode 100644 srt/SrtPusher.cpp create mode 100644 srt/SrtPusher.h create mode 100644 srt/SrtSession.cpp create mode 100644 srt/SrtSession.hpp create mode 100644 srt/SrtTransport.cpp create mode 100644 srt/SrtTransport.hpp create mode 100644 srt/SrtTransportImp.cpp create mode 100644 srt/SrtTransportImp.hpp create mode 100644 srt/Statistic.cpp create mode 100644 srt/Statistic.hpp create mode 100644 srt/srt.md create mode 100644 srt/srt_en.md create mode 100644 tests/CMakeLists.txt create mode 100644 tests/DeviceHK/DeviceHK.cpp create mode 100644 tests/DeviceHK/DeviceHK.h create mode 100644 tests/README.md create mode 100644 tests/bom.cpp create mode 100644 tests/tab.cpp create mode 100644 tests/test_bench_forward.cpp create mode 100644 tests/test_bench_proxy.cpp create mode 100644 tests/test_bench_pull.cpp create mode 100644 tests/test_bench_push.cpp create mode 100644 tests/test_flv.cpp create mode 100644 tests/test_httpApi.cpp create mode 100644 tests/test_httpClient.cpp create mode 100644 tests/test_http_head.cpp create mode 100644 tests/test_ps.cpp create mode 100644 tests/test_pusher.cpp create mode 100644 tests/test_pusherMp4.cpp create mode 100644 tests/test_rtcp_nack.cpp create mode 100644 tests/test_rtp.cpp create mode 100644 tests/test_rtp_pcap.cpp create mode 100644 tests/test_server.cpp create mode 100644 tests/test_sortor.cpp create mode 100644 tests/test_wsClient.cpp create mode 100644 tests/test_wsServer.cpp create mode 100644 tools/openapi/generates.py create mode 100644 tools/openapi/options.json create mode 100644 webrtc/CMakeLists.txt create mode 100644 webrtc/DtlsTransport.cpp create mode 100644 webrtc/DtlsTransport.hpp create mode 100644 webrtc/IceSession.cpp create mode 100644 webrtc/IceSession.hpp create mode 100644 webrtc/IceTransport.cpp create mode 100644 webrtc/IceTransport.hpp create mode 100644 webrtc/Nack.cpp create mode 100644 webrtc/Nack.h create mode 100644 webrtc/RtpExt.cpp create mode 100644 webrtc/RtpExt.h create mode 100644 webrtc/RtpMap.h create mode 100644 webrtc/SctpAssociation.cpp create mode 100644 webrtc/SctpAssociation.hpp create mode 100644 webrtc/Sdp.cpp create mode 100644 webrtc/Sdp.h create mode 100644 webrtc/SrtpSession.cpp create mode 100644 webrtc/SrtpSession.hpp create mode 100644 webrtc/StunPacket.cpp create mode 100644 webrtc/StunPacket.hpp create mode 100644 webrtc/TwccContext.cpp create mode 100644 webrtc/TwccContext.h create mode 100644 webrtc/USAGE.md create mode 100755 webrtc/WebRtcClient.cpp create mode 100755 webrtc/WebRtcClient.h create mode 100644 webrtc/WebRtcEchoTest.cpp create mode 100644 webrtc/WebRtcEchoTest.h create mode 100644 webrtc/WebRtcPlayer.cpp create mode 100644 webrtc/WebRtcPlayer.h create mode 100755 webrtc/WebRtcProxyPlayer.cpp create mode 100755 webrtc/WebRtcProxyPlayer.h create mode 100755 webrtc/WebRtcProxyPlayerImp.h create mode 100755 webrtc/WebRtcProxyPusher.cpp create mode 100755 webrtc/WebRtcProxyPusher.h create mode 100644 webrtc/WebRtcPusher.cpp create mode 100644 webrtc/WebRtcPusher.h create mode 100644 webrtc/WebRtcSession.cpp create mode 100644 webrtc/WebRtcSession.h create mode 100644 webrtc/WebRtcSignalingMsg.cpp create mode 100644 webrtc/WebRtcSignalingMsg.h create mode 100644 webrtc/WebRtcSignalingPeer.cpp create mode 100644 webrtc/WebRtcSignalingPeer.h create mode 100644 webrtc/WebRtcSignalingSession.cpp create mode 100644 webrtc/WebRtcSignalingSession.h create mode 100644 webrtc/WebRtcTalk.cpp create mode 100644 webrtc/WebRtcTalk.h create mode 100644 webrtc/WebRtcTransport.cpp create mode 100644 webrtc/WebRtcTransport.h create mode 100644 webrtc/answer.sdp create mode 100644 webrtc/janus_answer.sdp create mode 100644 webrtc/janus_offer.sdp create mode 100644 webrtc/logger.h create mode 100644 webrtc/offer-simulcast.sdp create mode 100644 webrtc/offer.sdp create mode 100644 webrtc/readme.md create mode 100644 webrtc/webrtcSignal.txt create mode 100644 www/ZLMRTCClient.js create mode 100644 www/ZLMRTCClient.js.map create mode 100644 www/logo.ico create mode 100644 www/logo.png create mode 100644 www/readme/index.html create mode 100644 www/swagger/favicon-16x16.png create mode 100644 www/swagger/favicon-32x32.png create mode 100644 www/swagger/index.css create mode 100644 www/swagger/index.html create mode 100644 www/swagger/oauth2-redirect.html create mode 100644 www/swagger/openapi.json create mode 100644 www/swagger/swagger-initializer.js create mode 100644 www/swagger/swagger-ui-bundle.js create mode 100644 www/swagger/swagger-ui-bundle.js.map create mode 100644 www/swagger/swagger-ui-es-bundle-core.js create mode 100644 www/swagger/swagger-ui-es-bundle-core.js.map create mode 100644 www/swagger/swagger-ui-es-bundle.js create mode 100644 www/swagger/swagger-ui-es-bundle.js.map create mode 100644 www/swagger/swagger-ui-standalone-preset.js create mode 100644 www/swagger/swagger-ui-standalone-preset.js.map create mode 100644 www/swagger/swagger-ui.css create mode 100644 www/swagger/swagger-ui.css.map create mode 100644 www/swagger/swagger-ui.js create mode 100644 www/swagger/swagger-ui.js.map create mode 100644 www/webassist/LICENSE create mode 100644 www/webassist/README.md create mode 100644 www/webassist/README_EN.md create mode 100644 www/webassist/assets/elementui/fonts/element-icons.woff create mode 100644 www/webassist/assets/elementui/index.css create mode 100644 www/webassist/assets/elementui/index.js create mode 100644 www/webassist/assets/favicon.ico create mode 100644 www/webassist/assets/logo.png create mode 100644 www/webassist/index.html create mode 100644 www/webassist/js/ZLMRTCClient.js create mode 100644 www/webassist/js/ZLMRTCClient.js.map create mode 100644 www/webassist/js/axios.min.js create mode 100644 www/webassist/js/echarts.min.js create mode 100644 www/webassist/js/qs.js create mode 100644 www/webassist/js/utils.js create mode 100644 www/webassist/js/vue.min.js create mode 100644 www/webrtc/ZLMRTCClient.js create mode 100644 www/webrtc/ZLMRTCClient.js.map create mode 100644 www/webrtc/index.html create mode 100644 www/webrtc/readme.txt diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..e7b8a73 --- /dev/null +++ b/.clang-format @@ -0,0 +1,89 @@ +# This is for clang-format >= 9.0. +# +# clang-format --version +# clang-format version 9.0.1 (Red Hat 9.0.1-2.module+el8.2.0+5494+7b8075cf) +# +# 详细说明见: https://clang.llvm.org/docs/ClangFormatStyleOptions.html +# 部分参数会随版本变化. +--- +Language: Cpp +# 基于 WebKit 的风格, https://www.webkit.org/coding/coding-style.html +BasedOnStyle: WebKit + +# 以下各选项按字母排序 + +# public/protected/private 不缩进 +AccessModifierOffset: -4 +# 参数过长时统一换行 +AlignAfterOpenBracket: AlwaysBreak +# clang-format >= 13 required, map 之类的内部列对齐 +# AlignArrayOfStructures: Left +# 换行符统一在 ColumnLimit 最右侧 +AlignEscapedNewlines: Right +# 不允许短代码块单行, 即不允许单行代码: if (x) return; +AllowShortBlocksOnASingleLine: false +# 只允许 Inline 函数单行 +AllowShortFunctionsOnASingleLine: Inline +# 模板声明换行 +AlwaysBreakTemplateDeclarations: Yes +# 左开括号不换行 +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + # BraceWrappingAfterControlStatementStyle: MultiLine + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +# 构造函数初始化时在 `,` 前换行, 和 `:` 对齐显得整齐 +BreakConstructorInitializers: BeforeComma +# 继承过长需要换行时也在 `,` 前 +BreakInheritanceList: BeforeComma +# 列宽 160 +ColumnLimit: 160 +# c++11 括号内起始/结束无空格, false 会加上 +Cpp11BracedListStyle: false +# 命名空间后的注释会修正为: // namespace_name +FixNamespaceComments: true + +#switch case的缩进 +IndentCaseLabels: true +#允许单行case +AllowShortCaseLabelsOnASingleLine: true + +# clang-format >= 13 required, lambda 函数内部缩进级别和外部一致, 默认会增加一级缩进 +# LambdaBodyIndentation: OuterScope +# 命名空间不缩进 +NamespaceIndentation: None +# PPIndentWidth: 2 +# */& 靠近变量, 向右靠 +PointerAlignment: Right +# c++11 使用 {} 构造时和变量加个空格 +SpaceBeforeCpp11BracedList: true +# 继承时 `:` 前加空格 +SpaceBeforeInheritanceColon: true +# () 前不加空格, do/for/if/switch/while 除外 +SpaceBeforeParens: ControlStatements +# 空 {} 中不加空格 +SpaceInEmptyBlock: false +Standard: C++11 +# Tab 占 4 位 +TabWidth: 4 +# 不使用 TAB +UseTab: Never +--- +Language: Java +--- +Language: JavaScript +... diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..b7f1a8b --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +*.h linguist-language=cpp +*.c linguist-language=cpp diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md new file mode 100644 index 0000000..6232151 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -0,0 +1,95 @@ +--- +name: bug 反馈 +about: 反馈 ZLMediaKit 代码本身的 bug +title: "[BUG] BUG现象描述(必填)" +labels: bug +assignees: '' + +--- + + + + + +## 现象描述 + + + +## 如何复现? + + + +## 相关日志或截图 + + + +
+展开查看详细日志 +
+
+```
+#详细日志粘在这里!
+```
+
+
+ +## 配置 + + + +
+展开查看详细配置 +
+
+```ini
+#config.ini内容粘在这里!
+```
+
+
+ +## 各种环境信息 + + + +* **代码提交记录/git commit hash**: +* **操作系统及版本**: +* **硬件信息**: +* **crash backtrace**: +``` +#崩溃信息backtrace粘贴至此 +``` +* **其他需要补充的信息**: diff --git a/.github/ISSUE_TEMPLATE/compile.md b/.github/ISSUE_TEMPLATE/compile.md new file mode 100644 index 0000000..611febf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/compile.md @@ -0,0 +1,57 @@ +--- +name: 编译问题反馈 +about: 反馈 ZLMediaKit 编译相关的问题 +title: "[编译问题] 编译问题描述(必填)" +labels: 编译问题 +assignees: '' + +--- + + + + + +## 相关日志及环境信息 + + + +**清除编译缓存后,完整执行 cmake && make 命令的输出** + +
+展开查看详细编译日志 +
+
+```
+详细日志粘在这里!
+```
+
+
+
+ +编译目录下的 `CMakeCache.txt` 文件内容,请直接上传为附件。 + +## 各种环境信息 + + + +* **代码提交记录/git commit hash**: +* **操作系统及版本**: +* **硬件信息**: +* **其他需要补充的信息**: diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..9dc6738 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,6 @@ +blank_issues_enabled: false + +contact_links: + - name: 技术咨询 + url: https://t.zsxq.com/FcVK5 + about: 请在知识星球发起技术咨询 diff --git a/.github/ISSUE_TEMPLATE/feature.md b/.github/ISSUE_TEMPLATE/feature.md new file mode 100644 index 0000000..348e1ea --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.md @@ -0,0 +1,14 @@ +--- +name: 新增功能请求 +about: 请求新增某些新功能或新特性,或者对已有功能的改进 +title: "[功能请求] 需求描述(必填)" +labels: 意见建议 +assignees: '' + +--- + +## 描述该功能的用处,可以提供相关资料描述该功能 + +## 该功能是否用于改进项目缺陷,如果是,请描述现有缺陷 + +## 描述你期望实现该功能的方式和最终效果 diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 0000000..2a0cdc8 --- /dev/null +++ b/.github/workflows/android.yml @@ -0,0 +1,59 @@ +name: Android +on: [push, pull_request] +jobs: + build: + runs-on: ubuntu-24.04 + steps: + + - name: 下载源码 + uses: actions/checkout@v1 + + - name: 配置JDK + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'temurin' + cache: gradle + + - name: 下载submodule源码 + run: mv -f .gitmodules_github .gitmodules && git submodule sync && git submodule update --init + + - name: 赋予gradlew文件可执行权限 + run: chmod +x ./Android/gradlew + + - name: 编译 + run: cd Android && ./gradlew build + + - name: 设置环境变量 + run: | + echo "BRANCH=$(echo ${GITHUB_REF#refs/heads/} | tr -s "/\?%*:|\"<>" "_")" >> $GITHUB_ENV + echo "BRANCH2=$(echo ${GITHUB_REF#refs/heads/} )" >> $GITHUB_ENV + echo "DATE=$(date +%Y-%m-%d)" >> $GITHUB_ENV + + - name: 打包二进制 + id: upload + uses: actions/upload-artifact@v4 + with: + name: ${{ github.workflow }}_${{ env.BRANCH }}_${{ env.DATE }} + path: Android/app/build/outputs/apk/debug/* + if-no-files-found: error + retention-days: 90 + + - name: issue评论 + if: github.event_name != 'pull_request' && github.ref != 'refs/heads/feature/test' + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.rest.issues.createComment({ + issue_number: ${{vars.VERSION_ISSUE_NO}}, + owner: context.repo.owner, + repo: context.repo.repo, + body: '- 下载地址: [${{ github.workflow }}_${{ env.BRANCH }}_${{ env.DATE }}](${{ steps.upload.outputs.artifact-url }})\n' + + '- 分支: ${{ env.BRANCH2 }}\n' + + '- git hash: ${{ github.sha }} \n' + + '- 编译日期: ${{ env.DATE }}\n' + + '- 编译记录: [${{ github.run_id }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})\n' + + '- 开启特性: 未开启openssl/webrtc/datachannel等功能\n' + + '- 打包ci名: ${{ github.workflow }}\n' + }) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..f6bd279 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,62 @@ +name: CodeQL + +on: [push, pull_request] + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-24.04 + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'cpp', 'javascript' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - uses: actions/checkout@v1 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + - name: 下载submodule源码 + run: mv -f .gitmodules_github .gitmodules && git submodule sync && git submodule update --init + + - name: apt-get安装依赖库(非必选) + run: sudo apt-get update && sudo apt-get install -y cmake libssl-dev libsdl-dev libavcodec-dev libavutil-dev libswscale-dev libresample-dev + + - name: 下载 SRTP + uses: actions/checkout@v2 + with: + repository: cisco/libsrtp + fetch-depth: 1 + ref: v2.7.0 + path: 3rdpart/libsrtp + + - name: 编译 SRTP + run: cd 3rdpart/libsrtp && ./configure --enable-openssl && make -j4 && sudo make install + + - name: 编译 + run: mkdir -p linux_build && cd linux_build && cmake .. -DENABLE_WEBRTC=true -DENABLE_FFMPEG=true && make -j $(nproc) + + - name: 运行MediaServer + run: pwd && cd release/linux/Debug && sudo ./MediaServer -d & + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + + + diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..e13fd5f --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,88 @@ +name: Docker + +on: + push: + branches: + - "master" + - "feature/*" + - "release/*" + +env: + # Use docker.io for Docker Hub if empty + REGISTRY: docker.io + IMAGE_NAME: zlmediakit/zlmediakit + +jobs: + build: + + runs-on: ubuntu-24.04 + permissions: + contents: read + packages: write + # This is used to complete the identity challenge + # with sigstore/fulcio when running outside of PRs. + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: 下载submodule源码 + run: mv -f .gitmodules_github .gitmodules && git submodule sync && git submodule update --init + + # Install the cosign tool except on PR + # https://github.com/sigstore/cosign-installer + - name: Install cosign + uses: sigstore/cosign-installer@d572c9c13673d2e0a26fabf90b5748f36886883f + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + + # Workaround: https://github.com/docker/build-push-action/issues/461 + - name: Setup Docker buildx + uses: docker/setup-buildx-action@79abd3f86f79a9d68a23c75a09a9a85889262adf + + # Login against a Docker registry except on PR + # https://github.com/docker/login-action + - name: Log into registry ${{ env.REGISTRY }} + uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c + with: + registry: ${{ env.REGISTRY }} + username: zlmediakit + password: ${{ secrets.DOCKER_IO_SECRET }} + + # Extract metadata (tags, labels) for Docker + # https://github.com/docker/metadata-action + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + # Build and push Docker image with Buildx (don't push on PR) + # https://github.com/docker/build-push-action + - name: Build and push Docker image + if: github.event_name != 'pull_request' && github.ref != 'refs/heads/feature/test' + id: build-and-push + uses: docker/build-push-action@ac9327eae2b366085ac7f6a2d02df8aa8ead720a + with: + context: . + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: MODEL=Release + platforms: linux/amd64,linux/arm64 + + # Sign the resulting Docker image digest except on PRs. + # This will only write to the public Rekor transparency log when the Docker + # repository is public to avoid leaking data. If you would like to publish + # transparency data even for private images, pass --force to cosign below. + # https://github.com/sigstore/cosign +# - name: Sign the published Docker image +# if: ${{ github.event_name != 'pull_request' }} +# env: +# COSIGN_EXPERIMENTAL: "true" +# # This step uses the identity token to provision an ephemeral certificate +# # against the sigstore community Fulcio instance. +# run: cosign sign ${{ steps.meta.outputs.tags }}@${{ steps.build-and-push.outputs.digest }} diff --git a/.github/workflows/issue_lint.yml b/.github/workflows/issue_lint.yml new file mode 100644 index 0000000..b4e4e56 --- /dev/null +++ b/.github/workflows/issue_lint.yml @@ -0,0 +1,58 @@ +name: issue_lint + +on: + issues: + types: [opened] + +jobs: + issue_lint: + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v3 + + - uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const fs = require('fs').promises; + + const getTitles = (str) => ( + [...str.matchAll(/^## (.*)/gm)].map((m) => m[0]) + ); + + const titles = getTitles(context.payload.issue.body); + + for (let file of await fs.readdir('.github/ISSUE_TEMPLATE')) { + if (!file.endsWith('.md')) { + continue; + } + + const template = await fs.readFile(`.github/ISSUE_TEMPLATE/${file}`, 'utf-8'); + const templateTitles = getTitles(template); + + if (templateTitles.every((title) => titles.includes(title))) { + process.exit(0); + } + } + + await github.rest.issues.createComment({ + owner: context.issue.owner, + repo: context.issue.repo, + issue_number: context.issue.number, + body: '此issue由于不符合模板规范已经自动关闭,请重新按照模板规范确保包含模板中所有章节标题再提交\n', + }); + + await github.rest.issues.addLabels({ + owner: context.issue.owner, + repo: context.issue.repo, + issue_number: context.issue.number, + labels: ['自动关闭'] + }); + + await github.rest.issues.update({ + owner: context.issue.owner, + repo: context.issue.repo, + issue_number: context.issue.number, + state: 'closed', + }); \ No newline at end of file diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml new file mode 100644 index 0000000..70bcd1a --- /dev/null +++ b/.github/workflows/linux.yml @@ -0,0 +1,135 @@ +name: Linux + +on: [push, pull_request] + +jobs: + build: + + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v1 + + - name: 下载submodule源码 + run: mv -f .gitmodules_github .gitmodules && git submodule sync && git submodule update --init + + - name: 下载 SRTP + uses: actions/checkout@v2 + with: + repository: cisco/libsrtp + fetch-depth: 1 + ref: v2.3.0 + path: 3rdpart/libsrtp + + - name: 下载 openssl + uses: actions/checkout@v2 + with: + repository: openssl/openssl + fetch-depth: 1 + ref: OpenSSL_1_1_1 + path: 3rdpart/openssl + + - name: 下载 usrsctp + uses: actions/checkout@v2 + with: + repository: sctplab/usrsctp + fetch-depth: 1 + ref: 0.9.5.0 + path: 3rdpart/usrsctp + + - name: 启动 Docker 容器, 在Docker 容器中执行脚本 + run: | + docker pull centos:7 + docker run -v $(pwd):/root -w /root --rm centos:7 sh -c " + #!/bin/bash + set -x + + # Backup original CentOS-Base.repo file + cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup + + # Define new repository configuration + cat < /etc/yum.repos.d/CentOS-Base.repo + [base] + name=CentOS-7 - Base - mirrors.aliyun.com + baseurl=http://mirrors.aliyun.com/centos/7/os/x86_64/ + gpgcheck=1 + gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7 + + [updates] + name=CentOS-7 - Updates - mirrors.aliyun.com + baseurl=http://mirrors.aliyun.com/centos/7/updates/x86_64/ + gpgcheck=1 + gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7 + EOF + + # Clean yum cache and recreate it + yum clean all + yum makecache + + echo \"CentOS 7 软件源已成功切换\" + yum install -y git wget gcc gcc-c++ make + + mkdir -p /root/install + + cd 3rdpart/openssl + ./config no-shared --prefix=/root/install + make -j $(nproc) + make install + cd ../../ + + wget https://github.com/Kitware/CMake/releases/download/v3.29.5/cmake-3.29.5.tar.gz + tar -xf cmake-3.29.5.tar.gz + cd cmake-3.29.5 + OPENSSL_ROOT_DIR=/root/install ./configure + make -j $(nproc) + make install + cd .. + + cd 3rdpart/usrsctp + mkdir build + cd build + cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON .. + make -j $(nproc) + make install + cd ../../../ + + cd 3rdpart/libsrtp && ./configure --enable-openssl --with-openssl-dir=/root/install && make -j $(nproc) && make install + cd ../../ + + mkdir -p linux_build && cd linux_build && cmake .. -DOPENSSL_ROOT_DIR=/root/install -DCMAKE_BUILD_TYPE=Release && make -j $(nproc) + " + + - name: 设置环境变量 + run: | + echo "BRANCH=$(echo ${GITHUB_REF#refs/heads/} | tr -s "/\?%*:|\"<>" "_")" >> $GITHUB_ENV + echo "BRANCH2=$(echo ${GITHUB_REF#refs/heads/} )" >> $GITHUB_ENV + echo "DATE=$(date +%Y-%m-%d)" >> $GITHUB_ENV + + - name: 打包二进制 + id: upload + uses: actions/upload-artifact@v4 + with: + name: ${{ github.workflow }}_${{ env.BRANCH }}_${{ env.DATE }} + path: release/* + if-no-files-found: error + retention-days: 90 + + - name: issue评论 + if: github.event_name != 'pull_request' && github.ref != 'refs/heads/feature/test' + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.rest.issues.createComment({ + issue_number: ${{vars.VERSION_ISSUE_NO}}, + owner: context.repo.owner, + repo: context.repo.repo, + body: '- 下载地址: [${{ github.workflow }}_${{ env.BRANCH }}_${{ env.DATE }}](${{ steps.upload.outputs.artifact-url }})\n' + + '- 分支: ${{ env.BRANCH2 }}\n' + + '- git hash: ${{ github.sha }} \n' + + '- 编译日期: ${{ env.DATE }}\n' + + '- 编译记录: [${{ github.run_id }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})\n' + + '- 打包ci名: ${{ github.workflow }}\n' + + '- 开启特性: openssl/webrtc/datachannel\n' + + '- 说明: 本二进制在centos7(x64)上编译,请确保您的机器系统不低于此版本\n' + }) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 0000000..2cd38dd --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,71 @@ +name: macOS + +on: [push, pull_request] + +jobs: + build: + + runs-on: macOS-latest + + steps: + - uses: actions/checkout@v1 + + - name: 下载submodule源码 + run: mv -f .gitmodules_github .gitmodules && git submodule sync && git submodule update --init + + - name: 配置 vcpkg + uses: lukka/run-vcpkg@v7 + with: + vcpkgDirectory: '${{github.workspace}}/vcpkg' + vcpkgTriplet: arm64-osx + # 2025.07.11 + vcpkgGitCommitId: 'efcfaaf60d7ec57a159fc3110403d939bfb69729' + vcpkgArguments: 'openssl libsrtp[openssl] usrsctp' + + - name: 安装指定 CMake + uses: jwlawson/actions-setup-cmake@v2 + with: + cmake-version: '3.30.5' + + - name: 编译 + uses: lukka/run-cmake@v3 + with: + useVcpkgToolchainFile: true + buildDirectory: '${{github.workspace}}/build' + cmakeAppendedArgs: '' + cmakeBuildType: 'Release' + + - name: 设置环境变量 + run: | + echo "BRANCH=$(echo ${GITHUB_REF#refs/heads/} | tr -s "/\?%*:|\"<>" "_")" >> $GITHUB_ENV + echo "BRANCH2=$(echo ${GITHUB_REF#refs/heads/} )" >> $GITHUB_ENV + echo "DATE=$(date +%Y-%m-%d)" >> $GITHUB_ENV + + - name: 打包二进制 + id: upload + uses: actions/upload-artifact@v4 + with: + name: ${{ github.workflow }}_${{ env.BRANCH }}_${{ env.DATE }} + path: release/* + if-no-files-found: error + retention-days: 90 + + - name: issue评论 + if: github.event_name != 'pull_request' && github.ref != 'refs/heads/feature/test' + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.rest.issues.createComment({ + issue_number: ${{vars.VERSION_ISSUE_NO}}, + owner: context.repo.owner, + repo: context.repo.repo, + body: '- 下载地址: [${{ github.workflow }}_${{ env.BRANCH }}_${{ env.DATE }}](${{ steps.upload.outputs.artifact-url }})\n' + + '- 分支: ${{ env.BRANCH2 }}\n' + + '- git hash: ${{ github.sha }} \n' + + '- 编译日期: ${{ env.DATE }}\n' + + '- 编译记录: [${{ github.run_id }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})\n' + + '- 打包ci名: ${{ github.workflow }}\n' + + '- 开启特性: openssl/webrtc/datachannel\n' + + '- 说明: 此二进制为arm64版本\n' + }) \ No newline at end of file diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml new file mode 100644 index 0000000..dc85e91 --- /dev/null +++ b/.github/workflows/style.yml @@ -0,0 +1,27 @@ +name: style check + +on: [pull_request] + +jobs: + check: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v2 + with: + # with all history + fetch-depth: 0 + - name: Validate BOM + run: | + ret=0 + for i in $(git diff --name-only origin/${GITHUB_BASE_REF}...${GITHUB_SHA}); do + if [ -f ${i} ]; then + case ${i} in + *.c|*.cc|*.cpp|*.h) + if file ${i} | grep -qv BOM; then + echo "Missing BOM in ${i}" && ret=1; + fi + ;; + esac + fi + done + exit ${ret} diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 0000000..8061fa1 --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,68 @@ +name: Windows + +on: [push, pull_request] + +jobs: + build: + runs-on: windows-2022 + + steps: + - uses: actions/checkout@v1 + + - name: 下载submodule源码 + run: mv -Force .gitmodules_github .gitmodules && git submodule sync && git submodule update --init + + - name: 配置 vcpkg + uses: lukka/run-vcpkg@v7 + with: + vcpkgDirectory: '${{github.workspace}}/vcpkg' + vcpkgTriplet: x64-windows-static + # 2025.07.11 + vcpkgGitCommitId: 'efcfaaf60d7ec57a159fc3110403d939bfb69729' + vcpkgArguments: 'openssl libsrtp[openssl] usrsctp' + + - name: 编译 + uses: lukka/run-cmake@v3 + with: + useVcpkgToolchainFile: true + buildDirectory: '${{github.workspace}}/build' + cmakeAppendedArgs: '' + cmakeBuildType: 'Release' + + - name: 设置环境变量 + run: | + $dateString = Get-Date -Format "yyyy-MM-dd" + $branch = $env:GITHUB_REF -replace "refs/heads/", "" -replace "[\\/\\\?\%\*:\|\x22<>]", "_" + $branch2 = $env:GITHUB_REF -replace "refs/heads/", "" + echo "BRANCH=$branch" >> $env:GITHUB_ENV + echo "BRANCH2=$branch2" >> $env:GITHUB_ENV + echo "DATE=$dateString" >> $env:GITHUB_ENV + + - name: 打包二进制 + id: upload + uses: actions/upload-artifact@v4 + with: + name: ${{ github.workflow }}_${{ env.BRANCH }}_${{ env.DATE }} + path: release/* + if-no-files-found: error + retention-days: 90 + + - name: issue评论 + if: github.event_name != 'pull_request' && github.ref != 'refs/heads/feature/test' + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.rest.issues.createComment({ + issue_number: ${{vars.VERSION_ISSUE_NO}}, + owner: context.repo.owner, + repo: context.repo.repo, + body: '- 下载地址: [${{ github.workflow }}_${{ env.BRANCH }}_${{ env.DATE }}](${{ steps.upload.outputs.artifact-url }})\n' + + '- 分支: ${{ env.BRANCH2 }}\n' + + '- git hash: ${{ github.sha }} \n' + + '- 编译日期: ${{ env.DATE }}\n' + + '- 编译记录: [${{ github.run_id }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})\n' + + '- 打包ci名: ${{ github.workflow }}\n' + + '- 开启特性: openssl/webrtc/datachannel\n' + + '- 说明: 此二进制为x64版本\n' + }) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..952d632 --- /dev/null +++ b/.gitignore @@ -0,0 +1,51 @@ +# Compiled Object files +*.slo +*.lo +*.o +*.obj +*.d + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +#*.dylib +#*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.lib + +# Executables +#*.exe +*.out +*.app +/X64/ + + +*.DS_Store + +/cmake-build-debug/ +/cmake-build-release/ +/linux/ +/.vs/ +/.vscode/ +/.idea/ +/c_wrapper/.idea/ +/release/ +/out/ +/Android/.idea/ +/Android/app/src/main/cpp/libs_export/ +/3rdpart/media-server/.idea/ +/3rdpart/media-server/.idea/ +/build/ +/3rdpart/media-server/.idea/ +/ios/ +/cmake-build-* +/3rdpart/ZLToolKit/cmake-build-mq/ diff --git a/.gitmodules_github b/.gitmodules_github new file mode 100644 index 0000000..87b576e --- /dev/null +++ b/.gitmodules_github @@ -0,0 +1,12 @@ +[submodule "ZLToolKit"] + path = 3rdpart/ZLToolKit + url = https://github.com/ZLMediaKit/ZLToolKit +[submodule "3rdpart/media-server"] + path = 3rdpart/media-server + url = https://github.com/ireader/media-server +[submodule "3rdpart/jsoncpp"] + path = 3rdpart/jsoncpp + url = https://github.com/open-source-parsers/jsoncpp.git +[submodule "www/webassist"] + path = www/webassist + url = https://github.com/1002victor/zlm_webassist \ No newline at end of file diff --git a/3rdpart/CMakeLists.txt b/3rdpart/CMakeLists.txt new file mode 100644 index 0000000..845984f --- /dev/null +++ b/3rdpart/CMakeLists.txt @@ -0,0 +1,123 @@ +# MIT License +# +# Copyright (c) 2016-2022 The ZLMediaKit project authors. All Rights Reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# + +############################################################################## + +# jsoncpp +file(GLOB JSONCPP_SRC_LIST + ${CMAKE_CURRENT_SOURCE_DIR}/jsoncpp/include/json/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/jsoncpp/src/lib_json/*.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/jsoncpp/src/lib_json/*.h) + +add_library(jsoncpp STATIC ${JSONCPP_SRC_LIST}) +target_compile_options(jsoncpp + PRIVATE ${COMPILE_OPTIONS_DEFAULT}) +target_include_directories(jsoncpp + PRIVATE + "$/jsoncpp/include" + PUBLIC + "$/jsoncpp/include") + +update_cached_list(MK_LINK_LIBRARIES jsoncpp) + +############################################################################## + +# media-server +set(MediaServer_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/media-server") +# TODO: 补一个函数处理各种库 + +# 添加 mov、flv 库用于 MP4 录制 +if (ENABLE_MP4) + # MOV + set(MediaServer_MOV_ROOT ${MediaServer_ROOT}/libmov) + aux_source_directory(${MediaServer_MOV_ROOT}/include MOV_SRC_LIST) + aux_source_directory(${MediaServer_MOV_ROOT}/source MOV_SRC_LIST) + add_library(mov STATIC ${MOV_SRC_LIST}) + add_library(MediaServer::mov ALIAS mov) + target_compile_options(mov PRIVATE ${COMPILE_OPTIONS_DEFAULT}) + target_include_directories(mov + PRIVATE + "$" + PUBLIC + "$") + + # FLV + set(MediaServer_FLV_ROOT ${MediaServer_ROOT}/libflv) + aux_source_directory(${MediaServer_FLV_ROOT}/include FLV_SRC_LIST) + aux_source_directory(${MediaServer_FLV_ROOT}/source FLV_SRC_LIST) + add_library(flv STATIC ${FLV_SRC_LIST}) + add_library(MediaServer::flv ALIAS flv) + target_compile_options(flv PRIVATE ${COMPILE_OPTIONS_DEFAULT}) + target_include_directories(flv + PRIVATE + "$" + PUBLIC + "$") + + update_cached_list(MK_LINK_LIBRARIES MediaServer::flv MediaServer::mov) + + if (ENABLE_MP4) + message(STATUS "ENABLE_MP4 defined") + update_cached_list(MK_COMPILE_DEFINITIONS ENABLE_MP4) + endif () +endif () + +# 添加 mpeg 用于支持 ts 生成 +if(ENABLE_RTPPROXY OR ENABLE_HLS) + # mpeg + set(MediaServer_MPEG_ROOT ${MediaServer_ROOT}/libmpeg) + aux_source_directory(${MediaServer_MPEG_ROOT}/include MPEG_SRC_LIST) + aux_source_directory(${MediaServer_MPEG_ROOT}/source MPEG_SRC_LIST) + add_library(mpeg STATIC ${MPEG_SRC_LIST}) + add_library(MediaServer::mpeg ALIAS mpeg) + # media-server库相关编译宏 + # MPEG_H26X_VERIFY - 视频流类型识别 + # MPEG_ZERO_PAYLOAD_LENGTH - 兼容hik流 + # MPEG_DAHUA_AAC_FROM_G711 - 兼容dahua流 + target_compile_options(mpeg + PRIVATE ${COMPILE_OPTIONS_DEFAULT} -DMPEG_H26X_VERIFY -DMPEG_ZERO_PAYLOAD_LENGTH -DMPEG_DAHUA_AAC_FROM_G711) + target_include_directories(mpeg + PRIVATE + "$" + PUBLIC + "$") + + update_cached_list(MK_LINK_LIBRARIES MediaServer::mpeg) + if(ENABLE_RTPPROXY) + message(STATUS "ENABLE_RTPPROXY defined") + update_cached_list(MK_COMPILE_DEFINITIONS ENABLE_RTPPROXY) + endif() + if(ENABLE_HLS) + message(STATUS "ENABLE_HLS defined") + update_cached_list(MK_COMPILE_DEFINITIONS ENABLE_HLS) + endif() +endif() + +############################################################################## + +# toolkit +add_subdirectory(ZLToolKit) +# 添加库别名 +add_library(ZLMediaKit::ToolKit ALIAS ZLToolKit) +# 添加依赖 +update_cached_list(MK_LINK_LIBRARIES ZLMediaKit::ToolKit) \ No newline at end of file diff --git a/3rdpart/ZLToolKit/.clang-format b/3rdpart/ZLToolKit/.clang-format new file mode 100644 index 0000000..7ae36ce --- /dev/null +++ b/3rdpart/ZLToolKit/.clang-format @@ -0,0 +1,89 @@ +# This is for clang-format >= 9.0. +# +# clang-format --version +# clang-format version 9.0.1 (Red Hat 9.0.1-2.module+el8.2.0+5494+7b8075cf) +# +# 详细说明见: https://clang.llvm.org/docs/ClangFormatStyleOptions.html +# 部分参数会随版本变化. +--- +Language: Cpp +# 基于 WebKit 的风格, https://www.webkit.org/coding/coding-style.html +BasedOnStyle: WebKit + +# 以下各选项按字母排序 + +# public/protected/private 不缩进 +AccessModifierOffset: -4 +# 参数过长时统一换行 +AlignAfterOpenBracket: AlwaysBreak +# clang-format >= 13 required, map 之类的内部列对齐 +# AlignArrayOfStructures: Left +# 换行符统一在 ColumnLimit 最右侧 +AlignEscapedNewlines: Right +# 不允许短代码块单行, 即不允许单行代码: if (x) return; +AllowShortBlocksOnASingleLine: false +# 只允许 Inline 函数单行 +AllowShortFunctionsOnASingleLine: Inline +# 模板声明换行 +AlwaysBreakTemplateDeclarations: Yes +# 左开括号不换行 +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + # BraceWrappingAfterControlStatementStyle: MultiLine + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +# 构造函数初始化时在 `,` 前换行, 和 `:` 对齐显得整齐 +BreakConstructorInitializers: BeforeComma +# 继承过长需要换行时也在 `,` 前 +BreakInheritanceList: BeforeComma +# 列宽 160 +ColumnLimit: 160 +# c++11 括号内起始/结束无空格, false 会加上 +Cpp11BracedListStyle: false +# 命名空间后的注释会修正为: // namespace_name +FixNamespaceComments: true + +#switch case的缩进 +IndentCaseLabels: true +#允许单行case +AllowShortCaseLabelsOnASingleLine: true + +# clang-format >= 13 required, lambda 函数内部缩进级别和外部一致, 默认会增加一级缩进 +# LambdaBodyIndentation: OuterScope +# 命名空间不缩进 +NamespaceIndentation: None +# PPIndentWidth: 2 +# */& 靠近变量, 向右靠 +PointerAlignment: Right +# c++11 使用 {} 构造时和变量加个空格 +SpaceBeforeCpp11BracedList: true +# 继承时 `:` 前加空格 +SpaceBeforeInheritanceColon: true +# () 前不加空格, do/for/if/switch/while 除外 +SpaceBeforeParens: ControlStatements +# 空 {} 中不加空格 +SpaceInEmptyBlock: false +Standard: C++11 +# Tab 占 4 位 +TabWidth: 4 +# 不使用 TAB +UseTab: Never +--- +Language: Java +--- +Language: JavaScript +... diff --git a/3rdpart/ZLToolKit/.github/workflows/linux.yml b/3rdpart/ZLToolKit/.github/workflows/linux.yml new file mode 100644 index 0000000..cd53d04 --- /dev/null +++ b/3rdpart/ZLToolKit/.github/workflows/linux.yml @@ -0,0 +1,46 @@ +name: Linux + +on: [push, pull_request] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + # The CMake configure and build commands are platform agnostic and should work equally + # well on Windows or Mac. You can convert this to a matrix build if you need + # cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Create Build Environment + # Some projects don't allow in-source building, so create a separate build directory + # We'll use this as our working directory for all subsequent commands + run: cmake -E make_directory ${{github.workspace}}/build + + - name: Configure CMake + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + shell: bash + working-directory: ${{github.workspace}}/build + # Note the current convention is to use the -S and -B options here to specify source + # and build directories, but this is only available with CMake 3.13 and higher. + # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE + + - name: Build + working-directory: ${{github.workspace}}/build + shell: bash + # Execute the build. You can specify a specific target with "--target " + run: cmake --build . --config $BUILD_TYPE -j $(nproc) + + - name: Test + working-directory: ${{github.workspace}}/build + shell: bash + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest -C $BUILD_TYPE diff --git a/3rdpart/ZLToolKit/.github/workflows/macos.yml b/3rdpart/ZLToolKit/.github/workflows/macos.yml new file mode 100644 index 0000000..1feb8ca --- /dev/null +++ b/3rdpart/ZLToolKit/.github/workflows/macos.yml @@ -0,0 +1,40 @@ +name: MacOS + +on: [push, pull_request] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + # The CMake configure and build commands are platform agnostic and should work equally + # well on Windows or Mac. You can convert this to a matrix build if you need + # cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: macOS-latest + + steps: + - uses: actions/checkout@v2 + + - name: 配置 vcpkg + uses: lukka/run-vcpkg@v7 + with: + vcpkgDirectory: '${{github.workspace}}/vcpkg' + vcpkgTriplet: arm64-osx + # 2025.07.11 + vcpkgGitCommitId: 'efcfaaf60d7ec57a159fc3110403d939bfb69729' + vcpkgArguments: 'openssl' + + - name: 安装指定 CMake + uses: jwlawson/actions-setup-cmake@v2 + with: + cmake-version: '3.30.5' + + - name: 编译 + uses: lukka/run-cmake@v3 + with: + useVcpkgToolchainFile: true + buildDirectory: '${{github.workspace}}/build' + cmakeAppendedArgs: '' + cmakeBuildType: 'RelWithDebInfo' diff --git a/3rdpart/ZLToolKit/.github/workflows/style.yml b/3rdpart/ZLToolKit/.github/workflows/style.yml new file mode 100644 index 0000000..361add9 --- /dev/null +++ b/3rdpart/ZLToolKit/.github/workflows/style.yml @@ -0,0 +1,27 @@ +name: style check + +on: [pull_request] + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + # with all history + fetch-depth: 0 + - name: Validate BOM + run: | + ret=0 + for i in $(git diff --name-only origin/${GITHUB_BASE_REF}...${GITHUB_SHA}); do + if [ -f ${i} ]; then + case ${i} in + *.c|*.cc|*.cpp|*.h) + if file ${i} | grep -qv BOM; then + echo "Missing BOM in ${i}" && ret=1; + fi + ;; + esac + fi + done + exit ${ret} diff --git a/3rdpart/ZLToolKit/.github/workflows/windows.yml b/3rdpart/ZLToolKit/.github/workflows/windows.yml new file mode 100644 index 0000000..517df5c --- /dev/null +++ b/3rdpart/ZLToolKit/.github/workflows/windows.yml @@ -0,0 +1,30 @@ +name: Windows + +on: [push, pull_request] + +jobs: + build: + runs-on: windows-2022 + + steps: + - uses: actions/checkout@v1 + with: + submodules: 'recursive' + fetch-depth: 1 + + - name: 配置 vcpkg + uses: lukka/run-vcpkg@v7 + with: + vcpkgDirectory: '${{github.workspace}}/vcpkg' + vcpkgTriplet: x64-windows-static + # 2021.05.12 + vcpkgGitCommitId: '5568f110b509a9fd90711978a7cb76bae75bb092' + vcpkgArguments: 'openssl' + + - name: 编译 + uses: lukka/run-cmake@v3 + with: + useVcpkgToolchainFile: true + buildDirectory: '${{github.workspace}}/build' + cmakeAppendedArgs: '' + cmakeBuildType: 'RelWithDebInfo' diff --git a/3rdpart/ZLToolKit/.gitignore b/3rdpart/ZLToolKit/.gitignore new file mode 100644 index 0000000..5bd0d4d --- /dev/null +++ b/3rdpart/ZLToolKit/.gitignore @@ -0,0 +1,34 @@ +# Compiled Object files +*.slo +*.lo +*.o +*.obj +*.d + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +#*.dylib +#*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.lib + +# Executables +*.exe +*.out +*.app +/X64/ + +*.DS_Store +/cmake-build-debug/ +/.idea/ +/.vs diff --git a/3rdpart/ZLToolKit/.travis.yml b/3rdpart/ZLToolKit/.travis.yml new file mode 100644 index 0000000..d0c77d9 --- /dev/null +++ b/3rdpart/ZLToolKit/.travis.yml @@ -0,0 +1,13 @@ +language: cpp +sudo: required +dist: trusty +compiler: +- gcc +os: +- linux +before_install: +script: +- ./build_for_linux.sh + + + diff --git a/3rdpart/ZLToolKit/AUTHORS b/3rdpart/ZLToolKit/AUTHORS new file mode 100644 index 0000000..d2b518c --- /dev/null +++ b/3rdpart/ZLToolKit/AUTHORS @@ -0,0 +1,5 @@ +#代码贡献者列表,提交pr时请留下您的联系方式 +#Code contributor list, please leave your contact information when submitting a pull request + +xiongziliang <771730766@qq.com> +[清涩绿茶](https://github.com/baiyfcu) diff --git a/3rdpart/ZLToolKit/CMakeLists.txt b/3rdpart/ZLToolKit/CMakeLists.txt new file mode 100644 index 0000000..169cece --- /dev/null +++ b/3rdpart/ZLToolKit/CMakeLists.txt @@ -0,0 +1,145 @@ +cmake_minimum_required(VERSION 3.1.3...3.26) +project(ZLToolKit) + +#使能c++11 +set(CMAKE_CXX_STANDARD 11) +# -fPIC +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +# 打印详情 +set(CMAKE_VERBOSE_MAKEFILE ON) + +#加载自定义模块 +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +option(ENABLE_OPENSSL "enable openssl" ON) +option(ENABLE_MYSQL "enable mysql" ON) +option(ENABLE_WEPOLL "Enable wepoll" ON) +option(ASAN_USE_DELETE "use delele[] or free when asan enabled" OFF) +option(BUILD_SHARED_LIBS "Build all libraries shared" ON) + +include(CheckStructHasMember) +include(CheckSymbolExists) + +list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) +check_struct_has_member("struct mmsghdr" msg_hdr sys/socket.h HAVE_MMSG_HDR) +check_symbol_exists(sendmmsg sys/socket.h HAVE_SENDMMSG_API) +check_symbol_exists(recvmmsg sys/socket.h HAVE_RECVMMSG_API) + +# 方便修改全局变量 +function(update_cached name value) + set("${name}" "${value}" CACHE INTERNAL "*** Internal ***" FORCE) +endfunction() + +function(update_cached_list name) + set(_tmp_list "${${name}}") + list(APPEND _tmp_list "${ARGN}") + list(REMOVE_DUPLICATES _tmp_list) + update_cached(${name} "${_tmp_list}") +endfunction() + +update_cached(TK_INC_PATHS "") +update_cached(TK_LINK_LIBRARIES "") +update_cached(TK_COMPILE_DEFINITIONS "") +update_cached(TK_COMPILE_OPTIONS "") + +if (HAVE_MMSG_HDR) + update_cached_list(TK_COMPILE_DEFINITIONS HAVE_MMSG_HDR) +endif () + +if (HAVE_SENDMMSG_API) + update_cached_list(TK_COMPILE_DEFINITIONS HAVE_SENDMMSG_API) +endif () + +if (HAVE_RECVMMSG_API) + update_cached_list(TK_COMPILE_DEFINITIONS HAVE_RECVMMSG_API) +endif () + +# check the socket buffer size set by the upper cmake project, if it is set, use the setting of the upper cmake project, otherwise set it to 256K +# if the socket buffer size is set to 0, it means that the socket buffer size is not set, and the kernel default value is used(just for linux) +if (DEFINED SOCKET_DEFAULT_BUF_SIZE) + if (SOCKET_DEFAULT_BUF_SIZE EQUAL 0) + message(STATUS "Socket default buffer size is not set, use the kernel default value") + else () + message(STATUS "Socket default buffer size is set to ${SOCKET_DEFAULT_BUF_SIZE}") + endif () + update_cached_list(TK_COMPILE_DEFINITIONS SOCKET_DEFAULT_BUF_SIZE=${SOCKET_DEFAULT_BUF_SIZE}) +endif () + +# 收集源码 +file(GLOB SRC_LIST + ${CMAKE_CURRENT_SOURCE_DIR}/src/*/*.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/*/*.mm + ${CMAKE_CURRENT_SOURCE_DIR}/src/*/*.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/*/*/*.cpp) + +if (WIN32) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + if (MSVC) + update_cached_list(TK_COMPILE_OPTIONS "/utf-8") + endif () + update_cached_list(TK_LINK_LIBRARIES WS2_32 Iphlpapi shlwapi) + #防止Windows.h包含Winsock.h + update_cached_list(TK_COMPILE_DEFINITIONS WIN32_LEAN_AND_MEAN MP4V2_NO_STDINT_DEFS _CRT_SECURE_NO_WARNINGS _WINSOCK_DEPRECATED_NO_WARNINGS) +else () + # 非Windows平台自带getopt api + list(FILTER SRC_LIST EXCLUDE REGEX "getopt.c$") + update_cached_list(TK_COMPILE_OPTIONS "-Wno-comment" "-Wno-deprecated-declarations" "-Wno-predefined-identifier-outside-function") +endif () + +if (NOT WIN32 OR NOT ENABLE_WEPOLL) + # 移除wepoll + list(FILTER SRC_LIST EXCLUDE REGEX "wepoll.c$") +else () + update_cached_list(TK_COMPILE_DEFINITIONS HAS_EPOLL) + update_cached_list(TK_INC_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/src/win32/) +endif () + +#非苹果平台移除.mm类型的文件 +if (NOT APPLE) + list(FILTER SRC_LIST EXCLUDE REGEX "Socket_ios.mm$") +endif () + +#查找openssl是否安装 +if (ENABLE_OPENSSL) + find_package(OpenSSL) + if (OPENSSL_FOUND) + update_cached_list(TK_INC_PATHS ${OPENSSL_INCLUDE_DIR}) + update_cached_list(TK_LINK_LIBRARIES ${OPENSSL_LIBRARIES}) + update_cached_list(TK_COMPILE_DEFINITIONS ENABLE_OPENSSL) + endif () +endif () + +#查找mysql是否安装 +if (ENABLE_MYSQL) + find_package(MYSQL) + if (MYSQL_FOUND) + update_cached_list(TK_INC_PATHS ${MYSQL_INCLUDE_DIR}) + update_cached_list(TK_INC_PATHS ${MYSQL_INCLUDE_DIR}/mysql) + update_cached_list(TK_LINK_LIBRARIES ${MYSQL_LIBRARIES}) + update_cached_list(TK_COMPILE_DEFINITIONS ENABLE_MYSQL) + endif () +endif () + +#是否使用delete[]替代free,用于解决开启asan后在MacOS上的卡死问题 +if (ASAN_USE_DELETE) + update_cached_list(TK_COMPILE_DEFINITIONS ASAN_USE_DELETE) +endif () + +# 库依赖 +add_library(${PROJECT_NAME}_deps INTERFACE) +target_link_libraries(${PROJECT_NAME}_deps INTERFACE ${TK_LINK_LIBRARIES}) + +#编译库 +add_library(${PROJECT_NAME} ${SRC_LIST}) +#引用头文件路径 +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src ${TK_INC_PATHS}) +target_link_libraries(${PROJECT_NAME} PUBLIC ${PROJECT_NAME}_deps) +target_compile_definitions(${PROJECT_NAME} PUBLIC ${TK_COMPILE_DEFINITIONS}) +target_compile_options(${PROJECT_NAME} PUBLIC ${TK_COMPILE_OPTIONS}) +set_target_properties(${PROJECT_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib + ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) + +if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + #本工程为root工程,则添加测试程序 + add_subdirectory(tests) +endif () \ No newline at end of file diff --git a/3rdpart/ZLToolKit/LICENSE b/3rdpart/ZLToolKit/LICENSE new file mode 100644 index 0000000..9a6eea4 --- /dev/null +++ b/3rdpart/ZLToolKit/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/3rdpart/ZLToolKit/README.md b/3rdpart/ZLToolKit/README.md new file mode 100644 index 0000000..3b6a0c8 --- /dev/null +++ b/3rdpart/ZLToolKit/README.md @@ -0,0 +1,138 @@ +# 一个基于C++11简单易用的轻量级网络编程框架 + +![](https://github.com/ZLMediaKit/ZLToolKit/actions/workflows/linux.yml/badge.svg) +![](https://github.com/ZLMediaKit/ZLToolKit/actions/workflows/macos.yml/badge.svg) +![](https://github.com/ZLMediaKit/ZLToolKit/actions/workflows/windows.yml/badge.svg) + +## 项目特点 +- 基于C++11开发,避免使用裸指针,代码稳定可靠;同时跨平台移植简单方便,代码清晰简洁。 +- 使用epoll+线程池+异步网络IO模式开发,并发性能优越。 +- 代码经过大量的稳定性、性能测试,可满足商用服务器项目。 +- 支持linux、macos、ios、android、windows平台 +- 了解更多:[ZLMediaKit](https://github.com/ZLMediaKit/ZLMediaKit) + +## 特性 +- 网络库 + - tcp/udp客户端,接口简单易用并且是线程安全的,用户不必关心具体的socket api操作。 + - tcp/udp服务器,使用非常简单,只要实现具体的tcp/udp会话(Session类)逻辑,使用模板的方式可以快速的构建高性能的服务器。 + - 对套接字多种操作的封装。 +- 线程库 + - 使用线程实现的简单易用的定时器。 + - 信号量。 + - 线程组。 + - 简单易用的线程池,可以异步或同步执行任务,支持functional 和 lambad表达式。 +- 工具库 + - 文件操作。 + - std::cout风格的日志库,支持颜色高亮、代码定位、异步打印。 + - INI配置文件的读写。 + - 监听者模式的消息广播器。 + - 基于智能指针的循环池,不需要显式手动释放。 + - 环形缓冲,支持主动读取和读取事件两种模式。 + - mysql链接池,使用占位符(?)方式生成sql语句,支持同步异步操作。 + - 简单易用的ssl加解密黑盒,支持多线程。 + - 其他一些有用的工具。 + - 命令行解析工具,可以很便捷的实现可配置应用程序 + +## 网络IO适配 + +| | Linux(Android) | Windows | MacOS(iOS/Unix) | +|:----:|:-----------------:|:-------------------:|:----------------:| +| 多路复用 | epoll/select | wepoll(iocp)/select | kqueue/select | +| udp | recvmmsg/sendmmsg | recvfrom/WSASend | recvfrom/sendto | +| tcp | recvfrom/sendmsg | recvfrom/WSASend | recvfrom/sendmsg | + +## 编译(Linux) +- 我的编译环境 + - Ubuntu16.04 64 bit + gcc5.4(最低gcc4.7) + - cmake 3.5.1 +- 编译 + + ``` + cd ZLToolKit + ./build_for_linux.sh + ``` + +## 编译(macOS) +- 我的编译环境 + - macOS Sierra(10.12.1) + xcode8.3.1 + - Homebrew 1.1.3 + - cmake 3.8.0 +- 编译 + + ``` + cd ZLToolKit + ./build_for_mac.sh + ``` + +## 编译(iOS) +- 编译环境:`请参考macOS的编译指导。` +- 编译 + + ``` + cd ZLToolKit + ./build_for_ios.sh + ``` +- 你也可以生成Xcode工程再编译: + + ``` + cd ZLToolKit + mkdir -p build + cd build + # 生成Xcode工程,工程文件在build目录下 + cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/iOS.cmake -DIOS_PLATFORM=SIMULATOR64 -G "Xcode" + ``` +## 编译(Android) +- 我的编译环境 + - macOS Sierra(10.12.1) + xcode8.3.1 + - Homebrew 1.1.3 + - cmake 3.8.0 + - [android-ndk-r14b](https://dl.google.com/android/repository/android-ndk-r14b-darwin-x86_64.zip) +- 编译 + + ``` + cd ZLToolKit + export ANDROID_NDK_ROOT=/path/to/ndk + ./build_for_android.sh + ``` +## 编译(Windows) +- 我的编译环境 + - windows 10 + - visual studio 2017 + - [openssl](http://slproweb.com/download/Win32OpenSSL-1_1_0f.exe) + - [mysqlclient](https://dev.mysql.com/downloads/file/?id=472430) + - [cmake-gui](https://cmake.org/files/v3.10/cmake-3.10.0-rc1-win32-x86.msi) + +- 编译 +``` +   1 使用cmake-gui打开工程并生成vs工程文件. +   2 找到工程文件(ZLToolKit.sln),双击用vs2017打开. +   3 选择编译Release 版本. +   4 依次编译 ZLToolKit_static、ZLToolKit_shared、ALL_BUILD、INSTALL. + 5 找到目标文件并运行测试用例. +   6 找到安装的头文件及库文件(在源码所在分区根目录). +``` +## 授权协议 + +本项目自有代码使用宽松的MIT协议,在保留版权信息的情况下可以自由应用于各自商用、非商业的项目。 +但是本项目也零碎的使用了一些其他的开源代码,在商用的情况下请自行替代或剔除; +由于使用本项目而产生的商业纠纷或侵权行为一概与本项项目及开发者无关,请自行承担法律风险。 + +## QA + - 该库性能怎么样? + +基于ZLToolKit,我实现了一个流媒体服务器[ZLMediaKit](https://github.com/ZLMediaKit/ZLMediaKit);作者已经对其进行了性能测试,可以查看[benchmark.md](https://github.com/ZLMediaKit/ZLMediaKit/blob/master/benchmark.md)了解详情。 + + - 该库稳定性怎么样? + +该库经过作者严格的valgrind测试,长时间大负荷的测试;作者也使用该库进行了多个线上项目的开发。实践证明该库稳定性很好;可以无看门狗脚本的方式连续运行几个月。 + + - 在windows下编译很多错误? + + 由于本项目主体代码在macOS/linux下开发,部分源码采用的是无bom头的UTF-8编码;由于windows对于utf-8支持不甚友好,所以如果发现编译错误请先尝试添加bom头再编译。 + + +## 联系方式 +- 邮箱:<1213642868@qq.com>(本项目相关或网络编程相关问题请走issue流程,否则恕不邮件答复) +- QQ群:542509000 + + diff --git a/3rdpart/ZLToolKit/cmake/FindMYSQL.cmake b/3rdpart/ZLToolKit/cmake/FindMYSQL.cmake new file mode 100644 index 0000000..6eebbad --- /dev/null +++ b/3rdpart/ZLToolKit/cmake/FindMYSQL.cmake @@ -0,0 +1,132 @@ +# - Try to find MySQL / MySQL Embedded library +# Find the MySQL includes and client library +# This module defines +# MYSQL_INCLUDE_DIR, where to find mysql.h +# MYSQL_LIBRARIES, the libraries needed to use MySQL. +# MYSQL_LIB_DIR, path to the MYSQL_LIBRARIES +# MYSQL_EMBEDDED_LIBRARIES, the libraries needed to use MySQL Embedded. +# MYSQL_EMBEDDED_LIB_DIR, path to the MYSQL_EMBEDDED_LIBRARIES +# MYSQL_FOUND, If false, do not try to use MySQL. +# MYSQL_EMBEDDED_FOUND, If false, do not try to use MySQL Embedded. + +# Copyright (c) 2006-2008, Jarosław Staniek +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +include(CheckCXXSourceCompiles) + +if(WIN32) + find_path(MYSQL_INCLUDE_DIR mysql.h + PATHS + $ENV{MYSQL_INCLUDE_DIR} + $ENV{MYSQL_DIR}/include + $ENV{ProgramFiles}/MySQL/*/include + $ENV{SystemDrive}/MySQL/*/include + $ENV{ProgramW6432}/MySQL/*/include + ) +else(WIN32) + #在Mac OS下, mysql.h的文件可能不是在mysql目录下 + #可能存在/usr/local/mysql/include/mysql.h + find_path(MYSQL_INCLUDE_DIR mysql.h + PATHS + $ENV{MYSQL_INCLUDE_DIR} + $ENV{MYSQL_DIR}/include + /usr/local/mysql/include + /usr/local/mysql/include/mysql + /opt/mysql/mysql/include + PATH_SUFFIXES + mysql + ) +endif(WIN32) + +if(WIN32) + if (${CMAKE_BUILD_TYPE}) + string(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_TOLOWER) + endif() + + # path suffix for debug/release mode + # binary_dist: mysql binary distribution + # build_dist: custom build + if(CMAKE_BUILD_TYPE_TOLOWER MATCHES "debug") + set(binary_dist debug) + set(build_dist Debug) + else(CMAKE_BUILD_TYPE_TOLOWER MATCHES "debug") + ADD_DEFINITIONS(-DDBUG_OFF) + set(binary_dist opt) + set(build_dist Release) + endif(CMAKE_BUILD_TYPE_TOLOWER MATCHES "debug") + +# find_library(MYSQL_LIBRARIES NAMES mysqlclient + set(MYSQL_LIB_PATHS + $ENV{MYSQL_DIR}/lib/${binary_dist} + $ENV{MYSQL_DIR}/libmysql/${build_dist} + $ENV{MYSQL_DIR}/client/${build_dist} + $ENV{ProgramFiles}/MySQL/*/lib/${binary_dist} + $ENV{SystemDrive}/MySQL/*/lib/${binary_dist} + $ENV{MYSQL_DIR}/lib/opt + $ENV{MYSQL_DIR}/client/release + $ENV{ProgramFiles}/MySQL/*/lib/opt + $ENV{ProgramFiles}/MySQL/*/lib/ + $ENV{SystemDrive}/MySQL/*/lib/opt + $ENV{ProgramW6432}/MySQL/*/lib + ) + find_library(MYSQL_LIBRARIES NAMES libmysql + PATHS + ${MYSQL_LIB_PATHS} + ) +else(WIN32) +# find_library(MYSQL_LIBRARIES NAMES mysqlclient + set(MYSQL_LIB_PATHS + $ENV{MYSQL_DIR}/libmysql_r/.libs + $ENV{MYSQL_DIR}/lib + $ENV{MYSQL_DIR}/lib/mysql + /usr/local/mysql/lib + /opt/mysql/mysql/lib + $ENV{MYSQL_DIR}/libmysql_r/.libs + $ENV{MYSQL_DIR}/lib + $ENV{MYSQL_DIR}/lib/mysql + /usr/local/mysql/lib + /opt/mysql/mysql/lib + PATH_SUFFIXES + mysql + ) + find_library(MYSQL_LIBRARIES NAMES mysqlclient + PATHS + ${MYSQL_LIB_PATHS} + ) +endif(WIN32) + +find_library(MYSQL_EMBEDDED_LIBRARIES NAMES mysqld + PATHS + ${MYSQL_LIB_PATHS} +) + +if(MYSQL_LIBRARIES) + get_filename_component(MYSQL_LIB_DIR ${MYSQL_LIBRARIES} PATH) +endif(MYSQL_LIBRARIES) + +if(MYSQL_EMBEDDED_LIBRARIES) + get_filename_component(MYSQL_EMBEDDED_LIB_DIR ${MYSQL_EMBEDDED_LIBRARIES} PATH) +endif(MYSQL_EMBEDDED_LIBRARIES) + +set( CMAKE_REQUIRED_INCLUDES ${MYSQL_INCLUDE_DIR} ) +set( CMAKE_REQUIRED_LIBRARIES ${MYSQL_EMBEDDED_LIBRARIES} ) +check_cxx_source_compiles( "#include \nint main() { int i = MYSQL_OPT_USE_EMBEDDED_CONNECTION; }" HAVE_MYSQL_OPT_EMBEDDED_CONNECTION ) +if(MYSQL_INCLUDE_DIR AND MYSQL_LIBRARIES) + set(MYSQL_FOUND TRUE) + message(STATUS "Found MySQL: ${MYSQL_INCLUDE_DIR}, ${MYSQL_LIBRARIES}") +else(MYSQL_INCLUDE_DIR AND MYSQL_LIBRARIES) + set(MYSQL_FOUND FALSE) + message(STATUS "MySQL not found.") +endif(MYSQL_INCLUDE_DIR AND MYSQL_LIBRARIES) + +if(MYSQL_INCLUDE_DIR AND MYSQL_EMBEDDED_LIBRARIES AND HAVE_MYSQL_OPT_EMBEDDED_CONNECTION) + set(MYSQL_EMBEDDED_FOUND TRUE) + message(STATUS "Found MySQL Embedded: ${MYSQL_INCLUDE_DIR}, ${MYSQL_EMBEDDED_LIBRARIES}") +else(MYSQL_INCLUDE_DIR AND MYSQL_EMBEDDED_LIBRARIES AND HAVE_MYSQL_OPT_EMBEDDED_CONNECTION) + set(MYSQL_EMBEDDED_FOUND FALSE) + message(STATUS "MySQL Embedded not found.") +endif(MYSQL_INCLUDE_DIR AND MYSQL_EMBEDDED_LIBRARIES AND HAVE_MYSQL_OPT_EMBEDDED_CONNECTION) + +mark_as_advanced(MYSQL_INCLUDE_DIR MYSQL_LIBRARIES MYSQL_EMBEDDED_LIBRARIES) diff --git a/3rdpart/ZLToolKit/src/Network/Buffer.cpp b/3rdpart/ZLToolKit/src/Network/Buffer.cpp new file mode 100644 index 0000000..fe8fd93 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Network/Buffer.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include "Buffer.h" +#include "Util/onceToken.h" + +namespace toolkit { + +StatisticImp(Buffer) +StatisticImp(BufferRaw) +StatisticImp(BufferLikeString) + +BufferRaw::Ptr BufferRaw::create(size_t size) { +#if 0 + static ResourcePool packet_pool; + static onceToken token([]() { + packet_pool.setSize(1024); + }); + auto ret = packet_pool.obtain2(); + ret->setSize(0); + return ret; +#else + return Ptr(new BufferRaw(size)); +#endif +} + +}//namespace toolkit diff --git a/3rdpart/ZLToolKit/src/Network/Buffer.h b/3rdpart/ZLToolKit/src/Network/Buffer.h new file mode 100644 index 0000000..05be45e --- /dev/null +++ b/3rdpart/ZLToolKit/src/Network/Buffer.h @@ -0,0 +1,491 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLTOOLKIT_BUFFER_H +#define ZLTOOLKIT_BUFFER_H + +#include +#include +#include +#include +#include +#include +#include "Util/util.h" +#include "Util/ResourcePool.h" + +namespace toolkit { + +template struct is_pointer : public std::false_type {}; +template struct is_pointer > : public std::true_type {}; +template struct is_pointer > : public std::true_type {}; +template struct is_pointer : public std::true_type {}; +template struct is_pointer : public std::true_type {}; + +//缓存基类 [AUTO-TRANSLATED:d130ab72] +//Cache base class +class Buffer : public noncopyable { +public: + using Ptr = std::shared_ptr; + + Buffer() = default; + virtual ~Buffer() = default; + + //返回数据长度 [AUTO-TRANSLATED:955f731c] + //Return data length + virtual char *data() const = 0; + virtual size_t size() const = 0; + + virtual std::string toString() const { + return std::string(data(), size()); + } + + virtual size_t getCapacity() const { + return size(); + } + +private: + //对象个数统计 [AUTO-TRANSLATED:3b43e8c2] + //Object count statistics + ObjectStatistic _statistic; +}; + +template +class BufferOffset : public Buffer { +public: + using Ptr = std::shared_ptr; + + BufferOffset(C data, size_t offset = 0, size_t len = 0) : _data(std::move(data)) { + setup(offset, len); + } + + ~BufferOffset() override = default; + + char *data() const override { + return const_cast(getPointer(_data)->data()) + _offset; + } + + size_t size() const override { + return _size; + } + + std::string toString() const override { + return std::string(data(), size()); + } + +private: + void setup(size_t offset = 0, size_t size = 0) { + auto max_size = getPointer(_data)->size(); + assert(offset + size <= max_size); + if (!size) { + size = max_size - offset; + } + _size = size; + _offset = offset; + } + + template + static typename std::enable_if<::toolkit::is_pointer::value, const T &>::type + getPointer(const T &data) { + return data; + } + + template + static typename std::enable_if::value, const T *>::type + getPointer(const T &data) { + return &data; + } + +private: + C _data; + size_t _size; + size_t _offset; +}; + +using BufferString = BufferOffset; + +//指针式缓存对象, [AUTO-TRANSLATED:c8403290] +//Pointer-style cache object, +class BufferRaw : public Buffer { +public: + using Ptr = std::shared_ptr; + + static Ptr create(size_t size = 0); + + ~BufferRaw() override { + if (_data) { + delete[] _data; + } + } + + //在写入数据时请确保内存是否越界 [AUTO-TRANSLATED:5602043e] + //When writing data, please ensure that the memory does not overflow + char *data() const override { + return _data; + } + + //有效数据大小 [AUTO-TRANSLATED:b8dcbda7] + //Effective data size + size_t size() const override { + return _size; + } + + //分配内存大小 [AUTO-TRANSLATED:cce87adf] + //Allocated memory size + void setCapacity(size_t capacity) { + if (_data) { + do { + if (capacity > _capacity) { + //请求的内存大于当前内存,那么重新分配 [AUTO-TRANSLATED:65306424] + //If the requested memory is greater than the current memory, reallocate + break; + } + + if (_capacity < 2 * 1024) { + //2K以下,不重复开辟内存,直接复用 [AUTO-TRANSLATED:056416c0] + //Less than 2K, do not repeatedly allocate memory, reuse directly + return; + } + + if (2 * capacity > _capacity) { + //如果请求的内存大于当前内存的一半,那么也复用 [AUTO-TRANSLATED:c189d660] + //If the requested memory is greater than half of the current memory, also reuse + return; + } + } while (false); + + delete[] _data; + } + _data = new char[capacity]; + _capacity = capacity; + } + + //设置有效数据大小 [AUTO-TRANSLATED:efc4fb3e] + //Set valid data size + virtual void setSize(size_t size) { + if (size > _capacity) { + throw std::invalid_argument("Buffer::setSize out of range"); + } + _size = size; + } + + //赋值数据 [AUTO-TRANSLATED:0b91b213] + //Assign data + void assign(const char *data, size_t size = 0) { + if (size <= 0) { + size = strlen(data); + } + setCapacity(size + 1); + memcpy(_data, data, size); + _data[size] = '\0'; + setSize(size); + } + + size_t getCapacity() const override { + return _capacity; + } + +protected: + friend class ResourcePool_l; + + BufferRaw(size_t capacity = 0) { + if (capacity) { + setCapacity(capacity); + } + } + + BufferRaw(const char *data, size_t size = 0) { + assign(data, size); + } + +private: + size_t _size = 0; + size_t _capacity = 0; + char *_data = nullptr; + //对象个数统计 [AUTO-TRANSLATED:3b43e8c2] + //Object count statistics + ObjectStatistic _statistic; +}; + +class BufferLikeString : public Buffer { +public: + ~BufferLikeString() override = default; + + BufferLikeString() { + _erase_head = 0; + _erase_tail = 0; + } + + BufferLikeString(std::string str) { + _str = std::move(str); + _erase_head = 0; + _erase_tail = 0; + } + + BufferLikeString &operator=(std::string str) { + _str = std::move(str); + _erase_head = 0; + _erase_tail = 0; + return *this; + } + + BufferLikeString(const char *str) { + _str = str; + _erase_head = 0; + _erase_tail = 0; + } + + BufferLikeString &operator=(const char *str) { + _str = str; + _erase_head = 0; + _erase_tail = 0; + return *this; + } + + BufferLikeString(BufferLikeString &&that) { + _str = std::move(that._str); + _erase_head = that._erase_head; + _erase_tail = that._erase_tail; + that._erase_head = 0; + that._erase_tail = 0; + } + + BufferLikeString &operator=(BufferLikeString &&that) { + _str = std::move(that._str); + _erase_head = that._erase_head; + _erase_tail = that._erase_tail; + that._erase_head = 0; + that._erase_tail = 0; + return *this; + } + + BufferLikeString(const BufferLikeString &that) { + _str = that._str; + _erase_head = that._erase_head; + _erase_tail = that._erase_tail; + } + + BufferLikeString &operator=(const BufferLikeString &that) { + _str = that._str; + _erase_head = that._erase_head; + _erase_tail = that._erase_tail; + return *this; + } + + char *data() const override { + return (char *) _str.data() + _erase_head; + } + + size_t size() const override { + return _str.size() - _erase_tail - _erase_head; + } + + BufferLikeString &erase(size_t pos = 0, size_t n = std::string::npos) { + if (pos == 0) { + //移除前面的数据 [AUTO-TRANSLATED:b025d3c5] + //Remove data from the front + if (n != std::string::npos) { + //移除部分 [AUTO-TRANSLATED:a650bef2] + //Remove part + if (n > size()) { + //移除太多数据了 [AUTO-TRANSLATED:64460d15] + //Removed too much data + throw std::out_of_range("BufferLikeString::erase out_of_range in head"); + } + //设置起始便宜量 [AUTO-TRANSLATED:7a0250bd] + //Set starting offset + _erase_head += n; + data()[size()] = '\0'; + return *this; + } + //移除全部数据 [AUTO-TRANSLATED:3d016f79] + //Remove all data + _erase_head = 0; + _erase_tail = _str.size(); + data()[0] = '\0'; + return *this; + } + + if (n == std::string::npos || pos + n >= size()) { + //移除末尾所有数据 [AUTO-TRANSLATED:efaf1165] + //Remove all data from the end + if (pos >= size()) { + //移除太多数据 [AUTO-TRANSLATED:dc9347c3] + //Removed too much data + throw std::out_of_range("BufferLikeString::erase out_of_range in tail"); + } + _erase_tail += size() - pos; + data()[size()] = '\0'; + return *this; + } + + //移除中间的 [AUTO-TRANSLATED:fd25344c] + //Remove the middle + if (pos + n > size()) { + //超过长度限制 [AUTO-TRANSLATED:9ae84929] + //Exceeds the length limit + throw std::out_of_range("BufferLikeString::erase out_of_range in middle"); + } + _str.erase(_erase_head + pos, n); + return *this; + } + + BufferLikeString &append(const BufferLikeString &str) { + return append(str.data(), str.size()); + } + + BufferLikeString &append(const std::string &str) { + return append(str.data(), str.size()); + } + + BufferLikeString &append(const char *data) { + return append(data, strlen(data)); + } + + BufferLikeString &append(const char *data, size_t len) { + if (len <= 0) { + return *this; + } + if (_erase_head > _str.capacity() / 2) { + moveData(); + } + if (_erase_tail == 0) { + _str.append(data, len); + return *this; + } + _str.insert(_erase_head + size(), data, len); + return *this; + } + + void push_back(char c) { + if (_erase_tail == 0) { + _str.push_back(c); + return; + } + data()[size()] = c; + --_erase_tail; + data()[size()] = '\0'; + } + + BufferLikeString &insert(size_t pos, const char *s, size_t n) { + _str.insert(_erase_head + pos, s, n); + return *this; + } + + BufferLikeString &assign(const char *data) { + return assign(data, strlen(data)); + } + + BufferLikeString &assign(const char *data, size_t len) { + if (len <= 0) { + return *this; + } + if (data >= _str.data() && data < _str.data() + _str.size()) { + _erase_head = data - _str.data(); + if (data + len > _str.data() + _str.size()) { + throw std::out_of_range("BufferLikeString::assign out_of_range"); + } + _erase_tail = _str.data() + _str.size() - (data + len); + return *this; + } + _str.assign(data, len); + _erase_head = 0; + _erase_tail = 0; + return *this; + } + + void clear() { + _erase_head = 0; + _erase_tail = 0; + _str.clear(); + } + + char &operator[](size_t pos) { + if (pos >= size()) { + throw std::out_of_range("BufferLikeString::operator[] out_of_range"); + } + return data()[pos]; + } + + const char &operator[](size_t pos) const { + return (*const_cast(this))[pos]; + } + + size_t capacity() const { + return _str.capacity(); + } + + void reserve(size_t size) { + _str.reserve(size); + } + + void resize(size_t size, char c = '\0') { + auto old_size = this->size(); + if (size == old_size) { + return; + } + if (size > old_size) { + auto append = size - old_size; + if (append > _erase_tail) { + _str.resize(append - _erase_tail, c); + memset(const_cast(_str.data()) + _erase_head + old_size, c, _erase_tail); + _erase_tail = 0; + } else { + _erase_tail -= append; + memset(const_cast(_str.data()) + _erase_head + old_size, c, append); + } + } else { + auto erased = old_size - size; + _erase_tail += erased; + memset(const_cast(_str.data()) + _erase_head + size, c, erased); + } + } + + bool empty() const { + return size() <= 0; + } + + std::string substr(size_t pos, size_t n = std::string::npos) const { + if (n == std::string::npos) { + //获取末尾所有的 [AUTO-TRANSLATED:8a0b92b6] + //Get all at the end + if (pos >= size()) { + throw std::out_of_range("BufferLikeString::substr out_of_range"); + } + return _str.substr(_erase_head + pos, size() - pos); + } + + //获取部分 [AUTO-TRANSLATED:d01310a4] + //Get part + if (pos + n > size()) { + throw std::out_of_range("BufferLikeString::substr out_of_range"); + } + return _str.substr(_erase_head + pos, n); + } + +protected: + size_t _erase_head; + size_t _erase_tail; + std::string _str; + +private: + void moveData() { + if (_erase_head) { + _str.erase(0, _erase_head); + _erase_head = 0; + } + } + + //对象个数统计 [AUTO-TRANSLATED:3b43e8c2] + //Object count statistics + ObjectStatistic _statistic; +}; + +}//namespace toolkit +#endif //ZLTOOLKIT_BUFFER_H diff --git a/3rdpart/ZLToolKit/src/Network/BufferSock.cpp b/3rdpart/ZLToolKit/src/Network/BufferSock.cpp new file mode 100644 index 0000000..37ca836 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Network/BufferSock.cpp @@ -0,0 +1,622 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include "BufferSock.h" +#include "Util/logger.h" +#include "Util/uv_errno.h" + +#if defined(__linux__) || defined(__linux) + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#ifndef MSG_WAITFORONE +#define MSG_WAITFORONE 0x10000 +#endif + +#ifndef HAVE_MMSG_HDR +struct mmsghdr { + struct msghdr msg_hdr; + unsigned msg_len; +}; +#endif + +#ifndef HAVE_SENDMMSG_API +#include +#include +static inline int sendmmsg(int fd, struct mmsghdr *mmsg, + unsigned vlen, unsigned flags) +{ + return syscall(__NR_sendmmsg, fd, mmsg, vlen, flags); +} +#endif + +#ifndef HAVE_RECVMMSG_API +#include +#include +static inline int recvmmsg(int fd, struct mmsghdr *mmsg, + unsigned vlen, unsigned flags, struct timespec *timeout) +{ + return syscall(__NR_recvmmsg, fd, mmsg, vlen, flags, timeout); +} +#endif + +#endif// defined(__linux__) || defined(__linux) + +namespace toolkit { + +StatisticImp(BufferList) + +/////////////////////////////////////// BufferSock /////////////////////////////////////// + +BufferSock::BufferSock(Buffer::Ptr buffer, struct sockaddr *addr, int addr_len) { + if (addr) { + _addr_len = addr_len ? addr_len : SockUtil::get_sock_len(addr); + memcpy(&_addr, addr, _addr_len); + } + assert(buffer); + _buffer = std::move(buffer); +} + +char *BufferSock::data() const { + return _buffer->data(); +} + +size_t BufferSock::size() const { + return _buffer->size(); +} + +const struct sockaddr *BufferSock::sockaddr() const { + return (struct sockaddr *)&_addr; +} + +socklen_t BufferSock::socklen() const { + return _addr_len; +} + +/////////////////////////////////////// BufferCallBack /////////////////////////////////////// + +class BufferCallBack { +public: + BufferCallBack(List > list, BufferList::SendResult cb) + : _cb(std::move(cb)) + , _pkt_list(std::move(list)) {} + + ~BufferCallBack() { + sendCompleted(false); + } + + void sendCompleted(bool flag) { + if (_cb) { + //全部发送成功或失败回调 [AUTO-TRANSLATED:6b9a9abf] + //All send success or failure callback + while (!_pkt_list.empty()) { + _cb(_pkt_list.front().first, flag); + _pkt_list.pop_front(); + } + } else { + _pkt_list.clear(); + } + } + + void sendFrontSuccess() { + if (_cb) { + //发送成功回调 [AUTO-TRANSLATED:52759efc] + //Send success callback + _cb(_pkt_list.front().first, true); + } + _pkt_list.pop_front(); + } + +protected: + BufferList::SendResult _cb; + List > _pkt_list; +}; + +/////////////////////////////////////// BufferSendMsg /////////////////////////////////////// +#if defined(_WIN32) +using SocketBuf = WSABUF; +#else +using SocketBuf = iovec; +#endif + +class BufferSendMsg final : public BufferList, public BufferCallBack { +public: + using SocketBufVec = std::vector; + + BufferSendMsg(List > list, SendResult cb); + ~BufferSendMsg() override = default; + + bool empty() override; + size_t count() override; + ssize_t send(int fd, int flags) override; + +private: + void reOffset(size_t n); + ssize_t send_l(int fd, int flags); + +private: + size_t _iovec_off = 0; + size_t _remain_size = 0; + SocketBufVec _iovec; +}; + +bool BufferSendMsg::empty() { + return _remain_size == 0; +} + +size_t BufferSendMsg::count() { + return _iovec.size() - _iovec_off; +} + +ssize_t BufferSendMsg::send_l(int fd, int flags) { + ssize_t n; +#if !defined(_WIN32) + do { + struct msghdr msg; + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = &(_iovec[_iovec_off]); + msg.msg_iovlen = _iovec.size() - _iovec_off; + if (msg.msg_iovlen > IOV_MAX) { + msg.msg_iovlen = IOV_MAX; + } + msg.msg_control = nullptr; + msg.msg_controllen = 0; + msg.msg_flags = flags; + n = sendmsg(fd, &msg, flags); + } while (-1 == n && UV_EINTR == get_uv_error(true)); +#else + do { + DWORD sent = 0; + n = WSASend(fd, const_cast(&_iovec[0]), static_cast(_iovec.size()), &sent, static_cast(flags), 0, 0); + if (n == SOCKET_ERROR) {return -1;} + n = sent; + } while (n < 0 && UV_ECANCELED == get_uv_error(true)); +#endif + + if (n >= (ssize_t)_remain_size) { + //全部写完了 [AUTO-TRANSLATED:c990f48a] + //All written + _remain_size = 0; + sendCompleted(true); + return n; + } + + if (n > 0) { + //部分发送成功 [AUTO-TRANSLATED:4c240905] + //Partial send success + reOffset(n); + return n; + } + + //一个字节都未发送 [AUTO-TRANSLATED:c33c611b] + //Not a single byte sent + return n; +} + +ssize_t BufferSendMsg::send(int fd, int flags) { + auto remain_size = _remain_size; + while (_remain_size && send_l(fd, flags) != -1); + + ssize_t sent = remain_size - _remain_size; + if (sent > 0) { + //部分或全部发送成功 [AUTO-TRANSLATED:a3f5e70e] + //Partial or all send success + return sent; + } + //一个字节都未发送成功 [AUTO-TRANSLATED:858b63e5] + //Not a single byte sent successfully + return -1; +} + +void BufferSendMsg::reOffset(size_t n) { + _remain_size -= n; + size_t offset = 0; + for (auto i = _iovec_off; i != _iovec.size(); ++i) { + auto &ref = _iovec[i]; +#if !defined(_WIN32) + offset += ref.iov_len; +#else + offset += ref.len; +#endif + if (offset < n) { + //此包发送完毕 [AUTO-TRANSLATED:759b9f0e] + //This package is sent + sendFrontSuccess(); + continue; + } + _iovec_off = i; + if (offset == n) { + //这是末尾发送完毕的一个包 [AUTO-TRANSLATED:6a3b77e4] + //This is the last package sent + ++_iovec_off; + sendFrontSuccess(); + break; + } + //这是末尾发送部分成功的一个包 [AUTO-TRANSLATED:64645cef] + //This is the last package partially sent + size_t remain = offset - n; +#if !defined(_WIN32) + ref.iov_base = (char *)ref.iov_base + ref.iov_len - remain; + ref.iov_len = remain; +#else + ref.buf = (CHAR *)ref.buf + ref.len - remain; + ref.len = remain; +#endif + break; + } +} + +BufferSendMsg::BufferSendMsg(List> list, SendResult cb) + : BufferCallBack(std::move(list), std::move(cb)) + , _iovec(_pkt_list.size()) { + auto it = _iovec.begin(); + _pkt_list.for_each([&](std::pair &pr) { +#if !defined(_WIN32) + it->iov_base = pr.first->data(); + it->iov_len = pr.first->size(); + _remain_size += it->iov_len; +#else + it->buf = pr.first->data(); + it->len = pr.first->size(); + _remain_size += it->len; +#endif + ++it; + }); +} + +/////////////////////////////////////// BufferSendTo /////////////////////////////////////// +class BufferSendTo final: public BufferList, public BufferCallBack { +public: + BufferSendTo(List > list, SendResult cb, bool is_udp); + ~BufferSendTo() override = default; + + bool empty() override; + size_t count() override; + ssize_t send(int fd, int flags) override; + +private: + bool _is_udp; + size_t _offset = 0; +}; + +BufferSendTo::BufferSendTo(List> list, BufferList::SendResult cb, bool is_udp) + : BufferCallBack(std::move(list), std::move(cb)) + , _is_udp(is_udp) {} + +bool BufferSendTo::empty() { + return _pkt_list.empty(); +} + +size_t BufferSendTo::count() { + return _pkt_list.size(); +} + +static inline BufferSock *getBufferSockPtr(std::pair &pr) { + if (!pr.second) { + return nullptr; + } + return static_cast(pr.first.get()); +} + +ssize_t BufferSendTo::send(int fd, int flags) { + size_t sent = 0; + ssize_t n; + while (!_pkt_list.empty()) { + auto &front = _pkt_list.front(); + auto &buffer = front.first; + if (_is_udp) { + auto ptr = getBufferSockPtr(front); + n = ::sendto(fd, buffer->data() + _offset, buffer->size() - _offset, flags, ptr ? ptr->sockaddr() : nullptr, ptr ? ptr->socklen() : 0); + } else { + n = ::send(fd, buffer->data() + _offset, buffer->size() - _offset, flags); + } + + if (n >= 0) { + assert(n); + _offset += n; + if (_offset == buffer->size()) { + sendFrontSuccess(); + _offset = 0; + } + sent += n; + continue; + } + + //n == -1的情况 [AUTO-TRANSLATED:305fb5bc] + //n == -1 case + if (get_uv_error(true) == UV_EINTR) { + //被打断,需要继续发送 [AUTO-TRANSLATED:6ef0b34d] + //interrupted, need to continue sending + continue; + } + //其他原因导致的send返回-1 [AUTO-TRANSLATED:299cddb7] + //other reasons causing send to return -1 + break; + } + return sent ? sent : -1; +} + +/////////////////////////////////////// BufferSendMmsg /////////////////////////////////////// + +#if defined(__linux__) || defined(__linux) + +class BufferSendMMsg : public BufferList, public BufferCallBack { +public: + BufferSendMMsg(List > list, SendResult cb); + ~BufferSendMMsg() override = default; + + bool empty() override; + size_t count() override; + ssize_t send(int fd, int flags) override; + +private: + void reOffset(size_t n); + ssize_t send_l(int fd, int flags); + +private: + size_t _remain_size = 0; + std::vector _iovec; + std::vector _hdrvec; +}; + +bool BufferSendMMsg::empty() { + return _remain_size == 0; +} + +size_t BufferSendMMsg::count() { + return _hdrvec.size(); +} + +ssize_t BufferSendMMsg::send_l(int fd, int flags) { + ssize_t n; + do { + n = sendmmsg(fd, &_hdrvec[0], _hdrvec.size(), flags); + } while (-1 == n && UV_EINTR == get_uv_error(true)); + + if (n > 0) { + //部分或全部发送成功 [AUTO-TRANSLATED:a3f5e70e] + //partially or fully sent successfully + reOffset(n); + return n; + } + + //一个字节都未发送 [AUTO-TRANSLATED:c33c611b] + //not a single byte sent + return n; +} + +ssize_t BufferSendMMsg::send(int fd, int flags) { + auto remain_size = _remain_size; + while (_remain_size && send_l(fd, flags) != -1); + ssize_t sent = remain_size - _remain_size; + if (sent > 0) { + //部分或全部发送成功 [AUTO-TRANSLATED:a3f5e70e] + //partially or fully sent successfully + return sent; + } + //一个字节都未发送成功 [AUTO-TRANSLATED:858b63e5] + //not a single byte sent successfully + return -1; +} + +void BufferSendMMsg::reOffset(size_t n) { + for (auto it = _hdrvec.begin(); it != _hdrvec.end();) { + auto &hdr = *it; + auto &io = *(hdr.msg_hdr.msg_iov); + assert(hdr.msg_len <= io.iov_len); + _remain_size -= hdr.msg_len; + if (hdr.msg_len == io.iov_len) { + //这个udp包全部发送成功 [AUTO-TRANSLATED:fce1cc86] + //this UDP packet sent successfully + it = _hdrvec.erase(it); + sendFrontSuccess(); + continue; + } + //部分发送成功 [AUTO-TRANSLATED:4c240905] + //partially sent successfully + io.iov_base = (char *)io.iov_base + hdr.msg_len; + io.iov_len -= hdr.msg_len; + break; + } +} + +BufferSendMMsg::BufferSendMMsg(List> list, SendResult cb) + : BufferCallBack(std::move(list), std::move(cb)) + , _iovec(_pkt_list.size()) + , _hdrvec(_pkt_list.size()) { + auto i = 0U; + _pkt_list.for_each([&](std::pair &pr) { + auto &io = _iovec[i]; + io.iov_base = pr.first->data(); + io.iov_len = pr.first->size(); + _remain_size += io.iov_len; + + auto ptr = getBufferSockPtr(pr); + auto &mmsg = _hdrvec[i]; + auto &msg = mmsg.msg_hdr; + mmsg.msg_len = 0; + msg.msg_name = ptr ? (void *)ptr->sockaddr() : nullptr; + msg.msg_namelen = ptr ? ptr->socklen() : 0; + msg.msg_iov = &io; + msg.msg_iovlen = 1; + msg.msg_control = nullptr; + msg.msg_controllen = 0; + msg.msg_flags = 0; + ++i; + }); +} + +#endif //defined(__linux__) || defined(__linux) + + +BufferList::Ptr BufferList::create(List > list, SendResult cb, bool is_udp) { +#if defined(_WIN32) + if (is_udp) { + // sendto/send 方案,待优化 [AUTO-TRANSLATED:e94184aa] + //sendto/send scheme, to be optimized + return std::make_shared(std::move(list), std::move(cb), is_udp); + } + // WSASend方案 [AUTO-TRANSLATED:9ac7bb81] + //WSASend scheme + return std::make_shared(std::move(list), std::move(cb)); +#elif defined(__linux__) || defined(__linux) + if (is_udp) { + // sendmmsg方案 [AUTO-TRANSLATED:4596c2c4] + //sendmmsg scheme + return std::make_shared(std::move(list), std::move(cb)); + } + // sendmsg方案 [AUTO-TRANSLATED:8846f9c4] + //sendmsg scheme + return std::make_shared(std::move(list), std::move(cb)); +#else + if (is_udp) { + // sendto/send 方案, 可优化? [AUTO-TRANSLATED:21dbae7c] + //sendto/send scheme, can be optimized? + return std::make_shared(std::move(list), std::move(cb), is_udp); + } + // sendmsg方案 [AUTO-TRANSLATED:8846f9c4] + //sendmsg scheme + return std::make_shared(std::move(list), std::move(cb)); +#endif +} + +#if defined(__linux) || defined(__linux__) +class SocketRecvmmsgBuffer : public SocketRecvBuffer { +public: + SocketRecvmmsgBuffer(size_t count, size_t size) + : _size(size) + , _iovec(count) + , _mmsgs(count) + , _buffers(count) + , _address(count) { + for (auto i = 0u; i < count; ++i) { + auto buf = BufferRaw::create(); + buf->setCapacity(size); + + _buffers[i] = buf; + auto &mmsg = _mmsgs[i]; + auto &addr = _address[i]; + mmsg.msg_len = 0; + mmsg.msg_hdr.msg_name = &addr; + mmsg.msg_hdr.msg_namelen = sizeof(addr); + mmsg.msg_hdr.msg_iov = &_iovec[i]; + mmsg.msg_hdr.msg_iov->iov_base = buf->data(); + mmsg.msg_hdr.msg_iov->iov_len = buf->getCapacity() - 1; + mmsg.msg_hdr.msg_iovlen = 1; + mmsg.msg_hdr.msg_control = nullptr; + mmsg.msg_hdr.msg_controllen = 0; + mmsg.msg_hdr.msg_flags = 0; + } + } + + ssize_t recvFromSocket(int fd, ssize_t &count) override { + for (auto i = 0; i < _last_count; ++i) { + auto &mmsg = _mmsgs[i]; + mmsg.msg_hdr.msg_namelen = sizeof(struct sockaddr_storage); + auto &buf = _buffers[i]; + if (!buf) { + auto raw = BufferRaw::create(); + raw->setCapacity(_size); + buf = raw; + mmsg.msg_hdr.msg_iov->iov_base = buf->data(); + } + } + do { + count = recvmmsg(fd, &_mmsgs[0], _mmsgs.size(), 0, nullptr); + } while (-1 == count && UV_EINTR == get_uv_error(true)); + + _last_count = count; + if (count <= 0) { + return count; + } + + ssize_t nread = 0; + for (auto i = 0; i < count; ++i) { + auto &mmsg = _mmsgs[i]; + nread += mmsg.msg_len; + + auto buf = std::static_pointer_cast(_buffers[i]); + buf->setSize(mmsg.msg_len); + buf->data()[mmsg.msg_len] = '\0'; + } + return nread; + } + + Buffer::Ptr &getBuffer(size_t index) override { return _buffers[index]; } + + struct sockaddr_storage &getAddress(size_t index) override { return _address[index]; } + +private: + size_t _size; + ssize_t _last_count { 0 }; + std::vector _iovec; + std::vector _mmsgs; + std::vector _buffers; + std::vector _address; +}; +#endif + +class SocketRecvFromBuffer : public SocketRecvBuffer { +public: + SocketRecvFromBuffer(size_t size): _size(size) {} + + ssize_t recvFromSocket(int fd, ssize_t &count) override { + ssize_t nread; + socklen_t len = sizeof(_address); + if (!_buffer) { + allocBuffer(); + } + + do { + nread = recvfrom(fd, _buffer->data(), _buffer->getCapacity() - 1, 0, (struct sockaddr *)&_address, &len); + } while (-1 == nread && UV_EINTR == get_uv_error(true)); + + if (nread > 0) { + count = 1; + _buffer->data()[nread] = '\0'; + std::static_pointer_cast(_buffer)->setSize(nread); + } + return nread; + } + + Buffer::Ptr &getBuffer(size_t index) override { return _buffer; } + + struct sockaddr_storage &getAddress(size_t index) override { return _address; } + +private: + void allocBuffer() { + auto buf = BufferRaw::create(); + buf->setCapacity(_size); + _buffer = std::move(buf); + } + +private: + size_t _size; + Buffer::Ptr _buffer; + struct sockaddr_storage _address; +}; + +static constexpr auto kPacketCount = 32; +static constexpr auto kBufferCapacity = 4 * 1024u; + +SocketRecvBuffer::Ptr SocketRecvBuffer::create(bool is_udp) { +#if defined(__linux) || defined(__linux__) + if (is_udp) { + return std::make_shared(kPacketCount, kBufferCapacity); + } +#endif + return std::make_shared(kPacketCount * kBufferCapacity); +} + +} //toolkit diff --git a/3rdpart/ZLToolKit/src/Network/BufferSock.h b/3rdpart/ZLToolKit/src/Network/BufferSock.h new file mode 100644 index 0000000..1320f85 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Network/BufferSock.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLTOOLKIT_BUFFERSOCK_H +#define ZLTOOLKIT_BUFFERSOCK_H + +#if !defined(_WIN32) +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include "Util/util.h" +#include "Util/List.h" +#include "Util/ResourcePool.h" +#include "sockutil.h" +#include "Buffer.h" + +namespace toolkit { + +#if !defined(IOV_MAX) +#define IOV_MAX 1024 +#endif + +class BufferSock : public Buffer { +public: + using Ptr = std::shared_ptr; + BufferSock(Buffer::Ptr ptr, struct sockaddr *addr = nullptr, int addr_len = 0); + ~BufferSock() override = default; + + char *data() const override; + size_t size() const override; + const struct sockaddr *sockaddr() const; + socklen_t socklen() const; + +private: + int _addr_len = 0; + struct sockaddr_storage _addr; + Buffer::Ptr _buffer; +}; + +class BufferList : public noncopyable { +public: + using Ptr = std::shared_ptr; + using SendResult = toolkit::function_safe; + + BufferList() = default; + virtual ~BufferList() = default; + + virtual bool empty() = 0; + virtual size_t count() = 0; + virtual ssize_t send(int fd, int flags) = 0; + + static Ptr create(List > list, SendResult cb, bool is_udp); + +private: + //对象个数统计 [AUTO-TRANSLATED:3b43e8c2] + //Object count statistics + ObjectStatistic _statistic; +}; + +class SocketRecvBuffer { +public: + using Ptr = std::shared_ptr; + + virtual ~SocketRecvBuffer() = default; + + virtual ssize_t recvFromSocket(int fd, ssize_t &count) = 0; + virtual Buffer::Ptr &getBuffer(size_t index) = 0; + virtual struct sockaddr_storage &getAddress(size_t index) = 0; + + static Ptr create(bool is_udp); +}; + +} +#endif //ZLTOOLKIT_BUFFERSOCK_H diff --git a/3rdpart/ZLToolKit/src/Network/Kcp.cpp b/3rdpart/ZLToolKit/src/Network/Kcp.cpp new file mode 100644 index 0000000..4363e58 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Network/Kcp.cpp @@ -0,0 +1,911 @@ +/* + * Copyright (c) 2021 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "Kcp.h" +#include "Util/Byte.hpp" + +using namespace std; + +namespace toolkit { + +static inline uint32_t _imin_(uint32_t a, uint32_t b) { + return a <= b ? a : b; +} + +static inline uint32_t _imax_(uint32_t a, uint32_t b) { + return a >= b ? a : b; +} + +static inline uint32_t _ibound_(uint32_t lower, uint32_t middle, uint32_t upper) { + return _imin_(_imax_(lower, middle), upper); +} + +static inline long _itimediff(uint32_t later, uint32_t earlier) { + return ((int32_t)(later - earlier)); +} + +uint32_t getCurrent() { + return (uint32_t)(getCurrentMillisecond() & 0xfffffffful); +} + +//////////// KcpHeader ////////////////////////// + +bool KcpHeader::loadHeaderFromData(const char *data, size_t len) { + if (HEADER_SIZE > len) { + WarnL << "data len: " << len << " too small"; + return false; + } + + int offset = 0; + _conv = Byte::Get4BytesLE((const uint8_t*)data, 0); + offset += 4; + _cmd = (Cmd)Byte::Get1Byte((const uint8_t*)data, offset); + offset += 1; + _frg = Byte::Get1Byte((const uint8_t*)data, offset); + offset += 1; + _wnd = Byte::Get2BytesLE((const uint8_t*)data, offset); + offset += 2; + _ts = Byte::Get4BytesLE((const uint8_t*)data, offset); + offset += 4; + _sn = Byte::Get4BytesLE((const uint8_t*)data, offset); + offset += 4; + _una = Byte::Get4BytesLE((const uint8_t*)data, offset); + offset += 4; + _len = Byte::Get4BytesLE((const uint8_t*)data, offset); + + return true; +} + +bool KcpHeader::storeHeaderToData(char *buf, size_t size) { + if (HEADER_SIZE > size) { + ErrorL << "size too smalle " << size; + return false; + } + char *ptr = buf; + int offset = 0; + Byte::Set4BytesLE((uint8_t*)buf, offset, _conv); + offset += 4; + Byte::Set1Byte((uint8_t*)buf, offset, (uint8_t)_cmd); + offset += 1; + Byte::Set1Byte((uint8_t*)buf, offset, _frg); + offset += 1; + Byte::Set2BytesLE((uint8_t*)buf, offset, _wnd); + offset += 2; + Byte::Set4BytesLE((uint8_t*)buf, offset, _ts); + offset += 4; + Byte::Set4BytesLE((uint8_t*)buf, offset, _sn); + offset += 4; + Byte::Set4BytesLE((uint8_t*)buf, offset, _una); + offset += 4; + Byte::Set4BytesLE((uint8_t*)buf, offset, _len); + + return true; +} + +//////////// KcpPacket ////////////////////////// + +KcpPacket::~KcpPacket() { +} + +KcpPacket::Ptr KcpPacket::parse(const char* data, size_t len) { + auto packet = std::make_shared(); + if (packet->loadFromData(data, len)) { + return packet; + } + return nullptr; +} + +bool KcpPacket::loadFromData(const char *data, size_t len) { + + if (!loadHeaderFromData(data, len)) { + return false; + } + + auto packetSize = getPacketSize(); + if (len < packetSize) { + WarnL << "data len: " << len << " is smaller than packet len: " << packetSize; + return false; + } + + assign((const char *)(data), packetSize); + return true; +} + +bool KcpPacket::storeToData() { + return storeHeaderToData(data(), size()); +} + +//////////// KcpTransport ////////////////////////// + +KcpTransport::KcpTransport(bool server_mode) { + _server_mode = server_mode; + if (!server_mode) { + //客户端 conv 随机生成 + _conv = makeRandNum(); + _conv_init = true; + } + _buffer_pool = BufferRaw::create(_mtu); +} + +KcpTransport::KcpTransport(bool server_mode, const EventPoller::Ptr &poller) +: KcpTransport(server_mode) { + _poller = poller ? poller : EventPollerPool::Instance().getPoller(); +} + +KcpTransport::~KcpTransport() { + update(); +} + +ssize_t KcpTransport::send(const Buffer::Ptr& buf, bool flush) { + if (!_timer) { + startTimer(); + } + + if (!_conv_init) { + WarnL << "conv should set before send"; + return -1; + } + + auto size = buf->size(); + if (size <= 0) { + return 0; + } + + if (size >= _mss * IKCP_WND_RCV) { + WarnL << "size : "<< size << "over size, send fail"; + //分片过大,拒绝发送 + return -1; + } + + auto cache = BufferRaw::create(size); + cache->assign(buf->data(), size); + + _poller->async([=] { + auto data = cache->data(); + auto leftLen = size; + auto extendLen = mergeSendQueue(data, leftLen); + data += extendLen; + leftLen -= extendLen; + + // fragment + int count = (leftLen + _mss - 1) / _mss; + for (int i = 0; i < count; i++) { + auto len = std::min(leftLen, _mss); + auto packet = std::make_shared(_conv, len); + memcpy(packet->getPayloadData(), data, len); + packet->setFrg(!_stream? (count - i - 1) : 0); + _snd_queue.push_back(packet); + + data += len; + leftLen -= len; + } + + if (flush) { + update(); + // sendSendQueue(); + } + + }, true); + return size; +} + +void KcpTransport::input(const Buffer::Ptr& buf) { + if (!_timer) { + startTimer(); + } + + auto cache = BufferRaw::create(buf->size()); + cache->assign(buf->data(), buf->size()); + + _poller->async([=] { + // DebugL << hexdump(cache->data(), cache->size()); + + auto data = cache->data(); + auto size = cache->size(); + uint32_t current = getCurrent(); + uint32_t prev_una = _snd_una; + uint32_t maxack = 0; + uint32_t latest_ts = 0; + bool fastAckFlag = false; + bool hasData = false; + + while (size) { + auto packet = KcpPacket::parse(data, size); + if (!packet) { + WarnL << "parse kcp packet fail"; + break; + } + data += packet->size(); + size -= packet->size(); + if (!_conv_init) { + _conv = packet->getConv(); + _conv_init = true; + } else { + if (_conv != packet->getConv()) { + WarnL << "_conv check fail, skip this packet"; + continue; + } + } + + auto cmd = packet->getCmd(); + if (cmd != KcpHeader::Cmd::CMD_PUSH && cmd != KcpHeader::Cmd::CMD_ACK && + cmd != KcpHeader::Cmd::CMD_WASK && cmd != KcpHeader::Cmd::CMD_WINS) { + WarnL << "unknow cmd: " << (uint8_t)cmd; + continue; + } + + handleAnyPacket(packet); + + switch (cmd) { + case KcpHeader::Cmd::CMD_ACK: { + auto sn = packet->getSn(); + auto ts = packet->getTs(); + handleCmdAck(packet, current); + if (!fastAckFlag) { + fastAckFlag = true; + maxack = sn; + latest_ts = ts; + } else { + if (sn > maxack) { + if (!_fastack_conserve || ts > latest_ts) { + //激进模式 + maxack = sn; + latest_ts = ts; + } + } + } + } + break; + case KcpHeader::Cmd::CMD_PUSH: + handleCmdPush(packet); + hasData = true; + break; + case KcpHeader::Cmd::CMD_WASK: + _probe |= IKCP_ASK_TELL; + break; + case KcpHeader::Cmd::CMD_WINS: + break; + default: + WarnL << "unknow cmd: " << (uint32_t)cmd; + break; + } + } + + if (fastAckFlag) { + updateFastAck(maxack, latest_ts); + } + + if (_snd_una > prev_una) { + //有新的应答,尝试增大拥塞窗口 + increaseCwnd(); + } + + if (hasData) { + onData(); + } + + }, true); + + return; +} + +void KcpTransport::startTimer() { + if (!_poller) { + _poller = EventPollerPool::Instance().getPoller(); + } + + std::weak_ptr weak_self = std::static_pointer_cast(shared_from_this()); + float interval = float(_interval)/ 1000.0; + _timer = std::make_shared(interval, [weak_self]() -> bool { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return false; + } + strong_self->update(); + return true; + }, _poller); + return; +} + +void KcpTransport::onData() { + bool fastRecover = false; + + sortRecvBuf(); + + if (_rcv_queue.size() >= _rcv_wnd) { + //接受队列当前超过接收窗口大小 + fastRecover = true; + } + + // merge fragment + while (int size = peeksize()) { + while (1) { + int offset = 0; + auto buffer = BufferRaw::create(size); + buffer->setSize(size); + auto packet = _rcv_queue.front(); + _rcv_queue.pop_front(); + memcpy(buffer->data() + offset, packet->getPayloadData(), packet->getLen()); + offset += packet->getLen(); + + if (packet->getFrg() == 0) { + onRead(buffer); + break; + } + } + } + + // fast recover + if (_rcv_queue.size() < _rcv_wnd && fastRecover) { + // ready to send back IKCP_CMD_WINS + // tell remote my window size + _probe |= IKCP_ASK_TELL; + } + return; +} + +int KcpTransport::peeksize() { + if (_rcv_queue.empty()) { + return 0; + } + + //分包数据还没发送完全 + if (_rcv_queue.size() < _rcv_queue.front()->getFrg() + 1) { + return 0; + } + + int length = 0; + for (auto it = _rcv_queue.begin(); it != _rcv_queue.end(); it++) { + auto seg = *it; + length += seg->getLen(); + if (seg->getFrg() == 0) { + break; + } + } + + return length; +} + +// move available data from rcv_buf -> rcv_queue +void KcpTransport::sortRecvBuf() { +#if 0 + //直送应用层,不考虑接受队列满的情况 + if (_rcv_queue.size() >= _rcv_wnd) { + //接收队列满 + return; + } +#endif + + while (!_rcv_buf.empty()) { + auto packet = _rcv_buf.front(); + if (packet->getSn() == _rcv_nxt) { + //接收缓存中序号正确,且接受队列窗口足够 + //将接收缓存中的包转到接受队列中 + _rcv_buf.pop_front(); + _rcv_queue.push_back(packet); + _rcv_nxt++; + } else { + break; + } + } + + return; +} + +// move data from snd_queue to snd_buf +void KcpTransport::sortSendQueue() { + uint32_t current = getCurrent(); + + uint32_t cwnd = _imin_(_snd_wnd, _rmt_wnd); + if (!_nocwnd) { + cwnd = _imin_(_cwnd, cwnd); + } + cwnd = _imax_(1, cwnd); + + while (!_snd_queue.empty()) { + if (_snd_nxt >= _snd_una + cwnd) { + // WarnL << "snd cwnd over size"; + break; + } + + auto packet = _snd_queue.front(); + _snd_queue.pop_front(); + + packet->setConv(_conv); + packet->setCmd(KcpHeader::Cmd::CMD_PUSH); + packet->setSn(_snd_nxt++); + packet->setXmit(0); + packet->setFastack(0); +#if 0 + packet->setTs(current); + packet->setWnd(getWaitSnd()); + packet->setUna(_rcv_nxt); + + packet->setResendts(current); + packet->setRto(_rx_rto); +#endif + + _snd_buf.push_back(packet); + } + return; +} + +size_t KcpTransport::mergeSendQueue(const char *buffer, size_t len) { + if (len <= 0) { + return 0; + } + + // 流发送模式,表示可以将当前buffer合并之前的包后面 + if (!_stream) { + return 0; + } + + //发送队列没有数据,不用合并 + if (_snd_queue.empty()) { + return 0; + } + + auto packet = _snd_queue.front(); + size_t oldLen = packet->getLen(); + if (oldLen >= _mss) { + //前一个包已经达到_mss长度,不允许合并 + return 0; + } + + size_t extendLen = std::min(len, _mss - oldLen); + packet->setPayLoadSize(oldLen + extendLen); + memcpy(packet->getPayloadData() + oldLen, buffer, extendLen); + packet->setLen(oldLen + extendLen); + packet->setFrg(0); + return extendLen; +} + +void KcpTransport::updateRtt(int32_t rtt) { + if (rtt < 0) { + return; + } + + int32_t rto = 0; + //Jacobson/Karels RTT估算算法 + if (_rx_srtt == 0) { + _rx_srtt = rtt; + _rx_rttval = rtt / 2; + } else { + long delta = abs(rtt - _rx_srtt); + _rx_rttval = (3 * _rx_rttval + delta) / 4; + _rx_srtt = (7 * _rx_srtt + rtt) / 8; + if (_rx_srtt < 1) { + _rx_srtt = 1; + } + } + + rto = _rx_srtt + _imax_(_interval, 4 * _rx_rttval); + _rx_rto = _ibound_(_rx_minrto, rto, IKCP_RTO_MAX); + + return; +} + +void KcpTransport::dropCacheByUna(uint32_t una) { + // TraceL << "recv una: " << una; + if (una <= _snd_una) { + return; + } + + while (!_snd_buf.empty()) { + if (una <= _snd_buf.front()->getSn()) { + break; + } + _snd_buf.pop_front(); + } + + _snd_una = _snd_buf.empty()? _snd_nxt : _snd_buf.front()->getSn(); + return; +} + +void KcpTransport::dropCacheByAck(uint32_t sn) { + // TraceL << "recv ack sn: " << sn; + if (sn < _snd_una) { + return; + } + + for (auto it = _snd_buf.begin(); it != _snd_buf.end(); it++) { + if (sn < (*it)->getSn()) { + break; + } else if (sn == (*it)->getSn()) { + _snd_buf.erase(it); + break; + } + } + + _snd_una = _snd_buf.empty()? _snd_nxt : _snd_buf.front()->getSn(); + return; +} + +void KcpTransport::updateFastAck(uint32_t sn, uint32_t ts) { + if (sn < _snd_una || sn >= _snd_nxt) { + return; + } + + for (auto it = _snd_buf.begin(); it != _snd_buf.end(); it++) { + auto seg = *it; + if (sn < seg->getSn()) { + break; + } else if (sn != seg->getSn()) { + if (!_fastack_conserve || ts > seg->getTs()) { + seg->setFastack(seg->getFastack() + 1); + } + } + } + return; +} + +void KcpTransport::increaseCwnd() { + if (_cwnd >= _rmt_wnd) { + return; + } + + uint32_t mss = _mss; + if (_cwnd < _ssthresh) { + //慢启动阶段,拥塞窗口指数增长 + _cwnd++; + _incr += mss; + } else { + //拥塞避免阶段,拥塞窗口线性增长 + if (_incr < mss) { + _incr = mss; + } + + _incr += (mss * mss) / _incr + (mss / 16); + if ((_cwnd + 1) * mss <= _incr) { +#if 1 + _cwnd = (_incr + mss - 1) / ((mss > 0)? mss : 1); +#else + _cwnd++; +#endif + } + } + + //控制不超过远端窗口大小 + if (_cwnd > _rmt_wnd) { + _cwnd = _rmt_wnd; + _incr = _rmt_wnd * mss; + } + return; +} + +void KcpTransport::handleAnyPacket(KcpPacket::Ptr packet) { + _rmt_wnd = packet->getWnd(); + dropCacheByUna(packet->getUna()); + return; +} + +void KcpTransport::handleCmdAck(KcpPacket::Ptr packet, uint32_t current) { + updateRtt(current - packet->getTs()); + dropCacheByAck(packet->getSn()); + return; +} + +void KcpTransport::handleCmdPush(KcpPacket::Ptr packet) { + auto sn = packet->getSn(); + auto ts = packet->getTs(); + // TraceL << "recv packet sn: " << sn << ", frg: " << (uint32_t)packet->getFrg(); + + if (sn >= _rcv_nxt + _rcv_wnd) { + // TraceL << "sn: " << sn << " is over wnd, _rcv_nxt: " << _rcv_nxt << ":, skip"; + //超出接受窗口数据 + return; + } + + _acklist.push_back(std::make_pair(sn, ts)); + if (sn < _rcv_nxt) { + // TraceL << "sn: " << sn << " is smaller than _rcv_nxt: " << _rcv_nxt << ":, skip"; + return; + } + + for (auto it = _rcv_buf.begin(); it != _rcv_buf.end(); it++) { + auto old = *it; + if (old->getSn() == sn) { + // TraceL << "sn: " << sn << " is repeat skip"; + return; + } + + if (old->getSn() > sn) { + _rcv_buf.insert(it, packet); + return; + } + } + + _rcv_buf.push_back(packet); + + return; +} + +//获取当前空闲接受队列窗口 +int KcpTransport::getRcvWndUnused() { + auto wnd = _rcv_wnd - _rcv_queue.size(); + if (wnd > 0) { + return wnd; + } + return 0; +} + + +void KcpTransport::update() { + sendAckList(); + sendProbePacket(); + sendSendQueue(); +} + +void KcpTransport::sendSendQueue() { + uint32_t resent; + uint32_t rtomin; + bool change = false; + bool lost = false; + uint32_t current = getCurrent(); + + sortSendQueue(); + + // calculate resent + resent = (_fastresend > 0)? (uint32_t)_fastresend : 0xffffffff; + rtomin = (_delay_mode == DelayMode::DELAY_MODE_NORMAL)? (_rx_rto >> 3) : 0; + + // flush data segments + for (auto it = _snd_buf.begin(); it != _snd_buf.end(); it++) { + bool needsend = false; + + auto packet = *it; + auto xmit = packet->getXmit(); + //没重传过,第一次发送数据包 + if (xmit == 0) { + // TraceL << "normal send sn: " << packet->getSn(); + needsend = true; + packet->setXmit(xmit + 1); + packet->setRto(_rx_rto); + packet->setResendts(current + _rx_rto + rtomin); + } else if (current >= packet->getResendts()) { + //普通重传 + // TraceL << "resend sn: " << packet->getSn() << ", xmit: " << packet->getXmit(); + needsend = true; + packet->setXmit(xmit + 1); + _xmit++; + auto rto = packet->getRto(); + if (_delay_mode == DelayMode::DELAY_MODE_NORMAL == 0) { + packet->setRto(rto + _imax_(rto, (uint32_t)_rx_rto)); + } else { + int32_t step = (_delay_mode == DelayMode::DELAY_MODE_FAST)? ((int32_t)(rto)) : _rx_rto; + packet->setRto(rto + step / 2); + } + packet->setResendts(current + rto); + lost = true; + } else if (packet->getFastack() >= resent) { + //快速重传 + if ((int)xmit <= _fastlimit || _fastlimit <= 0) { + // TraceL << "fast resend sn: " << packet->getSn() << ", xmit: " << packet->getXmit(); + auto rto = packet->getRto(); + needsend = true; + packet->setXmit(xmit + 1); + packet->setFastack(0); + packet->setResendts(current + rto); + change = true; + } + } + + if (needsend) { + int need; + packet->setTs(current); + packet->setWnd(getRcvWndUnused()); + packet->setUna(_rcv_nxt); + sendPacket(packet); + + if (packet->getXmit() >= _dead_link) { + onErr(SockException(Err_other, + (StrPrinter << "resend time : " << packet->getXmit() << " over " << _dead_link))); + } + } + } + + flushPool(); + + decreaseCwnd(change, lost); + return; +} + +void KcpTransport::sendAckList() { + while (!_acklist.empty()) { + auto front = _acklist.front(); + _acklist.pop_front(); + + auto packet = std::make_shared(_conv); + packet->setWnd(getRcvWndUnused()); + packet->setUna(_rcv_nxt); + packet->setSn(front.first); + packet->setTs(front.second); + sendPacket(packet); + // TraceL << "send ack sn: " << packet->getSn() << ", una: " << _rcv_nxt; + } + return; +} + +void KcpTransport::sendProbePacket() { + uint32_t current = getCurrent(); + + // probe window size (if remote window size equals zero) + if (_rmt_wnd == 0) { + if (_probe_wait == 0) { + _probe_wait = IKCP_PROBE_INIT; + _ts_probe = current + _probe_wait; + } else { + if (_itimediff(current, _ts_probe) >= 0) { + if (_probe_wait < IKCP_PROBE_INIT) { + _probe_wait = IKCP_PROBE_INIT; + } + _probe_wait += _probe_wait / 2; + if (_probe_wait > IKCP_PROBE_LIMIT) { + _probe_wait = IKCP_PROBE_LIMIT; + } + _ts_probe = current + _probe_wait; + _probe |= IKCP_ASK_SEND; + } + } + } else { + _ts_probe = 0; + _probe_wait = 0; + } + + // flush window probing commands + if (_probe & IKCP_ASK_SEND) { + auto packet = std::make_shared(_conv); + sendPacket(packet); + } + + // flush window probing commands + if (_probe & IKCP_ASK_TELL) { + auto packet = std::make_shared(_conv); + sendPacket(packet); + } + + _probe = 0; + return; +} + +int KcpTransport::getWaitSnd() { + return _snd_buf.size() + _snd_queue.size(); +} + +// update ssthresh +void KcpTransport::decreaseCwnd(bool change, bool lost) { + //处理因为快速重传或者丢包的情况下,进行拥塞窗口处理 + + uint32_t resent = (_fastresend > 0)? (uint32_t)_fastresend : 0xffffffff; + + // calculate window size + uint32_t cwnd = _imin_(_snd_wnd, _rmt_wnd); + if (_nocwnd == 0) { + cwnd = _imin_(_cwnd, cwnd); + } + + //快速重传表明网络出现轻微拥塞,采用相对温和的调整策略。 + //主动降低发送速率,但不是因为实际的丢包(可能是乱序) + if (change) { + //调整慢启动阈值为在途数据量的一半 + uint32_t inflight = _snd_nxt - _snd_una; + _ssthresh = inflight / 2; + if (_ssthresh < IKCP_THRESH_MIN) { + _ssthresh = IKCP_THRESH_MIN; + } + _cwnd = _ssthresh + resent; + _incr = _cwnd * _mss; + } + + //超时重传表明网络严重拥塞,采用激进的调整策略。 + if (lost) { + _ssthresh = cwnd / 2; + if (_ssthresh < IKCP_THRESH_MIN) { + _ssthresh = IKCP_THRESH_MIN; + } + //重置拥塞窗口,回到慢启动阶段 + _cwnd = 1; + _incr = _mss; + } + + if (_cwnd < 1) { + _cwnd = 1; + _incr = _mss; + } + return; +} + +void KcpTransport::setMtu(int mtu) { + if (mtu < 50 || mtu < KcpHeader::HEADER_SIZE) { + std::string err = (StrPrinter << "kcp setMtu " << mtu << "to small"); + throw std::runtime_error(err); + } + + _mtu = mtu; + _mss = _mtu - KcpHeader::HEADER_SIZE; + return; +} + +void KcpTransport::setInterval(int interval) { + _interval = _ibound_(10, interval, 5000); + return; +} + +void KcpTransport::setRxMinrto(int rx_minrto) { + _rx_minrto = rx_minrto; + return; +} + +void KcpTransport::setDelayMode(DelayMode delay_mode) { + if (delay_mode < DelayMode::DELAY_MODE_NORMAL + || delay_mode > DelayMode::DELAY_MODE_NO_DELAY) { + return; + } + + _delay_mode = delay_mode; + if (delay_mode == DelayMode::DELAY_MODE_NORMAL) { + _rx_minrto = IKCP_RTO_MIN; + } else { + _rx_minrto = IKCP_RTO_NDL; + } + return; +} + +void KcpTransport::setFastackConserve(bool flag) { + _fastack_conserve = flag; + return; +} + +void KcpTransport::setNoCwnd(bool flag) { + _nocwnd = flag; + return; +} + +void KcpTransport::setStreamMode(bool flag) { + _stream = flag; + return; +} + +void KcpTransport::setFastResend(int resend) { + _fastresend = resend; + return; +} + +void KcpTransport::setWndSize(int sndwnd, int rcvwnd) { + if (sndwnd > 0) { + _snd_wnd = sndwnd; + } + if (rcvwnd > 0) { // must >= max fragment size + _rcv_wnd = _imax_(rcvwnd, IKCP_WND_RCV); + } + return; +} + +void KcpTransport::sendPacket(KcpPacket::Ptr pkt, bool flush) { + pkt->storeToData(); + if (pkt->size() + _buffer_pool->size() > _mtu) { + flushPool(); + } + + memcpy(_buffer_pool->data() + _buffer_pool->size(), pkt->data(), pkt->size()); + _buffer_pool->setSize(_buffer_pool->size() + pkt->size()); + + if (flush) { + flushPool(); + } + return; +} + +void KcpTransport::flushPool() { + onWrite(_buffer_pool); + _buffer_pool->setSize(0); +} + +} // namespace toolkit diff --git a/3rdpart/ZLToolKit/src/Network/Kcp.h b/3rdpart/ZLToolKit/src/Network/Kcp.h new file mode 100644 index 0000000..9251b71 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Network/Kcp.h @@ -0,0 +1,385 @@ +/* + * Copyright (c) 2021 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + * + * code reference github.com/skywind3000/kcp/releases/tag/1.7. + */ + +#ifndef TOOLKIT_NETWORK_KCP_H +#define TOOLKIT_NETWORK_KCP_H + +#include "Network/Buffer.h" +#include "Network/sockutil.h" +#include "Poller/EventPoller.h" +#include "Poller/Timer.h" +#include "Util/TimeTicker.h" +#include "Socket.h" + +namespace toolkit { + +class KcpHeader { +public: + static const size_t HEADER_SIZE = 24; + + enum class Cmd : uint8_t { + CMD_PUSH = 81, // cmd: push data + CMD_ACK = 82, // cmd: ack + CMD_WASK = 83, // cmd: window probe (ask) + CMD_WINS = 84, // cmd: window size (tell) + }; + + uint32_t _conv; // 会话ID,用于标识一个会话 + Cmd _cmd; // 命令字段,用于标识数据包类型 + uint8_t _frg = 0; // 分片序号,用于消息分片,0表示最后一片 + uint16_t _wnd; // 接受窗口大小 + uint32_t _ts; // 时间戳,2^32ms,约49.7天会溢出一次 + uint32_t _sn; // 序列号 + uint32_t _una; // 待接收的第一个未确认包序号 + uint32_t _len = 0; // payload部分数据长度(不包含头长度) + +public: + + // Getters for KcpHeader members + uint32_t getConv() const { return _conv; } + Cmd getCmd() const { return _cmd; } + uint8_t getFrg() const { return _frg; } + uint16_t getWnd() const { return _wnd; } + uint32_t getTs() const { return _ts; } + uint32_t getSn() const { return _sn; } + uint32_t getUna() const { return _una; } + uint32_t getLen() const { return _len; } + + // Setters for KcpHeader members + void setConv(uint32_t conv) { _conv = conv; } + void setCmd(Cmd cmd) { _cmd = cmd; } + void setFrg(uint8_t frg) { _frg = frg; } + void setWnd(uint16_t wnd) { _wnd = wnd; } + void setTs(uint32_t ts) { _ts = ts; } + void setSn(uint32_t sn) { _sn = sn; } + void setUna(uint32_t una) { _una = una; } + void setLen(uint32_t len) { _len = len; } + + uint32_t getPacketSize() const { return _len + HEADER_SIZE; } + bool loadHeaderFromData(const char *data, size_t len); + bool storeHeaderToData(char *buf, size_t size); +}; + +class KcpPacket : public KcpHeader, public toolkit::BufferRaw { +public: + using Ptr = std::shared_ptr; + + static KcpPacket::Ptr parse(const char* data, size_t len); + + KcpPacket() {}; + KcpPacket(uint32_t conv, Cmd cmd, size_t payloadSize) { + setConv(conv); + setCmd(cmd); + setPayLoadSize(payloadSize); + }; + + KcpPacket(size_t payloadSize) { + setPayLoadSize(payloadSize); + } + + virtual ~KcpPacket(); + + bool storeToData(); + + char *getPayloadData() { + return data() + HEADER_SIZE; + }; + + uint32_t getResendts() const { return _resendts; } + uint32_t getRto() const { return _rto; } + uint32_t getFastack() const { return _fastack; } + uint32_t getXmit() const { return _xmit; } + + void setResendts(uint32_t resendts) { _resendts = resendts; } + void setRto(uint32_t rto) {_rto = rto; } + void setFastack(uint32_t fastack) { _fastack = fastack; } + void setXmit(uint32_t xmit) { _xmit = xmit; } + + void setPayLoadSize(size_t len) { + setCapacity(len + HEADER_SIZE + 1); + setSize(len + HEADER_SIZE); + setLen(len); + } + +protected: + bool loadFromData(const char *data, size_t len); + +private: + uint32_t _resendts; // 重传超时时间戳,表示该数据包下次重传的时间戳 + uint32_t _rto; // 超时重传时间,表示数据包在多长时间没收到ACK就重传,会基于rtt动态调整 + uint32_t _fastack; // 快速确认计数器 + uint32_t _xmit; // 传输次数,用于统计重传次数 +}; + +//数据包 +class KcpDataPacket : public KcpPacket { +public: + KcpDataPacket(uint32_t conv, size_t payloadSize) + : KcpPacket(conv, KcpHeader::Cmd::CMD_WASK, payloadSize) { + } +}; + +//ACK包 +class KcpAckPacket : public KcpPacket { +public: + KcpAckPacket(uint32_t conv) + : KcpPacket(conv, KcpHeader::Cmd::CMD_ACK, 0) { + } +}; + +//探测窗口大小包 +class KcpProbePacket : public KcpPacket { +public: + KcpProbePacket(uint32_t conv) + : KcpPacket(conv, KcpHeader::Cmd::CMD_WASK, 0) { + } + +}; + +//告知窗口大小包 +class KcpTellPacket : public KcpPacket { +public: + KcpTellPacket(uint32_t conv) + : KcpPacket(conv, KcpHeader::Cmd::CMD_WINS, 0) { + } +}; + +//可以根据实际需要调整参数 +//参考kcp V.1.7实现由以下推荐模式和参数 +//默认,开启流控: setDelayMode(DELAY_MODE_NORMAL); setInterval(10); setFastResend(0); setNoCwnd(false) +//普通,关闭流控: setDelayMode(DELAY_MODE_NORMAL); setInterval(10); setFastResend(0); setNoCwnd(true) +//快速,关闭流控: setDelayMode(DELAY_MODE_NO_DELAY); setInterval(10); setFastResend(1); setNoCwnd(true); setRxMinrto(10) +class KcpTransport : public std::enable_shared_from_this { +public: + using Ptr = std::shared_ptr; + + enum DelayMode { + DELAY_MODE_NORMAL = 0, // 正常模式, 每次重发rto翻倍,往外增加12.5%的最小rto + DELAY_MODE_FAST = 1, // 快速模式, 每次重发rto增加当前包rto的一半,不额外增加延时 + DELAY_MODE_NO_DELAY = 2, // 极速模式, 每次重发rto增加基础rto的一半,不额外增加延时 + }; + + static const uint32_t IKCP_ASK_SEND = 1; // need to send IKCP_CMD_WASK + static const uint32_t IKCP_ASK_TELL = 2; // need to send IKCP_CMD_WINS + + static const uint32_t IKCP_RTO_NDL = 30; // no delay min rto + static const uint32_t IKCP_RTO_MIN = 100; // normal min rto + static const uint32_t IKCP_RTO_DEF = 200; + static const uint32_t IKCP_RTO_MAX = 60000; + + static const uint32_t IKCP_WND_SND = 32; + static const uint32_t IKCP_WND_RCV = 128; // must >= max fragment size + static const uint32_t IKCP_MTU_DEF = 1400; + static const uint32_t IKCP_ACK_FAST = 3; + static const uint32_t IKCP_INTERVAL = 100; + static const uint32_t IKCP_THRESH_INIT = 2; + static const uint32_t IKCP_THRESH_MIN = 2; + static const uint32_t IKCP_PROBE_INIT = 7000; // 7 secs to probe window size + static const uint32_t IKCP_PROBE_LIMIT = 120000; // up to 120 secs to probe window + + using onReadCB = std::function; + using onWriteCB = std::function; + using OnErr = std::function; + + KcpTransport(bool serverMode); + KcpTransport(bool serverMode, const EventPoller::Ptr &poller); + virtual ~KcpTransport(); + + void setOnRead(onReadCB cb) { _on_read = std::move(cb); } + void setOnWrite(onWriteCB cb) { _on_write = std::move(cb); } + void setOnErr(OnErr cb) { _on_err = std::move(cb); } + + void setPoller(const EventPoller::Ptr &poller) { + _poller = poller ? poller : EventPollerPool::Instance().getPoller(); + } + + // 应用层将数据放到发送队列中 + ssize_t send(const Buffer::Ptr &buf, bool flush = false); + + // 应用层将socket层接收到的数据输入 + void input(const Buffer::Ptr &buf); + + // change MTU size, default is 1400 + void setMtu(int mtu); + + void setInterval(int intervoal); + + void setRxMinrto(int rx_minrto); + + // set maximum window size: sndwnd=32, rcvwnd=32 by default + void setWndSize(int sndwnd, int rcvwnd); + + //设置低延时模式 + //默认DELAY_MODE_NORMAL + void setDelayMode(DelayMode delay_mode); + + //设置快速重传的阈值 + //默认0,即不会快速重传 + void setFastResend(int resend); + + //设置快速重传保守模式 + //默认保守模式 + void setFastackConserve(bool flag); + + //设置是否关闭拥塞控制 + //默认开启 + void setNoCwnd(bool flag); + + //设置是否开启流传输模式 + //默认不开启 + void setStreamMode(bool flag); + +protected: + + void onWrite(const Buffer::Ptr &buf) { + if (_on_write) { + _on_write(buf); + } + } + + void onRead(const Buffer::Ptr &buf) { + if (_on_read) { + _on_read(buf); + } + } + + void onErr(const SockException &err) { + DebugL; + if (_on_err) { + _on_err(err); + } + } + + void startTimer(); + + //处理收到的数据,rcv_buf中有新数据时调用 + void onData(); + + //测量rcv_queue 下一个可以提取的包的长度 + int peeksize(); + + void handleAnyPacket(KcpPacket::Ptr packet); + void handleCmdAck(KcpPacket::Ptr packet, uint32_t current); + void handleCmdPush(KcpPacket::Ptr packet); + + // move available data from rcv_buf -> rcv_queue + void sortRecvBuf(); + void sortSendQueue(); + //流模式,合并发送包 + size_t mergeSendQueue(const char *buffer, size_t len); + + // 将发送队列的数据真正发送出去 + void update(); + void sendSendQueue(); + void sendAckList(); + void sendProbePacket(); + void sendPacket(KcpPacket::Ptr pkt, bool flush = false); + void flushPool(); + + //将发送缓存中对端已经确认的数据包丢弃 + //UNA模式,指定序列之前的包都已经确认,可以Drop + void dropCacheByUna(uint32_t una); + + //将发送缓存中对端已经确认的数据包丢弃 + //ACK模式,仅指定序列的包被确认 + void dropCacheByAck(uint32_t sn); + + //更新rtt + void updateRtt(int32_t rtt); + + //更新发送cache中packet的Faskack计数 + void updateFastAck(uint32_t sn, uint32_t ts); + + //扩大拥塞窗口 + void increaseCwnd(); + + //缩小拥塞窗口 + void decreaseCwnd(bool change, bool lost); + + // get how many packet is waiting to be sent + int getWaitSnd(); + + int getRcvWndUnused(); + +private: + onReadCB _on_read = nullptr; + onWriteCB _on_write = nullptr; + OnErr _on_err = nullptr; + + bool _server_mode; + bool _conv_init = false; + + EventPoller::Ptr _poller = nullptr; + Timer::Ptr _timer; + //刷新计时器 + Ticker _alive_ticker; + + bool _fastack_conserve = false; //快速重传保守模式 + + uint32_t _conv; // 会话ID,用于标识一个会话 + uint32_t _mtu = IKCP_MTU_DEF; // 最大传输单元,默认1400 + uint32_t _mss = IKCP_MTU_DEF - KcpPacket::HEADER_SIZE; // 最大分片大小,由MTU计算得到 + + uint32_t _interval = IKCP_INTERVAL; //内部flush的率先哪个间隔 + + uint32_t _fastresend = 0; //快速重传触发阈值,当packet的_fastack超过该值时,触发快速重传 + int _fastlimit = 5; //快速重传限制,限制触发快速重传的最大次数,防止过度重传 + + uint32_t _xmit = 0; //重传次数计数器 + uint32_t _dead_link = 20; //最大重传次数,当某个包的重传次数超过该值时,认为链路断开 + + uint32_t _snd_una = 0; //发送缓冲区中第一个未确认的包序号 + uint32_t _snd_nxt = 0; //下一个待分配的序号 + uint32_t _rcv_nxt = 0; //接收队列中待接收的下一个包序号 + + uint32_t _ts_recent = 0; //最近一次收到数据包的时间戳 + uint32_t _ts_lastack = 0;//最近一次发送ACK的时间戳 + + //rtt + int32_t _rx_rttval = 0; //RTT方差 + int32_t _rx_srtt = 0; //RTT(平滑后) + int32_t _rx_rto = IKCP_RTO_DEF; //重传超时时间(会基于rtt和rtt方差动态调整) + int32_t _rx_minrto = IKCP_RTO_MIN; //最小重传超时时间,防止RTO过小 + + //for 拥塞窗口控制 + uint32_t _snd_wnd = IKCP_WND_SND; //发送队列窗口,用于限制发送速率,用户配置(单位分片数量) + uint32_t _rcv_wnd = IKCP_WND_RCV; //接收队列窗口,用于限制接收速率,用户配置(单位分片数量) + uint32_t _rmt_wnd = IKCP_WND_RCV; //对端接收缓存拥塞窗口,对端通告(单位分片数量) + uint32_t _cwnd = 1; //发送缓存拥塞窗口大小,算法动态调整(单位分片数量) + uint32_t _incr = 0; //拥塞窗口增量,用于拥塞控制算法中动态窗口大小(单位字节) + uint32_t _ssthresh = IKCP_THRESH_INIT; //慢启动阈值 + + uint32_t _probe = 0; //探测标志,用于探测对端窗口大小 + uint32_t _ts_probe = 0; //探测时间戳,记录发送窗口探测包的时间戳 + uint32_t _probe_wait = 0;//探测等待时间, 控制探测包发送的时间间隔 + + DelayMode _delay_mode = DELAY_MODE_NORMAL; + int _nocwnd = false; //是否禁用拥塞控制 + bool _stream = false; //是否开启流传输模式 + + //传输链路: userdata->_snd_queue->_snd_buf->网络发送 + //_snd_queue:无限制 + //_snd_buf: min(_snd_wnd, _rmt_wnd, _cwnd) + //传输链路: 网络接收->_rcv_buf->_snd_queue->userdata + //_rcv_buf:无限制,乱序数据暂存 + //_snd_queue: _rcv_wnd + std::list _snd_queue; //发送队列,还未进入发送窗口 + std::list _rcv_queue; //接收队列,已经接收完全的包等待交给应用层 + std::list _snd_buf; //发送缓存,已经进入发送窗口,用于重传 + std::list _rcv_buf; //接收缓存,已经接受,但是因为乱序丢包等还不能交给应用层 + //待发送的ACK列表 + std::deque>_acklist; + BufferRaw::Ptr _buffer_pool; //用于合并多个kcp包到一个udp包中 +}; +} // namespace toolkit + +#endif // TOOLKIT_NETWORK_KCP_H diff --git a/3rdpart/ZLToolKit/src/Network/Server.cpp b/3rdpart/ZLToolKit/src/Network/Server.cpp new file mode 100644 index 0000000..6330285 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Network/Server.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2021 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "Server.h" + +using namespace std; + +namespace toolkit { + +Server::Server(EventPoller::Ptr poller) { + _poller = poller ? std::move(poller) : EventPollerPool::Instance().getPoller(); +} + +//////////////////////////////////////////////////////////////////////////////////// + +SessionHelper::SessionHelper(const std::weak_ptr &server, Session::Ptr session, std::string cls) { + _server = server; + _session = std::move(session); + _cls = std::move(cls); + //记录session至全局的map,方便后面管理 [AUTO-TRANSLATED:f90fce35] + //Record the session in the global map for easy management later + _session_map = SessionMap::Instance().shared_from_this(); + _identifier = _session->getIdentifier(); + _session_map->add(_identifier, _session); +} + +SessionHelper::~SessionHelper() { + if (!_server.lock()) { + //务必通知Session已从TcpServer脱离 [AUTO-TRANSLATED:6f55a358] + //Must notify that the session has been detached from TcpServer + _session->onError(SockException(Err_other, "Server shutdown")); + } + //从全局map移除相关记录 [AUTO-TRANSLATED:f0b0b2ad] + //Remove the related record from the global map + _session_map->del(_identifier); +} + +const Session::Ptr &SessionHelper::session() const { + return _session; +} + +const std::string &SessionHelper::className() const { + return _cls; +} + +//////////////////////////////////////////////////////////////////////////////////// + +bool SessionMap::add(const string &tag, const Session::Ptr &session) { + lock_guard lck(_mtx_session); + return _map_session.emplace(tag, session).second; +} + +bool SessionMap::del(const string &tag) { + lock_guard lck(_mtx_session); + return _map_session.erase(tag); +} + +Session::Ptr SessionMap::get(const string &tag) { + lock_guard lck(_mtx_session); + auto it = _map_session.find(tag); + if (it == _map_session.end()) { + return nullptr; + } + return it->second.lock(); +} + +void SessionMap::for_each_session(const function &cb) { + lock_guard lck(_mtx_session); + for (auto it = _map_session.begin(); it != _map_session.end();) { + auto session = it->second.lock(); + if (!session) { + it = _map_session.erase(it); + continue; + } + cb(it->first, session); + ++it; + } +} + +} // namespace toolkit \ No newline at end of file diff --git a/3rdpart/ZLToolKit/src/Network/Server.h b/3rdpart/ZLToolKit/src/Network/Server.h new file mode 100644 index 0000000..88d9eba --- /dev/null +++ b/3rdpart/ZLToolKit/src/Network/Server.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2021 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLTOOLKIT_SERVER_H +#define ZLTOOLKIT_SERVER_H + +#include +#include "Util/mini.h" +#include "Session.h" + +namespace toolkit { + +// 全局的 Session 记录对象, 方便后面管理 [AUTO-TRANSLATED:1c2725cb] +//Global Session record object, convenient for later management +// 线程安全的 [AUTO-TRANSLATED:efbca605] +//Thread-safe +class SessionMap : public std::enable_shared_from_this { +public: + friend class SessionHelper; + using Ptr = std::shared_ptr; + + //单例 [AUTO-TRANSLATED:8c2c95b4] + //Singleton + static SessionMap &Instance(); + ~SessionMap() = default; + + //获取Session [AUTO-TRANSLATED:08c6e0f2] + //Get Session + Session::Ptr get(const std::string &tag); + void for_each_session(const std::function &cb); + +private: + SessionMap() = default; + + //移除Session [AUTO-TRANSLATED:b6023f67] + //Remove Session + bool del(const std::string &tag); + //添加Session [AUTO-TRANSLATED:4bdf8277] + //Add Session + bool add(const std::string &tag, const Session::Ptr &session); + +private: + std::mutex _mtx_session; + std::unordered_map > _map_session; +}; + +class Server; + +class SessionHelper { +public: + bool enable = true; + + using Ptr = std::shared_ptr; + + SessionHelper(const std::weak_ptr &server, Session::Ptr session, std::string cls); + ~SessionHelper(); + + const Session::Ptr &session() const; + const std::string &className() const; + +private: + std::string _cls; + std::string _identifier; + Session::Ptr _session; + SessionMap::Ptr _session_map; + std::weak_ptr _server; +}; + +// server 基类, 暂时仅用于剥离 SessionHelper 对 TcpServer 的依赖 [AUTO-TRANSLATED:2fe50ede] +//Server base class, temporarily only used to decouple SessionHelper from TcpServer +// 后续将 TCP 与 UDP 服务通用部分加到这里. [AUTO-TRANSLATED:3d8429f3] +//Later, the common parts of TCP and UDP services will be added here. +class Server : public std::enable_shared_from_this, public mINI { +public: + using Ptr = std::shared_ptr; + + explicit Server(EventPoller::Ptr poller = nullptr); + virtual ~Server() = default; + +protected: + EventPoller::Ptr _poller; +}; + +} // namespace toolkit + +#endif // ZLTOOLKIT_SERVER_H \ No newline at end of file diff --git a/3rdpart/ZLToolKit/src/Network/Session.cpp b/3rdpart/ZLToolKit/src/Network/Session.cpp new file mode 100644 index 0000000..cad48a6 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Network/Session.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include "Session.h" + +using namespace std; + +namespace toolkit { + +class TcpSession : public Session {}; +class UdpSession : public Session {}; + +StatisticImp(UdpSession) +StatisticImp(TcpSession) + +Session::Session(const Socket::Ptr &sock) : SocketHelper(sock) { + if (sock->sockType() == SockNum::Sock_TCP) { + _statistic_tcp.reset(new ObjectStatistic); + } else { + _statistic_udp.reset(new ObjectStatistic); + } +} + +string Session::getIdentifier() const { + if (_id.empty()) { + static atomic s_session_index{0}; + _id = to_string(++s_session_index) + '-' + to_string(getSock()->rawFD()); + } + return _id; +} + +} // namespace toolkit diff --git a/3rdpart/ZLToolKit/src/Network/Session.h b/3rdpart/ZLToolKit/src/Network/Session.h new file mode 100644 index 0000000..774cc19 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Network/Session.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2021 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLTOOLKIT_SESSION_H +#define ZLTOOLKIT_SESSION_H + +#include +#include "Socket.h" +#include "Util/util.h" +#include "Util/SSLBox.h" +#include "Kcp.h" + +namespace toolkit { + +// 会话, 用于存储一对客户端与服务端间的关系 [AUTO-TRANSLATED:d69736ea] +//Session, used to store the relationship between a client and a server +class Server; +class TcpSession; +class UdpSession; + +class Session : public SocketHelper { +public: + using Ptr = std::shared_ptr; + + Session(const Socket::Ptr &sock); + ~Session() override = default; + + /** + * 在创建 Session 后, Server 会把自身的配置参数通过该函数传递给 Session + * @param server, 服务器对象 + * After creating a Session, the Server will pass its configuration parameters to the Session through this function + * @param server, server object + + * [AUTO-TRANSLATED:5ce03e96] + */ + virtual void attachServer(const Server &server) {} + + /** + * 作为该 Session 的唯一标识符 + * @return 唯一标识符 + * As the unique identifier of this Session + * @return unique identifier + + * [AUTO-TRANSLATED:3b046f26] + */ + std::string getIdentifier() const override; + +private: + mutable std::string _id; + std::unique_ptr > _statistic_tcp; + std::unique_ptr > _statistic_udp; +}; + +// 通过该模板可以让TCP服务器快速支持TLS [AUTO-TRANSLATED:fea218e6] +//This template allows the TCP server to quickly support TLS +template +class SessionWithSSL : public SessionType { +public: + template + SessionWithSSL(ArgsType &&...args) + : SessionType(std::forward(args)...) { + _ssl_box.setOnEncData([&](const Buffer::Ptr &buf) { public_send(buf); }); + _ssl_box.setOnDecData([&](const Buffer::Ptr &buf) { public_onRecv(buf); }); + } + + ~SessionWithSSL() override { _ssl_box.flush(); } + + void onRecv(const Buffer::Ptr &buf) override { _ssl_box.onRecv(buf); } + + // 添加public_onRecv和public_send函数是解决较低版本gcc一个lambad中不能访问protected或private方法的bug [AUTO-TRANSLATED:7b16e05b] + //Adding public_onRecv and public_send functions is to solve a bug in lower versions of gcc where a lambda cannot access protected or private methods + inline void public_onRecv(const Buffer::Ptr &buf) { SessionType::onRecv(buf); } + inline void public_send(const Buffer::Ptr &buf) { SessionType::send(buf); } + + bool overSsl() const override { return true; } + +protected: + ssize_t send(Buffer::Ptr buf) override { + auto size = buf->size(); + _ssl_box.onSend(std::move(buf)); + return size; + } + +private: + SSL_Box _ssl_box; +}; + +// 通过该模板可以让UDP服务器快速支持KCP +template +class SessionWithKCP : public SessionType { +public: + template + SessionWithKCP(ArgsType &&...args) + : SessionType(std::forward(args)...) { + _kcp_box = std::make_shared(true, std::forward(args)...); + _kcp_box->setOnWrite([&](const Buffer::Ptr &buf) { public_send(buf); }); + _kcp_box->setOnRead([&](const Buffer::Ptr &buf) { public_onRecv(buf); }); + _kcp_box->setOnErr([&](const SockException &ex) { public_onErr(ex); }); + } + + ~SessionWithKCP() override { } + + void onRecv(const Buffer::Ptr &buf) override { _kcp_box->input(buf); } + + inline void public_onRecv(const Buffer::Ptr &buf) { SessionType::onRecv(buf); } + inline void public_send(const Buffer::Ptr &buf) { SessionType::send(buf); } + inline void public_onErr(const SockException &ex) { SessionType::onError(ex); } + +protected: + ssize_t send(Buffer::Ptr buf) override { + return _kcp_box->send(std::move(buf)); + } + +private: + KcpTransport::Ptr _kcp_box; +}; + +} // namespace toolkit + +#endif // ZLTOOLKIT_SESSION_H diff --git a/3rdpart/ZLToolKit/src/Network/Socket.cpp b/3rdpart/ZLToolKit/src/Network/Socket.cpp new file mode 100644 index 0000000..e1b705c --- /dev/null +++ b/3rdpart/ZLToolKit/src/Network/Socket.cpp @@ -0,0 +1,1181 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include "sockutil.h" +#include "Socket.h" +#include "Util/util.h" +#include "Util/logger.h" +#include "Util/uv_errno.h" +#include "Thread/semaphore.h" +#include "Poller/EventPoller.h" +#include "Thread/WorkThreadPool.h" +using namespace std; + +#define LOCK_GUARD(mtx) lock_guard lck(mtx) + +namespace toolkit { + +StatisticImp(Socket) + +static SockException toSockException(int error) { + switch (error) { + case 0: + case UV_EAGAIN: return SockException(Err_success, "success"); + case UV_ECONNREFUSED: return SockException(Err_refused, uv_strerror(error), error); + case UV_ETIMEDOUT: return SockException(Err_timeout, uv_strerror(error), error); + case UV_ECONNRESET: return SockException(Err_reset, uv_strerror(error), error); + default: return SockException(Err_other, uv_strerror(error), error); + } +} + +static SockException getSockErr(int sock, bool try_errno = true) { + int error = 0, len = sizeof(int); + getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&error, (socklen_t *)&len); + if (error == 0) { + if (try_errno) { + error = get_uv_error(true); + } + } else { + error = uv_translate_posix_error(error); + } + return toSockException(error); +} + +Socket::Ptr Socket::createSocket(const EventPoller::Ptr &poller_in, bool enable_mutex) { + auto poller = poller_in ? poller_in : EventPollerPool::Instance().getPoller(); + std::weak_ptr weak_poller = poller; + return Socket::Ptr(new Socket(poller, enable_mutex), [weak_poller](Socket *ptr) { + if (auto poller = weak_poller.lock()) { + poller->async([ptr]() { delete ptr; }); + } else { + delete ptr; + } + }); +} + +Socket::Socket(EventPoller::Ptr poller, bool enable_mutex) + : _poller(std::move(poller)) + , _mtx_sock_fd(enable_mutex) + , _mtx_event(enable_mutex) + , _mtx_send_buf_waiting(enable_mutex) + , _mtx_send_buf_sending(enable_mutex) { + memset(&_peer_addr, 0, sizeof _peer_addr); + setOnRead(nullptr); + setOnErr(nullptr); + setOnAccept(nullptr); + setOnFlush(nullptr); + setOnBeforeAccept(nullptr); + setOnSendResult(nullptr); +} + +Socket::~Socket() { + closeSock(); +} + +void Socket::setOnRead(onReadCB cb) { + onMultiReadCB cb2; + if (cb) { + cb2 = [cb](Buffer::Ptr *buf, struct sockaddr_storage *addr, size_t count) { + for (auto i = 0u; i < count; ++i) { + cb(buf[i], (struct sockaddr *)(addr + i), sizeof(struct sockaddr_storage)); + } + }; + } + setOnMultiRead(std::move(cb2)); +} + +void Socket::setOnMultiRead(onMultiReadCB cb) { + LOCK_GUARD(_mtx_event); + if (cb) { + _on_multi_read = std::move(cb); + } else { + _on_multi_read = [](Buffer::Ptr *buf, struct sockaddr_storage *addr, size_t count) { + for (auto i = 0u; i < count; ++i) { + WarnL << "Socket not set read callback, data ignored: " << buf[i]->size(); + } + }; + } +} + +void Socket::setOnErr(onErrCB cb) { + LOCK_GUARD(_mtx_event); + if (cb) { + _on_err = std::move(cb); + } else { + _on_err = [](const SockException &err) { WarnL << "Socket not set err callback, err: " << err; }; + } +} + +void Socket::setOnAccept(onAcceptCB cb) { + LOCK_GUARD(_mtx_event); + if (cb) { + _on_accept = std::move(cb); + } else { + _on_accept = [](Socket::Ptr &sock, shared_ptr &complete) { WarnL << "Socket not set accept callback, peer fd: " << sock->rawFD(); }; + } +} + +void Socket::setOnFlush(onFlush cb) { + LOCK_GUARD(_mtx_event); + if (cb) { + _on_flush = std::move(cb); + } else { + _on_flush = []() { return true; }; + } +} + +void Socket::setOnBeforeAccept(onCreateSocket cb) { + LOCK_GUARD(_mtx_event); + if (cb) { + _on_before_accept = std::move(cb); + } else { + _on_before_accept = [](const EventPoller::Ptr &poller) { return nullptr; }; + } +} + +void Socket::setOnSendResult(onSendResult cb) { + LOCK_GUARD(_mtx_event); + _send_result = std::move(cb); +} + +void Socket::connect(const string &url, uint16_t port, const onErrCB &con_cb_in, float timeout_sec, const string &local_ip, uint16_t local_port) { + weak_ptr weak_self = shared_from_this(); + // 因为涉及到异步回调,所以在poller线程中执行确保线程安全 [AUTO-TRANSLATED:e4b29f5e] + //Because it involves asynchronous callbacks, execute in the poller thread to ensure thread safety + _poller->async([=] { + if (auto strong_self = weak_self.lock()) { + strong_self->connect_l(url, port, con_cb_in, timeout_sec, local_ip, local_port); + } + }); +} + +void Socket::connect_l(const string &url, uint16_t port, const onErrCB &con_cb_in, float timeout_sec, const string &local_ip, uint16_t local_port) { + // 重置当前socket [AUTO-TRANSLATED:b38093a6] + //Reset the current socket + closeSock(); + + weak_ptr weak_self = shared_from_this(); + auto con_cb = [con_cb_in, weak_self](const SockException &err) { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + strong_self->_async_con_cb = nullptr; + strong_self->_con_timer = nullptr; + if (err) { + strong_self->setSock(nullptr); + } + con_cb_in(err); + }; + + auto async_con_cb = std::make_shared>([weak_self, con_cb](const SockNum::Ptr &sock) { + auto strong_self = weak_self.lock(); + if (!sock || !strong_self) { + con_cb(SockException(Err_dns, get_uv_errmsg(true))); + return; + } + + // 监听该socket是否可写,可写表明已经连接服务器成功 [AUTO-TRANSLATED:e9809ee3] + //Listen for whether the socket is writable, writable indicates that the connection to the server is successful + int result = strong_self->_poller->addEvent(sock->rawFd(), EventPoller::Event_Write | EventPoller::Event_Error, [weak_self, sock, con_cb](int event) { + if (auto strong_self = weak_self.lock()) { + strong_self->onConnected(sock, con_cb); + } + }); + + if (result == -1) { + con_cb(SockException(Err_other, std::string("add event to poller failed when start connect:") + get_uv_errmsg())); + } else { + // 先创建SockFD对象,防止SockNum由于未执行delEvent无法析构 [AUTO-TRANSLATED:99d4e610] + //First create the SockFD object to prevent SockNum from being destructed due to not executing delEvent + strong_self->setSock(sock); + } + }); + + // 连接超时定时器 [AUTO-TRANSLATED:1f4471b2] + //Connection timeout timer + _con_timer = std::make_shared(timeout_sec,[weak_self, con_cb]() { + con_cb(SockException(Err_timeout, uv_strerror(UV_ETIMEDOUT))); + return false; + }, _poller); + + if (isIP(url.data())) { + auto fd = SockUtil::connect(url.data(), port, true, local_ip.data(), local_port); + (*async_con_cb)(fd == -1 ? nullptr : std::make_shared(fd, SockNum::Sock_TCP)); + } else { + auto poller = _poller; + weak_ptr> weak_task = async_con_cb; + WorkThreadPool::Instance().getExecutor()->async([url, port, local_ip, local_port, weak_task, poller]() { + // 阻塞式dns解析放在后台线程执行 [AUTO-TRANSLATED:e54694ea] + //Blocking DNS resolution is executed in the background thread + int fd = SockUtil::connect(url.data(), port, true, local_ip.data(), local_port); + auto sock = fd == -1 ? nullptr : std::make_shared(fd, SockNum::Sock_TCP); + poller->async([sock, weak_task]() { + if (auto strong_task = weak_task.lock()) { + (*strong_task)(sock); + } + }); + }); + _async_con_cb = async_con_cb; + } +} + +void Socket::onConnected(const SockNum::Ptr &sock, const onErrCB &cb) { + auto err = getSockErr(sock->rawFd(), false); + if (err) { + // 连接失败 [AUTO-TRANSLATED:50e99e6b] + //Connection failed + cb(err); + return; + } + + // 更新地址信息 [AUTO-TRANSLATED:bacb739d] + //Update address information + setSock(sock); + // 先删除之前的可写事件监听 [AUTO-TRANSLATED:ca424913] + //First delete the previous writable event listener + _poller->delEvent(sock->rawFd(), [sock](bool) {}); + if (!attachEvent(sock)) { + // 连接失败 [AUTO-TRANSLATED:50e99e6b] + //Connection failed + cb(SockException(Err_other, "add event to poller failed when connected")); + return; + } + + { + LOCK_GUARD(_mtx_sock_fd); + if (_sock_fd) { + _sock_fd->setConnected(); + } + } + // 连接成功 [AUTO-TRANSLATED:7db0fbc4] + //Connection successful + cb(err); +} + +bool Socket::attachEvent(const SockNum::Ptr &sock) { + weak_ptr weak_self = shared_from_this(); + if (sock->type() == SockNum::Sock_TCP_Server) { + // tcp服务器 [AUTO-TRANSLATED:f4b9757f] + //TCP server + auto result = _poller->addEvent(sock->rawFd(), EventPoller::Event_Read | EventPoller::Event_Error, [weak_self, sock](int event) { + if (auto strong_self = weak_self.lock()) { + strong_self->onAccept(sock, event); + } + }); + return -1 != result; + } + + // tcp客户端或udp [AUTO-TRANSLATED:00c16e7f] + //TCP client or UDP + auto read_buffer = _poller->getSharedBuffer(sock->type() == SockNum::Sock_UDP); + auto result = _poller->addEvent(sock->rawFd(), EventPoller::Event_Read | EventPoller::Event_Error | EventPoller::Event_Write, [weak_self, sock, read_buffer](int event) { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + + if (event & EventPoller::Event_Read) { + strong_self->onRead(sock, read_buffer); + } + if (event & EventPoller::Event_Write) { + strong_self->onWriteAble(sock); + } + if (event & EventPoller::Event_Error) { + if (sock->type() == SockNum::Sock_UDP) { + // udp ignore error + } else { + strong_self->emitErr(getSockErr(sock->rawFd())); + } + } + }); + + return -1 != result; +} + +ssize_t Socket::onRead(const SockNum::Ptr &sock, const SocketRecvBuffer::Ptr &buffer) noexcept { + ssize_t ret = 0, nread = 0, count = 0; + + while (_enable_recv) { + nread = buffer->recvFromSocket(sock->rawFd(), count); + if (nread == 0) { + if (sock->type() == SockNum::Sock_TCP) { + emitErr(SockException(Err_eof, "end of file")); + } else { + WarnL << "Recv eof on udp socket[" << sock->rawFd() << "]"; + } + return ret; + } + + if (nread == -1) { + auto err = get_uv_error(true); + if (err != UV_EAGAIN) { + if (sock->type() == SockNum::Sock_TCP) { + emitErr(toSockException(err)); + } else { + WarnL << "Recv err on udp socket[" << sock->rawFd() << "]: " << uv_strerror(err); + } + } + return ret; + } + + ret += nread; + if (_enable_speed) { + // 更新接收速率 [AUTO-TRANSLATED:1e24774c] + //Update receive rate + _recv_speed += nread; + } + + auto &buf = buffer->getBuffer(0); + auto &addr = buffer->getAddress(0); + try { + // 此处捕获异常,目的是防止数据未读尽,epoll边沿触发失效的问题 [AUTO-TRANSLATED:2f3f813b] + //Catch exception here, the purpose is to prevent data from not being read completely, and the epoll edge trigger fails + LOCK_GUARD(_mtx_event); + _on_multi_read(&buf, &addr, count); + } catch (std::exception &ex) { + ErrorL << "Exception occurred when emit on_read: " << ex.what(); + } + } + return 0; +} + +bool Socket::emitErr(const SockException &err) noexcept { + if (_err_emit) { + return true; + } + _err_emit = true; + weak_ptr weak_self = shared_from_this(); + _poller->async([weak_self, err]() { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + LOCK_GUARD(strong_self->_mtx_event); + try { + strong_self->_on_err(err); + } catch (std::exception &ex) { + ErrorL << "Exception occurred when emit on_err: " << ex.what(); + } + // 延后关闭socket,只移除其io事件,防止Session对象析构时获取fd相关信息失败 [AUTO-TRANSLATED:db5a0958] + //Delay closing the socket, only remove its IO event, to prevent Session object destruction from failing to get fd related information + strong_self->closeSock(false); + }); + return true; +} + +ssize_t Socket::send(const char *buf, size_t size, struct sockaddr *addr, socklen_t addr_len, bool try_flush) { + if (size <= 0) { + size = strlen(buf); + if (!size) { + return 0; + } + } + auto ptr = BufferRaw::create(); + ptr->assign(buf, size); + return send(std::move(ptr), addr, addr_len, try_flush); +} + +ssize_t Socket::send(string buf, struct sockaddr *addr, socklen_t addr_len, bool try_flush) { + return send(std::make_shared(std::move(buf)), addr, addr_len, try_flush); +} + +ssize_t Socket::send(Buffer::Ptr buf, struct sockaddr *addr, socklen_t addr_len, bool try_flush) { + if (!addr) { + if (!_udp_send_dst) { + return send_l(std::move(buf), false, try_flush); + } + // 本次发送未指定目标地址,但是目标定制已通过bindPeerAddr指定 [AUTO-TRANSLATED:afb6ce35] + //This send did not specify a target address, but the target is customized through bindPeerAddr + addr = (struct sockaddr *)_udp_send_dst.get(); + addr_len = SockUtil::get_sock_len(addr); + } else { + if (_peer_addr.ss_family != AF_UNSPEC) { + // udp connect后不能再sendto指定其他地址 + return send_l(std::move(buf), false, try_flush); + } + } + return send_l(std::make_shared(std::move(buf), addr, addr_len), true, try_flush); +} + +ssize_t Socket::send_l(Buffer::Ptr buf, bool is_buf_sock, bool try_flush) { + auto size = buf ? buf->size() : 0; + if (!size) { + return 0; + } + + { + LOCK_GUARD(_mtx_send_buf_waiting); + _send_buf_waiting.emplace_back(std::move(buf), is_buf_sock); + } + + if (try_flush) { + if (flushAll()) { + return -1; + } + } + + return size; +} + +int Socket::flushAll() { + LOCK_GUARD(_mtx_sock_fd); + + if (!_sock_fd) { + // 如果已断开连接或者发送超时 [AUTO-TRANSLATED:2e25a648] + //If the connection is already disconnected or the send has timed out + return -1; + } + if (_sendable) { + // 该socket可写 [AUTO-TRANSLATED:9b37b658] + //The socket is writable + return flushData(_sock_fd->sockNum(), false) ? 0 : -1; + } + + // 该socket不可写,判断发送超时 [AUTO-TRANSLATED:cad042e3] + //The socket is not writable, judging send timeout + if (_send_flush_ticker.elapsedTime() > _max_send_buffer_ms) { + // 如果发送列队中最老的数据距今超过超时时间限制,那么就断开socket连接 [AUTO-TRANSLATED:19ee680e] + //If the oldest data in the send queue exceeds the timeout limit, disconnect the socket connection + emitErr(SockException(Err_other, "socket send timeout")); + return -1; + } + return 0; +} + +void Socket::onFlushed() { + bool flag; + { + LOCK_GUARD(_mtx_event); + flag = _on_flush(); + } + if (!flag) { + setOnFlush(nullptr); + } +} + +void Socket::closeSock(bool close_fd) { + _sendable = true; + _enable_recv = true; + _enable_speed = false; + _con_timer = nullptr; + _async_con_cb = nullptr; + _send_flush_ticker.resetTime(); + + { + LOCK_GUARD(_mtx_send_buf_waiting); + _send_buf_waiting.clear(); + } + + { + LOCK_GUARD(_mtx_send_buf_sending); + _send_buf_sending.clear(); + } + + { + LOCK_GUARD(_mtx_sock_fd); + if (close_fd) { + _err_emit = false; + _sock_fd = nullptr; + } else if (_sock_fd) { + _sock_fd->delEvent(); + } + } +} + +size_t Socket::getSendBufferCount() { + size_t ret = 0; + { + LOCK_GUARD(_mtx_send_buf_waiting); + ret += _send_buf_waiting.size(); + } + + { + LOCK_GUARD(_mtx_send_buf_sending); + _send_buf_sending.for_each([&](BufferList::Ptr &buf) { ret += buf->count(); }); + } + return ret; +} + +uint64_t Socket::elapsedTimeAfterFlushed() { + return _send_flush_ticker.elapsedTime(); +} + +size_t Socket::getRecvSpeed() { + _enable_speed = true; + return _recv_speed.getSpeed(); +} + +size_t Socket::getSendSpeed() { + _enable_speed = true; + return _send_speed.getSpeed(); +} + +size_t Socket::getRecvTotalBytes() { + _enable_speed = true; + return _recv_speed.getTotalBytes(); +} + +size_t Socket::getSendTotalBytes() { + _enable_speed = true; + return _send_speed.getTotalBytes(); +} + +bool Socket::listen(uint16_t port, const string &local_ip, int backlog) { + closeSock(); + int fd = SockUtil::listen(port, local_ip.data(), backlog); + if (fd == -1) { + return false; + } + return fromSock_l(std::make_shared(fd, SockNum::Sock_TCP_Server)); +} + +bool Socket::bindUdpSock(uint16_t port, const string &local_ip, bool enable_reuse) { + closeSock(); + int fd = SockUtil::bindUdpSock(port, local_ip.data(), enable_reuse); + if (fd == -1) { + return false; + } + return fromSock_l(std::make_shared(fd, SockNum::Sock_UDP)); +} + +bool Socket::fromSock(int fd, SockNum::SockType type) { + closeSock(); + SockUtil::setNoSigpipe(fd); + SockUtil::setNoBlocked(fd); + SockUtil::setCloExec(fd); + return fromSock_l(std::make_shared(fd, type)); +} + +bool Socket::fromSock_l(SockNum::Ptr sock) { + if (!attachEvent(sock)) { + return false; + } + setSock(std::move(sock)); + return true; +} + +void Socket::moveTo(EventPoller::Ptr poller) { + LOCK_GUARD(_mtx_sock_fd); + if (poller) { + _poller = std::move(poller); + } + if (_sock_fd) { + _sock_fd = std::make_shared(_sock_fd->sockNum(), _poller); + } +} + +int Socket::onAccept(const SockNum::Ptr &sock, int event) noexcept { + int fd; + struct sockaddr_storage peer_addr; + socklen_t addr_len = sizeof(peer_addr); + while (true) { + if (event & EventPoller::Event_Read) { + do { + fd = (int)accept(sock->rawFd(), (struct sockaddr *)&peer_addr, &addr_len); + } while (-1 == fd && UV_EINTR == get_uv_error(true)); + + if (fd == -1) { + // accept失败 [AUTO-TRANSLATED:496cc51e] + //Accept failed + int err = get_uv_error(true); + if (err == UV_EAGAIN) { + // 没有新连接 [AUTO-TRANSLATED:4ddd97d6] + //No new connection + return 0; + } + auto ex = toSockException(err); + // emitErr(ex); https://github.com/ZLMediaKit/ZLMediaKit/issues/2946 + ErrorL << "Accept socket failed: " << ex.what(); + // 可能打开的文件描述符太多了:UV_EMFILE/UV_ENFILE [AUTO-TRANSLATED:ecd1b4f1] + //Possibly too many open file descriptors: UV_EMFILE/UV_ENFILE +#if (defined(HAS_EPOLL) && !defined(_WIN32)) || defined(HAS_KQUEUE) + // 边缘触发,还需要手动再触发accept事件, [AUTO-TRANSLATED:85fa9030] + //Edge trigger, need to manually trigger the accept event again + // wepoll, Edge-triggered (`EPOLLET`) mode isn't supported. + std::weak_ptr weak_self = shared_from_this(); + _poller->doDelayTask(100, [weak_self, sock]() { + if (auto strong_self = weak_self.lock()) { + // 100ms后再处理accept事件,说不定已经有空闲的fd [AUTO-TRANSLATED:532951a2] + //Process the accept event again after 100ms, maybe there are available fds + strong_self->onAccept(sock, EventPoller::Event_Read); + } + return 0; + }); + // 暂时不处理accept事件,等待100ms后手动触发onAccept(只有EAGAIN读空后才能通过epoll再次触发事件) [AUTO-TRANSLATED:32636aea] + //Temporarily do not process the accept event, wait 100ms and manually trigger onAccept (can only be triggered again through epoll after EAGAIN reads empty) + return -1; +#else + // 水平触发;休眠10ms,防止无谓的accept失败 [AUTO-TRANSLATED:6f8349bb] + //Level trigger; sleep 10ms to prevent unnecessary accept failures + this_thread::sleep_for(std::chrono::milliseconds(10)); + // 暂时不处理accept事件,由于是水平触发,下次还会再次自动进入onAccept函数 [AUTO-TRANSLATED:9aec1432] + //Temporarily do not process the accept event, as it is level trigger, it will automatically enter the onAccept function again next time + return -1; +#endif + } + + SockUtil::setNoSigpipe(fd); + SockUtil::setNoBlocked(fd); + SockUtil::setNoDelay(fd); + SockUtil::setSendBuf(fd); + SockUtil::setRecvBuf(fd); + SockUtil::setCloseWait(fd); + SockUtil::setCloExec(fd); + + Socket::Ptr peer_sock; + try { + // 此处捕获异常,目的是防止socket未accept尽,epoll边沿触发失效的问题 [AUTO-TRANSLATED:523d496d] + //Catch exceptions here to prevent the problem of epoll edge trigger failure when the socket is not fully accepted + LOCK_GUARD(_mtx_event); + // 拦截Socket对象的构造 [AUTO-TRANSLATED:b38b67b9] + //Intercept the Socket object's constructor + peer_sock = _on_before_accept(_poller); + } catch (std::exception &ex) { + ErrorL << "Exception occurred when emit on_before_accept: " << ex.what(); + close(fd); + continue; + } + + if (!peer_sock) { + // 此处是默认构造行为,也就是子Socket共用父Socket的poll线程并且关闭互斥锁 [AUTO-TRANSLATED:6c057de0] + //This is the default construction behavior, which means the child Socket shares the parent Socket's poll thread and closes the mutex lock + peer_sock = Socket::createSocket(_poller, false); + } + + auto sock = std::make_shared(fd, SockNum::Sock_TCP); + // 设置好fd,以备在onAccept事件中可以正常访问该fd [AUTO-TRANSLATED:e3e3c225] + //Set the fd properly, so that it can be accessed normally in the onAccept event + peer_sock->setSock(sock); + // 赋值peer ip,防止在执行setSock时,fd已经被reset断开 [AUTO-TRANSLATED:7ca197db] + //Assign the peer ip to prevent the fd from being reset and disconnected when executing setSock + memcpy(&peer_sock->_peer_addr, &peer_addr, addr_len); + + shared_ptr completed(nullptr, [peer_sock, sock](void *) { + try { + // 然后把该fd加入poll监听(确保先触发onAccept事件然后再触发onRead等事件) [AUTO-TRANSLATED:45618926] + //Then add the fd to the poll monitoring (ensure that the onAccept event is triggered first, followed by onRead and other events) + if (!peer_sock->attachEvent(sock)) { + // 加入poll监听失败,触发onErr事件,通知该Socket无效 [AUTO-TRANSLATED:e81fd478] + //If adding to poll monitoring fails, trigger the onErr event to notify that the Socket is invalid + peer_sock->emitErr(SockException(Err_eof, "add event to poller failed when accept a socket")); + } + } catch (std::exception &ex) { + ErrorL << "Exception occurred: " << ex.what(); + } + }); + + try { + // 此处捕获异常,目的是防止socket未accept尽,epoll边沿触发失效的问题 [AUTO-TRANSLATED:523d496d] + //Catch exceptions here to prevent the problem of socket not being accepted and epoll edge triggering failure + LOCK_GUARD(_mtx_event); + // 先触发onAccept事件,此时应该监听该Socket的onRead等事件 [AUTO-TRANSLATED:29734871] + //First trigger the onAccept event, at this point, you should listen for onRead and other events of the Socket + _on_accept(peer_sock, completed); + } catch (std::exception &ex) { + ErrorL << "Exception occurred when emit on_accept: " << ex.what(); + continue; + } + } + + if (event & EventPoller::Event_Error) { + auto ex = getSockErr(sock->rawFd()); + emitErr(ex); + ErrorL << "TCP listener occurred a err: " << ex.what(); + return -1; + } + } +} + +void Socket::setSock(SockNum::Ptr sock) { + LOCK_GUARD(_mtx_sock_fd); + if (sock) { + _sock_fd = std::make_shared(std::move(sock), _poller); + SockUtil::get_sock_local_addr(_sock_fd->rawFd(), _local_addr); + SockUtil::get_sock_peer_addr(_sock_fd->rawFd(), _peer_addr); + } else { + _sock_fd = nullptr; + } +} + +string Socket::get_local_ip() { + LOCK_GUARD(_mtx_sock_fd); + if (!_sock_fd) { + return ""; + } + return SockUtil::inet_ntoa((struct sockaddr *)&_local_addr); +} + +uint16_t Socket::get_local_port() { + LOCK_GUARD(_mtx_sock_fd); + if (!_sock_fd) { + return 0; + } + return SockUtil::inet_port((struct sockaddr *)&_local_addr); +} + +const sockaddr *Socket::get_local_addr() { + return (const sockaddr*)&_local_addr; +} + +const sockaddr *Socket::get_peer_addr() { + if (_udp_send_dst) + return (const sockaddr *)_udp_send_dst.get(); + else + return (const sockaddr *)&_peer_addr; +} + +string Socket::get_peer_ip() { + LOCK_GUARD(_mtx_sock_fd); + if (!_sock_fd) { + return ""; + } + if (_udp_send_dst) { + return SockUtil::inet_ntoa((struct sockaddr *)_udp_send_dst.get()); + } + return SockUtil::inet_ntoa((struct sockaddr *)&_peer_addr); +} + +uint16_t Socket::get_peer_port() { + LOCK_GUARD(_mtx_sock_fd); + if (!_sock_fd) { + return 0; + } + if (_udp_send_dst) { + return SockUtil::inet_port((struct sockaddr *)_udp_send_dst.get()); + } + return SockUtil::inet_port((struct sockaddr *)&_peer_addr); +} + +string Socket::getIdentifier() const { + static string class_name = "Socket: "; + return class_name + to_string(reinterpret_cast(this)); +} + +bool Socket::flushData(const SockNum::Ptr &sock, bool poller_thread) { + decltype(_send_buf_sending) send_buf_sending_tmp; + { + // 转移出二级缓存 [AUTO-TRANSLATED:a54264d2] + //Transfer out of the secondary cache + LOCK_GUARD(_mtx_send_buf_sending); + if (!_send_buf_sending.empty()) { + send_buf_sending_tmp.swap(_send_buf_sending); + } + } + + if (send_buf_sending_tmp.empty()) { + _send_flush_ticker.resetTime(); + do { + { + // 二级发送缓存为空,那么我们接着消费一级缓存中的数据 [AUTO-TRANSLATED:8ddb2962] + //The secondary send cache is empty, so we continue to consume data from the primary cache + LOCK_GUARD(_mtx_send_buf_waiting); + if (!_send_buf_waiting.empty()) { + // 把一级缓中数数据放置到二级缓存中并清空 [AUTO-TRANSLATED:4884aa58] + //Put the data from the first-level cache into the second-level cache and clear it + LOCK_GUARD(_mtx_event); + auto send_result = _enable_speed ? [this](const Buffer::Ptr &buffer, bool send_success) { + if (send_success) { + //更新发送速率 [AUTO-TRANSLATED:e35a1eba] + //Update the sending rate + _send_speed += buffer->size(); + } + LOCK_GUARD(_mtx_event); + if (_send_result) { + _send_result(buffer, send_success); + } + } : _send_result; + send_buf_sending_tmp.emplace_back(BufferList::create(std::move(_send_buf_waiting), std::move(send_result), sock->type() == SockNum::Sock_UDP)); + break; + } + } + // 如果一级缓存也为空,那么说明所有数据均写入socket了 [AUTO-TRANSLATED:6ae9ef8a] + //If the first-level cache is also empty, it means that all data has been written to the socket + if (poller_thread) { + // poller线程触发该函数,那么该socket应该已经加入了可写事件的监听; [AUTO-TRANSLATED:5a8e123d] + //The poller thread triggers this function, so the socket should have been added to the writable event listening + // 那么在数据列队清空的情况下,我们需要关闭监听以免触发无意义的事件回调 [AUTO-TRANSLATED:0fb35573] + //So, in the case of data queue clearing, we need to close the listening to avoid triggering meaningless event callbacks + stopWriteAbleEvent(sock); + onFlushed(); + } + return true; + } while (false); + } + + while (!send_buf_sending_tmp.empty()) { + auto &packet = send_buf_sending_tmp.front(); + auto n = packet->send(sock->rawFd(), _sock_flags); + if (n > 0) { + // 全部或部分发送成功 [AUTO-TRANSLATED:0721ed7c] + //All or part of the data was sent successfully + if (packet->empty()) { + // 全部发送成功 [AUTO-TRANSLATED:38a7d0ac] + //All data was sent successfully + send_buf_sending_tmp.pop_front(); + continue; + } + // 部分发送成功 [AUTO-TRANSLATED:bd6609dd] + //Part of the data was sent successfully + if (!poller_thread) { + // 如果该函数是poller线程触发的,那么该socket应该已经加入了可写事件的监听,所以我们不需要再次加入监听 [AUTO-TRANSLATED:917049f0] + //If this function is triggered by the poller thread, the socket should have been added to the writable event listening, so we don't need to add listening again + startWriteAbleEvent(sock); + } + break; + } + + // 一个都没发送成功 [AUTO-TRANSLATED:a3b4f257] + //None of the data was sent successfully + int err = get_uv_error(true); + if (err == UV_EAGAIN) { + // 等待下一次发送 [AUTO-TRANSLATED:22980496] + //Wait for the next send + if (!poller_thread) { + // 如果该函数是poller线程触发的,那么该socket应该已经加入了可写事件的监听,所以我们不需要再次加入监听 [AUTO-TRANSLATED:917049f0] + //If this function is triggered by the poller thread, the socket should have already been added to the writable event listener, so we don't need to add it again + startWriteAbleEvent(sock); + } + break; + } + + // 其他错误代码,发生异常 [AUTO-TRANSLATED:14cca084] + //Other error codes, an exception occurred + if (sock->type() == SockNum::Sock_UDP) { + // udp发送异常,把数据丢弃 [AUTO-TRANSLATED:3a7d095d] + //UDP send exception, discard the data + send_buf_sending_tmp.pop_front(); + WarnL << "Send udp socket[" << sock->rawFd() << "] failed, data ignored: " << uv_strerror(err); + continue; + } + // tcp发送失败时,触发异常 [AUTO-TRANSLATED:06f06449] + //TCP send failed, trigger an exception + emitErr(toSockException(err)); + return false; + } + + // 回滚未发送完毕的数据 [AUTO-TRANSLATED:9f67c1be] + //Roll back the unsent data + if (!send_buf_sending_tmp.empty()) { + // 有剩余数据 [AUTO-TRANSLATED:14a89b15] + //There is remaining data + LOCK_GUARD(_mtx_send_buf_sending); + send_buf_sending_tmp.swap(_send_buf_sending); + _send_buf_sending.append(send_buf_sending_tmp); + // 二级缓存未全部发送完毕,说明该socket不可写,直接返回 [AUTO-TRANSLATED:2d7f9f2f] + //The secondary cache has not been sent completely, indicating that the socket is not writable, return directly + return true; + } + + // 二级缓存已经全部发送完毕,说明该socket还可写,我们尝试继续写 [AUTO-TRANSLATED:2c2bc316] + //The secondary cache has been sent completely, indicating that the socket is still writable, we try to continue writing + // 如果是poller线程,我们尝试再次写一次(因为可能其他线程调用了send函数又有新数据了) [AUTO-TRANSLATED:392684a8] + //If it's the poller thread, we try to write again (because other threads may have called the send function and there is new data) + return poller_thread ? flushData(sock, poller_thread) : true; +} + +void Socket::onWriteAble(const SockNum::Ptr &sock) { + bool empty_waiting; + bool empty_sending; + { + LOCK_GUARD(_mtx_send_buf_waiting); + empty_waiting = _send_buf_waiting.empty(); + } + + { + LOCK_GUARD(_mtx_send_buf_sending); + empty_sending = _send_buf_sending.empty(); + } + + if (empty_waiting && empty_sending) { + // 数据已经清空了,我们停止监听可写事件 [AUTO-TRANSLATED:751f7e4e] + //Data has been cleared, we stop listening for writable events + stopWriteAbleEvent(sock); + } else { + // socket可写,我们尝试发送剩余的数据 [AUTO-TRANSLATED:d66e0207] + //Socket is writable, we try to send the remaining data + flushData(sock, true); + } +} + +void Socket::startWriteAbleEvent(const SockNum::Ptr &sock) { + // 开始监听socket可写事件 [AUTO-TRANSLATED:31ba90c5] + //Start listening for socket writable events + _sendable = false; + int flag = _enable_recv ? EventPoller::Event_Read : 0; + _poller->modifyEvent(sock->rawFd(), flag | EventPoller::Event_Error | EventPoller::Event_Write, [sock](bool) {}); +} + +void Socket::stopWriteAbleEvent(const SockNum::Ptr &sock) { + // 停止监听socket可写事件 [AUTO-TRANSLATED:4eb5b241] + //Stop listening for socket writable events + _sendable = true; + int flag = _enable_recv ? EventPoller::Event_Read : 0; + _poller->modifyEvent(sock->rawFd(), flag | EventPoller::Event_Error, [sock](bool) {}); +} + +void Socket::enableRecv(bool enabled) { + if (_enable_recv == enabled) { + return; + } + _enable_recv = enabled; + int read_flag = _enable_recv ? EventPoller::Event_Read : 0; + // 可写时,不监听可写事件 [AUTO-TRANSLATED:6a50e751] + //Do not listen for writable events when writable + int send_flag = _sendable ? 0 : EventPoller::Event_Write; + _poller->modifyEvent(rawFD(), read_flag | send_flag | EventPoller::Event_Error); +} + +int Socket::rawFD() const { + LOCK_GUARD(_mtx_sock_fd); + if (!_sock_fd) { + return -1; + } + return _sock_fd->rawFd(); +} + +bool Socket::alive() const { + LOCK_GUARD(_mtx_sock_fd); + return _sock_fd && !_err_emit; +} + +SockNum::SockType Socket::sockType() const { + LOCK_GUARD(_mtx_sock_fd); + if (!_sock_fd) { + return SockNum::Sock_Invalid; + } + return _sock_fd->type(); +} + +void Socket::setSendTimeOutSecond(uint32_t second) { + _max_send_buffer_ms = second * 1000; +} + +bool Socket::isSocketBusy() const { + return !_sendable.load(); +} + +const EventPoller::Ptr &Socket::getPoller() const { + return _poller; +} + +std::shared_ptr Socket::cloneSocket(const Socket &other) { + closeSock(); + SockNum::Ptr sock; + { + LOCK_GUARD(other._mtx_sock_fd); + if (!other._sock_fd) { + WarnL << "sockfd of src socket is null"; + return nullptr; + } + sock = other._sock_fd->sockNum(); + } + setSock(sock); + std::weak_ptr weak_self = shared_from_this(); + // 0x01无实际意义,仅代表成功 + return std::shared_ptr(reinterpret_cast(0x01), [weak_self, sock](void *) { + if (auto strong_self = weak_self.lock()) { + if (!strong_self->attachEvent(sock)) { + WarnL << "attachEvent failed: " << sock->rawFd(); + } + } + }); +} + +bool Socket::bindPeerAddr(const struct sockaddr *dst_addr, socklen_t addr_len, bool soft_bind) { + LOCK_GUARD(_mtx_sock_fd); + if (!_sock_fd) { + return false; + } + if (_sock_fd->type() != SockNum::Sock_UDP) { + return false; + } + addr_len = addr_len ? addr_len : SockUtil::get_sock_len(dst_addr); + if (soft_bind) { + // 软绑定,只保存地址 [AUTO-TRANSLATED:e74e9b53] + //Soft bind, only save the address + _udp_send_dst = std::make_shared(); + memcpy(_udp_send_dst.get(), dst_addr, addr_len); + } else { + // 硬绑定后,取消软绑定,防止memcpy目标地址的性能损失 [AUTO-TRANSLATED:f3f26702] + //After hard binding, cancel soft binding to prevent performance loss of memcpy target address + _udp_send_dst = nullptr; + if (-1 == ::connect(_sock_fd->rawFd(), dst_addr, addr_len)) { + WarnL << "Connect socket to peer address failed: " << SockUtil::inet_ntoa(dst_addr); + return false; + } + memcpy(&_peer_addr, dst_addr, addr_len); + } + return true; +} + +void Socket::setSendFlags(int flags) { + _sock_flags = flags; +} + +///////////////SockSender/////////////////// + +SockSender &SockSender::operator<<(const char *buf) { + send(buf); + return *this; +} + +SockSender &SockSender::operator<<(string buf) { + send(std::move(buf)); + return *this; +} + +SockSender &SockSender::operator<<(Buffer::Ptr buf) { + send(std::move(buf)); + return *this; +} + +ssize_t SockSender::send(string buf) { + return send(std::make_shared(std::move(buf))); +} + +ssize_t SockSender::send(const char *buf, size_t size) { + auto buffer = BufferRaw::create(); + buffer->assign(buf, size); + return send(std::move(buffer)); +} + +///////////////SocketHelper/////////////////// + +SocketHelper::SocketHelper(const Socket::Ptr &sock) { + setSock(sock); + setOnCreateSocket(nullptr); +} + +void SocketHelper::setPoller(const EventPoller::Ptr &poller) { + _poller = poller; +} + +void SocketHelper::setSock(const Socket::Ptr &sock) { + _sock = sock; + if (_sock) { + _poller = _sock->getPoller(); + } +} + +const EventPoller::Ptr &SocketHelper::getPoller() const { + assert(_poller); + return _poller; +} + +const Socket::Ptr &SocketHelper::getSock() const { + return _sock; +} + +int SocketHelper::flushAll() { + if (!_sock) { + return -1; + } + return _sock->flushAll(); +} + +ssize_t SocketHelper::send(Buffer::Ptr buf) { + if (!_sock) { + return -1; + } + return _sock->send(std::move(buf), nullptr, 0, _try_flush); +} + +ssize_t SocketHelper::sendto(Buffer::Ptr buf, struct sockaddr *addr, socklen_t addr_len) { + if (!_sock) { + return -1; + } + return _sock->send(std::move(buf), addr, addr_len, _try_flush); +} + +void SocketHelper::shutdown(const SockException &ex) { + if (_sock) { + _sock->emitErr(ex); + } +} + +void SocketHelper::safeShutdown(const SockException &ex) { + std::weak_ptr weak_self = shared_from_this(); + async_first([weak_self, ex]() { + if (auto strong_self = weak_self.lock()) { + strong_self->shutdown(ex); + } + }); +} + +string SocketHelper::get_local_ip() { + return _sock ? _sock->get_local_ip() : ""; +} + +uint16_t SocketHelper::get_local_port() { + return _sock ? _sock->get_local_port() : 0; +} + +string SocketHelper::get_peer_ip() { + return _sock ? _sock->get_peer_ip() : ""; +} + +uint16_t SocketHelper::get_peer_port() { + return _sock ? _sock->get_peer_port() : 0; +} + +const sockaddr * SocketHelper::get_peer_addr() { + return _sock ? _sock->get_peer_addr() : nullptr; +} + +const sockaddr *SocketHelper::get_local_addr() { + return _sock ? _sock->get_local_addr() : nullptr; +} + +bool SocketHelper::isSocketBusy() const { + if (!_sock) { + return true; + } + return _sock->isSocketBusy(); +} + +Task::Ptr SocketHelper::async(TaskIn task, bool may_sync) { + return _poller->async(std::move(task), may_sync); +} + +Task::Ptr SocketHelper::async_first(TaskIn task, bool may_sync) { + return _poller->async_first(std::move(task), may_sync); +} + +void SocketHelper::setSendFlushFlag(bool try_flush) { + _try_flush = try_flush; +} + +void SocketHelper::setSendFlags(int flags) { + if (!_sock) { + return; + } + _sock->setSendFlags(flags); +} + +void SocketHelper::setOnCreateSocket(Socket::onCreateSocket cb) { + if (cb) { + _on_create_socket = std::move(cb); + } else { + _on_create_socket = [](const EventPoller::Ptr &poller) { return Socket::createSocket(poller, false); }; + } +} + +Socket::Ptr SocketHelper::createSocket() { + return _on_create_socket(_poller); +} + +std::ostream &operator<<(std::ostream &ost, const SockException &err) { + ost << err.getErrCode() << "(" << err.what() << ")"; + return ost; +} + +} // namespace toolkit diff --git a/3rdpart/ZLToolKit/src/Network/Socket.h b/3rdpart/ZLToolKit/src/Network/Socket.h new file mode 100644 index 0000000..d883457 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Network/Socket.h @@ -0,0 +1,1067 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef NETWORK_SOCKET_H +#define NETWORK_SOCKET_H + +#include +#include +#include +#include +#include +#include +#include "Util/SpeedStatistic.h" +#include "sockutil.h" +#include "Poller/Timer.h" +#include "Poller/EventPoller.h" +#include "BufferSock.h" + +namespace toolkit { + +#if defined(MSG_NOSIGNAL) +#define FLAG_NOSIGNAL MSG_NOSIGNAL +#else +#define FLAG_NOSIGNAL 0 +#endif //MSG_NOSIGNAL + +#if defined(MSG_MORE) +#define FLAG_MORE MSG_MORE +#else +#define FLAG_MORE 0 +#endif //MSG_MORE + +#if defined(MSG_DONTWAIT) +#define FLAG_DONTWAIT MSG_DONTWAIT +#else +#define FLAG_DONTWAIT 0 +#endif //MSG_DONTWAIT + +//默认的socket flags:不触发SIGPIPE,非阻塞发送 [AUTO-TRANSLATED:fefc4946] +//Default socket flags: do not trigger SIGPIPE, non-blocking send +#define SOCKET_DEFAULE_FLAGS (FLAG_NOSIGNAL | FLAG_DONTWAIT ) + +//发送超时时间,如果在规定时间内一直没有发送数据成功,那么将触发onErr事件 [AUTO-TRANSLATED:9c5d8d87] +//Send timeout time, if no data is sent successfully within the specified time, the onErr event will be triggered +#define SEND_TIME_OUT_SEC 10 + +//错误类型枚举 [AUTO-TRANSLATED:c85ff6f6] +//Error type enumeration +typedef enum { + Err_success = 0, //成功 success + Err_eof, //eof + Err_timeout, //超时 socket timeout + Err_refused,//连接被拒绝 socket refused + Err_reset,//连接被重置 socket reset + Err_dns,//dns解析失败 dns resolve failed + Err_shutdown,//主动关闭 socket shutdown + Err_other = 0xFF,//其他错误 other error +} ErrCode; + +//错误信息类 [AUTO-TRANSLATED:5d337296] +//Error message class +class SockException : public std::exception { +public: + SockException(ErrCode code = Err_success, const std::string &msg = "", int custom_code = 0) { + _msg = msg; + _code = code; + _custom_code = custom_code; + } + + //重置错误 [AUTO-TRANSLATED:d421942a] + //Reset error + void reset(ErrCode code, const std::string &msg, int custom_code = 0) { + _msg = msg; + _code = code; + _custom_code = custom_code; + } + + //错误提示 [AUTO-TRANSLATED:989d5b29] + //Error prompt + const char *what() const noexcept override { + return _msg.c_str(); + } + + //错误代码 [AUTO-TRANSLATED:06930b2e] + //Error code + ErrCode getErrCode() const { + return _code; + } + + //用户自定义错误代码 [AUTO-TRANSLATED:aef77c4e] + //User-defined error code + int getCustomCode() const { + return _custom_code; + } + + //判断是否真的有错 [AUTO-TRANSLATED:b12fad69] + //Determine if there is really an error + operator bool() const { + return _code != Err_success; + } + +private: + ErrCode _code; + int _custom_code = 0; + std::string _msg; +}; + +//std::cout等输出流可以直接输出SockException对象 [AUTO-TRANSLATED:9b0a61e5] +//std::cout and other output streams can directly output SockException objects +std::ostream &operator<<(std::ostream &ost, const SockException &err); + +class SockNum { +public: + using Ptr = std::shared_ptr; + + typedef enum { + Sock_Invalid = -1, + Sock_TCP = 0, + Sock_UDP = 1, + Sock_TCP_Server = 2 + } SockType; + + SockNum(int fd, SockType type) { + _fd = fd; + _type = type; + } + + ~SockNum() { +#if defined (OS_IPHONE) + unsetSocketOfIOS(_fd); +#endif //OS_IPHONE + // 停止socket收发能力 [AUTO-TRANSLATED:73526f07] + //Stop socket send and receive capability + #if defined(_WIN32) + ::shutdown(_fd, SD_BOTH); + #else + ::shutdown(_fd, SHUT_RDWR); + #endif + close(_fd); + } + + int rawFd() const { + return _fd; + } + + SockType type() { + return _type; + } + + void setConnected() { +#if defined (OS_IPHONE) + setSocketOfIOS(_fd); +#endif //OS_IPHONE + } + +#if defined (OS_IPHONE) +private: + void *readStream=nullptr; + void *writeStream=nullptr; + bool setSocketOfIOS(int socket); + void unsetSocketOfIOS(int socket); +#endif //OS_IPHONE + +private: + int _fd; + SockType _type; +}; + +//socket 文件描述符的包装 [AUTO-TRANSLATED:d6705c7a] +//Socket file descriptor wrapper +//在析构时自动溢出监听并close套接字 [AUTO-TRANSLATED:3d9c96d9] +//Automatically overflow listening and close socket when destructing +//防止描述符溢出 [AUTO-TRANSLATED:17c2f2f0] +//Prevent descriptor overflow +class SockFD : public noncopyable { +public: + using Ptr = std::shared_ptr; + + /** + * 创建一个fd对象 + * @param num 文件描述符,int数字 + * @param poller 事件监听器 + * Create an fd object + * @param num File descriptor, int number + * @param poller Event listener + + * [AUTO-TRANSLATED:2eb468c4] + */ + SockFD(SockNum::Ptr num, EventPoller::Ptr poller) { + _num = std::move(num); + _poller = std::move(poller); + } + + /** + * 复制一个fd对象 + * @param that 源对象 + * @param poller 事件监听器 + * Copy an fd object + * @param that Source object + * @param poller Event listener + + * [AUTO-TRANSLATED:51fca132] + */ + SockFD(const SockFD &that, EventPoller::Ptr poller) { + _num = that._num; + _poller = std::move(poller); + if (_poller == that._poller) { + throw std::invalid_argument("Copy a SockFD with same poller"); + } + } + + ~SockFD() { delEvent(); } + + void delEvent() { + if (_poller) { + auto num = _num; + // 移除io事件成功后再close fd [AUTO-TRANSLATED:4b5e429f] + //Remove IO event successfully before closing fd + _poller->delEvent(num->rawFd(), [num](bool) {}); + _poller = nullptr; + } + } + + void setConnected() { + _num->setConnected(); + } + + int rawFd() const { + return _num->rawFd(); + } + + const SockNum::Ptr& sockNum() const { + return _num; + } + + SockNum::SockType type() { + return _num->type(); + } + + const EventPoller::Ptr& getPoller() const { + return _poller; + } + +private: + SockNum::Ptr _num; + EventPoller::Ptr _poller; +}; + +template +class MutexWrapper { +public: + MutexWrapper(bool enable) { + _enable = enable; + } + + ~MutexWrapper() = default; + + inline void lock() { + if (_enable) { + _mtx.lock(); + } + } + + inline void unlock() { + if (_enable) { + _mtx.unlock(); + } + } + +private: + bool _enable; + Mtx _mtx; +}; + +class SockInfo { +public: + SockInfo() = default; + virtual ~SockInfo() = default; + + //获取本机ip [AUTO-TRANSLATED:02d3901d] + //Get local IP + virtual std::string get_local_ip() = 0; + //获取本机端口号 [AUTO-TRANSLATED:f883cf62] + //Get local port number + virtual uint16_t get_local_port() = 0; + //获取对方ip [AUTO-TRANSLATED:f042aa78] + //Get peer IP + virtual std::string get_peer_ip() = 0; + //获取对方端口号 [AUTO-TRANSLATED:0d085eca] + //Get the peer's port number + virtual uint16_t get_peer_port() = 0; + //获取标识符 [AUTO-TRANSLATED:e623608c] + //Get the identifier + virtual std::string getIdentifier() const { return ""; } +}; + +#define TraceP(ptr) TraceL << ptr->getIdentifier() << "(" << ptr->get_peer_ip() << ":" << ptr->get_peer_port() << ") " +#define DebugP(ptr) DebugL << ptr->getIdentifier() << "(" << ptr->get_peer_ip() << ":" << ptr->get_peer_port() << ") " +#define InfoP(ptr) InfoL << ptr->getIdentifier() << "(" << ptr->get_peer_ip() << ":" << ptr->get_peer_port() << ") " +#define WarnP(ptr) WarnL << ptr->getIdentifier() << "(" << ptr->get_peer_ip() << ":" << ptr->get_peer_port() << ") " +#define ErrorP(ptr) ErrorL << ptr->getIdentifier() << "(" << ptr->get_peer_ip() << ":" << ptr->get_peer_port() << ") " + +//异步IO Socket对象,包括tcp客户端、服务器和udp套接字 [AUTO-TRANSLATED:8d4fc5c2] +//Asynchronous IO Socket object, including TCP client, server, and UDP socket +class Socket : public std::enable_shared_from_this, public noncopyable, public SockInfo { +public: + using Ptr = std::shared_ptr; + //接收数据回调 [AUTO-TRANSLATED:e3b7ff16] + //Receive data callback + using onReadCB = std::function; + using onMultiReadCB = toolkit::function_safe; + + //发生错误回调 [AUTO-TRANSLATED:d6897b99] + //Error callback + using onErrCB = toolkit::function_safe; + //tcp监听接收到连接请求 [AUTO-TRANSLATED:c4e1b206] + //TCP listen receives a connection request + using onAcceptCB = toolkit::function_safe &complete)>; + //socket发送缓存清空事件,返回true代表下次继续监听该事件,否则停止 [AUTO-TRANSLATED:2dd1c036] + //Socket send buffer is cleared event, returns true to continue listening for the event next time, otherwise stops + using onFlush = toolkit::function_safe; + //在接收到连接请求前,拦截Socket默认生成方式 [AUTO-TRANSLATED:2f07f268] + //Intercept the default generation method of the Socket before receiving a connection request + using onCreateSocket = toolkit::function_safe; + //发送buffer成功与否回调 [AUTO-TRANSLATED:4db5efb8] + //Send buffer success or failure callback + using onSendResult = BufferList::SendResult; + + /** + * 构造socket对象,尚未有实质操作 + * @param poller 绑定的poller线程 + * @param enable_mutex 是否启用互斥锁(接口是否线程安全) + * Construct a socket object, no actual operation yet + * @param poller The bound poller thread + * @param enable_mutex Whether to enable the mutex (whether the interface is thread-safe) + + * [AUTO-TRANSLATED:39bd767a] + */ + static Ptr createSocket(const EventPoller::Ptr &poller = nullptr, bool enable_mutex = true); + ~Socket() override; + + /** + * 创建tcp客户端并异步连接服务器 + * @param url 目标服务器ip或域名 + * @param port 目标服务器端口 + * @param con_cb 结果回调 + * @param timeout_sec 超时时间 + * @param local_ip 绑定本地网卡ip + * @param local_port 绑定本地网卡端口号 + * Create a TCP client and connect to the server asynchronously + * @param url Target server IP or domain name + * @param port Target server port + * @param con_cb Result callback + * @param timeout_sec Timeout time + * @param local_ip Local network card IP to bind + * @param local_port Local network card port number to bind + + * [AUTO-TRANSLATED:4f6c0d3e] + */ + void connect(const std::string &url, uint16_t port, const onErrCB &con_cb, float timeout_sec = 5, const std::string &local_ip = "::", uint16_t local_port = 0); + + /** + * 创建tcp监听服务器 + * @param port 监听端口,0则随机 + * @param local_ip 监听的网卡ip + * @param backlog tcp最大积压数 + * @return 是否成功 + * Create a TCP listening server + * @param port Listening port, 0 for random + * @param local_ip Network card IP to listen on + * @param backlog Maximum TCP backlog + * @return Whether successful + + * [AUTO-TRANSLATED:c90ff571] + */ + bool listen(uint16_t port, const std::string &local_ip = "::", int backlog = 1024); + + /** + * 创建udp套接字,udp是无连接的,所以可以作为服务器和客户端 + * @param port 绑定的端口为0则随机 + * @param local_ip 绑定的网卡ip + * @return 是否成功 + * Create a UDP socket, UDP is connectionless, so it can be used as a server and client + * @param port Port to bind, 0 for random + * @param local_ip Network card IP to bind + * @return Whether successful + + * [AUTO-TRANSLATED:e96342b5] + */ + bool bindUdpSock(uint16_t port, const std::string &local_ip = "::", bool enable_reuse = true); + + /** + * 包装外部fd,本对象负责close fd + * 内部会设置fd为NoBlocked,NoSigpipe,CloExec + * 其他设置需要自行使用SockUtil进行设置 + * Wrap an external file descriptor, this object is responsible for closing the file descriptor + * Internally, the file descriptor will be set to NoBlocked, NoSigpipe, CloExec + * Other settings need to be set manually using SockUtil + + * [AUTO-TRANSLATED:a72fd2ad] + */ + bool fromSock(int fd, SockNum::SockType type); + + /** + * 从另外一个Socket克隆 + * 目的是一个socket可以被多个poller对象监听,提高性能或实现Socket归属线程的迁移 + * @param other 原始的socket对象 + * @return 是否成功, 析构后添加poller事件监听 + * Clone from another Socket + * The purpose is to allow a socket to be listened to by multiple poller objects, improving performance or implementing socket migration between threads + * @param other Original socket object + * @return Whether successful + + * [AUTO-TRANSLATED:b3669f71] + */ + std::shared_ptr cloneSocket(const Socket &other); + + /** + * 切换poller线程,注意只能在onAccept之前调用 + * @param poller 新线程 + */ + void moveTo(EventPoller::Ptr poller); + + ////////////设置事件回调//////////// [AUTO-TRANSLATED:0bfc62ce] + //////////// Set event callbacks //////////// + + /** + * 设置数据接收回调,tcp或udp客户端有效 + * @param cb 回调对象 + * Set data receive callback, valid for TCP or UDP clients + * @param cb Callback object + + * [AUTO-TRANSLATED:d3f7ae8a] + */ + void setOnRead(onReadCB cb); + void setOnMultiRead(onMultiReadCB cb); + + /** + * 设置异常事件(包括eof等)回调 + * @param cb 回调对象 + * Set exception event (including EOF) callback + * @param cb Callback object + + * [AUTO-TRANSLATED:ffbea52f] + */ + void setOnErr(onErrCB cb); + + /** + * 设置tcp监听接收到连接回调 + * @param cb 回调对象 + * Set TCP listening receive connection callback + * @param cb Callback object + + * [AUTO-TRANSLATED:cdcfdb9c] + */ + void setOnAccept(onAcceptCB cb); + + /** + * 设置socket写缓存清空事件回调 + * 通过该回调可以实现发送流控 + * @param cb 回调对象 + * Set socket write buffer clear event callback + * This callback can be used to implement send flow control + * @param cb Callback object + + * [AUTO-TRANSLATED:a5ef862d] + */ + void setOnFlush(onFlush cb); + + /** + * 设置accept时,socket构造事件回调 + * @param cb 回调 + * Set accept callback when socket is constructed + * @param cb callback + + * [AUTO-TRANSLATED:d946409b] + */ + void setOnBeforeAccept(onCreateSocket cb); + + /** + * 设置发送buffer结果回调 + * @param cb 回调 + * Set send buffer result callback + * @param cb callback + + * [AUTO-TRANSLATED:1edb77bb] + */ + void setOnSendResult(onSendResult cb); + + ////////////发送数据相关接口//////////// [AUTO-TRANSLATED:c14ca1a7] + ////////////Data sending related interfaces//////////// + + /** + * 发送数据指针 + * @param buf 数据指针 + * @param size 数据长度 + * @param addr 目标地址 + * @param addr_len 目标地址长度 + * @param try_flush 是否尝试写socket + * @return -1代表失败(socket无效),0代表数据长度为0,否则返回数据长度 + * Send data pointer + * @param buf data pointer + * @param size data length + * @param addr target address + * @param addr_len target address length + * @param try_flush whether to try writing to the socket + * @return -1 represents failure (invalid socket), 0 represents data length is 0, otherwise returns data length + + * [AUTO-TRANSLATED:718d6192] + */ + ssize_t send(const char *buf, size_t size = 0, struct sockaddr *addr = nullptr, socklen_t addr_len = 0, bool try_flush = true); + + /** + * 发送string + * Send string + + * [AUTO-TRANSLATED:f9dfdfcf] + */ + ssize_t send(std::string buf, struct sockaddr *addr = nullptr, socklen_t addr_len = 0, bool try_flush = true); + + /** + * 发送Buffer对象,Socket对象发送数据的统一出口 + * socket对象发送数据的统一出口 + * Send Buffer object, unified exit for Socket object to send data + * unified exit for Socket object to send data + + * [AUTO-TRANSLATED:5e69facd] + */ + ssize_t send(Buffer::Ptr buf, struct sockaddr *addr = nullptr, socklen_t addr_len = 0, bool try_flush = true); + + /** + * 尝试将所有数据写socket + * @return -1代表失败(socket无效或者发送超时),0代表成功? + * Try to write all data to the socket + * @return -1 represents failure (invalid socket or send timeout), 0 represents success? + + * [AUTO-TRANSLATED:8e975c68] + */ + int flushAll(); + + /** + * 关闭socket且触发onErr回调,onErr回调将在poller线程中进行 + * @param err 错误原因 + * @return 是否成功触发onErr回调 + * Close the socket and trigger the onErr callback, the onErr callback will be executed in the poller thread + * @param err error reason + * @return whether the onErr callback is successfully triggered + + * [AUTO-TRANSLATED:366db327] + */ + bool emitErr(const SockException &err) noexcept; + + /** + * 关闭或开启数据接收 + * @param enabled 是否开启 + * Enable or disable data reception + * @param enabled whether to enable + + * [AUTO-TRANSLATED:95cdec39] + */ + void enableRecv(bool enabled); + + /** + * 获取裸文件描述符,请勿进行close操作(因为Socket对象会管理其生命周期) + * @return 文件描述符 + * Get the raw file descriptor, do not perform close operation (because the Socket object will manage its lifecycle) + * @return file descriptor + + * [AUTO-TRANSLATED:75417922] + */ + int rawFD() const; + + /** + * tcp客户端是否处于连接状态 + * 支持Sock_TCP类型socket + * Whether the TCP client is in a connected state + * Supports Sock_TCP type socket + + * [AUTO-TRANSLATED:42c0c094] + */ + bool alive() const; + + /** + * 返回socket类型 + * Returns the socket type + + * [AUTO-TRANSLATED:2009a5d2] + */ + SockNum::SockType sockType() const; + + /** + * 设置发送超时主动断开时间;默认10秒 + * @param second 发送超时数据,单位秒 + * Sets the send timeout to disconnect actively; default 10 seconds + * @param second Send timeout data, in seconds + + * [AUTO-TRANSLATED:49127ce8] + */ + void setSendTimeOutSecond(uint32_t second); + + /** + * 套接字是否忙,如果套接字写缓存已满则返回true + * @return 套接字是否忙 + * Whether the socket is busy, if the socket write buffer is full, returns true + * @return Whether the socket is busy + + * [AUTO-TRANSLATED:4b753c62] + */ + bool isSocketBusy() const; + + /** + * 获取poller线程对象 + * @return poller线程对象 + * Gets the poller thread object + * @return poller thread object + + * [AUTO-TRANSLATED:cfc5d2c4] + */ + const EventPoller::Ptr &getPoller() const; + + /** + * 绑定udp 目标地址,后续发送时就不用再单独指定了 + * @param dst_addr 目标地址 + * @param addr_len 目标地址长度 + * @param soft_bind 是否软绑定,软绑定时不调用udp connect接口,只保存目标地址信息,发送时再传递到sendto函数 + * @return 是否成功 + * Binds the UDP target address, subsequent sends do not need to specify it separately + * @param dst_addr Target address + * @param addr_len Target address length + * @param soft_bind Whether to soft bind, soft binding does not call the UDP connect interface, only saves the target address information, and sends it to the sendto function + * @return Whether successful + + * [AUTO-TRANSLATED:946bfe2a] + */ + bool bindPeerAddr(const struct sockaddr *dst_addr, socklen_t addr_len = 0, bool soft_bind = false); + + /** + * 设置发送flags + * @param flags 发送的flag + * Sets the send flags + * @param flags Send flags + + * [AUTO-TRANSLATED:2b11445c] + */ + void setSendFlags(int flags = SOCKET_DEFAULE_FLAGS); + + /** + * 关闭套接字 + * @param close_fd 是否关闭fd还是只移除io事件监听 + * Closes the socket + * @param close_fd Whether to close the fd or only remove the IO event listener + + * [AUTO-TRANSLATED:db624fc6] + */ + void closeSock(bool close_fd = true); + + /** + * 获取发送缓存包个数(不是字节数) + * Gets the number of packets in the send buffer (not the number of bytes) + + * [AUTO-TRANSLATED:2f853b18] + */ + size_t getSendBufferCount(); + + /** + * 获取上次socket发送缓存清空至今的毫秒数,单位毫秒 + * Gets the number of milliseconds since the last socket send buffer was cleared, in milliseconds + + * [AUTO-TRANSLATED:567c2818] + */ + uint64_t elapsedTimeAfterFlushed(); + + /** + * 获取接收速率,单位bytes/s + * Get the receiving rate, in bytes/s + + * [AUTO-TRANSLATED:5de8aa1c] + */ + size_t getRecvSpeed(); + + /** + * 获取发送速率,单位bytes/s + * Get the sending rate, in bytes/s + + * [AUTO-TRANSLATED:96a2595d] + */ + size_t getSendSpeed(); + + /** + * 获取接收总字节数 + * Get the total recv bytes + + * [AUTO-TRANSLATED:5de8aa1c] + */ + size_t getRecvTotalBytes(); + + /** + * 获取发送总字节数 + * Get the total send bytes + + * [AUTO-TRANSLATED:5de8aa1c] + */ + size_t getSendTotalBytes(); + + ////////////SockInfo override//////////// + std::string get_local_ip() override; + uint16_t get_local_port() override; + std::string get_peer_ip() override; + uint16_t get_peer_port() override; + std::string getIdentifier() const override; + const sockaddr *get_peer_addr(); + const sockaddr *get_local_addr(); + +private: + Socket(EventPoller::Ptr poller, bool enable_mutex = true); + + void setSock(SockNum::Ptr sock); + int onAccept(const SockNum::Ptr &sock, int event) noexcept; + ssize_t onRead(const SockNum::Ptr &sock, const SocketRecvBuffer::Ptr &buffer) noexcept; + void onWriteAble(const SockNum::Ptr &sock); + void onConnected(const SockNum::Ptr &sock, const onErrCB &cb); + void onFlushed(); + void startWriteAbleEvent(const SockNum::Ptr &sock); + void stopWriteAbleEvent(const SockNum::Ptr &sock); + bool flushData(const SockNum::Ptr &sock, bool poller_thread); + bool attachEvent(const SockNum::Ptr &sock); + ssize_t send_l(Buffer::Ptr buf, bool is_buf_sock, bool try_flush = true); + void connect_l(const std::string &url, uint16_t port, const onErrCB &con_cb_in, float timeout_sec, const std::string &local_ip, uint16_t local_port); + bool fromSock_l(SockNum::Ptr sock); + +private: + // send socket时的flag [AUTO-TRANSLATED:e364a1bf] + //Flag for sending socket + int _sock_flags = SOCKET_DEFAULE_FLAGS; + // 最大发送缓存,单位毫秒,距上次发送缓存清空时间不能超过该参数 [AUTO-TRANSLATED:3bd6dba3] + //Maximum send buffer, in milliseconds, the time since the last send buffer was cleared cannot exceed this parameter + uint32_t _max_send_buffer_ms = SEND_TIME_OUT_SEC * 1000; + // 控制是否接收监听socket可读事件,关闭后可用于流量控制 [AUTO-TRANSLATED:71de6ece] + //Control whether to receive listen socket readable events, can be used for traffic control after closing + std::atomic _enable_recv { true }; + // 标记该socket是否可写,socket写缓存满了就不可写 [AUTO-TRANSLATED:32392de2] + //Mark whether the socket is writable, the socket write buffer is full and cannot be written + std::atomic _sendable { true }; + // 是否已经触发err回调了 [AUTO-TRANSLATED:17ab8384] + //Whether the err callback has been triggered + bool _err_emit = false; + // 是否启用网速统计 [AUTO-TRANSLATED:c0c0e8ee] + //Whether to enable network speed statistics + bool _enable_speed = false; + // udp发送目标地址 [AUTO-TRANSLATED:cce2315a] + //UDP send target address + std::shared_ptr _udp_send_dst; + + // 接收速率统计 [AUTO-TRANSLATED:20dcd724] + //Receiving rate statistics + BytesSpeed _recv_speed; + // 发送速率统计 [AUTO-TRANSLATED:eab3486a] + //Send rate statistics + BytesSpeed _send_speed; + + // tcp连接超时定时器 [AUTO-TRANSLATED:1b3e5fc4] + //TCP connection timeout timer + Timer::Ptr _con_timer; + // tcp连接结果回调对象 [AUTO-TRANSLATED:4f1c366a] + //TCP connection result callback object + std::shared_ptr _async_con_cb; + + // 记录上次发送缓存(包括socket写缓存、应用层缓存)清空的计时器 [AUTO-TRANSLATED:2c44d156] + //Record the timer for the last send buffer (including socket write buffer and application layer buffer) cleared + Ticker _send_flush_ticker; + // socket fd的抽象类 [AUTO-TRANSLATED:31e4ea33] + //Abstract class for socket fd + SockFD::Ptr _sock_fd; + // 本socket绑定的poller线程,事件触发于此线程 [AUTO-TRANSLATED:6f782513] + //The poller thread bound to this socket, events are triggered in this thread + EventPoller::Ptr _poller; + // 跨线程访问_sock_fd时需要上锁 [AUTO-TRANSLATED:dc63f6c4] + //Need to lock when accessing _sock_fd across threads + mutable MutexWrapper _mtx_sock_fd; + + // socket异常事件(比如说断开) [AUTO-TRANSLATED:96c028e8] + //Socket exception event (such as disconnection) + onErrCB _on_err; + // 收到数据事件 [AUTO-TRANSLATED:23946f9b] + //Receive data event + onMultiReadCB _on_multi_read; + // socket缓存清空事件(可用于发送流速控制) [AUTO-TRANSLATED:976b84ef] + //Socket buffer cleared event (can be used for send flow control) + onFlush _on_flush; + // tcp监听收到accept请求事件 [AUTO-TRANSLATED:5fe01738] + //TCP listener receives an accept request event + onAcceptCB _on_accept; + // tcp监听收到accept请求,自定义创建peer Socket事件(可以控制子Socket绑定到其他poller线程) [AUTO-TRANSLATED:da85b845] + //TCP listener receives an accept request, custom creation of peer Socket event (can control binding of child Socket to other poller threads) + onCreateSocket _on_before_accept; + // 设置上述回调函数的锁 [AUTO-TRANSLATED:302ca377] + //Set the lock for the above callback function + MutexWrapper _mtx_event; + + // 一级发送缓存, socket可写时,会把一级缓存批量送入到二级缓存 [AUTO-TRANSLATED:26f1da58] + //First-level send cache, when the socket is writable, it will batch the first-level cache into the second-level cache + List> _send_buf_waiting; + // 一级发送缓存锁 [AUTO-TRANSLATED:9ec6c6a9] + //First-level send cache lock + MutexWrapper _mtx_send_buf_waiting; + // 二级发送缓存, socket可写时,会把二级缓存批量写入到socket [AUTO-TRANSLATED:cc665665] + //Second-level send cache, when the socket is writable, it will batch the second-level cache into the socket + List _send_buf_sending; + // 二级发送缓存锁 [AUTO-TRANSLATED:306e3472] + //Second-level send cache lock + MutexWrapper _mtx_send_buf_sending; + // 发送buffer结果回调 [AUTO-TRANSLATED:1cac46fd] + //Send buffer result callback + BufferList::SendResult _send_result; + // 对象个数统计 [AUTO-TRANSLATED:f4a012d0] + //Object count statistics + ObjectStatistic _statistic; + + // 链接缓存地址,防止tcp reset 导致无法获取对端的地址 [AUTO-TRANSLATED:f8847463] + //Connection cache address, to prevent TCP reset from causing the inability to obtain the peer's address + struct sockaddr_storage _local_addr; + struct sockaddr_storage _peer_addr; +}; + +class SockSender { +public: + SockSender() = default; + virtual ~SockSender() = default; + virtual ssize_t send(Buffer::Ptr buf) = 0; + virtual ssize_t sendto(Buffer::Ptr buf, struct sockaddr *addr = nullptr, socklen_t addr_len = 0) = 0; + virtual void shutdown(const SockException &ex = SockException(Err_shutdown, "self shutdown")) = 0; + + //发送char * [AUTO-TRANSLATED:ab84aeb3] + //Send char * + SockSender &operator << (const char *buf); + //发送字符串 [AUTO-TRANSLATED:3d678d0a] + //Send string + SockSender &operator << (std::string buf); + //发送Buffer对象 [AUTO-TRANSLATED:8a6fb71c] + //Send Buffer object + SockSender &operator << (Buffer::Ptr buf); + + //发送其他类型是数据 [AUTO-TRANSLATED:86b0319a] + //Send other types of data + template + SockSender &operator << (T &&buf) { + std::ostringstream ss; + ss << std::forward(buf); + send(ss.str()); + return *this; + } + + ssize_t send(std::string buf); + ssize_t send(const char *buf, size_t size = 0); +}; + +//Socket对象的包装类 [AUTO-TRANSLATED:9d384814] +//Socket object wrapper class +class SocketHelper : public SockSender, public SockInfo, public TaskExecutorInterface, public std::enable_shared_from_this { +public: + using Ptr = std::shared_ptr; + SocketHelper(const Socket::Ptr &sock); + ~SocketHelper() override = default; + + ///////////////////// Socket util std::functions ///////////////////// + /** + * 获取poller线程 + * Get poller thread + + * [AUTO-TRANSLATED:bd1ed6dc] + */ + const EventPoller::Ptr& getPoller() const; + + /** + * 设置批量发送标记,用于提升性能 + * @param try_flush 批量发送标记 + * Set batch send flag, used to improve performance + * @param try_flush Batch send flag + + * [AUTO-TRANSLATED:8c3f2ae1] + */ + void setSendFlushFlag(bool try_flush); + + /** + * 设置socket发送flags + * @param flags socket发送flags + * Set socket send flags + * @param flags Socket send flags + + * [AUTO-TRANSLATED:d5d2eec9] + */ + void setSendFlags(int flags); + + /** + * 套接字是否忙,如果套接字写缓存已满则返回true + * Whether the socket is busy, returns true if the socket write buffer is full + + * [AUTO-TRANSLATED:5c3cc85c] + */ + bool isSocketBusy() const; + + /** + * 设置Socket创建器,自定义Socket创建方式 + * @param cb 创建器 + * Set Socket creator, customize Socket creation method + * @param cb Creator + + * [AUTO-TRANSLATED:df045ccf] + */ + void setOnCreateSocket(Socket::onCreateSocket cb); + + /** + * 创建socket对象 + * Create a socket object + + * [AUTO-TRANSLATED:260848b5] + */ + Socket::Ptr createSocket(); + + /** + * 获取socket对象 + * Get the socket object + + * [AUTO-TRANSLATED:f737fb8d] + */ + const Socket::Ptr &getSock() const; + + /** + * 尝试将所有数据写socket + * @return -1代表失败(socket无效或者发送超时),0代表成功? + * Try to write all data to the socket + * @return -1 represents failure (invalid socket or send timeout), 0 represents success + + * [AUTO-TRANSLATED:8e975c68] + */ + int flushAll(); + + /** + * 是否ssl加密 + * Whether SSL encryption is enabled + + * [AUTO-TRANSLATED:95b748f2] + */ + virtual bool overSsl() const { return false; } + + ///////////////////// SockInfo override ///////////////////// + std::string get_local_ip() override; + uint16_t get_local_port() override; + std::string get_peer_ip() override; + uint16_t get_peer_port() override; + const sockaddr *get_peer_addr(); + const sockaddr *get_local_addr(); + + ///////////////////// TaskExecutorInterface override ///////////////////// + /** + * 任务切换到所属poller线程执行 + * @param task 任务 + * @param may_sync 是否运行同步执行任务 + * Switch the task to the poller thread for execution + * @param task The task to be executed + * @param may_sync Whether to run the task synchronously + + * [AUTO-TRANSLATED:c0a93c6e] + */ + Task::Ptr async(TaskIn task, bool may_sync = true) override; + Task::Ptr async_first(TaskIn task, bool may_sync = true) override; + + ///////////////////// SockSender override ///////////////////// + + /** + * 使能 SockSender 其他未被重写的send重载函数 + * Enable other non-overridden send functions in SockSender + + * [AUTO-TRANSLATED:e6baa93a] + */ + using SockSender::send; + + /** + * 统一发送数据的出口 + * Unified data sending outlet + + * [AUTO-TRANSLATED:6a7a5178] + */ + ssize_t send(Buffer::Ptr buf) override; + + /** + * 统一发送数据的出口 + */ + ssize_t sendto(Buffer::Ptr buf, struct sockaddr *addr = nullptr, socklen_t addr_len = 0) override; + + /** + * 触发onErr事件 + * Trigger the onErr event + + * [AUTO-TRANSLATED:b485450f] + */ + void shutdown(const SockException &ex = SockException(Err_shutdown, "self shutdown")) override; + + /** + * 线程安全的脱离 Server 并触发 onError 事件 + * @param ex 触发 onError 事件的原因 + * Safely detach from the Server and trigger the onError event in a thread-safe manner + * @param ex The reason for triggering the onError event + + * [AUTO-TRANSLATED:739455d5] + */ + void safeShutdown(const SockException &ex = SockException(Err_shutdown, "self shutdown")); + + ///////////////////// event functions ///////////////////// + /** + * 接收数据入口 + * @param buf 数据,可以重复使用内存区,不可被缓存使用 + * Data receiving entry point + * @param buf Data buffer, can be reused, and cannot be cached + + * [AUTO-TRANSLATED:9d498f56] + */ + virtual void onRecv(const Buffer::Ptr &buf) = 0; + + /** + * 收到 eof 或其他导致脱离 Server 事件的回调 + * 收到该事件时, 该对象一般将立即被销毁 + * @param err 原因 + * Callback received eof or other events that cause disconnection from Server + * When this event is received, the object is generally destroyed immediately + * @param err reason + + * [AUTO-TRANSLATED:a9349e0f] + */ + virtual void onError(const SockException &err) = 0; + + /** + * 数据全部发送完毕后回调 + * Callback after all data has been sent + + * [AUTO-TRANSLATED:8b2ba800] + */ + virtual void onFlush() {} + + /** + * 每隔一段时间触发, 用来做超时管理 + * Triggered at regular intervals, used for timeout management + + * [AUTO-TRANSLATED:af9e6c42] + */ + virtual void onManager() = 0; + +protected: + void setPoller(const EventPoller::Ptr &poller); + void setSock(const Socket::Ptr &sock); + +private: + bool _try_flush = true; + Socket::Ptr _sock; + EventPoller::Ptr _poller; + Socket::onCreateSocket _on_create_socket; +}; + +} // namespace toolkit +#endif /* NETWORK_SOCKET_H */ diff --git a/3rdpart/ZLToolKit/src/Network/Socket_ios.mm b/3rdpart/ZLToolKit/src/Network/Socket_ios.mm new file mode 100644 index 0000000..30a9fa7 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Network/Socket_ios.mm @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/xia-chu/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ +#import "Socket.h" +#include "Util/logger.h" + +#if defined (OS_IPHONE) +#import +#endif //OS_IPHONE + +namespace toolkit { + +#if defined (OS_IPHONE) +bool SockNum::setSocketOfIOS(int sock){ + + CFStreamCreatePairWithSocket(NULL, (CFSocketNativeHandle)sock, (CFReadStreamRef *)(&readStream), (CFWriteStreamRef*)(&writeStream)); + if (readStream) + CFReadStreamSetProperty((CFReadStreamRef)readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse); + if (writeStream) + CFWriteStreamSetProperty((CFWriteStreamRef)writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse); + if ((readStream == NULL) || (writeStream == NULL)) + { + WarnL<<"Unable to create read and write stream..."; + if (readStream) + { + CFReadStreamClose((CFReadStreamRef)readStream); + CFRelease(readStream); + readStream = NULL; + } + if (writeStream) + { + CFWriteStreamClose((CFWriteStreamRef)writeStream); + CFRelease(writeStream); + writeStream = NULL; + } + return false; + } + + + Boolean r1 = CFReadStreamSetProperty((CFReadStreamRef)readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); + Boolean r2 = CFWriteStreamSetProperty((CFWriteStreamRef)writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); + + if (!r1 || !r2) + { + return false; + } + + CFStreamStatus readStatus = CFReadStreamGetStatus((CFReadStreamRef)readStream); + CFStreamStatus writeStatus = CFWriteStreamGetStatus((CFWriteStreamRef)writeStream); + + if ((readStatus == kCFStreamStatusNotOpen) || (writeStatus == kCFStreamStatusNotOpen)) + { + BOOL r1 = CFReadStreamOpen((CFReadStreamRef)readStream); + BOOL r2 = CFWriteStreamOpen((CFWriteStreamRef)writeStream); + + if (!r1 || !r2) + { + WarnL<<"Error in CFStreamOpen"; + return false; + } + } + //NSLog(@"setSocketOfIOS:%d",sock); + return true; +} +void SockNum::unsetSocketOfIOS(int sock){ + //NSLog(@"unsetSocketOfIOS:%d",sock); + if (readStream) { + CFReadStreamClose((CFReadStreamRef)readStream); + readStream=NULL; + } + if (writeStream) { + CFWriteStreamClose((CFWriteStreamRef)writeStream); + writeStream=NULL; + } +} +#endif //OS_IPHONE + + + +} // namespace toolkit diff --git a/3rdpart/ZLToolKit/src/Network/TcpClient.cpp b/3rdpart/ZLToolKit/src/Network/TcpClient.cpp new file mode 100644 index 0000000..6138f41 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Network/TcpClient.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "TcpClient.h" + +using namespace std; + +namespace toolkit { + +StatisticImp(TcpClient) + +TcpClient::TcpClient(const EventPoller::Ptr &poller) : SocketHelper(nullptr) { + setPoller(poller ? poller : EventPollerPool::Instance().getPoller()); + setOnCreateSocket([](const EventPoller::Ptr &poller) { + //TCP客户端默认开启互斥锁 [AUTO-TRANSLATED:94fad9cd] + //TCP client defaults to enabling mutex lock + return Socket::createSocket(poller, true); + }); +} + +TcpClient::~TcpClient() { + TraceL << "~" << TcpClient::getIdentifier(); +} + +void TcpClient::shutdown(const SockException &ex) { + _timer.reset(); + SocketHelper::shutdown(ex); +} + +bool TcpClient::alive() const { + if (_timer) { + //连接中或已连接 [AUTO-TRANSLATED:bf2b744a] + //Connecting or already connected + return true; + } + //在websocket client(zlmediakit)相关代码中, [AUTO-TRANSLATED:d309d587] + //In websocket client (zlmediakit) related code, + //_timer一直为空,但是socket fd有效,alive状态也应该返回true [AUTO-TRANSLATED:344889b8] + //_timer is always empty, but socket fd is valid, and alive status should also return true + auto sock = getSock(); + return sock && sock->alive(); +} + +void TcpClient::setNetAdapter(const string &local_ip) { + _net_adapter = local_ip; +} + +void TcpClient::startConnect(const string &url, uint16_t port, float timeout_sec, uint16_t local_port) { + weak_ptr weak_self = static_pointer_cast(shared_from_this()); + _timer = std::make_shared(2.0f, [weak_self]() { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return false; + } + strong_self->onManager(); + return true; + }, getPoller()); + + setSock(createSocket()); + + auto sock_ptr = getSock().get(); + sock_ptr->setOnErr([weak_self, sock_ptr](const SockException &ex) { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + if (sock_ptr != strong_self->getSock().get()) { + //已经重连socket,上次的socket的事件忽略掉 [AUTO-TRANSLATED:9bf35a7a] + //Socket has been reconnected, last socket's event is ignored + return; + } + strong_self->_timer.reset(); + TraceL << strong_self->getIdentifier() << " on err: " << ex; + strong_self->onError(ex); + }); + + TraceL << getIdentifier() << " start connect " << url << ":" << port; + sock_ptr->connect(url, port, [weak_self](const SockException &err) { + auto strong_self = weak_self.lock(); + if (strong_self) { + strong_self->onSockConnect(err); + } + }, timeout_sec, _net_adapter, local_port); +} + +void TcpClient::onSockConnect(const SockException &ex) { + TraceL << getIdentifier() << " connect result: " << ex; + if (ex) { + //连接失败 [AUTO-TRANSLATED:33415985] + //Connection failed + _timer.reset(); + onConnect(ex); + return; + } + + auto sock_ptr = getSock().get(); + weak_ptr weak_self = static_pointer_cast(shared_from_this()); + sock_ptr->setOnFlush([weak_self, sock_ptr]() { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return false; + } + if (sock_ptr != strong_self->getSock().get()) { + //已经重连socket,上传socket的事件忽略掉 [AUTO-TRANSLATED:243a8c95] + //Socket has been reconnected, upload socket's event is ignored + return false; + } + strong_self->onFlush(); + return true; + }); + + sock_ptr->setOnRead([weak_self, sock_ptr](const Buffer::Ptr &pBuf, struct sockaddr *, int) { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + if (sock_ptr != strong_self->getSock().get()) { + //已经重连socket,上传socket的事件忽略掉 [AUTO-TRANSLATED:243a8c95] + //Socket has been reconnected, upload socket's event is ignored + return; + } + try { + strong_self->onRecv(pBuf); + } catch (std::exception &ex) { + strong_self->shutdown(SockException(Err_other, ex.what())); + } + }); + + onConnect(ex); +} + +std::string TcpClient::getIdentifier() const { + if (_id.empty()) { + static atomic s_index{ 0 }; + _id = toolkit::demangle(typeid(*this).name()) + "-" + to_string(++s_index); + } + return _id; +} + +size_t TcpClient::getSendSpeed() const { + auto sock = getSock(); + if (sock) { + return sock->getSendSpeed(); + } + return 0; +} + +size_t TcpClient::getRecvSpeed() const { + auto sock = getSock(); + if (sock) { + return sock->getRecvSpeed(); + } + return 0; +} + +size_t TcpClient::getRecvTotalBytes() const { + auto sock = getSock(); + if (sock) { + return sock->getRecvTotalBytes(); + } + return 0; +} + +size_t TcpClient::getSendTotalBytes() const { + auto sock = getSock(); + if (sock) { + return sock->getSendTotalBytes(); + } + return 0; +} + +} /* namespace toolkit */ diff --git a/3rdpart/ZLToolKit/src/Network/TcpClient.h b/3rdpart/ZLToolKit/src/Network/TcpClient.h new file mode 100644 index 0000000..7f3936f --- /dev/null +++ b/3rdpart/ZLToolKit/src/Network/TcpClient.h @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef NETWORK_TCPCLIENT_H +#define NETWORK_TCPCLIENT_H + +#include +#include "Socket.h" +#include "Util/SSLBox.h" + +namespace toolkit { + +//Tcp客户端,Socket对象默认开始互斥锁 [AUTO-TRANSLATED:5cc9a824] +//Tcp client, Socket object defaults to starting mutex lock +class TcpClient : public SocketHelper { +public: + using Ptr = std::shared_ptr; + TcpClient(const EventPoller::Ptr &poller = nullptr); + ~TcpClient() override; + + /** + * 开始连接tcp服务器 + * @param url 服务器ip或域名 + * @param port 服务器端口 + * @param timeout_sec 超时时间,单位秒 + * @param local_port 本地端口 + * Start connecting to the TCP server + * @param url Server IP or domain name + * @param port Server port + * @param timeout_sec Timeout time, in seconds + * @param local_port Local port + + * [AUTO-TRANSLATED:7aa87355] + */ + virtual void startConnect(const std::string &url, uint16_t port, float timeout_sec = 5, uint16_t local_port = 0); + + /** + * 通过代理开始连接tcp服务器 + * @param url 服务器ip或域名 + * @proxy_host 代理ip + * @proxy_port 代理端口 + * @param timeout_sec 超时时间,单位秒 + * @param local_port 本地端口 + * Start connecting to the TCP server through a proxy + * @param url Server IP or domain name + * @proxy_host Proxy IP + * @proxy_port Proxy port + * @param timeout_sec Timeout time, in seconds + * @param local_port Local port + + * [AUTO-TRANSLATED:2739bd58] + */ + virtual void startConnectWithProxy(const std::string &url, const std::string &proxy_host, uint16_t proxy_port, float timeout_sec = 5, uint16_t local_port = 0){}; + + /** + * 主动断开连接 + * @param ex 触发onErr事件时的参数 + * Actively disconnect the connection + * @param ex Parameter when triggering the onErr event + + * [AUTO-TRANSLATED:5f6f3017] + */ + void shutdown(const SockException &ex = SockException(Err_shutdown, "self shutdown")) override; + + /** + * 连接中或已连接返回true,断开连接时返回false + * Returns true if connected or connecting, returns false if disconnected + + * [AUTO-TRANSLATED:60595edc] + */ + virtual bool alive() const; + + /** + * 设置网卡适配器,使用该网卡与服务器通信 + * @param local_ip 本地网卡ip + * Set the network card adapter, use this network card to communicate with the server + * @param local_ip Local network card IP + + * [AUTO-TRANSLATED:2549c18d] + */ + virtual void setNetAdapter(const std::string &local_ip); + + /** + * 唯一标识 + * Unique identifier + + * [AUTO-TRANSLATED:6b21021f] + */ + std::string getIdentifier() const override; + + size_t getSendSpeed() const; + + size_t getRecvSpeed() const; + + size_t getRecvTotalBytes() const; + + size_t getSendTotalBytes() const; + +protected: + /** + * 连接服务器结果回调 + * @param ex 成功与否 + * Connection result callback + * @param ex Success or failure + + * [AUTO-TRANSLATED:103bb2cb] + */ + virtual void onConnect(const SockException &ex) = 0; + + /** + * tcp连接成功后每2秒触发一次该事件 + * Trigger this event every 2 seconds after a successful TCP connection + + * [AUTO-TRANSLATED:37b40b5d] + */ + void onManager() override {} + +private: + void onSockConnect(const SockException &ex); + +private: + mutable std::string _id; + std::string _net_adapter = "::"; + std::shared_ptr _timer; + //对象个数统计 [AUTO-TRANSLATED:3b43e8c2] + //Object count statistics + ObjectStatistic _statistic; +}; + +//用于实现TLS客户端的模板对象 [AUTO-TRANSLATED:e4d399a3] +//Template object for implementing TLS client +template +class TcpClientWithSSL : public TcpClientType { +public: + using Ptr = std::shared_ptr; + + template + TcpClientWithSSL(ArgsType &&...args):TcpClientType(std::forward(args)...) {} + + ~TcpClientWithSSL() override { + if (_ssl_box) { + _ssl_box->flush(); + } + } + + void onRecv(const Buffer::Ptr &buf) override { + if (_ssl_box) { + _ssl_box->onRecv(buf); + } else { + TcpClientType::onRecv(buf); + } + } + + // 使能其他未被重写的send函数 [AUTO-TRANSLATED:5f01f91b] + //Enable other unoverridden send functions + using TcpClientType::send; + + ssize_t send(Buffer::Ptr buf) override { + if (_ssl_box) { + auto size = buf->size(); + _ssl_box->onSend(std::move(buf)); + return size; + } + return TcpClientType::send(std::move(buf)); + } + + //添加public_onRecv和public_send函数是解决较低版本gcc一个lambad中不能访问protected或private方法的bug [AUTO-TRANSLATED:210f092e] + //Adding public_onRecv and public_send functions is to solve a bug in lower version gcc where a lambda cannot access protected or private methods + inline void public_onRecv(const Buffer::Ptr &buf) { + TcpClientType::onRecv(buf); + } + + inline void public_send(const Buffer::Ptr &buf) { + TcpClientType::send(buf); + } + + void startConnect(const std::string &url, uint16_t port, float timeout_sec = 5, uint16_t local_port = 0) override { + _host = url; + TcpClientType::startConnect(url, port, timeout_sec, local_port); + } + void startConnectWithProxy(const std::string &url, const std::string &proxy_host, uint16_t proxy_port, float timeout_sec = 5, uint16_t local_port = 0) override { + _host = url; + TcpClientType::startConnect(proxy_host, proxy_port, timeout_sec, local_port); + } + + bool overSsl() const override { return (bool)_ssl_box; } + +protected: + void onConnect(const SockException &ex) override { + if (!ex) { + _ssl_box = std::make_shared(false); + _ssl_box->setOnDecData([this](const Buffer::Ptr &buf) { + public_onRecv(buf); + }); + _ssl_box->setOnEncData([this](const Buffer::Ptr &buf) { + public_send(buf); + }); + + if (!isIP(_host.data())) { + //设置ssl域名 [AUTO-TRANSLATED:1286a860] + //Set ssl domain + _ssl_box->setHost(_host.data()); + } + } + TcpClientType::onConnect(ex); + } + /** + * 重置ssl, 主要为了解决一些302跳转时http与https的转换 + * Reset ssl, mainly to solve some 302 redirects when switching between http and https + + * [AUTO-TRANSLATED:12ad26da] + */ + void setDoNotUseSSL() { + _ssl_box.reset(); + } +private: + std::string _host; + std::shared_ptr _ssl_box; +}; + +} /* namespace toolkit */ +#endif /* NETWORK_TCPCLIENT_H */ diff --git a/3rdpart/ZLToolKit/src/Network/TcpServer.cpp b/3rdpart/ZLToolKit/src/Network/TcpServer.cpp new file mode 100644 index 0000000..6474022 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Network/TcpServer.cpp @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "TcpServer.h" +#include "Util/uv_errno.h" +#include "Util/onceToken.h" + +using namespace std; + +namespace toolkit { + +INSTANCE_IMP(SessionMap) +StatisticImp(TcpServer) + +TcpServer::TcpServer(const EventPoller::Ptr &poller) : Server(poller) { + _multi_poller = !poller; + setOnCreateSocket(nullptr); +} + +void TcpServer::setupEvent() { + _socket = createSocket(_poller); + weak_ptr weak_self = std::static_pointer_cast(shared_from_this()); +#if 1 + _socket->setOnBeforeAccept([weak_self](const EventPoller::Ptr &poller) -> Socket::Ptr { + if (auto strong_self = weak_self.lock()) { + return strong_self->onBeforeAcceptConnection(poller); + } + return nullptr; + }); + _socket->setOnAccept([weak_self](Socket::Ptr &sock, shared_ptr &complete) { + if (auto strong_self = weak_self.lock()) { + auto ptr = sock->getPoller().get(); + auto server = strong_self->getServer(ptr); + ptr->async([server, sock, complete]() { + // 该tcp客户端派发给对应线程的TcpServer服务器 [AUTO-TRANSLATED:662b882f] + // This TCP client is dispatched to the corresponding thread of the TcpServer server + server->onAcceptConnection(sock); + }); + } + }); +#else + _socket->setOnAccept([weak_self](Socket::Ptr &sock, shared_ptr &complete) { + if (auto strong_self = weak_self.lock()) { + if (strong_self->_multi_poller) { + EventPollerPool::Instance().getExecutor([sock, complete, weak_self](const TaskExecutor::Ptr &exe) { + if (auto strong_self = weak_self.lock()) { + sock->moveTo(static_pointer_cast(exe)); + strong_self->getServer(sock->getPoller().get())->onAcceptConnection(sock); + } + }); + } else { + strong_self->onAcceptConnection(sock); + } + } + }); +#endif +} + +TcpServer::~TcpServer() { + if (_main_server && _socket && _socket->rawFD() != -1) { + InfoL << "Close tcp server [" << _socket->get_local_ip() << "]: " << _socket->get_local_port(); + } + _timer.reset(); + //先关闭socket监听,防止收到新的连接 [AUTO-TRANSLATED:cd65064f] + //First close the socket listening to prevent receiving new connections + _socket.reset(); + _session_map.clear(); + _cloned_server.clear(); +} + +uint16_t TcpServer::getPort() { + if (!_socket) { + return 0; + } + return _socket->get_local_port(); +} + +void TcpServer::setOnCreateSocket(Socket::onCreateSocket cb) { + if (cb) { + _on_create_socket = std::move(cb); + } else { + _on_create_socket = [](const EventPoller::Ptr &poller) { + return Socket::createSocket(poller, false); + }; + } + for (auto &pr : _cloned_server) { + pr.second->setOnCreateSocket(cb); + } +} + +TcpServer::Ptr TcpServer::onCreatServer(const EventPoller::Ptr &poller) { + return Ptr(new TcpServer(poller), [poller](TcpServer *ptr) { poller->async([ptr]() { delete ptr; }); }); +} + +Socket::Ptr TcpServer::onBeforeAcceptConnection(const EventPoller::Ptr &poller) { + assert(_poller->isCurrentThread()); + //此处改成自定义获取poller对象,防止负载不均衡 [AUTO-TRANSLATED:16c66457] + //Modify this to a custom way of getting the poller object to prevent load imbalance + return createSocket(_multi_poller ? EventPollerPool::Instance().getPoller(false) : _poller); +} + +void TcpServer::cloneFrom(const TcpServer &that) { + if (!that._socket) { + throw std::invalid_argument("TcpServer::cloneFrom other with null socket"); + } + setupEvent(); + _main_server = false; + _on_create_socket = that._on_create_socket; + _session_alloc = that._session_alloc; + _multi_poller = that._multi_poller; + weak_ptr weak_self = std::static_pointer_cast(shared_from_this()); + _timer = std::make_shared(2.0f, [weak_self]() -> bool { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return false; + } + strong_self->onManagerSession(); + return true; + }, _poller); + this->mINI::operator=(that); + _parent = static_pointer_cast(const_cast(that).shared_from_this()); +} + +// 接收到客户端连接请求 [AUTO-TRANSLATED:8a67b72a] +//Received a client connection request +Session::Ptr TcpServer::onAcceptConnection(const Socket::Ptr &sock) { + assert(_poller->isCurrentThread()); + weak_ptr weak_self = std::static_pointer_cast(shared_from_this()); + //创建一个Session;这里实现创建不同的服务会话实例 [AUTO-TRANSLATED:9ed745be] + //Create a Session; here implement creating different service session instances + auto helper = _session_alloc(std::static_pointer_cast(shared_from_this()), sock); + auto session = helper->session(); + //把本服务器的配置传递给Session [AUTO-TRANSLATED:e3711484] + //Pass the configuration of this server to the Session + session->attachServer(*this); + + //_session_map::emplace肯定能成功 [AUTO-TRANSLATED:09d4aef7] + //_session_map::emplace will definitely succeed + auto success = _session_map.emplace(helper.get(), helper).second; + assert(success == true); + + weak_ptr weak_session = session; + //会话接收数据事件 [AUTO-TRANSLATED:f3f4cbbb] + //Session receives data event + sock->setOnRead([weak_session](const Buffer::Ptr &buf, struct sockaddr *, int) { + //获取会话强应用 [AUTO-TRANSLATED:187497e6] + //Get the strong application of the session + auto strong_session = weak_session.lock(); + if (!strong_session) { + return; + } + try { + strong_session->onRecv(buf); + } catch (SockException &ex) { + strong_session->shutdown(ex); + } catch (exception &ex) { + strong_session->shutdown(SockException(Err_shutdown, ex.what())); + } + }); + + SessionHelper *ptr = helper.get(); + auto cls = ptr->className(); + //会话接收到错误事件 [AUTO-TRANSLATED:b000e868] + //Session receives an error event + sock->setOnErr([weak_self, weak_session, ptr, cls](const SockException &err) { + //在本函数作用域结束时移除会话对象 [AUTO-TRANSLATED:5c4433b8] + //Remove the session object when the function scope ends + //目的是确保移除会话前执行其onError函数 [AUTO-TRANSLATED:1e6c65df] + //The purpose is to ensure that the onError function is executed before removing the session + //同时避免其onError函数抛异常时没有移除会话对象 [AUTO-TRANSLATED:6d541cbd] + //And avoid not removing the session object when the onError function throws an exception + onceToken token(nullptr, [&]() { + //移除掉会话 [AUTO-TRANSLATED:e7c27790] + //Remove the session + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + + assert(strong_self->_poller->isCurrentThread()); + if (!strong_self->_is_on_manager) { + //该事件不是onManager时触发的,直接操作map [AUTO-TRANSLATED:d90ee039] + //This event is not triggered by onManager, directly operate on the map + strong_self->_session_map.erase(ptr); + } else { + //遍历map时不能直接删除元素 [AUTO-TRANSLATED:0f00040c] + //Cannot directly delete elements when traversing the map + strong_self->_poller->async([weak_self, ptr]() { + auto strong_self = weak_self.lock(); + if (strong_self) { + strong_self->_session_map.erase(ptr); + } + }, false); + } + }); + + //获取会话强应用 [AUTO-TRANSLATED:187497e6] + //Get the strong reference of the session + auto strong_session = weak_session.lock(); + if (strong_session) { + //触发onError事件回调 [AUTO-TRANSLATED:825d16df] + //Trigger the onError event callback + TraceP(strong_session) << cls << " on err: " << err; + strong_session->onError(err); + } + }); + return session; +} + +void TcpServer::start_l(uint16_t port, const std::string &host, uint32_t backlog) { + setupEvent(); + + //新建一个定时器定时管理这些tcp会话 [AUTO-TRANSLATED:ef859bd7] + //Create a new timer to manage these TCP sessions periodically + weak_ptr weak_self = std::static_pointer_cast(shared_from_this()); + _timer = std::make_shared(2.0f, [weak_self]() -> bool { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return false; + } + strong_self->onManagerSession(); + return true; + }, _poller); + + if (_multi_poller) { + EventPollerPool::Instance().for_each([&](const TaskExecutor::Ptr &executor) { + EventPoller::Ptr poller = static_pointer_cast(executor); + if (poller == _poller) { + return; + } + auto &serverRef = _cloned_server[poller.get()]; + if (!serverRef) { + serverRef = onCreatServer(poller); + } + if (serverRef) { + serverRef->cloneFrom(*this); + } + }); + } + + if (!_socket->listen(port, host.c_str(), backlog)) { + // 创建tcp监听失败,可能是由于端口占用或权限问题 [AUTO-TRANSLATED:88ebdefc] + //TCP listener creation failed, possibly due to port occupation or permission issues + string err = (StrPrinter << "Listen on " << host << " " << port << " failed: " << get_uv_errmsg(true)); + throw std::runtime_error(err); + } + for (auto &pr: _cloned_server) { + // 启动子Server [AUTO-TRANSLATED:1820131c] + //Start the child Server + pr.second->_socket->cloneSocket(*_socket); + } + + InfoL << "TCP server listening on [" << host << "]: " << port; +} + +void TcpServer::onManagerSession() { + assert(_poller->isCurrentThread()); + + onceToken token([&]() { + _is_on_manager = true; + }, [&]() { + _is_on_manager = false; + }); + + for (auto &pr : _session_map) { + //遍历时,可能触发onErr事件(也会操作_session_map) [AUTO-TRANSLATED:7760b80d] + //When traversing, the onErr event may be triggered (also operates on _session_map) + try { + pr.second->session()->onManager(); + } catch (exception &ex) { + WarnL << ex.what(); + } + } +} + +Socket::Ptr TcpServer::createSocket(const EventPoller::Ptr &poller) { + return _on_create_socket(poller); +} + +TcpServer::Ptr TcpServer::getServer(const EventPoller *poller) const { + auto parent = _parent.lock(); + auto &ref = parent ? parent->_cloned_server : _cloned_server; + auto it = ref.find(poller); + if (it != ref.end()) { + //派发到cloned server [AUTO-TRANSLATED:8765ab56] + //Dispatch to the cloned server + return it->second; + } + //派发到parent server [AUTO-TRANSLATED:4cf34169] + //Dispatch to the parent server + return static_pointer_cast(parent ? parent : const_cast(this)->shared_from_this()); +} + +Session::Ptr TcpServer::createSession(const Socket::Ptr &sock) { + return getServer(sock->getPoller().get())->onAcceptConnection(sock); +} + +} /* namespace toolkit */ + diff --git a/3rdpart/ZLToolKit/src/Network/TcpServer.h b/3rdpart/ZLToolKit/src/Network/TcpServer.h new file mode 100644 index 0000000..344653c --- /dev/null +++ b/3rdpart/ZLToolKit/src/Network/TcpServer.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef TCPSERVER_TCPSERVER_H +#define TCPSERVER_TCPSERVER_H + +#include +#include +#include +#include "Server.h" +#include "Session.h" +#include "Poller/Timer.h" +#include "Util/util.h" + +namespace toolkit { + +//TCP服务器,可配置的;配置通过Session::attachServer方法传递给会话对象 [AUTO-TRANSLATED:4e55c332] +//Configurable TCP server; configuration is passed to the session object through the Session::attachServer method +class TcpServer : public Server { +public: + using Ptr = std::shared_ptr; + + /** + * 创建tcp服务器,listen fd的accept事件会加入到所有的poller线程中监听 + * 在调用TcpServer::start函数时,内部会创建多个子TcpServer对象, + * 这些子TcpServer对象通过Socket对象克隆的方式在多个poller线程中监听同一个listen fd + * 这样这个TCP服务器将会通过抢占式accept的方式把客户端均匀的分布到不同的poller线程 + * 通过该方式能实现客户端负载均衡以及提高连接接收速度 + * Creates a TCP server, the accept event of the listen fd will be added to all poller threads for listening + * When calling the TcpServer::start function, multiple child TcpServer objects will be created internally, + * These child TcpServer objects will be cloned through the Socket object in multiple poller threads to listen to the same listen fd + * This way, the TCP server will distribute clients evenly across different poller threads through a preemptive accept approach + * This approach can achieve client load balancing and improve connection acceptance speed + + * [AUTO-TRANSLATED:761a6b1e] + */ + explicit TcpServer(const EventPoller::Ptr &poller = nullptr); + ~TcpServer() override; + + /** + * @brief 开始tcp server + * @param port 本机端口,0则随机 + * @param host 监听网卡ip + * @param backlog tcp listen backlog + * @brief Starts the TCP server + * @param port Local port, 0 for random + * @param host Listening network card IP + * @param backlog TCP listen backlog + + * [AUTO-TRANSLATED:9bab69b6] + */ + template + void start(uint16_t port, const std::string &host = "::", uint32_t backlog = 1024, const std::function &)> &cb = nullptr) { + static std::string cls_name = toolkit::demangle(typeid(SessionType).name()); + // Session创建器,通过它创建不同类型的服务器 [AUTO-TRANSLATED:f5585e1e] + //Session creator, creates different types of servers through it + _session_alloc = [cb](const TcpServer::Ptr &server, const Socket::Ptr &sock) { + auto session = std::shared_ptr(new SessionType(sock), [](SessionType *ptr) { + TraceP(static_cast(ptr)) << "~" << cls_name; + delete ptr; + }); + if (cb) { + cb(session); + } + TraceP(static_cast(session.get())) << cls_name; + session->setOnCreateSocket(server->_on_create_socket); + return std::make_shared(server, std::move(session), cls_name); + }; + start_l(port, host, backlog); + } + + /** + * @brief 获取服务器监听端口号, 服务器可以选择监听随机端口 + * @brief Gets the server listening port number, the server can choose to listen on a random port + + * [AUTO-TRANSLATED:125ff8d8] + */ + uint16_t getPort(); + + /** + * @brief 自定义socket构建行为 + * @brief Custom socket construction behavior + + * [AUTO-TRANSLATED:4cf98e86] + */ + void setOnCreateSocket(Socket::onCreateSocket cb); + + /** + * 根据socket对象创建Session对象 + * 需要确保在socket归属poller线程执行本函数 + * Creates a Session object based on the socket object + * Ensures that this function is executed in the poller thread that owns the socket + + * [AUTO-TRANSLATED:1d52d9ee] + */ + Session::Ptr createSession(const Socket::Ptr &socket); + +protected: + virtual void cloneFrom(const TcpServer &that); + virtual TcpServer::Ptr onCreatServer(const EventPoller::Ptr &poller); + + virtual Session::Ptr onAcceptConnection(const Socket::Ptr &sock); + virtual Socket::Ptr onBeforeAcceptConnection(const EventPoller::Ptr &poller); + +private: + void onManagerSession(); + Socket::Ptr createSocket(const EventPoller::Ptr &poller); + void start_l(uint16_t port, const std::string &host, uint32_t backlog); + Ptr getServer(const EventPoller *) const; + void setupEvent(); + +private: + bool _multi_poller; + bool _is_on_manager = false; + bool _main_server = true; + std::weak_ptr _parent; + Socket::Ptr _socket; + std::shared_ptr _timer; + Socket::onCreateSocket _on_create_socket; + std::unordered_map _session_map; + std::function _session_alloc; + std::unordered_map _cloned_server; + //对象个数统计 [AUTO-TRANSLATED:3b43e8c2] + //Object count statistics + ObjectStatistic _statistic; +}; + +} /* namespace toolkit */ +#endif /* TCPSERVER_TCPSERVER_H */ diff --git a/3rdpart/ZLToolKit/src/Network/UdpClient.cpp b/3rdpart/ZLToolKit/src/Network/UdpClient.cpp new file mode 100644 index 0000000..f393467 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Network/UdpClient.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "UdpClient.h" + +using namespace std; + +namespace toolkit { + +StatisticImp(UdpClient) + +UdpClient::UdpClient(const EventPoller::Ptr &poller) : SocketHelper(nullptr) { + setPoller(poller ? poller : EventPollerPool::Instance().getPoller()); + setOnCreateSocket([](const EventPoller::Ptr &poller) { + //TCP客户端默认开启互斥锁 + return Socket::createSocket(poller, true); + }); +} + +UdpClient::~UdpClient() { + TraceL << "~" << UdpClient::getIdentifier(); +} + +void UdpClient::startConnect(const string &peer_host, uint16_t peer_port, uint16_t local_port) { + weak_ptr weak_self = static_pointer_cast(shared_from_this()); + _timer = std::make_shared(2.0f, [weak_self]() { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return false; + } + strong_self->onManager(); + return true; + }, getPoller()); + + setSock(createSocket()); + + auto sock_ptr = getSock().get(); + sock_ptr->setOnErr([weak_self, sock_ptr](const SockException &ex) { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + if (sock_ptr != strong_self->getSock().get()) { + //已经重连socket,上次的socket的事件忽略掉 + return; + } + strong_self->_timer.reset(); + TraceL << strong_self->getIdentifier() << " on err: " << ex; + strong_self->onError(ex); + }); + + sock_ptr->setOnFlush([weak_self, sock_ptr]() { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return false; + } + if (sock_ptr != strong_self->getSock().get()) { + //已经重连socket,上传socket的事件忽略掉 + return false; + } + strong_self->onFlush(); + return true; + }); + + sock_ptr->setOnRead([weak_self, sock_ptr](const Buffer::Ptr &pBuf, struct sockaddr * addr, int addr_len) { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + if (sock_ptr != strong_self->getSock().get()) { + //已经重连socket,上传socket的事件忽略掉 + return; + } + try { + strong_self->onRecvFrom(pBuf, addr, addr_len); + } catch (std::exception &ex) { + strong_self->shutdown(SockException(Err_other, ex.what())); + } + }); + + bool ret = getSock()->bindUdpSock(local_port, _net_adapter); + if (!ret) { + WarnL << "UDP output bind local error"; + } + auto peer_addr = SockUtil::make_sockaddr(peer_host.c_str(), peer_port); + + //只能软绑定 + ret = getSock()->bindPeerAddr((struct sockaddr *)&peer_addr, 0, true); + if (!ret) { + WarnL << "UDP output bind peer error"; + } + // TraceL << getIdentifier() << " start connect " << url << ":" << peer_port; +} + +void UdpClient::shutdown(const SockException &ex) { + _timer.reset(); + SocketHelper::shutdown(ex); +} + +bool UdpClient::alive() const { + if (_timer) { + //连接中或已连接 + return true; + } + //在websocket client(zlmediakit)相关代码中, + //_timer一直为空,但是socket fd有效,alive状态也应该返回true + auto sock = getSock(); + return sock && sock->alive(); +} + +void UdpClient::setNetAdapter(const string &local_ip) { + _net_adapter = local_ip; +} + +std::string UdpClient::getIdentifier() const { + if (_id.empty()) { + static atomic s_index { 0 }; + _id = toolkit::demangle(typeid(*this).name()) + "-" + to_string(++s_index); + } + return _id; +} + +} /* namespace toolkit */ diff --git a/3rdpart/ZLToolKit/src/Network/UdpClient.h b/3rdpart/ZLToolKit/src/Network/UdpClient.h new file mode 100644 index 0000000..a984235 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Network/UdpClient.h @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef NETWORK_UDPCLIENT_H +#define NETWORK_UDPCLIENT_H + +#include +#include "Socket.h" +#include "Util/SSLBox.h" +#include "Kcp.h" + +namespace toolkit { + +//Udp客户端,Socket对象默认开始互斥锁 +class UdpClient : public SocketHelper { +public: + using Ptr = std::shared_ptr; + using OnRecvFrom = std::function; + using OnErr = std::function; + + UdpClient(const EventPoller::Ptr &poller = nullptr); + ~UdpClient() override; + + /** + * 开始连接udp服务器 + * @param peer_host 服务器ip或域名 + * @param peer_port 服务器端口 + * @param local_port 本地端口 + */ + virtual void startConnect(const std::string &peer_host, uint16_t peer_port, uint16_t local_port = 0); + + /** + * 主动断开连接 + * @param ex 触发onErr事件时的参数 + */ + void shutdown(const SockException &ex = SockException(Err_shutdown, "self shutdown")) override; + + /** + * 连接中或已连接返回true,断开连接时返回false + */ + virtual bool alive() const; + + /** + * 设置网卡适配器,使用该网卡与服务器通信 + * @param local_ip 本地网卡ip + */ + virtual void setNetAdapter(const std::string &local_ip); + + /** + * 唯一标识 + */ + std::string getIdentifier() const override; + + void setOnRecvFrom(OnRecvFrom cb) { + _on_recvfrom = std::move(cb); + } + + void setOnError(OnErr cb) { + _on_err = std::move(cb); + } + +protected: + + virtual void onRecvFrom(const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len) { + if (_on_recvfrom) { + _on_recvfrom(buf, addr, addr_len); + } + } + + void onRecv(const Buffer::Ptr &buf) override {} + + void onError(const SockException &err) override { + DebugL; + if (_on_err) { + _on_err(err); + } + } + + /** + * udp连接成功后每2秒触发一次该事件 + */ + void onManager() override {} + +private: + mutable std::string _id; + std::string _net_adapter = "::"; + std::shared_ptr _timer; + //对象个数统计 + ObjectStatistic _statistic; + + OnRecvFrom _on_recvfrom; + OnErr _on_err; +}; + +//用于实现KCP客户端的模板对象 +template +class UdpClientWithKcp : public UdpClientType { +public: + using Ptr = std::shared_ptr; + + template + UdpClientWithKcp(ArgsType &&...args) + :UdpClientType(std::forward(args)...) { + _kcp_box = std::make_shared(false); + _kcp_box->setOnWrite([&](const Buffer::Ptr &buf) { public_send(buf); }); + _kcp_box->setOnRead([&](const Buffer::Ptr &buf) { public_onRecv(buf); }); + _kcp_box->setOnErr([&](const SockException &ex) { public_onErr(ex); }); + } + + ~UdpClientWithKcp() override { } + + void onRecvFrom(const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len) override { + //KCP 暂不支持一个UDP Socket 对多个目标,因此先忽略addr参数 + _kcp_box->input(buf); + } + + ssize_t send(Buffer::Ptr buf) override { + return _kcp_box->send(std::move(buf)); + } + + ssize_t sendto(Buffer::Ptr buf, struct sockaddr *addr = nullptr, socklen_t addr_len = 0) override { + //KCP 暂不支持一个UDP Socket 对多个目标,因此先忽略addr参数 + return _kcp_box->send(std::move(buf)); + } + + inline void public_onRecv(const Buffer::Ptr &buf) { + //KCP 暂不支持一个UDP Socket 对多个目标,因此固定采用bind的地址参数 + UdpClientType::onRecvFrom(buf, (struct sockaddr*)&_peer_addr, _peer_addr_len); + } + + inline void public_send(const Buffer::Ptr &buf) { + UdpClientType::send(buf); + } + + inline void public_onErr(const SockException &ex) { UdpClientWithKcp::onError(ex); } + + virtual void startConnect(const std::string &peer_host, uint16_t peer_port, uint16_t local_port = 0) override { + _kcp_box->setPoller(UdpClientType::getPoller()); + _peer_addr = SockUtil::make_sockaddr(peer_host.data(), peer_port); + _peer_addr_len = SockUtil::get_sock_len((const struct sockaddr*)&_peer_addr); + UdpClientType::startConnect(peer_host, peer_port, local_port); + } + + void setMtu(int mtu) { + _kcp_box->setMtu(mtu); + } + + void setInterval(int intervoal) { + _kcp_box->setInterval(intervoal); + } + + void setRxMinrto(int rx_minrto) { + _kcp_box->setRxMinrto(rx_minrto); + } + + void setWndSize(int sndwnd, int rcvwnd) { + _kcp_box->setWndSize(sndwnd, rcvwnd); + } + + void setDelayMode(KcpTransport::DelayMode delay_mode) { + _kcp_box->setDelayMode(delay_mode); + } + + void setFastResend(int resend) { + _kcp_box->setFastResend(resend); + } + + void setFastackConserve(bool flag) { + _kcp_box->setFastackConserve(flag); + } + + void setNoCwnd(bool flag) { + _kcp_box->setNoCwnd(flag); + } + + void setStreamMode(bool flag) { + _kcp_box->setStreamMode(flag); + } + +private: + struct sockaddr_storage _peer_addr; + int _peer_addr_len = 0; + KcpTransport::Ptr _kcp_box; +}; + +} /* namespace toolkit */ +#endif /* NETWORK_UDPCLIENT_H */ diff --git a/3rdpart/ZLToolKit/src/Network/UdpServer.cpp b/3rdpart/ZLToolKit/src/Network/UdpServer.cpp new file mode 100644 index 0000000..489c5b6 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Network/UdpServer.cpp @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2021 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "Util/uv_errno.h" +#include "Util/onceToken.h" +#include "UdpServer.h" + +using namespace std; + +namespace toolkit { + +static const uint8_t s_in6_addr_maped[] + = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }; + +static constexpr auto kUdpDelayCloseMS = 3 * 1000; + +static UdpServer::PeerIdType makeSockId(sockaddr *addr, int) { + UdpServer::PeerIdType ret; + switch (addr->sa_family) { + case AF_INET : { + ret[0] = ((struct sockaddr_in *) addr)->sin_port >> 8; + ret[1] = ((struct sockaddr_in *) addr)->sin_port & 0xFF; + //ipv4地址统一转换为ipv6方式处理 [AUTO-TRANSLATED:ad7cf8c3] + //Convert ipv4 addresses to ipv6 for unified processing + memcpy(&ret[2], &s_in6_addr_maped, 12); + memcpy(&ret[14], &(((struct sockaddr_in *) addr)->sin_addr), 4); + return ret; + } + case AF_INET6 : { + ret[0] = ((struct sockaddr_in6 *) addr)->sin6_port >> 8; + ret[1] = ((struct sockaddr_in6 *) addr)->sin6_port & 0xFF; + memcpy(&ret[2], &(((struct sockaddr_in6 *)addr)->sin6_addr), 16); + return ret; + } + default: throw std::invalid_argument("invalid sockaddr address"); + } +} + +UdpServer::UdpServer(const EventPoller::Ptr &poller) : Server(poller) { + _multi_poller = !poller; + setOnCreateSocket(nullptr); +} + +void UdpServer::setupEvent() { + _socket = createSocket(_poller); + std::weak_ptr weak_self = std::static_pointer_cast(shared_from_this()); + _socket->setOnRead([weak_self](Buffer::Ptr &buf, struct sockaddr *addr, int addr_len) { + if (auto strong_self = weak_self.lock()) { + strong_self->onRead(buf, addr, addr_len); + } + }); +} + +UdpServer::~UdpServer() { + if (!_cloned && _socket && _socket->rawFD() != -1) { + InfoL << "Close udp server [" << _socket->get_local_ip() << "]: " << _socket->get_local_port(); + } + _timer.reset(); + _socket.reset(); + _cloned_server.clear(); + if (!_cloned && _session_mutex && _session_map) { + lock_guard lck(*_session_mutex); + _session_map->clear(); + } +} + +void UdpServer::start_l(uint16_t port, const std::string &host) { + setupEvent(); + //主server才创建session map,其他cloned server共享之 [AUTO-TRANSLATED:113cf4fd] + //Only the main server creates a session map, other cloned servers share it + _session_mutex = std::make_shared(); + _session_map = std::make_shared(); + + // 新建一个定时器定时管理这些 udp 会话,这些对象只由主server做超时管理,cloned server不管理 [AUTO-TRANSLATED:d20478a2] + //Create a timer to manage these udp sessions periodically, these objects are only managed by the main server, cloned servers do not manage them + std::weak_ptr weak_self = std::static_pointer_cast(shared_from_this()); + _timer = std::make_shared(2.0f, [weak_self]() -> bool { + if (auto strong_self = weak_self.lock()) { + strong_self->onManagerSession(); + return true; + } + return false; + }, _poller); + + if (_multi_poller) { + // clone server至不同线程,让udp server支持多线程 [AUTO-TRANSLATED:15a85c8f] + //Clone the server to different threads to support multi-threading for the udp server + EventPollerPool::Instance().for_each([&](const TaskExecutor::Ptr &executor) { + auto poller = std::static_pointer_cast(executor); + if (poller == _poller) { + return; + } + auto &serverRef = _cloned_server[poller.get()]; + if (!serverRef) { + serverRef = onCreatServer(poller); + } + if (serverRef) { + serverRef->cloneFrom(*this); + } + }); + } + + if (!_socket->bindUdpSock(port, host.c_str())) { + // udp 绑定端口失败, 可能是由于端口占用或权限问题 [AUTO-TRANSLATED:c31eedba] + //Failed to bind udp port, possibly due to port occupation or permission issues + std::string err = (StrPrinter << "Bind udp socket on " << host << " " << port << " failed: " << get_uv_errmsg(true)); + throw std::runtime_error(err); + } + + for (auto &pr: _cloned_server) { + // 启动子Server [AUTO-TRANSLATED:1820131c] + //Start the child server +#if 0 + pr.second->_socket->cloneSocket(*_socket); +#else + // 实验发现cloneSocket方式虽然可以节省fd资源,但是在某些系统上线程漂移问题更严重 [AUTO-TRANSLATED:d6a88e17] + //Experiments have found that the cloneSocket method can save fd resources, but the thread drift problem is more serious on some systems + pr.second->_socket->bindUdpSock(_socket->get_local_port(), _socket->get_local_ip()); +#endif + } + InfoL << "UDP server bind to [" << host << "]: " << port; +} + +UdpServer::Ptr UdpServer::onCreatServer(const EventPoller::Ptr &poller) { + return Ptr(new UdpServer(poller), [poller](UdpServer *ptr) { poller->async([ptr]() { delete ptr; }); }); +} + +void UdpServer::cloneFrom(const UdpServer &that) { + if (!that._socket) { + throw std::invalid_argument("UdpServer::cloneFrom other with null socket"); + } + // 将socket的创建回调复制前置, 确保所有的socket都可以通过上层创建 + _on_create_socket = that._on_create_socket; + + setupEvent(); + _cloned = true; + // clone callbacks + _session_alloc = that._session_alloc; + _session_mutex = that._session_mutex; + _session_map = that._session_map; + _multi_poller = that._multi_poller; + // clone properties + this->mINI::operator=(that); +} + +void UdpServer::onRead(Buffer::Ptr &buf, sockaddr *addr, int addr_len) { + const auto id = makeSockId(addr, addr_len); + onRead_l(true, id, buf, addr, addr_len); +} + +static void emitSessionRecv(const SessionHelper::Ptr &helper, const Buffer::Ptr &buf) { + if (!helper->enable) { + // 延时销毁中 [AUTO-TRANSLATED:24d3d333] + //Delayed destruction in progress + return; + } + try { + helper->session()->onRecv(buf); + } catch (SockException &ex) { + helper->session()->shutdown(ex); + } catch (exception &ex) { + helper->session()->shutdown(SockException(Err_shutdown, ex.what())); + } +} + +void UdpServer::onRead_l(bool is_server_fd, const UdpServer::PeerIdType &id, Buffer::Ptr &buf, sockaddr *addr, int addr_len) { + // udp server fd收到数据时触发此函数;大部分情况下数据应该在peer fd触发,此函数应该不是热点函数 [AUTO-TRANSLATED:f347ff20] + //This function is triggered when the udp server fd receives data; in most cases, the data should be triggered by the peer fd, and this function should not be a hot spot + bool is_new = false; + if (auto helper = getOrCreateSession(id, buf, addr, addr_len, is_new)) { + if (helper->session()->getPoller()->isCurrentThread()) { + //当前线程收到数据,直接处理数据 [AUTO-TRANSLATED:07e5a596] + //The current thread receives data and processes it directly + emitSessionRecv(helper, buf); + } else { + //数据漂移到其他线程,需要先切换线程 [AUTO-TRANSLATED:15235f6f] + //Data migration to another thread requires switching threads first +#if !defined(_WIN32) + WarnL << "UDP packet incoming from other thread"; +#endif + std::weak_ptr weak_helper = helper; + //由于socket读buffer是该线程上所有socket共享复用的,所以不能跨线程使用,必须先转移走 [AUTO-TRANSLATED:1134538b] + //Since the socket read buffer is shared and reused by all sockets on this thread, it cannot be used across threads and must be transferred first + auto cacheable_buf = std::move(buf); + helper->session()->async([weak_helper, cacheable_buf]() { + if (auto strong_helper = weak_helper.lock()) { + emitSessionRecv(strong_helper, cacheable_buf); + } + }); + } + +#if !defined(NDEBUG) && !defined(_WIN32) + if (!is_new) { + TraceL << "UDP packet incoming from " << (is_server_fd ? "server fd" : "other peer fd"); + } +#endif + } +} + +void UdpServer::onManagerSession() { + decltype(_session_map) copy_map; + { + std::lock_guard lock(*_session_mutex); + //拷贝map,防止遍历时移除对象 [AUTO-TRANSLATED:ebbc7595] + //Copy the map to prevent objects from being removed during traversal + copy_map = std::make_shared(*_session_map); + } + auto lam = [copy_map]() { + for (auto &pr : *copy_map) { + auto &session = pr.second->session(); + if (!session->getPoller()->isCurrentThread()) { + // 该session不归属该poller管理 [AUTO-TRANSLATED:d5edb552] + //This session does not belong to the management of this poller + continue; + } + try { + // UDP 会话需要处理超时 [AUTO-TRANSLATED:0a51f8a1] + //UDP sessions need to handle timeouts + session->onManager(); + } catch (exception &ex) { + WarnL << "Exception occurred when emit onManager: " << ex.what(); + } + } + }; + if (_multi_poller){ + EventPollerPool::Instance().for_each([lam](const TaskExecutor::Ptr &executor) { + std::static_pointer_cast(executor)->async(lam); + }); + } else { + lam(); + } +} + +SessionHelper::Ptr UdpServer::getOrCreateSession(const UdpServer::PeerIdType &id, Buffer::Ptr &buf, sockaddr *addr, int addr_len, bool &is_new) { + { + //减小临界区 [AUTO-TRANSLATED:3d6089d8] + //Reduce the critical section + std::lock_guard lock(*_session_mutex); + auto it = _session_map->find(id); + if (it != _session_map->end()) { + return it->second; + } + } + is_new = true; + return createSession(id, buf, addr, addr_len); +} + +SessionHelper::Ptr UdpServer::createSession(const PeerIdType &id, Buffer::Ptr &buf, struct sockaddr *addr, int addr_len) { + // 此处改成自定义获取poller对象,防止负载不均衡 [AUTO-TRANSLATED:194e8460] + //Change to custom acquisition of poller objects to prevent load imbalance + auto socket = createSocket(_multi_poller ? EventPollerPool::Instance().getPoller(false) : _poller, buf, addr, addr_len); + if (!socket) { + //创建socket失败,本次onRead事件收到的数据直接丢弃 [AUTO-TRANSLATED:b218d68c] + //Socket creation failed, the data received by this onRead event is discarded + return nullptr; + } + + auto addr_str = string((char *) addr, addr_len); + std::weak_ptr weak_self = std::static_pointer_cast(shared_from_this()); + auto helper_creator = [this, weak_self, socket, addr_str, id]() -> SessionHelper::Ptr { + auto server = weak_self.lock(); + if (!server) { + return nullptr; + } + + //如果已经创建该客户端对应的UdpSession类,那么直接返回 [AUTO-TRANSLATED:c57a0d71] + //If the UdpSession class corresponding to this client has already been created, return directly + lock_guard lck(*_session_mutex); + auto it = _session_map->find(id); + if (it != _session_map->end()) { + return it->second; + } + + assert(_socket); + socket->bindUdpSock(_socket->get_local_port(), _socket->get_local_ip()); + socket->bindPeerAddr((struct sockaddr *) addr_str.data(), addr_str.size()); + + auto helper = _session_alloc(server, socket); + // 把本服务器的配置传递给 Session [AUTO-TRANSLATED:e3ed95ab] + //Pass the configuration of this server to the Session + helper->session()->attachServer(*this); + + std::weak_ptr weak_helper = helper; + socket->setOnRead([weak_self, weak_helper, id](Buffer::Ptr &buf, struct sockaddr *addr, int addr_len) { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + auto new_id = makeSockId(addr, addr_len); + //快速判断是否为本会话的的数据, 通常应该成立 [AUTO-TRANSLATED:d5d147e4] + //Quickly determine if it's data for the current session, usually should be true + if (id == new_id) { + if (auto strong_helper = weak_helper.lock()) { + emitSessionRecv(strong_helper, buf); + } + return; + } + + //收到非本peer fd的数据,让server去派发此数据到合适的session对象 [AUTO-TRANSLATED:e5f44445] + //Received data from a non-current peer fd, let the server dispatch this data to the appropriate session object + strong_self->onRead_l(false, new_id, buf, addr, addr_len); + }); + socket->setOnErr([weak_self, weak_helper, id](const SockException &err) { + // 在本函数作用域结束时移除会话对象 [AUTO-TRANSLATED:b2ade305] + //Remove the session object when this function scope ends + // 目的是确保移除会话前执行其 onError 函数 [AUTO-TRANSLATED:7d0329d7] + //The purpose is to ensure the onError function is executed before removing the session + // 同时避免其 onError 函数抛异常时没有移除会话对象 [AUTO-TRANSLATED:354191bd] + //And avoid not removing the session object when its onError function throws an exception + onceToken token(nullptr, [&]() { + // 移除掉会话 [AUTO-TRANSLATED:1d786335] + //Remove the session + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + // 延时移除udp session, 防止频繁快速重建对象 [AUTO-TRANSLATED:50dbd694] + //Delay removing the UDP session to prevent frequent and rapid object reconstruction + strong_self->_poller->doDelayTask(kUdpDelayCloseMS, [weak_self, id]() { + if (auto strong_self = weak_self.lock()) { + // 从共享map中移除本session对象 [AUTO-TRANSLATED:47ecbf11] + //Remove the current session object from the shared map + lock_guard lck(*strong_self->_session_mutex); + strong_self->_session_map->erase(id); + } + return 0; + }); + }); + + // 获取会话强应用 [AUTO-TRANSLATED:42283ea0] + //Get a strong reference to the session + if (auto strong_helper = weak_helper.lock()) { + // 触发 onError 事件回调 [AUTO-TRANSLATED:82070c3c] + //Trigger the onError event callback + TraceP(strong_helper->session()) << strong_helper->className() << " on err: " << err; + strong_helper->enable = false; + strong_helper->session()->onError(err); + } + }); + + auto pr = _session_map->emplace(id, std::move(helper)); + assert(pr.second); + return pr.first->second; + }; + + if (socket->getPoller()->isCurrentThread()) { + // 该socket分配在本线程,直接创建helper对象 [AUTO-TRANSLATED:18c9d95b] + //This socket is allocated in this thread, directly create a helper object + return helper_creator(); + } + + // 该socket分配在其他线程,需要先转移走buffer,然后在其所在线程创建helper对象并处理数据 [AUTO-TRANSLATED:7816a13f] + //This socket is allocated in another thread, need to transfer the buffer first, then create a helper object in its thread and process the data + auto cacheable_buf = std::move(buf); + socket->getPoller()->async([helper_creator, cacheable_buf]() { + // 在该socket所在线程创建helper对象 [AUTO-TRANSLATED:db8d6622] + //Create a helper object in the thread where the socket is located + auto helper = helper_creator(); + if (helper) { + // 可能未实质创建hlepr对象成功,可能获取到其他线程创建的helper对象 [AUTO-TRANSLATED:091f648e] + //May not have actually created a helper object successfully, may have obtained a helper object created by another thread + helper->session()->getPoller()->async([helper, cacheable_buf]() { + // 该数据不能丢弃,给session对象消费 [AUTO-TRANSLATED:6941e5fa] + //This data cannot be discarded, provided to the session object for consumption + emitSessionRecv(helper, cacheable_buf); + }); + } + }); + return nullptr; +} + +void UdpServer::setOnCreateSocket(onCreateSocket cb) { + if (cb) { + _on_create_socket = std::move(cb); + } else { + _on_create_socket = [](const EventPoller::Ptr &poller, const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len) { + return Socket::createSocket(poller, false); + }; + } + for (auto &pr : _cloned_server) { + pr.second->setOnCreateSocket(cb); + } +} + +uint16_t UdpServer::getPort() { + if (!_socket) { + return 0; + } + return _socket->get_local_port(); +} + +Socket::Ptr UdpServer::createSocket(const EventPoller::Ptr &poller, const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len) { + return _on_create_socket(poller, buf, addr, addr_len); +} + + +StatisticImp(UdpServer) + +} // namespace toolkit diff --git a/3rdpart/ZLToolKit/src/Network/UdpServer.h b/3rdpart/ZLToolKit/src/Network/UdpServer.h new file mode 100644 index 0000000..a5151f7 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Network/UdpServer.h @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2021 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef TOOLKIT_NETWORK_UDPSERVER_H +#define TOOLKIT_NETWORK_UDPSERVER_H + +#if __cplusplus >= 201703L +#include +#include +#endif +#include "Server.h" +#include "Session.h" + +namespace toolkit { + +class UdpServer : public Server { +public: +#if __cplusplus >= 201703L + class PeerIdType : public std::array { +#else + class PeerIdType : public std::string { +#endif + public: +#if __cplusplus < 201703L + PeerIdType() { + resize(18); + } +#endif + bool operator==(const PeerIdType &that) const { + return as(0) == that.as(0) && + as(8) == that.as(8) && + as(16) == that.as(16); + } + + private: + template + const T& as(size_t offset) const { + return *(reinterpret_cast(data() + offset)); + } + }; + + using Ptr = std::shared_ptr; + using onCreateSocket = std::function; + + explicit UdpServer(const EventPoller::Ptr &poller = nullptr); + ~UdpServer() override; + + /** + * @brief 开始监听服务器 + * @brief Start listening to the server + + * [AUTO-TRANSLATED:342e9d0e] + */ + template + void start(uint16_t port, const std::string &host = "::", const std::function &)> &cb = nullptr) { + static std::string cls_name = toolkit::demangle(typeid(SessionType).name()); + // Session 创建器, 通过它创建不同类型的服务器 [AUTO-TRANSLATED:a419bcd3] + //Session creator, creates different types of servers through it + _session_alloc = [cb](const UdpServer::Ptr &server, const Socket::Ptr &sock) { + auto session = std::shared_ptr(new SessionType(sock), [](SessionType * ptr) { + TraceP(static_cast(ptr)) << "~" << cls_name; + delete ptr; + }); + if (cb) { + cb(session); + } + TraceP(static_cast(session.get())) << cls_name; + auto sock_creator = server->_on_create_socket; + session->setOnCreateSocket([sock_creator](const EventPoller::Ptr &poller) { + return sock_creator(poller, nullptr, nullptr, 0); + }); + return std::make_shared(server, std::move(session), cls_name); + }; + start_l(port, host); + } + + /** + * @brief 获取服务器监听端口号, 服务器可以选择监听随机端口 + * @brief Get the server listening port number, the server can choose to listen to a random port + + * [AUTO-TRANSLATED:125ff8d8] + */ + uint16_t getPort(); + + /** + * @brief 自定义socket构建行为 + * @brief Custom socket construction behavior + + * [AUTO-TRANSLATED:4cf98e86] + */ + void setOnCreateSocket(onCreateSocket cb); + +protected: + virtual Ptr onCreatServer(const EventPoller::Ptr &poller); + virtual void cloneFrom(const UdpServer &that); + +private: + struct PeerIdHash { +#if __cplusplus >= 201703L + size_t operator()(const PeerIdType &v) const noexcept { return std::hash {}(std::string_view(v.data(), v.size())); } +#else + size_t operator()(const PeerIdType &v) const noexcept { return std::hash {}(v); } +#endif + }; + using SessionMapType = std::unordered_map; + + /** + * @brief 开始udp server + * @param port 本机端口,0则随机 + * @param host 监听网卡ip + * @brief Start UDP server + * @param port Local port, 0 for random + * @param host Listening network card IP + + * [AUTO-TRANSLATED:1c46778d] + */ + void start_l(uint16_t port, const std::string &host = "::"); + + /** + * @brief 定时管理 Session, UDP 会话需要根据需要处理超时 + * @brief Periodically manage Session, UDP sessions need to handle timeouts as needed + + * [AUTO-TRANSLATED:86ff2f9c] + */ + void onManagerSession(); + + void onRead(Buffer::Ptr &buf, struct sockaddr *addr, int addr_len); + + /** + * @brief 接收到数据,可能来自server fd,也可能来自peer fd + * @param is_server_fd 时候为server fd + * @param id 客户端id + * @param buf 数据 + * @param addr 客户端地址 + * @param addr_len 客户端地址长度 + * @brief Receive data, may come from server fd or peer fd + * @param is_server_fd Whether it is a server fd + * @param id Client ID + * @param buf Data + * @param addr Client address + * @param addr_len Client address length + + * [AUTO-TRANSLATED:1c02c9de] + */ + void onRead_l(bool is_server_fd, const PeerIdType &id, Buffer::Ptr &buf, struct sockaddr *addr, int addr_len); + + /** + * @brief 根据对端信息获取或创建一个会话 + * @brief Get or create a session based on peer information + + * [AUTO-TRANSLATED:c7e1f0c3] + */ + SessionHelper::Ptr getOrCreateSession(const PeerIdType &id, Buffer::Ptr &buf, struct sockaddr *addr, int addr_len, bool &is_new); + + /** + * @brief 创建一个会话, 同时进行必要的设置 + * @brief Create a session and perform necessary settings + + * [AUTO-TRANSLATED:355c4256] + */ + SessionHelper::Ptr createSession(const PeerIdType &id, Buffer::Ptr &buf, struct sockaddr *addr, int addr_len); + + /** + * @brief 创建socket + * @brief Create a socket + + * [AUTO-TRANSLATED:c9aacad4] + */ + Socket::Ptr createSocket(const EventPoller::Ptr &poller, const Buffer::Ptr &buf = nullptr, struct sockaddr *addr = nullptr, int addr_len = 0); + + void setupEvent(); + +private: + bool _cloned = false; + bool _multi_poller; + Socket::Ptr _socket; + std::shared_ptr _timer; + onCreateSocket _on_create_socket; + //cloned server共享主server的session map,防止数据在不同server间漂移 [AUTO-TRANSLATED:9a149e52] + //Cloned server shares the session map with the main server, preventing data drift between different servers + std::shared_ptr _session_mutex; + std::shared_ptr _session_map; + //主server持有cloned server的引用 [AUTO-TRANSLATED:04a6403a] + //Main server holds a reference to the cloned server + std::unordered_map _cloned_server; + std::function _session_alloc; + // 对象个数统计 [AUTO-TRANSLATED:f4a012d0] + //Object count statistics + ObjectStatistic _statistic; +}; + +} // namespace toolkit + +#endif // TOOLKIT_NETWORK_UDPSERVER_H diff --git a/3rdpart/ZLToolKit/src/Network/sockutil.cpp b/3rdpart/ZLToolKit/src/Network/sockutil.cpp new file mode 100644 index 0000000..0d30921 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Network/sockutil.cpp @@ -0,0 +1,1197 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ +#include +#include +#include +#include +#include +#include +#include +#include "sockutil.h" +#include "Util/util.h" +#include "Util/logger.h" +#include "Util/uv_errno.h" +#include "Util/onceToken.h" +#if defined (__APPLE__) +#include +#include +#endif +using namespace std; + +namespace toolkit { + +#if defined(_WIN32) +static onceToken g_token([]() { + WORD wVersionRequested = MAKEWORD(2, 2); + WSADATA wsaData; + WSAStartup(wVersionRequested, &wsaData); +}, []() { + WSACleanup(); +}); +int ioctl(int fd, long cmd, u_long *ptr) { + return ioctlsocket(fd, cmd, ptr); +} +int close(int fd) { + return closesocket(fd); +} +#if (_WIN32_WINNT < _WIN32_WINNT_VISTA) +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) { + struct sockaddr_storage ss; + unsigned long s = size; + + ZeroMemory(&ss, sizeof(ss)); + ss.ss_family = af; + + switch (af) { + case AF_INET: + ((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src; + break; + case AF_INET6: + ((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src; + break; + default: + return NULL; + } + /* cannot direclty use &size because of strict aliasing rules */ + return (WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0) ? dst : NULL; +} +int inet_pton(int af, const char *src, void *dst) { + struct sockaddr_storage ss; + int size = sizeof(ss); + char src_copy[INET6_ADDRSTRLEN + 1]; + + ZeroMemory(&ss, sizeof(ss)); + /* stupid non-const API */ + strncpy(src_copy, src, INET6_ADDRSTRLEN + 1); + src_copy[INET6_ADDRSTRLEN] = 0; + + if (WSAStringToAddress(src_copy, af, NULL, (struct sockaddr *)&ss, &size) == 0) { + switch (af) { + case AF_INET: + *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr; + return 1; + case AF_INET6: + *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr; + return 1; + } + } + return 0; +} +#endif +#endif // defined(_WIN32) + +static inline string my_inet_ntop(int af, const void *addr) { + string ret; + ret.resize(128); + if (!inet_ntop(af, const_cast(addr), (char *) ret.data(), ret.size())) { + ret.clear(); + } else { + ret.resize(strlen(ret.data())); + } + return ret; +} + +static inline bool support_ipv6_l() { + auto fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); + if (fd == -1) { + return false; + } + close(fd); + return true; +} + +bool SockUtil::support_ipv6() { + static auto flag = support_ipv6_l(); + return flag; +} + +string SockUtil::inet_ntoa(const struct in_addr &addr) { + return my_inet_ntop(AF_INET, &addr); +} + +std::string SockUtil::inet_ntoa(const struct in6_addr &addr) { + return my_inet_ntop(AF_INET6, &addr); +} + +std::string SockUtil::inet_ntoa(const struct sockaddr *addr, bool mapV4) { + switch (addr->sa_family) { + case AF_INET: return SockUtil::inet_ntoa(((struct sockaddr_in *)addr)->sin_addr); + case AF_INET6: { + if (mapV4 && IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)addr)->sin6_addr)) { + struct in_addr addr4; + memcpy(&addr4, 12 + (char *)&(((struct sockaddr_in6 *)addr)->sin6_addr), 4); + return SockUtil::inet_ntoa(addr4); + } + return SockUtil::inet_ntoa(((struct sockaddr_in6 *)addr)->sin6_addr); + } + default: return ""; + } +} + +uint16_t SockUtil::inet_port(const struct sockaddr *addr) { + switch (addr->sa_family) { + case AF_INET: return ntohs(((struct sockaddr_in *)addr)->sin_port); + case AF_INET6: return ntohs(((struct sockaddr_in6 *)addr)->sin6_port); + default: return 0; + } +} + +int SockUtil::setCloseWait(int fd, int second) { + linger m_sLinger; + //在调用closesocket()时还有数据未发送完,允许等待 [AUTO-TRANSLATED:8744ea4d] + //Allow waiting when calling closesocket() with data still to be sent + // 若m_sLinger.l_onoff=0;则调用closesocket()后强制关闭 [AUTO-TRANSLATED:07e5d642] + //Force close after calling closesocket() if m_sLinger.l_onoff = 0 + m_sLinger.l_onoff = (second > 0); + m_sLinger.l_linger = second; //设置等待时间为x秒 + int ret = setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &m_sLinger, sizeof(linger)); + if (ret == -1) { +#ifndef _WIN32 + TraceL << "setsockopt SO_LINGER failed"; +#endif + } + return ret; +} + +int SockUtil::setNoDelay(int fd, bool on) { + int opt = on ? 1 : 0; + int ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &opt, static_cast(sizeof(opt))); + if (ret == -1) { + TraceL << "setsockopt TCP_NODELAY failed"; + } + return ret; +} + +int SockUtil::setReuseable(int fd, bool on, bool reuse_port) { + int opt = on ? 1 : 0; + int ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, static_cast(sizeof(opt))); + if (ret == -1) { + TraceL << "setsockopt SO_REUSEADDR failed"; + return ret; + } +#if defined(SO_REUSEPORT) + if (reuse_port) { + ret = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *) &opt, static_cast(sizeof(opt))); + if (ret == -1) { + TraceL << "setsockopt SO_REUSEPORT failed"; + } + } +#endif + return ret; +} + +int SockUtil::setBroadcast(int fd, bool on) { + int opt = on ? 1 : 0; + int ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char *) &opt, static_cast(sizeof(opt))); + if (ret == -1) { + TraceL << "setsockopt SO_BROADCAST failed"; + } + return ret; +} + +int SockUtil::setKeepAlive(int fd, bool on, int interval, int idle, int times) { + // Enable/disable the keep-alive option + int opt = on ? 1 : 0; + int ret = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, static_cast(sizeof(opt))); + if (ret == -1) { + TraceL << "setsockopt SO_KEEPALIVE failed"; + } +#if !defined(_WIN32) +#if !defined(SOL_TCP) && defined(IPPROTO_TCP) + #define SOL_TCP IPPROTO_TCP +#endif +#if !defined(TCP_KEEPIDLE) && defined(TCP_KEEPALIVE) + #define TCP_KEEPIDLE TCP_KEEPALIVE +#endif + // Set the keep-alive parameters + if (on && interval > 0 && ret != -1) { + ret = setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, (char *) &idle, static_cast(sizeof(idle))); + if (ret == -1) { + TraceL << "setsockopt TCP_KEEPIDLE failed"; + } + ret = setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, (char *) &interval, static_cast(sizeof(interval))); + if (ret == -1) { + TraceL << "setsockopt TCP_KEEPINTVL failed"; + } + ret = setsockopt(fd, SOL_TCP, TCP_KEEPCNT, (char *) ×, static_cast(sizeof(times))); + if (ret == -1) { + TraceL << "setsockopt TCP_KEEPCNT failed"; + } + } +#endif + return ret; +} + +int SockUtil::setCloExec(int fd, bool on) { +#if !defined(_WIN32) + int flags = fcntl(fd, F_GETFD); + if (flags == -1) { + TraceL << "fcntl F_GETFD failed"; + return -1; + } + if (on) { + flags |= FD_CLOEXEC; + } else { + int cloexec = FD_CLOEXEC; + flags &= ~cloexec; + } + int ret = fcntl(fd, F_SETFD, flags); + if (ret == -1) { + TraceL << "fcntl F_SETFD failed"; + return -1; + } + return ret; +#else + return -1; +#endif +} + +int SockUtil::setNoSigpipe(int fd) { +#if defined(SO_NOSIGPIPE) + int set = 1; + auto ret = setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (char *) &set, sizeof(int)); + if (ret == -1) { + TraceL << "setsockopt SO_NOSIGPIPE failed"; + } + return ret; +#else + return -1; +#endif +} + +int SockUtil::setNoBlocked(int fd, bool noblock) { +#if defined(_WIN32) + unsigned long ul = noblock; +#else + int ul = noblock; +#endif //defined(_WIN32) + int ret = ioctl(fd, FIONBIO, &ul); //设置为非阻塞模式 + if (ret == -1) { + TraceL << "ioctl FIONBIO failed"; + } + + return ret; +} + +int SockUtil::setRecvBuf(int fd, int size) { + if (size <= 0) { + // use the system default value + return 0; + } + int ret = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof(size)); + if (ret == -1) { + TraceL << "setsockopt SO_RCVBUF failed"; + } + return ret; +} + +int SockUtil::setSendBuf(int fd, int size) { + if (size <= 0) { + return 0; + } + int ret = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &size, sizeof(size)); + if (ret == -1) { + TraceL << "setsockopt SO_SNDBUF failed"; + } + return ret; +} + +class DnsCache { +public: + static DnsCache &Instance() { + static DnsCache instance; + return instance; + } + + bool getDomainIP(const char *host, sockaddr_storage &storage, int ai_family = AF_INET, + int ai_socktype = SOCK_STREAM, int ai_protocol = IPPROTO_TCP, int expire_sec = 60) { + try { + storage = SockUtil::make_sockaddr(host, 0); + return true; + } catch (...) { + auto item = getCacheDomainIP(host, expire_sec); + if (!item) { + item = getSystemDomainIP(host); + if (item) { + setCacheDomainIP(host, item); + } + } + if (item) { + auto addr = getPerferredAddress(item.get(), ai_family, ai_socktype, ai_protocol); + memcpy(&storage, addr->ai_addr, addr->ai_addrlen); + } + return (bool)item; + } + } + +private: + class DnsItem { + public: + std::shared_ptr addr_info; + time_t create_time; + }; + + std::shared_ptr getCacheDomainIP(const char *host, int expireSec) { + lock_guard lck(_mtx); + auto it = _dns_cache.find(host); + if (it == _dns_cache.end()) { + //没有记录 [AUTO-TRANSLATED:e99e45df] + //No record + return nullptr; + } + if (it->second.create_time + expireSec < time(nullptr)) { + //已过期 [AUTO-TRANSLATED:5dbe0c9a] + //Expired + _dns_cache.erase(it); + return nullptr; + } + return it->second.addr_info; + } + + void setCacheDomainIP(const char *host, std::shared_ptr addr) { + lock_guard lck(_mtx); + DnsItem item; + item.addr_info = std::move(addr); + item.create_time = time(nullptr); + _dns_cache[host] = std::move(item); + } + + std::shared_ptr getSystemDomainIP(const char *host) { + struct addrinfo *answer = nullptr; + //阻塞式dns解析,可能被打断 [AUTO-TRANSLATED:89c8546f] + //Blocking DNS resolution, may be interrupted + int ret = -1; + do { + ret = getaddrinfo(host, nullptr, nullptr, &answer); + } while (ret == -1 && get_uv_error(true) == UV_EINTR); + + if (!answer) { + WarnL << "getaddrinfo failed: " << host; + return nullptr; + } + return std::shared_ptr(answer, freeaddrinfo); + } + + struct addrinfo *getPerferredAddress(struct addrinfo *answer, int ai_family, int ai_socktype, int ai_protocol) { + auto ptr = answer; + while (ptr) { + if (ptr->ai_family == ai_family && ptr->ai_socktype == ai_socktype && ptr->ai_protocol == ai_protocol) { + return ptr; + } + ptr = ptr->ai_next; + } + return answer; + } + +private: + mutex _mtx; + unordered_map _dns_cache; +}; + +bool SockUtil::getDomainIP(const char *host, uint16_t port, struct sockaddr_storage &addr, + int ai_family, int ai_socktype, int ai_protocol, int expire_sec) { + bool flag = DnsCache::Instance().getDomainIP(host, addr, ai_family, ai_socktype, ai_protocol, expire_sec); + if (flag) { + switch (addr.ss_family ) { + case AF_INET : ((sockaddr_in *) &addr)->sin_port = htons(port); break; + case AF_INET6 : ((sockaddr_in6 *) &addr)->sin6_port = htons(port); break; + default: break; + } + } + return flag; +} + +static int set_ipv6_only(int fd, bool flag) { + int opt = flag; + int ret = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&opt, sizeof opt); + if (ret == -1) { + TraceL << "setsockopt IPV6_V6ONLY failed"; + } + return ret; +} + +static int bind_sock6(int fd, const char *ifr_ip, uint16_t port) { + set_ipv6_only(fd, false); + struct sockaddr_in6 addr; + bzero(&addr, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_port = htons(port); + if (1 != inet_pton(AF_INET6, ifr_ip, &(addr.sin6_addr))) { + if (strcmp(ifr_ip, "0.0.0.0")) { + WarnL << "inet_pton to ipv6 address failed: " << ifr_ip; + } + addr.sin6_addr = IN6ADDR_ANY_INIT; + } + if (::bind(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) { + WarnL << "Bind socket failed: " << get_uv_errmsg(true); + return -1; + } + return 0; +} + +static int bind_sock4(int fd, const char *ifr_ip, uint16_t port) { + struct sockaddr_in addr; + bzero(&addr, sizeof(addr)); + + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + if (1 != inet_pton(AF_INET, ifr_ip, &(addr.sin_addr))) { + if (strcmp(ifr_ip, "::")) { + WarnL << "inet_pton to ipv4 address failed: " << ifr_ip; + } + addr.sin_addr.s_addr = INADDR_ANY; + } + if (::bind(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) { + WarnL << "Bind socket failed: " << get_uv_errmsg(true); + return -1; + } + return 0; +} + +static int bind_sock(int fd, const char *ifr_ip, uint16_t port, int family) { + switch (family) { + case AF_INET: return bind_sock4(fd, ifr_ip, port); + case AF_INET6: return bind_sock6(fd, ifr_ip, port); + default: return -1; + } +} + +int SockUtil::connect(const char *host, uint16_t port, bool async, const char *local_ip, uint16_t local_port) { + sockaddr_storage addr; + //优先使用ipv4地址 [AUTO-TRANSLATED:b7857afe] + //Prefer IPv4 address + if (!getDomainIP(host, port, addr, AF_INET, SOCK_STREAM, IPPROTO_TCP)) { + //dns解析失败 [AUTO-TRANSLATED:1d0cd32d] + //DNS resolution failed + return -1; + } + + int sockfd = (int) socket(addr.ss_family, SOCK_STREAM, IPPROTO_TCP); + if (sockfd < 0) { + WarnL << "Create socket failed: " << host; + return -1; + } + + setReuseable(sockfd); + setNoSigpipe(sockfd); + setNoBlocked(sockfd, async); + setNoDelay(sockfd); + setSendBuf(sockfd); + setRecvBuf(sockfd); + setCloseWait(sockfd); + setCloExec(sockfd); + + if (bind_sock(sockfd, local_ip, local_port, addr.ss_family) == -1) { + close(sockfd); + return -1; + } + + if (::connect(sockfd, (sockaddr *) &addr, get_sock_len((sockaddr *)&addr)) == 0) { + //同步连接成功 [AUTO-TRANSLATED:da143548] + //Synchronous connection successful + return sockfd; + } + if (async && get_uv_error(true) == UV_EAGAIN) { + //异步连接成功 [AUTO-TRANSLATED:44ac1cad] + //Asynchronous connection successful + return sockfd; + } + WarnL << "Connect socket to " << host << " " << port << " failed: " << get_uv_errmsg(true); + close(sockfd); + return -1; +} + +int SockUtil::listen(const uint16_t port, const char *local_ip, int back_log) { + int fd = -1; + int family = support_ipv6() ? (is_ipv4(local_ip) ? AF_INET : AF_INET6) : AF_INET; + if ((fd = (int)socket(family, SOCK_STREAM, IPPROTO_TCP)) == -1) { + WarnL << "Create socket failed: " << get_uv_errmsg(true); + return -1; + } + + setReuseable(fd, true, false); + setNoBlocked(fd); + setCloExec(fd); + + if (bind_sock(fd, local_ip, port, family) == -1) { + close(fd); + return -1; + } + + //开始监听 [AUTO-TRANSLATED:4404b1a8] + //Start listening + if (::listen(fd, back_log) == -1) { + WarnL << "Listen socket failed: " << get_uv_errmsg(true); + close(fd); + return -1; + } + + return fd; +} + +int SockUtil::getSockError(int fd) { + int opt; + socklen_t optLen = static_cast(sizeof(opt)); + + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *) &opt, &optLen) < 0) { + return get_uv_error(true); + } else { + return uv_translate_posix_error(opt); + } +} + +using getsockname_type = decltype(getsockname); + +static bool get_socket_addr(int fd, struct sockaddr_storage &addr, getsockname_type func) { + socklen_t addr_len = sizeof(addr); + if (-1 == func(fd, (struct sockaddr *)&addr, &addr_len)) { + return false; + } + return true; +} + +bool SockUtil::get_sock_local_addr(int fd, struct sockaddr_storage &addr) { + return get_socket_addr(fd, addr, getsockname); +} + +bool SockUtil::get_sock_peer_addr(int fd, struct sockaddr_storage &addr) { + return get_socket_addr(fd, addr, getpeername); +} + +static string get_socket_ip(int fd, getsockname_type func) { + struct sockaddr_storage addr; + if (!get_socket_addr(fd, addr, func)) { + return ""; + } + return SockUtil::inet_ntoa((struct sockaddr *)&addr); +} + +static uint16_t get_socket_port(int fd, getsockname_type func) { + struct sockaddr_storage addr; + if (!get_socket_addr(fd, addr, func)) { + return 0; + } + return SockUtil::inet_port((struct sockaddr *)&addr); +} + +bool SockUtil::is_same_addr(const struct sockaddr* a, const struct sockaddr* b) { + if (a->sa_family != b->sa_family) { + return false; + } + + switch (a->sa_family) { + case AF_INET: { + const struct sockaddr_in* a_in = reinterpret_cast(a); + const struct sockaddr_in* b_in = reinterpret_cast(b); + return a_in->sin_addr.s_addr == b_in->sin_addr.s_addr && a_in->sin_port == b_in->sin_port; + } + case AF_INET6: { + const struct sockaddr_in6* a_in6 = reinterpret_cast(a); + const struct sockaddr_in6* b_in6 = reinterpret_cast(b); + return memcmp(&a_in6->sin6_addr, &b_in6->sin6_addr, sizeof(a_in6->sin6_addr)) == 0 && a_in6->sin6_port == b_in6->sin6_port; + } + default: + return false; + } +} + +string SockUtil::get_local_ip(int fd) { + return get_socket_ip(fd, getsockname); +} + +string SockUtil::get_peer_ip(int fd) { + return get_socket_ip(fd, getpeername); +} + +uint16_t SockUtil::get_local_port(int fd) { + return get_socket_port(fd, getsockname); +} + +uint16_t SockUtil::get_peer_port(int fd) { + return get_socket_port(fd, getpeername); +} + +#if defined(__APPLE__) +template +void for_each_netAdapter_apple(FUN &&fun) { //type: struct ifaddrs * + struct ifaddrs *interfaces = nullptr; + struct ifaddrs *adapter = nullptr; + if (getifaddrs(&interfaces) == 0) { + adapter = interfaces; + while (adapter) { + if (adapter->ifa_addr->sa_family == AF_INET) { + if (fun(adapter)) { + break; + } + } + adapter = adapter->ifa_next; + } + freeifaddrs(interfaces); + } +} +#endif //defined(__APPLE__) + +#if defined(_WIN32) +template +void for_each_netAdapter_win32(FUN && fun) { //type: PIP_ADAPTER_INFO + unsigned long nSize = sizeof(IP_ADAPTER_INFO); + PIP_ADAPTER_INFO adapterList = (PIP_ADAPTER_INFO)new char[nSize]; + int nRet = GetAdaptersInfo(adapterList, &nSize); + if (ERROR_BUFFER_OVERFLOW == nRet) { + delete[] adapterList; + adapterList = (PIP_ADAPTER_INFO)new char[nSize]; + nRet = GetAdaptersInfo(adapterList, &nSize); + } + auto adapterPtr = adapterList; + while (adapterPtr && ERROR_SUCCESS == nRet) { + if (fun(adapterPtr)) { + break; + } + adapterPtr = adapterPtr->Next; + } + //释放内存空间 [AUTO-TRANSLATED:5310c138] + //Release memory space + delete[] adapterList; +} +#endif //defined(_WIN32) + +#if !defined(_WIN32) && !defined(__APPLE__) +template +void for_each_netAdapter_posix(FUN &&fun){ //type: struct ifreq * + struct ifconf ifconf; + char buf[1024 * 10]; + //初始化ifconf [AUTO-TRANSLATED:d9c144ee] + //Initialize ifconf + ifconf.ifc_len = sizeof(buf); + ifconf.ifc_buf = buf; + int sockfd = ::socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) { + WarnL << "Create socket failed: " << get_uv_errmsg(true); + return; + } + if (-1 == ioctl(sockfd, SIOCGIFCONF, &ifconf)) { //获取所有接口信息 + WarnL << "ioctl SIOCGIFCONF failed: " << get_uv_errmsg(true); + close(sockfd); + return; + } + close(sockfd); + //接下来一个一个的获取IP地址 [AUTO-TRANSLATED:6484a8b6] + //Get IP addresses one by one next + struct ifreq * adapter = (struct ifreq*) buf; + for (int i = (ifconf.ifc_len / sizeof(struct ifreq)); i > 0; --i,++adapter) { + if(fun(adapter)){ + break; + } + } +} +#endif //!defined(_WIN32) && !defined(__APPLE__) + +bool check_ip(string &address, const string &ip) { + if (ip != "127.0.0.1" && ip != "0.0.0.0") { + /*获取一个有效IP + /* Get a valid IP + * [AUTO-TRANSLATED:72b34922] + */ + address = ip; + uint32_t addressInNetworkOrder = htonl(inet_addr(ip.data())); + if (/*(addressInNetworkOrder >= 0x0A000000 && addressInNetworkOrder < 0x0E000000) ||*/ + (addressInNetworkOrder >= 0xAC100000 && addressInNetworkOrder < 0xAC200000) || + (addressInNetworkOrder >= 0xC0A80000 && addressInNetworkOrder < 0xC0A90000)) { + //A类私有IP地址: [AUTO-TRANSLATED:ef542777] + //A-class private IP address: + //10.0.0.0~10.255.255.255 [AUTO-TRANSLATED:dbbf8c5f] + //10.0.0.0~10.255.255.255 + //B类私有IP地址: [AUTO-TRANSLATED:7dfef625] + //B-class private IP address: + //172.16.0.0~172.31.255.255 [AUTO-TRANSLATED:a96262fa] + //172.16.0.0~172.31.255.255 + //C类私有IP地址: [AUTO-TRANSLATED:dc37505e] + //C-class private IP address: + //192.168.0.0~192.168.255.255 [AUTO-TRANSLATED:c8c47e43] + //192.168.0.0~192.168.255.255 + //如果是私有地址 说明在nat内部 [AUTO-TRANSLATED:92007abb] + //If it's a private address, it's inside the NAT + + /* 优先采用局域网地址,该地址很可能是wifi地址 + * 一般来说,无线路由器分配的地址段是BC类私有ip地址 + * 而A类地址多用于蜂窝移动网络 + /* Prefer to use the local area network address, this address is likely to be the WiFi address + * Generally, the address segment allocated by the wireless router is a BC-class private IP address + * While A-class addresses are often used for cellular mobile networks + + * [AUTO-TRANSLATED:134ad072] + */ + return true; + } + } + return false; +} + +string SockUtil::get_local_ip() { +#if defined(__APPLE__) + string address = "127.0.0.1"; + for_each_netAdapter_apple([&](struct ifaddrs *adapter) { + string ip = SockUtil::inet_ntoa(adapter->ifa_addr); + if (strstr(adapter->ifa_name, "docker")) { + return false; + } + return check_ip(address, ip); + }); + return address; +#elif defined(_WIN32) + string address = "127.0.0.1"; + for_each_netAdapter_win32([&](PIP_ADAPTER_INFO adapter) { + IP_ADDR_STRING *ipAddr = &(adapter->IpAddressList); + while (ipAddr) { + string ip = ipAddr->IpAddress.String; + if (strstr(adapter->AdapterName, "docker")) { + return false; + } + if(check_ip(address,ip)){ + return true; + } + ipAddr = ipAddr->Next; + } + return false; + }); + return address; +#else + string address = "127.0.0.1"; + for_each_netAdapter_posix([&](struct ifreq *adapter){ + string ip = SockUtil::inet_ntoa(&(adapter->ifr_addr)); + if (strstr(adapter->ifr_name, "docker")) { + return false; + } + return check_ip(address,ip); + }); + return address; +#endif +} + +vector > SockUtil::getInterfaceList() { + vector > ret; +#if defined(__APPLE__) + for_each_netAdapter_apple([&](struct ifaddrs *adapter) { + map obj; + obj["ip"] = SockUtil::inet_ntoa(adapter->ifa_addr); + obj["name"] = adapter->ifa_name; + ret.emplace_back(std::move(obj)); + return false; + }); +#elif defined(_WIN32) + for_each_netAdapter_win32([&](PIP_ADAPTER_INFO adapter) { + IP_ADDR_STRING *ipAddr = &(adapter->IpAddressList); + while (ipAddr) { + map obj; + obj["ip"] = ipAddr->IpAddress.String; + obj["name"] = adapter->AdapterName; + ret.emplace_back(std::move(obj)); + ipAddr = ipAddr->Next; + } + return false; + }); +#else + for_each_netAdapter_posix([&](struct ifreq *adapter){ + map obj; + obj["ip"] = SockUtil::inet_ntoa(&(adapter->ifr_addr)); + obj["name"] = adapter->ifr_name; + ret.emplace_back(std::move(obj)); + return false; + }); +#endif + return ret; +} + +int SockUtil::bindUdpSock(const uint16_t port, const char *local_ip, bool enable_reuse) { + int fd = -1; + int family = support_ipv6() ? (is_ipv4(local_ip) ? AF_INET : AF_INET6) : AF_INET; + if ((fd = (int)socket(family, SOCK_DGRAM, IPPROTO_UDP)) == -1) { + WarnL << "Create socket failed: " << get_uv_errmsg(true); + return -1; + } + if (enable_reuse) { + setReuseable(fd); + } + setNoSigpipe(fd); + setNoBlocked(fd); + setSendBuf(fd); + setRecvBuf(fd); + setCloseWait(fd); + setCloExec(fd); + + if (bind_sock(fd, local_ip, port, family) == -1) { + close(fd); + return -1; + } + return fd; +} + +int SockUtil::dissolveUdpSock(int fd) { + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + if (-1 == getsockname(fd, (struct sockaddr *)&addr, &addr_len)) { + return -1; + } + addr.ss_family = AF_UNSPEC; + if (-1 == ::connect(fd, (struct sockaddr *)&addr, addr_len) && get_uv_error() != UV_EAFNOSUPPORT) { + // mac/ios时返回EAFNOSUPPORT错误 [AUTO-TRANSLATED:bbe0621c] + //Returns EAFNOSUPPORT error on Mac/IOS + WarnL << "Connect socket AF_UNSPEC failed: " << get_uv_errmsg(true); + return -1; + } + return 0; +} + +string SockUtil::get_ifr_ip(const char *if_name) { +#if defined(__APPLE__) + string ret; + for_each_netAdapter_apple([&](struct ifaddrs *adapter) { + if (strcmp(adapter->ifa_name, if_name) == 0) { + ret = SockUtil::inet_ntoa(adapter->ifa_addr); + return true; + } + return false; + }); + return ret; +#elif defined(_WIN32) + string ret; + for_each_netAdapter_win32([&](PIP_ADAPTER_INFO adapter) { + IP_ADDR_STRING *ipAddr = &(adapter->IpAddressList); + while (ipAddr){ + if (strcmp(if_name,adapter->AdapterName) == 0){ + //ip匹配到了 [AUTO-TRANSLATED:6224132d] + //IP matched + ret.assign(ipAddr->IpAddress.String); + return true; + } + ipAddr = ipAddr->Next; + } + return false; + }); + return ret; +#else + string ret; + for_each_netAdapter_posix([&](struct ifreq *adapter){ + if(strcmp(adapter->ifr_name,if_name) == 0) { + ret = SockUtil::inet_ntoa(&(adapter->ifr_addr)); + return true; + } + return false; + }); + return ret; +#endif +} + +string SockUtil::get_ifr_name(const char *local_ip) { +#if defined(__APPLE__) + string ret = "en0"; + for_each_netAdapter_apple([&](struct ifaddrs *adapter) { + string ip = SockUtil::inet_ntoa(adapter->ifa_addr); + if (ip == local_ip) { + ret = adapter->ifa_name; + return true; + } + return false; + }); + return ret; +#elif defined(_WIN32) + string ret = "en0"; + for_each_netAdapter_win32([&](PIP_ADAPTER_INFO adapter) { + IP_ADDR_STRING *ipAddr = &(adapter->IpAddressList); + while (ipAddr){ + if (strcmp(local_ip,ipAddr->IpAddress.String) == 0){ + //ip匹配到了 [AUTO-TRANSLATED:6224132d] + //IP matched + ret.assign(adapter->AdapterName); + return true; + } + ipAddr = ipAddr->Next; + } + return false; + }); + return ret; +#else + string ret = "en0"; + for_each_netAdapter_posix([&](struct ifreq *adapter){ + string ip = SockUtil::inet_ntoa(&(adapter->ifr_addr)); + if(ip == local_ip) { + ret = adapter->ifr_name; + return true; + } + return false; + }); + return ret; +#endif +} + +string SockUtil::get_ifr_mask(const char *if_name) { +#if defined(__APPLE__) + string ret; + for_each_netAdapter_apple([&](struct ifaddrs *adapter) { + if (strcmp(if_name, adapter->ifa_name) == 0) { + ret = SockUtil::inet_ntoa(adapter->ifa_netmask); + return true; + } + return false; + }); + return ret; +#elif defined(_WIN32) + string ret; + for_each_netAdapter_win32([&](PIP_ADAPTER_INFO adapter) { + if (strcmp(if_name,adapter->AdapterName) == 0){ + //找到了该网卡 [AUTO-TRANSLATED:c56438bb] + //Found the network card + IP_ADDR_STRING *ipAddr = &(adapter->IpAddressList); + //获取第一个ip的子网掩码 [AUTO-TRANSLATED:b6df1b9d] + //Get the subnet mask of the first IP + ret.assign(ipAddr->IpMask.String); + return true; + } + return false; + }); + return ret; +#else + int fd; + struct ifreq ifr_mask; + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd == -1) { + WarnL << "Create socket failed: " << get_uv_errmsg(true); + return ""; + } + memset(&ifr_mask, 0, sizeof(ifr_mask)); + strncpy(ifr_mask.ifr_name, if_name, sizeof(ifr_mask.ifr_name) - 1); + if ((ioctl(fd, SIOCGIFNETMASK, &ifr_mask)) < 0) { + WarnL << "ioctl SIOCGIFNETMASK on " << if_name << " failed: " << get_uv_errmsg(true); + close(fd); + return ""; + } + close(fd); + return SockUtil::inet_ntoa(&(ifr_mask.ifr_netmask)); +#endif // defined(_WIN32) +} + +string SockUtil::get_ifr_brdaddr(const char *if_name) { +#if defined(__APPLE__) + string ret; + for_each_netAdapter_apple([&](struct ifaddrs *adapter) { + if (strcmp(if_name, adapter->ifa_name) == 0) { + ret = SockUtil::inet_ntoa(adapter->ifa_broadaddr); + return true; + } + return false; + }); + return ret; +#elif defined(_WIN32) + string ret; + for_each_netAdapter_win32([&](PIP_ADAPTER_INFO adapter) { + if (strcmp(if_name, adapter->AdapterName) == 0) { + //找到该网卡 [AUTO-TRANSLATED:23a900ba] + //Found the network card + IP_ADDR_STRING *ipAddr = &(adapter->IpAddressList); + in_addr broadcast; + broadcast.S_un.S_addr = (inet_addr(ipAddr->IpAddress.String) & inet_addr(ipAddr->IpMask.String)) | (~inet_addr(ipAddr->IpMask.String)); + ret = SockUtil::inet_ntoa(broadcast); + return true; + } + return false; + }); + return ret; +#else + int fd; + struct ifreq ifr_mask; + fd = socket( AF_INET, SOCK_STREAM, 0); + if (fd == -1) { + WarnL << "Create socket failed: " << get_uv_errmsg(true); + return ""; + } + memset(&ifr_mask, 0, sizeof(ifr_mask)); + strncpy(ifr_mask.ifr_name, if_name, sizeof(ifr_mask.ifr_name) - 1); + if ((ioctl(fd, SIOCGIFBRDADDR, &ifr_mask)) < 0) { + WarnL << "ioctl SIOCGIFBRDADDR failed: " << get_uv_errmsg(true); + close(fd); + return ""; + } + close(fd); + return SockUtil::inet_ntoa(&(ifr_mask.ifr_broadaddr)); +#endif +} + +#define ip_addr_netcmp(addr1, addr2, mask) (((addr1) & (mask)) == ((addr2) & (mask))) + +bool SockUtil::in_same_lan(const char *myIp, const char *dstIp) { + string mask = get_ifr_mask(get_ifr_name(myIp).data()); + return ip_addr_netcmp(inet_addr(myIp), inet_addr(dstIp), inet_addr(mask.data())); +} + +static void clearMulticastAllSocketOption(int socket) { +#if defined(IP_MULTICAST_ALL) + // This option is defined in modern versions of Linux to overcome a bug in the Linux kernel's default behavior. + // When set to 0, it ensures that we receive only packets that were sent to the specified IP multicast address, + // even if some other process on the same system has joined a different multicast group with the same port number. + int multicastAll = 0; + (void)setsockopt(socket, IPPROTO_IP, IP_MULTICAST_ALL, (void*)&multicastAll, sizeof multicastAll); + // Ignore the call's result. Should it fail, we'll still receive packets (just perhaps more than intended) +#endif +} + +int SockUtil::setMultiTTL(int fd, uint8_t ttl) { + int ret = -1; +#if defined(IP_MULTICAST_TTL) + ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &ttl, sizeof(ttl)); + if (ret == -1) { + TraceL << "setsockopt IP_MULTICAST_TTL failed"; + } +#endif + clearMulticastAllSocketOption(fd); + return ret; +} + +int SockUtil::setMultiIF(int fd, const char *local_ip) { + int ret = -1; +#if defined(IP_MULTICAST_IF) + struct in_addr addr; + addr.s_addr = inet_addr(local_ip); + ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (char *) &addr, sizeof(addr)); + if (ret == -1) { + TraceL << "setsockopt IP_MULTICAST_IF failed"; + } +#endif + clearMulticastAllSocketOption(fd); + return ret; +} + +int SockUtil::setMultiLOOP(int fd, bool accept) { + int ret = -1; +#if defined(IP_MULTICAST_LOOP) + uint8_t loop = accept; + ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &loop, sizeof(loop)); + if (ret == -1) { + TraceL << "setsockopt IP_MULTICAST_LOOP failed"; + } +#endif + clearMulticastAllSocketOption(fd); + return ret; +} + +int SockUtil::joinMultiAddr(int fd, const char *addr, const char *local_ip) { + int ret = -1; +#if defined(IP_ADD_MEMBERSHIP) + struct ip_mreq imr; + imr.imr_multiaddr.s_addr = inet_addr(addr); + imr.imr_interface.s_addr = inet_addr(local_ip); + ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &imr, sizeof(struct ip_mreq)); + if (ret == -1) { + TraceL << "setsockopt IP_ADD_MEMBERSHIP failed: " << get_uv_errmsg(true); + } +#endif + clearMulticastAllSocketOption(fd); + return ret; +} + +int SockUtil::leaveMultiAddr(int fd, const char *addr, const char *local_ip) { + int ret = -1; +#if defined(IP_DROP_MEMBERSHIP) + struct ip_mreq imr; + imr.imr_multiaddr.s_addr = inet_addr(addr); + imr.imr_interface.s_addr = inet_addr(local_ip); + ret = setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *) &imr, sizeof(struct ip_mreq)); + if (ret == -1) { + TraceL << "setsockopt IP_DROP_MEMBERSHIP failed: " << get_uv_errmsg(true); + } +#endif + clearMulticastAllSocketOption(fd); + return ret; +} + +template +static inline void write4Byte(A &&a, B &&b) { + memcpy(&a, &b, sizeof(a)); +} + +int SockUtil::joinMultiAddrFilter(int fd, const char *addr, const char *src_ip, const char *local_ip) { + int ret = -1; +#if defined(IP_ADD_SOURCE_MEMBERSHIP) + struct ip_mreq_source imr; + + write4Byte(imr.imr_multiaddr, inet_addr(addr)); + write4Byte(imr.imr_sourceaddr, inet_addr(src_ip)); + write4Byte(imr.imr_interface, inet_addr(local_ip)); + + ret = setsockopt(fd, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char *) &imr, sizeof(struct ip_mreq_source)); + if (ret == -1) { + TraceL << "setsockopt IP_ADD_SOURCE_MEMBERSHIP failed: " << get_uv_errmsg(true); + } +#endif + clearMulticastAllSocketOption(fd); + return ret; +} + +int SockUtil::leaveMultiAddrFilter(int fd, const char *addr, const char *src_ip, const char *local_ip) { + int ret = -1; +#if defined(IP_DROP_SOURCE_MEMBERSHIP) + struct ip_mreq_source imr; + + write4Byte(imr.imr_multiaddr, inet_addr(addr)); + write4Byte(imr.imr_sourceaddr, inet_addr(src_ip)); + write4Byte(imr.imr_interface, inet_addr(local_ip)); + + ret = setsockopt(fd, IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP, (char *) &imr, sizeof(struct ip_mreq_source)); + if (ret == -1) { + TraceL << "setsockopt IP_DROP_SOURCE_MEMBERSHIP failed: " << get_uv_errmsg(true); + } +#endif + clearMulticastAllSocketOption(fd); + return ret; +} + +bool SockUtil::is_ipv4(const char *host) { + struct in_addr addr; + return 1 == inet_pton(AF_INET, host, &addr); +} + +bool SockUtil::is_ipv6(const char *host) { + struct in6_addr addr; + return 1 == inet_pton(AF_INET6, host, &addr); +} + +socklen_t SockUtil::get_sock_len(const struct sockaddr *addr) { + switch (addr->sa_family) { + case AF_INET : return sizeof(sockaddr_in); + case AF_INET6 : return sizeof(sockaddr_in6); + default: assert(0); return 0; + } +} + +struct sockaddr_storage SockUtil::make_sockaddr(const char *host, uint16_t port) { + struct sockaddr_storage storage; + bzero(&storage, sizeof(storage)); + + struct in_addr addr; + struct in6_addr addr6; + if (1 == inet_pton(AF_INET, host, &addr)) { + // host是ipv4 [AUTO-TRANSLATED:ba5c03a7] + //Host is IPv4 + reinterpret_cast(storage).sin_addr = addr; + reinterpret_cast(storage).sin_family = AF_INET; + reinterpret_cast(storage).sin_port = htons(port); + return storage; + } + if (1 == inet_pton(AF_INET6, host, &addr6)) { + // host是ipv6 [AUTO-TRANSLATED:8048db0f] + //Host is IPv6 + reinterpret_cast(storage).sin6_addr = addr6; + reinterpret_cast(storage).sin6_family = AF_INET6; + reinterpret_cast(storage).sin6_port = htons(port); + return storage; + } + throw std::invalid_argument(string("Not ip address: ") + host); +} + +} // namespace toolkit diff --git a/3rdpart/ZLToolKit/src/Network/sockutil.h b/3rdpart/ZLToolKit/src/Network/sockutil.h new file mode 100644 index 0000000..6341ec9 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Network/sockutil.h @@ -0,0 +1,587 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef NETWORK_SOCKUTIL_H +#define NETWORK_SOCKUTIL_H + +#if defined(_WIN32) +#include +#include +#include +#pragma comment (lib, "Ws2_32.lib") +#pragma comment(lib,"Iphlpapi.lib") +#else +#include +#include +#include +#include +#include +#include +#include +#endif // defined(_WIN32) + +#include +#include +#include +#include +#include + +namespace toolkit { + +#if defined(_WIN32) +#ifndef socklen_t +#define socklen_t int +#endif //!socklen_t +int ioctl(int fd, long cmd, u_long *ptr); +int close(int fd); +#endif // defined(_WIN32) + +#if !defined(SOCKET_DEFAULT_BUF_SIZE) +#define SOCKET_DEFAULT_BUF_SIZE (256 * 1024) +#else +#if SOCKET_DEFAULT_BUF_SIZE == 0 && !defined(__linux__) +// just for linux, because in some high-throughput environments, +// kernel control is more efficient and reasonable than program +// settings. For example, refer to cloudflare's blog +#undef SOCKET_DEFAULT_BUF_SIZE +#define SOCKET_DEFAULT_BUF_SIZE (256 * 1024) +#endif +#endif +#define TCP_KEEPALIVE_INTERVAL 30 +#define TCP_KEEPALIVE_PROBE_TIMES 9 +#define TCP_KEEPALIVE_TIME 120 + +//套接字工具类,封装了socket、网络的一些基本操作 [AUTO-TRANSLATED:33a88b27] +//Socket tool class, encapsulating some basic socket and network operations +class SockUtil { +public: + struct SockAddrHash { + std::size_t operator()(const sockaddr_storage& addr) const { + switch (addr.ss_family) { + case AF_INET: { + const struct sockaddr_in* addr_in = reinterpret_cast(&addr); + return std::hash()(addr_in->sin_addr.s_addr) ^ std::hash()(addr_in->sin_port); + } + case AF_INET6: { + const struct sockaddr_in6* addr_in6 = reinterpret_cast(&addr); + std::size_t h = 0; + for (int i = 0; i < 16; ++i) { + h ^= std::hash()(addr_in6->sin6_addr.s6_addr[i]) << (i % 8); + } + return h ^ std::hash()(addr_in6->sin6_port); + } + default: + return 0; + } + } + }; + + struct SockAddrEqual { + bool operator()(const sockaddr_storage& a, const sockaddr_storage& b) const { + return toolkit::SockUtil::is_same_addr(reinterpret_cast(&a), reinterpret_cast(&b)); + } + }; + /** + * 创建tcp客户端套接字并连接服务器 + * @param host 服务器ip或域名 + * @param port 服务器端口号 + * @param async 是否异步连接 + * @param local_ip 绑定的本地网卡ip + * @param local_port 绑定的本地端口号 + * @return -1代表失败,其他为socket fd号 + * Create a TCP client socket and connect to the server + * @param host Server IP or domain name + * @param port Server port number + * @param async Whether to connect asynchronously + * @param local_ip Local network card IP to bind + * @param local_port Local port number to bind + * @return -1 represents failure, others are socket fd numbers + + * [AUTO-TRANSLATED:3f0a872c] + */ + static int connect(const char *host, uint16_t port, bool async = true, const char *local_ip = "::", uint16_t local_port = 0); + + /** + * 创建tcp监听套接字 + * @param port 监听的本地端口 + * @param local_ip 绑定的本地网卡ip + * @param back_log accept列队长度 + * @return -1代表失败,其他为socket fd号 + * Create a TCP listening socket + * @param port Local port to listen on + * @param local_ip Local network card IP to bind + * @param back_log Accept queue length + * @return -1 represents failure, others are socket fd numbers + + * [AUTO-TRANSLATED:d56ad901] + */ + static int listen(const uint16_t port, const char *local_ip = "::", int back_log = 1024); + + /** + * 创建udp套接字 + * @param port 监听的本地端口 + * @param local_ip 绑定的本地网卡ip + * @param enable_reuse 是否允许重复bind端口 + * @return -1代表失败,其他为socket fd号 + * Create a UDP socket + * @param port Local port to listen on + * @param local_ip Local network card IP to bind + * @param enable_reuse Whether to allow repeated bind port + * @return -1 represents failure, others are socket fd numbers + + * [AUTO-TRANSLATED:a3762f0f] + */ + static int bindUdpSock(const uint16_t port, const char *local_ip = "::", bool enable_reuse = true); + + /** + * @brief 解除与 sock 相关的绑定关系 + * @param sock, socket fd 号 + * @return 0 成功, -1 失败 + * @brief Release the binding relationship related to sock + * @param sock, socket fd number + * @return 0 Success, -1 Failure + + * [AUTO-TRANSLATED:50b002e8] + */ + static int dissolveUdpSock(int sock); + + /** + * 开启TCP_NODELAY,降低TCP交互延时 + * @param fd socket fd号 + * @param on 是否开启 + * @return 0代表成功,-1为失败 + * Enable TCP_NODELAY to reduce TCP interaction delay + * @param fd socket fd number + * @param on Whether to enable + * @return 0 represents success, -1 represents failure + + * [AUTO-TRANSLATED:11b57392] + */ + static int setNoDelay(int fd, bool on = true); + + /** + * 写socket不触发SIG_PIPE信号(貌似只有mac有效) + * @param fd socket fd号 + * @return 0代表成功,-1为失败 + * Write socket does not trigger SIG_PIPE signal (seems to be effective only on Mac) + * @param fd socket fd number + * @return 0 represents success, -1 represents failure + + * [AUTO-TRANSLATED:bdb49ca5] + */ + static int setNoSigpipe(int fd); + + /** + * 设置读写socket是否阻塞 + * @param fd socket fd号 + * @param noblock 是否阻塞 + * @return 0代表成功,-1为失败 + * Set whether the read and write socket is blocked + * @param fd socket fd number + * @param noblock Whether to block + * @return 0 represents success, -1 represents failure + + * [AUTO-TRANSLATED:2f9717df] + */ + static int setNoBlocked(int fd, bool noblock = true); + + /** + * 设置socket接收缓存,默认貌似8K左右,一般有设置上限 + * 可以通过配置内核配置文件调整 + * @param fd socket fd号 + * @param size 接收缓存大小 + * @return 0代表成功,-1为失败 + * Set the socket receive buffer, default is around 8K, generally has an upper limit + * Can be adjusted through kernel configuration file + * @param fd socket fd number + * @param size Receive buffer size + * @return 0 represents success, -1 represents failure + + * [AUTO-TRANSLATED:4dcaa8b8] + */ + static int setRecvBuf(int fd, int size = SOCKET_DEFAULT_BUF_SIZE); + + /** + * 设置socket接收缓存,默认貌似8K左右,一般有设置上限 + * 可以通过配置内核配置文件调整 + * @param fd socket fd号 + * @param size 接收缓存大小 + * @return 0代表成功,-1为失败 + * Set the socket receive buffer, default is around 8K, generally has an upper limit + * Can be adjusted through kernel configuration file + * @param fd socket fd number + * @param size Receive buffer size + * @return 0 represents success, -1 represents failure + + * [AUTO-TRANSLATED:4dcaa8b8] + */ + static int setSendBuf(int fd, int size = SOCKET_DEFAULT_BUF_SIZE); + + /** + * 设置后续可绑定复用端口(处于TIME_WAITE状态) + * @param fd socket fd号 + * @param on 是否开启该特性 + * @return 0代表成功,-1为失败 + * Set subsequent bindable reuse port (in TIME_WAIT state) + * @param fd socket fd number + * @param on whether to enable this feature + * @return 0 represents success, -1 for failure + + * [AUTO-TRANSLATED:4dcb4dff] + */ + static int setReuseable(int fd, bool on = true, bool reuse_port = true); + + /** + * 运行发送或接收udp广播信息 + * @param fd socket fd号 + * @param on 是否开启该特性 + * @return 0代表成功,-1为失败 + * Run sending or receiving UDP broadcast messages + * @param fd socket fd number + * @param on whether to enable this feature + * @return 0 represents success, -1 for failure + + * [AUTO-TRANSLATED:d5ce73e0] + */ + static int setBroadcast(int fd, bool on = true); + + /** + * 是否开启TCP KeepAlive特性 + * @param fd socket fd号 + * @param on 是否开启该特性 + * @param idle keepalive空闲时间 + * @param interval keepalive探测时间间隔 + * @param times keepalive探测次数 + * @return 0代表成功,-1为失败 + * Enable TCP KeepAlive feature + * @param fd socket fd number + * @param on whether to enable this feature + * @param idle keepalive idle time + * @param interval keepalive probe time interval + * @param times keepalive probe times + * @return 0 represents success, -1 for failure + + * [AUTO-TRANSLATED:9b44a8ec] + */ + static int setKeepAlive(int fd, bool on = true, int interval = TCP_KEEPALIVE_INTERVAL, int idle = TCP_KEEPALIVE_TIME, int times = TCP_KEEPALIVE_PROBE_TIMES); + + /** + * 是否开启FD_CLOEXEC特性(多进程相关) + * @param fd fd号,不一定是socket + * @param on 是否开启该特性 + * @return 0代表成功,-1为失败 + * Enable FD_CLOEXEC feature (related to multiple processes) + * @param fd fd number, not necessarily a socket + * @param on whether to enable this feature + * @return 0 represents success, -1 for failure + + * [AUTO-TRANSLATED:964368da] + */ + static int setCloExec(int fd, bool on = true); + + /** + * 开启SO_LINGER特性 + * @param sock socket fd号 + * @param second 内核等待关闭socket超时时间,单位秒 + * @return 0代表成功,-1为失败 + * Enable SO_LINGER feature + * @param sock socket fd number + * @param second kernel waiting time for closing socket timeout, in seconds + * @return 0 represents success, -1 for failure + + * [AUTO-TRANSLATED:92230daf] + */ + static int setCloseWait(int sock, int second = 0); + + /** + * dns解析 + * @param host 域名或ip + * @param port 端口号 + * @param addr sockaddr结构体 + * @return 是否成功 + * DNS resolution + * @param host domain name or IP + * @param port port number + * @param addr sockaddr structure + * @return whether successful + + * [AUTO-TRANSLATED:3b79cf5d] + */ + static bool getDomainIP(const char *host, uint16_t port, struct sockaddr_storage &addr, int ai_family = AF_INET, + int ai_socktype = SOCK_STREAM, int ai_protocol = IPPROTO_TCP, int expire_sec = 60); + + /** + * 设置组播ttl + * @param sock socket fd号 + * @param ttl ttl值 + * @return 0代表成功,-1为失败 + * Set multicast TTL + * @param sock socket fd number + * @param ttl TTL value + * @return 0 represents success, -1 for failure + + * [AUTO-TRANSLATED:1828beb5] + */ + static int setMultiTTL(int sock, uint8_t ttl = 64); + + /** + * 设置组播发送网卡 + * @param sock socket fd号 + * @param local_ip 本机网卡ip + * @return 0代表成功,-1为失败 + * Set multicast sending network card + * @param sock socket fd number + * @param local_ip local network card IP + * @return 0 represents success, -1 for failure + + * [AUTO-TRANSLATED:25e8e9d7] + */ + static int setMultiIF(int sock, const char *local_ip); + + /** + * 设置是否接收本机发出的组播包 + * @param fd socket fd号 + * @param acc 是否接收 + * @return 0代表成功,-1为失败 + * Set whether to receive multicast packets sent by the local machine + * @param fd socket fd number + * @param acc whether to receive + * @return 0 represents success, -1 for failure + + * [AUTO-TRANSLATED:83cec1e8] + */ + static int setMultiLOOP(int fd, bool acc = false); + + /** + * 加入组播 + * @param fd socket fd号 + * @param addr 组播地址 + * @param local_ip 本机网卡ip + * @return 0代表成功,-1为失败 + * Join multicast + * @param fd socket fd number + * @param addr multicast address + * @param local_ip local network card IP + * @return 0 represents success, -1 for failure + + * [AUTO-TRANSLATED:45523b25] + */ + static int joinMultiAddr(int fd, const char *addr, const char *local_ip = "0.0.0.0"); + + /** + * 退出组播 + * @param fd socket fd号 + * @param addr 组播地址 + * @param local_ip 本机网卡ip + * @return 0代表成功,-1为失败 + * Exit multicast + * @param fd socket fd number + * @param addr multicast address + * @param local_ip local network card ip + * @return 0 represents success, -1 for failure + + * [AUTO-TRANSLATED:081785d3] + */ + static int leaveMultiAddr(int fd, const char *addr, const char *local_ip = "0.0.0.0"); + + /** + * 加入组播并只接受该源端的组播数据 + * @param sock socket fd号 + * @param addr 组播地址 + * @param src_ip 数据源端地址 + * @param local_ip 本机网卡ip + * @return 0代表成功,-1为失败 + * Join multicast and only receive multicast data from the specified source + * @param sock socket fd number + * @param addr multicast address + * @param src_ip source address + * @param local_ip local network card ip + * @return 0 represents success, -1 for failure + + * [AUTO-TRANSLATED:061989eb] + */ + static int joinMultiAddrFilter(int sock, const char *addr, const char *src_ip, const char *local_ip = "0.0.0.0"); + + /** + * 退出组播 + * @param fd socket fd号 + * @param addr 组播地址 + * @param src_ip 数据源端地址 + * @param local_ip 本机网卡ip + * @return 0代表成功,-1为失败 + * Exit multicast + * @param fd socket fd number + * @param addr multicast address + * @param src_ip source address + * @param local_ip local network card ip + * @return 0 represents success, -1 for failure + + * [AUTO-TRANSLATED:9cd166c7] + */ + static int leaveMultiAddrFilter(int fd, const char *addr, const char *src_ip, const char *local_ip = "0.0.0.0"); + + /** + * 获取该socket当前发生的错误 + * @param fd socket fd号 + * @return 错误代码 + * Get the current error of the socket + * @param fd socket fd number + * @return error code + + * [AUTO-TRANSLATED:e4500a0f] + */ + static int getSockError(int fd); + + /** + * 获取网卡列表 + * @return vector > + * Get the list of network cards + * @return vector > + + * [AUTO-TRANSLATED:94687465] + */ + static std::vector> getInterfaceList(); + + /** + * 获取本机默认网卡ip + * Get the default local ip of the host + + * [AUTO-TRANSLATED:9eb5d031] + */ + static std::string get_local_ip(); + + /** + * 获取该socket绑定的本地ip + * @param sock socket fd号 + * Get the local ip bound to the socket + * @param sock socket fd number + + * [AUTO-TRANSLATED:4e7b6040] + */ + static std::string get_local_ip(int sock); + + /** + * 获取该socket绑定的本地端口 + * @param sock socket fd号 + * Get the local port bound to the socket + * @param sock socket fd number + + * [AUTO-TRANSLATED:7b212118] + */ + static uint16_t get_local_port(int sock); + + /** + * 获取该socket绑定的远端ip + * @param sock socket fd号 + * Get the remote ip bound to the socket + * @param sock socket fd number + + * [AUTO-TRANSLATED:952ddef8] + */ + static std::string get_peer_ip(int sock); + + /** + * 获取该socket绑定的远端端口 + * @param sock socket fd号 + * Get the remote port bound to the socket + * @param sock socket fd number + + * [AUTO-TRANSLATED:3b9bcf2e] + */ + static uint16_t get_peer_port(int sock); + + static bool support_ipv6(); + /** + * 线程安全的in_addr转ip字符串 + * Thread-safe conversion of in_addr to IP string + + * [AUTO-TRANSLATED:e0ff8b4b] + */ + static std::string inet_ntoa(const struct in_addr &addr); + static std::string inet_ntoa(const struct in6_addr &addr); + static std::string inet_ntoa(const struct sockaddr *addr, bool mapV4 = true); + static uint16_t inet_port(const struct sockaddr *addr); + static struct sockaddr_storage make_sockaddr(const char *ip, uint16_t port); + static socklen_t get_sock_len(const struct sockaddr *addr); + static bool get_sock_local_addr(int fd, struct sockaddr_storage &addr); + static bool get_sock_peer_addr(int fd, struct sockaddr_storage &addr); + static bool is_same_addr(const struct sockaddr* a, const struct sockaddr* b); + + /** + * 获取网卡ip + * @param if_name 网卡名 + * Get the IP of the network card + * @param if_name Network card name + + * [AUTO-TRANSLATED:e88f1554] + */ + static std::string get_ifr_ip(const char *if_name); + + /** + * 获取网卡名 + * @param local_op 网卡ip + * Get the network card name + * @param local_op Network card IP + + * [AUTO-TRANSLATED:cdaad7f0] + */ + static std::string get_ifr_name(const char *local_op); + + /** + * 根据网卡名获取子网掩码 + * @param if_name 网卡名 + * Get the subnet mask based on the network card name + * @param if_name Network card name + + * [AUTO-TRANSLATED:a6714ee2] + */ + static std::string get_ifr_mask(const char *if_name); + + /** + * 根据网卡名获取广播地址 + * @param if_name 网卡名 + * Get the broadcast address based on the network card name + * @param if_name Network card name + + * [AUTO-TRANSLATED:20348c92] + */ + static std::string get_ifr_brdaddr(const char *if_name); + + /** + * 判断两个ip是否为同一网段 + * @param src_ip 我的ip + * @param dts_ip 对方ip + * Determine if two IPs are in the same network segment + * @param src_ip My IP + * @param dts_ip Peer IP + + * [AUTO-TRANSLATED:95acb68f] + */ + static bool in_same_lan(const char *src_ip, const char *dts_ip); + + /** + * 判断是否为ipv4地址 + * Determine if it is an IPv4 address + + * [AUTO-TRANSLATED:b5af4ea0] + */ + static bool is_ipv4(const char *str); + + /** + * 判断是否为ipv6地址 + * Determine if it is an IPv6 address + + * [AUTO-TRANSLATED:70526900] + */ + static bool is_ipv6(const char *str); +}; + +} // namespace toolkit +#endif // !NETWORK_SOCKUTIL_H diff --git a/3rdpart/ZLToolKit/src/Poller/EventPoller.cpp b/3rdpart/ZLToolKit/src/Poller/EventPoller.cpp new file mode 100644 index 0000000..8622d25 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Poller/EventPoller.cpp @@ -0,0 +1,646 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "SelectWrap.h" +#include "EventPoller.h" +#include "Util/util.h" +#include "Util/uv_errno.h" +#include "Util/TimeTicker.h" +#include "Util/NoticeCenter.h" +#include "Network/sockutil.h" + +#if defined(HAS_EPOLL) +#include + +#if !defined(EPOLLEXCLUSIVE) +#define EPOLLEXCLUSIVE 0 +#endif + +#define EPOLL_SIZE 1024 + +//防止epoll惊群 [AUTO-TRANSLATED:ad53c775] +//Prevent epoll thundering +#ifndef EPOLLEXCLUSIVE +#define EPOLLEXCLUSIVE 0 +#endif + +#define toEpoll(event) (((event) & Event_Read) ? EPOLLIN : 0) \ + | (((event) & Event_Write) ? EPOLLOUT : 0) \ + | (((event) & Event_Error) ? (EPOLLHUP | EPOLLERR) : 0) \ + | (((event) & Event_LT) ? 0 : EPOLLET) + +#define toPoller(epoll_event) (((epoll_event) & (EPOLLIN | EPOLLRDNORM | EPOLLHUP)) ? Event_Read : 0) \ + | (((epoll_event) & (EPOLLOUT | EPOLLWRNORM)) ? Event_Write : 0) \ + | (((epoll_event) & EPOLLHUP) ? Event_Error : 0) \ + | (((epoll_event) & EPOLLERR) ? Event_Error : 0) +#define create_event() epoll_create(EPOLL_SIZE) +#if !defined(_WIN32) +#define close_event(fd) close(fd) +#else +#define close_event(fd) epoll_close(fd) +#endif +#endif //HAS_EPOLL + +#if defined(HAS_KQUEUE) +#include +#define KEVENT_SIZE 1024 +#define create_event() kqueue() +#define close_event(fd) close(fd) +#endif // HAS_KQUEUE + +using namespace std; + +namespace toolkit { + +EventPoller &EventPoller::Instance() { + return *(EventPollerPool::Instance().getFirstPoller()); +} + +void EventPoller::addEventPipe() { + SockUtil::setNoBlocked(_pipe.readFD()); + SockUtil::setNoBlocked(_pipe.writeFD()); + + // 添加内部管道事件 [AUTO-TRANSLATED:6a72e39a] + //Add internal pipe event + if (addEvent(_pipe.readFD(), EventPoller::Event_Read, [this](int event) { onPipeEvent(); }) == -1) { + throw std::runtime_error("Add pipe fd to poller failed"); + } +} + +EventPoller::EventPoller(std::string name) { +#if defined(HAS_EPOLL) || defined(HAS_KQUEUE) + _event_fd = create_event(); + if (_event_fd == INVALID_EVENT_FD) { + throw runtime_error(StrPrinter << "Create event fd failed: " << get_uv_errmsg()); + } +#if !defined(_WIN32) + SockUtil::setCloExec(_event_fd); +#endif +#endif + + _name = std::move(name); + _logger = Logger::Instance().shared_from_this(); + addEventPipe(); +} + +void EventPoller::shutdown() { + async_l([]() { + throw ExitException(); + }, false, true); + + if (_loop_thread) { + //防止作为子进程时崩溃 [AUTO-TRANSLATED:68727e34] + //Prevent crash when running as a child process + try { _loop_thread->join(); } catch (...) { _loop_thread->detach(); } + delete _loop_thread; + _loop_thread = nullptr; + } +} + +EventPoller::~EventPoller() { + shutdown(); + +#if defined(HAS_EPOLL) || defined(HAS_KQUEUE) + if (_event_fd != INVALID_EVENT_FD) { + close_event(_event_fd); + _event_fd = INVALID_EVENT_FD; + } +#endif + + //退出前清理管道中的数据 [AUTO-TRANSLATED:60e26f9a] + //Clean up pipe data before exiting + onPipeEvent(true); + InfoL << getThreadName(); +} + +int EventPoller::addEvent(int fd, int event, PollEventCB cb) { + TimeTicker(); + if (!cb) { + WarnL << "PollEventCB is empty"; + return -1; + } + + if (isCurrentThread()) { +#if defined(HAS_EPOLL) + struct epoll_event ev = {0}; + ev.events = toEpoll(event) ; + ev.data.fd = fd; + int ret = epoll_ctl(_event_fd, EPOLL_CTL_ADD, fd, &ev); + if (ret != -1) { + _event_map.emplace(fd, std::make_shared(std::move(cb))); + } + _fd_count = _event_map.size(); + return ret; +#elif defined(HAS_KQUEUE) + struct kevent kev[2]; + int index = 0; + if (event & Event_Read) { + EV_SET(&kev[index++], fd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, nullptr); + } + if (event & Event_Write) { + EV_SET(&kev[index++], fd, EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, nullptr); + } + int ret = kevent(_event_fd, kev, index, nullptr, 0, nullptr); + if (ret != -1) { + _event_map.emplace(fd, std::make_shared(std::move(cb))); + } + _fd_count = _event_map.size(); + return ret; +#else +#ifndef _WIN32 + // win32平台,socket套接字不等于文件描述符,所以可能不适用这个限制 [AUTO-TRANSLATED:6adfc664] + //On the win32 platform, the socket does not equal the file descriptor, so this restriction may not apply + if (fd >= FD_SETSIZE) { + WarnL << "select() can not watch fd bigger than " << FD_SETSIZE; + return -1; + } +#endif + auto record = std::make_shared(); + record->fd = fd; + record->event = event; + record->call_back = std::move(cb); + _event_map.emplace(fd, record); + _fd_count = _event_map.size(); + return 0; +#endif + } + + async([this, fd, event, cb]() mutable { + addEvent(fd, event, std::move(cb)); + }); + return 0; +} + +int EventPoller::delEvent(int fd, PollCompleteCB cb) { + TimeTicker(); + if (!cb) { + cb = [](bool success) {}; + } + + if (isCurrentThread()) { +#if defined(HAS_EPOLL) + int ret = -1; + if (_event_map.erase(fd)) { + _event_cache_expired.emplace(fd); + ret = epoll_ctl(_event_fd, EPOLL_CTL_DEL, fd, nullptr); + } + cb(ret != -1); + _fd_count = _event_map.size(); + return ret; +#elif defined(HAS_KQUEUE) + int ret = -1; + if (_event_map.erase(fd)) { + _event_cache_expired.emplace(fd); + struct kevent kev[2]; + int index = 0; + EV_SET(&kev[index++], fd, EVFILT_READ, EV_DELETE, 0, 0, nullptr); + EV_SET(&kev[index++], fd, EVFILT_WRITE, EV_DELETE, 0, 0, nullptr); + ret = kevent(_event_fd, kev, index, nullptr, 0, nullptr); + } + cb(ret != -1); + _fd_count = _event_map.size(); + return ret; +#else + int ret = -1; + if (_event_map.erase(fd)) { + _event_cache_expired.emplace(fd); + ret = 0; + } + cb(ret != -1); + _fd_count = _event_map.size(); + return ret; +#endif //HAS_EPOLL + } + + //跨线程操作 [AUTO-TRANSLATED:4e116519] + //Cross-thread operation + async([this, fd, cb]() mutable { + delEvent(fd, std::move(cb)); + }); + return 0; +} + +int EventPoller::modifyEvent(int fd, int event, PollCompleteCB cb) { + TimeTicker(); + if (!cb) { + cb = [](bool success) {}; + } + if (isCurrentThread()) { +#if defined(HAS_EPOLL) + struct epoll_event ev = { 0 }; + ev.events = toEpoll(event); + ev.data.fd = fd; + auto ret = epoll_ctl(_event_fd, EPOLL_CTL_MOD, fd, &ev); + cb(ret != -1); + return ret; +#elif defined(HAS_KQUEUE) + struct kevent kev[2]; + int index = 0; + EV_SET(&kev[index++], fd, EVFILT_READ, event & Event_Read ? EV_ADD | EV_CLEAR : EV_DELETE, 0, 0, nullptr); + EV_SET(&kev[index++], fd, EVFILT_WRITE, event & Event_Write ? EV_ADD | EV_CLEAR : EV_DELETE, 0, 0, nullptr); + int ret = kevent(_event_fd, kev, index, nullptr, 0, nullptr); + cb(ret != -1); + return ret; +#else + auto it = _event_map.find(fd); + if (it != _event_map.end()) { + it->second->event = event; + } + cb(it != _event_map.end()); + return it != _event_map.end() ? 0 : -1; +#endif // HAS_EPOLL + } + async([this, fd, event, cb]() mutable { + modifyEvent(fd, event, std::move(cb)); + }); + return 0; +} + +size_t EventPoller::fdCount() const { + return _fd_count; +} + +Task::Ptr EventPoller::async(TaskIn task, bool may_sync) { + return async_l(std::move(task), may_sync, false); +} + +Task::Ptr EventPoller::async_first(TaskIn task, bool may_sync) { + return async_l(std::move(task), may_sync, true); +} + +Task::Ptr EventPoller::async_l(TaskIn task, bool may_sync, bool first) { + TimeTicker(); + if (may_sync && isCurrentThread()) { + task(); + return nullptr; + } + + auto ret = std::make_shared(std::move(task)); + { + lock_guard lck(_mtx_task); + if (first) { + _list_task.emplace_front(ret); + } else { + _list_task.emplace_back(ret); + } + } + //写数据到管道,唤醒主线程 [AUTO-TRANSLATED:2ead8182] + //Write data to the pipe and wake up the main thread + _pipe.write("", 1); + return ret; +} + +bool EventPoller::isCurrentThread() { + return !_loop_thread || _loop_thread->get_id() == this_thread::get_id(); +} + +inline void EventPoller::onPipeEvent(bool flush) { + char buf[1024]; + int err = 0; + if (!flush) { + for (;;) { + if ((err = _pipe.read(buf, sizeof(buf))) > 0) { + // 读到管道数据,继续读,直到读空为止 [AUTO-TRANSLATED:47bd325c] + //Read data from the pipe, continue reading until it's empty + continue; + } + if (err == 0 || get_uv_error(true) != UV_EAGAIN) { + // 收到eof或非EAGAIN(无更多数据)错误,说明管道无效了,重新打开管道 [AUTO-TRANSLATED:5f7a013d] + //Received eof or non-EAGAIN (no more data) error, indicating that the pipe is invalid, reopen the pipe + ErrorL << "Invalid pipe fd of event poller, reopen it"; + delEvent(_pipe.readFD()); + _pipe.reOpen(); + addEventPipe(); + } + break; + } + } + + decltype(_list_task) _list_swap; + { + lock_guard lck(_mtx_task); + _list_swap.swap(_list_task); + } + + _list_swap.for_each([&](const Task::Ptr &task) { + try { + (*task)(); + } catch (ExitException &) { + _exit_flag = true; + } catch (std::exception &ex) { + ErrorL << "Exception occurred when do async task: " << ex.what(); + } + }); +} + +SocketRecvBuffer::Ptr EventPoller::getSharedBuffer(bool is_udp) { +#if !defined(__linux) && !defined(__linux__) + // 非Linux平台下,tcp和udp共享recvfrom方案,使用同一个buffer [AUTO-TRANSLATED:2d2ee7bf] + //On non-Linux platforms, tcp and udp share the recvfrom scheme, using the same buffer + is_udp = 0; +#endif + auto ret = _shared_buffer[is_udp].lock(); + if (!ret) { + ret = SocketRecvBuffer::create(is_udp); + _shared_buffer[is_udp] = ret; + } + return ret; +} + +thread::id EventPoller::getThreadId() const { + return _loop_thread ? _loop_thread->get_id() : thread::id(); +} + +const std::string& EventPoller::getThreadName() const { + return _name; +} + +static thread_local std::weak_ptr s_current_poller; + +// static +EventPoller::Ptr EventPoller::getCurrentPoller() { + return s_current_poller.lock(); +} + +void EventPoller::runLoop(bool blocked, bool ref_self) { + if (blocked) { + if (ref_self) { + s_current_poller = shared_from_this(); + } + _sem_run_started.post(); + _exit_flag = false; + int64_t minDelay; +#if defined(HAS_EPOLL) + struct epoll_event events[EPOLL_SIZE]; + while (!_exit_flag) { + minDelay = getMinDelay(); + startSleep(); // 用于统计当前线程负载情况 + int ret = epoll_wait(_event_fd, events, EPOLL_SIZE, minDelay); + sleepWakeUp(); // 用于统计当前线程负载情况 + if (ret <= 0) { + // 超时或被打断 [AUTO-TRANSLATED:7005fded] + // Timed out or interrupted + continue; + } + + _event_cache_expired.clear(); + + for (int i = 0; i < ret; ++i) { + struct epoll_event &ev = events[i]; + int fd = ev.data.fd; + if (_event_cache_expired.count(fd)) { + // event cache refresh + continue; + } + + auto it = _event_map.find(fd); + if (it == _event_map.end()) { + epoll_ctl(_event_fd, EPOLL_CTL_DEL, fd, nullptr); + continue; + } + auto cb = it->second; + try { + (*cb)(toPoller(ev.events)); + } catch (std::exception &ex) { + ErrorL << "Exception occurred when do event task: " << ex.what(); + } + } + } +#elif defined(HAS_KQUEUE) + struct kevent kevents[KEVENT_SIZE]; + while (!_exit_flag) { + minDelay = getMinDelay(); + struct timespec timeout = { (long)minDelay / 1000, (long)minDelay % 1000 * 1000000 }; + + startSleep(); + int ret = kevent(_event_fd, nullptr, 0, kevents, KEVENT_SIZE, minDelay == -1 ? nullptr : &timeout); + sleepWakeUp(); + if (ret <= 0) { + continue; + } + + _event_cache_expired.clear(); + + for (int i = 0; i < ret; ++i) { + auto &kev = kevents[i]; + auto fd = kev.ident; + if (_event_cache_expired.count(fd)) { + // event cache refresh + continue; + } + + auto it = _event_map.find(fd); + if (it == _event_map.end()) { + EV_SET(&kev, fd, kev.filter, EV_DELETE, 0, 0, nullptr); + kevent(_event_fd, &kev, 1, nullptr, 0, nullptr); + continue; + } + auto cb = it->second; + int event = 0; + switch (kev.filter) { + case EVFILT_READ: event = Event_Read; break; + case EVFILT_WRITE: event = Event_Write; break; + default: WarnL << "unknown kevent filter: " << kev.filter; break; + } + + try { + (*cb)(event); + } catch (std::exception &ex) { + ErrorL << "Exception occurred when do event task: " << ex.what(); + } + } + } +#else + int ret, max_fd; + FdSet set_read, set_write, set_err; + List callback_list; + struct timeval tv; + while (!_exit_flag) { + // 定时器事件中可能操作_event_map [AUTO-TRANSLATED:f2a50ee2] + // Possible operations on _event_map in timer events + minDelay = getMinDelay(); + tv.tv_sec = (decltype(tv.tv_sec))(minDelay / 1000); + tv.tv_usec = 1000 * (minDelay % 1000); + + set_read.fdZero(); + set_write.fdZero(); + set_err.fdZero(); + max_fd = 0; + for (auto &pr : _event_map) { + if (pr.first > max_fd) { + max_fd = pr.first; + } + if (pr.second->event & Event_Read) { + set_read.fdSet(pr.first); // 监听管道可读事件 + } + if (pr.second->event & Event_Write) { + set_write.fdSet(pr.first); // 监听管道可写事件 + } + if (pr.second->event & Event_Error) { + set_err.fdSet(pr.first); // 监听管道错误事件 + } + } + + startSleep(); // 用于统计当前线程负载情况 + ret = zl_select(max_fd + 1, &set_read, &set_write, &set_err, minDelay == -1 ? nullptr : &tv); + sleepWakeUp(); // 用于统计当前线程负载情况 + + if (ret <= 0) { + // 超时或被打断 [AUTO-TRANSLATED:7005fded] + // Timed out or interrupted + continue; + } + + _event_cache_expired.clear(); + + // 收集select事件类型 [AUTO-TRANSLATED:9a5c41d3] + // Collect select event types + for (auto &pr : _event_map) { + int event = 0; + if (set_read.isSet(pr.first)) { + event |= Event_Read; + } + if (set_write.isSet(pr.first)) { + event |= Event_Write; + } + if (set_err.isSet(pr.first)) { + event |= Event_Error; + } + if (event != 0) { + pr.second->attach = event; + callback_list.emplace_back(pr.second); + } + } + + callback_list.for_each([&](Poll_Record::Ptr &record) { + if (_event_cache_expired.count(record->fd)) { + // event cache refresh + return; + } + + try { + record->call_back(record->attach); + } catch (std::exception &ex) { + ErrorL << "Exception occurred when do event task: " << ex.what(); + } + }); + callback_list.clear(); + } +#endif //HAS_EPOLL + } else { + _loop_thread = new thread(&EventPoller::runLoop, this, true, ref_self); + _sem_run_started.wait(); + } +} + +int64_t EventPoller::flushDelayTask(uint64_t now_time) { + decltype(_delay_task_map) task_copy; + task_copy.swap(_delay_task_map); + + for (auto it = task_copy.begin(); it != task_copy.end() && it->first <= now_time; it = task_copy.erase(it)) { + //已到期的任务 [AUTO-TRANSLATED:849cdc29] + //Expired tasks + try { + auto next_delay = (*(it->second))(); + if (next_delay) { + //可重复任务,更新时间截止线 [AUTO-TRANSLATED:c7746a21] + //Repeatable tasks, update deadline + _delay_task_map.emplace(next_delay + now_time, std::move(it->second)); + } + } catch (std::exception &ex) { + ErrorL << "Exception occurred when do delay task: " << ex.what(); + } + } + + task_copy.insert(_delay_task_map.begin(), _delay_task_map.end()); + task_copy.swap(_delay_task_map); + + auto it = _delay_task_map.begin(); + if (it == _delay_task_map.end()) { + //没有剩余的定时器了 [AUTO-TRANSLATED:23b1119e] + //No remaining timers + return -1; + } + //最近一个定时器的执行延时 [AUTO-TRANSLATED:2535621b] + //Delay in execution of the last timer + return it->first - now_time; +} + +int64_t EventPoller::getMinDelay() { + auto it = _delay_task_map.begin(); + if (it == _delay_task_map.end()) { + //没有剩余的定时器了 [AUTO-TRANSLATED:23b1119e] + //No remaining timers + return -1; + } + auto now = getCurrentMillisecond(); + if (it->first > now) { + //所有任务尚未到期 [AUTO-TRANSLATED:8d80eabf] + //All tasks have not expired + return it->first - now; + } + //执行已到期的任务并刷新休眠延时 [AUTO-TRANSLATED:cd6348b7] + //Execute expired tasks and refresh sleep delay + return flushDelayTask(now); +} + +EventPoller::DelayTask::Ptr EventPoller::doDelayTask(uint64_t delay_ms, function task) { + DelayTask::Ptr ret = std::make_shared(std::move(task)); + auto time_line = getCurrentMillisecond() + delay_ms; + async_first([time_line, ret, this]() { + //异步执行的目的是刷新select或epoll的休眠时间 [AUTO-TRANSLATED:a6b5c8d7] + //The purpose of asynchronous execution is to refresh the sleep time of select or epoll + _delay_task_map.emplace(time_line, ret); + }); + return ret; +} + + +/////////////////////////////////////////////// + +static size_t s_pool_size = 0; +static bool s_enable_cpu_affinity = true; + +INSTANCE_IMP(EventPollerPool) + +EventPoller::Ptr EventPollerPool::getFirstPoller() { + return static_pointer_cast(_threads.front()); +} + +EventPoller::Ptr EventPollerPool::getPoller(bool prefer_current_thread) { + auto poller = EventPoller::getCurrentPoller(); + if (prefer_current_thread && _prefer_current_thread && poller) { + return poller; + } + return static_pointer_cast(getExecutor()); +} + +void EventPollerPool::preferCurrentThread(bool flag) { + _prefer_current_thread = flag; +} + +const std::string EventPollerPool::kOnStarted = "kBroadcastEventPollerPoolStarted"; + +EventPollerPool::EventPollerPool() { + auto size = addPoller("event poller", s_pool_size, ThreadPool::PRIORITY_HIGHEST, true, s_enable_cpu_affinity); + NOTICE_EMIT(EventPollerPoolOnStartedArgs, kOnStarted, *this, size); + InfoL << "EventPoller created size: " << size; +} + +void EventPollerPool::setPoolSize(size_t size) { + s_pool_size = size; +} + +void EventPollerPool::enableCpuAffinity(bool enable) { + s_enable_cpu_affinity = enable; +} + +} // namespace toolkit + diff --git a/3rdpart/ZLToolKit/src/Poller/EventPoller.h b/3rdpart/ZLToolKit/src/Poller/EventPoller.h new file mode 100644 index 0000000..c196ea8 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Poller/EventPoller.h @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef EventPoller_h +#define EventPoller_h + +#include +#include +#include +#include +#include +#include +#include +#include "PipeWrap.h" +#include "Util/logger.h" +#include "Util/List.h" +#include "Thread/TaskExecutor.h" +#include "Thread/ThreadPool.h" +#include "Network/Buffer.h" +#include "Network/BufferSock.h" + +#if defined(__linux__) || defined(__linux) +#define HAS_EPOLL +#endif //__linux__ + +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#define HAS_KQUEUE +#endif // __APPLE__ + +#if defined(HAS_EPOLL) || defined(HAS_KQUEUE) +#if defined(_WIN32) +using epoll_fd = void *; +constexpr epoll_fd INVALID_EVENT_FD = nullptr; +#else +using epoll_fd = int; +constexpr epoll_fd INVALID_EVENT_FD = -1; +#endif +#endif + +namespace toolkit { + +class EventPoller : public TaskExecutor, public AnyStorage, public std::enable_shared_from_this { +public: + friend class TaskExecutorGetterImp; + + using Ptr = std::shared_ptr; + using PollEventCB = std::function; + using PollCompleteCB = std::function; + using DelayTask = TaskCancelableImp; + + typedef enum { + Event_Read = 1 << 0, // 读事件 + Event_Write = 1 << 1, // 写事件 + Event_Error = 1 << 2, // 错误事件 + Event_LT = 1 << 3, // 水平触发 + } Poll_Event; + + ~EventPoller(); + + /** + * 获取EventPollerPool单例中的第一个EventPoller实例, + * 保留该接口是为了兼容老代码 + * @return 单例 + * Gets the first EventPoller instance from the EventPollerPool singleton, + * This interface is preserved for compatibility with old code. + * @return singleton + * [AUTO-TRANSLATED:b536ebf6] + */ + static EventPoller &Instance(); + + /** + * 添加事件监听 + * @param fd 监听的文件描述符 + * @param event 事件类型,例如 Event_Read | Event_Write + * @param cb 事件回调functional + * @return -1:失败,0:成功 + * Adds an event listener + * @param fd The file descriptor to listen to + * @param event The event type, e.g. Event_Read | Event_Write + * @param cb The event callback function + * @return -1: failed, 0: success + * [AUTO-TRANSLATED:cfba4c75] + */ + int addEvent(int fd, int event, PollEventCB cb); + + /** + * 删除事件监听 + * @param fd 监听的文件描述符 + * @param cb 删除成功回调functional + * @return -1:失败,0:成功 + * Deletes an event listener + * @param fd The file descriptor to stop listening to + * @param cb The callback function for successful deletion + * @return -1: failed, 0: success + * [AUTO-TRANSLATED:be6fdf51] + */ + int delEvent(int fd, PollCompleteCB cb = nullptr); + + /** + * 修改监听事件类型 + * @param fd 监听的文件描述符 + * @param event 事件类型,例如 Event_Read | Event_Write + * @return -1:失败,0:成功 + * Modifies the event type being listened to + * @param fd The file descriptor to modify + * @param event The new event type, e.g. Event_Read | Event_Write + * @return -1: failed, 0: success + * [AUTO-TRANSLATED:becf3d09] + */ + int modifyEvent(int fd, int event, PollCompleteCB cb = nullptr); + + /** + * 返回获取监听了多少个fd事件 + */ + size_t fdCount() const; + + /** + * 异步执行任务 + * @param task 任务 + * @param may_sync 如果调用该函数的线程就是本对象的轮询线程,那么may_sync为true时就是同步执行任务 + * @return 是否成功,一定会返回true + * Executes a task asynchronously + * @param task The task to execute + * @param may_sync If the calling thread is the polling thread of this object, + * then if may_sync is true, the task will be executed synchronously + * @return Whether the task was executed successfully (always returns true) + * [AUTO-TRANSLATED:071f7ed8] + */ + Task::Ptr async(TaskIn task, bool may_sync = true) override; + + /** + * 同async方法,不过是把任务打入任务列队头,这样任务优先级最高 + * @param task 任务 + * @param may_sync 如果调用该函数的线程就是本对象的轮询线程,那么may_sync为true时就是同步执行任务 + * @return 是否成功,一定会返回true + * Similar to async, but adds the task to the head of the task queue, + * giving it the highest priority + * @param task The task to execute + * @param may_sync If the calling thread is the polling thread of this object, + * then if may_sync is true, the task will be executed synchronously + * @return Whether the task was executed successfully (always returns true) + * [AUTO-TRANSLATED:9ef5169b] + */ + Task::Ptr async_first(TaskIn task, bool may_sync = true) override; + + /** + * 判断执行该接口的线程是否为本对象的轮询线程 + * @return 是否为本对象的轮询线程 + * Checks if the thread calling this interface is the polling thread of this object + * @return Whether the calling thread is the polling thread + * [AUTO-TRANSLATED:db9a4916] + */ + bool isCurrentThread(); + + /** + * 延时执行某个任务 + * @param delay_ms 延时毫秒数 + * @param task 任务,返回值为0时代表不再重复任务,否则为下次执行延时,如果任务中抛异常,那么默认不重复任务 + * @return 可取消的任务标签 + * Delays the execution of a task + * @param delay_ms The delay in milliseconds + * @param task The task to execute, returns 0 to stop repeating the task, + * otherwise returns the delay for the next execution. + * If an exception is thrown in the task, it defaults to not repeating the task. + * @return A cancellable task label + * [AUTO-TRANSLATED:61f97e64] + */ + DelayTask::Ptr doDelayTask(uint64_t delay_ms, std::function task); + + /** + * 获取当前线程关联的Poller实例 + * Gets the Poller instance associated with the current thread + * [AUTO-TRANSLATED:debcf0e2] + */ + static EventPoller::Ptr getCurrentPoller(); + + /** + * 获取当前线程下所有socket共享的读缓存 + * Gets the shared read buffer for all sockets in the current thread + * [AUTO-TRANSLATED:2796f458] + */ + SocketRecvBuffer::Ptr getSharedBuffer(bool is_udp); + + /** + * 获取poller线程id + * Get the poller thread ID + * [AUTO-TRANSLATED:1c968752] + */ + std::thread::id getThreadId() const; + + /** + * 获取线程名 + * Get the thread name + * [AUTO-TRANSLATED:842652d9] + */ + const std::string &getThreadName() const; + +private: + /** + * 本对象只允许在EventPollerPool中构造 + * This object can only be constructed in EventPollerPool + * [AUTO-TRANSLATED:0c9a8a28] + */ + EventPoller(std::string name); + + /** + * 执行事件轮询 + * @param blocked 是否用执行该接口的线程执行轮询 + * @param ref_self 是记录本对象到thread local变量 + * Perform event polling + * @param blocked Whether to execute polling with the thread that calls this interface + * @param ref_self Whether to record this object to thread local variable + * [AUTO-TRANSLATED:b0ac803c] + */ + void runLoop(bool blocked, bool ref_self); + + /** + * 内部管道事件,用于唤醒轮询线程用 + * Internal pipe event, used to wake up the polling thread + * [AUTO-TRANSLATED:022754b9] + */ + void onPipeEvent(bool flush = false); + + /** + * 切换线程并执行任务 + * @param task + * @param may_sync + * @param first + * @return 可取消的任务本体,如果已经同步执行,则返回nullptr + * Switch threads and execute tasks + * @param task + * @param may_sync + * @param first + * @return The cancellable task itself, or nullptr if it has been executed synchronously + * [AUTO-TRANSLATED:e7019c4a] + */ + Task::Ptr async_l(TaskIn task, bool may_sync = true, bool first = false); + + /** + * 结束事件轮询 + * 需要指出的是,一旦结束就不能再次恢复轮询线程 + * End event polling + * Note that once ended, the polling thread cannot be resumed + * [AUTO-TRANSLATED:4f232154] + */ + void shutdown(); + + /** + * 刷新延时任务 + * Refresh delayed tasks + * [AUTO-TRANSLATED:88104b90] + */ + int64_t flushDelayTask(uint64_t now); + + /** + * 获取select或epoll休眠时间 + * Get the sleep time for select or epoll + * [AUTO-TRANSLATED:34e0384e] + */ + int64_t getMinDelay(); + + /** + * 添加管道监听事件 + * Add pipe listening event + * [AUTO-TRANSLATED:06e5bc67] + */ + void addEventPipe(); + +private: + class ExitException : public std::exception {}; + +private: + // 标记loop线程是否退出 [AUTO-TRANSLATED:98250f84] + // 标记loop线程是否退出 + // Mark the loop thread as exited + bool _exit_flag; + // 统计监听了多少个fd + size_t _fd_count = 0; + // 线程名 [AUTO-TRANSLATED:f1d62d9f] + // 线程名 + // Thread name + std::string _name; + // 当前线程下,所有socket共享的读缓存 [AUTO-TRANSLATED:6ce70017] + // 当前线程下,所有socket共享的读缓存 + // Shared read buffer for all sockets under the current thread + std::weak_ptr _shared_buffer[2]; + // 执行事件循环的线程 [AUTO-TRANSLATED:2465cc75] + // 执行事件循环的线程 + // Thread that executes the event loop + std::thread *_loop_thread = nullptr; + // 通知事件循环的线程已启动 [AUTO-TRANSLATED:61f478cf] + // 通知事件循环的线程已启动 + // Notify the event loop thread that it has started + semaphore _sem_run_started; + + // 内部事件管道 [AUTO-TRANSLATED:dc1d3a93] + // 内部事件管道 + // Internal event pipe + PipeWrap _pipe; + // 从其他线程切换过来的任务 [AUTO-TRANSLATED:d16917d6] + // 从其他线程切换过来的任务 + // Tasks switched from other threads + std::mutex _mtx_task; + List _list_task; + + // 保持日志可用 [AUTO-TRANSLATED:4a6c2438] + // 保持日志可用 + // Keep the log available + Logger::Ptr _logger; + +#if defined(HAS_EPOLL) || defined(HAS_KQUEUE) + // epoll和kqueue相关 [AUTO-TRANSLATED:84d2785e] + // epoll和kqueue相关 + // epoll and kqueue related + epoll_fd _event_fd = INVALID_EVENT_FD; + std::unordered_map> _event_map; +#else + // select相关 [AUTO-TRANSLATED:bf3e2edd] + // select相关 + // select related + struct Poll_Record { + using Ptr = std::shared_ptr; + int fd; + int event; + int attach; + PollEventCB call_back; + }; + std::unordered_map _event_map; +#endif // HAS_EPOLL + std::unordered_set _event_cache_expired; + + // 定时器相关 [AUTO-TRANSLATED:fa2e84da] + // Timer related + std::multimap _delay_task_map; +}; + +class EventPollerPool : public std::enable_shared_from_this, public TaskExecutorGetterImp { +public: + using Ptr = std::shared_ptr; + static const std::string kOnStarted; +#define EventPollerPoolOnStartedArgs EventPollerPool &pool, size_t &size + + ~EventPollerPool() = default; + + /** + * 获取单例 + * @return + * Get singleton + * @return + * [AUTO-TRANSLATED:1cb32aa7] + */ + static EventPollerPool &Instance(); + + /** + * 设置EventPoller个数,在EventPollerPool单例创建前有效 + * 在不调用此方法的情况下,默认创建thread::hardware_concurrency()个EventPoller实例 + * @param size EventPoller个数,如果为0则为thread::hardware_concurrency() + * Set the number of EventPoller instances, effective before the EventPollerPool singleton is created + * If this method is not called, the default is to create thread::hardware_concurrency() EventPoller instances + * @param size Number of EventPoller instances, 0 means thread::hardware_concurrency() + * [AUTO-TRANSLATED:bdc02181] + */ + static void setPoolSize(size_t size = 0); + + /** + * 内部创建线程是否设置cpu亲和性,默认设置cpu亲和性 + * Whether to set CPU affinity for internal thread creation, default is to set CPU affinity + * [AUTO-TRANSLATED:46941c9f] + */ + static void enableCpuAffinity(bool enable); + + /** + * 获取第一个实例 + * @return + * Get the first instance + * @return + * [AUTO-TRANSLATED:a76aad3b] + */ + EventPoller::Ptr getFirstPoller(); + + /** + * 根据负载情况获取轻负载的实例 + * 如果优先返回当前线程,那么会返回当前线程 + * 返回当前线程的目的是为了提高线程安全性 + * @param prefer_current_thread 是否优先获取当前线程 + * Get a lightly loaded instance based on the load + * If prioritizing the current thread, it will return the current thread + * The purpose of returning the current thread is to improve thread safety + * @param prefer_current_thread Whether to prioritize getting the current thread + * [AUTO-TRANSLATED:f0830806] + */ + EventPoller::Ptr getPoller(bool prefer_current_thread = true); + + /** + * 设置 getPoller() 是否优先返回当前线程 + * 在批量创建Socket对象时,如果优先返回当前线程, + * 那么将导致负载不够均衡,所以可以暂时关闭然后再开启 + * @param flag 是否优先返回当前线程 + * Set whether getPoller() prioritizes returning the current thread + * When creating Socket objects in batches, if prioritizing the current thread, + * it will cause the load to be unbalanced, so it can be temporarily closed and then reopened + * @param flag Whether to prioritize returning the current thread + * [AUTO-TRANSLATED:c354e1d5] + */ + void preferCurrentThread(bool flag = true); + +private: + EventPollerPool(); + +private: + bool _prefer_current_thread = true; +}; + +} // namespace toolkit +#endif /* EventPoller_h */ diff --git a/3rdpart/ZLToolKit/src/Poller/Pipe.cpp b/3rdpart/ZLToolKit/src/Poller/Pipe.cpp new file mode 100644 index 0000000..f2338ca --- /dev/null +++ b/3rdpart/ZLToolKit/src/Poller/Pipe.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include "Pipe.h" +#include "Network/sockutil.h" + +using namespace std; + +namespace toolkit { + +Pipe::Pipe(const onRead &cb, const EventPoller::Ptr &poller) { + _poller = poller; + if (!_poller) { + _poller = EventPollerPool::Instance().getPoller(); + } + _pipe = std::make_shared(); + auto pipe = _pipe; + _poller->addEvent(_pipe->readFD(), EventPoller::Event_Read, [cb, pipe](int event) { +#if defined(_WIN32) + unsigned long nread = 1024; +#else + int nread = 1024; +#endif //defined(_WIN32) + ioctl(pipe->readFD(), FIONREAD, &nread); +#if defined(_WIN32) + std::shared_ptr buf(new char[nread + 1], [](char *ptr) {delete[] ptr; }); + buf.get()[nread] = '\0'; + nread = pipe->read(buf.get(), nread + 1); + if (cb) { + cb(nread, buf.get()); + } +#else + char buf[nread + 1]; + buf[nread] = '\0'; + nread = pipe->read(buf, sizeof(buf)); + if (cb) { + cb(nread, buf); + } +#endif // defined(_WIN32) + }); +} + +Pipe::~Pipe() { + if (_pipe) { + auto pipe = _pipe; + _poller->delEvent(pipe->readFD(), [pipe](bool success) {}); + } +} + +void Pipe::send(const char *buf, int size) { + _pipe->write(buf, size); +} + +} // namespace toolkit \ No newline at end of file diff --git a/3rdpart/ZLToolKit/src/Poller/Pipe.h b/3rdpart/ZLToolKit/src/Poller/Pipe.h new file mode 100644 index 0000000..801ae31 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Poller/Pipe.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef Pipe_h +#define Pipe_h + +#include +#include "PipeWrap.h" +#include "EventPoller.h" + +namespace toolkit { + +class Pipe { +public: + using onRead = std::function; + + Pipe(const onRead &cb = nullptr, const EventPoller::Ptr &poller = nullptr); + ~Pipe(); + + void send(const char *send, int size = 0); + +private: + std::shared_ptr _pipe; + EventPoller::Ptr _poller; +}; + +} // namespace toolkit +#endif /* Pipe_h */ \ No newline at end of file diff --git a/3rdpart/ZLToolKit/src/Poller/PipeWrap.cpp b/3rdpart/ZLToolKit/src/Poller/PipeWrap.cpp new file mode 100644 index 0000000..12d7c8a --- /dev/null +++ b/3rdpart/ZLToolKit/src/Poller/PipeWrap.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include "PipeWrap.h" +#include "Util/util.h" +#include "Util/uv_errno.h" +#include "Network/sockutil.h" + +using namespace std; + +#define checkFD(fd) \ + if (fd == -1) { \ + clearFD(); \ + throw runtime_error(StrPrinter << "Create windows pipe failed: " << get_uv_errmsg());\ + } + +#define closeFD(fd) \ + if (fd != -1) { \ + close(fd);\ + fd = -1;\ + } + +namespace toolkit { + +PipeWrap::PipeWrap() { + reOpen(); +} + +void PipeWrap::reOpen() { + clearFD(); +#if defined(_WIN32) + const char *localip = "127.0.0.1"; + auto listener_fd = SockUtil::listen(0, localip); + checkFD(listener_fd) + SockUtil::setNoBlocked(listener_fd,false); + auto localPort = SockUtil::get_local_port(listener_fd); + _pipe_fd[1] = SockUtil::connect(localip, localPort,false); + checkFD(_pipe_fd[1]) + _pipe_fd[0] = (int)accept(listener_fd, nullptr, nullptr); + checkFD(_pipe_fd[0]) + SockUtil::setNoDelay(_pipe_fd[0]); + SockUtil::setNoDelay(_pipe_fd[1]); + close(listener_fd); +#else + if (pipe(_pipe_fd) == -1) { + throw runtime_error(StrPrinter << "Create posix pipe failed: " << get_uv_errmsg()); + } +#endif // defined(_WIN32) + SockUtil::setNoBlocked(_pipe_fd[0], true); + SockUtil::setNoBlocked(_pipe_fd[1], false); + SockUtil::setCloExec(_pipe_fd[0]); + SockUtil::setCloExec(_pipe_fd[1]); +} + +void PipeWrap::clearFD() { + closeFD(_pipe_fd[0]); + closeFD(_pipe_fd[1]); +} + +PipeWrap::~PipeWrap() { + clearFD(); +} + +int PipeWrap::write(const void *buf, int n) { + int ret; + do { +#if defined(_WIN32) + ret = send(_pipe_fd[1], (char *)buf, n, 0); +#else + ret = ::write(_pipe_fd[1], buf, n); +#endif // defined(_WIN32) + } while (-1 == ret && UV_EINTR == get_uv_error(true)); + return ret; +} + +int PipeWrap::read(void *buf, int n) { + int ret; + do { +#if defined(_WIN32) + ret = recv(_pipe_fd[0], (char *)buf, n, 0); +#else + ret = ::read(_pipe_fd[0], buf, n); +#endif // defined(_WIN32) + } while (-1 == ret && UV_EINTR == get_uv_error(true)); + return ret; +} + +} /* namespace toolkit*/ diff --git a/3rdpart/ZLToolKit/src/Poller/PipeWrap.h b/3rdpart/ZLToolKit/src/Poller/PipeWrap.h new file mode 100644 index 0000000..e457df6 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Poller/PipeWrap.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef PipeWarp_h +#define PipeWarp_h + +namespace toolkit { + +class PipeWrap { +public: + PipeWrap(); + ~PipeWrap(); + int write(const void *buf, int n); + int read(void *buf, int n); + int readFD() const { return _pipe_fd[0]; } + int writeFD() const { return _pipe_fd[1]; } + void reOpen(); + +private: + void clearFD(); + +private: + int _pipe_fd[2] = { -1, -1 }; +}; + +} /* namespace toolkit */ +#endif // !PipeWarp_h + diff --git a/3rdpart/ZLToolKit/src/Poller/SelectWrap.cpp b/3rdpart/ZLToolKit/src/Poller/SelectWrap.cpp new file mode 100644 index 0000000..475c590 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Poller/SelectWrap.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "SelectWrap.h" + +using namespace std; + +namespace toolkit { + +FdSet::FdSet() { + _ptr = new fd_set; +} + +FdSet::~FdSet() { + delete (fd_set *)_ptr; +} + +void FdSet::fdZero() { + FD_ZERO((fd_set *)_ptr); +} + +void FdSet::fdClr(int fd) { + FD_CLR(fd, (fd_set *)_ptr); +} + +void FdSet::fdSet(int fd) { + FD_SET(fd, (fd_set *)_ptr); +} + +bool FdSet::isSet(int fd) { + return FD_ISSET(fd, (fd_set *)_ptr); +} + +int zl_select(int cnt, FdSet *read, FdSet *write, FdSet *err, struct timeval *tv) { + void *rd, *wt, *er; + rd = read ? read->_ptr : nullptr; + wt = write ? write->_ptr : nullptr; + er = err ? err->_ptr : nullptr; + return ::select(cnt, (fd_set *) rd, (fd_set *) wt, (fd_set *) er, tv); +} + +} /* namespace toolkit */ + + + diff --git a/3rdpart/ZLToolKit/src/Poller/SelectWrap.h b/3rdpart/ZLToolKit/src/Poller/SelectWrap.h new file mode 100644 index 0000000..c68b35a --- /dev/null +++ b/3rdpart/ZLToolKit/src/Poller/SelectWrap.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_POLLER_SELECTWRAP_H_ +#define SRC_POLLER_SELECTWRAP_H_ + +#include "Util/util.h" + +namespace toolkit { + +class FdSet { +public: + FdSet(); + ~FdSet(); + void fdZero(); + void fdSet(int fd); + void fdClr(int fd); + bool isSet(int fd); + void *_ptr; +}; + +int zl_select(int cnt, FdSet *read, FdSet *write, FdSet *err, struct timeval *tv); + +} /* namespace toolkit */ +#endif /* SRC_POLLER_SELECTWRAP_H_ */ diff --git a/3rdpart/ZLToolKit/src/Poller/Timer.cpp b/3rdpart/ZLToolKit/src/Poller/Timer.cpp new file mode 100644 index 0000000..3161834 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Poller/Timer.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "Timer.h" + +namespace toolkit { + +Timer::Timer(float second, const std::function &cb, const EventPoller::Ptr &poller) { + _poller = poller; + if (!_poller) { + _poller = EventPollerPool::Instance().getPoller(); + } + _tag = _poller->doDelayTask((uint64_t) (second * 1000), [cb, second]() { + try { + if (cb()) { + //重复的任务 [AUTO-TRANSLATED:2d440b54] + //Recurring task + return (uint64_t) (1000 * second); + } + //该任务不再重复 [AUTO-TRANSLATED:4249fc53] + //This task no longer recurs + return (uint64_t) 0; + } catch (std::exception &ex) { + ErrorL << "Exception occurred when do timer task: " << ex.what(); + return (uint64_t) (1000 * second); + } + }); +} + +Timer::~Timer() { + auto tag = _tag.lock(); + if (tag) { + tag->cancel(); + } +} + +} // namespace toolkit diff --git a/3rdpart/ZLToolKit/src/Poller/Timer.h b/3rdpart/ZLToolKit/src/Poller/Timer.h new file mode 100644 index 0000000..222da86 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Poller/Timer.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef Timer_h +#define Timer_h + +#include +#include "EventPoller.h" + +namespace toolkit { + +class Timer { +public: + using Ptr = std::shared_ptr; + + /** + * 构造定时器 + * @param second 定时器重复秒数 + * @param cb 定时器任务,返回true表示重复下次任务,否则不重复,如果任务中抛异常,则默认重复下次任务 + * @param poller EventPoller对象,可以为nullptr + * Constructs a timer + * @param second Timer repeat interval in seconds + * @param cb Timer task, returns true to repeat the next task, otherwise does not repeat. If an exception is thrown in the task, it defaults to repeating the next task + * @param poller EventPoller object, can be nullptr + + * [AUTO-TRANSLATED:7dc94698] + */ + Timer(float second, const std::function &cb, const EventPoller::Ptr &poller); + ~Timer(); + +private: + std::weak_ptr _tag; + //定时器保持EventPoller的强引用 [AUTO-TRANSLATED:d171cd2f] + //Timer keeps a strong reference to EventPoller + EventPoller::Ptr _poller; +}; + +} // namespace toolkit +#endif /* Timer_h */ diff --git a/3rdpart/ZLToolKit/src/README.md b/3rdpart/ZLToolKit/src/README.md new file mode 100644 index 0000000..f961818 --- /dev/null +++ b/3rdpart/ZLToolKit/src/README.md @@ -0,0 +1,64 @@ +源代码放置在`src`文件夹下,里面有若干模块: + +``` +src +| +|-- NetWork # 网络模块 +| |-- Socket.cpp # 套接字抽象封装,包含了TCP服务器/客户端,UDP套接字 +| |-- Socket.h +| |-- sockutil.cpp # 系统网络相关API的统一封装 +| |-- sockutil.h +| |-- TcpClient.cpp # TCP客户端封装,派生该类可以很容易实现客户端程序 +| |-- TcpClient.h +| |-- TcpServer.h # TCP服务器模板类,可以很容易就实现一个高性能私有协议服务器 +| |-- Session.h # TCP/UDP服务私有协议实现会话基类,用于处理TCP/UDP长连接数据及响应 +| +|-- Poller # 主线程事件轮询模块 +| |-- EventPoller.cpp # 主线程,所有网络事件由此线程轮询并触发 +| |-- EventPoller.h +| |-- Pipe.cpp # 管道的对象封装 +| |-- Pipe.h +| |-- PipeWrap.cpp # 管道的包装,windows下由socket模拟 +| |-- SelectWrap.cpp # select 模型的简单包装 +| |-- SelectWrap.h +| |-- Timer.cpp # 在主线程触发的定时器 +| |-- Timer.h +| +|-- Thread # 线程模块 +| |-- AsyncTaskThread.cpp # 后台异步任务线程,可以提交一个可定时重复的任务后台执行 +| |-- AsyncTaskThread.h +| |-- rwmutex.h # 读写锁,实验性质的 +| |-- semaphore.h # 信号量,由条件变量实现 +| |-- spin_mutex.h # 自旋锁,在低延时临界区适用,单核/低性能设备慎用 +| |-- TaskQueue.h # functional的任务列队 +| |-- threadgroup.h # 线程组,移植自boost +| |-- ThreadPool.h # 线程池,可以输入functional任务至后台线程执行 +| |-- WorkThreadPool.cpp # 获取一个可用的线程池(可以加入线程负载均衡分配算法) +| |-- WorkThreadPool.h +| +|-- Util # 工具模块 + |-- File.cpp # 文件/目录操作模块 + |-- File.h + |-- function_traits.h # 函数、lambda转functional + |-- logger.h # 日志模块 + |-- MD5.cpp # md5加密模块 + |-- MD5.h + |-- mini.h # ini配置文件读写模块,支持unix/windows格式的回车符 + |-- NoticeCenter.h # 消息广播器,可以广播传递任意个数任意类型参数 + |-- onceToken.h # 使用RAII模式实现,可以在对象构造和析构时执行一段代码 + |-- ResourcePool.h # 基于智能指针实现的一个循环池,不需要手动回收对象 + |-- RingBuffer.h # 环形缓冲,可以自适应大小,适用于GOP缓存等 + |-- SqlConnection.cpp # mysql客户端 + |-- SqlConnection.h + |-- SqlPool.h # mysql连接池,以及简单易用的sql语句生成工具 + |-- SSLBox.cpp # openssl的黑盒封装,屏蔽了ssl握手细节,支持多线程 + |-- SSLBox.h + |-- TimeTicker.h # 计时器,可以用于统计函数执行时间 + |-- util.cpp # 其他一些工具代码,适配了多种系统 + |-- util.h + |-- uv_errno.cpp # 提取自libuv的错误代码系统,主要是为了兼容windows + |-- uv_errno.h + +``` + + diff --git a/3rdpart/ZLToolKit/src/Thread/TaskExecutor.cpp b/3rdpart/ZLToolKit/src/Thread/TaskExecutor.cpp new file mode 100644 index 0000000..c7082b1 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Thread/TaskExecutor.cpp @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include +#include "TaskExecutor.h" +#include "Poller/EventPoller.h" +#include "Util/onceToken.h" +#include "Util/TimeTicker.h" + +using namespace std; + +namespace toolkit { + +ThreadLoadCounter::ThreadLoadCounter(uint64_t max_size, uint64_t max_usec) { + _last_sleep_time = _last_wake_time = getCurrentMicrosecond(); + _max_size = max_size; + _max_usec = max_usec; +} + +void ThreadLoadCounter::startSleep() { + lock_guard lck(_mtx); + _sleeping = true; + auto current_time = getCurrentMicrosecond(); + auto run_time = current_time - _last_wake_time; + _last_sleep_time = current_time; + _time_list.emplace_back(run_time, false); + if (_time_list.size() > _max_size) { + _time_list.pop_front(); + } +} + +void ThreadLoadCounter::sleepWakeUp() { + lock_guard lck(_mtx); + _sleeping = false; + auto current_time = getCurrentMicrosecond(); + auto sleep_time = current_time - _last_sleep_time; + _last_wake_time = current_time; + _time_list.emplace_back(sleep_time, true); + if (_time_list.size() > _max_size) { + _time_list.pop_front(); + } +} + +int ThreadLoadCounter::load() { + lock_guard lck(_mtx); + uint64_t totalSleepTime = 0; + uint64_t totalRunTime = 0; + _time_list.for_each([&](const TimeRecord &rcd) { + if (rcd._sleep) { + totalSleepTime += rcd._time; + } else { + totalRunTime += rcd._time; + } + }); + + if (_sleeping) { + totalSleepTime += (getCurrentMicrosecond() - _last_sleep_time); + } else { + totalRunTime += (getCurrentMicrosecond() - _last_wake_time); + } + + uint64_t totalTime = totalRunTime + totalSleepTime; + while ((_time_list.size() != 0) && (totalTime > _max_usec || _time_list.size() > _max_size)) { + TimeRecord &rcd = _time_list.front(); + if (rcd._sleep) { + totalSleepTime -= rcd._time; + } else { + totalRunTime -= rcd._time; + } + totalTime -= rcd._time; + _time_list.pop_front(); + } + if (totalTime == 0) { + return 0; + } + return (int) (totalRunTime * 100 / totalTime); +} + +//////////////////////////////////////////////////////////////////////////// + +Task::Ptr TaskExecutorInterface::async_first(TaskIn task, bool may_sync) { + return async(std::move(task), may_sync); +} + +void TaskExecutorInterface::sync(const TaskIn &task) { + semaphore sem; + auto ret = async([&]() { + onceToken token(nullptr, [&]() { + //通过RAII原理防止抛异常导致不执行这句代码 [AUTO-TRANSLATED:206bd80e] + //Prevent this code from not being executed due to an exception being thrown through RAII principle + sem.post(); + }); + task(); + }); + if (ret && *ret) { + sem.wait(); + } +} + +void TaskExecutorInterface::sync_first(const TaskIn &task) { + semaphore sem; + auto ret = async_first([&]() { + onceToken token(nullptr, [&]() { + //通过RAII原理防止抛异常导致不执行这句代码 [AUTO-TRANSLATED:206bd80e] + //Prevent this code from not being executed due to an exception being thrown through RAII principle + sem.post(); + }); + task(); + }); + if (ret && *ret) { + sem.wait(); + } +} + +////////////////////////////////////////////////////////////////// + +TaskExecutor::TaskExecutor(uint64_t max_size, uint64_t max_usec) : ThreadLoadCounter(max_size, max_usec) {} + +////////////////////////////////////////////////////////////////// + +TaskExecutor::Ptr TaskExecutorGetterImp::getExecutor() { + auto thread_pos = _thread_pos; + if (thread_pos >= _threads.size()) { + thread_pos = 0; + } + + TaskExecutor::Ptr executor_min_load = _threads[thread_pos]; + auto min_load = executor_min_load->load(); + + for (size_t i = 0; i < _threads.size(); ++i) { + ++thread_pos; + if (thread_pos >= _threads.size()) { + thread_pos = 0; + } + + auto th = _threads[thread_pos]; + auto load = th->load(); + + if (load < min_load) { + min_load = load; + executor_min_load = th; + } + if (min_load == 0) { + break; + } + } + _thread_pos = thread_pos; + return executor_min_load; +} + +vector TaskExecutorGetterImp::getExecutorLoad() { + vector vec(_threads.size()); + int i = 0; + for (auto &executor : _threads) { + vec[i++] = executor->load(); + } + return vec; +} + +void TaskExecutorGetterImp::getExecutorDelay(const function &)> &callback) { + std::shared_ptr > delay_vec = std::make_shared>(_threads.size()); + shared_ptr finished(nullptr, [callback, delay_vec](void *) { + //此析构回调触发时,说明已执行完毕所有async任务 [AUTO-TRANSLATED:8adf8212] + //When this destructor callback is triggered, it means all async tasks have been executed + callback((*delay_vec)); + }); + int index = 0; + for (auto &th : _threads) { + std::shared_ptr delay_ticker = std::make_shared(); + th->async([finished, delay_vec, index, delay_ticker]() { + (*delay_vec)[index] = (int) delay_ticker->elapsedTime(); + }, false); + ++index; + } +} + +using onGetExecutor = std::function; +class onGetExecutorCB { +public: + onGetExecutorCB(onGetExecutor cb): _cb(std::move(cb)) {} + + void operator()(const TaskExecutor::Ptr &exe) { + bool expected = false; + if (_done.compare_exchange_strong(expected, true)) { + _cb(exe); + _cb = nullptr; + } + } + +private: + std::atomic _done { false }; + std::function _cb; +}; + +void TaskExecutorGetterImp::getExecutor(const onGetExecutor &cb) { + auto callback = std::make_shared(cb); + auto thread_pos = _thread_pos; + if (thread_pos >= _threads.size()) { + thread_pos = 0; + } + for (size_t i = 0; i < _threads.size(); ++i) { + ++thread_pos; + if (thread_pos >= _threads.size()) { + thread_pos = 0; + } + auto &th = _threads[thread_pos]; + th->async([th, callback]() mutable { (*callback)(th); }, false); + } + _thread_pos = thread_pos; +} + +void TaskExecutorGetterImp::for_each(const function &cb) { + for (auto &th : _threads) { + cb(th); + } +} + +size_t TaskExecutorGetterImp::getExecutorSize() const { + return _threads.size(); +} + +size_t TaskExecutorGetterImp::addPoller(const string &name, size_t size, int priority, bool register_thread, bool enable_cpu_affinity) { + auto cpus = thread::hardware_concurrency(); + size = size > 0 ? size : cpus; + for (size_t i = 0; i < size; ++i) { + auto full_name = name + " " + to_string(i); + auto cpu_index = i % cpus; + EventPoller::Ptr poller(new EventPoller(full_name)); + poller->runLoop(false, register_thread); + poller->async([cpu_index, full_name, priority, enable_cpu_affinity]() { + // 设置线程优先级 [AUTO-TRANSLATED:2966f860] + //Set thread priority + ThreadPool::setPriority((ThreadPool::Priority)priority); + // 设置线程名 [AUTO-TRANSLATED:f5eb4704] + //Set thread name + setThreadName(full_name.data()); + // 设置cpu亲和性 [AUTO-TRANSLATED:ba213aed] + //Set CPU affinity + if (enable_cpu_affinity) { + setThreadAffinity(cpu_index); + } + }); + _threads.emplace_back(std::move(poller)); + } + return size; +} + +}//toolkit diff --git a/3rdpart/ZLToolKit/src/Thread/TaskExecutor.h b/3rdpart/ZLToolKit/src/Thread/TaskExecutor.h new file mode 100644 index 0000000..e12236f --- /dev/null +++ b/3rdpart/ZLToolKit/src/Thread/TaskExecutor.h @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLTOOLKIT_TASKEXECUTOR_H +#define ZLTOOLKIT_TASKEXECUTOR_H + +#include +#include +#include +#include "Util/List.h" +#include "Util/util.h" + +namespace toolkit { + +/** +* cpu负载计算器 + * CPU Load Calculator + + * [AUTO-TRANSLATED:46dad663] +*/ +class ThreadLoadCounter { +public: + /** + * 构造函数 + * @param max_size 统计样本数量 + * @param max_usec 统计时间窗口,亦即最近{max_usec}的cpu负载率 + * Constructor + * @param max_size Number of statistical samples + * @param max_usec Statistical time window, i.e., the CPU load rate for the most recent {max_usec} + + * [AUTO-TRANSLATED:718cb173] + */ + ThreadLoadCounter(uint64_t max_size, uint64_t max_usec); + ~ThreadLoadCounter() = default; + + /** + * 线程进入休眠 + * Thread enters sleep + + * [AUTO-TRANSLATED:d831fad1] + */ + void startSleep(); + + /** + * 休眠唤醒,结束休眠 + * Wake up from sleep, end sleep + + * [AUTO-TRANSLATED:361831f8] + */ + void sleepWakeUp(); + + /** + * 返回当前线程cpu使用率,范围为 0 ~ 100 + * @return 当前线程cpu使用率 + * Returns the current thread's CPU usage rate, ranging from 0 to 100 + * @return Current thread's CPU usage rate + + * [AUTO-TRANSLATED:c9953342] + */ + int load(); + +private: + struct TimeRecord { + TimeRecord(uint64_t tm, bool slp) { + _time = tm; + _sleep = slp; + } + + bool _sleep; + uint64_t _time; + }; + +private: + bool _sleeping = true; + uint64_t _last_sleep_time; + uint64_t _last_wake_time; + uint64_t _max_size; + uint64_t _max_usec; + std::mutex _mtx; + List _time_list; +}; + +class TaskCancelable : public noncopyable { +public: + TaskCancelable() = default; + virtual ~TaskCancelable() = default; + virtual void cancel() = 0; +}; + +template +class TaskCancelableImp; + +template +class TaskCancelableImp : public TaskCancelable { +public: + using Ptr = std::shared_ptr; + using func_type = std::function; + + ~TaskCancelableImp() = default; + + template + TaskCancelableImp(FUNC &&task) { + _strongTask = std::make_shared(std::forward(task)); + _weakTask = _strongTask; + } + + void cancel() override { + _strongTask = nullptr; + } + + operator bool() { + return _strongTask && *_strongTask; + } + + void operator=(std::nullptr_t) { + _strongTask = nullptr; + } + + R operator()(ArgTypes ...args) const { + auto strongTask = _weakTask.lock(); + if (strongTask && *strongTask) { + return (*strongTask)(std::forward(args)...); + } + return defaultValue(); + } + + template + static typename std::enable_if::value, void>::type + defaultValue() {} + + template + static typename std::enable_if::value, T>::type + defaultValue() { + return nullptr; + } + + template + static typename std::enable_if::value, T>::type + defaultValue() { + return 0; + } + +protected: + std::weak_ptr _weakTask; + std::shared_ptr _strongTask; +}; + +using TaskIn = std::function; +using Task = TaskCancelableImp; + +class TaskExecutorInterface { +public: + TaskExecutorInterface() = default; + virtual ~TaskExecutorInterface() = default; + + /** + * 异步执行任务 + * @param task 任务 + * @param may_sync 是否允许同步执行该任务 + * @return 任务是否添加成功 + * Asynchronously execute a task + * @param task Task + * @param may_sync Whether to allow synchronous execution of the task + * @return Whether the task was added successfully + + * [AUTO-TRANSLATED:271d48a2] + */ + virtual Task::Ptr async(TaskIn task, bool may_sync = true) = 0; + + /** + * 最高优先级方式异步执行任务 + * @param task 任务 + * @param may_sync 是否允许同步执行该任务 + * @return 任务是否添加成功 + * Asynchronously execute a task with the highest priority + * @param task Task + * @param may_sync Whether to allow synchronous execution of the task + * @return Whether the task was added successfully + + * [AUTO-TRANSLATED:d52ce80b] + */ + virtual Task::Ptr async_first(TaskIn task, bool may_sync = true); + + /** + * 同步执行任务 + * @param task + * @return + * Synchronously execute a task + * @param task + * @return + + * [AUTO-TRANSLATED:24854b4a] + */ + void sync(const TaskIn &task); + + /** + * 最高优先级方式同步执行任务 + * @param task + * @return + * Synchronously execute a task with the highest priority + * @param task + * @return + + * [AUTO-TRANSLATED:3d15452d] + */ + void sync_first(const TaskIn &task); +}; + +/** +* 任务执行器 + * Task Executor + + * [AUTO-TRANSLATED:630c364f] +*/ +class TaskExecutor : public ThreadLoadCounter, public TaskExecutorInterface { +public: + using Ptr = std::shared_ptr; + + /** + * 构造函数 + * @param max_size cpu负载统计样本数 + * @param max_usec cpu负载统计时间窗口大小 + */ + TaskExecutor(uint64_t max_size = 32, uint64_t max_usec = 2 * 1000 * 1000); + ~TaskExecutor() = default; +}; + +class TaskExecutorGetter { +public: + using Ptr = std::shared_ptr; + + virtual ~TaskExecutorGetter() = default; + + /** + * 获取任务执行器 + * @return 任务执行器 + */ + virtual TaskExecutor::Ptr getExecutor() = 0; + + /** + * 异步获取延时最低的任务执行器 + * @param cb 回调 + */ + virtual void getExecutor(const std::function &cb) = 0; + + /** + * 获取执行器个数 + */ + virtual size_t getExecutorSize() const = 0; +}; + +class TaskExecutorGetterImp : public TaskExecutorGetter { +public: + TaskExecutorGetterImp() = default; + ~TaskExecutorGetterImp() = default; + + /** + * 根据线程负载情况,获取最空闲的任务执行器 + * @return 任务执行器 + */ + TaskExecutor::Ptr getExecutor() override; + + /** + * 异步获取延时最低的任务执行器 + * @param cb 回调 + */ + void getExecutor(const std::function &cb) override; + + /** + * 获取所有线程的负载率 + * @return 所有线程的负载率 + */ + std::vector getExecutorLoad(); + + /** + * 获取所有线程任务执行延时,单位毫秒 + * 通过此函数也可以大概知道线程负载情况 + * @return + */ + void getExecutorDelay(const std::function &)> &callback); + + /** + * 遍历所有线程 + */ + void for_each(const std::function &cb); + + /** + * 获取线程数 + */ + size_t getExecutorSize() const override; + +protected: + size_t addPoller(const std::string &name, size_t size, int priority, bool register_thread, bool enable_cpu_affinity = true); + +protected: + size_t _thread_pos = 0; + std::vector _threads; +}; + +}//toolkit +#endif //ZLTOOLKIT_TASKEXECUTOR_H diff --git a/3rdpart/ZLToolKit/src/Thread/TaskQueue.h b/3rdpart/ZLToolKit/src/Thread/TaskQueue.h new file mode 100644 index 0000000..edd6e56 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Thread/TaskQueue.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef TASKQUEUE_H_ +#define TASKQUEUE_H_ + +#include +#include "Util/List.h" +#include "semaphore.h" + +namespace toolkit { + +//实现了一个基于函数对象的任务列队,该列队是线程安全的,任务列队任务数由信号量控制 [AUTO-TRANSLATED:67e02e93] +//Implemented a task queue based on function objects, which is thread-safe, and the number of tasks in the task queue is controlled by a semaphore +template +class TaskQueue { +public: + //打入任务至列队 [AUTO-TRANSLATED:d08b5817] + //Put a task into the queue + template + void push_task(C &&task_func) { + { + std::lock_guard lock(_mutex); + _queue.emplace_back(std::forward(task_func)); + } + _sem.post(); + } + + template + void push_task_first(C &&task_func) { + { + std::lock_guard lock(_mutex); + _queue.emplace_front(std::forward(task_func)); + } + _sem.post(); + } + + //清空任务列队 [AUTO-TRANSLATED:dbcd7fe9] + //Clear the task queue + void push_exit(size_t n) { + _sem.post(n); + } + + //从列队获取一个任务,由执行线程执行 [AUTO-TRANSLATED:4a1143ae] + //Get a task from the queue and execute it by the executing thread + bool get_task(T &tsk) { + _sem.wait(); + std::lock_guard lock(_mutex); + if (_queue.empty()) { + return false; + } + tsk = std::move(_queue.front()); + _queue.pop_front(); + return true; + } + + size_t size() const { + std::lock_guard lock(_mutex); + return _queue.size(); + } + +private: + List _queue; + mutable std::mutex _mutex; + semaphore _sem; +}; + +} /* namespace toolkit */ +#endif /* TASKQUEUE_H_ */ diff --git a/3rdpart/ZLToolKit/src/Thread/ThreadPool.h b/3rdpart/ZLToolKit/src/Thread/ThreadPool.h new file mode 100644 index 0000000..6c1f95c --- /dev/null +++ b/3rdpart/ZLToolKit/src/Thread/ThreadPool.h @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef THREADPOOL_H_ +#define THREADPOOL_H_ + +#include "threadgroup.h" +#include "TaskQueue.h" +#include "TaskExecutor.h" +#include "Util/util.h" +#include "Util/logger.h" + +namespace toolkit { + +class ThreadPool : public TaskExecutor { +public: + enum Priority { + PRIORITY_LOWEST = 0, + PRIORITY_LOW, + PRIORITY_NORMAL, + PRIORITY_HIGH, + PRIORITY_HIGHEST + }; + + ThreadPool(int num = 1, Priority priority = PRIORITY_HIGHEST, bool auto_run = true, bool set_affinity = true, + const std::string &pool_name = "thread pool") { + _thread_num = num; + _on_setup = [pool_name, priority, set_affinity, num](int index) { + std::string name = num > 1 ? pool_name + ' ' + std::to_string(index) : pool_name; + setPriority(priority); + setThreadName(name.data()); + if (set_affinity) { + setThreadAffinity(index % std::thread::hardware_concurrency()); + } + }; + _logger = Logger::Instance().shared_from_this(); + if (auto_run) { + start(); + } + } + + ~ThreadPool() { + shutdown(); + wait(); + } + + //把任务打入线程池并异步执行 [AUTO-TRANSLATED:651c8d5a] + //Put the task into the thread pool and execute it asynchronously + Task::Ptr async(TaskIn task, bool may_sync = true) override { + if (may_sync && _thread_group.is_this_thread_in()) { + task(); + return nullptr; + } + auto ret = std::make_shared(std::move(task)); + _queue.push_task([ret](size_t) { + (*ret)(); + }); + return ret; + } + + Task::Ptr async_first(TaskIn task, bool may_sync = true) override { + if (may_sync && _thread_group.is_this_thread_in()) { + task(); + return nullptr; + } + + auto ret = std::make_shared(std::move(task)); + _queue.push_task_first([ret](size_t) { + (*ret)(); + }); + return ret; + } + + void async2(std::function task, bool may_sync = true, bool first = false) { + if (may_sync && _thread_group.is_this_thread_in()) { + task(0); + return; + } + if (first) { + _queue.push_task_first(std::move(task)); + } else { + _queue.push_task(std::move(task)); + } + } + + size_t size() { + return _queue.size(); + } + + static bool setPriority(Priority priority = PRIORITY_NORMAL, std::thread::native_handle_type threadId = 0) { + // set priority +#if defined(_WIN32) + static int Priorities[] = { THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST }; + if (priority != PRIORITY_NORMAL && SetThreadPriority(GetCurrentThread(), Priorities[priority]) == 0) { + return false; + } + return true; +#else + static int Min = sched_get_priority_min(SCHED_FIFO); + if (Min == -1) { + return false; + } + static int Max = sched_get_priority_max(SCHED_FIFO); + if (Max == -1) { + return false; + } + static int Priorities[] = {Min, Min + (Max - Min) / 4, Min + (Max - Min) / 2, Min + (Max - Min) * 3 / 4, Max}; + + if (threadId == 0) { + threadId = pthread_self(); + } + struct sched_param params; + params.sched_priority = Priorities[priority]; + return pthread_setschedparam(threadId, SCHED_FIFO, ¶ms) == 0; +#endif + } + + void start() { + if (_thread_num <= 0) { + return; + } + size_t total = _thread_num - _thread_group.size(); + for (size_t i = 0; i < total; ++i) { + _thread_group.create_thread([this, i]() {run(i);}); + } + } + +private: + void run(size_t index) { + _on_setup(index); + std::function task; + while (true) { + startSleep(); + if (!_queue.get_task(task)) { + // 空任务,退出线程 [AUTO-TRANSLATED:583e2f11] + // Empty task, exit the thread + break; + } + sleepWakeUp(); + try { + task(index); + task = nullptr; + } catch (std::exception &ex) { + ErrorL << "ThreadPool catch a exception: " << ex.what(); + } + } + } + + void wait() { + _thread_group.join_all(); + } + + void shutdown() { + _queue.push_exit(_thread_num); + } + +private: + size_t _thread_num; + Logger::Ptr _logger; + thread_group _thread_group; + TaskQueue> _queue; + std::function _on_setup; +}; + +} /* namespace toolkit */ +#endif /* THREADPOOL_H_ */ diff --git a/3rdpart/ZLToolKit/src/Thread/WorkThreadPool.cpp b/3rdpart/ZLToolKit/src/Thread/WorkThreadPool.cpp new file mode 100644 index 0000000..a632025 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Thread/WorkThreadPool.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "WorkThreadPool.h" + +namespace toolkit { + +static size_t s_pool_size = 0; +static bool s_enable_cpu_affinity = true; + +INSTANCE_IMP(WorkThreadPool) + +EventPoller::Ptr WorkThreadPool::getFirstPoller() { + return std::static_pointer_cast(_threads.front()); +} + +EventPoller::Ptr WorkThreadPool::getPoller() { + return std::static_pointer_cast(getExecutor()); +} + +WorkThreadPool::WorkThreadPool() { + //最低优先级 [AUTO-TRANSLATED:cd1f0dbc] + //Lowest priority + addPoller("work poller", s_pool_size, ThreadPool::PRIORITY_LOWEST, false, s_enable_cpu_affinity); +} + +void WorkThreadPool::setPoolSize(size_t size) { + s_pool_size = size; +} + +void WorkThreadPool::enableCpuAffinity(bool enable) { + s_enable_cpu_affinity = enable; +} + +} /* namespace toolkit */ + diff --git a/3rdpart/ZLToolKit/src/Thread/WorkThreadPool.h b/3rdpart/ZLToolKit/src/Thread/WorkThreadPool.h new file mode 100644 index 0000000..0424723 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Thread/WorkThreadPool.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef UTIL_WORKTHREADPOOL_H_ +#define UTIL_WORKTHREADPOOL_H_ + +#include +#include "Poller/EventPoller.h" + +namespace toolkit { + +class WorkThreadPool : public std::enable_shared_from_this, public TaskExecutorGetterImp { +public: + using Ptr = std::shared_ptr; + + ~WorkThreadPool() override = default; + + /** + * 获取单例 + * Get the singleton instance + + * [AUTO-TRANSLATED:c8852589] + */ + static WorkThreadPool &Instance(); + + /** + * 设置EventPoller个数,在WorkThreadPool单例创建前有效 + * 在不调用此方法的情况下,默认创建thread::hardware_concurrency()个EventPoller实例 + * @param size EventPoller个数,如果为0则为thread::hardware_concurrency() + * Set the number of EventPoller instances, effective before the WorkThreadPool singleton is created + * If this method is not called, the default is to create thread::hardware_concurrency() EventPoller instances + * @param size The number of EventPoller instances, if 0 then use thread::hardware_concurrency() + + * [AUTO-TRANSLATED:bb236d87] + */ + static void setPoolSize(size_t size = 0); + + /** + * 内部创建线程是否设置cpu亲和性,默认设置cpu亲和性 + * Whether to set CPU affinity when creating internal threads, CPU affinity is set by default + + * [AUTO-TRANSLATED:46941c9f] + */ + static void enableCpuAffinity(bool enable); + + /** + * 获取第一个实例 + * @return + * Get the first instance + * @return + + * [AUTO-TRANSLATED:a76aad3b] + */ + EventPoller::Ptr getFirstPoller(); + + /** + * 根据负载情况获取轻负载的实例 + * 如果优先返回当前线程,那么会返回当前线程 + * 返回当前线程的目的是为了提高线程安全性 + * @return + * Get a lightly loaded instance based on the load situation + * If priority is given to the current thread, it will return the current thread + * The purpose of returning the current thread is to improve thread safety + * @return + + * [AUTO-TRANSLATED:1282b772] + */ + EventPoller::Ptr getPoller(); + +protected: + WorkThreadPool(); +}; + +} /* namespace toolkit */ +#endif /* UTIL_WORKTHREADPOOL_H_ */ diff --git a/3rdpart/ZLToolKit/src/Thread/semaphore.h b/3rdpart/ZLToolKit/src/Thread/semaphore.h new file mode 100644 index 0000000..a2e5e96 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Thread/semaphore.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SEMAPHORE_H_ +#define SEMAPHORE_H_ + +#include +#include +#include + +namespace toolkit { + +class semaphore { +public: + explicit semaphore(size_t initial = 0) { +#if defined(HAVE_SEM) + sem_init(&_sem, 0, initial); +#else + _count = initial; +#endif + } + + ~semaphore() { +#if defined(HAVE_SEM) + sem_destroy(&_sem); +#endif + } + + void post(size_t n = 1) { +#if defined(HAVE_SEM) + while (n--) { + sem_post(&_sem); + } +#else + std::unique_lock lock(_mutex); + _count += n; + if (n == 1) { + _condition.notify_one(); + } else { + _condition.notify_all(); + } +#endif + } + + void wait() { +#if defined(HAVE_SEM) + sem_wait(&_sem); +#else + std::unique_lock lock(_mutex); + while (_count == 0) { + _condition.wait(lock); + } + --_count; +#endif + } + + bool wait(unsigned int timeout_ms) { +#if defined(HAVE_SEM) + struct timespec ts; + // 获取当前时间 + if (clock_gettime(CLOCK_REALTIME, &ts) == -1) { + perror("clock_gettime failed"); + return -1; + } + // 添加超时时间到当前时间以得到绝对时间 + ts.tv_sec += timeout_ms / 1000; + ts.tv_nsec += (timeout_ms % 1000) * 1000000; + if (ts.tv_nsec >= 1000000000) { + ts.tv_sec += ts.tv_nsec / 1000000000; + ts.tv_nsec = ts.tv_nsec % 1000000000; + } + sem_timedwait(&_sem, &ts); + + struct timespec ts; + if (clock_gettime(CLOCK_REALTIME, &ts) == -1) { + return false; + } + + ts.tv_sec += timeout_ms / 1000; + ts.tv_nsec += (timeout_ms % 1000) * 1000000; + if (ts.tv_nsec >= 1000000000) { + ts.tv_sec += ts.tv_nsec / 1000000000; + ts.tv_nsec = ts.tv_nsec % 1000000000; + } + + int result = sem_timedwait(&_sem, &ts); + return result == 0; // 成功返回true,超时/失败返回false +#else + + std::unique_lock lock(_mutex); + auto now = std::chrono::system_clock::now(); + auto waitTime = now + std::chrono::milliseconds(timeout_ms); + + bool success = _condition.wait_until(lock, waitTime, [this] { return _count > 0; }); + + if (success) { + --_count; + } + return success; +#endif + } + +private: +#if defined(HAVE_SEM) + sem_t _sem; +#else + size_t _count; + std::recursive_mutex _mutex; + std::condition_variable_any _condition; +#endif +}; + +} /* namespace toolkit */ +#endif /* SEMAPHORE_H_ */ diff --git a/3rdpart/ZLToolKit/src/Thread/threadgroup.h b/3rdpart/ZLToolKit/src/Thread/threadgroup.h new file mode 100644 index 0000000..21d689e --- /dev/null +++ b/3rdpart/ZLToolKit/src/Thread/threadgroup.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef THREADGROUP_H_ +#define THREADGROUP_H_ + +#include +#include +#include + +namespace toolkit { + +class thread_group { +private: + thread_group(thread_group const &); + thread_group &operator=(thread_group const &); + +public: + thread_group() {} + + ~thread_group() { + _threads.clear(); + } + + bool is_this_thread_in() { + auto thread_id = std::this_thread::get_id(); + if (_thread_id == thread_id) { + return true; + } + return _threads.find(thread_id) != _threads.end(); + } + + bool is_thread_in(std::thread *thrd) { + if (!thrd) { + return false; + } + auto it = _threads.find(thrd->get_id()); + return it != _threads.end(); + } + + template + std::thread *create_thread(F &&threadfunc) { + auto thread_new = std::make_shared(std::forward(threadfunc)); + _thread_id = thread_new->get_id(); + _threads[_thread_id] = thread_new; + return thread_new.get(); + } + + void remove_thread(std::thread *thrd) { + auto it = _threads.find(thrd->get_id()); + if (it != _threads.end()) { + _threads.erase(it); + } + } + + void join_all() { + if (is_this_thread_in()) { + throw std::runtime_error("Trying joining itself in thread_group"); + } + for (auto &it : _threads) { + if (it.second->joinable()) { + it.second->join(); //等待线程主动退出 + } + } + _threads.clear(); + } + + size_t size() { + return _threads.size(); + } + +private: + std::thread::id _thread_id; + std::unordered_map> _threads; +}; + +} /* namespace toolkit */ +#endif /* THREADGROUP_H_ */ diff --git a/3rdpart/ZLToolKit/src/Util/Byte.hpp b/3rdpart/ZLToolKit/src/Util/Byte.hpp new file mode 100644 index 0000000..8de5436 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Util/Byte.hpp @@ -0,0 +1,175 @@ +/** +ISC License + +Copyright © 2015, Iñaki Baz Castillo + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef SRC_UTIL_BYTE_H_ +#define SRC_UTIL_BYTE_H_ + +#if defined(_WIN32) +#include +#include +#pragma comment (lib, "Ws2_32.lib") +#else +#include +#endif // defined(_WIN32) + +#include // PRIu64, etc +#include // size_t +#include // uint8_t, etc + +namespace toolkit { + +class Byte { +public: + /** + * Getters below get value in Host Byte Order. + * Setters below set value in Network Byte Order. + */ + static uint8_t Get1Byte(const uint8_t *data, size_t i); + static uint16_t Get2Bytes(const uint8_t *data, size_t i); + static uint32_t Get3Bytes(const uint8_t *data, size_t i); + static uint32_t Get4Bytes(const uint8_t *data, size_t i); + static uint64_t Get8Bytes(const uint8_t *data, size_t i); + static void Set1Byte(uint8_t *data, size_t i, uint8_t value); + static void Set2Bytes(uint8_t *data, size_t i, uint16_t value); + static void Set3Bytes(uint8_t *data, size_t i, uint32_t value); + static void Set4Bytes(uint8_t *data, size_t i, uint32_t value); + static void Set8Bytes(uint8_t *data, size_t i, uint64_t value); + static uint16_t PadTo4Bytes(uint16_t size); + static uint32_t PadTo4Bytes(uint32_t size); + + static uint16_t Get2BytesLE(const uint8_t *data, size_t i); + static uint32_t Get3BytesLE(const uint8_t *data, size_t i); + static uint32_t Get4BytesLE(const uint8_t *data, size_t i); + static uint64_t Get8BytesLE(const uint8_t *data, size_t i); + static void Set2BytesLE(uint8_t *data, size_t i, uint16_t value); + static void Set3BytesLE(uint8_t *data, size_t i, uint32_t value); + static void Set4BytesLE(uint8_t *data, size_t i, uint32_t value); + static void Set8BytesLE(uint8_t *data, size_t i, uint64_t value); + +}; + +/* Inline static methods. */ + +inline uint8_t Byte::Get1Byte(const uint8_t *data, size_t i) { return data[i]; } + +inline uint16_t Byte::Get2Bytes(const uint8_t *data, size_t i) { + return uint16_t{data[i + 1]} | uint16_t{data[i]} << 8; +} + +inline uint32_t Byte::Get3Bytes(const uint8_t *data, size_t i) { + return uint32_t{data[i + 2]} | uint32_t{data[i + 1]} << 8 | uint32_t{data[i]} << 16; +} + +inline uint32_t Byte::Get4Bytes(const uint8_t *data, size_t i) { + return uint32_t{data[i + 3]} | uint32_t{data[i + 2]} << 8 | uint32_t{data[i + 1]} << 16 | + uint32_t{data[i]} << 24; +} + +inline uint64_t Byte::Get8Bytes(const uint8_t *data, size_t i) { + return uint64_t{Byte::Get4Bytes(data, i)} << 32 | Byte::Get4Bytes(data, i + 4); +} + +inline void Byte::Set1Byte(uint8_t *data, size_t i, uint8_t value) { data[i] = value; } + +inline void Byte::Set2Bytes(uint8_t *data, size_t i, uint16_t value) { + data[i + 1] = static_cast(value); + data[i] = static_cast(value >> 8); +} + +inline void Byte::Set3Bytes(uint8_t *data, size_t i, uint32_t value) { + data[i + 2] = static_cast(value); + data[i + 1] = static_cast(value >> 8); + data[i] = static_cast(value >> 16); +} + +inline void Byte::Set4Bytes(uint8_t *data, size_t i, uint32_t value) { + data[i + 3] = static_cast(value); + data[i + 2] = static_cast(value >> 8); + data[i + 1] = static_cast(value >> 16); + data[i] = static_cast(value >> 24); +} + +inline void Byte::Set8Bytes(uint8_t *data, size_t i, uint64_t value) { + data[i + 7] = static_cast(value); + data[i + 6] = static_cast(value >> 8); + data[i + 5] = static_cast(value >> 16); + data[i + 4] = static_cast(value >> 24); + data[i + 3] = static_cast(value >> 32); + data[i + 2] = static_cast(value >> 40); + data[i + 1] = static_cast(value >> 48); + data[i] = static_cast(value >> 56); +} + + +inline uint16_t Byte::Get2BytesLE(const uint8_t *data, size_t i) { + return uint16_t{data[i]} | uint16_t{data[i + 1]} << 8; +} + +inline uint32_t Byte::Get3BytesLE(const uint8_t *data, size_t i) { + return uint32_t{data[i]} | uint32_t{data[i + 1]} << 8 | uint32_t{data[i + 2]} << 16; +} + +inline uint32_t Byte::Get4BytesLE(const uint8_t *data, size_t i) { + return uint32_t{data[i]} | uint32_t{data[i + 1]} << 8 | uint32_t{data[i + 2]} << 16 | + uint32_t{data[i + 3]} << 24; +} + +inline uint64_t Byte::Get8BytesLE(const uint8_t *data, size_t i) { + return uint64_t{Byte::Get4Bytes(data, i + 4)} << 32 | Byte::Get4Bytes(data, i); +} + +inline void Byte::Set2BytesLE(uint8_t *data, size_t i, uint16_t value) { + data[i] = static_cast(value); + data[i + 1] = static_cast(value >> 8); +} + +inline void Byte::Set3BytesLE(uint8_t *data, size_t i, uint32_t value) { + data[i] = static_cast(value); + data[i + 1] = static_cast(value >> 8); + data[i + 2] = static_cast(value >> 16); +} + +inline void Byte::Set4BytesLE(uint8_t *data, size_t i, uint32_t value) { + data[i] = static_cast(value); + data[i + 1] = static_cast(value >> 8); + data[i + 2] = static_cast(value >> 16); + data[i + 3] = static_cast(value >> 24); +} + +inline void Byte::Set8BytesLE(uint8_t *data, size_t i, uint64_t value) { + data[i] = static_cast(value); + data[i + 1] = static_cast(value >> 8); + data[i + 2] = static_cast(value >> 16); + data[i + 3] = static_cast(value >> 24); + data[i + 4] = static_cast(value >> 32); + data[i + 5] = static_cast(value >> 40); + data[i + 6] = static_cast(value >> 48); + data[i + 7] = static_cast(value >> 56); +} + +inline uint16_t Byte::PadTo4Bytes(uint16_t size) { + // If size is not multiple of 32 bits then pad it. + if (size & 0x03) + return (size & 0xFFFC) + 4; + else + return size; +} + +}// namespace toolkit + +#endif //SRC_UTIL_BYTE_H_ diff --git a/3rdpart/ZLToolKit/src/Util/CMD.cpp b/3rdpart/ZLToolKit/src/Util/CMD.cpp new file mode 100644 index 0000000..3d810a4 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Util/CMD.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "CMD.h" +#include "onceToken.h" + +#if defined(_WIN32) +#include "win32/getopt.h" +#else +#include +#endif // defined(_WIN32) + +using namespace std; + +namespace toolkit { + +//默认注册exit/quit/help/clear命令 [AUTO-TRANSLATED:1411f05e] +//Default registration of exit/quit/help/clear commands +static onceToken s_token([]() { + REGIST_CMD(exit) + REGIST_CMD(quit) + REGIST_CMD(help) + REGIST_CMD(clear) +}); + +CMDRegister &CMDRegister::Instance() { + static CMDRegister instance; + return instance; +} + +void OptionParser::operator()(mINI &all_args, int argc, char *argv[], const std::shared_ptr &stream) { + vector vec_long_opt; + string str_short_opt; + do { + struct option tmp; + for (auto &pr : _map_options) { + auto &opt = pr.second; + //long opt + tmp.name = (char *) opt._long_opt.data(); + tmp.has_arg = opt._type; + tmp.flag = nullptr; + tmp.val = pr.first; + vec_long_opt.emplace_back(tmp); + //short opt + if (!opt._short_opt) { + continue; + } + str_short_opt.push_back(opt._short_opt); + switch (opt._type) { + case Option::ArgRequired: str_short_opt.append(":"); break; + case Option::ArgOptional: str_short_opt.append("::"); break; + default: break; + } + } + tmp.flag = 0; + tmp.name = 0; + tmp.has_arg = 0; + tmp.val = 0; + vec_long_opt.emplace_back(tmp); + } while (0); + + static mutex s_mtx_opt; + lock_guard lck(s_mtx_opt); + + int index; + optind = 0; + opterr = 0; + while ((index = getopt_long(argc, argv, &str_short_opt[0], &vec_long_opt[0], nullptr)) != -1) { + stringstream ss; + ss << " 未识别的选项,输入\"-h\"获取帮助."; + if (index < 0xFF) { + //短参数 [AUTO-TRANSLATED:87b4c1df] + //Short parameters + auto it = _map_char_index.find(index); + if (it == _map_char_index.end()) { + throw std::invalid_argument(ss.str()); + } + index = it->second; + } + + auto it = _map_options.find(index); + if (it == _map_options.end()) { + throw std::invalid_argument(ss.str()); + } + auto &opt = it->second; + auto pr = all_args.emplace(opt._long_opt, optarg ? optarg : ""); + if (!opt(stream, pr.first->second)) { + return; + } + optarg = nullptr; + } + for (auto &pr : _map_options) { + if (pr.second._default_value && all_args.find(pr.second._long_opt) == all_args.end()) { + //有默认值,赋值默认值 [AUTO-TRANSLATED:9a82f49c] + //Has default value, assigns default value + all_args.emplace(pr.second._long_opt, *pr.second._default_value); + } + } + for (auto &pr : _map_options) { + if (pr.second._must_exist) { + if (all_args.find(pr.second._long_opt) == all_args.end()) { + stringstream ss; + ss << " 参数\"" << pr.second._long_opt << "\"必须提供,输入\"-h\"选项获取帮助"; + throw std::invalid_argument(ss.str()); + } + } + } + if (all_args.empty() && _map_options.size() > 1 && !_enable_empty_args) { + _helper(stream, ""); + return; + } + if (_on_completed) { + _on_completed(stream, all_args); + } +} + +}//namespace toolkit \ No newline at end of file diff --git a/3rdpart/ZLToolKit/src/Util/CMD.h b/3rdpart/ZLToolKit/src/Util/CMD.h new file mode 100644 index 0000000..c4e8364 --- /dev/null +++ b/3rdpart/ZLToolKit/src/Util/CMD.h @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_UTIL_CMD_H_ +#define SRC_UTIL_CMD_H_ + +#include +#include +#include +#include +#include +#include +#include +#include "mini.h" + +namespace toolkit{ + +class Option { +public: + using OptionHandler = std::function &stream, const std::string &arg)>; + + enum ArgType { + ArgNone = 0,//no_argument, + ArgRequired = 1,//required_argument, + ArgOptional = 2,//optional_argument + }; + + Option() = default; + + Option(char short_opt, const char *long_opt, enum ArgType type, const char *default_value, bool must_exist, + const char *des, const OptionHandler &cb) { + _short_opt = short_opt; + _long_opt = long_opt; + _type = type; + if (type != ArgNone) { + if (default_value) { + _default_value = std::make_shared(default_value); + } + if (!_default_value && must_exist) { + _must_exist = true; + } + } + _des = des; + _cb = cb; + } + + bool operator()(const std::shared_ptr &stream, const std::string &arg) { + return _cb ? _cb(stream, arg) : true; + } + +private: + friend class OptionParser; + bool _must_exist = false; + char _short_opt; + enum ArgType _type; + std::string _des; + std::string _long_opt; + OptionHandler _cb; + std::shared_ptr _default_value; +}; + +class OptionParser { +public: + using OptionCompleted = std::function &, mINI &)>; + + OptionParser(const OptionCompleted &cb = nullptr, bool enable_empty_args = true) { + _on_completed = cb; + _enable_empty_args = enable_empty_args; + _helper = Option('h', "help", Option::ArgNone, nullptr, false, "打印此信息", + [this](const std::shared_ptr &stream,const std::string &arg)->bool { + static const char *argsType[] = {"无参", "有参", "选参"}; + static const char *mustExist[] = {"选填", "必填"}; + static std::string defaultPrefix = "默认:"; + static std::string defaultNull = "null"; + + std::stringstream printer; + size_t maxLen_longOpt = 0; + auto maxLen_default = defaultNull.size(); + + for (auto &pr : _map_options) { + auto &opt = pr.second; + if (opt._long_opt.size() > maxLen_longOpt) { + maxLen_longOpt = opt._long_opt.size(); + } + if (opt._default_value) { + if (opt._default_value->size() > maxLen_default) { + maxLen_default = opt._default_value->size(); + } + } + } + for (auto &pr : _map_options) { + auto &opt = pr.second; + //打印短参和长参名 + if (opt._short_opt) { + printer << " -" << opt._short_opt << " --" << opt._long_opt; + } else { + printer << " " << " " << " --" << opt._long_opt; + } + for (size_t i = 0; i < maxLen_longOpt - opt._long_opt.size(); ++i) { + printer << " "; + } + //打印是否有参 + printer << " " << argsType[opt._type]; + //打印默认参数 + std::string defaultValue = defaultNull; + if (opt._default_value) { + defaultValue = *opt._default_value; + } + printer << " " << defaultPrefix << defaultValue; + for (size_t i = 0; i < maxLen_default - defaultValue.size(); ++i) { + printer << " "; + } + //打印是否必填参数 + printer << " " << mustExist[opt._must_exist]; + //打印描述 + printer << " " << opt._des << std::endl; + } + throw std::invalid_argument(printer.str()); + }); + (*this) << _helper; + } + + OptionParser &operator<<(Option &&option) { + int index = 0xFF + (int) _map_options.size(); + if (option._short_opt) { + _map_char_index.emplace(option._short_opt, index); + } + _map_options.emplace(index, std::forward