diff --git a/ZLM/.clang-format b/ZLM/.clang-format
new file mode 100644
index 0000000..e7b8a73
--- /dev/null
+++ b/ZLM/.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/ZLM/.gitattributes b/ZLM/.gitattributes
new file mode 100644
index 0000000..b7f1a8b
--- /dev/null
+++ b/ZLM/.gitattributes
@@ -0,0 +1,2 @@
+*.h linguist-language=cpp
+*.c linguist-language=cpp
diff --git a/ZLM/.github/ISSUE_TEMPLATE/bug.md b/ZLM/.github/ISSUE_TEMPLATE/bug.md
new file mode 100644
index 0000000..6232151
--- /dev/null
+++ b/ZLM/.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/ZLM/.github/ISSUE_TEMPLATE/compile.md b/ZLM/.github/ISSUE_TEMPLATE/compile.md
new file mode 100644
index 0000000..611febf
--- /dev/null
+++ b/ZLM/.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/ZLM/.github/ISSUE_TEMPLATE/config.yml b/ZLM/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..9dc6738
--- /dev/null
+++ b/ZLM/.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/ZLM/.github/ISSUE_TEMPLATE/feature.md b/ZLM/.github/ISSUE_TEMPLATE/feature.md
new file mode 100644
index 0000000..348e1ea
--- /dev/null
+++ b/ZLM/.github/ISSUE_TEMPLATE/feature.md
@@ -0,0 +1,14 @@
+---
+name: 新增功能请求
+about: 请求新增某些新功能或新特性,或者对已有功能的改进
+title: "[功能请求] 需求描述(必填)"
+labels: 意见建议
+assignees: ''
+
+---
+
+## 描述该功能的用处,可以提供相关资料描述该功能
+
+## 该功能是否用于改进项目缺陷,如果是,请描述现有缺陷
+
+## 描述你期望实现该功能的方式和最终效果
diff --git a/ZLM/.github/workflows/android.yml b/ZLM/.github/workflows/android.yml
new file mode 100644
index 0000000..2a0cdc8
--- /dev/null
+++ b/ZLM/.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/ZLM/.github/workflows/codeql.yml b/ZLM/.github/workflows/codeql.yml
new file mode 100644
index 0000000..f6bd279
--- /dev/null
+++ b/ZLM/.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/ZLM/.github/workflows/docker.yml b/ZLM/.github/workflows/docker.yml
new file mode 100644
index 0000000..e13fd5f
--- /dev/null
+++ b/ZLM/.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/ZLM/.github/workflows/issue_lint.yml b/ZLM/.github/workflows/issue_lint.yml
new file mode 100644
index 0000000..b4e4e56
--- /dev/null
+++ b/ZLM/.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/ZLM/.github/workflows/linux.yml b/ZLM/.github/workflows/linux.yml
new file mode 100644
index 0000000..70bcd1a
--- /dev/null
+++ b/ZLM/.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/ZLM/.github/workflows/macos.yml b/ZLM/.github/workflows/macos.yml
new file mode 100644
index 0000000..2cd38dd
--- /dev/null
+++ b/ZLM/.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/ZLM/.github/workflows/style.yml b/ZLM/.github/workflows/style.yml
new file mode 100644
index 0000000..dc85e91
--- /dev/null
+++ b/ZLM/.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/ZLM/.github/workflows/windows.yml b/ZLM/.github/workflows/windows.yml
new file mode 100644
index 0000000..8061fa1
--- /dev/null
+++ b/ZLM/.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/ZLM/.gitignore b/ZLM/.gitignore
new file mode 100644
index 0000000..952d632
--- /dev/null
+++ b/ZLM/.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/ZLM/3rdpart/CMakeLists.txt b/ZLM/3rdpart/CMakeLists.txt
new file mode 100644
index 0000000..845984f
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/.clang-format b/ZLM/3rdpart/ZLToolKit/.clang-format
new file mode 100644
index 0000000..7ae36ce
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/.github/workflows/linux.yml b/ZLM/3rdpart/ZLToolKit/.github/workflows/linux.yml
new file mode 100644
index 0000000..cd53d04
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/.github/workflows/macos.yml b/ZLM/3rdpart/ZLToolKit/.github/workflows/macos.yml
new file mode 100644
index 0000000..1feb8ca
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/.github/workflows/style.yml b/ZLM/3rdpart/ZLToolKit/.github/workflows/style.yml
new file mode 100644
index 0000000..361add9
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/.github/workflows/windows.yml b/ZLM/3rdpart/ZLToolKit/.github/workflows/windows.yml
new file mode 100644
index 0000000..517df5c
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/.gitignore b/ZLM/3rdpart/ZLToolKit/.gitignore
new file mode 100644
index 0000000..5bd0d4d
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/.travis.yml b/ZLM/3rdpart/ZLToolKit/.travis.yml
new file mode 100644
index 0000000..d0c77d9
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/AUTHORS b/ZLM/3rdpart/ZLToolKit/AUTHORS
new file mode 100644
index 0000000..d2b518c
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/CMakeLists.txt b/ZLM/3rdpart/ZLToolKit/CMakeLists.txt
new file mode 100644
index 0000000..169cece
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/LICENSE b/ZLM/3rdpart/ZLToolKit/LICENSE
new file mode 100644
index 0000000..9a6eea4
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/README.md b/ZLM/3rdpart/ZLToolKit/README.md
new file mode 100644
index 0000000..3b6a0c8
--- /dev/null
+++ b/ZLM/3rdpart/ZLToolKit/README.md
@@ -0,0 +1,138 @@
+# 一个基于C++11简单易用的轻量级网络编程框架
+
+
+
+
+
+## 项目特点
+- 基于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/ZLM/3rdpart/ZLToolKit/cmake/FindMYSQL.cmake b/ZLM/3rdpart/ZLToolKit/cmake/FindMYSQL.cmake
new file mode 100644
index 0000000..6eebbad
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/src/Network/Buffer.cpp b/ZLM/3rdpart/ZLToolKit/src/Network/Buffer.cpp
new file mode 100644
index 0000000..fe8fd93
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/src/Network/Buffer.h b/ZLM/3rdpart/ZLToolKit/src/Network/Buffer.h
new file mode 100644
index 0000000..05be45e
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/src/Network/BufferSock.cpp b/ZLM/3rdpart/ZLToolKit/src/Network/BufferSock.cpp
new file mode 100644
index 0000000..37ca836
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/src/Network/BufferSock.h b/ZLM/3rdpart/ZLToolKit/src/Network/BufferSock.h
new file mode 100644
index 0000000..1320f85
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/src/Network/Kcp.cpp b/ZLM/3rdpart/ZLToolKit/src/Network/Kcp.cpp
new file mode 100644
index 0000000..4363e58
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/src/Network/Kcp.h b/ZLM/3rdpart/ZLToolKit/src/Network/Kcp.h
new file mode 100644
index 0000000..9251b71
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/src/Network/Server.cpp b/ZLM/3rdpart/ZLToolKit/src/Network/Server.cpp
new file mode 100644
index 0000000..6330285
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/src/Network/Server.h b/ZLM/3rdpart/ZLToolKit/src/Network/Server.h
new file mode 100644
index 0000000..88d9eba
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/src/Network/Session.cpp b/ZLM/3rdpart/ZLToolKit/src/Network/Session.cpp
new file mode 100644
index 0000000..cad48a6
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/src/Network/Session.h b/ZLM/3rdpart/ZLToolKit/src/Network/Session.h
new file mode 100644
index 0000000..774cc19
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/src/Network/Socket.cpp b/ZLM/3rdpart/ZLToolKit/src/Network/Socket.cpp
new file mode 100644
index 0000000..bb545bf
--- /dev/null
+++ b/ZLM/3rdpart/ZLToolKit/src/Network/Socket.cpp
@@ -0,0 +1,1172 @@
+/*
+ * 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;
+}
+
+bool 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 false;
+ }
+ sock = other._sock_fd->sockNum();
+ }
+ return fromSock_l(sock);
+}
+
+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/ZLM/3rdpart/ZLToolKit/src/Network/Socket.h b/ZLM/3rdpart/ZLToolKit/src/Network/Socket.h
new file mode 100644
index 0000000..fe27eee
--- /dev/null
+++ b/ZLM/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 是否成功
+ * 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]
+ */
+ bool 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/ZLM/3rdpart/ZLToolKit/src/Network/Socket_ios.mm b/ZLM/3rdpart/ZLToolKit/src/Network/Socket_ios.mm
new file mode 100644
index 0000000..30a9fa7
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/src/Network/TcpClient.cpp b/ZLM/3rdpart/ZLToolKit/src/Network/TcpClient.cpp
new file mode 100644
index 0000000..6138f41
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/src/Network/TcpClient.h b/ZLM/3rdpart/ZLToolKit/src/Network/TcpClient.h
new file mode 100644
index 0000000..7f3936f
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/src/Network/TcpServer.cpp b/ZLM/3rdpart/ZLToolKit/src/Network/TcpServer.cpp
new file mode 100644
index 0000000..6474022
--- /dev/null
+++ b/ZLM/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/ZLM/3rdpart/ZLToolKit/src/Network/TcpServer.h b/ZLM/3rdpart/ZLToolKit/src/Network/TcpServer.h
new file mode 100644
index 0000000..344653c
--- /dev/null
+++ b/ZLM/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