整理:重新追踪文件

This commit is contained in:
若汝棋茗
2024-11-17 20:53:15 +08:00
parent 3bc98edc6e
commit 2aab3cf78f
2060 changed files with 333080 additions and 0 deletions

4
.gitattributes vendored Normal file
View File

@@ -0,0 +1,4 @@
*.js linguist-language=csharp
*.css linguist-language=csharp
*.html linguist-language=csharp
*.java linguist-language=csharp

View 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` 仓库 DEMODEMO 提供最简单的错误逻辑代码,否则将无法得到答复。⚠⚠**
您的代码下载地址?
---
### 期待结果
期待的结果是?
---

View 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我们会尽快处理。

View 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: 慷慨赞助

View 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: |
感谢你的参与!

View 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

View 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
View 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
View File

@@ -0,0 +1,3 @@
{
"commentTranslate.targetLanguage": "en",
}

View 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:
- .*

View 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
View 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
View 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
View 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">
[![NuGet(TouchSocket)](https://img.shields.io/nuget/v/TouchSocket.svg?label=TouchSocket)](https://www.nuget.org/packages/TouchSocket/)
[![NuGet(TouchSocket)](https://img.shields.io/nuget/dt/TouchSocket.svg)](https://www.nuget.org/packages/TouchSocket/)
[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)
[![star](https://gitee.com/RRQM_Home/TouchSocket/badge/star.svg?theme=gvp)](https://gitee.com/RRQM_Home/TouchSocket/stargazers)
[![fork](https://gitee.com/RRQM_Home/TouchSocket/badge/fork.svg?theme=gvp)](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>
[![NuGet(TouchSocket)](https://img.shields.io/github/stars/RRQM/TouchSocket?logo=github)](https://github.com/RRQM/TouchSocket)
</div>
<div align="center">
三十功名尘与土,八千里路云和月。
</div>
## 🎀Description
![Alt](https://repobeats.axiom.co/api/embed/7b543e0b31f0488b08dfd319fafca0044dfd1050.svg "Repobeats analytics image")
'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.
![dotnetchina](https://images.gitee.com/uploads/images/2021/0324/120117_2da9922c_416720.png "132645_21007ea0_974299.png")

190
README.zh.md Normal file
View 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">
[![NuGet(TouchSocket)](https://img.shields.io/nuget/v/TouchSocket.svg?label=TouchSocket)](https://www.nuget.org/packages/TouchSocket/)
[![NuGet(TouchSocket)](https://img.shields.io/nuget/dt/TouchSocket.svg)](https://www.nuget.org/packages/TouchSocket/)
[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)
[![star](https://gitee.com/RRQM_Home/TouchSocket/badge/star.svg?theme=gvp)](https://gitee.com/RRQM_Home/TouchSocket/stargazers)
[![fork](https://gitee.com/RRQM_Home/TouchSocket/badge/fork.svg?theme=gvp)](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>
[![NuGet(TouchSocket)](https://img.shields.io/github/stars/RRQM/TouchSocket?logo=github)](https://github.com/RRQM/TouchSocket)
</div>
<div align="center">
三十功名尘与土,八千里路云和月。
</div>
## 🎀描述
![Alt](https://repobeats.axiom.co/api/embed/7b543e0b31f0488b08dfd319fafca0044dfd1050.svg "Repobeats analytics image")
`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) 组织。
![dotnetchina](https://images.gitee.com/uploads/images/2021/0324/120117_2da9922c_416720.png "132645_21007ea0_974299.png")

View 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

View 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();
}
}
}

View 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;
}
}

View 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);
}
}
}

View 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; }
}
}

View 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; }
}
}

View 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))
{
}
}
}
}
}

View 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;
}
}
}

View 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
{
}
}

View 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
{
}
}

View 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();
}
}
}

View 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();
}
}
}
}

View 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)
{
}
}
}

View 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

View 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

View 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";
}
}
}

View 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

View 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);
}
}
}

View 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

View 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>

View 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
View 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());
}
}
}
}

View 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

View File

@@ -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>

View 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; }
}
}

View 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>

View 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");
}
}
}

View 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>

View 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
}

View File

@@ -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>

View 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
}

View 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) { }
}
}

View 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>

View 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; }
}
}

View File

@@ -0,0 +1,12 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"WebApiConsoleApp": {
"commandName": "Project",
"dotnetRunMessages": true,
"environmentVariables": {
"DOTNET_ENVIRONMENT": "Development"
}
}
}
}

View 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>

View 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;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View 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">20218<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>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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);

View 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();
});
})();

View 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();
});
})();

View File

@@ -0,0 +1 @@
本静态网页来源于https://gitee.com/iGaoWei/big-data-view

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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"
}
}
}
}

View File

@@ -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>

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@@ -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();
}
}
}

View File

@@ -0,0 +1,11 @@
{
"profiles": {
"WorkerService_NET6": {
"commandName": "Project",
"dotnetRunMessages": true,
"environmentVariables": {
"DOTNET_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -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>

View File

@@ -0,0 +1,12 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"Tcp": {
"Iphost": "127.0.0.1:7789"
}
}

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -0,0 +1,12 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"WorkerService_NET8": {
"commandName": "Project",
"dotnetRunMessages": true,
"environmentVariables": {
"DOTNET_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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>

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

View 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

View 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>

View 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
}
}

View File

@@ -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>

View 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("测试结束");
}
}
}

View File

@@ -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>

View 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();
}
}
}

View File

@@ -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>

View 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; }
}
}

View File

@@ -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>

View 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
}
}

View File

@@ -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>

View 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
}
}

View File

@@ -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>

View 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;
}
}
}

View File

@@ -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>

View 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