mirror of
https://github.com/RRQM/TouchSocket.git
synced 2025-12-17 17:06:45 +08:00
整理:重新追踪文件
This commit is contained in:
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
*.js linguist-language=csharp
|
||||
*.css linguist-language=csharp
|
||||
*.html linguist-language=csharp
|
||||
*.java linguist-language=csharp
|
||||
93
.gitee/ISSUE_TEMPLATE.zh-CN.md
Normal file
93
.gitee/ISSUE_TEMPLATE.zh-CN.md
Normal file
@@ -0,0 +1,93 @@
|
||||
## 💢 特别说明:如果 Issue 没有严格按照模板编写且未提供测试源码下载或 Git 测试仓库地址,则视为无效 `Issue`,将无法得到答复。
|
||||
|
||||
### TouchSocket(Pro) 版本号
|
||||
|
||||
哪个版本号?
|
||||
|
||||
---
|
||||
|
||||
### 哪个组件
|
||||
|
||||
- [ ] Tcp
|
||||
- [ ] Udp
|
||||
- [ ] Http
|
||||
- [x] Websocket
|
||||
- [ ] Dmtp(基于:tcp、udp、http、websocket协议)
|
||||
- [ ] JsonRpc(基于:tcp、http、websocket)
|
||||
- [ ] XmlRpc
|
||||
- [ ] WebApi
|
||||
- [ ] 其他工具类组件(说明)
|
||||
|
||||
---
|
||||
|
||||
|
||||
### .NET SDK 版本号
|
||||
|
||||
- [ ] .NET45
|
||||
- [ ] .NET461
|
||||
- [ ] .NET462
|
||||
- [ ] .NET472
|
||||
- [ ] .NET481
|
||||
- [ ] .NET5
|
||||
- [x] .NET6
|
||||
- [ ] .NET7
|
||||
- [ ] .NET8
|
||||
- [ ] .NET9
|
||||
|
||||
---
|
||||
|
||||
### 项目类型
|
||||
|
||||
- [ ] Console
|
||||
- [x] Winform
|
||||
- [ ] WPF
|
||||
- [ ] Unity3d
|
||||
- [ ] Blazor Server
|
||||
- [ ] MVC
|
||||
- [ ] 其他(请说明)
|
||||
|
||||
|
||||
---
|
||||
|
||||
### 操作系统和版本
|
||||
|
||||
- [x] Windows(版本)
|
||||
- [ ] Linux(版本)
|
||||
- [ ] MacOS(版本)
|
||||
- [ ] 其他(版本)
|
||||
|
||||
---
|
||||
|
||||
### 代码环境
|
||||
|
||||
- [x] 开发环境(Development)
|
||||
- [ ] 生产环境(Production)
|
||||
- [ ] 测试环境(Tests/单元测试/集成测试 )
|
||||
|
||||
---
|
||||
|
||||
### 描述你的问题
|
||||
|
||||
发生了什么?
|
||||
|
||||
---
|
||||
|
||||
### 异常堆栈信息
|
||||
|
||||
异常堆栈是什么?
|
||||
|
||||
---
|
||||
|
||||
### 测试项目代码
|
||||
|
||||
> **⚠⚠ 必须提供完整可运行且包含错误的 `Git` 仓库 DEMO,DEMO 提供最简单的错误逻辑代码,否则将无法得到答复。⚠⚠**
|
||||
|
||||
您的代码下载地址?
|
||||
|
||||
---
|
||||
|
||||
### 期待结果
|
||||
|
||||
期待的结果是?
|
||||
|
||||
---
|
||||
164
.gitee/ISSUE_TEMPLATE/bug.yml
Normal file
164
.gitee/ISSUE_TEMPLATE/bug.yml
Normal file
@@ -0,0 +1,164 @@
|
||||
name: 问题反馈
|
||||
description: 当你中发现了一个 Bug,导致应用崩溃或抛出异常,或者有一个组件存在问题,或者某些地方看起来不对劲。
|
||||
title:
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## 💢 特别说明:如果 Issue 没有严格按照模板编写且未提供测试源码下载或 Git 测试仓库地址,则视为无效 `Issue`,将无法得到答复。
|
||||
- type: dropdown
|
||||
id: component
|
||||
attributes:
|
||||
label: 组件
|
||||
description: 请选择项目使用的哪个组件?
|
||||
options:
|
||||
- Tcp
|
||||
- Udp
|
||||
- NamedPipe
|
||||
- WebSocket
|
||||
- Http
|
||||
- Dmtp-Tcp
|
||||
- Dmtp-Udp
|
||||
- Dmtp-WebSocket
|
||||
- Dmtp-Http
|
||||
- Dmtp-NamedPipe
|
||||
- Rpc
|
||||
- JsonRpc
|
||||
- XmlRpc
|
||||
- WebApi
|
||||
- 其他
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: 版本号
|
||||
description: 请输入项目使用的 TouchSocket 版本?
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: net_sdk
|
||||
attributes:
|
||||
label: .NET SDK
|
||||
description: 请选择项目使用的 .NET SDK 版本?
|
||||
options:
|
||||
- .NET Framework 45 以上
|
||||
- .NET Framework 462 以上
|
||||
- .NET Framework 472 以上
|
||||
- .NET Framework 481
|
||||
- .NET6
|
||||
- .NET7
|
||||
- .NET8
|
||||
- .NET9
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: project_type
|
||||
attributes:
|
||||
label: 项目类型
|
||||
description: 请选择目标项目类型?
|
||||
options:
|
||||
- WebApi
|
||||
- Unity
|
||||
- MAUI
|
||||
- Blazor
|
||||
- WinForm
|
||||
- WPF
|
||||
- Console
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: os_type
|
||||
attributes:
|
||||
label: 操作系统
|
||||
description: 请选择操作系统类型?
|
||||
options:
|
||||
- Windows 11
|
||||
- Windows 10
|
||||
- Windows 8
|
||||
- Ubuntu
|
||||
- CentOS
|
||||
- Debian
|
||||
- Deepin
|
||||
- Red Hat Linux
|
||||
- 其他 Linux
|
||||
- MacOS
|
||||
- 其他
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: environment
|
||||
attributes:
|
||||
label: 运行环境
|
||||
description: 请选择代码运行环境?
|
||||
options:
|
||||
- 开发环境 (Development)
|
||||
- 生产环境 (Production)
|
||||
- 测试环境 (单元测试/集成测试)
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 这个问题是否已经存在?
|
||||
options:
|
||||
- label: 我已经搜索过现有的问题 (https://gitee.com/RRQM_Home/TouchSocket/issues)
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 如何复现
|
||||
description: 请详细告诉我们如何复现你遇到的问题,如涉及代码,可提供一个最小代码示例,并使用反引号```附上它
|
||||
placeholder: |
|
||||
1. ...
|
||||
2. ...
|
||||
3. ...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 预期结果
|
||||
description: 请告诉我们你预期会发生什么。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 实际结果
|
||||
description: 请告诉我们实际发生了什么。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 异常信息
|
||||
description: 如果有异常请把详细异常堆栈粘贴上来。如果可以的话,上传任何关于 bug 的截图。
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: demo
|
||||
attributes:
|
||||
label: Demo 地址(仅限Git地址)
|
||||
description: 请提供复现错误的 Demo 下载
|
||||
placeholder: https://gitee.com/your_id/your_test_project.git
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 承诺支持
|
||||
options:
|
||||
- label: 我确定我已经对TouchSocket项目进行了“Star”操作。
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 承诺规范
|
||||
options:
|
||||
- label: 我确定已完整阅读[Issue提问规范](https://touchsocket.net/docs/current/troubleshootissue),并按照要求填写。
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 承诺友好
|
||||
options:
|
||||
- label: 我承诺将本着相互尊重、理解和友善的态度进行交流,共同维护好 TouchSocket 来之不易的良好的社区氛围。
|
||||
required: true
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
感谢你提交 bug,我们会尽快处理。
|
||||
9
.gitee/ISSUE_TEMPLATE/config.yml
Normal file
9
.gitee/ISSUE_TEMPLATE/config.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
blank_issues_enabled: true
|
||||
|
||||
contact_links:
|
||||
- name: 使用文档
|
||||
url: https://touchsocket.net/
|
||||
about: 一款简单易用的基础网络通讯组件库。
|
||||
- name: 慷慨赞助
|
||||
url: https://touchsocket.net/docs/current/donate
|
||||
about: 慷慨赞助
|
||||
47
.gitee/ISSUE_TEMPLATE/feature.yml
Normal file
47
.gitee/ISSUE_TEMPLATE/feature.yml
Normal file
@@ -0,0 +1,47 @@
|
||||
name: 功能建议
|
||||
description: 对本项目提出一个功能建议
|
||||
title:
|
||||
labels: ["enhancement"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
感谢提出功能建议,我们将仔细考虑!
|
||||
- type: textarea
|
||||
id: related-problem
|
||||
attributes:
|
||||
label: 你的功能建议是否和某个问题相关?
|
||||
description: 清晰并简洁地描述问题是什么,例如,当我...时,我总是感到困扰。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: desired-solution
|
||||
attributes:
|
||||
label: 你希望看到什么解决方案?
|
||||
description: 清晰并简洁地描述你希望发生的事情。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: 你考虑过哪些替代方案?
|
||||
description: 清晰并简洁地描述你考虑过的任何替代解决方案或功能。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: 你有其他上下文或截图吗?
|
||||
description: 在此处添加有关功能请求的任何其他上下文或截图。
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 意向参与贡献
|
||||
options:
|
||||
- label: 我有意向参与具体功能的开发实现并将代码贡献回到上游社区
|
||||
required: true
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
感谢你的参与!
|
||||
115
.gitee/ISSUE_TEMPLATE/question.yml
Normal file
115
.gitee/ISSUE_TEMPLATE/question.yml
Normal file
@@ -0,0 +1,115 @@
|
||||
name: 使用疑问
|
||||
description: 当你不明白某个功能如何使用时,可以提交该Issue。
|
||||
title:
|
||||
labels: ["question"]
|
||||
body:
|
||||
- type: dropdown
|
||||
id: component
|
||||
attributes:
|
||||
label: 组件
|
||||
description: 请选择项目使用的哪个组件?
|
||||
options:
|
||||
- Tcp
|
||||
- Udp
|
||||
- NamedPipe
|
||||
- WebSocket
|
||||
- Http
|
||||
- Dmtp-Tcp
|
||||
- Dmtp-Udp
|
||||
- Dmtp-WebSocket
|
||||
- Dmtp-Http
|
||||
- Dmtp-NamedPipe
|
||||
- Rpc
|
||||
- JsonRpc
|
||||
- XmlRpc
|
||||
- WebApi
|
||||
- 其他
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: 版本号
|
||||
description: 请输入项目使用的 TouchSocket 版本?
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: net_sdk
|
||||
attributes:
|
||||
label: .NET SDK
|
||||
description: 请选择项目使用的 .NET SDK 版本?
|
||||
options:
|
||||
- .NET Framework 45 以上
|
||||
- .NET Framework 462 以上
|
||||
- .NET Framework 472 以上
|
||||
- .NET Framework 481
|
||||
- .NET6
|
||||
- .NET7
|
||||
- .NET8
|
||||
- .NET9
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: project_type
|
||||
attributes:
|
||||
label: 项目类型
|
||||
description: 请选择目标项目类型?
|
||||
options:
|
||||
- WebApi
|
||||
- Unity
|
||||
- MAUI
|
||||
- Blazor Server
|
||||
- WinForm
|
||||
- WPF
|
||||
- Console
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: os_type
|
||||
attributes:
|
||||
label: 操作系统
|
||||
description: 请选择操作系统类型?
|
||||
options:
|
||||
- Windows 11
|
||||
- Windows 10
|
||||
- Windows 8
|
||||
- Ubuntu
|
||||
- CentOS
|
||||
- Debian
|
||||
- Deepin
|
||||
- Red Hat Linux
|
||||
- 其他 Linux
|
||||
- MacOS
|
||||
- 其他
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 你的需求是什么?
|
||||
description: 清晰并简洁地描述你希望发生的事情。
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 这个问题是否已经存在?
|
||||
options:
|
||||
- label: 我已经搜索过现有的问题 (https://gitee.com/RRQM_Home/TouchSocket/issues)
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 承诺支持
|
||||
options:
|
||||
- label: 我确定我已经对TouchSocket项目进行了“Star”操作。
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 承诺规范
|
||||
options:
|
||||
- label: 我确定已完整阅读[Issue提问规范](https://touchsocket.net/docs/current/troubleshootissue),并按照要求填写。
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 承诺友好
|
||||
options:
|
||||
- label: 我承诺将本着相互尊重、理解和友善的态度进行交流,共同维护好 TouchSocket 来之不易的良好的社区氛围。
|
||||
required: true
|
||||
50
.gitee/ISSUE_TEMPLATE/upgradation.yml
Normal file
50
.gitee/ISSUE_TEMPLATE/upgradation.yml
Normal file
@@ -0,0 +1,50 @@
|
||||
name: 版本升级说明
|
||||
description: 当从旧版本升级到某个新版本时,如果有不兼容情况,可以创建该Issue以帮助其他人更快的升级。(注意:该issue的作用不是提问,而是解答)
|
||||
title:
|
||||
labels: ["upgradation"]
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
label: 上个版本?
|
||||
description: 请输入您使用的上个版本的版本号。
|
||||
placeholder: 例如:v2.0.0-beta
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 受影响组件
|
||||
options:
|
||||
- label: All
|
||||
- label: Core
|
||||
- label: Tcp
|
||||
- label: Udp
|
||||
- label: NamedPipe
|
||||
- label: WebSocket
|
||||
- label: Http
|
||||
- label: Redis
|
||||
- label: Dmtp-Tcp
|
||||
- label: Dmtp-Udp
|
||||
- label: Dmtp-WebSocket
|
||||
- label: Dmtp-Http
|
||||
- label: Dmtp-NamedPipe
|
||||
- label: 其他
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 改动内容?
|
||||
description: 请清晰并简洁地描述版本之间的差异性。
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 这个问题是否已经存在?
|
||||
options:
|
||||
- label: 我已经搜索过现有的问题 (https://gitee.com/RRQM_Home/TouchSocket/issues)
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 友好承诺
|
||||
options:
|
||||
- label: 我承诺将本着相互尊重、理解和友善的态度进行交流,共同维护好 TouchSocket 来之不易的良好的社区氛围。
|
||||
required: true
|
||||
342
.gitignore
vendored
Normal file
342
.gitignore
vendored
Normal file
@@ -0,0 +1,342 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true
|
||||
**/wwwroot/lib/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
handbook/package-lock.json
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"commentTranslate.targetLanguage": "en",
|
||||
}
|
||||
51
.workflow/branch-pipeline.yml
Normal file
51
.workflow/branch-pipeline.yml
Normal file
@@ -0,0 +1,51 @@
|
||||
version: '1.0'
|
||||
name: branch-pipeline
|
||||
displayName: BranchPipeline
|
||||
stages:
|
||||
- stage:
|
||||
name: compile
|
||||
displayName: 编译
|
||||
steps:
|
||||
- step: build@nodejs
|
||||
name: build_nodejs
|
||||
displayName: Nodejs 构建
|
||||
# 支持8.16.2、10.17.0、12.16.1、14.16.0、15.12.0五个版本
|
||||
nodeVersion: 14.16.0
|
||||
# 构建命令:安装依赖 -> 清除上次打包产物残留 -> 执行构建 【请根据项目实际产出进行填写】
|
||||
commands:
|
||||
- npm install && rm -rf ./dist && npm run build
|
||||
# 非必填字段,开启后表示将构建产物暂存,但不会上传到制品库中,7天后自动清除
|
||||
artifacts:
|
||||
# 构建产物名字,作为产物的唯一标识可向下传递,支持自定义,默认为BUILD_ARTIFACT。在下游可以通过${BUILD_ARTIFACT}方式引用来获取构建物地址
|
||||
- name: BUILD_ARTIFACT
|
||||
# 构建产物获取路径,是指代码编译完毕之后构建物的所在路径
|
||||
path:
|
||||
- ./dist
|
||||
- step: publish@general_artifacts
|
||||
name: publish_general_artifacts
|
||||
displayName: 上传制品
|
||||
# 上游构建任务定义的产物名,默认BUILD_ARTIFACT
|
||||
dependArtifact: BUILD_ARTIFACT
|
||||
# 上传到制品库时的制品命名,默认output
|
||||
artifactName: output
|
||||
dependsOn: build_nodejs
|
||||
- stage:
|
||||
name: release
|
||||
displayName: 发布
|
||||
steps:
|
||||
- step: publish@release_artifacts
|
||||
name: publish_release_artifacts
|
||||
displayName: '发布'
|
||||
# 上游上传制品任务的产出
|
||||
dependArtifact: output
|
||||
# 发布制品版本号
|
||||
version: '1.0.0.0'
|
||||
# 是否开启版本号自增,默认开启
|
||||
autoIncrement: true
|
||||
triggers:
|
||||
push:
|
||||
branches:
|
||||
exclude:
|
||||
- master
|
||||
include:
|
||||
- .*
|
||||
49
.workflow/master-pipeline.yml
Normal file
49
.workflow/master-pipeline.yml
Normal file
@@ -0,0 +1,49 @@
|
||||
version: '1.0'
|
||||
name: master-pipeline
|
||||
displayName: MasterPipeline
|
||||
stages:
|
||||
- stage:
|
||||
name: compile
|
||||
displayName: 编译
|
||||
steps:
|
||||
- step: build@nodejs
|
||||
name: build_nodejs
|
||||
displayName: Nodejs 构建
|
||||
# 支持8.16.2、10.17.0、12.16.1、14.16.0、15.12.0五个版本
|
||||
nodeVersion: 14.16.0
|
||||
# 构建命令:安装依赖 -> 清除上次打包产物残留 -> 执行构建 【请根据项目实际产出进行填写】
|
||||
commands:
|
||||
- npm install && rm -rf ./dist && npm run build
|
||||
# 非必填字段,开启后表示将构建产物暂存,但不会上传到制品库中,7天后自动清除
|
||||
artifacts:
|
||||
# 构建产物名字,作为产物的唯一标识可向下传递,支持自定义,默认为BUILD_ARTIFACT。在下游可以通过${BUILD_ARTIFACT}方式引用来获取构建物地址
|
||||
- name: BUILD_ARTIFACT
|
||||
# 构建产物获取路径,是指代码编译完毕之后构建物的所在路径
|
||||
path:
|
||||
- ./dist
|
||||
- step: publish@general_artifacts
|
||||
name: publish_general_artifacts
|
||||
displayName: 上传制品
|
||||
# 上游构建任务定义的产物名,默认BUILD_ARTIFACT
|
||||
dependArtifact: BUILD_ARTIFACT
|
||||
# 上传到制品库时的制品命名,默认output
|
||||
artifactName: output
|
||||
dependsOn: build_nodejs
|
||||
- stage:
|
||||
name: release
|
||||
displayName: 发布
|
||||
steps:
|
||||
- step: publish@release_artifacts
|
||||
name: publish_release_artifacts
|
||||
displayName: '发布'
|
||||
# 上游上传制品任务的产出
|
||||
dependArtifact: output
|
||||
# 发布制品版本号
|
||||
version: '1.0.0.0'
|
||||
# 是否开启版本号自增,默认开启
|
||||
autoIncrement: true
|
||||
triggers:
|
||||
push:
|
||||
branches:
|
||||
include:
|
||||
- master
|
||||
36
.workflow/pr-pipeline.yml
Normal file
36
.workflow/pr-pipeline.yml
Normal file
@@ -0,0 +1,36 @@
|
||||
version: '1.0'
|
||||
name: pr-pipeline
|
||||
displayName: PRPipeline
|
||||
stages:
|
||||
- stage:
|
||||
name: compile
|
||||
displayName: 编译
|
||||
steps:
|
||||
- step: build@nodejs
|
||||
name: build_nodejs
|
||||
displayName: Nodejs 构建
|
||||
# 支持8.16.2、10.17.0、12.16.1、14.16.0、15.12.0五个版本
|
||||
nodeVersion: 14.16.0
|
||||
# 构建命令:安装依赖 -> 清除上次打包产物残留 -> 执行构建 【请根据项目实际产出进行填写】
|
||||
commands:
|
||||
- npm install && rm -rf ./dist && npm run build
|
||||
# 非必填字段,开启后表示将构建产物暂存,但不会上传到制品库中,7天后自动清除
|
||||
artifacts:
|
||||
# 构建产物名字,作为产物的唯一标识可向下传递,支持自定义,默认为BUILD_ARTIFACT。在下游可以通过${BUILD_ARTIFACT}方式引用来获取构建物地址
|
||||
- name: BUILD_ARTIFACT
|
||||
# 构建产物获取路径,是指代码编译完毕之后构建物的所在路径
|
||||
path:
|
||||
- ./dist
|
||||
- step: publish@general_artifacts
|
||||
name: publish_general_artifacts
|
||||
displayName: 上传制品
|
||||
# 上游构建任务定义的产物名,默认BUILD_ARTIFACT
|
||||
dependArtifact: BUILD_ARTIFACT
|
||||
# 上传到制品库时的制品命名,默认output
|
||||
artifactName: output
|
||||
dependsOn: build_nodejs
|
||||
triggers:
|
||||
pr:
|
||||
branches:
|
||||
include:
|
||||
- master
|
||||
201
LICENSE
Normal file
201
LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
190
README.md
Normal file
190
README.md
Normal file
@@ -0,0 +1,190 @@
|
||||
**En** | [中](./README.zh.md)
|
||||
<p></p>
|
||||
<p></p>
|
||||
<p align="center">
|
||||
<img src="logo.png" width = "100" height = "100" alt="The name of the image" align=center />
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://www.nuget.org/packages/TouchSocket/)
|
||||
[](https://www.nuget.org/packages/TouchSocket/)
|
||||
[](https://www.apache.org/licenses/LICENSE-2.0.html)
|
||||
[](https://gitee.com/RRQM_Home/TouchSocket/stargazers)
|
||||
[](https://gitee.com/RRQM_Home/TouchSocket/members)
|
||||
<a href="https://jq.qq.com/?_wv=1027&k=gN7UL4fw">
|
||||
<img src="https://img.shields.io/badge/QQ group-234762506-red" alt="QQ">
|
||||
</a>
|
||||
[](https://github.com/RRQM/TouchSocket)
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
|
||||
三十功名尘与土,八千里路云和月。
|
||||
|
||||
</div>
|
||||
|
||||
## 🎀Description
|
||||
|
||||

|
||||
|
||||
'TouchSocket' is a powerful and easy-to-use.NET network communication framework for languages such as C#, VB.Net, and F#. It provides a variety of communication modules, including TCP, UDP, SSL, WebSocket, Modbus, etc. It supports solving the problem of TCP packet subcontracting and UDP large packet fragment combination. The framework supports a variety of protocol templates to quickly parse data packets such as fixed headers, fixed lengths, and interval characters.
|
||||
|
||||
***
|
||||
|
||||
## 🌟Related Documentation
|
||||
|
||||
- [The first page of the document](https://touchsocket.net/)
|
||||
- [Getting started as a newbie](https://touchsocket.net/docs/current/startguide)
|
||||
- [API documentation](https://touchsocket.net/api/)
|
||||
|
||||
***
|
||||
|
||||
## 🖥Supporting the environment
|
||||
|
||||
- .NET Framework 4.5 or later.
|
||||
- .NET 6.0 and above.
|
||||
- .NET Standard 2.0 or later.
|
||||
|
||||
## 🥪Support frameworks
|
||||
|
||||
- Console
|
||||
- WPF
|
||||
- Winform
|
||||
- Blazor
|
||||
- Xamarin
|
||||
- MAUI
|
||||
- Avalonia
|
||||
- Mono
|
||||
- Unity 3D(except WebGL)
|
||||
- Other (i.e. all C# faculties)
|
||||
|
||||
|
||||
## 🌴A quick overview of TouchSocket features
|
||||
|
||||
#### Traditional IOCP and TouchSocket IOCP modes
|
||||
|
||||
The IOCP of TouchSocket is not the same as the traditional one, just take Microsoft's official example as an example, he uses MemoryBuffer to open up a piece of memory, divide it evenly, and then assign a zone to each session to receive, and then **copy** the received data, and then process the copied data. The TouchSocket is to take a usable memory block from the memory pool before each reception, and then use it directly for receiving, and after receiving the data, the memory block is directly thrown out for processing, so as to avoid the **copy operation**, although it is only a small design, but when transmitting **10w**times**64kb**data, the performance difference is **10 times**.
|
||||
|
||||
#### Data Processing Adapters
|
||||
|
||||
I believe that everyone has used other Socket products, so TouchSocket is also designed to learn from the excellent design concept of other products, [data processing adapter](https://touchsocket.net/docs/current/adapterdescription) is one of them, but unlike the design of other products, the adapter of TouchSocket is more powerful and easy to use. and flexible. Not only can it parse packets in advance, but it can also parse data objects, which can be replaced at any time and then take effect immediately. For example, you can use a fixed packet header to preprocess the data, so as to solve the problem of data subcontracting and sticky packet. It can also directly parse the HTTP data protocol, WebSocket data protocol, etc.
|
||||
|
||||
#### Compatibility and adaptation
|
||||
|
||||
TouchSocket provides a variety of framework models and is fully compatible with all protocols based on TCP and UDP protocols. For example, TcpService and TcpClient have the same basic functions as Sockets, but they enhance the robustness and concurrency of the framework, and throw the connection and received data in the form of events, so that users can use it more user-friendly.
|
||||
|
||||
## 🔗Contact the author
|
||||
|
||||
- [Blog Homepage](https://blog.csdn.net/qq_40374647)
|
||||
- [Bilibili video](https://space.bilibili.com/94253567)
|
||||
- [Source code repository home page](https://gitee.com/RRQM_Home)
|
||||
- QQ group:[234762506](https://jq.qq.com/?_wv=1027&k=gN7UL4fw)
|
||||
|
||||
|
||||
## 👑Functional mapping
|
||||
|
||||
<p align="center">
|
||||
<img src="images/1.png" alt="The name of the image" align=center />
|
||||
</p>
|
||||
|
||||
## ✨Simple example
|
||||
|
||||
The following is just a simple example of how to create an example, please see [Documentation](https://touchsocket.net/) for more details.
|
||||
|
||||
#### TcpService
|
||||
|
||||
```
|
||||
TcpService service = new TcpService();
|
||||
service.Connecting = (client, e) => {return EasyTask.CompletedTask; };//有客户端正在连接
|
||||
service.Connected = (client, e) => {return EasyTask.CompletedTask; };//有客户端连接
|
||||
service.Disconnected = (client, e) => {return EasyTask.CompletedTask; };//有客户端断开连接
|
||||
service.Received = (client, e) =>
|
||||
{
|
||||
//Received information from the client
|
||||
string mes = e.ByteBlock.Span.ToString(Encoding.UTF8);
|
||||
Console.WriteLine($"Removed from {client. ID} Message received: {}");
|
||||
return EasyTask.CompletedTask;
|
||||
};
|
||||
await service.StartAsync(7789);//Start
|
||||
```
|
||||
|
||||
#### TcpClient
|
||||
|
||||
```
|
||||
TcpClient tcpClient = new TcpClient();
|
||||
tcpClient.Connected = (client, e) => {return EasyTask.CompletedTask; };//Successfully connected to the server
|
||||
tcpClient.Disconnected = (client, e) => {return EasyTask.CompletedTask; };//Disconnect from the server, which is not triggered when the connection is unsuccessful.
|
||||
tcpClient.Received = (client, e) =>
|
||||
{
|
||||
//Information is received from the server
|
||||
string mes = e.ByteBlock.Span.ToString(Encoding.UTF8);
|
||||
Console.WriteLine($"Message received: {mes}");
|
||||
return EasyTask.CompletedTask;
|
||||
};
|
||||
|
||||
await tcpClient.ConnectAsync("127.0.0.1:7789");
|
||||
await tcpClient.SendAsync("Hello");
|
||||
```
|
||||
|
||||
#### TcpClient reconnect
|
||||
|
||||
In the plug-in configuration of Config, you can use the reconnection plug-in.
|
||||
|
||||
```
|
||||
.ConfigurePlugins(a=>
|
||||
{
|
||||
a.UseReconnection(5, true, 1000);
|
||||
});
|
||||
```
|
||||
|
||||
#### FixedHeaderPackageAdapter package
|
||||
|
||||
The adapter mainly solves the problem of Tcp sticking and packing, and the data format adopts a simple and efficient "Baotou + Data Body" mode, in which Baotou supports:
|
||||
|
||||
- Byte mode (1+n), a maximum of 255 bytes of data can be received at one time.
|
||||
- Ushort mode (2+n), the maximum number of bytes received at a time is 65535 bytes.
|
||||
- Int mode (4+n), maximum reception of 2G data at a time.
|
||||
|
||||
The above data headers all use the default side mode (little-end mode) of TouchSocketBitConverter, and users can switch the default side mode according to their needs.
|
||||
|
||||
```
|
||||
TouchSocketBitConverter.DefaultEndianType = EndianType.Little;
|
||||
```
|
||||
|
||||
#### CustomFixedHeaderDataHandlingAdapter
|
||||
|
||||
The user-defined fixed header adapter mainly helps users solve the data frame information with fixed headers. For example, the following data formats only need to implement a few interfaces to complete the parsing, please refer to the API for details.
|
||||
|
||||
|1|1|1|**********|
|
||||
|
||||
#### CustomUnfixedHeaderDataHandlingAdapter
|
||||
|
||||
The user-defined non-fixed header adapter mainly helps users solve the problem of data frame information with unfixed headers. For example, the most typical HTTP packets have a data header separated from the data body by "\r\n", and the data header is not fixed because of the different request information of the requester, and the length of the data body is also explicitly specified by the value of the ContentLength of the data header, so you can consider using CustomUnfixedHeaderDataHandlingAdapter to parse, which can also be achieved through simple development.
|
||||
|
||||
***
|
||||
|
||||
## Thanks
|
||||
|
||||
Thank you for your support of TouchSocket, if you have any other questions, please submit an Issue, or join the group QQ: [234762506](https://jq.qq.com/?_wv=1027&k=gN7UL4fw) to discuss.
|
||||
|
||||
Thanks to the support of the following tools
|
||||
|
||||
- [Visual Studio](https://visualstudio.microsoft.com/zh-hans/)
|
||||
- [JetBrains](https://www.jetbrains.com/)
|
||||
- [Visual Studio Code](https://code.visualstudio.com/)
|
||||
|
||||
## Support the author
|
||||
|
||||
- [Tipping support](https://touchsocket.net/docs/current/donate)
|
||||
- [Pro support](https://touchsocket.net/docs/current/enterprise)
|
||||
|
||||
## Special Statement
|
||||
|
||||
The TouchSocket project has been added to the [dotNET China](https://gitee.com/dotnetchina) organization.
|
||||
|
||||
|
||||

|
||||
|
||||
190
README.zh.md
Normal file
190
README.zh.md
Normal file
@@ -0,0 +1,190 @@
|
||||
**中** | [En](./README.md)
|
||||
<p></p>
|
||||
<p></p>
|
||||
<p align="center">
|
||||
<img src="logo.png" width = "100" height = "100" alt="图片名称" align=center />
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://www.nuget.org/packages/TouchSocket/)
|
||||
[](https://www.nuget.org/packages/TouchSocket/)
|
||||
[](https://www.apache.org/licenses/LICENSE-2.0.html)
|
||||
[](https://gitee.com/RRQM_Home/TouchSocket/stargazers)
|
||||
[](https://gitee.com/RRQM_Home/TouchSocket/members)
|
||||
<a href="https://jq.qq.com/?_wv=1027&k=gN7UL4fw">
|
||||
<img src="https://img.shields.io/badge/QQ群-234762506-red" alt="QQ">
|
||||
</a>
|
||||
[](https://github.com/RRQM/TouchSocket)
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
|
||||
三十功名尘与土,八千里路云和月。
|
||||
|
||||
</div>
|
||||
|
||||
## 🎀描述
|
||||
|
||||

|
||||
|
||||
`TouchSocket`是一个功能强大且易于使用的.NET 网络通信框架,适用于C#、VB.Net 和 F#等语言。它提供了多种通信模块,包括TCP、UDP、SSL、WebSocket、Modbus等。支持解决TCP黏包分包问题和UDP大数据包分片组合问题。框架支持多种协议模板,快速实现固定包头、固定长度和区间字符等数据报文解析。
|
||||
|
||||
***
|
||||
|
||||
## 🌟相关文档
|
||||
|
||||
- [文档首页](https://touchsocket.net/)
|
||||
- [新手入门](https://touchsocket.net/docs/current/startguide)
|
||||
- [API文档](https://touchsocket.net/api/)
|
||||
|
||||
***
|
||||
|
||||
## 🖥支持环境
|
||||
|
||||
- .NET Framework4.5及以上。
|
||||
- .NET 6.0及以上。
|
||||
- .NET Standard2.0及以上。
|
||||
|
||||
## 🥪支持框架
|
||||
|
||||
- Console
|
||||
- WPF
|
||||
- Winform
|
||||
- Blazor
|
||||
- Xamarin
|
||||
- MAUI
|
||||
- Avalonia
|
||||
- Mono
|
||||
- Unity 3D(除WebGL)
|
||||
- 其他(即所有C#系)
|
||||
|
||||
|
||||
## 🌴TouchSocket特点速览
|
||||
|
||||
#### 传统IOCP和TouchSocket的IOCP模式
|
||||
|
||||
TouchSocket的IOCP和传统也不一样,就以微软官方示例为例,他是使用MemoryBuffer开辟一块内存,均分,然后给每个会话分配一个区接收,等收到数据后,再**复制**接收的数据,然后把复制的数据进行处理。而TouchSocket是每次接收之前,从内存池拿一个可用内存块,然后**直接用于接收**,等收到数据以后,直接就把这个内存块抛出处理,这样就避免了**复制操作**,虽然只是细小的设计,但是在传输**10w**次**64kb**的数据时,性能相差了**10倍**。
|
||||
|
||||
#### 数据处理适配器
|
||||
|
||||
相信大家都使用过其他的Socket产品,那么TouchSocket在设计时也是借鉴了其他产品的优秀设计理念,[数据处理适配器](https://touchsocket.net/docs/current/adapterdescription)就是其中之一,但和其他产品的设计不同的是,TouchSocket的适配器功能更加强大,易用,且灵活。它不仅可以提前解析数据包,还可以解析数据对象,可以随时替换,然后立即生效。例如:可以使用固定包头对数据进行预处理,从而解决**数据分包**、**粘包**的问题。也可以直接解析**HTTP**数据协议、WebSocket数据协议等。
|
||||
|
||||
#### 兼容性与适配
|
||||
|
||||
TouchSocket提供多种框架模型,能够完全兼容基于TCP、UDP协议的所有协议。例如:TcpService与TcpClient,其基础功能和Socket一模一样,只是增强了框架的**坚固性**和**并发性**,将**连接**和**接收数据**通过事件的形式抛出,让使用者能够更加友好的使用。
|
||||
|
||||
## 🔗联系作者
|
||||
|
||||
- [CSDN博客主页](https://blog.csdn.net/qq_40374647)
|
||||
- [哔哩哔哩视频](https://space.bilibili.com/94253567)
|
||||
- [源代码仓库主页](https://gitee.com/RRQM_Home)
|
||||
- 交流QQ群:[234762506](https://jq.qq.com/?_wv=1027&k=gN7UL4fw)
|
||||
|
||||
|
||||
## 👑功能导图
|
||||
|
||||
<p align="center">
|
||||
<img src="images/1.png" alt="图片名称" align=center />
|
||||
</p>
|
||||
|
||||
## ✨简单示例
|
||||
|
||||
**_以下仅以最简方式创建示例,更多详情请查看[说明文档](https://touchsocket.net/)。_**
|
||||
|
||||
#### TcpService
|
||||
|
||||
```
|
||||
TcpService service = new TcpService();
|
||||
service.Connecting = (client, e) => {return EasyTask.CompletedTask; };//有客户端正在连接
|
||||
service.Connected = (client, e) => {return EasyTask.CompletedTask; };//有客户端连接
|
||||
service.Disconnected = (client, e) => {return EasyTask.CompletedTask; };//有客户端断开连接
|
||||
service.Received = (client, e) =>
|
||||
{
|
||||
//从客户端收到信息
|
||||
string mes = e.ByteBlock.Span.ToString(Encoding.UTF8);
|
||||
Console.WriteLine($"已从{client.Id}接收到信息:{mes}");
|
||||
return EasyTask.CompletedTask;
|
||||
};
|
||||
await service.StartAsync(7789);//启动
|
||||
```
|
||||
|
||||
#### TcpClient
|
||||
|
||||
```
|
||||
TcpClient tcpClient = new TcpClient();
|
||||
tcpClient.Connected = (client, e) => {return EasyTask.CompletedTask; };//成功连接到服务器
|
||||
tcpClient.Disconnected = (client, e) => {return EasyTask.CompletedTask; };//从服务器断开连接,当连接不成功时不会触发。
|
||||
tcpClient.Received = (client, e) =>
|
||||
{
|
||||
//从服务器收到信息
|
||||
string mes = e.ByteBlock.Span.ToString(Encoding.UTF8);
|
||||
Console.WriteLine($"接收到信息:{mes}");
|
||||
return EasyTask.CompletedTask;
|
||||
};
|
||||
|
||||
await tcpClient.ConnectAsync("127.0.0.1:7789");
|
||||
await tcpClient.SendAsync("Hello");
|
||||
```
|
||||
|
||||
#### TcpClient 断线重连
|
||||
|
||||
在Config的插件配置中,使用重连插件即可。
|
||||
|
||||
```
|
||||
.ConfigurePlugins(a=>
|
||||
{
|
||||
a.UseReconnection(5, true, 1000);
|
||||
});
|
||||
```
|
||||
|
||||
#### FixedHeaderPackageAdapter包模式
|
||||
|
||||
该适配器主要解决Tcp粘分包问题,数据格式采用简单而高效的“包头+数据体”的模式,其中包头支持:
|
||||
|
||||
- Byte模式(1+n),一次性最大接收255字节的数据。
|
||||
- Ushort模式(2+n),一次最大接收65535字节。
|
||||
- Int模式(4+n),一次最大接收2G数据。
|
||||
|
||||
以上数据头均采用TouchSocketBitConverter的默认端模式(小端模式),使用者可以根据需求切换默认端模式。
|
||||
|
||||
```
|
||||
TouchSocketBitConverter.DefaultEndianType = EndianType.Little;
|
||||
```
|
||||
|
||||
#### CustomFixedHeaderDataHandlingAdapter
|
||||
|
||||
用户自定义固定包头适配器,主要帮助用户解决具有固定包头的数据帧信息。例如:下列数据格式,仅需要实现几个接口,就能完成解析,详细操作请参照API。
|
||||
|
||||
|1|1|1|**********|
|
||||
|
||||
#### CustomUnfixedHeaderDataHandlingAdapter
|
||||
|
||||
用户自定义不固定包头适配器,主要帮助用户解决具有包头不固定的数据帧信息。例如:最典型的HTTP数据包,其数据头和数据体由“\r\n”隔开,而数据头又因为请求者的请求信息的不同,头部数据也不固定,而数据体的长度,也是由数据头的ContentLength的值显式指定的,所以可以考虑使用CustomUnfixedHeaderDataHandlingAdapter解析,也是仅通过简单的开发,就能实现。
|
||||
|
||||
***
|
||||
|
||||
## 致谢
|
||||
|
||||
谢谢大家对TouchSocket的支持,如果还有其他问题,请提交Issue,或者加群QQ:[234762506](https://jq.qq.com/?_wv=1027&k=gN7UL4fw)讨论。
|
||||
|
||||
感谢下列工具软件的支持
|
||||
|
||||
- [Visual Studio](https://visualstudio.microsoft.com/zh-hans/)
|
||||
- [JetBrains](https://www.jetbrains.com/)
|
||||
- [Visual Studio Code](https://code.visualstudio.com/)
|
||||
|
||||
## 支持作者
|
||||
|
||||
- [打赏支持](https://touchsocket.net/docs/current/donate)
|
||||
- [Pro支持](https://touchsocket.net/docs/current/enterprise)
|
||||
|
||||
## 特别声明
|
||||
|
||||
TouchSocket项目已加入[dotNET China](https://gitee.com/dotnetchina) 组织。
|
||||
|
||||
|
||||

|
||||
|
||||
140
benchmark/Benchmark/BenchmarkAccess.cs
Normal file
140
benchmark/Benchmark/BenchmarkAccess.cs
Normal file
@@ -0,0 +1,140 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if NET8_0_OR_GREATER
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace BenchmarkConsoleApp.Benchmark
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net80)]
|
||||
[MemoryDiagnoser]
|
||||
public class BenchmarkAccess : BenchmarkBase
|
||||
{
|
||||
public static readonly A TestInstance = new();
|
||||
public static readonly Action<A, int> SetDelegate;
|
||||
public static readonly Func<A, int> GetDelegate;
|
||||
public static readonly PropertyInfo ValueProperty; public static readonly MethodInfo SetValueMethod; public static readonly MethodInfo GetValueMethod;
|
||||
|
||||
public static readonly Func<A, int> GetValueExpressionFunc; public static readonly Action<A, int> SetValueExpressionAction; static BenchmarkAccess()
|
||||
{
|
||||
TestInstance = new(); ValueProperty = typeof(A).GetProperty("Value");
|
||||
SetValueMethod = ValueProperty.GetSetMethod();
|
||||
GetValueMethod = ValueProperty.GetGetMethod();
|
||||
SetDelegate = CreateSetDelegate();
|
||||
GetDelegate = CreateGetDelegate();
|
||||
GetValueExpressionFunc = CreateGetValueExpressionFunc();
|
||||
SetValueExpressionAction = CreateSetValueExpressionAction();
|
||||
}
|
||||
|
||||
[UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_Value")]
|
||||
private static extern int GetValueUnsafe(A a);
|
||||
|
||||
[UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_Value")]
|
||||
private static extern void SetValueUnsafe(A a, int value);
|
||||
|
||||
[Benchmark]
|
||||
public void UnsafeAccessor()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
SetValueUnsafe(TestInstance, 10);
|
||||
var value = GetValueUnsafe(TestInstance);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Reflection()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
SetValueMethod.Invoke(TestInstance, new object[] { 10 });
|
||||
var value = GetValueMethod.Invoke(TestInstance, new object[] { });
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Emit()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
SetDelegate(TestInstance, 10);
|
||||
var value = GetDelegate(TestInstance);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void ExpressionTrees()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
SetValueExpressionAction(TestInstance, 10);
|
||||
var value = GetValueExpressionFunc(TestInstance);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Direct()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
TestInstance.Value = 10;
|
||||
var value = TestInstance.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private static Action<A, int> CreateSetDelegate()
|
||||
{
|
||||
var dynamicMethod = new DynamicMethod("SetValue", null, new[] { typeof(A), typeof(int) }, typeof(A)); var ilGenerator = dynamicMethod.GetILGenerator();
|
||||
ilGenerator.Emit(OpCodes.Ldarg_0);
|
||||
ilGenerator.Emit(OpCodes.Ldarg_1);
|
||||
ilGenerator.EmitCall(OpCodes.Call, SetValueMethod, null); ilGenerator.Emit(OpCodes.Ret);
|
||||
return (Action<A, int>)dynamicMethod.CreateDelegate(typeof(Action<A, int>));
|
||||
}
|
||||
|
||||
private static Func<A, int> CreateGetDelegate()
|
||||
{
|
||||
var dynamicMethod = new DynamicMethod("GetValue", typeof(int), new[] { typeof(A) }, typeof(A));
|
||||
var ilGenerator = dynamicMethod.GetILGenerator();
|
||||
ilGenerator.Emit(OpCodes.Ldarg_0);
|
||||
ilGenerator.EmitCall(OpCodes.Call, GetValueMethod, null); ilGenerator.Emit(OpCodes.Ret);
|
||||
return (Func<A, int>)dynamicMethod.CreateDelegate(typeof(Func<A, int>));
|
||||
}
|
||||
|
||||
private static Func<A, int> CreateGetValueExpressionFunc()
|
||||
{
|
||||
var instance = Expression.Parameter(typeof(A), "instance");
|
||||
var getValueExpression = Expression.Lambda<Func<A, int>>(Expression.Property(instance, ValueProperty), instance); return getValueExpression.Compile();
|
||||
}
|
||||
|
||||
private static Action<A, int> CreateSetValueExpressionAction()
|
||||
{
|
||||
var instance = Expression.Parameter(typeof(A), "instance");
|
||||
var value = Expression.Parameter(typeof(int), "value");
|
||||
var setValueExpression = Expression.Lambda<Action<A, int>>(Expression.Call(instance, ValueProperty.GetSetMethod(true), value), instance, value);
|
||||
return setValueExpression.Compile();
|
||||
}
|
||||
}
|
||||
|
||||
public class A
|
||||
{
|
||||
public int Value { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
30
benchmark/Benchmark/BenchmarkAdapter.cs
Normal file
30
benchmark/Benchmark/BenchmarkAdapter.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace BenchmarkConsoleApp.Benchmark
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net461)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[MemoryDiagnoser]
|
||||
public class BenchmarkAdapter : BenchmarkBase
|
||||
{
|
||||
[Benchmark]
|
||||
public void FixedHeaderPackageAdapter()
|
||||
{
|
||||
var adapter = new FixedHeaderPackageAdapter();
|
||||
}
|
||||
}
|
||||
}
|
||||
19
benchmark/Benchmark/BenchmarkBase.cs
Normal file
19
benchmark/Benchmark/BenchmarkBase.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace BenchmarkConsoleApp.Benchmark
|
||||
{
|
||||
public class BenchmarkBase
|
||||
{
|
||||
public int Count { get; set; } = 10000;
|
||||
}
|
||||
}
|
||||
170
benchmark/Benchmark/BenchmarkBytePool.cs
Normal file
170
benchmark/Benchmark/BenchmarkBytePool.cs
Normal file
@@ -0,0 +1,170 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using System;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace BenchmarkConsoleApp.Benchmark
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net461)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
//[SimpleJob(RuntimeMoniker.Net70)]
|
||||
//[SimpleJob(RuntimeMoniker.Net80)]
|
||||
[MemoryDiagnoser]
|
||||
public class BenchmarkBytePool : BenchmarkBase
|
||||
{
|
||||
public BenchmarkBytePool()
|
||||
{
|
||||
//this.Count = 1000000;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void CreateByteBlock()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
using (var byteBlock = new ByteBlock(1024 * 64))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void CreateValueByteBlock()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
using (var byteBlock = new ValueByteBlock(1024 * 64))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void CreateArrayPoolBytes()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var bytes = System.Buffers.ArrayPool<byte>.Shared.Rent(1024 * 64);
|
||||
System.Buffers.ArrayPool<byte>.Shared.Return(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void CreateBytePoolBytes()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var bytes = BytePool.Default.Rent(1024 * 64);
|
||||
BytePool.Default.Return(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RunByteBlock()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)//10000
|
||||
{
|
||||
using (var byteBlock = new ByteBlock(1024 * 64))
|
||||
{
|
||||
byteBlock.Write(10);
|
||||
byteBlock.Write((byte)1);
|
||||
byteBlock.Write(10.0d);
|
||||
byteBlock.Write('I');
|
||||
byteBlock.Write("love");
|
||||
byteBlock.Write(DateTime.Now);
|
||||
byteBlock.Write(DateTime.Now.TimeOfDay);
|
||||
byteBlock.SeekToStart();
|
||||
byteBlock.ReadInt32();
|
||||
byteBlock.ReadByte();
|
||||
byteBlock.ReadDouble();
|
||||
byteBlock.ReadChar();
|
||||
byteBlock.ReadString();
|
||||
byteBlock.ReadDateTime();
|
||||
byteBlock.ReadTimeSpan();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RunValueByteBlock()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
using (var byteBlock = new ValueByteBlock(1024 * 64))
|
||||
{
|
||||
byteBlock.Write(10);
|
||||
byteBlock.Write((byte)1);
|
||||
byteBlock.Write(10.0d);
|
||||
byteBlock.Write('I');
|
||||
byteBlock.Write("love");
|
||||
byteBlock.Write(DateTime.Now);
|
||||
byteBlock.Write(DateTime.Now.TimeOfDay);
|
||||
byteBlock.SeekToStart();
|
||||
byteBlock.ReadInt32();
|
||||
byteBlock.ReadByte();
|
||||
byteBlock.ReadDouble();
|
||||
byteBlock.ReadChar();
|
||||
byteBlock.ReadString();
|
||||
byteBlock.ReadDateTime();
|
||||
byteBlock.ReadTimeSpan();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RunByteBlockRef()
|
||||
{
|
||||
using (var byteBlock = new ByteBlock(1024 * 64))
|
||||
{
|
||||
var a = 0;
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
ByteBlockRef(byteBlock, ref a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ByteBlockRef(ByteBlock valueByteBlock, ref int a)
|
||||
{
|
||||
if (a++ > 10000)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ByteBlockRef(valueByteBlock, ref a);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RunValueByteBlockRef()
|
||||
{
|
||||
using (var byteBlock = new ValueByteBlock(1024 * 64))
|
||||
{
|
||||
var a = 0;
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
ValueByteBlockRef(byteBlock, ref a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValueByteBlockRef(ValueByteBlock valueByteBlock, ref int a)
|
||||
{
|
||||
if (a++ > 10000)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ValueByteBlockRef(valueByteBlock, ref a);
|
||||
}
|
||||
}
|
||||
}
|
||||
119
benchmark/Benchmark/BenchmarkContainer.cs
Normal file
119
benchmark/Benchmark/BenchmarkContainer.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace BenchmarkConsoleApp.Benchmark
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net461)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[SimpleJob(RuntimeMoniker.Net70)]
|
||||
[SimpleJob(RuntimeMoniker.Net80)]
|
||||
[MemoryDiagnoser]
|
||||
public class BenchmarkContainer : BenchmarkBase
|
||||
{
|
||||
public BenchmarkContainer()
|
||||
{
|
||||
this.m_touchIoc.RegisterSingleton<MySingletonClass>();
|
||||
this.m_touchIoc.RegisterTransient<MyTransientClass>();
|
||||
|
||||
this.m_services.AddSingleton<MySingletonClass>();
|
||||
this.m_services.AddTransient<MyTransientClass>();
|
||||
|
||||
this.m_serviceProvider = this.m_services.BuildServiceProvider();
|
||||
this.m_container = new MyContainer();
|
||||
}
|
||||
|
||||
private readonly Container m_touchIoc = new Container();
|
||||
private readonly ServiceCollection m_services = new ServiceCollection();
|
||||
private readonly IServiceProvider m_serviceProvider;
|
||||
private readonly MyContainer m_container;
|
||||
|
||||
[Benchmark]
|
||||
public void TouchCreateSingleton()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var ins = this.m_touchIoc.Resolve(typeof(MySingletonClass));
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void AspCreateSingleton()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var ins = this.m_serviceProvider.GetService(typeof(MySingletonClass));
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TouchCreateSingletonForGenerator()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var ins = this.m_container.Resolve(typeof(MySingletonClass));
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TouchCreateTransient()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var ins = this.m_touchIoc.Resolve(typeof(MyTransientClass));
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void AspCreateTransient()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var ins = this.m_serviceProvider.GetService(typeof(MyTransientClass));
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TouchCreateTransientForGenerator()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var ins = this.m_container.Resolve(typeof(MyTransientClass));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[AddSingletonInject(typeof(MySingletonClass))]
|
||||
[GeneratorContainer]
|
||||
internal sealed partial class MyContainer : ManualContainer
|
||||
{
|
||||
}
|
||||
|
||||
[AutoInjectForSingleton]
|
||||
public class MySingletonClass
|
||||
{
|
||||
public int MyProperty { get; set; }
|
||||
public string MyProperty2 { get; set; }
|
||||
}
|
||||
|
||||
[AutoInjectForTransient]
|
||||
public class MyTransientClass
|
||||
{
|
||||
public int MyProperty { get; set; }
|
||||
public string MyProperty2 { get; set; }
|
||||
}
|
||||
}
|
||||
191
benchmark/Benchmark/BenchmarkDependencyObject.cs
Normal file
191
benchmark/Benchmark/BenchmarkDependencyObject.cs
Normal file
@@ -0,0 +1,191 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace BenchmarkConsoleApp.Benchmark
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net461)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[SimpleJob(RuntimeMoniker.Net70)]
|
||||
[SimpleJob(RuntimeMoniker.Net80)]
|
||||
[MemoryDiagnoser]
|
||||
public class BenchmarkDependencyObject : BenchmarkBase
|
||||
{
|
||||
[Benchmark]
|
||||
public void RunDependencyIntTryGetValue()
|
||||
{
|
||||
var myClassObject = new MyClassObject();
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var a = myClassObject.TryGetValue(MyClassObject.Obj2PropertyProperty, out var ob);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RunDependencyIntGet()
|
||||
{
|
||||
var myClassObject = new MyClassObject();
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var a = myClassObject.Int2Property;
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RunDependencyIntSet()
|
||||
{
|
||||
var myClassObject = new MyClassObject();
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
myClassObject.Int2Property = i;
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RunDependencyObjGet()
|
||||
{
|
||||
var myClassObject = new MyClassObject();
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var a = myClassObject.Obj2Property;
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RunDependencyObjSet()
|
||||
{
|
||||
var myClassObject = new MyClassObject();
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
myClassObject.Obj2Property = new object();
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RunIntGet()
|
||||
{
|
||||
var myClassObject = new MyClassObject();
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var a = myClassObject.IntProperty;
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RunIntSet()
|
||||
{
|
||||
var myClassObject = new MyClassObject();
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
myClassObject.IntProperty = i;
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RunObjGet()
|
||||
{
|
||||
var myClassObject = new MyClassObject();
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var a = myClassObject.ObjProperty;
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RunObjSet()
|
||||
{
|
||||
var myClassObject = new MyClassObject();
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
myClassObject.ObjProperty = new object();
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RunCreateDependencyObject()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var myClassObject = new MyClassObject();
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RunNormalObj()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var disposableObject = new DisposableObject();
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RunObj()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var disposableObject = new Obj();
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RunExpandoObject()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var disposableObject = new ExpandoObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class Obj : DisposableObject
|
||||
{
|
||||
private Dictionary<string, string> m_keyValuePairs = new Dictionary<string, string>();
|
||||
public int Int2Property { get; set; }
|
||||
|
||||
public int IntProperty { get; set; }
|
||||
|
||||
public object Obj2Property { get; set; }
|
||||
|
||||
public object ObjProperty { get; set; }
|
||||
}
|
||||
|
||||
internal class MyClassObject : DependencyObject
|
||||
{
|
||||
public static readonly DependencyProperty<int> Int2PropertyProperty =
|
||||
DependencyProperty<int>.Register("Int2Property", 0);
|
||||
|
||||
public static readonly DependencyProperty<object> Obj2PropertyProperty =
|
||||
DependencyProperty<object>.Register("ObjProperty", null);
|
||||
|
||||
public int Int2Property
|
||||
{
|
||||
get { return (int)this.GetValue(Int2PropertyProperty); }
|
||||
set { this.SetValue(Int2PropertyProperty, value); }
|
||||
}
|
||||
|
||||
public int IntProperty { get; set; }
|
||||
|
||||
public object Obj2Property
|
||||
{
|
||||
get { return (object)this.GetValue(Obj2PropertyProperty); }
|
||||
set { this.SetValue(Obj2PropertyProperty, value); }
|
||||
}
|
||||
|
||||
public object ObjProperty { get; set; }
|
||||
}
|
||||
}
|
||||
37
benchmark/Benchmark/BenchmarkHttpServerAdapter.cs
Normal file
37
benchmark/Benchmark/BenchmarkHttpServerAdapter.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace BenchmarkConsoleApp.Benchmark
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net461)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[SimpleJob(RuntimeMoniker.Net70)]
|
||||
[SimpleJob(RuntimeMoniker.Net80)]
|
||||
[MemoryDiagnoser]
|
||||
public class BenchmarkHttpServerAdapter : BenchmarkBase
|
||||
{
|
||||
[Benchmark]
|
||||
public void CreateByteBlock()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
using (var byteBlock = new ByteBlock(1024 * 64))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
185
benchmark/Benchmark/BenchmarkInvokeMethod.cs
Normal file
185
benchmark/Benchmark/BenchmarkInvokeMethod.cs
Normal file
@@ -0,0 +1,185 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using System;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace BenchmarkConsoleApp.Benchmark
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net461)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[SimpleJob(RuntimeMoniker.Net70)]
|
||||
[SimpleJob(RuntimeMoniker.Net80)]
|
||||
[MemoryDiagnoser]
|
||||
public class BenchmarkInvokeMethod : BenchmarkBase
|
||||
{
|
||||
public BenchmarkInvokeMethod()
|
||||
{
|
||||
this.m_method = new Method(typeof(MyClass2).GetMethod("Add"));
|
||||
|
||||
var AddStatic = typeof(MyClass2).GetProperty("AddStaticAction");
|
||||
|
||||
this.m_myClass3 = new MyClass3();
|
||||
|
||||
this.m_myClass3.Func = (Func<object, object[], object>)AddStatic.GetValue(null);
|
||||
}
|
||||
|
||||
private MyClass3 m_myClass3;
|
||||
private Method m_method;
|
||||
|
||||
[Benchmark]
|
||||
public void DirectRun()
|
||||
{
|
||||
var myClass1 = new MyClass1();
|
||||
|
||||
var a = 10;
|
||||
var b = 20;
|
||||
var c = 0;
|
||||
|
||||
var objects = new object[] { a, b, c };
|
||||
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
myClass1.Add(a, b, out c);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void MethodILRun()
|
||||
{
|
||||
var myClass2 = new MyClass2();
|
||||
|
||||
var a = 10;
|
||||
var b = 20;
|
||||
var c = 0;
|
||||
|
||||
var objects = new object[] { a, b, c };
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
this.m_method.Invoke(myClass2, objects);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void MethodInfoRun()
|
||||
{
|
||||
var myClass2 = new MyClass2();
|
||||
|
||||
var a = 10;
|
||||
var b = 20;
|
||||
var c = 0;
|
||||
|
||||
var objects = new object[] { a, b, c };
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
this.m_method.Info.Invoke(myClass2, objects);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void StaticMethodRun1()
|
||||
{
|
||||
var myClass1 = new MyClass1();
|
||||
|
||||
var a = 10;
|
||||
var b = 20;
|
||||
var c = 0;
|
||||
|
||||
var objects = new object[] { a, b, c };
|
||||
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
MyClass1.AddStatic(myClass1, a, b, out c);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void StaticMethodRun2()
|
||||
{
|
||||
var myClass2 = new MyClass2();
|
||||
|
||||
var a = 10;
|
||||
var b = 20;
|
||||
var c = 0;
|
||||
|
||||
var objects = new object[] { a, b, c };
|
||||
|
||||
Func<object, object[], object> action = MyClass2.AddStatic;
|
||||
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
action.Invoke(myClass2, objects);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void StaticMethodRun3()
|
||||
{
|
||||
var myClass2 = new MyClass2();
|
||||
|
||||
var a = 10;
|
||||
var b = 20;
|
||||
var c = 0;
|
||||
|
||||
var objects = new object[] { a, b, c };
|
||||
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
this.m_myClass3.Func.Invoke(myClass2, objects);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class MyClass1
|
||||
{
|
||||
//这个是被调用函数。
|
||||
public int Add(int a, int b, out int sum)
|
||||
{
|
||||
sum = a + b;
|
||||
return a + b;
|
||||
}
|
||||
|
||||
public static int AddStatic(MyClass1 myClass, int a, int b, out int sum)
|
||||
{
|
||||
var result = myClass.Add(a, b, out sum);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class MyClass2
|
||||
{
|
||||
public static Func<object, object[], object> AddStaticAction => AddStatic;
|
||||
|
||||
public static object AddStatic(object myClass, object[] ps)
|
||||
{
|
||||
var a = (int)ps[0];
|
||||
var b = (int)ps[1];
|
||||
var result = ((MyClass2)myClass).Add(a, b, out var c);
|
||||
ps[2] = c;
|
||||
return result;
|
||||
}
|
||||
|
||||
//这个是被调用函数。
|
||||
public int Add(int a, int b, out int sum)
|
||||
{
|
||||
sum = a + b;
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
|
||||
private class MyClass3
|
||||
{
|
||||
public Func<object, object[], object> Func;
|
||||
}
|
||||
}
|
||||
}
|
||||
80
benchmark/Benchmark/BenchmarkLazy.cs
Normal file
80
benchmark/Benchmark/BenchmarkLazy.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using System;
|
||||
|
||||
namespace BenchmarkConsoleApp.Benchmark
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net461)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[SimpleJob(RuntimeMoniker.Net70)]
|
||||
[SimpleJob(RuntimeMoniker.Net80)]
|
||||
[MemoryDiagnoser]
|
||||
public class BenchmarkLazy : BenchmarkBase
|
||||
{
|
||||
public BenchmarkLazy()
|
||||
{
|
||||
this.Count = 1000000;
|
||||
}
|
||||
|
||||
private Lazy<MyBenchmarkLazyClass> m_lazyClass;
|
||||
private MyBenchmarkLazyClass m_class;
|
||||
private MyBenchmarkLazyClass m_class1;
|
||||
|
||||
[Benchmark]
|
||||
public void Lazy()
|
||||
{
|
||||
this.m_lazyClass = new Lazy<MyBenchmarkLazyClass>(() => new MyBenchmarkLazyClass());
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var v = this.m_lazyClass.Value;
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Direct()
|
||||
{
|
||||
this.m_class = new MyBenchmarkLazyClass();
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var v = this.m_class;
|
||||
}
|
||||
}
|
||||
|
||||
private object root = new object();
|
||||
|
||||
[Benchmark]
|
||||
public void DirectLock()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
if (this.m_class1 == null)
|
||||
{
|
||||
lock (this.root)
|
||||
{
|
||||
if (this.m_class1 != null)
|
||||
{
|
||||
this.m_class1 = new MyBenchmarkLazyClass();
|
||||
}
|
||||
}
|
||||
}
|
||||
var v = this.m_class1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class MyBenchmarkLazyClass
|
||||
{
|
||||
}
|
||||
}
|
||||
53
benchmark/Benchmark/BenchmarkList.cs
Normal file
53
benchmark/Benchmark/BenchmarkList.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BenchmarkConsoleApp.Benchmark
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net461)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[SimpleJob(RuntimeMoniker.Net70)]
|
||||
[SimpleJob(RuntimeMoniker.Net80)]
|
||||
[MemoryDiagnoser]
|
||||
public class BenchmarkList : BenchmarkBase
|
||||
{
|
||||
[Benchmark]
|
||||
public void ListAdd()
|
||||
{
|
||||
var myListClass = new MyListClass();
|
||||
var list = new List<MyListClass>();
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
list.Add(myListClass);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void ArrayListAdd()
|
||||
{
|
||||
var myListClass = new MyListClass();
|
||||
var list = new ArrayList();
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
list.Add(myListClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class MyListClass
|
||||
{
|
||||
}
|
||||
}
|
||||
133
benchmark/Benchmark/BenchmarkNewCreateObject.cs
Normal file
133
benchmark/Benchmark/BenchmarkNewCreateObject.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace BenchmarkConsoleApp.Benchmark
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net461)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[SimpleJob(RuntimeMoniker.Net70)]
|
||||
[SimpleJob(RuntimeMoniker.Net80)]
|
||||
[MemoryDiagnoser]
|
||||
public class BenchmarkNewCreateObject : BenchmarkBase
|
||||
{
|
||||
[Benchmark]
|
||||
public void NewCreate()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var myClass = new MyClass();
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void ExpressionsCreate()
|
||||
{
|
||||
var type = typeof(MyClass);
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var myClass = (MyClass)CreateInstance.CreateInstanceByExpression(type);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TCreate()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var myClass = CreateInstance.Create<MyClass>();
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void ActivatorCreate()
|
||||
{
|
||||
var type = typeof(MyClass);
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var myClass = (MyClass)Activator.CreateInstance(type);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void ActivatorCreateStringList()
|
||||
{
|
||||
var type = typeof(string);
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
this.GoType(type);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void NewCreateStringList()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
this.GoT<string>();
|
||||
}
|
||||
}
|
||||
|
||||
private object GoType(Type type)
|
||||
{
|
||||
var list = Activator.CreateInstance(typeof(List<>).MakeGenericType(type)) as IList;
|
||||
return list;
|
||||
}
|
||||
|
||||
private List<T> GoT<T>()
|
||||
{
|
||||
var t = new List<T>();
|
||||
return t;
|
||||
}
|
||||
|
||||
private class MyClass
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static class CreateInstance
|
||||
{
|
||||
private static Hashtable paramCache = Hashtable.Synchronized(new Hashtable());//缓存
|
||||
|
||||
/// <summary>
|
||||
/// 根据对象类型创建对象实例
|
||||
/// </summary>
|
||||
/// <param name="key">对象类型</param>
|
||||
/// <returns></returns>
|
||||
public static object CreateInstanceByExpression(Type key)
|
||||
{
|
||||
var value = (Func<object>)paramCache[key];
|
||||
if (value == null)
|
||||
{
|
||||
value = CreateInstanceByType(key);
|
||||
paramCache[key] = value;
|
||||
}
|
||||
return value();
|
||||
}
|
||||
|
||||
private static Func<object> CreateInstanceByType(Type type)
|
||||
{
|
||||
return Expression.Lambda<Func<object>>(Expression.New(type), null).Compile();
|
||||
}
|
||||
|
||||
public static T Create<T>() where T : new()
|
||||
{
|
||||
return new T();
|
||||
}
|
||||
}
|
||||
}
|
||||
181
benchmark/Benchmark/BenchmarkPluginsManager.cs
Normal file
181
benchmark/Benchmark/BenchmarkPluginsManager.cs
Normal file
@@ -0,0 +1,181 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace BenchmarkConsoleApp.Benchmark
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net461)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[SimpleJob(RuntimeMoniker.Net70)]
|
||||
[SimpleJob(RuntimeMoniker.Net80)]
|
||||
[MemoryDiagnoser]
|
||||
public class BenchmarkPluginManager : BenchmarkBase
|
||||
{
|
||||
private IPluginManager m_plugins1;
|
||||
private IPluginManager m_plugins2;
|
||||
private Method method;
|
||||
private int plugCount = 10;
|
||||
private MethodInfo m_methodInfo;
|
||||
|
||||
public BenchmarkPluginManager()
|
||||
{
|
||||
//this.Count = 1;
|
||||
|
||||
this.m_plugins1 = new PluginManager(new Container())
|
||||
{
|
||||
Enable = true
|
||||
};
|
||||
for (var i = 0; i < this.plugCount; i++)
|
||||
{
|
||||
this.m_plugins1.Add(new MyPlugin());
|
||||
}
|
||||
|
||||
this.m_plugins2 = new PluginManager(new Container())
|
||||
{
|
||||
Enable = true
|
||||
};
|
||||
for (var i = 0; i < this.plugCount; i++)
|
||||
{
|
||||
this.m_plugins2.Add(nameof(MyPlugin.Test), this.Test);
|
||||
}
|
||||
this.m_methodInfo = typeof(MyPlugin).GetMethod("Test");
|
||||
this.method = new Method(this.m_methodInfo);
|
||||
}
|
||||
|
||||
private Task Test(object sender, PluginEventArgs e)
|
||||
{
|
||||
return e.InvokeNext();
|
||||
}
|
||||
|
||||
private interface MyPluginInterface : IPlugin
|
||||
{
|
||||
Task Test(object sender, PluginEventArgs e);
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true)]
|
||||
public void DirectRun()
|
||||
{
|
||||
var myPlugin = new MyPlugin();
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var sender = new object();
|
||||
var e = new PluginEventArgs();
|
||||
var ps = new object[] { sender, e };
|
||||
for (var j = 0; j < this.plugCount; j++)
|
||||
{
|
||||
myPlugin.Test(sender, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void ActionRun()
|
||||
{
|
||||
var myPlugin = new MyPlugin();
|
||||
Func<object, PluginEventArgs, Task> func = myPlugin.Test;
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var sender = new object();
|
||||
var e = new PluginEventArgs();
|
||||
var ps = new object[] { sender, e };
|
||||
for (var j = 0; j < this.plugCount; j++)
|
||||
{
|
||||
func.Invoke(sender, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void MethodInfoRun()
|
||||
{
|
||||
var myPlugin = new MyPlugin();
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var sender = new object();
|
||||
var e = new PluginEventArgs();
|
||||
var ps = new object[] { sender, e };
|
||||
for (var j = 0; j < this.plugCount; j++)
|
||||
{
|
||||
this.m_methodInfo.Invoke(myPlugin, ps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void ExpressionRun()
|
||||
{
|
||||
var myPlugin = new MyPlugin();
|
||||
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var sender = new object();
|
||||
var e = new PluginEventArgs();
|
||||
var ps = new object[] { sender, e };
|
||||
for (var j = 0; j < this.plugCount; j++)
|
||||
{
|
||||
this.method.Invoke(myPlugin, sender, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void PluginRun()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
this.m_plugins1.Raise(nameof(MyPluginInterface.Test), new object(), new PluginEventArgs());
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void PluginActionRun()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
this.m_plugins2.Raise(nameof(MyPluginInterface.Test), new object(), new PluginEventArgs());
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public async Task PluginActionRunAsync()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
await this.m_plugins2.RaiseAsync(nameof(MyPluginInterface.Test), new object(), new PluginEventArgs());
|
||||
}
|
||||
}
|
||||
|
||||
private class MyPlugin : MyPluginInterface
|
||||
{
|
||||
public int Order { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public void Loaded(IPluginManager pluginManager)
|
||||
{
|
||||
}
|
||||
|
||||
public Task Test(object sender, PluginEventArgs e)
|
||||
{
|
||||
return e.InvokeNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
107
benchmark/Benchmark/BenchmarkRefParameter.cs
Normal file
107
benchmark/Benchmark/BenchmarkRefParameter.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
|
||||
namespace BenchmarkConsoleApp.Benchmark
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net461)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[SimpleJob(RuntimeMoniker.Net70)]
|
||||
[SimpleJob(RuntimeMoniker.Net80)]
|
||||
[MemoryDiagnoser]
|
||||
public class BenchmarkRefParameter : BenchmarkBase
|
||||
{
|
||||
private string m_str = "123123123xssdfsdfsdfdsfdsfsdfl;l123123ol12312lk3m123m12312k312ko312oki312k312k312k31k23k13k12312k3k;123123l123l1212;3;123;12;'3;'123;12;312;3;123;12;312;312;312;312;312;3";
|
||||
private int m_intValue = int.MaxValue;
|
||||
private long m_longValue = long.MaxValue;
|
||||
|
||||
[Benchmark]
|
||||
public void StringParameter()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
this.PrivateStringParameter(this.m_str);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RefStringParameter()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
this.PrivateStringInParameter(this.m_str);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void IntParameter()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
this.PrivateIntParameter(this.m_intValue);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RefIntParameter()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
this.PrivateIntInParameter(this.m_intValue);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void LongPameter()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
this.PrivateLongParameter(this.m_longValue);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RefLongParameter()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
this.PrivateLongInParameter(this.m_longValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void PrivateStringParameter(string value)
|
||||
{
|
||||
}
|
||||
|
||||
private void PrivateStringInParameter(in string value)
|
||||
{
|
||||
}
|
||||
|
||||
private void PrivateIntParameter(int value)
|
||||
{
|
||||
}
|
||||
|
||||
private void PrivateIntInParameter(in int value)
|
||||
{
|
||||
}
|
||||
|
||||
private void PrivateLongParameter(long value)
|
||||
{
|
||||
}
|
||||
|
||||
private void PrivateLongInParameter(in long value)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
168
benchmark/Benchmark/BenchmarkSerialization.cs
Normal file
168
benchmark/Benchmark/BenchmarkSerialization.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using MemoryPack;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace BenchmarkConsoleApp.Benchmark
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net461)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[SimpleJob(RuntimeMoniker.Net70)]
|
||||
[SimpleJob(RuntimeMoniker.Net80)]
|
||||
[MemoryDiagnoser]
|
||||
public class BenchmarkSerialization : BenchmarkBase
|
||||
{
|
||||
[Benchmark(Baseline = true)]
|
||||
public void DirectNew()
|
||||
{
|
||||
var v = new MyPackPerson { Age = 40, Name = "John" };
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var bytes = new byte[8];//开辟小内存模拟序列化
|
||||
var val = new MyPackPerson { Age = 40, Name = "John" };//直接新建,模拟反序列化
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void MemoryPack()
|
||||
{
|
||||
var v = new MemoryPackPerson { Age = 40, Name = "John" };
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var bin = MemoryPackSerializer.Serialize(v);
|
||||
var val = MemoryPackSerializer.Deserialize<MemoryPackPerson>(bin);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TouchPack_1()
|
||||
{
|
||||
var v = new MyPackPerson { Age = 40, Name = "John" };
|
||||
|
||||
using (var byteBlock = new ByteBlock())
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
byteBlock.Reset();//避免ByteBlock的创建
|
||||
byteBlock.WritePackage(v);//序列化
|
||||
byteBlock.Seek(0);
|
||||
var val = byteBlock.ReadPackage<MyPackPerson>();//反序列化
|
||||
}
|
||||
}
|
||||
}
|
||||
[Benchmark]
|
||||
public void TouchPack_2()
|
||||
{
|
||||
var v = new MyPackPerson { Age = 40, Name = "John" };
|
||||
using (var byteBlock = new ByteBlock())
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
byteBlock.Reset();//避免ByteBlock的创建
|
||||
byteBlock.WritePackage(v);//序列化
|
||||
byteBlock.Seek(0);
|
||||
var myPackPerson = new MyPackPerson();
|
||||
myPackPerson.Unpackage(byteBlock);//反序列化
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void NewtonsoftJson()
|
||||
{
|
||||
var v = new MyPackPerson { Age = 40, Name = "John" };
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var str = Newtonsoft.Json.JsonConvert.SerializeObject(v);
|
||||
var val = Newtonsoft.Json.JsonConvert.DeserializeObject<MyPackPerson>(str);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void SystemTextJson()
|
||||
{
|
||||
var v = new MyPackPerson { Age = 40, Name = "John" };
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var str = System.Text.Json.JsonSerializer.Serialize(v);
|
||||
var val = System.Text.Json.JsonSerializer.Deserialize<MyPackPerson>(str);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void FastBinarySerialize()
|
||||
{
|
||||
var v = new MyPackPerson { Age = 40, Name = "John" };
|
||||
using (var byteBlock = new ByteBlock())
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
byteBlock.Reset();
|
||||
SerializeConvert.FastBinarySerialize(byteBlock, v);
|
||||
byteBlock.Seek(0);
|
||||
var val = SerializeConvert.FastBinaryDeserialize<MyPackPerson>(byteBlock.Buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void SystemBinarySerialize()
|
||||
{
|
||||
var v = new MyPackPerson { Age = 40, Name = "John" };
|
||||
using (var byteBlock = new ByteBlock())
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
byteBlock.Reset();
|
||||
SerializeConvert.BinarySerialize(byteBlock, v);
|
||||
byteBlock.Seek(0);
|
||||
var val = SerializeConvert.BinaryDeserialize<MyPackPerson>(byteBlock.Buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
public partial class MemoryPackPerson
|
||||
{
|
||||
public int Age { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
|
||||
[Serializable]
|
||||
public class MyPackPerson : PackageBase
|
||||
{
|
||||
public int Age { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
public override void Package(in ByteBlock byteBlock)
|
||||
{
|
||||
byteBlock.Write(this.Age);
|
||||
byteBlock.Write(this.Name);
|
||||
}
|
||||
|
||||
public override void Unpackage(in ByteBlock byteBlock)
|
||||
{
|
||||
this.Age = byteBlock.ReadInt32();
|
||||
this.Name = byteBlock.ReadString();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
198
benchmark/Benchmark/BenchmarkSerializationComposite.cs
Normal file
198
benchmark/Benchmark/BenchmarkSerializationComposite.cs
Normal file
@@ -0,0 +1,198 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace BenchmarkConsoleApp.Benchmark
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net461)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[SimpleJob(RuntimeMoniker.Net70)]
|
||||
[SimpleJob(RuntimeMoniker.Net80)]
|
||||
[MemoryDiagnoser]
|
||||
public class BenchmarkSerializationComposite : BenchmarkBase
|
||||
{
|
||||
public BenchmarkSerializationComposite()
|
||||
{
|
||||
this.Count = 10;
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true)]
|
||||
public void DirectNew()
|
||||
{
|
||||
var v = this.GetStudent();
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var bytes = new byte[80 * 1024];//开辟小内存模拟序列化
|
||||
var val = this.GetStudent();//直接新建,模拟反序列化
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void NewtonsoftJson()
|
||||
{
|
||||
var v = this.GetStudent();
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var str = Newtonsoft.Json.JsonConvert.SerializeObject(v);
|
||||
var val = Newtonsoft.Json.JsonConvert.DeserializeObject<Student>(str);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void SystemTextJson()
|
||||
{
|
||||
var v = this.GetStudent();
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var str = System.Text.Json.JsonSerializer.Serialize(v);
|
||||
var val = System.Text.Json.JsonSerializer.Deserialize<Student>(str);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void FastBinarySerialize()
|
||||
{
|
||||
var v = this.GetStudent();
|
||||
using (var byteBlock = new ByteBlock(1024 * 512))
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
byteBlock.Reset();
|
||||
SerializeConvert.FastBinarySerialize(byteBlock, v);
|
||||
byteBlock.Seek(0);
|
||||
var val = SerializeConvert.FastBinaryDeserialize<Student>(byteBlock.Buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void SystemBinarySerialize()
|
||||
{
|
||||
var v = this.GetStudent();
|
||||
using (var byteBlock = new ByteBlock(1024 * 512))
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
byteBlock.Reset();
|
||||
SerializeConvert.BinarySerialize(byteBlock, v);
|
||||
byteBlock.Seek(0);
|
||||
var val = SerializeConvert.BinaryDeserialize<Student>(byteBlock.Buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Student GetStudent()
|
||||
{
|
||||
var student = new Student();
|
||||
student.P1 = 10;
|
||||
student.P2 = "若汝棋茗";
|
||||
student.P3 = 100;
|
||||
student.P4 = 0;
|
||||
student.P5 = DateTime.Now;
|
||||
student.P6 = 10;
|
||||
student.P7 = new byte[1024 * 64];
|
||||
|
||||
var random = new Random();
|
||||
random.NextBytes(student.P7);
|
||||
|
||||
student.List1 = new List<int>();
|
||||
student.List1.Add(1);
|
||||
student.List1.Add(2);
|
||||
student.List1.Add(3);
|
||||
|
||||
student.List2 = new List<string>();
|
||||
student.List2.Add("1");
|
||||
student.List2.Add("2");
|
||||
student.List2.Add("3");
|
||||
|
||||
student.List3 = new List<byte[]>();
|
||||
student.List3.Add(new byte[1024]);
|
||||
student.List3.Add(new byte[1024]);
|
||||
student.List3.Add(new byte[1024]);
|
||||
|
||||
student.Dic1 = new Dictionary<int, int>();
|
||||
student.Dic1.Add(1, 1);
|
||||
student.Dic1.Add(2, 2);
|
||||
student.Dic1.Add(3, 3);
|
||||
|
||||
student.Dic2 = new Dictionary<int, string>();
|
||||
student.Dic2.Add(1, "1");
|
||||
student.Dic2.Add(2, "2");
|
||||
student.Dic2.Add(3, "3");
|
||||
|
||||
student.Dic3 = new Dictionary<string, string>();
|
||||
student.Dic3.Add("1", "1");
|
||||
student.Dic3.Add("2", "2");
|
||||
student.Dic3.Add("3", "3");
|
||||
|
||||
student.Dic4 = new Dictionary<int, Arg>();
|
||||
student.Dic4.Add(1, new Arg(1));
|
||||
student.Dic4.Add(2, new Arg(2));
|
||||
student.Dic4.Add(3, new Arg(3));
|
||||
|
||||
return student;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Student
|
||||
{
|
||||
public int P1 { get; set; }
|
||||
public string P2 { get; set; }
|
||||
public long P3 { get; set; }
|
||||
public byte P4 { get; set; }
|
||||
public DateTime P5 { get; set; }
|
||||
public double P6 { get; set; }
|
||||
public byte[] P7 { get; set; }
|
||||
|
||||
public List<int> List1 { get; set; }
|
||||
public List<string> List2 { get; set; }
|
||||
public List<byte[]> List3 { get; set; }
|
||||
|
||||
public Dictionary<int, int> Dic1 { get; set; }
|
||||
public Dictionary<int, string> Dic2 { get; set; }
|
||||
public Dictionary<string, string> Dic3 { get; set; }
|
||||
public Dictionary<int, Arg> Dic4 { get; set; }
|
||||
}
|
||||
[Serializable]
|
||||
public class Arg
|
||||
{
|
||||
public Arg(int myProperty)
|
||||
{
|
||||
this.MyProperty = myProperty;
|
||||
}
|
||||
|
||||
public Arg()
|
||||
{
|
||||
var person = new Person();
|
||||
person.Name = "张三";
|
||||
person.Age = 18;
|
||||
}
|
||||
|
||||
public int MyProperty { get; set; }
|
||||
}
|
||||
[Serializable]
|
||||
public class Person
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public int Age { get; set; }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
125
benchmark/Benchmark/BenchmarkStringSwitch.cs
Normal file
125
benchmark/Benchmark/BenchmarkStringSwitch.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using System;
|
||||
|
||||
namespace BenchmarkConsoleApp.Benchmark
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net461)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[SimpleJob(RuntimeMoniker.Net70)]
|
||||
[SimpleJob(RuntimeMoniker.Net80)]
|
||||
[MemoryDiagnoser]
|
||||
public class BenchmarkStringSwitch : BenchmarkBase
|
||||
{
|
||||
private string ss = "asdasd>AKJINMD.asdsads12313.ADSa";
|
||||
|
||||
[Benchmark]
|
||||
public void Switch()
|
||||
{
|
||||
var a = this.GetString();
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
switch (a)
|
||||
{
|
||||
case "asdasd>AKJINMD.asdsads12313.ADSa1":
|
||||
{
|
||||
break;
|
||||
}
|
||||
case "asdasd>AKJINMD.asdsads12313.AD":
|
||||
{
|
||||
break;
|
||||
}
|
||||
case "asdasd>AKJINMD.asdsads12313.ADSa":
|
||||
{
|
||||
break;
|
||||
}
|
||||
case "assasdasdd>AKJINMD.asdsads12313.AD":
|
||||
{
|
||||
break;
|
||||
}
|
||||
case "assasdasdd>AKJINMD.asdsasdaads12313.AD":
|
||||
{
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void IF()
|
||||
{
|
||||
var a = this.GetString();
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
if (a == "asdasd>AKJINMD.asdsads12313.ADSa1")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (a == "asdasd>AKJINMD.asdsads12313.AD")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (a == "asdasd>AKJINMD.asdsads12313.ADSa")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (a == "assasdasdd>AKJINMD.asdsads12313.AD")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (a == "assasdasdd>AKJINMD.asdsasdaads12313.AD")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void IFEquals()
|
||||
{
|
||||
var a = this.GetString();
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
if (a.Equals("asdasd>AKJINMD.asdsads12313.ADSa1", StringComparison.Ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (a.Equals("asdasd>AKJINMD.asdsads12313.AD", StringComparison.Ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (a.Equals("asdasd>AKJINMD.asdsads12313.ADSa", StringComparison.Ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (a.Equals("assasdasdd>AKJINMD.asdsads12313.AD", StringComparison.Ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (a.Equals("assasdasdd>AKJINMD.asdsasdaads12313.AD", StringComparison.Ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetString()
|
||||
{
|
||||
return "asdasd>AKJINMD.asdsads12313.ADSa";
|
||||
}
|
||||
}
|
||||
}
|
||||
323
benchmark/Benchmark/BenchmarkStructSerialization.cs
Normal file
323
benchmark/Benchmark/BenchmarkStructSerialization.cs
Normal file
@@ -0,0 +1,323 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using MemoryPack;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace BenchmarkConsoleApp.Benchmark
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[SimpleJob(RuntimeMoniker.Net70)]
|
||||
[MemoryDiagnoser]
|
||||
public class BenchmarkStructSerialization : BenchmarkBase
|
||||
{
|
||||
[Benchmark(Baseline = true)]
|
||||
public void DirectNew()
|
||||
{
|
||||
var v = new MemoryPackTagAccountInfo
|
||||
{
|
||||
Account = "123",
|
||||
Email = "505554090@qq.com",
|
||||
Header = 10,
|
||||
Password = "abc",
|
||||
Phone = "123456789",
|
||||
ReadId = "id"
|
||||
};
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var bytes = new byte[50];//开辟小内存模拟序列化
|
||||
var val = new MemoryPackTagAccountInfo
|
||||
{
|
||||
Account = "123",
|
||||
Email = "505554090@qq.com",
|
||||
Header = 10,
|
||||
Password = "abc",
|
||||
Phone = "123456789",
|
||||
ReadId = "id"
|
||||
};//直接新建,模拟反序列化
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void MemoryPack()
|
||||
{
|
||||
var v = new MemoryPackTagAccountInfo
|
||||
{
|
||||
Account = "123",
|
||||
Email = "505554090@qq.com",
|
||||
Header = 10,
|
||||
Password = "abc",
|
||||
Phone = "123456789",
|
||||
ReadId = "id"
|
||||
};
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var bin = MemoryPackSerializer.Serialize(v);
|
||||
var val = MemoryPackSerializer.Deserialize<MemoryPackTagAccountInfo>(bin);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TouchPack()
|
||||
{
|
||||
var v = new TagAccountInfo
|
||||
{
|
||||
Account = "123",
|
||||
Email = "505554090@qq.com",
|
||||
Header = 10,
|
||||
Password = "abc",
|
||||
Phone = "123456789",
|
||||
ReadId = "id"
|
||||
};
|
||||
using (var byteBlock = new ByteBlock())
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
byteBlock.Reset();//避免ByteBlock的创建
|
||||
v.Package(byteBlock);//序列化
|
||||
byteBlock.Seek(0);
|
||||
var val = new TagAccountInfo();
|
||||
val.Unpackage(byteBlock);//反序列化
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void FastBinary()
|
||||
{
|
||||
var v = new TagAccountInfo
|
||||
{
|
||||
Account = "123",
|
||||
Email = "505554090@qq.com",
|
||||
Header = 10,
|
||||
Password = "abc",
|
||||
Phone = "123456789",
|
||||
ReadId = "id"
|
||||
};
|
||||
using (var byteBlock = new ByteBlock())
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
byteBlock.Reset();//避免ByteBlock的创建
|
||||
SerializeConvert.FastBinarySerialize(byteBlock, v);//序列化
|
||||
byteBlock.Seek(0);
|
||||
var val = SerializeConvert.FastBinaryDeserialize<TagAccountInfo>(byteBlock.Buffer);//反序列化
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void NewtonsoftJson()
|
||||
{
|
||||
var v = new TagAccountInfo
|
||||
{
|
||||
Account = "123",
|
||||
Email = "505554090@qq.com",
|
||||
Header = 10,
|
||||
Password = "abc",
|
||||
Phone = "123456789",
|
||||
ReadId = "id"
|
||||
};
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var str = Newtonsoft.Json.JsonConvert.SerializeObject(v);
|
||||
var val = Newtonsoft.Json.JsonConvert.DeserializeObject<TagAccountInfo>(str);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void SafeStructSerialize()
|
||||
{
|
||||
var v = new TagAccountInfo
|
||||
{
|
||||
Account = "123",
|
||||
Email = "505554090@qq.com",
|
||||
Header = 10,
|
||||
Password = "abc",
|
||||
Phone = "123456789",
|
||||
ReadId = "id"
|
||||
};
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var bytes = StuctToBytes(v);
|
||||
var val = BytesToStuct<TagAccountInfo>(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Benchmark]
|
||||
public unsafe void UnsafeStructSerialize()
|
||||
{
|
||||
var v = new TagAccountInfo
|
||||
{
|
||||
Account = "123",
|
||||
Email = "505554090@qq.com",
|
||||
Header = 10,
|
||||
Password = "abc",
|
||||
Phone = "123456789",
|
||||
ReadId = "id"
|
||||
};
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
var array = ClassToBytes(v);
|
||||
fixed (byte* pArray = array)
|
||||
{
|
||||
var val = BytesToClass<TagAccountInfo>(pArray, array.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 结构体转byte数组
|
||||
/// </summary>
|
||||
/// <param name="structObj">要转换的结构体</param>
|
||||
/// <returns>转换后的byte数组</returns>
|
||||
public static byte[] StuctToBytes<T>(T structObj) where T : struct
|
||||
{
|
||||
// if ("Byte[]" == structObj.GetType().Name)
|
||||
// return (byte[])structObj;
|
||||
// else if ("String" == structObj.GetType().Name)
|
||||
// return Encoding.GetEncoding("gb2312").GetBytes((string)structObj);
|
||||
|
||||
//得到结构体的大小
|
||||
var size = Marshal.SizeOf(structObj);
|
||||
//创建byte数组
|
||||
var bytes = new byte[size];
|
||||
//分配结构体大小的内存空间
|
||||
var structPtr = Marshal.AllocHGlobal(size);
|
||||
//将结构体拷到分配好的内存空间
|
||||
Marshal.StructureToPtr(structObj, structPtr, false);
|
||||
//从内存空间拷到byte数组
|
||||
Marshal.Copy(structPtr, bytes, 0, size);
|
||||
//释放内存空间
|
||||
Marshal.FreeHGlobal(structPtr);
|
||||
//返回byte数组
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// byte数组转结构体
|
||||
/// </summary>
|
||||
/// <param name="bytes">byte数组</param>
|
||||
/// <param name="type">结构体类型</param>
|
||||
/// <returns>转换后的结构体</returns>
|
||||
public static T BytesToStuct<T>(byte[] bytes) where T : struct
|
||||
{
|
||||
//得到结构体的大小
|
||||
var size = Marshal.SizeOf<T>();
|
||||
//byte数组长度小于结构体的大小
|
||||
if (size > bytes.Length)
|
||||
{
|
||||
//返回空
|
||||
#pragma warning disable CS8603 // 可能返回 null 引用。
|
||||
return default;
|
||||
#pragma warning restore CS8603 // 可能返回 null 引用。
|
||||
}
|
||||
//分配结构体大小的内存空间
|
||||
var structPtr = Marshal.AllocHGlobal(size);
|
||||
//将byte数组拷到分配好的内存空间
|
||||
Marshal.Copy(bytes, 0, structPtr, size);
|
||||
//将内存空间转换为目标结构体
|
||||
object obj = Marshal.PtrToStructure<T>(structPtr);
|
||||
//释放内存空间
|
||||
Marshal.FreeHGlobal(structPtr);
|
||||
//返回结构体
|
||||
#pragma warning disable CS8603 // 可能返回 null 引用。
|
||||
return (T)obj;
|
||||
#pragma warning restore CS8603 // 可能返回 null 引用。
|
||||
}
|
||||
|
||||
public static unsafe byte[] ClassToBytes(object obj)
|
||||
{
|
||||
var typeSize = Marshal.SizeOf(obj);
|
||||
var array = new byte[typeSize];
|
||||
fixed (byte* pArray = array)
|
||||
Marshal.StructureToPtr(obj, (IntPtr)pArray, false);
|
||||
return array;
|
||||
}
|
||||
|
||||
public static unsafe T BytesToClass<T>(byte* pArray, int length)
|
||||
{
|
||||
var type = typeof(T);
|
||||
var typeSize = Marshal.SizeOf(type);
|
||||
if (typeSize > length)
|
||||
return default;
|
||||
|
||||
return (T)Marshal.PtrToStructure((IntPtr)pArray, type);
|
||||
}
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
|
||||
public partial struct MemoryPackTagAccountInfo //0x04
|
||||
{
|
||||
public byte Header;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
|
||||
public string Account;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
|
||||
public string Password;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
|
||||
public string ReadId;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
|
||||
public string Phone;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)]
|
||||
public string Email;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
|
||||
public struct TagAccountInfo : IPackage //0x04
|
||||
{
|
||||
public byte Header;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
|
||||
public string Account;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
|
||||
public string Password;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
|
||||
public string ReadId;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
|
||||
public string Phone;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)]
|
||||
public string Email;
|
||||
|
||||
public void Package(in ByteBlock byteBlock)
|
||||
{
|
||||
byteBlock.Write(this.Header);
|
||||
byteBlock.Write(this.Account);
|
||||
byteBlock.Write(this.Password);
|
||||
byteBlock.Write(this.ReadId);
|
||||
byteBlock.Write(this.Phone);
|
||||
byteBlock.Write(this.Email);
|
||||
}
|
||||
|
||||
public void Unpackage(in ByteBlock byteBlock)
|
||||
{
|
||||
this.Header = (byte)byteBlock.ReadByte();
|
||||
this.Account = byteBlock.ReadString();
|
||||
this.Password = byteBlock.ReadString();
|
||||
this.ReadId = byteBlock.ReadString();
|
||||
this.Phone = byteBlock.ReadString();
|
||||
this.Email = byteBlock.ReadString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
67
benchmark/Benchmark/BenchmarkType.cs
Normal file
67
benchmark/Benchmark/BenchmarkType.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using System;
|
||||
|
||||
namespace BenchmarkConsoleApp.Benchmark
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net461)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[SimpleJob(RuntimeMoniker.Net70)]
|
||||
[SimpleJob(RuntimeMoniker.Net80)]
|
||||
[MemoryDiagnoser]
|
||||
public class BenchmarkType : BenchmarkBase
|
||||
{
|
||||
[Benchmark]
|
||||
public void RunTypeof()
|
||||
{
|
||||
var type = this.GetTypeOne();
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
if (type.FullName == typeof(BenchmarkType).FullName)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RunGetType()
|
||||
{
|
||||
var type = this.GetTypeOne();
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
if (type.FullName == this.GetType().FullName)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RunTypeString()
|
||||
{
|
||||
var type = this.GetTypeOne();
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
if (type.FullName == "BenchmarkConsoleApp.Benchmark.BenchmarkType")
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Type GetTypeOne()
|
||||
{
|
||||
return typeof(BenchmarkType);
|
||||
}
|
||||
}
|
||||
}
|
||||
71
benchmark/Benchmark/BenchmarkValueEnumerator.cs
Normal file
71
benchmark/Benchmark/BenchmarkValueEnumerator.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace BenchmarkConsoleApp.Benchmark
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[SimpleJob(RuntimeMoniker.Net70)]
|
||||
[MemoryDiagnoser]
|
||||
public class BenchmarkValueEnumerator : BenchmarkBase
|
||||
{
|
||||
private int[] list;
|
||||
public BenchmarkValueEnumerator()
|
||||
{
|
||||
this.list = Enumerable.Range(0, 10000).ToArray();
|
||||
}
|
||||
[Benchmark]
|
||||
public void RunFor()
|
||||
{
|
||||
for (var j = 0; j < this.Count; j++)
|
||||
{
|
||||
for (var i = 0; i < this.list.Length; i++)
|
||||
{
|
||||
var value = this.list[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RunEnumerator()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
foreach (var item in this.list)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void RunRangeEnumerator()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
foreach (var item in this.list[0..this.list.Length])
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
17
benchmark/BenchmarkConsoleApp.csproj
Normal file
17
benchmark/BenchmarkConsoleApp.csproj
Normal file
@@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net6.0;net461;net7.0;net8.0</TargetFrameworks>
|
||||
<LangVersion>11.0</LangVersion>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.12" />
|
||||
<PackageReference Include="MemoryPack" Version="1.10.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageReference Include="TouchSocketPro" Version="2.0.0-beta.258" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
25
benchmark/BenchmarkConsoleApp.sln
Normal file
25
benchmark/BenchmarkConsoleApp.sln
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.7.34009.444
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BenchmarkConsoleApp", "BenchmarkConsoleApp.csproj", "{B7F4BC07-5E95-47D8-BBB5-E707289049A7}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{B7F4BC07-5E95-47D8-BBB5-E707289049A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B7F4BC07-5E95-47D8-BBB5-E707289049A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B7F4BC07-5E95-47D8-BBB5-E707289049A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B7F4BC07-5E95-47D8-BBB5-E707289049A7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {AA6BD673-862A-414D-9598-DAE66B56552C}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
56
benchmark/Program.cs
Normal file
56
benchmark/Program.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
using BenchmarkConsoleApp.Benchmark;
|
||||
using BenchmarkDotNet.Running;
|
||||
using System;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace BenchmarkConsoleApp
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var consoleAction = new ConsoleAction();
|
||||
|
||||
consoleAction.Add("0", "全部", () => { BenchmarkRunner.Run(typeof(Program).Assembly); });
|
||||
consoleAction.Add("1", "内存池", () => { BenchmarkRunner.Run<BenchmarkBytePool>(); });
|
||||
consoleAction.Add("2", "PluginManager", () => { BenchmarkRunner.Run<BenchmarkPluginManager>(); });
|
||||
consoleAction.Add("3", "BenchmarkType", () => { BenchmarkRunner.Run<BenchmarkType>(); });
|
||||
#if NET6_0_OR_GREATER
|
||||
consoleAction.Add("4", "BenchmarkValueEnumerator", () => { BenchmarkRunner.Run<BenchmarkValueEnumerator>(); });
|
||||
#endif
|
||||
consoleAction.Add("5", "CreateObject", () => { BenchmarkRunner.Run<BenchmarkNewCreateObject>(); });
|
||||
consoleAction.Add("6", "BenchmarkList", () => { BenchmarkRunner.Run<BenchmarkList>(); });
|
||||
#if NET6_0_OR_GREATER
|
||||
consoleAction.Add("7", "序列化", () => { BenchmarkRunner.Run<BenchmarkSerialization>(); });
|
||||
consoleAction.Add("8", "结构体序列化", () => { BenchmarkRunner.Run<BenchmarkStructSerialization>(); });
|
||||
consoleAction.Add("9", "复杂序列化测试", () => { BenchmarkRunner.Run<BenchmarkSerializationComposite>(); });
|
||||
#endif
|
||||
consoleAction.Add("10", "容器创建测试", () => { BenchmarkRunner.Run<BenchmarkContainer>(); });
|
||||
consoleAction.Add("11", "依赖属性性能", () => { BenchmarkRunner.Run<BenchmarkDependencyObject>(); });
|
||||
consoleAction.Add("12", "方法调用", () => { BenchmarkRunner.Run<BenchmarkInvokeMethod>(); });
|
||||
consoleAction.Add("13", "Ref传递", () => { BenchmarkRunner.Run<BenchmarkRefParameter>(); });
|
||||
consoleAction.Add("14", "Lazy测试", () => { BenchmarkRunner.Run<BenchmarkLazy>(); });
|
||||
consoleAction.Add("15", "字符串匹配", () => { BenchmarkRunner.Run<BenchmarkStringSwitch>(); });
|
||||
#if NET8_0_OR_GREATER
|
||||
consoleAction.Add("16", "不安全私有访问", () => { BenchmarkRunner.Run<BenchmarkAccess>(); });
|
||||
#endif
|
||||
consoleAction.ShowAll();
|
||||
while (true)
|
||||
{
|
||||
consoleAction.Run(Console.ReadLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
71
examples-aot/AotExamples.sln
Normal file
71
examples-aot/AotExamples.sln
Normal file
@@ -0,0 +1,71 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.7.34031.279
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tcp", "Tcp", "{F95587FB-433F-4D36-AD1F-B59B88A645C6}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TcpConsoleApp", "Tcp\TcpConsoleApp\TcpConsoleApp.csproj", "{4E9607D7-48F7-426E-89D4-2BA0B4FAFAB4}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Rpc", "Rpc", "{835AD087-6FDC-47DC-B5C4-0850F91B577D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DmtpRpcConsoleApp", "Rpc\DmtpRpcConsoleApp\DmtpRpcConsoleApp.csproj", "{5F28E40F-FCAF-4BDA-A0C1-854057E7422B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DmtpRpcPerformanceConsoleApp", "Rpc\DmtpRpcPerformanceConsoleApp\DmtpRpcPerformanceConsoleApp.csproj", "{867CECFB-6E54-4FD0-8F50-FD3A3E7666F8}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{1C5264FB-E4D3-474E-BD8B-BC59F2786C07}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastBinaryFormatterConsoleApp", "Core\FastBinaryFormatterConsoleApp\FastBinaryFormatterConsoleApp.csproj", "{8BF2B719-0B9C-43F4-9EA5-32A19DBC9998}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebApi", "WebApi", "{75D559D7-F52D-4065-8072-3970811D04CE}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApiConsoleApp", "WebApi\WebApiConsoleApp\WebApiConsoleApp.csproj", "{E4E96E75-C1C2-47C7-B48D-9E2C647D6E09}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PluginConsoleApp", "Core\PluginConsoleApp\PluginConsoleApp.csproj", "{F2D96B93-23AD-41CC-9E3F-B22B0AA94A77}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{4E9607D7-48F7-426E-89D4-2BA0B4FAFAB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4E9607D7-48F7-426E-89D4-2BA0B4FAFAB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4E9607D7-48F7-426E-89D4-2BA0B4FAFAB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4E9607D7-48F7-426E-89D4-2BA0B4FAFAB4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5F28E40F-FCAF-4BDA-A0C1-854057E7422B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5F28E40F-FCAF-4BDA-A0C1-854057E7422B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5F28E40F-FCAF-4BDA-A0C1-854057E7422B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5F28E40F-FCAF-4BDA-A0C1-854057E7422B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{867CECFB-6E54-4FD0-8F50-FD3A3E7666F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{867CECFB-6E54-4FD0-8F50-FD3A3E7666F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{867CECFB-6E54-4FD0-8F50-FD3A3E7666F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{867CECFB-6E54-4FD0-8F50-FD3A3E7666F8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8BF2B719-0B9C-43F4-9EA5-32A19DBC9998}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8BF2B719-0B9C-43F4-9EA5-32A19DBC9998}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8BF2B719-0B9C-43F4-9EA5-32A19DBC9998}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8BF2B719-0B9C-43F4-9EA5-32A19DBC9998}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E4E96E75-C1C2-47C7-B48D-9E2C647D6E09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E4E96E75-C1C2-47C7-B48D-9E2C647D6E09}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E4E96E75-C1C2-47C7-B48D-9E2C647D6E09}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E4E96E75-C1C2-47C7-B48D-9E2C647D6E09}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F2D96B93-23AD-41CC-9E3F-B22B0AA94A77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F2D96B93-23AD-41CC-9E3F-B22B0AA94A77}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F2D96B93-23AD-41CC-9E3F-B22B0AA94A77}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F2D96B93-23AD-41CC-9E3F-B22B0AA94A77}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{4E9607D7-48F7-426E-89D4-2BA0B4FAFAB4} = {F95587FB-433F-4D36-AD1F-B59B88A645C6}
|
||||
{5F28E40F-FCAF-4BDA-A0C1-854057E7422B} = {835AD087-6FDC-47DC-B5C4-0850F91B577D}
|
||||
{867CECFB-6E54-4FD0-8F50-FD3A3E7666F8} = {835AD087-6FDC-47DC-B5C4-0850F91B577D}
|
||||
{8BF2B719-0B9C-43F4-9EA5-32A19DBC9998} = {1C5264FB-E4D3-474E-BD8B-BC59F2786C07}
|
||||
{E4E96E75-C1C2-47C7-B48D-9E2C647D6E09} = {75D559D7-F52D-4065-8072-3970811D04CE}
|
||||
{F2D96B93-23AD-41CC-9E3F-B22B0AA94A77} = {1C5264FB-E4D3-474E-BD8B-BC59F2786C07}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {D63AFF97-3A68-43E1-92DA-A19E6889439C}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,22 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<PublishAot>true</PublishAot>
|
||||
<InvariantGlobalization>true</InvariantGlobalization>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="TouchSocket.Core" Version="3.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--<ItemGroup>
|
||||
<Reference Include="TouchSocket.Core">
|
||||
<HintPath>..\..\..\..\TouchSocketPro\Build\Debug\net6.0\TouchSocket.Core.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>-->
|
||||
|
||||
</Project>
|
||||
178
examples-aot/Core/FastBinaryFormatterConsoleApp/Program.cs
Normal file
178
examples-aot/Core/FastBinaryFormatterConsoleApp/Program.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
using System.Diagnostics;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace FastBinaryFormatterConsoleApp
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
//未完成
|
||||
//SerializeConvert.AddFastBinary<Arg>();
|
||||
//SerializeConvert.AddFastBinary<List<int>>();
|
||||
//SerializeConvert.AddFastBinary<List<string>>();
|
||||
//SerializeConvert.AddFastBinary<List<byte[]>>();
|
||||
//SerializeConvert.AddFastBinary<Dictionary<int,int>>();
|
||||
//SerializeConvert.AddFastBinary<Dictionary<int,string>>();
|
||||
//SerializeConvert.AddFastBinary<Dictionary<string,string>>();
|
||||
//SerializeConvert.AddFastBinary<Dictionary<int,Arg>>();
|
||||
|
||||
GlobalEnvironment.DynamicBuilderType = DynamicBuilderType.Reflect;
|
||||
ShouldSerializeObjBeOk();
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
public static void ShouldSerializeObjBeOk()
|
||||
{
|
||||
var student = new Student();
|
||||
student.P1 = 10;
|
||||
student.P2 = "若汝棋茗";
|
||||
student.P3 = 100;
|
||||
student.P4 = 0;
|
||||
student.P5 = DateTime.Now;
|
||||
student.P6 = 10;
|
||||
student.P7 = new byte[1024 * 64];
|
||||
student.P8 = new string[] { "I", "love", "you" };
|
||||
|
||||
var random = new Random();
|
||||
random.NextBytes(student.P7);
|
||||
|
||||
student.List1 = new List<int>();
|
||||
student.List1.Add(1);
|
||||
student.List1.Add(2);
|
||||
student.List1.Add(3);
|
||||
|
||||
student.List2 = new List<string>();
|
||||
student.List2.Add("1");
|
||||
student.List2.Add("2");
|
||||
student.List2.Add("3");
|
||||
|
||||
student.List3 = new List<byte[]>();
|
||||
student.List3.Add(new byte[1024]);
|
||||
student.List3.Add(new byte[1024]);
|
||||
student.List3.Add(new byte[1024]);
|
||||
|
||||
student.Dic1 = new Dictionary<int, int>();
|
||||
student.Dic1.Add(1, 1);
|
||||
student.Dic1.Add(2, 2);
|
||||
student.Dic1.Add(3, 3);
|
||||
|
||||
student.Dic2 = new Dictionary<int, string>();
|
||||
student.Dic2.Add(1, "1");
|
||||
student.Dic2.Add(2, "2");
|
||||
student.Dic2.Add(3, "3");
|
||||
|
||||
student.Dic3 = new Dictionary<string, string>();
|
||||
student.Dic3.Add("1", "1");
|
||||
student.Dic3.Add("2", "2");
|
||||
student.Dic3.Add("3", "3");
|
||||
|
||||
student.Dic4 = new Dictionary<int, Arg>();
|
||||
student.Dic4.Add(1, new Arg(1));
|
||||
student.Dic4.Add(2, new Arg(2));
|
||||
student.Dic4.Add(3, new Arg(3));
|
||||
|
||||
var byteBlock = new ByteBlock(1024 * 512);
|
||||
FastBinaryFormatter.Serialize(byteBlock, student);
|
||||
|
||||
Console.WriteLine("序列化完成");
|
||||
|
||||
var newStudent = FastBinaryFormatter.Deserialize<Student>(byteBlock);
|
||||
byteBlock.Dispose();
|
||||
|
||||
Console.WriteLine(Equals(student.P1, newStudent.P1));
|
||||
Console.WriteLine(Equals(student.P2, newStudent.P2));
|
||||
Console.WriteLine(Equals(student.P3, newStudent.P3));
|
||||
Console.WriteLine(Equals(student.P4, newStudent.P4));
|
||||
Console.WriteLine(Equals(student.P5, newStudent.P5));
|
||||
Console.WriteLine(Equals(student.P6, newStudent.P6));
|
||||
|
||||
|
||||
Console.WriteLine(newStudent.P7 != null);
|
||||
Console.WriteLine(Equals(student.P7.Length, newStudent.P7.Length));
|
||||
|
||||
//Assert.NotNull(newStudent.P8);
|
||||
//Assert.Equal(student.P8.Length, newStudent.P8.Length);
|
||||
|
||||
//for (var i = 0; i < student.P8.Length; i++)
|
||||
//{
|
||||
// Assert.Equal(student.P8[i], newStudent.P8[i]);
|
||||
//}
|
||||
|
||||
//for (var i = 0; i < student.P7.Length; i++)
|
||||
//{
|
||||
// Assert.Equal(student.P7[i], newStudent.P7[i]);
|
||||
//}
|
||||
|
||||
//Assert.NotNull(newStudent.List1);
|
||||
//Assert.Equal(student.List1.Count, newStudent.List1.Count);
|
||||
//for (var i = 0; i < student.List1.Count; i++)
|
||||
//{
|
||||
// Assert.Equal(student.List1[i], newStudent.List1[i]);
|
||||
//}
|
||||
|
||||
//Assert.NotNull(newStudent.List2);
|
||||
//Assert.Equal(student.List2.Count, newStudent.List2.Count);
|
||||
//for (var i = 0; i < student.List2.Count; i++)
|
||||
//{
|
||||
// Assert.Equal(student.List2[i], newStudent.List2[i]);
|
||||
//}
|
||||
|
||||
//Assert.NotNull(newStudent.List3);
|
||||
//Assert.Equal(student.List3.Count, newStudent.List3.Count);
|
||||
//for (var i = 0; i < student.List3.Count; i++)
|
||||
//{
|
||||
// Assert.Equal(student.List3[i].Length, newStudent.List3[i].Length);
|
||||
//}
|
||||
|
||||
//Assert.NotNull(newStudent.Dic1);
|
||||
//Assert.Equal(student.Dic1.Count, newStudent.Dic1.Count);
|
||||
|
||||
//Assert.NotNull(newStudent.Dic2);
|
||||
//Assert.Equal(student.Dic2.Count, newStudent.Dic2.Count);
|
||||
|
||||
//Assert.NotNull(newStudent.Dic3);
|
||||
//Assert.Equal(student.Dic3.Count, newStudent.Dic3.Count);
|
||||
|
||||
//Assert.NotNull(newStudent.Dic4);
|
||||
//Assert.Equal(student.Dic4.Count, newStudent.Dic4.Count);
|
||||
}
|
||||
}
|
||||
|
||||
public class Arg
|
||||
{
|
||||
public Arg()
|
||||
{
|
||||
}
|
||||
|
||||
public Arg(int myProperty)
|
||||
{
|
||||
this.MyProperty = myProperty;
|
||||
}
|
||||
|
||||
public int MyProperty { get; set; }
|
||||
}
|
||||
|
||||
public class Student
|
||||
{
|
||||
public int P1 { get; set; }
|
||||
public string P2 { get; set; }
|
||||
public long P3 { get; set; }
|
||||
public byte P4 { get; set; }
|
||||
public DateTime P5 { get; set; }
|
||||
public double P6 { get; set; }
|
||||
public byte[] P7 { get; set; }
|
||||
public string[] P8 { get; set; }
|
||||
|
||||
public List<int> List1 { get; set; }
|
||||
public List<string> List2 { get; set; }
|
||||
public List<byte[]> List3 { get; set; }
|
||||
|
||||
public Dictionary<int, int> Dic1 { get; set; }
|
||||
public Dictionary<int, string> Dic2 { get; set; }
|
||||
public Dictionary<string, string> Dic3 { get; set; }
|
||||
public Dictionary<int, Arg> Dic4 { get; set; }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
13
examples-aot/Core/PluginConsoleApp/PluginConsoleApp.csproj
Normal file
13
examples-aot/Core/PluginConsoleApp/PluginConsoleApp.csproj
Normal file
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<PublishAot>true</PublishAot>
|
||||
<InvariantGlobalization>true</InvariantGlobalization>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="TouchSocket.Core" Version="3.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
226
examples-aot/Core/PluginConsoleApp/Program.cs
Normal file
226
examples-aot/Core/PluginConsoleApp/Program.cs
Normal file
@@ -0,0 +1,226 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading.Tasks;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace PluginConsoleApp
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
private static async Task Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
//Type interfacetype = typeof(ISayPlugin);
|
||||
//var interfaceMethods = interfacetype.GetMethods();
|
||||
|
||||
//Console.WriteLine("begin print interfaceMethods------");
|
||||
//foreach (var method in interfaceMethods)
|
||||
//{
|
||||
// Console.WriteLine(method.Name);
|
||||
//}
|
||||
//Console.WriteLine("end print interfaceMethods------");
|
||||
|
||||
PluginManager pluginManager = new PluginManager(new Container())
|
||||
{
|
||||
Enable = true//必须启用
|
||||
};
|
||||
|
||||
//pluginManager.UseAot();
|
||||
|
||||
//添加插件
|
||||
pluginManager.Add<SayHelloPlugin>();
|
||||
pluginManager.Add<SayHelloAction>();
|
||||
pluginManager.Add<SayHelloGenerator>();
|
||||
pluginManager.Add<SayHiPlugin>();
|
||||
pluginManager.Add<LastSayPlugin>();
|
||||
|
||||
//pluginManager.AddAot<SayHelloPlugin>();
|
||||
//pluginManager.AddAot<SayHelloAction>();
|
||||
//pluginManager.AddAot<SayHelloGenerator>();
|
||||
//pluginManager.AddAot<SayHiPlugin>();
|
||||
//pluginManager.AddAot<LastSayPlugin>();
|
||||
|
||||
|
||||
//订阅插件,不仅可以使用声明插件的方式,还可以使用委托。
|
||||
pluginManager.Add(typeof(ISayPlugin), () =>
|
||||
{
|
||||
//无参委托,一般做通知
|
||||
Console.WriteLine("在Action1中获得");
|
||||
});
|
||||
|
||||
pluginManager.Add(typeof(ISayPlugin), async (MyPluginEventArgs e) =>
|
||||
{
|
||||
//只1个指定参数,当参数是事件参数时,需要主动InvokeNext
|
||||
Console.WriteLine("在Action2中获得");
|
||||
await e.InvokeNext();
|
||||
});
|
||||
|
||||
pluginManager.Add(typeof(ISayPlugin), async (client, e) =>
|
||||
{
|
||||
//2个不指定参数,需要主动InvokeNext
|
||||
Console.WriteLine("在Action3中获得");
|
||||
await e.InvokeNext();
|
||||
});
|
||||
|
||||
pluginManager.Add(typeof(ISayPlugin), async (object client, MyPluginEventArgs e) =>
|
||||
{
|
||||
//2个指定参数,需要主动InvokeNext
|
||||
Console.WriteLine("在Action3中获得");
|
||||
await e.InvokeNext();
|
||||
});
|
||||
|
||||
while (true)
|
||||
{
|
||||
Console.WriteLine("请输入hello、helloaction、hellogenerator、hi或者其他");
|
||||
await pluginManager.RaiseAsync(typeof(ISayPlugin), new object(), new MyPluginEventArgs()
|
||||
{
|
||||
Words = Console.ReadLine()
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
}
|
||||
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
||||
|
||||
public class MyPluginEventArgs : PluginEventArgs
|
||||
{
|
||||
public string Words { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 定义一个插件接口,使其继承<see cref="IPlugin"/>
|
||||
/// </summary>
|
||||
[DynamicMethod]
|
||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.PublicMethods)]
|
||||
public interface ISayPlugin : IPlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Say。定义一个插件方法,必须遵循:
|
||||
/// 1.必须是两个参数,第一个参数可以是任意类型,一般表示触发源。第二个参数必须继承<see cref="PluginEventArgs"/>
|
||||
/// 2.返回值必须是Task。
|
||||
/// </summary>
|
||||
/// <param name="sender">触发主体</param>
|
||||
/// <param name="e">传递参数</param>
|
||||
/// <returns></returns>
|
||||
|
||||
Task Say(object sender, MyPluginEventArgs e);
|
||||
}
|
||||
|
||||
|
||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
|
||||
public partial class SayHelloPlugin : PluginBase, ISayPlugin
|
||||
{
|
||||
async Task ISayPlugin.Say(object sender, MyPluginEventArgs e)
|
||||
{
|
||||
Console.WriteLine($"{this.GetType().Name}------Enter");
|
||||
if (e.Words == "hello")
|
||||
{
|
||||
Console.WriteLine($"{this.GetType().Name}------Say");
|
||||
//当满足的时候输出,且不在调用下一个插件。
|
||||
|
||||
//亦或者设置e.Handled = true,即使调用下一个插件,也会无效
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
await e.InvokeNext();
|
||||
Console.WriteLine($"{this.GetType().Name}------Leave");
|
||||
}
|
||||
}
|
||||
|
||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
|
||||
public partial class SayHiPlugin : PluginBase, ISayPlugin
|
||||
{
|
||||
async Task ISayPlugin.Say(object sender, MyPluginEventArgs e)
|
||||
{
|
||||
Console.WriteLine($"{this.GetType().Name}------Enter");
|
||||
if (e.Words == "hi")
|
||||
{
|
||||
Console.WriteLine($"{this.GetType().Name}------Say");
|
||||
//当满足的时候输出,且不在调用下一个插件。
|
||||
|
||||
//亦或者设置e.Handled = true,即使调用下一个插件,也会无效
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
await e.InvokeNext();
|
||||
Console.WriteLine($"{this.GetType().Name}------Leave");
|
||||
}
|
||||
}
|
||||
|
||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
|
||||
internal partial class LastSayPlugin : PluginBase, ISayPlugin
|
||||
{
|
||||
async Task ISayPlugin.Say(object sender, MyPluginEventArgs e)
|
||||
{
|
||||
Console.WriteLine($"{this.GetType().Name}------Enter");
|
||||
Console.WriteLine($"您输入的{e.Words}似乎不被任何插件处理");
|
||||
await e.InvokeNext();
|
||||
Console.WriteLine($"{this.GetType().Name}------Leave");
|
||||
}
|
||||
}
|
||||
|
||||
public class SayHelloAction : PluginBase
|
||||
{
|
||||
protected override void Loaded(IPluginManager pluginManager)
|
||||
{
|
||||
base.Loaded(pluginManager);
|
||||
|
||||
//注册本地方法为委托
|
||||
pluginManager.Add<object, MyPluginEventArgs>(typeof(ISayPlugin), this.Say);
|
||||
}
|
||||
|
||||
public async Task Say(object sender, MyPluginEventArgs e)
|
||||
{
|
||||
Console.WriteLine($"{this.GetType().Name}------Enter");
|
||||
if (e.Words == "helloaction")
|
||||
{
|
||||
Console.WriteLine($"{this.GetType().Name}------Say");
|
||||
//当满足的时候输出,且不在调用下一个插件。
|
||||
|
||||
//亦或者设置e.Handled = true,即使调用下一个插件,也会无效
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
await e.InvokeNext();
|
||||
Console.WriteLine($"{this.GetType().Name}------Leave");
|
||||
}
|
||||
}
|
||||
|
||||
public partial class SayHelloGenerator : PluginBase, ISayPlugin
|
||||
{
|
||||
async Task ISayPlugin.Say(object sender, MyPluginEventArgs e)
|
||||
{
|
||||
Console.WriteLine($"{this.GetType().Name}------Enter");
|
||||
if (e.Words == "hellogenerator")
|
||||
{
|
||||
Console.WriteLine($"{this.GetType().Name}------Say");
|
||||
//当满足的时候输出,且不在调用下一个插件。
|
||||
|
||||
//亦或者设置e.Handled = true,即使调用下一个插件,也会无效
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
await e.InvokeNext();
|
||||
Console.WriteLine($"{this.GetType().Name}------Leave");
|
||||
}
|
||||
}
|
||||
}
|
||||
14
examples-aot/Rpc/DmtpRpcConsoleApp/DmtpRpcConsoleApp.csproj
Normal file
14
examples-aot/Rpc/DmtpRpcConsoleApp/DmtpRpcConsoleApp.csproj
Normal file
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<PublishAot>true</PublishAot>
|
||||
<!--<PublishTrimmed>false</PublishTrimmed>-->
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="TouchSocket.Dmtp" Version="3.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
141
examples-aot/Rpc/DmtpRpcConsoleApp/Program.cs
Normal file
141
examples-aot/Rpc/DmtpRpcConsoleApp/Program.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Dmtp;
|
||||
using TouchSocket.Dmtp.Rpc;
|
||||
using TouchSocket.Rpc;
|
||||
using TouchSocket.Rpc.Generators;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace DmtpRpcConsoleApp
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
var service = GetService();
|
||||
var client = GetClient();
|
||||
while (true)
|
||||
{
|
||||
Console.WriteLine("请输入账号和密码,用空格隔开。");
|
||||
var strs = Console.ReadLine()?.Split(" ");
|
||||
if (strs == null || strs.Length != 2)
|
||||
{
|
||||
Console.WriteLine("无效输入");
|
||||
continue;
|
||||
}
|
||||
|
||||
var result = client.GetDmtpRpcActor().Login(strs[0], strs[1]);
|
||||
Console.WriteLine($"结果:{result}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ConsoleLogger.Default.Exception(ex);
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
}
|
||||
static TcpDmtpClient GetClient()
|
||||
{
|
||||
var client = new TcpDmtpClient();
|
||||
client.Setup(new TouchSocketConfig()
|
||||
//.SetRegistrator(new MyContainer())
|
||||
.ConfigureContainer(a =>
|
||||
{
|
||||
a.AddConsoleLogger();
|
||||
})
|
||||
.SetRemoteIPHost("127.0.0.1:7789")
|
||||
.ConfigurePlugins(a =>
|
||||
{
|
||||
a.UseDmtpRpc();
|
||||
})
|
||||
.SetDmtpOption(new DmtpOption()
|
||||
{
|
||||
VerifyToken = "Rpc"
|
||||
}));
|
||||
client.Connect();
|
||||
client.Logger.Info($"客户端已连接");
|
||||
return client;
|
||||
}
|
||||
static TcpDmtpService GetService()
|
||||
{
|
||||
var service = new TcpDmtpService();
|
||||
var config = new TouchSocketConfig()//配置
|
||||
.SetListenIPHosts(7789)
|
||||
//.SetRegistrator(new MyContainer())
|
||||
.ConfigureContainer(a =>
|
||||
{
|
||||
a.AddConsoleLogger();
|
||||
a.AddRpcStore(store =>
|
||||
{
|
||||
store.RegisterServer<IMyRpcServer, MyRpcServer>();//注册服务
|
||||
});
|
||||
})
|
||||
.ConfigurePlugins(a =>
|
||||
{
|
||||
a.UseDmtpRpc();
|
||||
})
|
||||
.SetDmtpOption(new DmtpOption()
|
||||
{
|
||||
VerifyToken = "Rpc"
|
||||
});
|
||||
|
||||
service.Setup(config);
|
||||
service.Start();
|
||||
|
||||
service.Logger.Info($"{service.GetType().Name}已启动");
|
||||
return service;
|
||||
}
|
||||
}
|
||||
|
||||
#region Rpc服务
|
||||
[GeneratorRpcProxy]
|
||||
[AutoInjectForSingleton(ToType = typeof(MyRpcServer))]
|
||||
public interface IMyRpcServer : IRpcServer
|
||||
{
|
||||
[DmtpRpc(MethodInvoke =true)]
|
||||
[Description("登录")]//服务描述,在生成代理时,会变成注释。
|
||||
bool Login(string account, string password);
|
||||
}
|
||||
|
||||
public partial class MyRpcServer : IMyRpcServer
|
||||
{
|
||||
public bool Login(string account, string password)
|
||||
{
|
||||
if (account == "123" && password == "abc")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
//#region IOC
|
||||
///// <summary>
|
||||
///// IOC容器
|
||||
///// </summary>
|
||||
//[AddSingletonInject(typeof(IPluginManager), typeof(PluginManager))]
|
||||
//[AddSingletonInject(typeof(IPluginManager), typeof(PluginManager))]
|
||||
//[AddSingletonInject(typeof(ILog), typeof(LoggerGroup))]
|
||||
//[AddSingletonInject(typeof(DmtpRpcFeature))]
|
||||
//[AddSingletonInject(typeof(IRpcServerProvider), typeof(RpcServerProvider))]
|
||||
//[GeneratorContainer]
|
||||
//public partial class MyContainer : ManualContainer
|
||||
//{
|
||||
// public override bool IsRegistered(Type fromType, string key = "")
|
||||
// {
|
||||
// if (fromType == typeof(IDmtpRouteService))
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// return base.IsRegistered(fromType, key);
|
||||
// }
|
||||
//}
|
||||
//#endregion
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<PublishAot>true</PublishAot>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="TouchSocket.Dmtp" Version="3.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
188
examples-aot/Rpc/DmtpRpcPerformanceConsoleApp/Program.cs
Normal file
188
examples-aot/Rpc/DmtpRpcPerformanceConsoleApp/Program.cs
Normal file
@@ -0,0 +1,188 @@
|
||||
using System.Text;
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Dmtp;
|
||||
using TouchSocket.Dmtp.Rpc;
|
||||
using TouchSocket.Rpc;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace RpcPerformanceConsoleApp
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
var consoleAction = new ConsoleAction("h|help|?");//设置帮助命令
|
||||
consoleAction.OnException += ConsoleAction_OnException;//订阅执行异常输出
|
||||
|
||||
StartServer();
|
||||
|
||||
var count = 100000;
|
||||
|
||||
consoleAction.Add("3.1", "DmtpRpc测试Sum", () => StartSumClient(count));
|
||||
consoleAction.Add("3.2", "DmtpRpc测试GetBytes", () => StartGetBytesClient(count));
|
||||
consoleAction.Add("3.3", "DmtpRpc测试BigString", () => StartBigStringClient(count));
|
||||
|
||||
consoleAction.ShowAll();
|
||||
|
||||
await consoleAction.RunCommandLineAsync();
|
||||
}
|
||||
|
||||
public static void StartServer()
|
||||
{
|
||||
var service = new TcpDmtpService();
|
||||
var config = new TouchSocketConfig()//配置
|
||||
.SetListenIPHosts(7789)
|
||||
.ConfigureContainer(a =>
|
||||
{
|
||||
a.AddConsoleLogger();
|
||||
a.AddRpcStore(store =>
|
||||
{
|
||||
store.RegisterServer<TestController>();
|
||||
});
|
||||
})
|
||||
.ConfigurePlugins(a =>
|
||||
{
|
||||
a.UseDmtpRpc();
|
||||
})
|
||||
.SetDmtpOption(new DmtpOption()
|
||||
{
|
||||
VerifyToken = "Rpc"//设定连接口令,作用类似账号密码
|
||||
});
|
||||
|
||||
service.Setup(config);
|
||||
service.Start();
|
||||
|
||||
service.Logger.Info($"{service.GetType().Name}已启动");
|
||||
}
|
||||
|
||||
public static void StartSumClient(int count)
|
||||
{
|
||||
var client = GetClient();
|
||||
var timeSpan = TimeMeasurer.Run(() =>
|
||||
{
|
||||
var actor = client.GetDmtpRpcActor();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var rs = actor.InvokeT<int>("Sum", InvokeOption.WaitInvoke, i, i);
|
||||
if (rs != i + i)
|
||||
{
|
||||
Console.WriteLine("调用结果不一致");
|
||||
}
|
||||
if (i % 1000 == 0)
|
||||
{
|
||||
Console.WriteLine(i);
|
||||
}
|
||||
}
|
||||
});
|
||||
Console.WriteLine(timeSpan);
|
||||
}
|
||||
private static TcpDmtpClient GetClient()
|
||||
{
|
||||
var client = new TcpDmtpClient();
|
||||
client.Setup(new TouchSocketConfig()
|
||||
//.SetRegistrator(new MyContainer())
|
||||
.ConfigurePlugins(a =>
|
||||
{
|
||||
a.UseDmtpRpc();
|
||||
})
|
||||
.SetRemoteIPHost("127.0.0.1:7789")
|
||||
.SetDmtpOption(new DmtpOption()
|
||||
{
|
||||
VerifyToken = "Rpc"
|
||||
}));
|
||||
client.Connect();
|
||||
return client;
|
||||
}
|
||||
public static void StartGetBytesClient(int count)
|
||||
{
|
||||
var client = GetClient();
|
||||
var timeSpan = TimeMeasurer.Run(() =>
|
||||
{
|
||||
var actor = client.GetDmtpRpcActor();
|
||||
for (var i = 1; i < count; i++)
|
||||
{
|
||||
var rs = actor.InvokeT<byte[]>("GetBytes", InvokeOption.WaitInvoke, i);//测试10k数据
|
||||
if (rs.Length != i)
|
||||
{
|
||||
Console.WriteLine("调用结果不一致");
|
||||
}
|
||||
if (i % 1000 == 0)
|
||||
{
|
||||
Console.WriteLine(i);
|
||||
}
|
||||
}
|
||||
});
|
||||
Console.WriteLine(timeSpan);
|
||||
}
|
||||
|
||||
public static void StartBigStringClient(int count)
|
||||
{
|
||||
var client = GetClient();
|
||||
var timeSpan = TimeMeasurer.Run(() =>
|
||||
{
|
||||
var actor = client.GetDmtpRpcActor();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var rs = actor.InvokeT<string>("GetBigString", InvokeOption.WaitInvoke);
|
||||
if (i % 1000 == 0)
|
||||
{
|
||||
Console.WriteLine(i);
|
||||
}
|
||||
}
|
||||
});
|
||||
Console.WriteLine(timeSpan);
|
||||
}
|
||||
|
||||
private static void ConsoleAction_OnException(Exception ex)
|
||||
{
|
||||
ConsoleLogger.Default.Exception(ex);
|
||||
}
|
||||
}
|
||||
|
||||
[AutoInjectForSingleton]
|
||||
public partial class TestController : RpcServer
|
||||
{
|
||||
[DmtpRpc(MethodInvoke =true)]
|
||||
public int Sum(int a, int b) => a + b;
|
||||
|
||||
[DmtpRpc(MethodInvoke = true)]
|
||||
public byte[] GetBytes(int length)
|
||||
{
|
||||
return new byte[length];
|
||||
}
|
||||
|
||||
[DmtpRpc(MethodInvoke = true)]
|
||||
public string GetBigString()
|
||||
{
|
||||
var stringBuilder = new StringBuilder();
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
stringBuilder.Append("RRQM");
|
||||
}
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
#region IOC
|
||||
/// <summary>
|
||||
/// IOC容器
|
||||
/// </summary>
|
||||
[AddSingletonInject(typeof(IPluginManager), typeof(PluginManager))]
|
||||
[AddSingletonInject(typeof(ILog), typeof(LoggerGroup))]
|
||||
[AddSingletonInject(typeof(DmtpRpcFeature))]
|
||||
[AddSingletonInject(typeof(IRpcServerProvider), typeof(RpcServerProvider))]
|
||||
[AddSingletonInject(typeof(IDmtpRouteService), typeof(DmtpRouteService))]
|
||||
[GeneratorContainer]
|
||||
public partial class MyContainer : ManualContainer
|
||||
{
|
||||
public override bool IsRegistered(Type fromType, string key = "")
|
||||
{
|
||||
if (fromType == typeof(IDmtpRouteService))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.IsRegistered(fromType, key);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
174
examples-aot/Tcp/TcpConsoleApp/Program.cs
Normal file
174
examples-aot/Tcp/TcpConsoleApp/Program.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace TcpConsoleApp
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
var service = CreateService();
|
||||
var client = CreateClient();
|
||||
Console.WriteLine("输入任意内容,回车发送");
|
||||
while (true)
|
||||
{
|
||||
client.Send(Console.ReadLine());
|
||||
}
|
||||
}
|
||||
|
||||
private static TcpService CreateService()
|
||||
{
|
||||
var service = new TcpService();
|
||||
service.Setup(new TouchSocketConfig()//载入配置
|
||||
.SetListenIPHosts("tcp://127.0.0.1:7789", 7790)//同时监听两个地址
|
||||
.ConfigureContainer(a =>//容器的配置顺序应该在最前面
|
||||
{
|
||||
a.AddConsoleLogger();//添加一个控制台日志注入(注意:在maui中控制台日志不可用)
|
||||
a.RegisterSingleton(service);//将服务器以单例注入。便于插件或其他地方获取。
|
||||
})
|
||||
.ConfigurePlugins(a =>
|
||||
{
|
||||
a.Add<ClosePlugin>();
|
||||
a.Add<TcpServiceReceivedPlugin>();
|
||||
a.Add<MyServicePluginClass>();
|
||||
//a.Add();//此处可以添加插件
|
||||
}));
|
||||
service.Start();//启动
|
||||
return service;
|
||||
}
|
||||
|
||||
private static TcpClient CreateClient()
|
||||
{
|
||||
var tcpClient = new TcpClient();
|
||||
tcpClient.Received = (client, e) =>
|
||||
{
|
||||
//从服务器收到信息
|
||||
var mes = e.ByteBlock.Span.ToString(Encoding.UTF8);
|
||||
|
||||
tcpClient.Logger.Info($"客户端接收到信息:{mes}");
|
||||
return EasyTask.CompletedTask;
|
||||
};
|
||||
|
||||
//载入配置
|
||||
tcpClient.Setup(new TouchSocketConfig()
|
||||
.SetRemoteIPHost(new IPHost("127.0.0.1:7789"))
|
||||
.ConfigurePlugins(a =>
|
||||
{
|
||||
a.UseTcpReconnection()
|
||||
.UsePolling(TimeSpan.FromSeconds(1));
|
||||
})
|
||||
.ConfigureContainer(a =>
|
||||
{
|
||||
a.AddConsoleLogger();//添加一个日志注入
|
||||
}));
|
||||
tcpClient.Connect();
|
||||
tcpClient.Logger.Info("客户端成功连接");
|
||||
return tcpClient;
|
||||
}
|
||||
}
|
||||
|
||||
internal partial class MyServicePluginClass : PluginBase, IServerStartedPlugin, IServerStopedPlugin
|
||||
{
|
||||
public Task OnServerStarted(IServiceBase sender, ServiceStateEventArgs e)
|
||||
{
|
||||
if (sender is ITcpService service)
|
||||
{
|
||||
foreach (var item in service.Monitors)
|
||||
{
|
||||
ConsoleLogger.Default.Info($"iphost={item.Option.IpHost}");
|
||||
}
|
||||
}
|
||||
if (e.ServerState == ServerState.Running)
|
||||
{
|
||||
ConsoleLogger.Default.Info($"服务器成功启动");
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsoleLogger.Default.Info($"服务器启动失败,状态:{e.ServerState},异常:{e.Exception}");
|
||||
}
|
||||
return e.InvokeNext();
|
||||
}
|
||||
|
||||
public Task OnServerStoped(IServiceBase sender, ServiceStateEventArgs e)
|
||||
{
|
||||
Console.WriteLine("服务已停止");
|
||||
return e.InvokeNext();
|
||||
}
|
||||
}
|
||||
|
||||
partial class TcpServiceReceivedPlugin : PluginBase, ITcpReceivedPlugin
|
||||
{
|
||||
public async Task OnTcpReceived(ITcpSession client, ReceivedDataEventArgs e)
|
||||
{
|
||||
//从客户端收到信息
|
||||
var mes = e.ByteBlock.Span.ToString(Encoding.UTF8);
|
||||
if (mes == "close")
|
||||
{
|
||||
throw new CloseException(mes);
|
||||
}
|
||||
client.Logger.Info($"已从{client.GetIPPort()}接收到信息:{mes}");
|
||||
|
||||
if (client is not ITcpSessionClient sessionClient)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await sessionClient.SendAsync(mes);//将收到的信息直接返回给发送方
|
||||
|
||||
//await sessionClient.SendAsync("id", mes);//将收到的信息返回给特定ID的客户端
|
||||
|
||||
//注意,此处是使用的当前客户端的接收线程做发送,实际使用中不可以这样做。不然一个客户端阻塞,将导致本客户端无法接收数据。
|
||||
var ids = sessionClient.Service.GetIds();
|
||||
foreach (var clientId in ids)//将收到的信息返回给在线的所有客户端。
|
||||
{
|
||||
if (clientId != sessionClient.Id)//不给自己发
|
||||
{
|
||||
await sessionClient.SendAsync(clientId, mes);
|
||||
}
|
||||
}
|
||||
|
||||
//await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应一个网友要求,该插件主要实现,在接收数据时如果触发<see cref="CloseException"/>异常,则断开连接。
|
||||
/// </summary>
|
||||
partial class ClosePlugin : PluginBase, ITcpReceivedPlugin
|
||||
{
|
||||
private readonly ILog m_logger;
|
||||
|
||||
public ClosePlugin(ILog logger)
|
||||
{
|
||||
this.m_logger = logger;
|
||||
}
|
||||
|
||||
public async Task OnTcpReceived(ITcpSession client, ReceivedDataEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
await e.InvokeNext();
|
||||
}
|
||||
catch (CloseException ex)
|
||||
{
|
||||
m_logger.Info("拦截到CloseException");
|
||||
await client.CloseAsync(ex.Message);
|
||||
}
|
||||
catch (Exception exx)
|
||||
{
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CloseException : Exception
|
||||
{
|
||||
public CloseException(string msg) : base(msg) { }
|
||||
}
|
||||
}
|
||||
15
examples-aot/Tcp/TcpConsoleApp/TcpConsoleApp.csproj
Normal file
15
examples-aot/Tcp/TcpConsoleApp/TcpConsoleApp.csproj
Normal file
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<PublishAot>true</PublishAot>
|
||||
<InvariantGlobalization>true</InvariantGlobalization>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="TouchSocket" Version="3.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
87
examples-aot/WebApi/WebApiConsoleApp/Program.cs
Normal file
87
examples-aot/WebApi/WebApiConsoleApp/Program.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Http;
|
||||
using TouchSocket.Rpc;
|
||||
using TouchSocket.Sockets;
|
||||
using TouchSocket.WebApi;
|
||||
|
||||
namespace WebApiConsoleApp
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var builder = Host.CreateApplicationBuilder(args);
|
||||
|
||||
builder.Services.AddServiceHostedService<IHttpService, HttpService>(config =>
|
||||
{
|
||||
config.SetListenIPHosts(7789)
|
||||
.ConfigureContainer(a =>
|
||||
{
|
||||
a.AddRpcStore(store =>
|
||||
{
|
||||
store.RegisterServer<ApiServer>();//ע<><D7A2><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
});
|
||||
|
||||
a.AddConsoleLogger();
|
||||
})
|
||||
.ConfigurePlugins(a =>
|
||||
{
|
||||
//a.UseCheckClear();
|
||||
|
||||
a.UseWebApi();
|
||||
|
||||
//a.UseHttpStaticPage()
|
||||
//.SetNavigateAction(request =>
|
||||
//{
|
||||
// //<2F>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ض<EFBFBD><D8B6><EFBFBD>
|
||||
// return request.RelativeURL;
|
||||
//})
|
||||
//.SetResponseAction(response =>
|
||||
//{
|
||||
// //<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӧͷ
|
||||
//})
|
||||
//.AddFolder("api/");//<2F><><EFBFBD>Ӿ<EFBFBD>̬ҳ<CCAC><D2B3><EFBFBD>ļ<EFBFBD><C4BC>У<EFBFBD><D0A3><EFBFBD>ʹ<EFBFBD><CAB9> http://127.0.0.1:7789/index.html <20><><EFBFBD>ʾ<EFBFBD>̬<EFBFBD><CCAC>ҳ
|
||||
|
||||
////<2F>˲<EFBFBD><CBB2><EFBFBD><EFBFBD><EFBFBD>http<74>Ķ<EFBFBD><C4B6>ײ<EFBFBD><D7B2><EFBFBD><EFBFBD><EFBFBD>Ӧ<EFBFBD><D3A6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӡ<EFBFBD><D3A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǵ<EFBFBD><C7B5><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD>ɲ<EFBFBD>ƥ<EFBFBD><C6A5>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD>404.<2E><><EFBFBD>ڲ<EFBFBD>Ҳ<EFBFBD>ᴦ<EFBFBD><E1B4A6>Option<6F><6E><EFBFBD><EFBFBD><F3A1A3BF>Ը<EFBFBD><D4B8>õĴ<C3B5><C4B4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ<EFBFBD><C4BF><EFBFBD>̽<EFBFBD>⡣
|
||||
//a.UseDefaultHttpServicePlugin();
|
||||
});
|
||||
});
|
||||
|
||||
var host = builder.Build();
|
||||
host.Run();
|
||||
}
|
||||
}
|
||||
|
||||
public partial class ApiServer : RpcServer
|
||||
{
|
||||
private readonly ILog m_logger;
|
||||
|
||||
public ApiServer(ILog logger)
|
||||
{
|
||||
this.m_logger = logger;
|
||||
}
|
||||
|
||||
[Router("[api]/[action]ab")]//<2F><>·<EFBFBD>ɻ<EFBFBD><C9BB><EFBFBD>"/Server/Sumab"ʵ<><CAB5>
|
||||
[Router("[api]/[action]")]//<2F><>·<EFBFBD>ɻ<EFBFBD><C9BB><EFBFBD>"/Server/Sum"ʵ<><CAB5>
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
public int Sum(int a, int b)
|
||||
{
|
||||
//m_logger.Info("Sum");
|
||||
return a + b;
|
||||
}
|
||||
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
public int TestPost(MyClass myClass)
|
||||
{
|
||||
m_logger.Info("TestPost");
|
||||
return myClass.A + myClass.B;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class MyClass
|
||||
{
|
||||
public int A { get; set; }
|
||||
public int B { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"WebApiConsoleApp": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"environmentVariables": {
|
||||
"DOTNET_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
59
examples-aot/WebApi/WebApiConsoleApp/WebApiConsoleApp.csproj
Normal file
59
examples-aot/WebApi/WebApiConsoleApp/WebApiConsoleApp.csproj
Normal file
@@ -0,0 +1,59 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Worker">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<InvariantGlobalization>true</InvariantGlobalization>
|
||||
<PublishAot>true</PublishAot>
|
||||
<UserSecretsId>dotnet-WebApiConsoleApp-9b0df4ac-2e15-40a3-90cf-2b2d890fe9a5</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
|
||||
<PackageReference Include="TouchSocket.Hosting" Version="3.0.0" />
|
||||
<PackageReference Include="TouchSocket.WebApi" Version="3.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="api\css\index.css">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="api\fonts\DS-DIGIT.TTF">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="api\img\head_bg.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="api\img\jt.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="api\img\lbx.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="api\img\map.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="api\index.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="api\js\china.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="api\js\echarts.min.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="api\js\flexible.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="api\js\index.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="api\js\myMap.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="api\readme.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
297
examples-aot/WebApi/WebApiConsoleApp/api/css/index.css
Normal file
297
examples-aot/WebApi/WebApiConsoleApp/api/css/index.css
Normal file
@@ -0,0 +1,297 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: electronicFont;
|
||||
src: url(../fonts/DS-DIGIT.TTF);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-repeat: no-repeat;
|
||||
background-color: #06164A;
|
||||
background-size: cover;
|
||||
/* 行高是字体1.15倍 */
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
header {
|
||||
position: relative;
|
||||
height: 1.25rem;
|
||||
background: url(../img/head_bg.png) no-repeat top center;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 0.475rem;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
line-height: 1rem;
|
||||
}
|
||||
|
||||
header .showTime {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0.375rem;
|
||||
line-height: 0.9375rem;
|
||||
font-size: 0.25rem;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.mainbox {
|
||||
min-width: 1024px;
|
||||
max-width: 1920px;
|
||||
padding: 0.125rem 0.125rem 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.mainbox .column {
|
||||
flex: 3;
|
||||
}
|
||||
|
||||
.mainbox .column:nth-child(2) {
|
||||
flex: 5;
|
||||
margin: 0 0.125rem 0.1875rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.panel {
|
||||
position: relative;
|
||||
height: 3.875rem;
|
||||
border: 1px solid rgba(25, 186, 139, 0.17);
|
||||
background: rgba(255, 255, 255, 0.04) url(../images/line\(1\).png);
|
||||
padding: 0 0.1875rem 0.5rem;
|
||||
margin-bottom: 0.1875rem;
|
||||
}
|
||||
|
||||
.panel::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
content: "";
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-top: 2px solid #02a6b5;
|
||||
border-left: 2px solid #02a6b5;
|
||||
border-radius: 20%;
|
||||
}
|
||||
|
||||
.panel::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
content: "";
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-top: 2px solid #02a6b5;
|
||||
border-right: 2px solid #02a6b5;
|
||||
border-radius: 20%;
|
||||
}
|
||||
|
||||
.panel .panel-footer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.panel .panel-footer::before {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
content: "";
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-bottom: 2px solid #02a6b5;
|
||||
border-left: 2px solid #02a6b5;
|
||||
border-radius: 20%;
|
||||
}
|
||||
|
||||
.panel .panel-footer::after {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
content: "";
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-bottom: 2px solid #02a6b5;
|
||||
border-right: 2px solid #02a6b5;
|
||||
border-radius: 20%;
|
||||
}
|
||||
|
||||
.panel h2 {
|
||||
height: 0.6rem;
|
||||
line-height: 0.6rem;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
font-size: 0.25rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.panel h2 a {
|
||||
margin: 0 0.1875rem;
|
||||
color: #fff;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.panel .chart {
|
||||
height: 3rem;
|
||||
}
|
||||
|
||||
.no {
|
||||
background: rgba(101, 132, 226, 0.1);
|
||||
padding: 0.1875rem;
|
||||
}
|
||||
|
||||
.no .no-hd {
|
||||
position: relative;
|
||||
border: 1px solid rgba(25, 186, 139, 0.17);
|
||||
}
|
||||
|
||||
.no .no-hd::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 30px;
|
||||
height: 10px;
|
||||
border-top: 2px solid #02a6b5;
|
||||
border-left: 2px solid #02a6b5;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.no .no-hd::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 30px;
|
||||
height: 10px;
|
||||
border-bottom: 2px solid #02a6b5;
|
||||
border-right: 2px solid #02a6b5;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.no .no-hd ul {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.no .no-hd ul li {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
height: 1rem;
|
||||
line-height: 1rem;
|
||||
font-size: 0.875rem;
|
||||
color: #ffeb7b;
|
||||
padding: 0.05rem 0;
|
||||
font-family: electronicFont;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.no .no-hd ul li:first-child::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
height: 50%;
|
||||
width: 1px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
right: 0;
|
||||
top: 25%;
|
||||
}
|
||||
|
||||
.no .no-bd ul {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.no .no-bd ul li {
|
||||
flex: 1;
|
||||
height: 0.5rem;
|
||||
line-height: 0.5rem;
|
||||
text-align: center;
|
||||
font-size: 0.225rem;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
padding-top: 0.125rem;
|
||||
}
|
||||
|
||||
.map {
|
||||
position: relative;
|
||||
height: 10.125rem;
|
||||
}
|
||||
|
||||
.map .chart {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 5;
|
||||
height: 10.125rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.map .map1,
|
||||
.map .map2,
|
||||
.map .map3 {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 6.475rem;
|
||||
height: 6.475rem;
|
||||
background: url(../img/map.png) no-repeat;
|
||||
background-size: 100% 100%;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.map .map2 {
|
||||
width: 8.0375rem;
|
||||
height: 8.0375rem;
|
||||
background-image: url(../img/lbx.png);
|
||||
opacity: 0.6;
|
||||
animation: rotate 15s linear infinite;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.map .map3 {
|
||||
width: 7.075rem;
|
||||
height: 7.075rem;
|
||||
background-image: url(../img/jt.png);
|
||||
animation: rotate1 10s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
from {
|
||||
transform: translate(-50%, -50%) rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translate(-50%, -50%) rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotate1 {
|
||||
from {
|
||||
transform: translate(-50%, -50%) rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translate(-50%, -50%) rotate(-360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1024px) {
|
||||
html {
|
||||
font-size: 42px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1920) {
|
||||
html {
|
||||
font-size: 80px !important;
|
||||
}
|
||||
}
|
||||
BIN
examples-aot/WebApi/WebApiConsoleApp/api/fonts/DS-DIGIT.TTF
Normal file
BIN
examples-aot/WebApi/WebApiConsoleApp/api/fonts/DS-DIGIT.TTF
Normal file
Binary file not shown.
BIN
examples-aot/WebApi/WebApiConsoleApp/api/img/head_bg.png
Normal file
BIN
examples-aot/WebApi/WebApiConsoleApp/api/img/head_bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
BIN
examples-aot/WebApi/WebApiConsoleApp/api/img/jt.png
Normal file
BIN
examples-aot/WebApi/WebApiConsoleApp/api/img/jt.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
examples-aot/WebApi/WebApiConsoleApp/api/img/lbx.png
Normal file
BIN
examples-aot/WebApi/WebApiConsoleApp/api/img/lbx.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
BIN
examples-aot/WebApi/WebApiConsoleApp/api/img/map.png
Normal file
BIN
examples-aot/WebApi/WebApiConsoleApp/api/img/map.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
103
examples-aot/WebApi/WebApiConsoleApp/api/index.html
Normal file
103
examples-aot/WebApi/WebApiConsoleApp/api/index.html
Normal file
@@ -0,0 +1,103 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>程序员数据可视化大屏展示</title>
|
||||
<link rel="stylesheet" href="css/index.css">
|
||||
<script src="js/myMap.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<h1>程序员数据可视化大屏展示</h1>
|
||||
<div class="showTime">当前时间:2021年8月<span></span></div>
|
||||
</header>
|
||||
<section class="mainbox">
|
||||
<div class="column">
|
||||
<div class="panel bar">
|
||||
<h2>
|
||||
柱状图-各行业程序员数量 <a href="javascript:;">2021</a>
|
||||
<a href="javacript:;"> 2020</a>
|
||||
</h2>
|
||||
<div class="chart"></div>
|
||||
<div class="panel-footer"></div>
|
||||
</div>
|
||||
<div class="panel line">
|
||||
<h2>折线图-学习变化</h2>
|
||||
<div class="chart"></div>
|
||||
<div class="panel-footer"></div>
|
||||
</div>
|
||||
<div class="panel pie">
|
||||
<h2>饼形图-工程师年龄分布</h2>
|
||||
<div class="chart"></div>
|
||||
<div class="panel-footer"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="no">
|
||||
<div class="no-hd">
|
||||
<ul>
|
||||
<li>225811</li>
|
||||
<li>104563</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="no-bd">
|
||||
<ul>
|
||||
<li>程序员需求人数</li>
|
||||
<li>程序员供应人数</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="map">
|
||||
<div class="chart"></div>
|
||||
<div class="map1"></div>
|
||||
<div class="map2"></div>
|
||||
<div class="map3"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="panel bar1">
|
||||
<h2>柱状图-开发技能</h2>
|
||||
<div class="chart"></div>
|
||||
<div class="panel-footer"></div>
|
||||
</div>
|
||||
<div class="panel line1">
|
||||
<h2>折线图-公司人员流动</h2>
|
||||
<div class="chart"></div>
|
||||
<div class="panel-footer"></div>
|
||||
</div>
|
||||
<div class="panel pie1">
|
||||
<h2>饼形图-各地区程序员占比</h2>
|
||||
<div class="chart"></div>
|
||||
<div class="panel-footer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script src="js/flexible.js"></script>
|
||||
<script src="js/echarts.min.js"></script>
|
||||
<script src="js/index.js"></script>
|
||||
<script src="js/china.js"></script>
|
||||
<script src="js/myMap.js"></script>
|
||||
|
||||
<script>
|
||||
(function(fn) {
|
||||
fn();
|
||||
setInterval(fn, 1000);
|
||||
})(function() {
|
||||
var dt = new Date();
|
||||
document.querySelector(".showTime span").innerHTML =
|
||||
dt.getDate() + "日-" +
|
||||
dt.getHours() + "时" +
|
||||
dt.getMinutes() + "分" +
|
||||
dt.getSeconds() + "秒";
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
1915
examples-aot/WebApi/WebApiConsoleApp/api/js/china.js
Normal file
1915
examples-aot/WebApi/WebApiConsoleApp/api/js/china.js
Normal file
File diff suppressed because it is too large
Load Diff
35400
examples-aot/WebApi/WebApiConsoleApp/api/js/echarts.min.js
vendored
Normal file
35400
examples-aot/WebApi/WebApiConsoleApp/api/js/echarts.min.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
43
examples-aot/WebApi/WebApiConsoleApp/api/js/flexible.js
Normal file
43
examples-aot/WebApi/WebApiConsoleApp/api/js/flexible.js
Normal file
@@ -0,0 +1,43 @@
|
||||
(function flexible(window, document) {
|
||||
var docEl = document.documentElement;
|
||||
var dpr = window.devicePixelRatio || 1;
|
||||
|
||||
// adjust body font size
|
||||
function setBodyFontSize() {
|
||||
if (document.body) {
|
||||
document.body.style.fontSize = 12 * dpr + "px";
|
||||
} else {
|
||||
document.addEventListener("DOMContentLoaded", setBodyFontSize);
|
||||
}
|
||||
}
|
||||
setBodyFontSize();
|
||||
|
||||
// set 1rem = viewWidth / 10
|
||||
function setRemUnit() {
|
||||
var rem = docEl.clientWidth / 24;
|
||||
docEl.style.fontSize = rem + "px";
|
||||
}
|
||||
|
||||
setRemUnit();
|
||||
|
||||
// reset rem unit on page resize
|
||||
window.addEventListener("resize", setRemUnit);
|
||||
window.addEventListener("pageshow", function(e) {
|
||||
if (e.persisted) {
|
||||
setRemUnit();
|
||||
}
|
||||
});
|
||||
|
||||
// detect 0.5px supports
|
||||
if (dpr >= 2) {
|
||||
var fakeBody = document.createElement("body");
|
||||
var testElement = document.createElement("div");
|
||||
testElement.style.border = ".5px solid transparent";
|
||||
fakeBody.appendChild(testElement);
|
||||
docEl.appendChild(fakeBody);
|
||||
if (testElement.offsetHeight === 1) {
|
||||
docEl.classList.add("hairlines");
|
||||
}
|
||||
docEl.removeChild(fakeBody);
|
||||
}
|
||||
})(window, document);
|
||||
763
examples-aot/WebApi/WebApiConsoleApp/api/js/index.js
Normal file
763
examples-aot/WebApi/WebApiConsoleApp/api/js/index.js
Normal file
@@ -0,0 +1,763 @@
|
||||
// 柱状图1模块
|
||||
(function() {
|
||||
// 实例化对象
|
||||
var myChart = echarts.init(document.querySelector(".bar .chart"));
|
||||
// 指定配置和数据
|
||||
var option = {
|
||||
color: ["#2f89cf"],
|
||||
tooltip: {
|
||||
trigger: "axis",
|
||||
axisPointer: {
|
||||
// 坐标轴指示器,坐标轴触发有效
|
||||
type: "shadow" // 默认为直线,可选为:'line' | 'shadow'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: "0%",
|
||||
top: "10px",
|
||||
right: "0%",
|
||||
bottom: "4%",
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [{
|
||||
type: "category",
|
||||
data: [
|
||||
"旅游行业",
|
||||
"教育培训",
|
||||
"游戏行业",
|
||||
"医疗行业",
|
||||
"电商行业",
|
||||
"社交行业",
|
||||
"金融行业"
|
||||
],
|
||||
axisTick: {
|
||||
alignWithLabel: true
|
||||
},
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: "rgba(255,255,255,.6)",
|
||||
fontSize: "12"
|
||||
}
|
||||
},
|
||||
axisLine: {
|
||||
show: false
|
||||
}
|
||||
}],
|
||||
yAxis: [{
|
||||
type: "value",
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: "rgba(255,255,255,.6)",
|
||||
fontSize: "12"
|
||||
}
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: "rgba(255,255,255,.1)"
|
||||
// width: 1,
|
||||
// type: "solid"
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: "rgba(255,255,255,.1)"
|
||||
}
|
||||
}
|
||||
}],
|
||||
series: [{
|
||||
name: "直接访问",
|
||||
type: "bar",
|
||||
barWidth: "35%",
|
||||
data: [200, 300, 300, 900, 1500, 1200, 600],
|
||||
itemStyle: {
|
||||
barBorderRadius: 5
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
// 把配置给实例对象
|
||||
myChart.setOption(option);
|
||||
window.addEventListener("resize", function() {
|
||||
myChart.resize();
|
||||
});
|
||||
|
||||
// 数据变化
|
||||
var dataAll = [{
|
||||
year: "2019",
|
||||
data: [200, 300, 300, 900, 1500, 1200, 600]
|
||||
},
|
||||
{
|
||||
year: "2020",
|
||||
data: [300, 400, 350, 800, 1800, 1400, 700]
|
||||
}
|
||||
];
|
||||
|
||||
document.querySelector(".bar h2").addEventListener("click", function(e) {
|
||||
var i = e.target == this.children[0] ? 0 : 1;
|
||||
option.series[0].data = dataAll[i].data;
|
||||
myChart.setOption(option);
|
||||
});
|
||||
})();
|
||||
|
||||
// 折线图定制
|
||||
(function() {
|
||||
// 基于准备好的dom,初始化echarts实例
|
||||
var myChart = echarts.init(document.querySelector(".line .chart"));
|
||||
|
||||
// (1)准备数据
|
||||
var data = {
|
||||
year: [
|
||||
[24, 40, 101, 134, 90, 230, 210, 230, 120, 230, 210, 120],
|
||||
[40, 64, 191, 324, 290, 330, 310, 213, 180, 200, 180, 79]
|
||||
]
|
||||
};
|
||||
|
||||
// 2. 指定配置和数据
|
||||
var option = {
|
||||
color: ["#00f2f1", "#ed3f35"],
|
||||
tooltip: {
|
||||
// 通过坐标轴来触发
|
||||
trigger: "axis"
|
||||
},
|
||||
legend: {
|
||||
// 距离容器10%
|
||||
right: "10%",
|
||||
// 修饰图例文字的颜色
|
||||
textStyle: {
|
||||
color: "#4c9bfd"
|
||||
}
|
||||
// 如果series 里面设置了name,此时图例组件的data可以省略
|
||||
// data: ["邮件营销", "联盟广告"]
|
||||
},
|
||||
grid: {
|
||||
top: "20%",
|
||||
left: "3%",
|
||||
right: "4%",
|
||||
bottom: "3%",
|
||||
show: true,
|
||||
borderColor: "#012f4a",
|
||||
containLabel: true
|
||||
},
|
||||
|
||||
xAxis: {
|
||||
type: "category",
|
||||
boundaryGap: false,
|
||||
data: [
|
||||
"1月",
|
||||
"2月",
|
||||
"3月",
|
||||
"4月",
|
||||
"5月",
|
||||
"6月",
|
||||
"7月",
|
||||
"8月",
|
||||
"9月",
|
||||
"10月",
|
||||
"11月",
|
||||
"12月"
|
||||
],
|
||||
// 去除刻度
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
// 修饰刻度标签的颜色
|
||||
axisLabel: {
|
||||
color: "rgba(255,255,255,.7)"
|
||||
},
|
||||
// 去除x坐标轴的颜色
|
||||
axisLine: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: "value",
|
||||
// 去除刻度
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
// 修饰刻度标签的颜色
|
||||
axisLabel: {
|
||||
color: "rgba(255,255,255,.7)"
|
||||
},
|
||||
// 修改y轴分割线的颜色
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: "#012f4a"
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
name: "新增项目",
|
||||
type: "line",
|
||||
stack: "总量",
|
||||
// 是否让线条圆滑显示
|
||||
smooth: true,
|
||||
data: data.year[0]
|
||||
},
|
||||
{
|
||||
name: "新增技能",
|
||||
type: "line",
|
||||
stack: "总量",
|
||||
smooth: true,
|
||||
data: data.year[1]
|
||||
}
|
||||
]
|
||||
};
|
||||
// 3. 把配置和数据给实例对象
|
||||
myChart.setOption(option);
|
||||
|
||||
// 重新把配置好的新数据给实例对象
|
||||
myChart.setOption(option);
|
||||
window.addEventListener("resize", function() {
|
||||
myChart.resize();
|
||||
});
|
||||
})();
|
||||
|
||||
// 饼形图定制
|
||||
// 折线图定制
|
||||
(function() {
|
||||
// 基于准备好的dom,初始化echarts实例
|
||||
var myChart = echarts.init(document.querySelector(".pie .chart"));
|
||||
|
||||
option = {
|
||||
tooltip: {
|
||||
trigger: "item",
|
||||
formatter: "{a} <br/>{b}: {c} ({d}%)",
|
||||
position: function(p) {
|
||||
//其中p为当前鼠标的位置
|
||||
return [p[0] + 10, p[1] - 10];
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
top: "90%",
|
||||
itemWidth: 10,
|
||||
itemHeight: 10,
|
||||
data: ["0岁以下", "20-29岁", "30-39岁", "40-49岁", "50岁以上"],
|
||||
textStyle: {
|
||||
color: "rgba(255,255,255,.5)",
|
||||
fontSize: "12"
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
name: "年龄分布",
|
||||
type: "pie",
|
||||
center: ["50%", "42%"],
|
||||
radius: ["40%", "60%"],
|
||||
color: [
|
||||
"#065aab",
|
||||
"#066eab",
|
||||
"#0682ab",
|
||||
"#0696ab",
|
||||
"#06a0ab",
|
||||
"#06b4ab",
|
||||
"#06c8ab",
|
||||
"#06dcab",
|
||||
"#06f0ab"
|
||||
],
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: [{
|
||||
value: 1,
|
||||
name: "0岁以下"
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
name: "20-29岁"
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
name: "30-39岁"
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
name: "40-49岁"
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
name: "50岁以上"
|
||||
}
|
||||
]
|
||||
}]
|
||||
};
|
||||
|
||||
// 使用刚指定的配置项和数据显示图表。
|
||||
myChart.setOption(option);
|
||||
window.addEventListener("resize", function() {
|
||||
myChart.resize();
|
||||
});
|
||||
})();
|
||||
// 学习进度柱状图模块
|
||||
(function() {
|
||||
// 基于准备好的dom,初始化echarts实例
|
||||
var myChart = echarts.init(document.querySelector(".bar1 .chart"));
|
||||
|
||||
var data = [90, 80, 75, 65, 65];
|
||||
var titlename = ["C#", "C++", "GO", "HTML5", "VUE"];
|
||||
var valdata = ["精通", "熟练", "熟练", "掌握", "掌握"];
|
||||
var myColor = ["#1089E7", "#F57474", "#56D0E3", "#F8B448", "#8B78F6"];
|
||||
option = {
|
||||
//图标位置
|
||||
grid: {
|
||||
top: "10%",
|
||||
left: "22%",
|
||||
bottom: "10%"
|
||||
},
|
||||
xAxis: {
|
||||
show: false
|
||||
},
|
||||
yAxis: [{
|
||||
show: true,
|
||||
data: titlename,
|
||||
inverse: true,
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
color: "#fff",
|
||||
|
||||
rich: {
|
||||
lg: {
|
||||
backgroundColor: "#339911",
|
||||
color: "#fff",
|
||||
borderRadius: 15,
|
||||
// padding: 5,
|
||||
align: "center",
|
||||
width: 15,
|
||||
height: 15
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
show: true,
|
||||
inverse: true,
|
||||
data: valdata,
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
fontSize: 12,
|
||||
color: "#fff"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [{
|
||||
name: "条",
|
||||
type: "bar",
|
||||
yAxisIndex: 0,
|
||||
data: data,
|
||||
barCategoryGap: 50,
|
||||
barWidth: 10,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
barBorderRadius: 20,
|
||||
color: function(params) {
|
||||
var num = myColor.length;
|
||||
return myColor[params.dataIndex % num];
|
||||
}
|
||||
}
|
||||
},
|
||||
label: {
|
||||
normal: {
|
||||
show: true,
|
||||
position: "inside",
|
||||
formatter: "{c}%"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "框",
|
||||
type: "bar",
|
||||
yAxisIndex: 1,
|
||||
barCategoryGap: 50,
|
||||
data: [100, 100, 100, 100, 100],
|
||||
barWidth: 15,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: "none",
|
||||
borderColor: "#00c1de",
|
||||
borderWidth: 3,
|
||||
barBorderRadius: 15
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// 使用刚指定的配置项和数据显示图表。
|
||||
myChart.setOption(option);
|
||||
window.addEventListener("resize", function() {
|
||||
myChart.resize();
|
||||
});
|
||||
})();
|
||||
// 折线图 优秀作品
|
||||
(function() {
|
||||
// 基于准备好的dom,初始化echarts实例
|
||||
var myChart = echarts.init(document.querySelector(".line1 .chart"));
|
||||
|
||||
option = {
|
||||
tooltip: {
|
||||
trigger: "axis",
|
||||
axisPointer: {
|
||||
lineStyle: {
|
||||
color: "#dddc6b"
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
top: "0%",
|
||||
textStyle: {
|
||||
color: "rgba(255,255,255,.5)",
|
||||
fontSize: "12"
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: "10",
|
||||
top: "30",
|
||||
right: "10",
|
||||
bottom: "10",
|
||||
containLabel: true
|
||||
},
|
||||
|
||||
xAxis: [{
|
||||
type: "category",
|
||||
boundaryGap: false,
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: "rgba(255,255,255,.6)",
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: "rgba(255,255,255,.2)"
|
||||
}
|
||||
},
|
||||
|
||||
data: [
|
||||
"01",
|
||||
"02",
|
||||
"03",
|
||||
"04",
|
||||
"05",
|
||||
"06",
|
||||
"07",
|
||||
"08",
|
||||
"09",
|
||||
"11",
|
||||
"12",
|
||||
"13",
|
||||
"14",
|
||||
"15",
|
||||
"16",
|
||||
"17",
|
||||
"18",
|
||||
"19",
|
||||
"20",
|
||||
"21",
|
||||
"22",
|
||||
"23",
|
||||
"24",
|
||||
"25",
|
||||
"26",
|
||||
"27",
|
||||
"28",
|
||||
"29",
|
||||
"30"
|
||||
]
|
||||
},
|
||||
{
|
||||
axisPointer: {
|
||||
show: false
|
||||
},
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
position: "bottom",
|
||||
offset: 20
|
||||
}
|
||||
],
|
||||
|
||||
yAxis: [{
|
||||
type: "value",
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: "rgba(255,255,255,.1)"
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: "rgba(255,255,255,.6)",
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: "rgba(255,255,255,.1)"
|
||||
}
|
||||
}
|
||||
}],
|
||||
series: [{
|
||||
name: "流入",
|
||||
type: "line",
|
||||
smooth: true,
|
||||
symbol: "circle",
|
||||
symbolSize: 5,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
normal: {
|
||||
color: "#0184d5",
|
||||
width: 2
|
||||
}
|
||||
},
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
[{
|
||||
offset: 0,
|
||||
color: "rgba(1, 132, 213, 0.4)"
|
||||
},
|
||||
{
|
||||
offset: 0.8,
|
||||
color: "rgba(1, 132, 213, 0.1)"
|
||||
}
|
||||
],
|
||||
false
|
||||
),
|
||||
shadowColor: "rgba(0, 0, 0, 0.1)"
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: "#0184d5",
|
||||
borderColor: "rgba(221, 220, 107, .1)",
|
||||
borderWidth: 12
|
||||
}
|
||||
},
|
||||
data: [
|
||||
30,
|
||||
40,
|
||||
30,
|
||||
40,
|
||||
30,
|
||||
40,
|
||||
30,
|
||||
60,
|
||||
20,
|
||||
40,
|
||||
20,
|
||||
40,
|
||||
30,
|
||||
40,
|
||||
30,
|
||||
40,
|
||||
30,
|
||||
40,
|
||||
30,
|
||||
60,
|
||||
20,
|
||||
40,
|
||||
20,
|
||||
40,
|
||||
30,
|
||||
60,
|
||||
20,
|
||||
40,
|
||||
20,
|
||||
40
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "流出",
|
||||
type: "line",
|
||||
smooth: true,
|
||||
symbol: "circle",
|
||||
symbolSize: 5,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
normal: {
|
||||
color: "#00d887",
|
||||
width: 2
|
||||
}
|
||||
},
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
[{
|
||||
offset: 0,
|
||||
color: "rgba(0, 216, 135, 0.4)"
|
||||
},
|
||||
{
|
||||
offset: 0.8,
|
||||
color: "rgba(0, 216, 135, 0.1)"
|
||||
}
|
||||
],
|
||||
false
|
||||
),
|
||||
shadowColor: "rgba(0, 0, 0, 0.1)"
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: "#00d887",
|
||||
borderColor: "rgba(221, 220, 107, .1)",
|
||||
borderWidth: 12
|
||||
}
|
||||
},
|
||||
data: [
|
||||
50,
|
||||
30,
|
||||
50,
|
||||
60,
|
||||
10,
|
||||
50,
|
||||
30,
|
||||
50,
|
||||
60,
|
||||
40,
|
||||
60,
|
||||
40,
|
||||
80,
|
||||
30,
|
||||
50,
|
||||
60,
|
||||
10,
|
||||
50,
|
||||
30,
|
||||
70,
|
||||
20,
|
||||
50,
|
||||
10,
|
||||
40,
|
||||
50,
|
||||
30,
|
||||
70,
|
||||
20,
|
||||
50,
|
||||
10,
|
||||
40
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// 使用刚指定的配置项和数据显示图表。
|
||||
myChart.setOption(option);
|
||||
window.addEventListener("resize", function() {
|
||||
myChart.resize();
|
||||
});
|
||||
})();
|
||||
|
||||
// 点位分布统计模块
|
||||
(function() {
|
||||
// 1. 实例化对象
|
||||
var myChart = echarts.init(document.querySelector(".pie1 .chart"));
|
||||
// 2. 指定配置项和数据
|
||||
var option = {
|
||||
legend: {
|
||||
top: "90%",
|
||||
itemWidth: 10,
|
||||
itemHeight: 10,
|
||||
textStyle: {
|
||||
color: "rgba(255,255,255,.5)",
|
||||
fontSize: "12"
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
trigger: "item",
|
||||
formatter: "{a} <br/>{b} : {c} ({d}%)"
|
||||
},
|
||||
// 注意颜色写的位置
|
||||
color: [
|
||||
"#006cff",
|
||||
"#60cda0",
|
||||
"#ed8884",
|
||||
"#ff9f7f",
|
||||
"#0096ff",
|
||||
"#9fe6b8",
|
||||
"#32c5e9",
|
||||
"#1d9dff"
|
||||
],
|
||||
series: [{
|
||||
name: "点位统计",
|
||||
type: "pie",
|
||||
// 如果radius是百分比则必须加引号
|
||||
radius: ["10%", "70%"],
|
||||
center: ["50%", "42%"],
|
||||
roseType: "radius",
|
||||
data: [{
|
||||
value: 20,
|
||||
name: "西安"
|
||||
},
|
||||
{
|
||||
value: 26,
|
||||
name: "北京"
|
||||
},
|
||||
{
|
||||
value: 24,
|
||||
name: "上海"
|
||||
},
|
||||
{
|
||||
value: 25,
|
||||
name: "其他"
|
||||
},
|
||||
{
|
||||
value: 20,
|
||||
name: "武汉"
|
||||
},
|
||||
{
|
||||
value: 25,
|
||||
name: "杭州"
|
||||
},
|
||||
{
|
||||
value: 30,
|
||||
name: "深圳"
|
||||
},
|
||||
{
|
||||
value: 42,
|
||||
name: "广州"
|
||||
}
|
||||
],
|
||||
// 修饰饼形图文字相关的样式 label对象
|
||||
label: {
|
||||
fontSize: 10
|
||||
},
|
||||
// 修饰引导线样式
|
||||
labelLine: {
|
||||
// 连接到图形的线长度
|
||||
length: 10,
|
||||
// 连接到文字的线长度
|
||||
length2: 10
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
// 3. 配置项和数据给我们的实例化对象
|
||||
myChart.setOption(option);
|
||||
// 4. 当我们浏览器缩放的时候,图表也等比例缩放
|
||||
window.addEventListener("resize", function() {
|
||||
// 让我们的图表调用 resize这个方法
|
||||
myChart.resize();
|
||||
});
|
||||
})();
|
||||
362
examples-aot/WebApi/WebApiConsoleApp/api/js/myMap.js
Normal file
362
examples-aot/WebApi/WebApiConsoleApp/api/js/myMap.js
Normal file
@@ -0,0 +1,362 @@
|
||||
(function() {
|
||||
// 1. 实例化对象
|
||||
var myChart = echarts.init(document.querySelector(".map .chart"));
|
||||
// 2. 指定配置和数据
|
||||
// 2. 指定配置和数据
|
||||
var geoCoordMap = {
|
||||
上海: [121.4648, 31.2891],
|
||||
东莞: [113.8953, 22.901],
|
||||
东营: [118.7073, 37.5513],
|
||||
中山: [113.4229, 22.478],
|
||||
临汾: [111.4783, 36.1615],
|
||||
临沂: [118.3118, 35.2936],
|
||||
丹东: [124.541, 40.4242],
|
||||
丽水: [119.5642, 28.1854],
|
||||
乌鲁木齐: [87.9236, 43.5883],
|
||||
佛山: [112.8955, 23.1097],
|
||||
保定: [115.0488, 39.0948],
|
||||
兰州: [103.5901, 36.3043],
|
||||
包头: [110.3467, 41.4899],
|
||||
北京: [116.4551, 40.2539],
|
||||
北海: [109.314, 21.6211],
|
||||
南京: [118.8062, 31.9208],
|
||||
南宁: [108.479, 23.1152],
|
||||
南昌: [116.0046, 28.6633],
|
||||
南通: [121.1023, 32.1625],
|
||||
厦门: [118.1689, 24.6478],
|
||||
台州: [121.1353, 28.6688],
|
||||
合肥: [117.29, 32.0581],
|
||||
呼和浩特: [111.4124, 40.4901],
|
||||
咸阳: [108.4131, 34.8706],
|
||||
哈尔滨: [127.9688, 45.368],
|
||||
唐山: [118.4766, 39.6826],
|
||||
嘉兴: [120.9155, 30.6354],
|
||||
大同: [113.7854, 39.8035],
|
||||
大连: [122.2229, 39.4409],
|
||||
天津: [117.4219, 39.4189],
|
||||
太原: [112.3352, 37.9413],
|
||||
威海: [121.9482, 37.1393],
|
||||
宁波: [121.5967, 29.6466],
|
||||
宝鸡: [107.1826, 34.3433],
|
||||
宿迁: [118.5535, 33.7775],
|
||||
常州: [119.4543, 31.5582],
|
||||
广州: [113.5107, 23.2196],
|
||||
廊坊: [116.521, 39.0509],
|
||||
延安: [109.1052, 36.4252],
|
||||
张家口: [115.1477, 40.8527],
|
||||
徐州: [117.5208, 34.3268],
|
||||
德州: [116.6858, 37.2107],
|
||||
惠州: [114.6204, 23.1647],
|
||||
成都: [103.9526, 30.7617],
|
||||
扬州: [119.4653, 32.8162],
|
||||
承德: [117.5757, 41.4075],
|
||||
拉萨: [91.1865, 30.1465],
|
||||
无锡: [120.3442, 31.5527],
|
||||
日照: [119.2786, 35.5023],
|
||||
昆明: [102.9199, 25.4663],
|
||||
杭州: [119.5313, 29.8773],
|
||||
枣庄: [117.323, 34.8926],
|
||||
柳州: [109.3799, 24.9774],
|
||||
株洲: [113.5327, 27.0319],
|
||||
武汉: [114.3896, 30.6628],
|
||||
汕头: [117.1692, 23.3405],
|
||||
江门: [112.6318, 22.1484],
|
||||
沈阳: [123.1238, 42.1216],
|
||||
沧州: [116.8286, 38.2104],
|
||||
河源: [114.917, 23.9722],
|
||||
泉州: [118.3228, 25.1147],
|
||||
泰安: [117.0264, 36.0516],
|
||||
泰州: [120.0586, 32.5525],
|
||||
济南: [117.1582, 36.8701],
|
||||
济宁: [116.8286, 35.3375],
|
||||
海口: [110.3893, 19.8516],
|
||||
淄博: [118.0371, 36.6064],
|
||||
淮安: [118.927, 33.4039],
|
||||
深圳: [114.5435, 22.5439],
|
||||
清远: [112.9175, 24.3292],
|
||||
温州: [120.498, 27.8119],
|
||||
渭南: [109.7864, 35.0299],
|
||||
湖州: [119.8608, 30.7782],
|
||||
湘潭: [112.5439, 27.7075],
|
||||
滨州: [117.8174, 37.4963],
|
||||
潍坊: [119.0918, 36.524],
|
||||
烟台: [120.7397, 37.5128],
|
||||
玉溪: [101.9312, 23.8898],
|
||||
珠海: [113.7305, 22.1155],
|
||||
盐城: [120.2234, 33.5577],
|
||||
盘锦: [121.9482, 41.0449],
|
||||
石家庄: [114.4995, 38.1006],
|
||||
福州: [119.4543, 25.9222],
|
||||
秦皇岛: [119.2126, 40.0232],
|
||||
绍兴: [120.564, 29.7565],
|
||||
聊城: [115.9167, 36.4032],
|
||||
肇庆: [112.1265, 23.5822],
|
||||
舟山: [122.2559, 30.2234],
|
||||
苏州: [120.6519, 31.3989],
|
||||
莱芜: [117.6526, 36.2714],
|
||||
菏泽: [115.6201, 35.2057],
|
||||
营口: [122.4316, 40.4297],
|
||||
葫芦岛: [120.1575, 40.578],
|
||||
衡水: [115.8838, 37.7161],
|
||||
衢州: [118.6853, 28.8666],
|
||||
西宁: [101.4038, 36.8207],
|
||||
西安: [109.1162, 34.2004],
|
||||
贵阳: [106.6992, 26.7682],
|
||||
连云港: [119.1248, 34.552],
|
||||
邢台: [114.8071, 37.2821],
|
||||
邯郸: [114.4775, 36.535],
|
||||
郑州: [113.4668, 34.6234],
|
||||
鄂尔多斯: [108.9734, 39.2487],
|
||||
重庆: [107.7539, 30.1904],
|
||||
金华: [120.0037, 29.1028],
|
||||
铜川: [109.0393, 35.1947],
|
||||
银川: [106.3586, 38.1775],
|
||||
镇江: [119.4763, 31.9702],
|
||||
长春: [125.8154, 44.2584],
|
||||
长沙: [113.0823, 28.2568],
|
||||
长治: [112.8625, 36.4746],
|
||||
阳泉: [113.4778, 38.0951],
|
||||
青岛: [120.4651, 36.3373],
|
||||
韶关: [113.7964, 24.7028]
|
||||
};
|
||||
|
||||
var XAData = [
|
||||
[{
|
||||
name: "西安"
|
||||
}, {
|
||||
name: "北京",
|
||||
value: 100
|
||||
}],
|
||||
[{
|
||||
name: "西安"
|
||||
}, {
|
||||
name: "上海",
|
||||
value: 100
|
||||
}],
|
||||
[{
|
||||
name: "西安"
|
||||
}, {
|
||||
name: "广州",
|
||||
value: 100
|
||||
}],
|
||||
[{
|
||||
name: "西安"
|
||||
}, {
|
||||
name: "西宁",
|
||||
value: 100
|
||||
}],
|
||||
[{
|
||||
name: "西安"
|
||||
}, {
|
||||
name: "拉萨",
|
||||
value: 100
|
||||
}]
|
||||
];
|
||||
|
||||
var XNData = [
|
||||
[{
|
||||
name: "西宁"
|
||||
}, {
|
||||
name: "北京",
|
||||
value: 100
|
||||
}],
|
||||
[{
|
||||
name: "西宁"
|
||||
}, {
|
||||
name: "上海",
|
||||
value: 100
|
||||
}],
|
||||
[{
|
||||
name: "西宁"
|
||||
}, {
|
||||
name: "广州",
|
||||
value: 100
|
||||
}],
|
||||
[{
|
||||
name: "西宁"
|
||||
}, {
|
||||
name: "西安",
|
||||
value: 100
|
||||
}],
|
||||
[{
|
||||
name: "西宁"
|
||||
}, {
|
||||
name: "银川",
|
||||
value: 100
|
||||
}]
|
||||
];
|
||||
|
||||
var YCData = [
|
||||
[{
|
||||
name: "拉萨"
|
||||
}, {
|
||||
name: "北京",
|
||||
value: 100
|
||||
}],
|
||||
[{
|
||||
name: "拉萨"
|
||||
}, {
|
||||
name: "潍坊",
|
||||
value: 100
|
||||
}],
|
||||
[{
|
||||
name: "拉萨"
|
||||
}, {
|
||||
name: "哈尔滨",
|
||||
value: 100
|
||||
}]
|
||||
];
|
||||
|
||||
var planePath =
|
||||
"path://M1705.06,1318.313v-89.254l-319.9-221.799l0.073-208.063c0.521-84.662-26.629-121.796-63.961-121.491c-37.332-0.305-64.482,36.829-63.961,121.491l0.073,208.063l-319.9,221.799v89.254l330.343-157.288l12.238,241.308l-134.449,92.931l0.531,42.034l175.125-42.917l175.125,42.917l0.531-42.034l-134.449-92.931l12.238-241.308L1705.06,1318.313z";
|
||||
//var planePath = 'arrow';
|
||||
var convertData = function(data) {
|
||||
var res = [];
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var dataItem = data[i];
|
||||
|
||||
var fromCoord = geoCoordMap[dataItem[0].name];
|
||||
var toCoord = geoCoordMap[dataItem[1].name];
|
||||
if (fromCoord && toCoord) {
|
||||
res.push({
|
||||
fromName: dataItem[0].name,
|
||||
toName: dataItem[1].name,
|
||||
coords: [fromCoord, toCoord],
|
||||
value: dataItem[1].value
|
||||
});
|
||||
}
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
var color = ["#fff", "#fff", "#fff"]; //航线的颜色
|
||||
var series = [];
|
||||
[
|
||||
["西安", XAData],
|
||||
["西宁", XNData],
|
||||
["银川", YCData]
|
||||
].forEach(function(item, i) {
|
||||
series.push({
|
||||
name: item[0] + " Top3",
|
||||
type: "lines",
|
||||
zlevel: 1,
|
||||
effect: {
|
||||
show: true,
|
||||
period: 6,
|
||||
trailLength: 0.7,
|
||||
color: "red", //arrow箭头的颜色
|
||||
symbolSize: 3
|
||||
},
|
||||
lineStyle: {
|
||||
normal: {
|
||||
color: color[i],
|
||||
width: 0,
|
||||
curveness: 0.2
|
||||
}
|
||||
},
|
||||
data: convertData(item[1])
|
||||
}, {
|
||||
name: item[0] + " Top3",
|
||||
type: "lines",
|
||||
zlevel: 2,
|
||||
symbol: ["none", "arrow"],
|
||||
symbolSize: 10,
|
||||
effect: {
|
||||
show: true,
|
||||
period: 6,
|
||||
trailLength: 0,
|
||||
symbol: planePath,
|
||||
symbolSize: 15
|
||||
},
|
||||
lineStyle: {
|
||||
normal: {
|
||||
color: color[i],
|
||||
width: 1,
|
||||
opacity: 0.6,
|
||||
curveness: 0.2
|
||||
}
|
||||
},
|
||||
data: convertData(item[1])
|
||||
}, {
|
||||
name: item[0] + " Top3",
|
||||
type: "effectScatter",
|
||||
coordinateSystem: "geo",
|
||||
zlevel: 2,
|
||||
rippleEffect: {
|
||||
brushType: "stroke"
|
||||
},
|
||||
label: {
|
||||
normal: {
|
||||
show: true,
|
||||
position: "right",
|
||||
formatter: "{b}"
|
||||
}
|
||||
},
|
||||
symbolSize: function(val) {
|
||||
return val[2] / 8;
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: color[i]
|
||||
},
|
||||
emphasis: {
|
||||
areaColor: "#2B91B7"
|
||||
}
|
||||
},
|
||||
data: item[1].map(function(dataItem) {
|
||||
return {
|
||||
name: dataItem[1].name,
|
||||
value: geoCoordMap[dataItem[1].name].concat([dataItem[1].value])
|
||||
};
|
||||
})
|
||||
});
|
||||
});
|
||||
var option = {
|
||||
tooltip: {
|
||||
trigger: "item",
|
||||
formatter: function(params, ticket, callback) {
|
||||
if (params.seriesType == "effectScatter") {
|
||||
return "线路:" + params.data.name + "" + params.data.value[2];
|
||||
} else if (params.seriesType == "lines") {
|
||||
return (
|
||||
params.data.fromName +
|
||||
">" +
|
||||
params.data.toName +
|
||||
"<br />" +
|
||||
params.data.value
|
||||
);
|
||||
} else {
|
||||
return params.name;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
geo: {
|
||||
map: "china",
|
||||
label: {
|
||||
emphasis: {
|
||||
show: true,
|
||||
color: "#fff"
|
||||
}
|
||||
},
|
||||
roam: false,
|
||||
// 放大我们的地图
|
||||
zoom: 1,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
areaColor: "rgba(43, 196, 243, 0.42)",
|
||||
borderColor: "rgba(43, 196, 243, 1)",
|
||||
borderWidth: 1
|
||||
},
|
||||
emphasis: {
|
||||
areaColor: "#2B91B7"
|
||||
}
|
||||
}
|
||||
},
|
||||
series: series
|
||||
};
|
||||
myChart.setOption(option);
|
||||
window.addEventListener("resize", function() {
|
||||
myChart.resize();
|
||||
});
|
||||
})();
|
||||
1
examples-aot/WebApi/WebApiConsoleApp/api/readme.txt
Normal file
1
examples-aot/WebApi/WebApiConsoleApp/api/readme.txt
Normal file
@@ -0,0 +1 @@
|
||||
本静态网页来源于https://gitee.com/iGaoWei/big-data-view
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
8
examples-aot/WebApi/WebApiConsoleApp/appsettings.json
Normal file
8
examples-aot/WebApi/WebApiConsoleApp/appsettings.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace WebApplication_NET6.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
public class WeatherForecastController : ControllerBase
|
||||
{
|
||||
private static readonly string[] Summaries = new[]
|
||||
{
|
||||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
||||
};
|
||||
|
||||
private readonly ILogger<WeatherForecastController> _logger;
|
||||
|
||||
public WeatherForecastController(ILogger<WeatherForecastController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace WebApplication_NET6
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
|
||||
builder.Services.AddControllers();
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
#region <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Tcp<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
builder.Services.AddTcpService(config =>
|
||||
{
|
||||
config.SetListenIPHosts(7789);
|
||||
});
|
||||
#endregion
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:26141",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"WebApplication_NET6": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "http://localhost:5270",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.0.0" />
|
||||
<PackageReference Include="TouchSocket.AspNetCore" Version="3.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using TouchSocket.Dmtp;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace WorkerService_NET6
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var builder = Host.CreateDefaultBuilder(args)
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
//<2F><><EFBFBD><EFBFBD>Tcp<63><70><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĭ<EFBFBD><C4AC><EFBFBD>ǵ<EFBFBD><C7B5><EFBFBD>
|
||||
services.AddTcpService(config =>
|
||||
{
|
||||
config.SetListenIPHosts(7789);
|
||||
});
|
||||
|
||||
services.AddTransientTcpClient(config =>
|
||||
{
|
||||
config.SetRemoteIPHost("127.0.0.1:7789");
|
||||
});
|
||||
|
||||
services.AddTcpService<ITcpDmtpService, TcpDmtpService>(config =>
|
||||
{
|
||||
config.SetListenIPHosts(8848);
|
||||
});
|
||||
|
||||
////<2F><><EFBFBD><EFBFBD>˲̬Tcp<63><70><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
////˲̬<CBB2><CCAC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>host<73><74><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Լ<EFBFBD>start<72><74><EFBFBD><EFBFBD>Ҫ<EFBFBD>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>
|
||||
//services.AddScopedSetupConfigObject<ITcpService, TcpService>(config =>
|
||||
//{
|
||||
// config.SetListenIPHosts(7789);
|
||||
//});
|
||||
});
|
||||
|
||||
var host = builder.Build();
|
||||
|
||||
host.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"profiles": {
|
||||
"WorkerService_NET6": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"environmentVariables": {
|
||||
"DOTNET_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Worker">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UserSecretsId>dotnet-WorkerService_NET6-1588a36c-1aa0-42fa-9971-921deb9d14ad</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
|
||||
<PackageReference Include="TouchSocket.Dmtp" Version="3.0.0" />
|
||||
<PackageReference Include="TouchSocket.Hosting" Version="3.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
|
||||
"Tcp": {
|
||||
"Iphost": "127.0.0.1:7789"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace WorkerService_NET8
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var builder = Host.CreateApplicationBuilder(args);
|
||||
builder.Services.AddHostedService<Worker>();
|
||||
#region <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Tcp<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
builder.Services.AddTcpService(config =>
|
||||
{
|
||||
config.SetListenIPHosts(7789);
|
||||
});
|
||||
#endregion
|
||||
var host = builder.Build();
|
||||
host.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"WorkerService_NET8": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"environmentVariables": {
|
||||
"DOTNET_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace WorkerService_NET8
|
||||
{
|
||||
public class Worker : BackgroundService
|
||||
{
|
||||
private readonly ILogger<Worker> _logger;
|
||||
|
||||
public Worker(ILogger<Worker> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
if (_logger.IsEnabled(LogLevel.Information))
|
||||
{
|
||||
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
|
||||
}
|
||||
await Task.Delay(1000, stoppingToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Worker">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UserSecretsId>dotnet-WorkerService_NET8-6a9dbdb7-5270-45c4-b5e3-0e9ebc959c04</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
|
||||
<PackageReference Include="TouchSocket.Hosting" Version="3.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
46
examples-host/Examples.Host/Examples.Host.sln
Normal file
46
examples-host/Examples.Host/Examples.Host.sln
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.8.34322.80
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Aspnetcore", "Aspnetcore", "{317E3933-5817-49B4-9C2B-067DA6D051EF}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApplication_NET6", "Aspnetcore\WebApplication_NET6\WebApplication_NET6.csproj", "{9F419CC4-F34E-4B6C-B2B8-F86FE7A4DE52}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BackgroundService", "BackgroundService", "{D2725070-7A5A-4336-9B00-6031B430536F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkerService_NET6", "BackgroundService\WorkerService_NET6\WorkerService_NET6.csproj", "{3E985BD5-A861-4955-AA5B-91729EF1C0CD}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkerService_NET8", "BackgroundService\WorkerService_Net7\WorkerService_NET8\WorkerService_NET8.csproj", "{190A2386-CD43-4353-8F73-7D2E44614154}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{9F419CC4-F34E-4B6C-B2B8-F86FE7A4DE52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9F419CC4-F34E-4B6C-B2B8-F86FE7A4DE52}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9F419CC4-F34E-4B6C-B2B8-F86FE7A4DE52}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9F419CC4-F34E-4B6C-B2B8-F86FE7A4DE52}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3E985BD5-A861-4955-AA5B-91729EF1C0CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3E985BD5-A861-4955-AA5B-91729EF1C0CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3E985BD5-A861-4955-AA5B-91729EF1C0CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3E985BD5-A861-4955-AA5B-91729EF1C0CD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{190A2386-CD43-4353-8F73-7D2E44614154}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{190A2386-CD43-4353-8F73-7D2E44614154}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{190A2386-CD43-4353-8F73-7D2E44614154}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{190A2386-CD43-4353-8F73-7D2E44614154}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{9F419CC4-F34E-4B6C-B2B8-F86FE7A4DE52} = {317E3933-5817-49B4-9C2B-067DA6D051EF}
|
||||
{3E985BD5-A861-4955-AA5B-91729EF1C0CD} = {D2725070-7A5A-4336-9B00-6031B430536F}
|
||||
{190A2386-CD43-4353-8F73-7D2E44614154} = {D2725070-7A5A-4336-9B00-6031B430536F}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {04D833EF-6EB7-4BAA-A5F0-1C587472EA86}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
11
examples/Adapter/AdapterConsoleApp/AdapterConsoleApp.csproj
Normal file
11
examples/Adapter/AdapterConsoleApp/AdapterConsoleApp.csproj
Normal file
@@ -0,0 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="TouchSocket" Version="3.0.3" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
274
examples/Adapter/AdapterConsoleApp/Program.cs
Normal file
274
examples/Adapter/AdapterConsoleApp/Program.cs
Normal file
@@ -0,0 +1,274 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace AdapterConsoleApp
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
private static async Task Main(string[] args)
|
||||
{
|
||||
var service = await CreateService();
|
||||
var client = await CreateClient();
|
||||
|
||||
ConsoleLogger.Default.Info("输入任意内容,回车发送(将会循环发送10次)");
|
||||
while (true)
|
||||
{
|
||||
var str = Console.ReadLine();
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
await client.SendAsync(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<TcpClient> CreateClient()
|
||||
{
|
||||
var client = new TcpClient();
|
||||
//载入配置
|
||||
await client.SetupAsync(new TouchSocketConfig()
|
||||
.SetRemoteIPHost("127.0.0.1:7789")
|
||||
.SetTcpDataHandlingAdapter(() => new MyDataHandleAdapter())
|
||||
.ConfigureContainer(a =>
|
||||
{
|
||||
a.AddConsoleLogger();//添加一个日志注入
|
||||
}));
|
||||
|
||||
await client.ConnectAsync();//调用连接,当连接不成功时,会抛出异常。
|
||||
client.Logger.Info("客户端成功连接");
|
||||
return client;
|
||||
}
|
||||
|
||||
private static async Task<TcpService> CreateService()
|
||||
{
|
||||
var service = new TcpService();
|
||||
service.Received = (client, e) =>
|
||||
{
|
||||
//从客户端收到信息
|
||||
var mes = e.ByteBlock.Span.ToString(Encoding.UTF8);
|
||||
client.Logger.Info($"已从{client.Id}接收到信息:{mes}");
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
await service.SetupAsync(new TouchSocketConfig()//载入配置
|
||||
.SetListenIPHosts("tcp://127.0.0.1:7789", 7790)//同时监听两个地址
|
||||
.SetTcpDataHandlingAdapter(() => new PeriodPackageAdapter() { CacheTimeout=TimeSpan.FromSeconds(1) })
|
||||
.ConfigureContainer(a =>
|
||||
{
|
||||
a.AddConsoleLogger();//添加一个控制台日志注入(注意:在maui中控制台日志不可用)
|
||||
})
|
||||
.ConfigurePlugins(a =>
|
||||
{
|
||||
//a.Add();//此处可以添加插件
|
||||
}));
|
||||
await service.StartAsync();//启动
|
||||
service.Logger.Info("服务器已启动");
|
||||
return service;
|
||||
}
|
||||
}
|
||||
|
||||
internal class MyDataHandleAdapter : SingleStreamDataHandlingAdapter
|
||||
{
|
||||
/// <summary>
|
||||
/// 包剩余长度
|
||||
/// </summary>
|
||||
private byte m_surPlusLength;
|
||||
|
||||
/// <summary>
|
||||
/// 临时包,此包仅当前实例储存
|
||||
/// </summary>
|
||||
private ByteBlock m_tempByteBlock;
|
||||
|
||||
public override bool CanSendRequestInfo => false;
|
||||
|
||||
public override bool CanSplicingSend => false;
|
||||
|
||||
protected override async Task PreviewReceivedAsync(ByteBlock byteBlock)
|
||||
{
|
||||
//收到从原始流式数据。
|
||||
|
||||
var buffer = byteBlock.TotalMemory.GetArray().Array;
|
||||
var r = byteBlock.Length;
|
||||
if (this.m_tempByteBlock == null)//如果没有临时包,则直接分包。
|
||||
{
|
||||
await this.SplitPackageAsync(buffer, 0, r);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.m_surPlusLength == r)//接收长度正好等于剩余长度,组合完数据以后直接处理数据。
|
||||
{
|
||||
this.m_tempByteBlock.Write(new ReadOnlySpan<byte>(buffer, 0, this.m_surPlusLength));
|
||||
await this.PreviewHandleAsync(this.m_tempByteBlock);
|
||||
this.m_tempByteBlock = null;
|
||||
this.m_surPlusLength = 0;
|
||||
}
|
||||
else if (this.m_surPlusLength < r)//接收长度大于剩余长度,先组合包,然后处理包,然后将剩下的分包。
|
||||
{
|
||||
this.m_tempByteBlock.Write(new ReadOnlySpan<byte>(buffer, 0, this.m_surPlusLength));
|
||||
await this.PreviewHandleAsync(this.m_tempByteBlock);
|
||||
this.m_tempByteBlock = null;
|
||||
await this.SplitPackageAsync(buffer, this.m_surPlusLength, r);
|
||||
}
|
||||
else//接收长度小于剩余长度,无法处理包,所以必须先组合包,然后等下次接收。
|
||||
{
|
||||
this.m_tempByteBlock.Write(new ReadOnlySpan<byte>(buffer, 0, r));
|
||||
this.m_surPlusLength -= (byte)r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected override async Task PreviewSendAsync(ReadOnlyMemory<byte> memory)
|
||||
{
|
||||
//在发送流式数据之前
|
||||
|
||||
var length = memory.Length;
|
||||
if (length > byte.MaxValue)//超长判断
|
||||
{
|
||||
throw new OverlengthException("发送数据太长。");
|
||||
}
|
||||
|
||||
//从内存池申请内存块,因为此处数据绝不超过255,所以避免内存池碎片化,每次申请1K
|
||||
//ByteBlock byteBlock = new ByteBlock(dataLen+1);//实际写法。
|
||||
using (var byteBlock = new ByteBlock(1024))
|
||||
{
|
||||
byteBlock.WriteByte((byte)length);//先写长度
|
||||
byteBlock.Write(memory.Span);//再写数据
|
||||
await this.GoSendAsync(byteBlock.Memory);
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task PreviewSendAsync(IList<ArraySegment<byte>> transferBytes)
|
||||
{
|
||||
//使用拼接模式发送,在发送流式数据之前
|
||||
|
||||
var dataLen = 0;
|
||||
foreach (var item in transferBytes)
|
||||
{
|
||||
dataLen += item.Count;
|
||||
}
|
||||
if (dataLen > byte.MaxValue)//超长判断
|
||||
{
|
||||
throw new OverlengthException("发送数据太长。");
|
||||
}
|
||||
|
||||
//从内存池申请内存块,因为此处数据绝不超过255,所以避免内存池碎片化,每次申请1K
|
||||
//ByteBlock byteBlock = new ByteBlock(dataLen+1);//实际写法。
|
||||
|
||||
using (var byteBlock = new ByteBlock(1024))
|
||||
{
|
||||
byteBlock.WriteByte((byte)dataLen);//先写长度
|
||||
foreach (var item in transferBytes)
|
||||
{
|
||||
byteBlock.Write(new ReadOnlySpan<byte>(item.Array, item.Offset, item.Count));//依次写入
|
||||
}
|
||||
await this.GoSendAsync(byteBlock.Memory);
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task PreviewSendAsync(IRequestInfo requestInfo)
|
||||
{
|
||||
//使用对象发送,在发送流式数据之前
|
||||
|
||||
if (requestInfo is MyClass myClass)
|
||||
{
|
||||
var data = myClass.Data ?? Array.Empty<byte>();
|
||||
if (data.Length > byte.MaxValue)//超长判断
|
||||
{
|
||||
throw new OverlengthException("发送数据太长。");
|
||||
}
|
||||
|
||||
//从内存池申请内存块,因为此处数据绝不超过255,所以避免内存池碎片化,每次申请1K
|
||||
//ByteBlock byteBlock = new ByteBlock(dataLen+1);//实际写法。
|
||||
using (var byteBlock = new ByteBlock(1024))
|
||||
{
|
||||
byteBlock.WriteByte((byte)data.Length);//先写长度
|
||||
byteBlock.WriteByte((byte)myClass.DataType);//然后数据类型
|
||||
byteBlock.WriteByte((byte)myClass.OrderType);//然后指令类型
|
||||
byteBlock.Write(data);//再写数据
|
||||
await this.GoSendAsync(byteBlock.Memory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理数据
|
||||
/// </summary>
|
||||
/// <param name="byteBlock"></param>
|
||||
private async Task PreviewHandleAsync(ByteBlock byteBlock)
|
||||
{
|
||||
try
|
||||
{
|
||||
await this.GoReceivedAsync(byteBlock, null);
|
||||
}
|
||||
finally
|
||||
{
|
||||
byteBlock.Dispose();//在框架里面将内存块释放
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 分解包
|
||||
/// </summary>
|
||||
/// <param name="dataBuffer"></param>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="r"></param>
|
||||
private async Task SplitPackageAsync(byte[] dataBuffer, int index, int r)
|
||||
{
|
||||
while (index < r)
|
||||
{
|
||||
var length = dataBuffer[index];
|
||||
var recedSurPlusLength = r - index - 1;
|
||||
if (recedSurPlusLength >= length)
|
||||
{
|
||||
var byteBlock = new ByteBlock(length);
|
||||
byteBlock.Write(new ReadOnlySpan<byte>(dataBuffer, index + 1, length));
|
||||
await this.PreviewHandleAsync(byteBlock);
|
||||
this.m_surPlusLength = 0;
|
||||
}
|
||||
else//半包
|
||||
{
|
||||
this.m_tempByteBlock = new ByteBlock(length);
|
||||
this.m_surPlusLength = (byte)(length - recedSurPlusLength);
|
||||
this.m_tempByteBlock.Write(new ReadOnlySpan<byte>(dataBuffer, index + 1, recedSurPlusLength));
|
||||
}
|
||||
index += length + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class MyClass : IRequestInfo
|
||||
{
|
||||
public OrderType OrderType { get; set; }
|
||||
public DataType DataType { get; set; }
|
||||
|
||||
public byte[] Data { get; set; }
|
||||
}
|
||||
|
||||
internal enum DataType : byte
|
||||
{
|
||||
Down = 0,
|
||||
Up = 1
|
||||
}
|
||||
|
||||
internal enum OrderType : byte
|
||||
{
|
||||
Hold = 0,
|
||||
Go = 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="TouchSocket" Version="3.0.3" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
73
examples/Adapter/AdapterTesterConsoleApp/Program.cs
Normal file
73
examples/Adapter/AdapterTesterConsoleApp/Program.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace AdapterTesterConsoleApp
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
private static async Task Main(string[] args)
|
||||
{
|
||||
var action = new ConsoleAction();
|
||||
action.OnException += Action_OnException;
|
||||
action.Add("1", "测试Tcp适配器", TcpDataAdapterTester);
|
||||
|
||||
action.ShowAll();
|
||||
await action.RunCommandLineAsync();
|
||||
}
|
||||
|
||||
private static void Action_OnException(Exception obj)
|
||||
{
|
||||
Console.WriteLine(obj.Message);
|
||||
}
|
||||
|
||||
private static void TcpDataAdapterTester()
|
||||
{
|
||||
//Tcp适配器测试
|
||||
//bufferLength的作用是模拟tcp接收缓存区,例如:
|
||||
|
||||
//发送数据为{0,1,2,3,4}时
|
||||
//当bufferLength=1时,会先接收一个字节,然后适配器判断无法解析,然后缓存,然后再接收下一个字节,直到成功解析。
|
||||
//该模式能很好的模拟网络很差的环境。
|
||||
//当bufferLength=8时,会先接收{0,1,2,3,4,0,1,2},然后适配器判断解析前五字节,然后缓存后三字节,然后再接收下一个续包,直到解析结束。
|
||||
|
||||
for (var bufferLength = 1; bufferLength < 1024 * 10; bufferLength += 1024)
|
||||
{
|
||||
var isSuccess = true;
|
||||
var data = new byte[] { 0, 1, 2, 3, 4 };
|
||||
var tester = TouchSocket.Sockets.TcpDataAdapterTester.CreateTester(new FixedHeaderPackageAdapter()
|
||||
, bufferLength, async (byteBlock, requestInfo) =>
|
||||
{
|
||||
//此处就是接收,如果是自定义适配器,可以将requestInfo强制转换为实际对象,然后判断数据的确定性
|
||||
if (byteBlock.Length != 5 || (!byteBlock.ToArray().SequenceEqual(data)))
|
||||
{
|
||||
isSuccess = false;
|
||||
}
|
||||
|
||||
await EasyTask.CompletedTask;
|
||||
});
|
||||
|
||||
//data是发送的数据,因为此处使用的是固定包头适配器,
|
||||
//发送前适配器会自动添加包头,所以,此处只发送数据即可。
|
||||
//如果测试的是自定义适配器,发送前没有封装的话,就需要自行构建发送数据。
|
||||
//随后的两个参数,10,10是测试次数,和期望次数,一般这两个值是相等的。
|
||||
//意为:本次数据将循环发送10次,且会接收10次。不然此处会一直阻塞。
|
||||
//最后一个参数是测试的最大超时时间。
|
||||
var time = tester.Run(data, 10, 10, 1000 * 10);
|
||||
Thread.Sleep(1000);
|
||||
Console.WriteLine($"测试结束,状态:{isSuccess},用时:{time}");
|
||||
}
|
||||
Console.WriteLine("测试结束");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="TouchSocket" Version="3.0.3" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
141
examples/Adapter/BetweenAndConsoleApp/Program.cs
Normal file
141
examples/Adapter/BetweenAndConsoleApp/Program.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Text;
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace BetweenAndConsoleApp
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
private static async Task Main(string[] args)
|
||||
{
|
||||
var service = await CreateService();
|
||||
var client = await CreateClient();
|
||||
|
||||
ConsoleLogger.Default.Info("按任意键发送10次");
|
||||
while (true)
|
||||
{
|
||||
Console.ReadKey();
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
await client.SendAsync("**12##12##");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<TcpClient> CreateClient()
|
||||
{
|
||||
var client = new TcpClient();
|
||||
//载入配置
|
||||
await client.SetupAsync(new TouchSocketConfig()
|
||||
.SetRemoteIPHost("127.0.0.1:7789")
|
||||
.SetTcpDataHandlingAdapter(() => new MyCustomBetweenAndDataHandlingAdapter())
|
||||
.ConfigureContainer(a =>
|
||||
{
|
||||
a.AddConsoleLogger();//添加一个日志注入
|
||||
}));
|
||||
|
||||
await client.ConnectAsync();//调用连接,当连接不成功时,会抛出异常。
|
||||
client.Logger.Info("客户端成功连接");
|
||||
return client;
|
||||
}
|
||||
|
||||
private static async Task<TcpService> CreateService()
|
||||
{
|
||||
var service = new TcpService();
|
||||
service.Received = (client, e) =>
|
||||
{
|
||||
//从客户端收到信息
|
||||
|
||||
if (e.RequestInfo is MyBetweenAndRequestInfo myRequest)
|
||||
{
|
||||
client.Logger.Info($"已从{client.Id}接收到:消息={Encoding.UTF8.GetString(myRequest.Body)}");
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
await service.SetupAsync(new TouchSocketConfig()//载入配置
|
||||
.SetListenIPHosts("tcp://127.0.0.1:7789", 7790)//同时监听两个地址
|
||||
.SetTcpDataHandlingAdapter(() => new MyCustomBetweenAndDataHandlingAdapter())
|
||||
.ConfigureContainer(a =>
|
||||
{
|
||||
a.AddConsoleLogger();//添加一个控制台日志注入(注意:在maui中控制台日志不可用)
|
||||
})
|
||||
.ConfigurePlugins(a =>
|
||||
{
|
||||
//a.Add();//此处可以添加插件
|
||||
}));
|
||||
await service.StartAsync();//启动
|
||||
service.Logger.Info("服务器已启动");
|
||||
return service;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 以**12##12##,Min=5为例。
|
||||
/// </summary>
|
||||
class MyBetweenAndRequestInfo : IBetweenAndRequestInfo
|
||||
{
|
||||
public MyBetweenAndRequestInfo()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public MyBetweenAndRequestInfo(byte[] body)
|
||||
{
|
||||
this.Body = body;
|
||||
}
|
||||
|
||||
public byte[] Body { get; private set; }
|
||||
|
||||
public void OnParsingBody(ReadOnlySpan<byte> body)
|
||||
{
|
||||
this.Body = body.ToArray();
|
||||
}
|
||||
|
||||
public bool OnParsingEndCode(ReadOnlySpan<byte> endCode)
|
||||
{
|
||||
return true;//该返回值决定,是否执行Receive
|
||||
}
|
||||
|
||||
public bool OnParsingStartCode(ReadOnlySpan<byte> startCode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class MyCustomBetweenAndDataHandlingAdapter : CustomBetweenAndDataHandlingAdapter<MyBetweenAndRequestInfo>
|
||||
{
|
||||
public MyCustomBetweenAndDataHandlingAdapter()
|
||||
{
|
||||
this.MinSize = 5;//表示,实际数据体不会小于5,例如“**12##12##”数据,解析后会解析成“12##12”
|
||||
|
||||
this.m_startCode=Encoding.UTF8.GetBytes("**");//可以为0长度字节,意味着没有起始标识。
|
||||
this.m_endCode=Encoding.UTF8.GetBytes("##");//必须为有效值。
|
||||
}
|
||||
|
||||
private readonly byte[] m_startCode;
|
||||
private readonly byte[] m_endCode;
|
||||
|
||||
public override byte[] StartCode => m_startCode;
|
||||
|
||||
public override byte[] EndCode => m_endCode;
|
||||
|
||||
protected override MyBetweenAndRequestInfo GetInstance()
|
||||
{
|
||||
return new MyBetweenAndRequestInfo();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="TouchSocket" Version="3.0.3" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
174
examples/Adapter/CustomAdapterConsoleApp/Program.cs
Normal file
174
examples/Adapter/CustomAdapterConsoleApp/Program.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Text;
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace CustomAdapterConsoleApp
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
private static async Task Main(string[] args)
|
||||
{
|
||||
var service = await CreateService();
|
||||
var client = await CreateClient();
|
||||
|
||||
ConsoleLogger.Default.Info("按任意键发送10次");
|
||||
while (true)
|
||||
{
|
||||
Console.ReadKey();
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
var myRequestInfo = new MyRequestInfo()
|
||||
{
|
||||
Body = Encoding.UTF8.GetBytes("hello"),
|
||||
DataType = (byte)i,
|
||||
OrderType = (byte)i
|
||||
};
|
||||
|
||||
//构建发送数据
|
||||
using (var byteBlock = new ByteBlock(1024))
|
||||
{
|
||||
byteBlock.WriteByte((byte)(myRequestInfo.Body.Length + 2));//先写长度,因为该长度还包含数据类型和指令类型,所以+2
|
||||
byteBlock.WriteByte((byte)myRequestInfo.DataType);//然后数据类型
|
||||
byteBlock.WriteByte((byte)myRequestInfo.OrderType);//然后指令类型
|
||||
byteBlock.Write(myRequestInfo.Body);//再写数据
|
||||
await client.SendAsync(byteBlock.Memory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<TcpClient> CreateClient()
|
||||
{
|
||||
var client = new TcpClient();
|
||||
//载入配置
|
||||
await client.SetupAsync(new TouchSocketConfig()
|
||||
.SetRemoteIPHost("127.0.0.1:7789")
|
||||
.SetTcpDataHandlingAdapter(() => new MyCustomDataHandlingAdapter())
|
||||
.ConfigureContainer(a =>
|
||||
{
|
||||
a.AddConsoleLogger();//添加一个日志注入
|
||||
}));
|
||||
|
||||
await client.ConnectAsync();//调用连接,当连接不成功时,会抛出异常。
|
||||
client.Logger.Info("客户端成功连接");
|
||||
return client;
|
||||
}
|
||||
|
||||
private static async Task<TcpService> CreateService()
|
||||
{
|
||||
var service = new TcpService();
|
||||
service.Received = (client, e) =>
|
||||
{
|
||||
//从客户端收到信息
|
||||
|
||||
if (e.RequestInfo is MyRequestInfo myRequest)
|
||||
{
|
||||
client.Logger.Info($"已从{client.Id}接收到:DataType={myRequest.DataType},OrderType={myRequest.OrderType},消息={Encoding.UTF8.GetString(myRequest.Body)}");
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
await service.SetupAsync(new TouchSocketConfig()//载入配置
|
||||
.SetListenIPHosts("tcp://127.0.0.1:7789", 7790)//同时监听两个地址
|
||||
.SetTcpDataHandlingAdapter(() => new MyCustomDataHandlingAdapter())
|
||||
.ConfigureContainer(a =>
|
||||
{
|
||||
a.AddConsoleLogger();//添加一个控制台日志注入(注意:在maui中控制台日志不可用)
|
||||
})
|
||||
.ConfigurePlugins(a =>
|
||||
{
|
||||
//a.Add();//此处可以添加插件
|
||||
}));
|
||||
await service.StartAsync();//启动
|
||||
service.Logger.Info("服务器已启动");
|
||||
return service;
|
||||
}
|
||||
}
|
||||
|
||||
internal class MyCustomDataHandlingAdapter : CustomDataHandlingAdapter<MyRequestInfo>
|
||||
{
|
||||
/// <summary>
|
||||
/// 筛选解析数据。实例化的TRequest会一直保存,直至解析成功,或手动清除。
|
||||
/// <para>当不满足解析条件时,请返回<see cref="FilterResult.Cache"/>,此时会保存<see cref="ByteBlock.CanReadLen"/>的数据</para>
|
||||
/// <para>当数据部分异常时,请移动<see cref="ByteBlock.Pos"/>到指定位置,然后返回<see cref="FilterResult.GoOn"/></para>
|
||||
/// <para>当完全满足解析条件时,请返回<see cref="FilterResult.Success"/>最后将<see cref="ByteBlock.Pos"/>移至指定位置。</para>
|
||||
/// </summary>
|
||||
/// <param name="byteBlock">字节块</param>
|
||||
/// <param name="beCached">是否为上次遗留对象,当该参数为True时,request也将是上次实例化的对象。</param>
|
||||
/// <param name="request">对象。</param>
|
||||
/// <param name="tempCapacity">缓存容量指导,指示当需要缓存时,应该申请多大的内存。</param>
|
||||
/// <returns></returns>
|
||||
protected override FilterResult Filter<TByteBlock>(ref TByteBlock byteBlock, bool beCached, ref MyRequestInfo request, ref int tempCapacity)
|
||||
{
|
||||
//以下解析思路为一次性解析,不考虑缓存的临时对象。
|
||||
|
||||
if (byteBlock.CanReadLength < 3)
|
||||
{
|
||||
return FilterResult.Cache;//当头部都无法解析时,直接缓存
|
||||
}
|
||||
|
||||
var pos = byteBlock.Position;//记录初始游标位置,防止本次无法解析时,回退游标。
|
||||
|
||||
var myRequestInfo = new MyRequestInfo();
|
||||
|
||||
//此操作实际上有两个作用,
|
||||
//1.填充header
|
||||
//2.将byteBlock.Pos递增3的长度。
|
||||
var header = byteBlock.ReadToSpan(3);//填充header
|
||||
|
||||
//因为第一个字节表示所有长度,而DataType、OrderType已经包含在了header里面。
|
||||
//所有只需呀再读取header[0]-2个长度即可。
|
||||
var bodyLength = (byte)(header[0] - 2);
|
||||
|
||||
if (bodyLength > byteBlock.CanReadLength)
|
||||
{
|
||||
//body数据不足。
|
||||
byteBlock.Position = pos;//回退游标
|
||||
return FilterResult.Cache;
|
||||
}
|
||||
else
|
||||
{
|
||||
//此操作实际上有两个作用,
|
||||
//1.填充body
|
||||
//2.将byteBlock.Pos递增bodyLength的长度。
|
||||
var body = byteBlock.ReadToSpan(bodyLength);
|
||||
|
||||
myRequestInfo.DataType = header[1];
|
||||
myRequestInfo.OrderType = header[2];
|
||||
myRequestInfo.Body = body.ToArray();
|
||||
request = myRequestInfo;//赋值ref
|
||||
return FilterResult.Success;//返回成功
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class MyRequestInfo : IRequestInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 自定义属性,Body
|
||||
/// </summary>
|
||||
public byte[] Body { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// 自定义属性,DataType
|
||||
/// </summary>
|
||||
public byte DataType { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// 自定义属性,OrderType
|
||||
/// </summary>
|
||||
public byte OrderType { get; internal set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="TouchSocket" Version="3.0.3" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
150
examples/Adapter/CustomBigFixedHeaderConsoleApp/Program.cs
Normal file
150
examples/Adapter/CustomBigFixedHeaderConsoleApp/Program.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
using System.Text;
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace CustomBigFixedHeaderConsoleApp
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
private static async Task Main(string[] args)
|
||||
{
|
||||
var service = await CreateService();
|
||||
var client = await CreateClient();
|
||||
|
||||
ConsoleLogger.Default.Info("按任意键发送10次");
|
||||
while (true)
|
||||
{
|
||||
Console.ReadKey();
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
var myRequestInfo = new MyBigFixedHeaderRequestInfo()
|
||||
{
|
||||
Body = Encoding.UTF8.GetBytes("hello"),
|
||||
DataType = (byte)i,
|
||||
OrderType = (byte)i
|
||||
};
|
||||
|
||||
//构建发送数据
|
||||
using (var byteBlock = new ByteBlock(1024))
|
||||
{
|
||||
byteBlock.WriteByte((byte)(myRequestInfo.Body.Length + 2));//先写长度,因为该长度还包含数据类型和指令类型,所以+2
|
||||
byteBlock.WriteByte((byte)myRequestInfo.DataType);//然后数据类型
|
||||
byteBlock.WriteByte((byte)myRequestInfo.OrderType);//然后指令类型
|
||||
byteBlock.Write(myRequestInfo.Body);//再写数据
|
||||
|
||||
await client.SendAsync(byteBlock.Memory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<TcpClient> CreateClient()
|
||||
{
|
||||
var client = new TcpClient();
|
||||
//载入配置
|
||||
await client.SetupAsync(new TouchSocketConfig()
|
||||
.SetRemoteIPHost("127.0.0.1:7789")
|
||||
.SetTcpDataHandlingAdapter(() => new MyCustomBigFixedHeaderDataHandlingAdapter())
|
||||
.ConfigureContainer(a =>
|
||||
{
|
||||
a.AddConsoleLogger();//添加一个日志注入
|
||||
}));
|
||||
|
||||
await client.ConnectAsync();//调用连接,当连接不成功时,会抛出异常。
|
||||
client.Logger.Info("客户端成功连接");
|
||||
return client;
|
||||
}
|
||||
|
||||
private static async Task<TcpService> CreateService()
|
||||
{
|
||||
var service = new TcpService();
|
||||
service.Received = (client, e) =>
|
||||
{
|
||||
//从客户端收到信息
|
||||
|
||||
if (e.RequestInfo is MyBigFixedHeaderRequestInfo myRequest)
|
||||
{
|
||||
client.Logger.Info($"已从{client.Id}接收到:DataType={myRequest.DataType},OrderType={myRequest.OrderType},消息={Encoding.UTF8.GetString(myRequest.Body)}");
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
await service.SetupAsync(new TouchSocketConfig()//载入配置
|
||||
.SetListenIPHosts("tcp://127.0.0.1:7789", 7790)//同时监听两个地址
|
||||
.SetTcpDataHandlingAdapter(() => new MyCustomBigFixedHeaderDataHandlingAdapter())
|
||||
.ConfigureContainer(a =>
|
||||
{
|
||||
a.AddConsoleLogger();//添加一个控制台日志注入(注意:在maui中控制台日志不可用)
|
||||
})
|
||||
.ConfigurePlugins(a =>
|
||||
{
|
||||
//a.Add();//此处可以添加插件
|
||||
}));
|
||||
await service.StartAsync();//启动
|
||||
service.Logger.Info("服务器已启动");
|
||||
return service;
|
||||
}
|
||||
}
|
||||
|
||||
class MyCustomBigFixedHeaderDataHandlingAdapter : CustomBigFixedHeaderDataHandlingAdapter<MyBigFixedHeaderRequestInfo>
|
||||
{
|
||||
public override int HeaderLength => 3;
|
||||
|
||||
protected override MyBigFixedHeaderRequestInfo GetInstance()
|
||||
{
|
||||
return new MyBigFixedHeaderRequestInfo();
|
||||
}
|
||||
}
|
||||
|
||||
class MyBigFixedHeaderRequestInfo : IBigFixedHeaderRequestInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 自定义属性,标识数据类型
|
||||
/// </summary>
|
||||
public byte DataType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 自定义属性,标识指令类型
|
||||
/// </summary>
|
||||
public byte OrderType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 自定义属性,标识实际数据
|
||||
/// </summary>
|
||||
public byte[] Body { get; set; }
|
||||
|
||||
private long m_bodyLength;
|
||||
|
||||
List<byte> m_bytes=new List<byte>();
|
||||
|
||||
#region 接口成员
|
||||
long IBigFixedHeaderRequestInfo.BodyLength => m_bodyLength;
|
||||
|
||||
void IBigFixedHeaderRequestInfo.OnAppendBody(ReadOnlySpan<byte> buffer)
|
||||
{
|
||||
//每次追加数据
|
||||
m_bytes.AddRange(buffer.ToArray());
|
||||
}
|
||||
|
||||
bool IBigFixedHeaderRequestInfo.OnFinished()
|
||||
{
|
||||
if (m_bytes.Count == this.m_bodyLength)
|
||||
{
|
||||
this.Body = m_bytes.ToArray();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IBigFixedHeaderRequestInfo.OnParsingHeader(ReadOnlySpan<byte> header)
|
||||
{
|
||||
//在该示例中,第一个字节表示后续的所有数据长度,但是header设置的是3,所以后续还应当接收length-2个长度。
|
||||
this.m_bodyLength = header[0] - 2;
|
||||
this.DataType = header[1];
|
||||
this.OrderType = header[2];
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="TouchSocket" Version="3.0.3" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
164
examples/Adapter/CustomBigUnfixedHeaderConsoleApp/Program.cs
Normal file
164
examples/Adapter/CustomBigUnfixedHeaderConsoleApp/Program.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
using System.Text;
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace CustomBigUnfixedHeaderConsoleApp
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
private static async Task Main(string[] args)
|
||||
{
|
||||
var service = await CreateService();
|
||||
var client = await CreateClient();
|
||||
|
||||
ConsoleLogger.Default.Info("按任意键发送10次");
|
||||
while (true)
|
||||
{
|
||||
Console.ReadKey();
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
var myRequestInfo = new MyBigUnfixedHeaderRequestInfo()
|
||||
{
|
||||
Body = Encoding.UTF8.GetBytes("hello"),
|
||||
DataType = (byte)i,
|
||||
OrderType = (byte)i
|
||||
};
|
||||
|
||||
//构建发送数据
|
||||
using (var byteBlock = new ByteBlock(1024))
|
||||
{
|
||||
byteBlock.WriteByte((byte)(myRequestInfo.Body.Length + 2));//先写长度,因为该长度还包含数据类型和指令类型,所以+2
|
||||
byteBlock.WriteByte((byte)myRequestInfo.DataType);//然后数据类型
|
||||
byteBlock.WriteByte((byte)myRequestInfo.OrderType);//然后指令类型
|
||||
byteBlock.Write(myRequestInfo.Body);//再写数据
|
||||
|
||||
await client.SendAsync(byteBlock.Memory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<TcpClient> CreateClient()
|
||||
{
|
||||
var client = new TcpClient();
|
||||
//载入配置
|
||||
await client.SetupAsync(new TouchSocketConfig()
|
||||
.SetRemoteIPHost("127.0.0.1:7789")
|
||||
.SetTcpDataHandlingAdapter(() => new MyCustomBigUnfixedHeaderDataHandlingAdapter())
|
||||
.ConfigureContainer(a =>
|
||||
{
|
||||
a.AddConsoleLogger();//添加一个日志注入
|
||||
}));
|
||||
|
||||
await client.ConnectAsync();//调用连接,当连接不成功时,会抛出异常。
|
||||
client.Logger.Info("客户端成功连接");
|
||||
return client;
|
||||
}
|
||||
|
||||
private static async Task<TcpService> CreateService()
|
||||
{
|
||||
var service = new TcpService();
|
||||
service.Received = (client, e) =>
|
||||
{
|
||||
//从客户端收到信息
|
||||
|
||||
if (e.RequestInfo is MyBigUnfixedHeaderRequestInfo myRequest)
|
||||
{
|
||||
client.Logger.Info($"已从{client.Id}接收到:DataType={myRequest.DataType},OrderType={myRequest.OrderType},消息={Encoding.UTF8.GetString(myRequest.Body)}");
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
await service.SetupAsync(new TouchSocketConfig()//载入配置
|
||||
.SetListenIPHosts("tcp://127.0.0.1:7789", 7790)//同时监听两个地址
|
||||
.SetTcpDataHandlingAdapter(() => new MyCustomBigUnfixedHeaderDataHandlingAdapter())
|
||||
.ConfigureContainer(a =>
|
||||
{
|
||||
a.AddConsoleLogger();//添加一个控制台日志注入(注意:在maui中控制台日志不可用)
|
||||
})
|
||||
.ConfigurePlugins(a =>
|
||||
{
|
||||
//a.Add();//此处可以添加插件
|
||||
}));
|
||||
await service.StartAsync();//启动
|
||||
service.Logger.Info("服务器已启动");
|
||||
return service;
|
||||
}
|
||||
}
|
||||
|
||||
class MyCustomBigUnfixedHeaderDataHandlingAdapter : CustomBigUnfixedHeaderDataHandlingAdapter<MyBigUnfixedHeaderRequestInfo>
|
||||
{
|
||||
protected override MyBigUnfixedHeaderRequestInfo GetInstance()
|
||||
{
|
||||
return new MyBigUnfixedHeaderRequestInfo();
|
||||
}
|
||||
}
|
||||
|
||||
class MyBigUnfixedHeaderRequestInfo : IBigUnfixedHeaderRequestInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 自定义属性,标识数据类型
|
||||
/// </summary>
|
||||
public byte DataType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 自定义属性,标识指令类型
|
||||
/// </summary>
|
||||
public byte OrderType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 自定义属性,标识实际数据
|
||||
/// </summary>
|
||||
public byte[] Body { get; set; }
|
||||
|
||||
|
||||
List<byte> m_bytes = new List<byte>();
|
||||
|
||||
private int m_headerLength;
|
||||
private long m_bodyLength;
|
||||
|
||||
#region 接口成员
|
||||
int IBigUnfixedHeaderRequestInfo.HeaderLength => m_headerLength;
|
||||
|
||||
long IBigUnfixedHeaderRequestInfo.BodyLength => m_bodyLength;
|
||||
|
||||
void IBigUnfixedHeaderRequestInfo.OnAppendBody(ReadOnlySpan<byte> buffer)
|
||||
{
|
||||
//每次追加数据
|
||||
m_bytes.AddRange(buffer.ToArray());
|
||||
}
|
||||
|
||||
bool IBigUnfixedHeaderRequestInfo.OnFinished()
|
||||
{
|
||||
if (m_bytes.Count == this.m_bodyLength)
|
||||
{
|
||||
this.Body = m_bytes.ToArray();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IBigUnfixedHeaderRequestInfo.OnParsingHeader<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
if (byteBlock.CanReadLength<3)//判断可读数据是否满足一定长度
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var pos= byteBlock.Position;//可以先记录游标位置,当解析不能进行时回退游标
|
||||
|
||||
//在该示例中,第一个字节表示后续的所有数据长度,但是header设置的是3,所以后续还应当接收length-2个长度。
|
||||
this.m_bodyLength = byteBlock.ReadByte() - 2;
|
||||
this.DataType = byteBlock.ReadByte();
|
||||
this.OrderType = byteBlock.ReadByte();
|
||||
|
||||
|
||||
//当执行到这里时,byteBlock.Position已经递增了3个长度。
|
||||
//所以无需再其他操作,如果是其他,则需要手动移动byteBlock.Position到指定位置。
|
||||
|
||||
this.m_headerLength = 3;//表示Header消耗了3个字节,实际上可以省略这一行,但是为了性能,最好加上
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="TouchSocket" Version="3.0.3" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
159
examples/Adapter/CustomFixedHeaderConsoleApp/Program.cs
Normal file
159
examples/Adapter/CustomFixedHeaderConsoleApp/Program.cs
Normal file
@@ -0,0 +1,159 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Text;
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace CustomFixedHeaderConsoleApp
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
private static async Task Main(string[] args)
|
||||
{
|
||||
var service = await CreateService();
|
||||
var client =await CreateClient();
|
||||
|
||||
ConsoleLogger.Default.Info("按任意键发送10次");
|
||||
while (true)
|
||||
{
|
||||
Console.ReadKey();
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
var myRequestInfo = new MyFixedHeaderRequestInfo()
|
||||
{
|
||||
Body = Encoding.UTF8.GetBytes("hello"),
|
||||
DataType = (byte)i,
|
||||
OrderType = (byte)i
|
||||
};
|
||||
|
||||
//构建发送数据
|
||||
using (var byteBlock = new ByteBlock(1024))
|
||||
{
|
||||
byteBlock.WriteByte((byte)(myRequestInfo.Body.Length + 2));//先写长度,因为该长度还包含数据类型和指令类型,所以+2
|
||||
byteBlock.WriteByte((byte)myRequestInfo.DataType);//然后数据类型
|
||||
byteBlock.WriteByte((byte)myRequestInfo.OrderType);//然后指令类型
|
||||
byteBlock.Write(myRequestInfo.Body);//再写数据
|
||||
|
||||
await client.SendAsync(byteBlock.Memory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<TcpClient> CreateClient()
|
||||
{
|
||||
var client = new TcpClient();
|
||||
//载入配置
|
||||
await client.SetupAsync(new TouchSocketConfig()
|
||||
.SetRemoteIPHost("127.0.0.1:7789")
|
||||
.SetTcpDataHandlingAdapter(() => new MyFixedHeaderCustomDataHandlingAdapter())
|
||||
.ConfigureContainer(a =>
|
||||
{
|
||||
a.AddConsoleLogger();//添加一个日志注入
|
||||
}));
|
||||
|
||||
await client.ConnectAsync();//调用连接,当连接不成功时,会抛出异常。
|
||||
client.Logger.Info("客户端成功连接");
|
||||
return client;
|
||||
}
|
||||
|
||||
private static async Task<TcpService> CreateService()
|
||||
{
|
||||
var service = new TcpService();
|
||||
service.Received = (client, e) =>
|
||||
{
|
||||
//从客户端收到信息
|
||||
|
||||
if (e.RequestInfo is MyFixedHeaderRequestInfo myRequest)
|
||||
{
|
||||
client.Logger.Info($"已从{client.Id}接收到:DataType={myRequest.DataType},OrderType={myRequest.OrderType},消息={Encoding.UTF8.GetString(myRequest.Body)}");
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
await service.SetupAsync(new TouchSocketConfig()//载入配置
|
||||
.SetListenIPHosts("tcp://127.0.0.1:7789", 7790)//同时监听两个地址
|
||||
.SetTcpDataHandlingAdapter(() => new MyFixedHeaderCustomDataHandlingAdapter())
|
||||
.ConfigureContainer(a =>
|
||||
{
|
||||
a.AddConsoleLogger();//添加一个控制台日志注入(注意:在maui中控制台日志不可用)
|
||||
})
|
||||
.ConfigurePlugins(a =>
|
||||
{
|
||||
//a.Add();//此处可以添加插件
|
||||
}));
|
||||
await service.StartAsync();//启动
|
||||
service.Logger.Info("服务器已启动");
|
||||
return service;
|
||||
}
|
||||
}
|
||||
|
||||
public class MyFixedHeaderCustomDataHandlingAdapter : CustomFixedHeaderDataHandlingAdapter<MyFixedHeaderRequestInfo>
|
||||
{
|
||||
/// <summary>
|
||||
/// 接口实现,指示固定包头长度
|
||||
/// </summary>
|
||||
public override int HeaderLength => 3;
|
||||
|
||||
/// <summary>
|
||||
/// 获取新实例
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override MyFixedHeaderRequestInfo GetInstance()
|
||||
{
|
||||
return new MyFixedHeaderRequestInfo();
|
||||
}
|
||||
}
|
||||
|
||||
public class MyFixedHeaderRequestInfo : IFixedHeaderRequestInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 接口实现,标识数据长度
|
||||
/// </summary>
|
||||
public int BodyLength { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 自定义属性,标识数据类型
|
||||
/// </summary>
|
||||
public byte DataType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 自定义属性,标识指令类型
|
||||
/// </summary>
|
||||
public byte OrderType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 自定义属性,标识实际数据
|
||||
/// </summary>
|
||||
public byte[] Body { get; set; }
|
||||
|
||||
public bool OnParsingBody(ReadOnlySpan<byte> body)
|
||||
{
|
||||
if (body.Length == this.BodyLength)
|
||||
{
|
||||
this.Body = body.ToArray();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnParsingHeader(ReadOnlySpan<byte> header)
|
||||
{
|
||||
//在该示例中,第一个字节表示后续的所有数据长度,但是header设置的是3,所以后续还应当接收length-2个长度。
|
||||
this.BodyLength = header[0] - 2;
|
||||
this.DataType = header[1];
|
||||
this.OrderType = header[2];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="TouchSocket" Version="3.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
167
examples/Adapter/CustomUnfixedHeaderConsoleApp/Program.cs
Normal file
167
examples/Adapter/CustomUnfixedHeaderConsoleApp/Program.cs
Normal file
@@ -0,0 +1,167 @@
|
||||
using System.Text;
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace CustomUnfixedHeaderConsoleApp
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
private static async Task Main(string[] args)
|
||||
{
|
||||
var service = await CreateService();
|
||||
var client = await CreateClient();
|
||||
|
||||
ConsoleLogger.Default.Info("按任意键发送10次");
|
||||
while (true)
|
||||
{
|
||||
Console.ReadKey();
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
var myRequestInfo = new MyUnfixedHeaderRequestInfo()
|
||||
{
|
||||
Body = Encoding.UTF8.GetBytes("hello"),
|
||||
DataType = (byte)i,
|
||||
OrderType = (byte)i
|
||||
};
|
||||
|
||||
//构建发送数据
|
||||
using (var byteBlock = new ByteBlock(1024))
|
||||
{
|
||||
byteBlock.WriteByte((byte)(myRequestInfo.Body.Length + 2));//先写长度,因为该长度还包含数据类型和指令类型,所以+2
|
||||
byteBlock.WriteByte((byte)myRequestInfo.DataType);//然后数据类型
|
||||
byteBlock.WriteByte((byte)myRequestInfo.OrderType);//然后指令类型
|
||||
byteBlock.Write(myRequestInfo.Body);//再写数据
|
||||
|
||||
await client.SendAsync(byteBlock.Memory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<TcpClient> CreateClient()
|
||||
{
|
||||
var client = new TcpClient();
|
||||
//载入配置
|
||||
await client.SetupAsync(new TouchSocketConfig()
|
||||
.SetRemoteIPHost("127.0.0.1:7789")
|
||||
.SetTcpDataHandlingAdapter(() => new MyUnfixedHeaderCustomDataHandlingAdapter())
|
||||
.ConfigureContainer(a =>
|
||||
{
|
||||
a.AddConsoleLogger();//添加一个日志注入
|
||||
}));
|
||||
|
||||
await client.ConnectAsync();//调用连接,当连接不成功时,会抛出异常。
|
||||
client.Logger.Info("客户端成功连接");
|
||||
return client;
|
||||
}
|
||||
|
||||
private static async Task<TcpService> CreateService()
|
||||
{
|
||||
var service = new TcpService();
|
||||
service.Received = (client, e) =>
|
||||
{
|
||||
//从客户端收到信息
|
||||
|
||||
if (e.RequestInfo is MyUnfixedHeaderRequestInfo myRequest)
|
||||
{
|
||||
client.Logger.Info($"已从{client.Id}接收到:DataType={myRequest.DataType},OrderType={myRequest.OrderType},消息={Encoding.UTF8.GetString(myRequest.Body)}");
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
await service.SetupAsync(new TouchSocketConfig()//载入配置
|
||||
.SetListenIPHosts("tcp://127.0.0.1:7789", 7790)//同时监听两个地址
|
||||
.SetTcpDataHandlingAdapter(() => new MyUnfixedHeaderCustomDataHandlingAdapter())
|
||||
.ConfigureContainer(a =>
|
||||
{
|
||||
a.AddConsoleLogger();//添加一个控制台日志注入(注意:在maui中控制台日志不可用)
|
||||
})
|
||||
.ConfigurePlugins(a =>
|
||||
{
|
||||
//a.Add();//此处可以添加插件
|
||||
}));
|
||||
await service.StartAsync();//启动
|
||||
service.Logger.Info("服务器已启动");
|
||||
return service;
|
||||
}
|
||||
}
|
||||
|
||||
public class MyUnfixedHeaderCustomDataHandlingAdapter : CustomUnfixedHeaderDataHandlingAdapter<MyUnfixedHeaderRequestInfo>
|
||||
{
|
||||
protected override MyUnfixedHeaderRequestInfo GetInstance()
|
||||
{
|
||||
return new MyUnfixedHeaderRequestInfo();
|
||||
}
|
||||
}
|
||||
|
||||
public class MyUnfixedHeaderRequestInfo : IUnfixedHeaderRequestInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 接口实现,标识数据长度
|
||||
/// </summary>
|
||||
public int BodyLength { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 自定义属性,标识数据类型
|
||||
/// </summary>
|
||||
public byte DataType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 自定义属性,标识指令类型
|
||||
/// </summary>
|
||||
public byte OrderType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 自定义属性,标识实际数据
|
||||
/// </summary>
|
||||
public byte[] Body { get; set; }
|
||||
|
||||
|
||||
|
||||
public int HeaderLength { get; private set; }
|
||||
|
||||
public bool OnParsingBody(ReadOnlySpan<byte> body)
|
||||
{
|
||||
if (body.Length == this.BodyLength)
|
||||
{
|
||||
this.Body = body.ToArray();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public bool OnParsingHeader<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlock
|
||||
{
|
||||
//在使用不固定包头解析时
|
||||
|
||||
//【首先】需要先解析包头
|
||||
if (byteBlock.CanReadLength < 3)
|
||||
{
|
||||
//即直接缓存
|
||||
return false;
|
||||
}
|
||||
|
||||
//先保存一下初始游标,如果解析时还需要缓存,可能需要回退游标
|
||||
var position = byteBlock.Position;
|
||||
|
||||
//【然后】ReadToSpan会递增游标,所以不需要再递增游标
|
||||
var header = byteBlock.ReadToSpan(3);
|
||||
|
||||
//如果使用Span自行裁剪的话,就需要手动递增游标
|
||||
//var header=byteBlock.Span.Slice(position,3);
|
||||
//byteBlock.Position += 3;
|
||||
|
||||
//【然后】解析包头,和BodyLength
|
||||
//在该示例中,第一个字节表示后续的所有数据长度,但是header设置的是3,所以后续还应当接收length-2个长度。
|
||||
this.BodyLength = header[0] - 2;
|
||||
this.DataType = header[1];
|
||||
this.OrderType = header[2];
|
||||
|
||||
//【最后】对HeaderLength做有效赋值
|
||||
this.HeaderLength = 3;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user