mirror of
https://github.com/RRQM/TouchSocket.git
synced 2025-12-17 17:06:45 +08:00
调整:源码格式
This commit is contained in:
215
TouchSocket.sln
Normal file
215
TouchSocket.sln
Normal file
@@ -0,0 +1,215 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.3.32825.248
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TouchSocket.Core", "Src\TouchSocket.Core\TouchSocket.Core.csproj", "{12471445-B5C5-49E2-A7AC-15782299C5A9}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TouchSocket", "Src\TouchSocket\TouchSocket.csproj", "{375335C0-529E-4EE9-B80B-A3EC861783A1}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TouchSocket.Http", "Src\TouchSocket.Http\TouchSocket.Http.csproj", "{5DA6EA96-6762-4390-89E3-EECA272487F8}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TouchSocket.WebApi", "Src\TouchSocket.WebApi\TouchSocket.WebApi.csproj", "{B2591766-592C-45BD-A1A5-1395ACAA7EEB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TouchSocket.JsonRpc", "Src\TouchSocket.JsonRpc\TouchSocket.JsonRpc.csproj", "{D31FBB16-D47E-4811-BBAA-D6E05C850DB8}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TouchSocket.Rpc", "Src\TouchSocket.Rpc\TouchSocket.Rpc.csproj", "{BA8EABBF-89EA-4D24-9066-7003ADC9CD66}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TouchSocket.XmlRpc", "Src\TouchSocket.XmlRpc\TouchSocket.XmlRpc.csproj", "{3EA36139-BC17-4A9A-8B34-B19CBB9DD60A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TouchSocket.Core.DependencyInjection", "Src\TouchSocket.Core.DependencyInjection\TouchSocket.Core.DependencyInjection.csproj", "{AF0DAD14-1212-43D2-86CF-B14463D46C46}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TouchSocket.AspNetCore", "Src\TouchSocket.AspNetCore\TouchSocket.AspNetCore.csproj", "{8846520A-DE8F-4FCF-8313-015D6C2D1B54}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "开源版", "开源版", "{065EA3E8-D90E-4074-9979-D651EE4E6B3E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TouchSocket.Dmtp", "Src\TouchSocket.Dmtp\TouchSocket.Dmtp.csproj", "{FA1CB5DE-B746-4E27-BFA2-CD727E086B0D}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "解决方案配置", "解决方案配置", "{8D840BCD-6E10-4BF0-A44A-DC7BD97C1ADA}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
Src\Directory.Build.props = Src\Directory.Build.props
|
||||
Src\Props\DirectoryPro.Build.props = Src\Props\DirectoryPro.Build.props
|
||||
TouchSocketVersion.props = TouchSocketVersion.props
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TouchSocket.NamedPipe", "Src\TouchSocket.NamedPipe\TouchSocket.NamedPipe.csproj", "{DB92CBD6-A2D9-490B-A2C7-004D8C45CE21}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TouchSocket.WebApi.Swagger", "Src\TouchSocket.WebApi.Swagger\TouchSocket.WebApi.Swagger.csproj", "{6D9EDD94-B7D2-4609-86A9-000EE8D3838C}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TouchSocket.Hosting", "Src\TouchSocket.Hosting\TouchSocket.Hosting.csproj", "{F2151B0D-E8C8-4434-9DDF-7112B708ED8A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TouchSocket.SerialPorts", "Src\TouchSocket.SerialPorts\TouchSocket.SerialPorts.csproj", "{BB60C1A6-D7EE-4252-B9E8-F68BECE57D0A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TouchSocket.Core.Autofac", "Src\TouchSocket.Core.Autofac\TouchSocket.Core.Autofac.csproj", "{EB7FF3DC-7DFD-4917-9A0A-01E2DD3F5406}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TouchSocket.Modbus", "Src\TouchSocket.Modbus\TouchSocket.Modbus.csproj", "{DE45D1DC-6764-433B-929C-F17147D338AC}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "源生成器", "源生成器", "{33F8723D-9853-4E00-97DB-521E34EC895C}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TouchSocket.Core.SourceGenerator", "Src\TouchSocket.Core.SourceGenerator\TouchSocket.Core.SourceGenerator.csproj", "{5FC2A71D-D865-4D53-A0B8-DCCABC9926A6}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TouchSocket.Rpc.SourceGenerator", "Src\TouchSocket.Rpc.SourceGenerator\TouchSocket.Rpc.SourceGenerator.csproj", "{C9A87AE1-1512-4AD8-8E0C-9F0028E0F72B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TouchSocket.Rpc.RateLimiting", "Src\TouchSocket.Rpc.RateLimiting\TouchSocket.Rpc.RateLimiting.csproj", "{781923FE-AF2E-4CB0-B7EF-98EA09DDEDB3}"
|
||||
EndProject
|
||||
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "TouchSocket.Shared", "Src\TouchSocket.Shared\TouchSocket.Shared.shproj", "{BA463F9B-D28C-4C8A-AEB7-ECDC7FC1C3A1}"
|
||||
EndProject
|
||||
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "TouchSocket.SourceGenerator.SharedProject", "Src\TouchSocket.SourceGenerator.SharedProject\TouchSocket.SourceGenerator.SharedProject.shproj", "{5D348476-DB26-4BBA-AB83-272412C3BE71}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TouchSocket.XmlRpc.SourceGenerator", "Src\TouchSocket.XmlRpc.SourceGenerator\TouchSocket.XmlRpc.SourceGenerator.csproj", "{4F50F291-2110-4D10-B5F7-9F2AAC40AA27}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TouchSocket.Dmtp.SourceGenerator", "Src\TouchSocket.Dmtp.SourceGenerator\TouchSocket.Dmtp.SourceGenerator.csproj", "{087F7541-0310-4EC6-B3D0-595AF37E30C8}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TouchSocket.JsonRpc.SourceGenerator", "Src\TouchSocket.JsonRpc.SourceGenerator\TouchSocket.JsonRpc.SourceGenerator.csproj", "{8C2A5D9A-5A3B-4643-922A-71344C5E5991}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TouchSocket.WebApi.SourceGenerator", "Src\TouchSocket.WebApi.SourceGenerator\TouchSocket.WebApi.SourceGenerator.csproj", "{0A228D44-AE15-4515-A85A-DC9026E52615}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TouchSocket.Mqtt", "Src\TouchSocket.Mqtt\TouchSocket.Mqtt.csproj", "{201772F1-8C5E-4815-85F2-0CD8507006E7}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{12471445-B5C5-49E2-A7AC-15782299C5A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{12471445-B5C5-49E2-A7AC-15782299C5A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{12471445-B5C5-49E2-A7AC-15782299C5A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{12471445-B5C5-49E2-A7AC-15782299C5A9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{375335C0-529E-4EE9-B80B-A3EC861783A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{375335C0-529E-4EE9-B80B-A3EC861783A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{375335C0-529E-4EE9-B80B-A3EC861783A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{375335C0-529E-4EE9-B80B-A3EC861783A1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5DA6EA96-6762-4390-89E3-EECA272487F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5DA6EA96-6762-4390-89E3-EECA272487F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5DA6EA96-6762-4390-89E3-EECA272487F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5DA6EA96-6762-4390-89E3-EECA272487F8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B2591766-592C-45BD-A1A5-1395ACAA7EEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B2591766-592C-45BD-A1A5-1395ACAA7EEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B2591766-592C-45BD-A1A5-1395ACAA7EEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B2591766-592C-45BD-A1A5-1395ACAA7EEB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D31FBB16-D47E-4811-BBAA-D6E05C850DB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D31FBB16-D47E-4811-BBAA-D6E05C850DB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D31FBB16-D47E-4811-BBAA-D6E05C850DB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D31FBB16-D47E-4811-BBAA-D6E05C850DB8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BA8EABBF-89EA-4D24-9066-7003ADC9CD66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BA8EABBF-89EA-4D24-9066-7003ADC9CD66}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BA8EABBF-89EA-4D24-9066-7003ADC9CD66}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BA8EABBF-89EA-4D24-9066-7003ADC9CD66}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3EA36139-BC17-4A9A-8B34-B19CBB9DD60A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3EA36139-BC17-4A9A-8B34-B19CBB9DD60A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3EA36139-BC17-4A9A-8B34-B19CBB9DD60A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3EA36139-BC17-4A9A-8B34-B19CBB9DD60A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AF0DAD14-1212-43D2-86CF-B14463D46C46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AF0DAD14-1212-43D2-86CF-B14463D46C46}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AF0DAD14-1212-43D2-86CF-B14463D46C46}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AF0DAD14-1212-43D2-86CF-B14463D46C46}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8846520A-DE8F-4FCF-8313-015D6C2D1B54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8846520A-DE8F-4FCF-8313-015D6C2D1B54}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8846520A-DE8F-4FCF-8313-015D6C2D1B54}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8846520A-DE8F-4FCF-8313-015D6C2D1B54}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FA1CB5DE-B746-4E27-BFA2-CD727E086B0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FA1CB5DE-B746-4E27-BFA2-CD727E086B0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FA1CB5DE-B746-4E27-BFA2-CD727E086B0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FA1CB5DE-B746-4E27-BFA2-CD727E086B0D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DB92CBD6-A2D9-490B-A2C7-004D8C45CE21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DB92CBD6-A2D9-490B-A2C7-004D8C45CE21}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DB92CBD6-A2D9-490B-A2C7-004D8C45CE21}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DB92CBD6-A2D9-490B-A2C7-004D8C45CE21}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6D9EDD94-B7D2-4609-86A9-000EE8D3838C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6D9EDD94-B7D2-4609-86A9-000EE8D3838C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6D9EDD94-B7D2-4609-86A9-000EE8D3838C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6D9EDD94-B7D2-4609-86A9-000EE8D3838C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F2151B0D-E8C8-4434-9DDF-7112B708ED8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F2151B0D-E8C8-4434-9DDF-7112B708ED8A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F2151B0D-E8C8-4434-9DDF-7112B708ED8A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F2151B0D-E8C8-4434-9DDF-7112B708ED8A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BB60C1A6-D7EE-4252-B9E8-F68BECE57D0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BB60C1A6-D7EE-4252-B9E8-F68BECE57D0A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BB60C1A6-D7EE-4252-B9E8-F68BECE57D0A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BB60C1A6-D7EE-4252-B9E8-F68BECE57D0A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EB7FF3DC-7DFD-4917-9A0A-01E2DD3F5406}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EB7FF3DC-7DFD-4917-9A0A-01E2DD3F5406}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EB7FF3DC-7DFD-4917-9A0A-01E2DD3F5406}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EB7FF3DC-7DFD-4917-9A0A-01E2DD3F5406}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DE45D1DC-6764-433B-929C-F17147D338AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DE45D1DC-6764-433B-929C-F17147D338AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DE45D1DC-6764-433B-929C-F17147D338AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DE45D1DC-6764-433B-929C-F17147D338AC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5FC2A71D-D865-4D53-A0B8-DCCABC9926A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5FC2A71D-D865-4D53-A0B8-DCCABC9926A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5FC2A71D-D865-4D53-A0B8-DCCABC9926A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5FC2A71D-D865-4D53-A0B8-DCCABC9926A6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C9A87AE1-1512-4AD8-8E0C-9F0028E0F72B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C9A87AE1-1512-4AD8-8E0C-9F0028E0F72B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C9A87AE1-1512-4AD8-8E0C-9F0028E0F72B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C9A87AE1-1512-4AD8-8E0C-9F0028E0F72B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{781923FE-AF2E-4CB0-B7EF-98EA09DDEDB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{781923FE-AF2E-4CB0-B7EF-98EA09DDEDB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{781923FE-AF2E-4CB0-B7EF-98EA09DDEDB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{781923FE-AF2E-4CB0-B7EF-98EA09DDEDB3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4F50F291-2110-4D10-B5F7-9F2AAC40AA27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4F50F291-2110-4D10-B5F7-9F2AAC40AA27}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4F50F291-2110-4D10-B5F7-9F2AAC40AA27}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4F50F291-2110-4D10-B5F7-9F2AAC40AA27}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{087F7541-0310-4EC6-B3D0-595AF37E30C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{087F7541-0310-4EC6-B3D0-595AF37E30C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{087F7541-0310-4EC6-B3D0-595AF37E30C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{087F7541-0310-4EC6-B3D0-595AF37E30C8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8C2A5D9A-5A3B-4643-922A-71344C5E5991}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8C2A5D9A-5A3B-4643-922A-71344C5E5991}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8C2A5D9A-5A3B-4643-922A-71344C5E5991}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8C2A5D9A-5A3B-4643-922A-71344C5E5991}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0A228D44-AE15-4515-A85A-DC9026E52615}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0A228D44-AE15-4515-A85A-DC9026E52615}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0A228D44-AE15-4515-A85A-DC9026E52615}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0A228D44-AE15-4515-A85A-DC9026E52615}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{201772F1-8C5E-4815-85F2-0CD8507006E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{201772F1-8C5E-4815-85F2-0CD8507006E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{201772F1-8C5E-4815-85F2-0CD8507006E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{201772F1-8C5E-4815-85F2-0CD8507006E7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{12471445-B5C5-49E2-A7AC-15782299C5A9} = {065EA3E8-D90E-4074-9979-D651EE4E6B3E}
|
||||
{375335C0-529E-4EE9-B80B-A3EC861783A1} = {065EA3E8-D90E-4074-9979-D651EE4E6B3E}
|
||||
{5DA6EA96-6762-4390-89E3-EECA272487F8} = {065EA3E8-D90E-4074-9979-D651EE4E6B3E}
|
||||
{B2591766-592C-45BD-A1A5-1395ACAA7EEB} = {065EA3E8-D90E-4074-9979-D651EE4E6B3E}
|
||||
{D31FBB16-D47E-4811-BBAA-D6E05C850DB8} = {065EA3E8-D90E-4074-9979-D651EE4E6B3E}
|
||||
{BA8EABBF-89EA-4D24-9066-7003ADC9CD66} = {065EA3E8-D90E-4074-9979-D651EE4E6B3E}
|
||||
{3EA36139-BC17-4A9A-8B34-B19CBB9DD60A} = {065EA3E8-D90E-4074-9979-D651EE4E6B3E}
|
||||
{AF0DAD14-1212-43D2-86CF-B14463D46C46} = {065EA3E8-D90E-4074-9979-D651EE4E6B3E}
|
||||
{8846520A-DE8F-4FCF-8313-015D6C2D1B54} = {065EA3E8-D90E-4074-9979-D651EE4E6B3E}
|
||||
{FA1CB5DE-B746-4E27-BFA2-CD727E086B0D} = {065EA3E8-D90E-4074-9979-D651EE4E6B3E}
|
||||
{DB92CBD6-A2D9-490B-A2C7-004D8C45CE21} = {065EA3E8-D90E-4074-9979-D651EE4E6B3E}
|
||||
{6D9EDD94-B7D2-4609-86A9-000EE8D3838C} = {065EA3E8-D90E-4074-9979-D651EE4E6B3E}
|
||||
{F2151B0D-E8C8-4434-9DDF-7112B708ED8A} = {065EA3E8-D90E-4074-9979-D651EE4E6B3E}
|
||||
{BB60C1A6-D7EE-4252-B9E8-F68BECE57D0A} = {065EA3E8-D90E-4074-9979-D651EE4E6B3E}
|
||||
{EB7FF3DC-7DFD-4917-9A0A-01E2DD3F5406} = {065EA3E8-D90E-4074-9979-D651EE4E6B3E}
|
||||
{DE45D1DC-6764-433B-929C-F17147D338AC} = {065EA3E8-D90E-4074-9979-D651EE4E6B3E}
|
||||
{5FC2A71D-D865-4D53-A0B8-DCCABC9926A6} = {33F8723D-9853-4E00-97DB-521E34EC895C}
|
||||
{C9A87AE1-1512-4AD8-8E0C-9F0028E0F72B} = {33F8723D-9853-4E00-97DB-521E34EC895C}
|
||||
{781923FE-AF2E-4CB0-B7EF-98EA09DDEDB3} = {065EA3E8-D90E-4074-9979-D651EE4E6B3E}
|
||||
{BA463F9B-D28C-4C8A-AEB7-ECDC7FC1C3A1} = {065EA3E8-D90E-4074-9979-D651EE4E6B3E}
|
||||
{5D348476-DB26-4BBA-AB83-272412C3BE71} = {33F8723D-9853-4E00-97DB-521E34EC895C}
|
||||
{4F50F291-2110-4D10-B5F7-9F2AAC40AA27} = {33F8723D-9853-4E00-97DB-521E34EC895C}
|
||||
{087F7541-0310-4EC6-B3D0-595AF37E30C8} = {33F8723D-9853-4E00-97DB-521E34EC895C}
|
||||
{8C2A5D9A-5A3B-4643-922A-71344C5E5991} = {33F8723D-9853-4E00-97DB-521E34EC895C}
|
||||
{0A228D44-AE15-4515-A85A-DC9026E52615} = {33F8723D-9853-4E00-97DB-521E34EC895C}
|
||||
{201772F1-8C5E-4815-85F2-0CD8507006E7} = {065EA3E8-D90E-4074-9979-D651EE4E6B3E}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {AD1F133D-9B7E-4CD1-97E0-31D529E5CD90}
|
||||
RESX_NeutralResourcesLanguage = en-001
|
||||
EndGlobalSection
|
||||
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
||||
Src\TouchSocket.Shared\TouchSocket.Shared.projitems*{375335c0-529e-4ee9-b80b-a3ec861783a1}*SharedItemsImports = 5
|
||||
Src\TouchSocket.SourceGenerator.SharedProject\TouchSocket.SourceGenerator.SharedProject.projitems*{5d348476-db26-4bba-ab83-272412c3be71}*SharedItemsImports = 13
|
||||
Src\TouchSocket.SourceGenerator.SharedProject\TouchSocket.SourceGenerator.SharedProject.projitems*{5fc2a71d-d865-4d53-a0b8-dccabc9926a6}*SharedItemsImports = 5
|
||||
Src\TouchSocket.Shared\TouchSocket.Shared.projitems*{ba463f9b-d28c-4c8a-aeb7-ecdc7fc1c3a1}*SharedItemsImports = 13
|
||||
Src\TouchSocket.SourceGenerator.SharedProject\TouchSocket.SourceGenerator.SharedProject.projitems*{c9a87ae1-1512-4ad8-8e0c-9f0028e0f72b}*SharedItemsImports = 5
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
13
TouchSocketVersion.props
Normal file
13
TouchSocketVersion.props
Normal file
@@ -0,0 +1,13 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<BaseVersion>3.1.0</BaseVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'">
|
||||
<TouchSocketVersion>$(BaseVersion)</TouchSocketVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<TouchSocketVersion>$(BaseVersion)</TouchSocketVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
64
src/Props/DirectoryPro.Build.props
Normal file
64
src/Props/DirectoryPro.Build.props
Normal file
@@ -0,0 +1,64 @@
|
||||
<Project>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'!='net45' And '!$(MSBuildProjectName.EndsWith(`SourceGenerator`))'">
|
||||
<ProjectReference Include="..\TouchSocket.Core.SourceGenerator\TouchSocket.Core.SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(IsPro)'!='True'">
|
||||
<None Include="$(SolutionDir)\Src\Resources\LICENSE.txt" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(IsPro)'=='True'">
|
||||
<None Include="$(SolutionDir)\Src\Resources\LICENSEPRO.txt" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(IsPro)'!='True' And '$(Configuration)' == 'Release' And '$(IsSourceGenerator)'!='True'">
|
||||
<!--<IncludeSymbols>True</IncludeSymbols>
|
||||
<DebugSymbols>True</DebugSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<EmbedAllSources>True</EmbedAllSources>-->
|
||||
<PackageIcon>logo.png</PackageIcon>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
|
||||
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
|
||||
<DebugSymbols>True</DebugSymbols>
|
||||
<DebugType>Embedded</DebugType>
|
||||
<EmbedAllSources>True</EmbedAllSources>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(IsPro)'!='True' And '$(Configuration)' == 'Debug' And '$(IsSourceGenerator)'!='True'">
|
||||
<!--<IncludeSymbols>True</IncludeSymbols>
|
||||
<DebugSymbols>True</DebugSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<EmbedAllSources>True</EmbedAllSources>-->
|
||||
<PackageIcon>logo.png</PackageIcon>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
|
||||
<DebugSymbols>True</DebugSymbols>
|
||||
<DebugType>Embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(IsPro)'=='True' And '$(Configuration)' == 'Release' And '$(IsSourceGenerator)'!='True'">
|
||||
<PackageIcon>logo.png</PackageIcon>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
|
||||
<PackageLicenseFile>LICENSEPRO.txt</PackageLicenseFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(IsPro)'=='True' And '$(Configuration)' == 'Debug' And '$(IsSourceGenerator)'!='True'">
|
||||
<PackageIcon>logo.png</PackageIcon>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<PackageLicenseFile>LICENSEPRO.txt</PackageLicenseFile>
|
||||
<DebugSymbols>True</DebugSymbols>
|
||||
<DebugType>Embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- 包含PDB文件到主NuGet包 --><!--
|
||||
<ItemGroup Condition="'$(Configuration)' == 'Debug' And '$(GeneratePackageOnBuild)' == 'true'">
|
||||
<None Include="$(OutputPath)/$(TargetFramework)/$(AssemblyName).pdb"
|
||||
Pack="true"
|
||||
PackagePath="lib\$(TargetFramework)"
|
||||
Visible="false"/>
|
||||
</ItemGroup>-->
|
||||
|
||||
</Project>
|
||||
@@ -186,7 +186,7 @@ Apache License
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright (c) 2020-2023 若汝棋茗
|
||||
Copyright (c) 2020-2024 南昌高新区若汝棋茗软件研发工作室
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -198,4 +198,4 @@ Apache License
|
||||
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.
|
||||
limitations under the License.
|
||||
8
src/Resources/LICENSEPRO.txt
Normal file
8
src/Resources/LICENSEPRO.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
TouchSocketPro是TouchSocket的企业版,其99%功能与TouchSocket一致。所有版权归作者若汝棋茗所有。
|
||||
|
||||
TouchSocketPro许可证遵循:
|
||||
|
||||
1.限时免费测试,测试期间可参与商业使用。
|
||||
2.付费使用,购买后还须遵循相关使用协议,详情咨询若汝棋茗。
|
||||
|
||||
TouchSocketPro程序集源代码不公开开源,需要付费购买。
|
||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,16 @@
|
||||
; Shipped analyzer releases
|
||||
; https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
|
||||
|
||||
## Release 1.1
|
||||
|
||||
### New Rules
|
||||
|
||||
| Rule ID | Category | Severity | Notes |
|
||||
|---------------------|-----------------|----------|--------------------------------|
|
||||
| Package0001 | Package | Error | Package0001_AnalyzerName |
|
||||
| Package0002 | Package | Error | Package0002_AnalyzerName |
|
||||
| Plugin0001 | Plugin | Error | Plugin0001_AnalyzerName |
|
||||
| Plugin0002 | Plugin | Error | Plugin0002_AnalyzerName |
|
||||
| Plugin0003 | Plugin | Error | Plugin0003_AnalyzerName |
|
||||
| Plugin0004 | Plugin | Error | Plugin0004_AnalyzerName |
|
||||
| FastSerialize0001 | FastSerialize | Error | FastSerialize0001_AnalyzerName |
|
||||
@@ -0,0 +1,2 @@
|
||||
; Unshipped analyzer release
|
||||
|
||||
145
src/TouchSocket.Core.SourceGenerator/Analyzers/CoreAnalyzer.cs
Normal file
145
src/TouchSocket.Core.SourceGenerator/Analyzers/CoreAnalyzer.cs
Normal file
@@ -0,0 +1,145 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TouchSocket.SourceGenerator;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
internal class CoreAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
public const string IPlugin = "TouchSocket.Core.IPlugin";
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
|
||||
{
|
||||
get
|
||||
{
|
||||
return ImmutableArray.Create(
|
||||
DiagnosticDescriptors.m_rule_Package0001,
|
||||
DiagnosticDescriptors.m_rule_Package0002,
|
||||
DiagnosticDescriptors.m_rule_Plugin0001,
|
||||
DiagnosticDescriptors.m_rule_Plugin0002,
|
||||
DiagnosticDescriptors.m_rule_Plugin0003,
|
||||
DiagnosticDescriptors.m_rule_Plugin0004,
|
||||
DiagnosticDescriptors.m_rule_FastSerialize0001
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||
context.EnableConcurrentExecution();
|
||||
|
||||
//context.RegisterOperationAction(this.AnalyzeSymbol, OperationKind.PropertyReference);
|
||||
context.RegisterSymbolAction(this.AnalyzePluginSymbol, SymbolKind.NamedType);
|
||||
}
|
||||
|
||||
public static bool IsPluginInterface(INamedTypeSymbol namedTypeSymbol)
|
||||
{
|
||||
if (namedTypeSymbol.TypeKind != TypeKind.Interface)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (namedTypeSymbol.ToDisplayString() == IPlugin)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!namedTypeSymbol.IsInheritFrom(IPlugin))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void AnalyzePluginSymbol(SymbolAnalysisContext context)
|
||||
{
|
||||
//Debugger.Launch();
|
||||
var namedTypeSymbol = (INamedTypeSymbol)context.Symbol;
|
||||
|
||||
if (!IsPluginInterface(namedTypeSymbol))
|
||||
{
|
||||
return;
|
||||
}
|
||||
//if (namedTypeSymbol.ToDisplayString() == IPlugin)
|
||||
//{
|
||||
// return;
|
||||
//}
|
||||
//if (!namedTypeSymbol.IsInheritFrom(IPlugin))
|
||||
//{
|
||||
// return;
|
||||
//}
|
||||
|
||||
if (namedTypeSymbol.IsGenericType)
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.m_rule_Plugin0002, namedTypeSymbol.Locations[0]));
|
||||
return;
|
||||
}
|
||||
|
||||
var methods = namedTypeSymbol.GetMembers().OfType<IMethodSymbol>();
|
||||
var count = methods.Count();
|
||||
if (count != 1)
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.m_rule_Plugin0003, namedTypeSymbol.Locations[0]));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!namedTypeSymbol.HasAttribute(MethodInvokeSyntaxReceiver.DynamicMethod))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.m_rule_Plugin0004, namedTypeSymbol.Locations[0]));
|
||||
return;
|
||||
}
|
||||
foreach (var methodSymbol in methods)
|
||||
{
|
||||
if (methodSymbol.ReturnType.ToDisplayString() != typeof(Task).ToString())
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.m_rule_Plugin0001, methodSymbol.Locations[0]));
|
||||
return;
|
||||
}
|
||||
|
||||
if (methodSymbol.Parameters.Length != 2)
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.m_rule_Plugin0001, methodSymbol.Locations[0]));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!methodSymbol.Parameters[1].Type.IsInheritFrom("TouchSocket.Core.PluginEventArgs"))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.m_rule_Plugin0001, methodSymbol.Locations[0]));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AnalyzeSymbol(OperationAnalysisContext context)
|
||||
{
|
||||
//if (context.Operation is IPropertyReferenceOperation property)
|
||||
//{
|
||||
// if (property.Property.Name == "Buffer" && (property.Property.ContainingType.Name == "ByteBlock" || property.Property.ContainingType.Name == "ValueByteBlock"))
|
||||
// {
|
||||
// if (property.Parent is IPropertyReferenceOperation parent)
|
||||
// {
|
||||
// //Debugger.Launch();
|
||||
// if ((parent.Property.Name == "Length" || parent.Property.Name == "LongLength") && parent.Property.ContainingType.Name == "Array")
|
||||
// {
|
||||
// var diagnostic = Diagnostic.Create(DiagnosticDescriptors.m_rule_ByteBlock0001, parent.Syntax.GetLocation());
|
||||
// context.ReportDiagnostic(diagnostic);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
@@ -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 Microsoft.CodeAnalysis;
|
||||
|
||||
namespace TouchSocket;
|
||||
|
||||
internal static class DiagnosticDescriptors
|
||||
{
|
||||
#region IPackage
|
||||
|
||||
public static readonly DiagnosticDescriptor m_rule_Package0001 = new DiagnosticDescriptor(
|
||||
"Package0001",
|
||||
"判断实现IPackage接口的是结构体还是类",
|
||||
"只有结构体能直接实现IPackage,如果是类,请直接或间接继承PackageBase。",
|
||||
"Package", DiagnosticSeverity.Error, isEnabledByDefault: true);
|
||||
|
||||
public static readonly DiagnosticDescriptor m_rule_Package0002 = new DiagnosticDescriptor(
|
||||
"Package0002",
|
||||
"判断IPackage成员必须仍然为IPackage",
|
||||
"属性{0}的类型{1}并非IPackage,所以无法解决类型嵌套问题。",
|
||||
"Package", DiagnosticSeverity.Error, isEnabledByDefault: true);
|
||||
|
||||
#endregion IPackage
|
||||
|
||||
#region ByteBlock
|
||||
|
||||
#endregion ByteBlock
|
||||
|
||||
#region Plugin
|
||||
|
||||
public static readonly DiagnosticDescriptor m_rule_Plugin0001 = new DiagnosticDescriptor(
|
||||
"Plugin0001",
|
||||
"判断声明的插件接口是否符合定义",
|
||||
"声明的插件方法不符合定义,要求:返回值必须为Task,参数数量必须为2,第二个参数必须继承自PluginEventArgs。",
|
||||
"Plugin", DiagnosticSeverity.Error, isEnabledByDefault: true);
|
||||
|
||||
public static readonly DiagnosticDescriptor m_rule_Plugin0002 = new DiagnosticDescriptor(
|
||||
"Plugin0002",
|
||||
"判断声明的插件接口是否为泛型",
|
||||
"声明的插件不允许为泛型。",
|
||||
"Plugin", DiagnosticSeverity.Error, isEnabledByDefault: true);
|
||||
|
||||
public static readonly DiagnosticDescriptor m_rule_Plugin0003 = new DiagnosticDescriptor(
|
||||
"Plugin0003",
|
||||
"判断声明的插件接口是否仅包含一个方法",
|
||||
"声明的插件接口中必须有,且仅允许一个方法。",
|
||||
"Plugin", DiagnosticSeverity.Error, isEnabledByDefault: true);
|
||||
|
||||
public static readonly DiagnosticDescriptor m_rule_Plugin0004 = new DiagnosticDescriptor(
|
||||
"Plugin0004",
|
||||
"判断声明的插件接口是否包含[DynamicMethod]特性",
|
||||
"声明的插件接口必须标识[DynamicMethod]特性。",
|
||||
"Plugin", DiagnosticSeverity.Error, isEnabledByDefault: true);
|
||||
#endregion Plugin
|
||||
|
||||
#region FastSerialize
|
||||
public static readonly DiagnosticDescriptor m_rule_FastSerialize0001 = new DiagnosticDescriptor(
|
||||
"FastSerialize0001",
|
||||
"判断添加的类型是否已实现IPackage接口",
|
||||
"类型{0}并非IPackage,所以无法使用源生成Fast序列化。",
|
||||
"FastSerialize", DiagnosticSeverity.Error, isEnabledByDefault: true);
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace TouchSocket;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
internal class DiagnosticSuppressor : Microsoft.CodeAnalysis.Diagnostics.DiagnosticSuppressor
|
||||
{
|
||||
private static readonly SuppressionDescriptor m_rule_SAppMessage001 = new(
|
||||
"SAppMessage001",
|
||||
"CA1822",
|
||||
"AppMessage方法,所以抑制");
|
||||
public const string AppMessageAttribute = "TouchSocket.Core.AppMessageAttribute";
|
||||
|
||||
public override void ReportSuppressions(SuppressionAnalysisContext context)
|
||||
{
|
||||
foreach (var diagnostic in context.ReportedDiagnostics)
|
||||
{
|
||||
var syntaxNode = diagnostic.Location.SourceTree?.GetRoot(context.CancellationToken)
|
||||
.FindNode(diagnostic.Location.SourceSpan);
|
||||
|
||||
if (syntaxNode is not null)
|
||||
{
|
||||
var semanticModel = context.GetSemanticModel(syntaxNode.SyntaxTree);
|
||||
|
||||
var declaredSymbol = semanticModel.GetDeclaredSymbol(syntaxNode, context.CancellationToken);
|
||||
|
||||
if (declaredSymbol is IMethodSymbol methodSymbol)
|
||||
{
|
||||
var att = methodSymbol.HasAttribute(AppMessageAttribute);
|
||||
if (att)
|
||||
{
|
||||
context.ReportSuppression(Suppression.Create(m_rule_SAppMessage001, diagnostic));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions => ImmutableArray.Create(m_rule_SAppMessage001);
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 Microsoft.CodeAnalysis;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TouchSocket
|
||||
{
|
||||
internal static class Utils
|
||||
{
|
||||
public static bool IsInheritFrom(this ITypeSymbol typeSymbol, string baseType)
|
||||
{
|
||||
if (typeSymbol.ToDisplayString() == baseType)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeSymbol.BaseType != null)
|
||||
{
|
||||
var b = IsInheritFrom(typeSymbol.BaseType, baseType);
|
||||
if (b)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var item in typeSymbol.AllInterfaces)
|
||||
{
|
||||
var b = IsInheritFrom(item, baseType);
|
||||
if (b)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static string RenameCamelCase(this string str)
|
||||
{
|
||||
var firstChar = str[0];
|
||||
|
||||
if (firstChar == char.ToLowerInvariant(firstChar))
|
||||
{
|
||||
return str;
|
||||
}
|
||||
|
||||
var name = str.ToCharArray();
|
||||
name[0] = char.ToLowerInvariant(firstChar);
|
||||
|
||||
return new string(name);
|
||||
}
|
||||
|
||||
public static bool HasAttribute(this ISymbol symbol, INamedTypeSymbol attribute)
|
||||
{
|
||||
foreach (var attr in symbol.GetAttributes())
|
||||
{
|
||||
var attrClass = attr.AttributeClass;
|
||||
if (attrClass != null && attrClass.ToDisplayString() == attribute.ToDisplayString())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool HasAttribute(this ISymbol symbol, string attribute, out AttributeData attributeData)
|
||||
{
|
||||
foreach (var attr in symbol.GetAttributes())
|
||||
{
|
||||
var attrClass = attr.AttributeClass;
|
||||
if (attrClass != null && attrClass.ToDisplayString() == attribute)
|
||||
{
|
||||
attributeData = attr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
attributeData = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool HasFlags(int value, int flag)
|
||||
{
|
||||
return (value & flag) == flag;
|
||||
}
|
||||
|
||||
public static bool HasReturn(this IMethodSymbol method)
|
||||
{
|
||||
if (method.ReturnsVoid || method.ReturnType.ToDisplayString() == typeof(Task).FullName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,153 +5,165 @@
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//using System;
|
||||
//using System.Collections.Generic;
|
||||
//using System.Text;
|
||||
/*
|
||||
此代码由SourceGenerator工具直接生成,非必要请不要修改此处代码
|
||||
*/
|
||||
|
||||
//namespace TouchSocket.Core
|
||||
//{
|
||||
// /// <summary>
|
||||
// /// 源生成容器特性
|
||||
// /// </summary>
|
||||
// internal class GeneratorContainerAttribute : Attribute
|
||||
// {
|
||||
// }
|
||||
#pragma warning disable
|
||||
|
||||
// internal class BaseInjectAttribute : Attribute
|
||||
// {
|
||||
// /// <summary>
|
||||
// /// 注册类型
|
||||
// /// </summary>
|
||||
// public Type FromType { get; set; }
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
// /// <summary>
|
||||
// /// 实例类型
|
||||
// /// </summary>
|
||||
// public Type ToType { get; set; }
|
||||
namespace TouchSocket.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// 源生成容器特性
|
||||
/// </summary>
|
||||
/*GeneratedCode*/
|
||||
internal class GeneratorContainerAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// 注册额外键
|
||||
// /// </summary>
|
||||
// public string Key { get; set; }
|
||||
// }
|
||||
/*GeneratedCode*/
|
||||
internal class BaseInjectAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// 注册类型
|
||||
/// </summary>
|
||||
public Type FromType { get; set; }
|
||||
|
||||
// /// <summary>
|
||||
// /// 自动注入为单例。
|
||||
// /// </summary>
|
||||
// [AttributeUsage(AttributeTargets.Class| AttributeTargets.Interface, AllowMultiple = true)]
|
||||
// internal class AutoInjectForSingletonAttribute : BaseInjectAttribute
|
||||
// {
|
||||
// }
|
||||
/// <summary>
|
||||
/// 实例类型
|
||||
/// </summary>
|
||||
public Type ToType { get; set; }
|
||||
|
||||
// /// <summary>
|
||||
// /// 自动注入为瞬时。
|
||||
// /// </summary>
|
||||
// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true)]
|
||||
// internal class AutoInjectForTransientAttribute : BaseInjectAttribute
|
||||
// {
|
||||
// }
|
||||
/// <summary>
|
||||
/// 注册额外键
|
||||
/// </summary>
|
||||
public string Key { get; set; }
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// 添加单例注入。
|
||||
// /// <para>
|
||||
// /// 该标签仅添加在继承<see cref="ManualContainer"/>的容器上时有用。
|
||||
// /// </para>
|
||||
// /// </summary>
|
||||
// [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||
// internal class AddSingletonInjectAttribute : BaseInjectAttribute
|
||||
// {
|
||||
// /// <summary>
|
||||
// /// 添加单例注入。
|
||||
// /// <para>
|
||||
// /// 该标签仅添加在继承<see cref="ManualContainer"/>的容器上时有用。
|
||||
// /// </para>
|
||||
// /// </summary>
|
||||
// /// <param name="fromType">注册类型</param>
|
||||
// /// <param name="toType">实例类型</param>
|
||||
// /// <param name="key">注册额外键</param>
|
||||
// public AddSingletonInjectAttribute(Type fromType, Type toType, string key)
|
||||
// {
|
||||
// this.FromType = fromType;
|
||||
// this.ToType = toType;
|
||||
// this.Key = key;
|
||||
// }
|
||||
/// <summary>
|
||||
/// 自动注入为单例。
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true)]
|
||||
/*GeneratedCode*/
|
||||
internal class AutoInjectForSingletonAttribute : BaseInjectAttribute
|
||||
{
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// 添加单例注入。
|
||||
// /// <para>
|
||||
// /// 该标签仅添加在继承<see cref="ManualContainer"/>的容器上时有用。
|
||||
// /// </para>
|
||||
// /// </summary>
|
||||
// /// <param name="type">注册类型与实例类型一致</param>
|
||||
// public AddSingletonInjectAttribute(Type type) : this(type, type, null)
|
||||
// {
|
||||
// }
|
||||
/// <summary>
|
||||
/// 自动注入为瞬时。
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true)]
|
||||
/*GeneratedCode*/
|
||||
internal class AutoInjectForTransientAttribute : BaseInjectAttribute
|
||||
{
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// 添加单例注入。
|
||||
// /// <para>
|
||||
// /// 该标签仅添加在继承<see cref="ManualContainer"/>的容器上时有用。
|
||||
// /// </para>
|
||||
// /// </summary>
|
||||
// /// <param name="fromType">注册类型</param>
|
||||
// /// <param name="toType">实例类型</param>
|
||||
// public AddSingletonInjectAttribute(Type fromType, Type toType) : this(fromType, toType, null)
|
||||
// {
|
||||
// }
|
||||
// }
|
||||
/// <summary>
|
||||
/// 添加单例注入。
|
||||
/// <para>
|
||||
/// 该标签仅添加在继承<see cref="ManualContainer"/>的容器上时有用。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||
/*GeneratedCode*/
|
||||
internal class AddSingletonInjectAttribute : BaseInjectAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// 添加单例注入。
|
||||
/// <para>
|
||||
/// 该标签仅添加在继承<see cref="ManualContainer"/>的容器上时有用。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="fromType">注册类型</param>
|
||||
/// <param name="toType">实例类型</param>
|
||||
/// <param name="key">注册额外键</param>
|
||||
public AddSingletonInjectAttribute(Type fromType, Type toType, string key)
|
||||
{
|
||||
this.FromType = fromType;
|
||||
this.ToType = toType;
|
||||
this.Key = key;
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// 添加瞬态注入。
|
||||
// /// <para>
|
||||
// /// 该标签仅添加在继承<see cref="ManualContainer"/>的容器上时有用。
|
||||
// /// </para>
|
||||
// /// </summary>
|
||||
// [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||
// internal class AddTransientInjectAttribute : BaseInjectAttribute
|
||||
// {
|
||||
// /// <summary>
|
||||
// /// 添加瞬态注入。
|
||||
// /// <para>
|
||||
// /// 该标签仅添加在继承<see cref="ManualContainer"/>的容器上时有用。
|
||||
// /// </para>
|
||||
// /// </summary>
|
||||
// /// <param name="fromType">注册类型</param>
|
||||
// /// <param name="toType">实例类型</param>
|
||||
// /// <param name="key">注册额外键</param>
|
||||
// public AddTransientInjectAttribute(Type fromType, Type toType, string key)
|
||||
// {
|
||||
// this.FromType = fromType;
|
||||
// this.ToType = toType;
|
||||
// this.Key = key;
|
||||
// }
|
||||
/// <summary>
|
||||
/// 添加单例注入。
|
||||
/// <para>
|
||||
/// 该标签仅添加在继承<see cref="ManualContainer"/>的容器上时有用。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="type">注册类型与实例类型一致</param>
|
||||
public AddSingletonInjectAttribute(Type type) : this(type, type, null)
|
||||
{
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// 添加瞬态注入。
|
||||
// /// <para>
|
||||
// /// 该标签仅添加在继承<see cref="ManualContainer"/>的容器上时有用。
|
||||
// /// </para>
|
||||
// /// </summary>
|
||||
// /// <param name="type">注册类型与实例类型一致</param>
|
||||
// public AddTransientInjectAttribute(Type type) : this(type, type, null)
|
||||
// {
|
||||
// }
|
||||
/// <summary>
|
||||
/// 添加单例注入。
|
||||
/// <para>
|
||||
/// 该标签仅添加在继承<see cref="ManualContainer"/>的容器上时有用。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="fromType">注册类型</param>
|
||||
/// <param name="toType">实例类型</param>
|
||||
public AddSingletonInjectAttribute(Type fromType, Type toType) : this(fromType, toType, null)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// 添加瞬态注入。
|
||||
// /// <para>
|
||||
// /// 该标签仅添加在继承<see cref="ManualContainer"/>的容器上时有用。
|
||||
// /// </para>
|
||||
// /// </summary>
|
||||
// /// <param name="fromType">注册类型</param>
|
||||
// /// <param name="toType">实例类型</param>
|
||||
// public AddTransientInjectAttribute(Type fromType, Type toType) : this(fromType, toType, null)
|
||||
// {
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
/// <summary>
|
||||
/// 添加瞬态注入。
|
||||
/// <para>
|
||||
/// 该标签仅添加在继承<see cref="ManualContainer"/>的容器上时有用。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||
/*GeneratedCode*/
|
||||
internal class AddTransientInjectAttribute : BaseInjectAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// 添加瞬态注入。
|
||||
/// <para>
|
||||
/// 该标签仅添加在继承<see cref="ManualContainer"/>的容器上时有用。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="fromType">注册类型</param>
|
||||
/// <param name="toType">实例类型</param>
|
||||
/// <param name="key">注册额外键</param>
|
||||
public AddTransientInjectAttribute(Type fromType, Type toType, string key)
|
||||
{
|
||||
this.FromType = fromType;
|
||||
this.ToType = toType;
|
||||
this.Key = key;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加瞬态注入。
|
||||
/// <para>
|
||||
/// 该标签仅添加在继承<see cref="ManualContainer"/>的容器上时有用。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="type">注册类型与实例类型一致</param>
|
||||
public AddTransientInjectAttribute(Type type) : this(type, type, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加瞬态注入。
|
||||
/// <para>
|
||||
/// 该标签仅添加在继承<see cref="ManualContainer"/>的容器上时有用。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="fromType">注册类型</param>
|
||||
/// <param name="toType">实例类型</param>
|
||||
public AddTransientInjectAttribute(Type fromType, Type toType) : this(fromType, toType, null)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -13,13 +13,20 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace TouchSocket
|
||||
namespace TouchSocket;
|
||||
|
||||
[Generator]
|
||||
public class ContainerSourceGenerator : ISourceGenerator
|
||||
{
|
||||
[Generator]
|
||||
public class ContainerSourceGenerator : ISourceGenerator
|
||||
{
|
||||
private string m_generatorAttribute = @"
|
||||
private readonly string m_generatorContainerAttribute = @"
|
||||
/*
|
||||
此代码由SourceGenerator工具直接生成,非必要请不要修改此处代码
|
||||
*/
|
||||
|
||||
#pragma warning disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
@@ -29,10 +36,12 @@ namespace TouchSocket.Core
|
||||
/// <summary>
|
||||
/// 源生成容器特性
|
||||
/// </summary>
|
||||
/*GeneratedCode*/
|
||||
internal class GeneratorContainerAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
/*GeneratedCode*/
|
||||
internal class BaseInjectAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
@@ -54,7 +63,8 @@ namespace TouchSocket.Core
|
||||
/// <summary>
|
||||
/// 自动注入为单例。
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class| AttributeTargets.Interface, AllowMultiple = true)]
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true)]
|
||||
/*GeneratedCode*/
|
||||
internal class AutoInjectForSingletonAttribute : BaseInjectAttribute
|
||||
{
|
||||
}
|
||||
@@ -63,6 +73,7 @@ namespace TouchSocket.Core
|
||||
/// 自动注入为瞬时。
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true)]
|
||||
/*GeneratedCode*/
|
||||
internal class AutoInjectForTransientAttribute : BaseInjectAttribute
|
||||
{
|
||||
}
|
||||
@@ -74,6 +85,7 @@ namespace TouchSocket.Core
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||
/*GeneratedCode*/
|
||||
internal class AddSingletonInjectAttribute : BaseInjectAttribute
|
||||
{
|
||||
/// <summary>
|
||||
@@ -123,6 +135,7 @@ namespace TouchSocket.Core
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||
/*GeneratedCode*/
|
||||
internal class AddTransientInjectAttribute : BaseInjectAttribute
|
||||
{
|
||||
/// <summary>
|
||||
@@ -165,42 +178,42 @@ namespace TouchSocket.Core
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
";
|
||||
|
||||
public void Initialize(GeneratorInitializationContext context)
|
||||
public void Initialize(GeneratorInitializationContext context)
|
||||
{
|
||||
context.RegisterForPostInitialization(a =>
|
||||
{
|
||||
context.RegisterForPostInitialization(a =>
|
||||
{
|
||||
a.AddSource(nameof(this.m_generatorAttribute), this.m_generatorAttribute);
|
||||
});
|
||||
context.RegisterForSyntaxNotifications(() => new ContainerSyntaxReceiver());
|
||||
}
|
||||
var sourceCode = this.m_generatorContainerAttribute.Replace("/*GeneratedCode*/", $"[global::System.CodeDom.Compiler.GeneratedCode(\"TouchSocket.SourceGenerator\",\"{Assembly.GetExecutingAssembly().GetName().Version.ToString()}\")]");
|
||||
|
||||
public void Execute(GeneratorExecutionContext context)
|
||||
a.AddSource(nameof(this.m_generatorContainerAttribute), sourceCode);
|
||||
});
|
||||
context.RegisterForSyntaxNotifications(() => new ContainerSyntaxReceiver());
|
||||
}
|
||||
|
||||
public void Execute(GeneratorExecutionContext context)
|
||||
{
|
||||
var s = context.Compilation.GetMetadataReference(context.Compilation.Assembly);
|
||||
|
||||
if (context.SyntaxReceiver is ContainerSyntaxReceiver receiver)
|
||||
{
|
||||
var s = context.Compilation.GetMetadataReference(context.Compilation.Assembly);
|
||||
var types1 = receiver.GetAutoInjectForSingletonClassTypes(context.Compilation);
|
||||
var types2 = receiver.GetAutoInjectForTransientClassTypes(context.Compilation);
|
||||
|
||||
if (context.SyntaxReceiver is ContainerSyntaxReceiver receiver)
|
||||
var builders = receiver
|
||||
.GetContainerClassTypes(context.Compilation)
|
||||
.Select(i => new ContainerCodeBuilder(i, types1, types2))
|
||||
.Distinct(CodeBuilderEqualityComparer<ContainerCodeBuilder>.Default);
|
||||
//Debugger.Launch();
|
||||
|
||||
foreach (var builder in builders)
|
||||
{
|
||||
var types1 = receiver.GetAutoInjectForSingletonClassTypes(context.Compilation);
|
||||
var types2 = receiver.GetAutoInjectForTransientClassTypes(context.Compilation);
|
||||
|
||||
var builders = receiver
|
||||
.GetContainerClassTypes(context.Compilation)
|
||||
.Select(i => new ContainerCodeBuilder(i, types1, types2))
|
||||
.Distinct();
|
||||
//Debugger.Launch();
|
||||
|
||||
foreach (var builder in builders)
|
||||
if (builder.TryToSourceText(out var sourceText))
|
||||
{
|
||||
if (builder.TryToSourceText(out var sourceText))
|
||||
{
|
||||
var tree = CSharpSyntaxTree.ParseText(sourceText);
|
||||
var root = tree.GetRoot().NormalizeWhitespace();
|
||||
var ret = root.ToFullString();
|
||||
context.AddSource($"{builder.GetFileName()}.g.cs", ret);
|
||||
}
|
||||
var tree = CSharpSyntaxTree.ParseText(sourceText);
|
||||
var root = tree.GetRoot().NormalizeWhitespace();
|
||||
var ret = root.ToFullString();
|
||||
context.AddSource($"{builder.GetFileName()}.g.cs", ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -16,174 +16,173 @@ using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace TouchSocket
|
||||
namespace TouchSocket;
|
||||
|
||||
internal sealed class ContainerSyntaxReceiver : ISyntaxReceiver
|
||||
{
|
||||
internal sealed class ContainerSyntaxReceiver : ISyntaxReceiver
|
||||
public const string GeneratorContainerAttributeTypeName = "TouchSocket.Core.GeneratorContainerAttribute";
|
||||
public const string AutoInjectForSingletonAttributeTypeName = "TouchSocket.Core.AutoInjectForSingletonAttribute";
|
||||
public const string AutoInjectForTransientAttributeTypeName = "TouchSocket.Core.AutoInjectForTransientAttribute";
|
||||
public const string ManualContainerTypeName = "TouchSocket.Core.ManualContainer";
|
||||
|
||||
public static INamedTypeSymbol GeneratorContainerAttribute { get; private set; }
|
||||
public static INamedTypeSymbol AutoInjectForSingletonAttribute { get; private set; }
|
||||
public static INamedTypeSymbol AutoInjectForTransientAttribute { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 接口列表
|
||||
/// </summary>
|
||||
private readonly List<TypeDeclarationSyntax> m_classSyntaxList = new List<TypeDeclarationSyntax>();
|
||||
|
||||
/// <summary>
|
||||
/// 访问语法树
|
||||
/// </summary>
|
||||
/// <param name="syntaxNode"></param>
|
||||
void ISyntaxReceiver.OnVisitSyntaxNode(SyntaxNode syntaxNode)
|
||||
{
|
||||
public const string GeneratorContainerAttributeTypeName = "TouchSocket.Core.GeneratorContainerAttribute";
|
||||
public const string AutoInjectForSingletonAttributeTypeName = "TouchSocket.Core.AutoInjectForSingletonAttribute";
|
||||
public const string AutoInjectForTransientAttributeTypeName = "TouchSocket.Core.AutoInjectForTransientAttribute";
|
||||
public const string ManualContainerTypeName = "TouchSocket.Core.ManualContainer";
|
||||
|
||||
public static INamedTypeSymbol GeneratorContainerAttribute { get; private set; }
|
||||
public static INamedTypeSymbol AutoInjectForSingletonAttribute { get; private set; }
|
||||
public static INamedTypeSymbol AutoInjectForTransientAttribute { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 接口列表
|
||||
/// </summary>
|
||||
private readonly List<TypeDeclarationSyntax> m_classSyntaxList = new List<TypeDeclarationSyntax>();
|
||||
|
||||
/// <summary>
|
||||
/// 访问语法树
|
||||
/// </summary>
|
||||
/// <param name="syntaxNode"></param>
|
||||
void ISyntaxReceiver.OnVisitSyntaxNode(SyntaxNode syntaxNode)
|
||||
if (syntaxNode is ClassDeclarationSyntax syntax)
|
||||
{
|
||||
if (syntaxNode is ClassDeclarationSyntax syntax)
|
||||
{
|
||||
this.m_classSyntaxList.Add(syntax);
|
||||
}
|
||||
else if (syntaxNode is InterfaceDeclarationSyntax @interface)
|
||||
{
|
||||
this.m_classSyntaxList.Add(@interface);
|
||||
}
|
||||
this.m_classSyntaxList.Add(syntax);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有Container符号
|
||||
/// </summary>
|
||||
/// <param name="compilation"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<INamedTypeSymbol> GetContainerClassTypes(Compilation compilation)
|
||||
else if (syntaxNode is InterfaceDeclarationSyntax @interface)
|
||||
{
|
||||
// Debugger.Launch();
|
||||
GeneratorContainerAttribute = compilation.GetTypeByMetadataName(GeneratorContainerAttributeTypeName);
|
||||
if (GeneratorContainerAttribute == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
foreach (var classSyntax in this.m_classSyntaxList)
|
||||
{
|
||||
var @class = compilation.GetSemanticModel(classSyntax.SyntaxTree).GetDeclaredSymbol(classSyntax);
|
||||
if (@class != null && IsContainerClass(@class))
|
||||
{
|
||||
yield return @class;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<InjectDescription> GetAutoInjectForSingletonClassTypes(Compilation compilation)
|
||||
{
|
||||
// Debugger.Launch();
|
||||
AutoInjectForSingletonAttribute = compilation.GetTypeByMetadataName(AutoInjectForSingletonAttributeTypeName);
|
||||
if (AutoInjectForSingletonAttribute == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
foreach (var classSyntax in this.m_classSyntaxList)
|
||||
{
|
||||
var @class = compilation.GetSemanticModel(classSyntax.SyntaxTree).GetDeclaredSymbol(classSyntax);
|
||||
if (@class != null && IsAutoInjectForSingletonClass(@class, out var attributeData))
|
||||
{
|
||||
yield return this.Create(@class, attributeData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<InjectDescription> GetAutoInjectForTransientClassTypes(Compilation compilation)
|
||||
{
|
||||
// Debugger.Launch();
|
||||
AutoInjectForTransientAttribute = compilation.GetTypeByMetadataName(AutoInjectForTransientAttributeTypeName);
|
||||
if (AutoInjectForTransientAttribute == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
foreach (var classSyntax in this.m_classSyntaxList)
|
||||
{
|
||||
var @class = compilation.GetSemanticModel(classSyntax.SyntaxTree).GetDeclaredSymbol(classSyntax);
|
||||
if (@class != null && IsAutoInjectForTransientClass(@class, out var attributeData))
|
||||
{
|
||||
yield return this.Create(@class, attributeData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private InjectDescription Create(INamedTypeSymbol typeSymbol, AttributeData attributeData)
|
||||
{
|
||||
var dic = attributeData.NamedArguments.ToImmutableDictionary();
|
||||
var description = new InjectDescription();
|
||||
if (dic.TryGetValue("FromType", out var typedConstant))
|
||||
{
|
||||
description.From = (INamedTypeSymbol)typedConstant.Value;
|
||||
}
|
||||
if (dic.TryGetValue("ToType", out typedConstant))
|
||||
{
|
||||
description.To = (INamedTypeSymbol)typedConstant.Value;
|
||||
}
|
||||
if (dic.TryGetValue("Key", out typedConstant))
|
||||
{
|
||||
description.Key = typedConstant.Value.ToString();
|
||||
}
|
||||
description.From ??= typeSymbol;
|
||||
description.To ??= typeSymbol;
|
||||
return description;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否为容器生成
|
||||
/// </summary>
|
||||
/// <param name="class"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsContainerClass(INamedTypeSymbol @class)
|
||||
{
|
||||
if (GeneratorContainerAttribute is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
//Debugger.Launch();
|
||||
|
||||
if (!@class.HasAttribute(GeneratorContainerAttributeTypeName, out _))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (@class.IsInheritFrom(ManualContainerTypeName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsAutoInjectForSingletonClass(INamedTypeSymbol @class, out AttributeData attributeData)
|
||||
{
|
||||
if (AutoInjectForSingletonAttribute is null)
|
||||
{
|
||||
attributeData = null;
|
||||
return false;
|
||||
}
|
||||
//Debugger.Launch();
|
||||
|
||||
if (@class.HasAttribute(AutoInjectForSingletonAttributeTypeName, out attributeData))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsAutoInjectForTransientClass(INamedTypeSymbol @class, out AttributeData attributeData)
|
||||
{
|
||||
if (AutoInjectForTransientAttribute is null)
|
||||
{
|
||||
attributeData = null;
|
||||
return false;
|
||||
}
|
||||
//Debugger.Launch();
|
||||
|
||||
if (@class.HasAttribute(AutoInjectForTransientAttributeTypeName, out attributeData))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
this.m_classSyntaxList.Add(@interface);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有Container符号
|
||||
/// </summary>
|
||||
/// <param name="compilation"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<INamedTypeSymbol> GetContainerClassTypes(Compilation compilation)
|
||||
{
|
||||
// Debugger.Launch();
|
||||
GeneratorContainerAttribute = compilation.GetTypeByMetadataName(GeneratorContainerAttributeTypeName);
|
||||
if (GeneratorContainerAttribute == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
foreach (var classSyntax in this.m_classSyntaxList)
|
||||
{
|
||||
var @class = compilation.GetSemanticModel(classSyntax.SyntaxTree).GetDeclaredSymbol(classSyntax);
|
||||
if (@class != null && IsContainerClass(@class))
|
||||
{
|
||||
yield return @class;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<InjectDescription> GetAutoInjectForSingletonClassTypes(Compilation compilation)
|
||||
{
|
||||
// Debugger.Launch();
|
||||
AutoInjectForSingletonAttribute = compilation.GetTypeByMetadataName(AutoInjectForSingletonAttributeTypeName);
|
||||
if (AutoInjectForSingletonAttribute == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
foreach (var classSyntax in this.m_classSyntaxList)
|
||||
{
|
||||
var @class = compilation.GetSemanticModel(classSyntax.SyntaxTree).GetDeclaredSymbol(classSyntax);
|
||||
if (@class != null && IsAutoInjectForSingletonClass(@class, out var attributeData))
|
||||
{
|
||||
yield return this.Create(@class, attributeData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<InjectDescription> GetAutoInjectForTransientClassTypes(Compilation compilation)
|
||||
{
|
||||
// Debugger.Launch();
|
||||
AutoInjectForTransientAttribute = compilation.GetTypeByMetadataName(AutoInjectForTransientAttributeTypeName);
|
||||
if (AutoInjectForTransientAttribute == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
foreach (var classSyntax in this.m_classSyntaxList)
|
||||
{
|
||||
var @class = compilation.GetSemanticModel(classSyntax.SyntaxTree).GetDeclaredSymbol(classSyntax);
|
||||
if (@class != null && IsAutoInjectForTransientClass(@class, out var attributeData))
|
||||
{
|
||||
yield return this.Create(@class, attributeData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private InjectDescription Create(INamedTypeSymbol typeSymbol, AttributeData attributeData)
|
||||
{
|
||||
var dic = attributeData.NamedArguments.ToImmutableDictionary();
|
||||
var description = new InjectDescription();
|
||||
if (dic.TryGetValue("FromType", out var typedConstant))
|
||||
{
|
||||
description.From = (INamedTypeSymbol)typedConstant.Value;
|
||||
}
|
||||
if (dic.TryGetValue("ToType", out typedConstant))
|
||||
{
|
||||
description.To = (INamedTypeSymbol)typedConstant.Value;
|
||||
}
|
||||
if (dic.TryGetValue("Key", out typedConstant))
|
||||
{
|
||||
description.Key = typedConstant.Value.ToString();
|
||||
}
|
||||
description.From ??= typeSymbol;
|
||||
description.To ??= typeSymbol;
|
||||
return description;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否为容器生成
|
||||
/// </summary>
|
||||
/// <param name="class"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsContainerClass(INamedTypeSymbol @class)
|
||||
{
|
||||
if (GeneratorContainerAttribute is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
//Debugger.Launch();
|
||||
|
||||
if (!@class.HasAttribute(GeneratorContainerAttributeTypeName, out _))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (@class.IsInheritFrom(ManualContainerTypeName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsAutoInjectForSingletonClass(INamedTypeSymbol @class, out AttributeData attributeData)
|
||||
{
|
||||
if (AutoInjectForSingletonAttribute is null)
|
||||
{
|
||||
attributeData = null;
|
||||
return false;
|
||||
}
|
||||
//Debugger.Launch();
|
||||
|
||||
if (@class.HasAttribute(AutoInjectForSingletonAttributeTypeName, out attributeData))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsAutoInjectForTransientClass(INamedTypeSymbol @class, out AttributeData attributeData)
|
||||
{
|
||||
if (AutoInjectForTransientAttribute is null)
|
||||
{
|
||||
attributeData = null;
|
||||
return false;
|
||||
}
|
||||
//Debugger.Launch();
|
||||
|
||||
if (@class.HasAttribute(AutoInjectForTransientAttributeTypeName, out attributeData))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -5,62 +5,59 @@
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TouchSocket
|
||||
namespace TouchSocket;
|
||||
|
||||
internal class InjectDescriptionCompare : IEqualityComparer<InjectDescription>
|
||||
{
|
||||
class InjectDescriptionCompare : IEqualityComparer<InjectDescription>
|
||||
public bool Equals(InjectDescription x, InjectDescription y)
|
||||
{
|
||||
public bool Equals(InjectDescription x, InjectDescription y)
|
||||
if (string.IsNullOrEmpty(x.Key) || string.IsNullOrEmpty(y.Key))
|
||||
{
|
||||
if (string.IsNullOrEmpty(x.Key) || string.IsNullOrEmpty(y.Key))
|
||||
{
|
||||
return x.From.ToDisplayString() == y.From.ToDisplayString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return x.From.ToDisplayString() == y.From.ToDisplayString() && x.Key == y.Key;
|
||||
}
|
||||
return x.From.ToDisplayString() == y.From.ToDisplayString();
|
||||
}
|
||||
|
||||
public int GetHashCode(InjectDescription obj)
|
||||
else
|
||||
{
|
||||
if (string.IsNullOrEmpty(obj.Key))
|
||||
{
|
||||
return obj.From.ToDisplayString().GetHashCode();
|
||||
}
|
||||
else
|
||||
{
|
||||
return obj.From.ToDisplayString().GetHashCode()^ obj.Key.GetHashCode();
|
||||
}
|
||||
return x.From.ToDisplayString() == y.From.ToDisplayString() && x.Key == y.Key;
|
||||
}
|
||||
}
|
||||
internal class InjectDescription
|
||||
{
|
||||
public INamedTypeSymbol From { get; set; }
|
||||
public INamedTypeSymbol To { get; set; }
|
||||
public string Key { get; set; }
|
||||
|
||||
|
||||
}
|
||||
|
||||
internal class InjectPropertyDescription
|
||||
public int GetHashCode(InjectDescription obj)
|
||||
{
|
||||
public ITypeSymbol Type { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Key { get; set; }
|
||||
if (string.IsNullOrEmpty(obj.Key))
|
||||
{
|
||||
return obj.From.ToDisplayString().GetHashCode();
|
||||
}
|
||||
else
|
||||
{
|
||||
return obj.From.ToDisplayString().GetHashCode() ^ obj.Key.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class InjectMethodDescription
|
||||
{
|
||||
public IEnumerable<InjectPropertyDescription> Types { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
internal class InjectDescription
|
||||
{
|
||||
public INamedTypeSymbol From { get; set; }
|
||||
public INamedTypeSymbol To { get; set; }
|
||||
public string Key { get; set; }
|
||||
}
|
||||
|
||||
internal class InjectPropertyDescription
|
||||
{
|
||||
public ITypeSymbol Type { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Key { get; set; }
|
||||
}
|
||||
|
||||
internal class InjectMethodDescription
|
||||
{
|
||||
public IEnumerable<InjectPropertyDescription> Types { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
此代码由SourceGenerator工具直接生成,非必要请不要修改此处代码
|
||||
*/
|
||||
|
||||
#pragma warning disable
|
||||
|
||||
using System;
|
||||
|
||||
namespace TouchSocket.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// 标识源生成Fast序列化相关的实现。
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||
/*GeneratedCode*/
|
||||
internal class FastSerializableAttribute : Attribute
|
||||
{
|
||||
public FastSerializableAttribute(Type type, TypeMode typeMode)
|
||||
{
|
||||
this.Type = type;
|
||||
this.TypeMode = typeMode;
|
||||
}
|
||||
|
||||
public Type Type { get; }
|
||||
|
||||
public FastSerializableAttribute(Type type) : this(type, TypeMode.Self)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public TypeMode TypeMode { get; }
|
||||
}
|
||||
|
||||
[Flags]
|
||||
/*GeneratedCode*/
|
||||
internal enum TypeMode
|
||||
{
|
||||
All = -1,
|
||||
Self = 0,
|
||||
Field = 1,
|
||||
Property = 2,
|
||||
MethodReturn = 4,
|
||||
MethodParameter = 8
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 Microsoft.CodeAnalysis;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace TouchSocket;
|
||||
|
||||
internal class FastSerializeCodeBuilder : CodeBuilder
|
||||
{
|
||||
private readonly INamedTypeSymbol m_namedTypeSymbol;
|
||||
|
||||
public FastSerializeCodeBuilder(INamedTypeSymbol type, List<INamedTypeSymbol> namedTypeSymbols)
|
||||
{
|
||||
this.m_namedTypeSymbol = type;
|
||||
this.m_namedTypeSymbols = namedTypeSymbols;
|
||||
}
|
||||
|
||||
public INamedTypeSymbol NamedTypeSymbol => this.m_namedTypeSymbol;
|
||||
|
||||
public virtual IEnumerable<string> Usings
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return "using System;";
|
||||
yield return "using System.Diagnostics;";
|
||||
yield return "using TouchSocket.Core;";
|
||||
yield return "using System.Threading.Tasks;";
|
||||
}
|
||||
}
|
||||
|
||||
public override string Id => this.m_namedTypeSymbol.ToDisplayString();
|
||||
|
||||
private readonly List<INamedTypeSymbol> m_namedTypeSymbols;
|
||||
|
||||
public override string GetFileName()
|
||||
{
|
||||
return this.GetGeneratorTypeName() + "Generator";
|
||||
}
|
||||
|
||||
private string GetGeneratorTypeName()
|
||||
{
|
||||
var typeName = this.m_namedTypeSymbol.Name;
|
||||
|
||||
return typeName;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var codeString = new StringBuilder();
|
||||
codeString.AppendLine("/*");
|
||||
codeString.AppendLine("此代码由Rpc工具直接生成,非必要请不要修改此处代码");
|
||||
codeString.AppendLine("*/");
|
||||
codeString.AppendLine("#pragma warning disable");
|
||||
|
||||
foreach (var item in this.Usings)
|
||||
{
|
||||
codeString.AppendLine(item);
|
||||
}
|
||||
if (!this.NamedTypeSymbol.ContainingNamespace.IsGlobalNamespace)
|
||||
{
|
||||
codeString.AppendLine($"namespace {this.NamedTypeSymbol.ContainingNamespace}");
|
||||
codeString.AppendLine("{");
|
||||
}
|
||||
|
||||
codeString.AppendLine($"partial class {this.GetGeneratorTypeName()}");
|
||||
codeString.AppendLine("{");
|
||||
|
||||
codeString.AppendLine($"public {this.GetGeneratorTypeName()} ()");
|
||||
codeString.AppendLine("{");
|
||||
foreach (var item in this.m_namedTypeSymbols)
|
||||
{
|
||||
this.BuildItems(codeString, item);
|
||||
}
|
||||
codeString.AppendLine("}");
|
||||
|
||||
codeString.AppendLine("}");
|
||||
|
||||
if (!this.NamedTypeSymbol.ContainingNamespace.IsGlobalNamespace)
|
||||
{
|
||||
codeString.AppendLine("}");
|
||||
}
|
||||
|
||||
// System.Diagnostics.Debugger.Launch();
|
||||
return codeString.ToString();
|
||||
}
|
||||
|
||||
private void BuildItems(StringBuilder codeString, INamedTypeSymbol namedTypeSymbol)
|
||||
{
|
||||
var typeName = namedTypeSymbol.ToDisplayString();
|
||||
codeString.AppendLine($"this.AddConverter(typeof({typeName}), new PackageFastBinaryConverter<{typeName}>());");
|
||||
}
|
||||
|
||||
private string GetTypeName(INamedTypeSymbol namedTypeSymbol)
|
||||
{
|
||||
return Utils.MakeIdentifier(namedTypeSymbol.ToDisplayString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 Microsoft.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace TouchSocket;
|
||||
|
||||
[Generator]
|
||||
public class FastSerializeGenerator : ISourceGenerator
|
||||
{
|
||||
public const string FastSerializableAttributeString = "TouchSocket.Core.FastSerializableAttribute";
|
||||
public void Execute(GeneratorExecutionContext context)
|
||||
{
|
||||
var compilation = context.Compilation;
|
||||
var s = compilation.GetMetadataReference(context.Compilation.Assembly);
|
||||
|
||||
var fastSerializableAttribute = compilation.GetTypeByMetadataName(FastSerializableAttributeString);
|
||||
|
||||
if (context.SyntaxReceiver is FastSerializeSyntaxReceiver receiver)
|
||||
{
|
||||
var pairs = receiver.GetFastSerializeContexts(context);
|
||||
|
||||
var builders = pairs.Select(a => new FastSerializeCodeBuilder(a.Key, a.Value));
|
||||
foreach (var builder in builders)
|
||||
{
|
||||
context.AddSource($"{builder.GetFileName()}.g.cs", builder.ToSourceText());
|
||||
}
|
||||
|
||||
//foreach (var builder in pairs.Keys.Select(a => new MethodInvokeTitleCodeBuilder(a)))
|
||||
//{
|
||||
// context.AddSource($"{builder.GetFileName()}.g.cs", builder.ToSourceText());
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly string fastSerializableAttribute = @"
|
||||
|
||||
/*
|
||||
此代码由SourceGenerator工具直接生成,非必要请不要修改此处代码
|
||||
*/
|
||||
|
||||
#pragma warning disable
|
||||
|
||||
using System;
|
||||
|
||||
namespace TouchSocket.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// 标识源生成Fast序列化相关的实现。
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class,AllowMultiple =true)]
|
||||
/*GeneratedCode*/
|
||||
internal class FastSerializableAttribute : Attribute
|
||||
{
|
||||
public FastSerializableAttribute(Type type, TypeMode typeMode)
|
||||
{
|
||||
this.Type = type;
|
||||
this.TypeMode = typeMode;
|
||||
}
|
||||
|
||||
public Type Type { get; }
|
||||
|
||||
public FastSerializableAttribute(Type type) : this(type, TypeMode.Self)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public TypeMode TypeMode { get; }
|
||||
}
|
||||
|
||||
[Flags]
|
||||
/*GeneratedCode*/
|
||||
internal enum TypeMode
|
||||
{
|
||||
All = -1,
|
||||
Self = 0,
|
||||
Field = 1,
|
||||
Property = 2,
|
||||
MethodReturn = 4,
|
||||
MethodParameter = 8
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
";
|
||||
|
||||
public void Initialize(GeneratorInitializationContext context)
|
||||
{
|
||||
context.RegisterForPostInitialization(a =>
|
||||
{
|
||||
a.AddSource($"{nameof(this.fastSerializableAttribute)}.g.cs", this.fastSerializableAttribute);
|
||||
});
|
||||
context.RegisterForSyntaxNotifications(() => new FastSerializeSyntaxReceiver());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace TouchSocket;
|
||||
|
||||
internal sealed class FastSerializeSyntaxReceiver : ISyntaxReceiver
|
||||
{
|
||||
private readonly List<TypeDeclarationSyntax> m_syntaxList = new List<TypeDeclarationSyntax>();
|
||||
|
||||
public Dictionary<INamedTypeSymbol, List<INamedTypeSymbol>> GetFastSerializeContexts(GeneratorExecutionContext context)
|
||||
{
|
||||
var compilation = context.Compilation;
|
||||
|
||||
var pairs = new Dictionary<INamedTypeSymbol, List<INamedTypeSymbol>>();
|
||||
//Debugger.Launch();
|
||||
foreach (var syntax in this.m_syntaxList)
|
||||
{
|
||||
var namedTypeSymbol = compilation.GetSemanticModel(syntax.SyntaxTree).GetDeclaredSymbol(syntax);
|
||||
if (!namedTypeSymbol.HasAttributes(FastSerializeGenerator.FastSerializableAttributeString, out var atts))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var types = new List<INamedTypeSymbol>();
|
||||
|
||||
foreach (var item in atts)
|
||||
{
|
||||
if (item.ConstructorArguments.Length == 1)
|
||||
{
|
||||
if (item.ConstructorArguments[0].Value is not INamedTypeSymbol typeSymbol)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
this.GetINamedTypeSymbols(typeSymbol, TypeMode.Self, ref types, context, item);
|
||||
}
|
||||
else if (item.ConstructorArguments.Length == 2)
|
||||
{
|
||||
if (item.ConstructorArguments[0].Value is not INamedTypeSymbol typeSymbol)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.ConstructorArguments[1].Value is not int value)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var typeMode = (TypeMode)value;
|
||||
this.GetINamedTypeSymbols(typeSymbol, typeMode, ref types, context, item);
|
||||
}
|
||||
}
|
||||
|
||||
types = types.Distinct().ToList();
|
||||
pairs.Add(namedTypeSymbol, types);
|
||||
}
|
||||
|
||||
return pairs;
|
||||
}
|
||||
|
||||
public void GetINamedTypeSymbols(INamedTypeSymbol namedTypeSymbol, TypeMode typeMode, ref List<INamedTypeSymbol> namedTypeSymbols, GeneratorExecutionContext context, AttributeData attributeData)
|
||||
{
|
||||
if (typeMode == TypeMode.Self)
|
||||
{
|
||||
//Debugger.Launch();
|
||||
if (CanBeAdd(namedTypeSymbol, context, attributeData))
|
||||
{
|
||||
namedTypeSymbols.Add(namedTypeSymbol);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var symbol in namedTypeSymbol.GetMembers())
|
||||
{
|
||||
switch (symbol)
|
||||
{
|
||||
case IFieldSymbol targetSymbol:
|
||||
{
|
||||
if (typeMode == TypeMode.All || typeMode.HasFlag(TypeMode.Field))
|
||||
{
|
||||
if (targetSymbol.Type is INamedTypeSymbol namedType && CanBeAdd(namedType, context, attributeData))
|
||||
{
|
||||
namedTypeSymbols.Add(namedType);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IPropertySymbol targetSymbol:
|
||||
{
|
||||
if (typeMode == TypeMode.All || typeMode.HasFlag(TypeMode.Property))
|
||||
{
|
||||
if (targetSymbol.Type is INamedTypeSymbol namedType && CanBeAdd(namedType, context, attributeData))
|
||||
{
|
||||
namedTypeSymbols.Add(namedType);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IMethodSymbol targetSymbol:
|
||||
{
|
||||
if (typeMode == TypeMode.All || typeMode.HasFlag(TypeMode.MethodReturn))
|
||||
{
|
||||
if (targetSymbol.HasReturn())
|
||||
{
|
||||
if (targetSymbol.GetRealReturnType() is INamedTypeSymbol namedType && CanBeAdd(namedType, context, attributeData))
|
||||
{
|
||||
namedTypeSymbols.Add(namedType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeMode == TypeMode.All || typeMode.HasFlag(TypeMode.MethodParameter))
|
||||
{
|
||||
foreach (var parameterSymbol in targetSymbol.Parameters)
|
||||
{
|
||||
if (parameterSymbol.Type is INamedTypeSymbol namedType && CanBeAdd(namedType, context, attributeData))
|
||||
{
|
||||
namedTypeSymbols.Add(namedType);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 访问语法树
|
||||
/// </summary>
|
||||
/// <param name="syntaxNode"></param>
|
||||
void ISyntaxReceiver.OnVisitSyntaxNode(SyntaxNode syntaxNode)
|
||||
{
|
||||
if (syntaxNode is TypeDeclarationSyntax syntax)
|
||||
{
|
||||
this.m_syntaxList.Add(syntax);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool CanBeAdd(INamedTypeSymbol namedTypeSymbol, GeneratorExecutionContext context, AttributeData attributeData)
|
||||
{
|
||||
if (namedTypeSymbol.IsPrimitive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (namedTypeSymbol.IsAbstract || namedTypeSymbol.TypeKind == TypeKind.Interface)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!namedTypeSymbol.IsInheritFrom(PackageSyntaxReceiver.IPackageTypeName))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.m_rule_FastSerialize0001, attributeData.ApplicationSyntaxReference.GetSyntax().GetLocation(), namedTypeSymbol.ToDisplayString()));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
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 (c) 2020-2023 若汝棋茗
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,231 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 Microsoft.CodeAnalysis;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace TouchSocket;
|
||||
|
||||
internal class MethodInvokeCodeBuilder : CodeBuilder
|
||||
{
|
||||
private readonly INamedTypeSymbol m_namedTypeSymbol;
|
||||
|
||||
public MethodInvokeCodeBuilder(INamedTypeSymbol type, List<IMethodSymbol> methodSymbols)
|
||||
{
|
||||
this.m_namedTypeSymbol = type;
|
||||
this.MethodSymbols = methodSymbols;
|
||||
}
|
||||
|
||||
public INamedTypeSymbol NamedTypeSymbol => this.m_namedTypeSymbol;
|
||||
|
||||
public virtual IEnumerable<string> Usings
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return "using System;";
|
||||
yield return "using System.Diagnostics;";
|
||||
yield return "using TouchSocket.Core;";
|
||||
yield return "using System.Threading.Tasks;";
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual string GeneratorTypeNamespace => "TouchSocket.Core.__Internals";
|
||||
|
||||
public override string Id => this.m_namedTypeSymbol.ToDisplayString();
|
||||
|
||||
public List<IMethodSymbol> MethodSymbols { get; }
|
||||
|
||||
public override string GetFileName()
|
||||
{
|
||||
return this.GeneratorTypeNamespace + this.GetGeneratorTypeName() + "Generator";
|
||||
}
|
||||
|
||||
private string GetGeneratorTypeName()
|
||||
{
|
||||
var typeName = $"__{Utils.MakeIdentifier(this.m_namedTypeSymbol.ToDisplayString())}MethodExtension";
|
||||
|
||||
return typeName;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var codeString = new StringBuilder();
|
||||
codeString.AppendLine("/*");
|
||||
codeString.AppendLine("此代码由Rpc工具直接生成,非必要请不要修改此处代码");
|
||||
codeString.AppendLine("*/");
|
||||
codeString.AppendLine("#pragma warning disable");
|
||||
|
||||
foreach (var item in this.Usings)
|
||||
{
|
||||
codeString.AppendLine(item);
|
||||
}
|
||||
if (!this.NamedTypeSymbol.ContainingNamespace.IsGlobalNamespace)
|
||||
{
|
||||
codeString.AppendLine($"namespace {this.GeneratorTypeNamespace}");
|
||||
codeString.AppendLine("{");
|
||||
}
|
||||
|
||||
codeString.AppendLine($"partial class {this.GetGeneratorTypeName()}");
|
||||
codeString.AppendLine("{");
|
||||
var methods = this.MethodSymbols;
|
||||
|
||||
foreach (var item in methods)
|
||||
{
|
||||
this.BuildMethodFunc(codeString, item);
|
||||
this.BuildMethod(codeString, item);
|
||||
}
|
||||
codeString.AppendLine("}");
|
||||
|
||||
if (!this.NamedTypeSymbol.ContainingNamespace.IsGlobalNamespace)
|
||||
{
|
||||
codeString.AppendLine("}");
|
||||
}
|
||||
|
||||
// System.Diagnostics.Debugger.Launch();
|
||||
return codeString.ToString();
|
||||
}
|
||||
|
||||
private void BuildMethod(StringBuilder codeString, IMethodSymbol method)
|
||||
{
|
||||
var objectName = this.GetObjectName(method);
|
||||
|
||||
codeString.AppendLine($"private static object {this.GetMethodName(method)}(object {objectName}, object[] ps)");
|
||||
codeString.AppendLine("{");
|
||||
|
||||
var ps = new List<string>();
|
||||
for (var i = 0; i < method.Parameters.Length; i++)
|
||||
{
|
||||
var parameter = method.Parameters[i];
|
||||
codeString.AppendLine($"var {parameter.Name}=({parameter.Type.ToDisplayString()})ps[{i}];");
|
||||
|
||||
if (parameter.RefKind == RefKind.Ref)
|
||||
{
|
||||
ps.Add($"ref {parameter.Name}");
|
||||
}
|
||||
else if (parameter.RefKind == RefKind.Out)
|
||||
{
|
||||
ps.Add($"out {parameter.Name}");
|
||||
}
|
||||
else
|
||||
{
|
||||
ps.Add(parameter.Name);
|
||||
}
|
||||
}
|
||||
if (ps.Count > 0)
|
||||
{
|
||||
if (method.ReturnsVoid)
|
||||
{
|
||||
if (method.IsStatic)
|
||||
{
|
||||
codeString.AppendLine($"{method.ContainingType.ToDisplayString()}.{method.Name}({string.Join(",", ps)});");
|
||||
}
|
||||
else
|
||||
{
|
||||
codeString.AppendLine($"(({method.ContainingType.ToDisplayString()}){objectName}).{method.Name}({string.Join(",", ps)});");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (method.IsStatic)
|
||||
{
|
||||
codeString.AppendLine($"var result = {method.ContainingType.ToDisplayString()}.{method.Name}({string.Join(",", ps)});");
|
||||
}
|
||||
else
|
||||
{
|
||||
codeString.AppendLine($"var result = (({method.ContainingType.ToDisplayString()}){objectName}).{method.Name}({string.Join(",", ps)});");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (method.ReturnsVoid)
|
||||
{
|
||||
if (method.IsStatic)
|
||||
{
|
||||
codeString.AppendLine($"{method.ContainingType.ToDisplayString()}.{method.Name}();");
|
||||
}
|
||||
else
|
||||
{
|
||||
codeString.AppendLine($"(({method.ContainingType.ToDisplayString()}){objectName}).{method.Name}();");
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (method.IsStatic)
|
||||
{
|
||||
codeString.AppendLine($"var result = {method.ContainingType.ToDisplayString()}.{method.Name}();");
|
||||
}
|
||||
else
|
||||
{
|
||||
codeString.AppendLine($"var result = (({method.ContainingType.ToDisplayString()}){objectName}).{method.Name}();");
|
||||
}
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < method.Parameters.Length; i++)
|
||||
{
|
||||
var parameter = method.Parameters[i];
|
||||
|
||||
if (parameter.RefKind == RefKind.Ref || parameter.RefKind == RefKind.Out)
|
||||
{
|
||||
codeString.AppendLine($"ps[{i}]={parameter.Name};");
|
||||
}
|
||||
}
|
||||
|
||||
if (method.ReturnsVoid)
|
||||
{
|
||||
codeString.AppendLine("return default;");
|
||||
}
|
||||
else
|
||||
{
|
||||
codeString.AppendLine("return result;");
|
||||
}
|
||||
codeString.AppendLine("}");
|
||||
}
|
||||
|
||||
private void BuildMethodFunc(StringBuilder codeString, IMethodSymbol method)
|
||||
{
|
||||
codeString.AppendLine($"public static Func<object, object[], object> {this.GetMethodName(method)}Func => {this.GetMethodName(method)};");
|
||||
}
|
||||
|
||||
|
||||
private string GetMethodName(IMethodSymbol method)
|
||||
{
|
||||
return method.GetDeterminantName();
|
||||
}
|
||||
|
||||
|
||||
private string GetObjectName(IMethodSymbol method)
|
||||
{
|
||||
foreach (var item1 in new string[] { "obj", "targetObj", "target", "@obj", "@targetObj", "@target" })
|
||||
{
|
||||
var same = false;
|
||||
foreach (var item2 in method.Parameters)
|
||||
{
|
||||
if (item2.Name == item1)
|
||||
{
|
||||
same = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!same)
|
||||
{
|
||||
return item1;
|
||||
}
|
||||
}
|
||||
|
||||
return "@obj";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 Microsoft.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace TouchSocket;
|
||||
|
||||
[Generator]
|
||||
public class MethodInvokeSourceGenerator : ISourceGenerator
|
||||
{
|
||||
public void Execute(GeneratorExecutionContext context)
|
||||
{
|
||||
var s = context.Compilation.GetMetadataReference(context.Compilation.Assembly);
|
||||
|
||||
if (context.SyntaxReceiver is MethodInvokeSyntaxReceiver receiver)
|
||||
{
|
||||
//Debugger.Launch();
|
||||
try
|
||||
{
|
||||
var pairs = receiver.GetInvokePairs(context.Compilation);
|
||||
|
||||
var builders = pairs.Select(a => new MethodInvokeCodeBuilder(a.Key, a.Value));
|
||||
foreach (var builder in builders)
|
||||
{
|
||||
context.AddSource($"{builder.GetFileName()}.g.cs", builder.ToSourceText());
|
||||
}
|
||||
|
||||
foreach (var builder in pairs.Keys.Select(a => new MethodInvokeTitleCodeBuilder(a)))
|
||||
{
|
||||
context.AddSource($"{builder.GetFileName()}.g.cs", builder.ToSourceText());
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void Initialize(GeneratorInitializationContext context)
|
||||
{
|
||||
context.RegisterForSyntaxNotifications(() => new MethodInvokeSyntaxReceiver());
|
||||
}
|
||||
}
|
||||
@@ -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首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace TouchSocket;
|
||||
|
||||
internal sealed class MethodInvokeSyntaxReceiver : ISyntaxReceiver
|
||||
{
|
||||
public const string DynamicMethod = "TouchSocket.Core.DynamicMethodAttribute";
|
||||
private readonly List<TypeDeclarationSyntax> m_syntaxList = new List<TypeDeclarationSyntax>();
|
||||
|
||||
public static INamedTypeSymbol GeneratorPluginAttributeAttribute { get; private set; }
|
||||
|
||||
public Dictionary<INamedTypeSymbol, List<IMethodSymbol>> GetInvokePairs(Compilation compilation)
|
||||
{
|
||||
var pairs = new Dictionary<INamedTypeSymbol, List<IMethodSymbol>>();
|
||||
|
||||
foreach (var syntax in this.m_syntaxList)
|
||||
{
|
||||
var namedTypeSymbol = compilation.GetSemanticModel(syntax.SyntaxTree).GetDeclaredSymbol(syntax);
|
||||
|
||||
switch (namedTypeSymbol.DeclaredAccessibility)
|
||||
{
|
||||
case Accessibility.ProtectedAndInternal:
|
||||
case Accessibility.Internal:
|
||||
case Accessibility.ProtectedOrInternal:
|
||||
case Accessibility.Public:
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
//Debugger.Launch();
|
||||
|
||||
var methodSymbols = this.GetMethodSymbols(namedTypeSymbol);
|
||||
|
||||
if (methodSymbols.Any())
|
||||
{
|
||||
if (pairs.TryGetValue(namedTypeSymbol, out var methods))
|
||||
{
|
||||
foreach (var item in methodSymbols)
|
||||
{
|
||||
if (!methods.Contains(item))
|
||||
{
|
||||
methods.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
methods = new List<IMethodSymbol>(methodSymbols);
|
||||
pairs.Add(namedTypeSymbol, methods);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pairs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 访问语法树
|
||||
/// </summary>
|
||||
/// <param name="syntaxNode"></param>
|
||||
void ISyntaxReceiver.OnVisitSyntaxNode(SyntaxNode syntaxNode)
|
||||
{
|
||||
if (syntaxNode is TypeDeclarationSyntax syntax)
|
||||
{
|
||||
this.m_syntaxList.Add(syntax);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<IMethodSymbol> GetMethodSymbols(INamedTypeSymbol namedTypeSymbol)
|
||||
{
|
||||
if (namedTypeSymbol.IsGenericType)
|
||||
{
|
||||
return Array.Empty<IMethodSymbol>();
|
||||
}
|
||||
//Debugger.Launch();
|
||||
if (this.IsDynamicMethod(namedTypeSymbol))
|
||||
{
|
||||
return namedTypeSymbol.GetMembers().OfType<IMethodSymbol>().Where(a =>
|
||||
{
|
||||
if (a.MethodKind != MethodKind.Ordinary)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (a.IsGenericMethod)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (a.DeclaredAccessibility)
|
||||
{
|
||||
case Accessibility.ProtectedAndInternal:
|
||||
case Accessibility.Internal:
|
||||
case Accessibility.ProtectedOrInternal:
|
||||
case Accessibility.Public:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
return namedTypeSymbol.GetMembers().OfType<IMethodSymbol>().Where(a =>
|
||||
{
|
||||
if (a.MethodKind != MethodKind.Ordinary)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (a.IsGenericMethod)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!this.IsDynamicMethod(a))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
switch (a.DeclaredAccessibility)
|
||||
{
|
||||
case Accessibility.ProtectedAndInternal:
|
||||
case Accessibility.Protected:
|
||||
case Accessibility.Internal:
|
||||
case Accessibility.ProtectedOrInternal:
|
||||
case Accessibility.Public:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsDynamicMethod(ISymbol symbol)
|
||||
{
|
||||
foreach (var attribute in symbol.GetAttributes())
|
||||
{
|
||||
if (attribute.AttributeClass?.ToDisplayString() == DynamicMethod)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (attribute.AttributeClass.HasAttribute(DynamicMethod))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 Microsoft.CodeAnalysis;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace TouchSocket;
|
||||
|
||||
internal class MethodInvokeTitleCodeBuilder : CodeBuilder
|
||||
{
|
||||
private readonly INamedTypeSymbol m_namedTypeSymbol;
|
||||
|
||||
public MethodInvokeTitleCodeBuilder(INamedTypeSymbol type)
|
||||
{
|
||||
this.m_namedTypeSymbol = type;
|
||||
}
|
||||
|
||||
public override string Id => this.m_namedTypeSymbol.ToDisplayString();
|
||||
public INamedTypeSymbol NamedTypeSymbol => this.m_namedTypeSymbol;
|
||||
|
||||
public virtual IEnumerable<string> Usings
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return "using System;";
|
||||
yield return "using System.Diagnostics;";
|
||||
yield return "using TouchSocket.Core;";
|
||||
yield return "using System.Threading.Tasks;";
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual string GeneratorTypeNamespace => "TouchSocket.Core.__Internals";
|
||||
|
||||
public override string GetFileName()
|
||||
{
|
||||
return this.GeneratorTypeNamespace + this.GetGeneratorTypeName() + "Title.Generator";
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var codeString = new StringBuilder();
|
||||
codeString.AppendLine("/*");
|
||||
codeString.AppendLine("此代码由Rpc工具直接生成,非必要请不要修改此处代码");
|
||||
codeString.AppendLine("*/");
|
||||
codeString.AppendLine("#pragma warning disable");
|
||||
|
||||
foreach (var item in this.Usings)
|
||||
{
|
||||
codeString.AppendLine(item);
|
||||
}
|
||||
if (!this.NamedTypeSymbol.ContainingNamespace.IsGlobalNamespace)
|
||||
{
|
||||
codeString.AppendLine($"namespace {this.GeneratorTypeNamespace}");
|
||||
codeString.AppendLine("{");
|
||||
}
|
||||
|
||||
codeString.AppendLine($"[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]");
|
||||
codeString.AppendLine($"[global::System.Obsolete(\"此方法不允许直接调用\")]");
|
||||
codeString.AppendLine(Utils.GetGeneratedCodeString());
|
||||
|
||||
codeString.AppendLine($"#if NET6_0_OR_GREATER");
|
||||
codeString.AppendLine($"[global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)]");
|
||||
codeString.AppendLine($"[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]");
|
||||
codeString.AppendLine($"[global::System.Diagnostics.DebuggerNonUserCode]");
|
||||
codeString.AppendLine($"#endif");
|
||||
codeString.AppendLine($"partial class {this.GetGeneratorTypeName()}");
|
||||
codeString.AppendLine("{");
|
||||
codeString.AppendLine($"#if NET6_0_OR_GREATER");
|
||||
codeString.AppendLine("[System.Runtime.CompilerServices.ModuleInitializer]");
|
||||
codeString.AppendLine($"[System.Diagnostics.CodeAnalysis.DynamicDependency( System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties,typeof({this.GetGeneratorTypeName()}))]");
|
||||
codeString.AppendLine("public static void TouchSocketModuleInitializer()");
|
||||
codeString.AppendLine("{");
|
||||
codeString.AppendLine("");
|
||||
codeString.AppendLine("}");
|
||||
codeString.AppendLine($"#endif");
|
||||
|
||||
codeString.AppendLine("}");
|
||||
|
||||
if (!this.NamedTypeSymbol.ContainingNamespace.IsGlobalNamespace)
|
||||
{
|
||||
codeString.AppendLine("}");
|
||||
}
|
||||
|
||||
// System.Diagnostics.Debugger.Launch();
|
||||
return codeString.ToString();
|
||||
}
|
||||
|
||||
private string GetGeneratorTypeName()
|
||||
{
|
||||
var typeName = $"__{Utils.MakeIdentifier(this.m_namedTypeSymbol.ToDisplayString())}MethodExtension";
|
||||
|
||||
return typeName;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -14,164 +14,201 @@ using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace TouchSocket
|
||||
namespace TouchSocket;
|
||||
|
||||
internal sealed class PluginCodeBuilder : CodeBuilder
|
||||
{
|
||||
internal sealed class PluginCodeBuilder
|
||||
private const string IPluginManagerString = "TouchSocket.Core.IPluginManager";
|
||||
private const string PluginBaseString = "TouchSocket.Core.PluginBase";
|
||||
private const string PluginEventArgsString = "TouchSocket.Core.PluginEventArgs";
|
||||
private readonly INamedTypeSymbol m_pluginClass;
|
||||
|
||||
public PluginCodeBuilder(INamedTypeSymbol pluginClass)
|
||||
{
|
||||
private readonly INamedTypeSymbol m_pluginClass;
|
||||
this.m_pluginClass = pluginClass;
|
||||
}
|
||||
|
||||
private const string PluginEventArgsString = "TouchSocket.Core.PluginEventArgs";
|
||||
private const string PluginBaseString = "TouchSocket.Core.PluginBase";
|
||||
private const string IPluginManagerString = "TouchSocket.Core.IPluginManager";
|
||||
public override string Id => this.m_pluginClass.ToDisplayString();
|
||||
public string Prefix { get; set; }
|
||||
|
||||
public PluginCodeBuilder(INamedTypeSymbol pluginClass)
|
||||
public IEnumerable<string> Usings
|
||||
{
|
||||
get
|
||||
{
|
||||
this.m_pluginClass = pluginClass;
|
||||
}
|
||||
|
||||
public string Prefix { get; set; }
|
||||
|
||||
public IEnumerable<string> Usings
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return "using System;";
|
||||
yield return "using System.Diagnostics;";
|
||||
yield return "using TouchSocket.Core;";
|
||||
yield return "using System.Threading.Tasks;";
|
||||
}
|
||||
}
|
||||
|
||||
public string GetFileName()
|
||||
{
|
||||
return this.m_pluginClass.ToDisplayString() + "Generator";
|
||||
}
|
||||
|
||||
public bool TryToSourceText(out SourceText sourceText)
|
||||
{
|
||||
var code = this.ToString();
|
||||
if (string.IsNullOrEmpty(code))
|
||||
{
|
||||
sourceText = null;
|
||||
return false;
|
||||
}
|
||||
sourceText = SourceText.From(code, Encoding.UTF8);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var methods = this.FindMethods().ToList();
|
||||
if (methods.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var codeString = new StringBuilder();
|
||||
codeString.AppendLine("/*");
|
||||
codeString.AppendLine("此代码由Plugin工具直接生成,非必要请不要修改此处代码");
|
||||
codeString.AppendLine("*/");
|
||||
codeString.AppendLine("#pragma warning disable");
|
||||
|
||||
foreach (var item in this.Usings)
|
||||
{
|
||||
codeString.AppendLine(item);
|
||||
}
|
||||
|
||||
//Debugger.Launch();
|
||||
|
||||
codeString.AppendLine($"namespace {this.m_pluginClass.ContainingNamespace}");
|
||||
codeString.AppendLine("{");
|
||||
codeString.AppendLine($"[global::System.CodeDom.Compiler.GeneratedCode(\"TouchSocket.SourceGenerator\",\"{Assembly.GetExecutingAssembly().GetName().Version.ToString()}\")]");
|
||||
codeString.AppendLine($"partial class {this.m_pluginClass.Name}");
|
||||
codeString.AppendLine("{");
|
||||
codeString.AppendLine("private int RegisterPlugins(IPluginManager pluginManager)");
|
||||
codeString.AppendLine("{");
|
||||
foreach (var item in methods)
|
||||
{
|
||||
this.BuildMethod(codeString, item);
|
||||
}
|
||||
codeString.AppendLine($"return {methods.Count};");
|
||||
codeString.AppendLine("}");
|
||||
this.TryBuildInvokeRedister(codeString);
|
||||
codeString.AppendLine("}");
|
||||
codeString.AppendLine("}");
|
||||
|
||||
// System.Diagnostics.Debugger.Launch();
|
||||
return codeString.ToString();
|
||||
}
|
||||
|
||||
private void TryBuildInvokeRedister(StringBuilder stringBuilder)
|
||||
{
|
||||
if (!this.m_pluginClass.IsInheritFrom(PluginBaseString))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.HasOverrideMethod())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
stringBuilder.AppendLine("protected override void Loaded(IPluginManager pluginManager)");
|
||||
stringBuilder.AppendLine("{");
|
||||
stringBuilder.AppendLine("base.Loaded(pluginManager);");
|
||||
stringBuilder.AppendLine("this.RegisterPlugins(pluginManager);");
|
||||
stringBuilder.AppendLine("}");
|
||||
}
|
||||
|
||||
private void BuildMethod(StringBuilder stringBuilder, IMethodSymbol methodSymbol)
|
||||
{
|
||||
// Debugger.Launch();
|
||||
var attributeData = methodSymbol.GetAttributes().FirstOrDefault(a => a.AttributeClass.ToDisplayString() == PluginSyntaxReceiver.GeneratorPluginAttributeTypeName);
|
||||
stringBuilder.AppendLine();
|
||||
stringBuilder.Append("pluginManager.Add<");
|
||||
stringBuilder.Append($"{methodSymbol.Parameters[0].Type.ToDisplayString()},");
|
||||
stringBuilder.Append($"{methodSymbol.Parameters[1].Type.ToDisplayString()}>(");
|
||||
stringBuilder.Append($"\"{attributeData.ConstructorArguments[0].Value}\",this.");
|
||||
stringBuilder.Append($"{methodSymbol.Name});");
|
||||
stringBuilder.AppendLine();
|
||||
}
|
||||
|
||||
private bool HasOverrideMethod()
|
||||
{
|
||||
return this.m_pluginClass
|
||||
.GetMembers()
|
||||
.OfType<IMethodSymbol>()
|
||||
.Any(m =>
|
||||
{
|
||||
if (m.Name == "Loaded" && m.IsOverride && m.Parameters.Length == 1 && m.Parameters[0].Type.ToDisplayString() == IPluginManagerString)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
private IEnumerable<IMethodSymbol> FindMethods()
|
||||
{
|
||||
return this.m_pluginClass
|
||||
.GetMembers()
|
||||
.OfType<IMethodSymbol>()
|
||||
.Where(m =>
|
||||
{
|
||||
if (m.Parameters.Length != 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m.ReturnType.ToDisplayString() != typeof(Task).FullName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!m.Parameters[1].Type.IsInheritFrom(PluginEventArgsString))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return m.GetAttributes().Any(a => a.AttributeClass.ToDisplayString() == PluginSyntaxReceiver.GeneratorPluginAttributeTypeName);
|
||||
});
|
||||
yield return "using System;";
|
||||
yield return "using System.Diagnostics;";
|
||||
yield return "using TouchSocket.Core;";
|
||||
yield return "using System.Threading.Tasks;";
|
||||
}
|
||||
}
|
||||
|
||||
public override string GetFileName()
|
||||
{
|
||||
return this.m_pluginClass.ToDisplayString() + "ExtensionsGenerator";
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var method = this.FindMethod();
|
||||
if (method is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (method.Parameters.Length != 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var codeString = new StringBuilder();
|
||||
codeString.AppendLine("/*");
|
||||
codeString.AppendLine("此代码由Plugin工具直接生成,非必要请不要修改此处代码");
|
||||
codeString.AppendLine("*/");
|
||||
codeString.AppendLine("#pragma warning disable");
|
||||
|
||||
foreach (var item in this.Usings)
|
||||
{
|
||||
codeString.AppendLine(item);
|
||||
}
|
||||
|
||||
//Debugger.Launch();
|
||||
|
||||
var pluginClassName = this.GetPluginClassName();
|
||||
var pluginMethodName = method.Name;
|
||||
var firstType = method.Parameters[0].Type;
|
||||
var secondType = method.Parameters[1].Type;
|
||||
// var xml = ExtractSummary((method.GetDocumentationCommentXml() ?? this.m_pluginClass.GetDocumentationCommentXml()) ?? string.Empty);
|
||||
|
||||
if (!this.m_pluginClass.ContainingNamespace.IsGlobalNamespace)
|
||||
{
|
||||
codeString.AppendLine($"namespace {this.m_pluginClass.ContainingNamespace}");
|
||||
codeString.AppendLine("{");
|
||||
}
|
||||
|
||||
codeString.AppendLine($"/// <inheritdoc cref=\"{this.m_pluginClass.ToDisplayString()}\"/>");
|
||||
codeString.AppendLine($"public static class _{pluginClassName}Extensions");
|
||||
codeString.AppendLine("{");
|
||||
//1
|
||||
codeString.AppendLine($"/// <inheritdoc cref=\"{this.m_pluginClass.ToDisplayString()}.{method.Name}({firstType.ToDisplayString()},{secondType.ToDisplayString()})\"/>");
|
||||
codeString.AppendLine($"public static void Add{pluginClassName}(this IPluginManager pluginManager, Func<{firstType.ToDisplayString()}, {secondType.ToDisplayString()}, Task> func)");
|
||||
|
||||
codeString.AppendLine("{");
|
||||
codeString.AppendLine($"pluginManager.Add(typeof({this.m_pluginClass.ToDisplayString()}), func);");
|
||||
codeString.AppendLine("}");
|
||||
|
||||
//2
|
||||
codeString.AppendLine($"/// <inheritdoc cref=\"{this.m_pluginClass.ToDisplayString()}.{method.Name}({firstType.ToDisplayString()},{secondType.ToDisplayString()})\"/>");
|
||||
codeString.AppendLine($"public static void Add{pluginClassName}(this IPluginManager pluginManager, Action<{firstType.ToDisplayString()}> action)");
|
||||
|
||||
codeString.AppendLine("{");
|
||||
codeString.AppendLine("Task newFunc(object sender, PluginEventArgs e)");
|
||||
codeString.AppendLine("{");
|
||||
codeString.AppendLine($"action(({firstType.ToDisplayString()})sender);");
|
||||
codeString.AppendLine("return e.InvokeNext();");
|
||||
codeString.AppendLine("}");
|
||||
codeString.AppendLine($"pluginManager.Add(typeof({this.m_pluginClass.ToDisplayString()}), newFunc, action);");
|
||||
codeString.AppendLine("}");
|
||||
|
||||
//3
|
||||
codeString.AppendLine($"/// <inheritdoc cref=\"{this.m_pluginClass.ToDisplayString()}.{method.Name}({firstType.ToDisplayString()},{secondType.ToDisplayString()})\"/>");
|
||||
codeString.AppendLine($"public static void Add{pluginClassName}(this IPluginManager pluginManager, Action action)");
|
||||
|
||||
codeString.AppendLine("{");
|
||||
codeString.AppendLine($"pluginManager.Add(typeof({this.m_pluginClass.ToDisplayString()}), action);");
|
||||
codeString.AppendLine("}");
|
||||
|
||||
//4
|
||||
codeString.AppendLine($"/// <inheritdoc cref=\"{this.m_pluginClass.ToDisplayString()}.{method.Name}({firstType.ToDisplayString()},{secondType.ToDisplayString()})\"/>");
|
||||
codeString.AppendLine($"public static void Add{pluginClassName}(this IPluginManager pluginManager, Func<{secondType.ToDisplayString()},Task> func)");
|
||||
|
||||
codeString.AppendLine("{");
|
||||
codeString.AppendLine($"pluginManager.Add(typeof({this.m_pluginClass.ToDisplayString()}), func);");
|
||||
codeString.AppendLine("}");
|
||||
|
||||
//5
|
||||
codeString.AppendLine($"/// <inheritdoc cref=\"{this.m_pluginClass.ToDisplayString()}.{method.Name}({firstType.ToDisplayString()},{secondType.ToDisplayString()})\"/>");
|
||||
codeString.AppendLine($"public static void Add{pluginClassName}(this IPluginManager pluginManager, Func<{firstType.ToDisplayString()},Task> func)");
|
||||
|
||||
codeString.AppendLine("{");
|
||||
codeString.AppendLine("async Task newFunc(object sender, PluginEventArgs e)");
|
||||
codeString.AppendLine("{");
|
||||
codeString.AppendLine($"await func(({firstType.ToDisplayString()})sender).ConfigureAwait(EasyTask.ContinueOnCapturedContext);");
|
||||
codeString.AppendLine("await e.InvokeNext().ConfigureAwait(EasyTask.ContinueOnCapturedContext);");
|
||||
codeString.AppendLine("}");
|
||||
codeString.AppendLine($"pluginManager.Add(typeof({this.m_pluginClass.ToDisplayString()}), newFunc, func);");
|
||||
codeString.AppendLine("}");
|
||||
|
||||
//6
|
||||
codeString.AppendLine($"/// <inheritdoc cref=\"{this.m_pluginClass.ToDisplayString()}.{method.Name}({firstType.ToDisplayString()},{secondType.ToDisplayString()})\"/>");
|
||||
codeString.AppendLine($"public static void Add{pluginClassName}(this IPluginManager pluginManager, Func<Task> func)");
|
||||
|
||||
codeString.AppendLine("{");
|
||||
codeString.AppendLine($"pluginManager.Add(typeof({this.m_pluginClass.ToDisplayString()}), func);");
|
||||
codeString.AppendLine("}");
|
||||
|
||||
//class end
|
||||
codeString.AppendLine("}");
|
||||
if (!this.m_pluginClass.ContainingNamespace.IsGlobalNamespace)
|
||||
{
|
||||
codeString.AppendLine("}");
|
||||
}
|
||||
|
||||
// System.Diagnostics.Debugger.Launch();
|
||||
return codeString.ToString();
|
||||
}
|
||||
|
||||
private string ExtractSummary(string xmlDoc)
|
||||
{
|
||||
if (string.IsNullOrEmpty(xmlDoc))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
try
|
||||
{
|
||||
var doc = XDocument.Parse(xmlDoc);
|
||||
var summaryElement = doc.Descendants("summary").FirstOrDefault();
|
||||
var summary = summaryElement?.Value.Trim();
|
||||
|
||||
if (string.IsNullOrEmpty(summary))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
//去掉换行符
|
||||
return summary.Replace("\n", "").Replace("\r", "");
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetPluginClassName()
|
||||
{
|
||||
var name = this.m_pluginClass.Name;
|
||||
if (name.StartsWith("I"))
|
||||
{
|
||||
return name.Substring(1);
|
||||
}
|
||||
return this.m_pluginClass.Name;
|
||||
}
|
||||
|
||||
private IMethodSymbol FindMethod()
|
||||
{
|
||||
return this.m_pluginClass.GetMembers().OfType<IMethodSymbol>().FirstOrDefault();
|
||||
}
|
||||
|
||||
public bool TryToSourceText(out SourceText sourceText)
|
||||
{
|
||||
var code = this.ToString();
|
||||
if (string.IsNullOrEmpty(code))
|
||||
{
|
||||
sourceText = null;
|
||||
return false;
|
||||
}
|
||||
sourceText = SourceText.From(code, Encoding.UTF8);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -14,12 +14,19 @@ using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using System.Linq;
|
||||
|
||||
namespace TouchSocket
|
||||
namespace TouchSocket;
|
||||
|
||||
[Generator]
|
||||
public class PluginSourceGenerator : ISourceGenerator
|
||||
{
|
||||
[Generator]
|
||||
public class PluginSourceGenerator : ISourceGenerator
|
||||
{
|
||||
private string m_generatorPluginAttribute = @"
|
||||
private readonly string m_generatorPluginAttribute = @"
|
||||
|
||||
/*
|
||||
此代码由SourceGenerator工具直接生成,非必要请不要修改此处代码
|
||||
*/
|
||||
|
||||
#pragma warning disable
|
||||
|
||||
using System;
|
||||
|
||||
namespace TouchSocket.Core
|
||||
@@ -27,54 +34,58 @@ namespace TouchSocket.Core
|
||||
/// <summary>
|
||||
/// 使用源生成插件的调用。
|
||||
/// </summary>
|
||||
internal class GeneratorPluginAttribute:Attribute
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
[Obsolete(""此特性已被弃用,请直接使用接口实现插件,支持AOT"",true)]
|
||||
/*GeneratedCode*/
|
||||
internal class GeneratorPluginAttribute : Attribute
|
||||
{
|
||||
public string PluginName { get; set; }
|
||||
public Type PluginType { get;}
|
||||
|
||||
/// <summary>
|
||||
/// 使用源生成插件的调用。
|
||||
/// </summary>
|
||||
/// <param name=""pluginName"">插件名称,一般建议使用nameof()解决。</param>
|
||||
public GeneratorPluginAttribute(string pluginName)
|
||||
/// <param name=""pluginType"">插件名称,一般建议使用<see langword=""typeof""/>解决。</param>
|
||||
public GeneratorPluginAttribute(Type pluginType)
|
||||
{
|
||||
this.PluginName = pluginName;
|
||||
this.PluginType = pluginType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
";
|
||||
|
||||
public void Initialize(GeneratorInitializationContext context)
|
||||
{
|
||||
context.RegisterForPostInitialization(a =>
|
||||
{
|
||||
a.AddSource(nameof(this.m_generatorPluginAttribute), this.m_generatorPluginAttribute);
|
||||
});
|
||||
context.RegisterForSyntaxNotifications(() => new PluginSyntaxReceiver());
|
||||
}
|
||||
public void Execute(GeneratorExecutionContext context)
|
||||
{
|
||||
var s = context.Compilation.GetMetadataReference(context.Compilation.Assembly);
|
||||
|
||||
public void Execute(GeneratorExecutionContext context)
|
||||
if (context.SyntaxReceiver is PluginSyntaxReceiver receiver)
|
||||
{
|
||||
var s = context.Compilation.GetMetadataReference(context.Compilation.Assembly);
|
||||
|
||||
if (context.SyntaxReceiver is PluginSyntaxReceiver receiver)
|
||||
var builders = receiver
|
||||
.GetPluginPluginInterfaceTypes(context.Compilation)
|
||||
.Select(i => new PluginCodeBuilder(i))
|
||||
.Distinct(CodeBuilderEqualityComparer<PluginCodeBuilder>.Default);
|
||||
//Debugger.Launch();
|
||||
foreach (var builder in builders)
|
||||
{
|
||||
var builders = receiver
|
||||
.GetPluginClassTypes(context.Compilation)
|
||||
.Select(i => new PluginCodeBuilder(i))
|
||||
.Distinct();
|
||||
//Debugger.Launch();
|
||||
foreach (var builder in builders)
|
||||
if (builder.TryToSourceText(out var sourceText))
|
||||
{
|
||||
if (builder.TryToSourceText(out var sourceText))
|
||||
{
|
||||
var tree = CSharpSyntaxTree.ParseText(sourceText);
|
||||
var root = tree.GetRoot().NormalizeWhitespace();
|
||||
var ret = root.ToFullString();
|
||||
context.AddSource($"{builder.GetFileName()}.g.cs", ret);
|
||||
}
|
||||
var tree = CSharpSyntaxTree.ParseText(sourceText);
|
||||
var root = tree.GetRoot().NormalizeWhitespace();
|
||||
var ret = root.ToFullString();
|
||||
context.AddSource($"{builder.GetFileName()}.g.cs", ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Initialize(GeneratorInitializationContext context)
|
||||
{
|
||||
context.RegisterForPostInitialization(a =>
|
||||
{
|
||||
var sourceCode = this.m_generatorPluginAttribute.Replace("/*GeneratedCode*/", Utils.GetGeneratedCodeString());
|
||||
|
||||
a.AddSource(nameof(this.m_generatorPluginAttribute), sourceCode);
|
||||
});
|
||||
context.RegisterForSyntaxNotifications(() => new PluginSyntaxReceiver());
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -14,103 +14,59 @@ using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using TouchSocket.SourceGenerator;
|
||||
|
||||
namespace TouchSocket
|
||||
namespace TouchSocket;
|
||||
|
||||
/// <summary>
|
||||
/// RpcApi语法接收器
|
||||
/// </summary>
|
||||
internal sealed class PluginSyntaxReceiver : ISyntaxReceiver
|
||||
{
|
||||
//public const string GeneratorPluginAttributeTypeName = "TouchSocket.Core.GeneratorPluginAttribute";
|
||||
|
||||
/// <summary>
|
||||
/// RpcApi语法接收器
|
||||
/// 接口列表
|
||||
/// </summary>
|
||||
internal sealed class PluginSyntaxReceiver : ISyntaxReceiver
|
||||
private readonly List<InterfaceDeclarationSyntax> m_classSyntaxList = new();
|
||||
|
||||
//public static INamedTypeSymbol GeneratorPluginAttributeAttribute { get; private set; }
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有插件符号
|
||||
/// </summary>
|
||||
/// <param name="compilation"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<INamedTypeSymbol> GetPluginPluginInterfaceTypes(Compilation compilation)
|
||||
{
|
||||
public const string GeneratorPluginAttributeTypeName = "TouchSocket.Core.GeneratorPluginAttribute";
|
||||
|
||||
/// <summary>
|
||||
/// 接口列表
|
||||
/// </summary>
|
||||
private readonly List<ClassDeclarationSyntax> m_classSyntaxList = new List<ClassDeclarationSyntax>();
|
||||
|
||||
/// <summary>
|
||||
/// 访问语法树
|
||||
/// </summary>
|
||||
/// <param name="syntaxNode"></param>
|
||||
void ISyntaxReceiver.OnVisitSyntaxNode(SyntaxNode syntaxNode)
|
||||
// Debugger.Launch();
|
||||
//GeneratorPluginAttributeAttribute = compilation.GetTypeByMetadataName(GeneratorPluginAttributeTypeName);
|
||||
//if (GeneratorPluginAttributeAttribute == null)
|
||||
//{
|
||||
// yield break;
|
||||
//}
|
||||
foreach (var classSyntax in this.m_classSyntaxList)
|
||||
{
|
||||
if (syntaxNode is ClassDeclarationSyntax syntax)
|
||||
var @class = compilation.GetSemanticModel(classSyntax.SyntaxTree).GetDeclaredSymbol(classSyntax);
|
||||
if (@class != null && CoreAnalyzer.IsPluginInterface(@class) && @class.DeclaredAccessibility == Accessibility.Public)
|
||||
{
|
||||
this.m_classSyntaxList.Add(syntax);
|
||||
yield return @class;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static INamedTypeSymbol GeneratorPluginAttributeAttribute { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有插件符号
|
||||
/// </summary>
|
||||
/// <param name="compilation"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<INamedTypeSymbol> GetPluginClassTypes(Compilation compilation)
|
||||
/// <summary>
|
||||
/// 访问语法树
|
||||
/// </summary>
|
||||
/// <param name="syntaxNode"></param>
|
||||
void ISyntaxReceiver.OnVisitSyntaxNode(SyntaxNode syntaxNode)
|
||||
{
|
||||
if (syntaxNode is InterfaceDeclarationSyntax syntax)
|
||||
{
|
||||
// Debugger.Launch();
|
||||
GeneratorPluginAttributeAttribute = compilation.GetTypeByMetadataName(GeneratorPluginAttributeTypeName);
|
||||
if (GeneratorPluginAttributeAttribute == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
foreach (var classSyntax in this.m_classSyntaxList)
|
||||
{
|
||||
var @class = compilation.GetSemanticModel(classSyntax.SyntaxTree).GetDeclaredSymbol(classSyntax);
|
||||
if (@class != null && IsPluginClass(@class))
|
||||
{
|
||||
yield return @class;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否为插件
|
||||
/// </summary>
|
||||
/// <param name="class"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsPluginClass(INamedTypeSymbol @class)
|
||||
{
|
||||
if (GeneratorPluginAttributeAttribute is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
//Debugger.Launch();
|
||||
|
||||
if (@class.IsAbstract)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return @class.AllInterfaces.Any(a =>
|
||||
{
|
||||
if (a.ToDisplayString() == "TouchSocket.Core.IPlugin")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回是否声明指定的特性
|
||||
/// </summary>
|
||||
/// <param name="symbol"></param>
|
||||
/// <param name="attribute"></param>
|
||||
/// <returns></returns>
|
||||
public static bool HasAttribute(ISymbol symbol, INamedTypeSymbol attribute)
|
||||
{
|
||||
foreach (var attr in symbol.GetAttributes())
|
||||
{
|
||||
var attrClass = attr.AttributeClass;
|
||||
if (attrClass != null && attrClass.AllInterfaces.Contains(attribute))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
this.m_classSyntaxList.Add(syntax);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,21 @@
|
||||
<GenerateDocumentationFile>False</GenerateDocumentationFile>
|
||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||
<SignAssembly>false</SignAssembly>
|
||||
<DefineConstants>SourceGenerator</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.9.0" PrivateAssets="all" />
|
||||
<!--<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.9.0" />-->
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="AnalyzerReleases.Shipped.md" />
|
||||
<AdditionalFiles Include="AnalyzerReleases.Unshipped.md" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Common\" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\TouchSocket.SourceGenerator.SharedProject\TouchSocket.SourceGenerator.SharedProject.projitems" Label="Shared" />
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
此代码由SourceGenerator工具直接生成,非必要请不要修改此处代码
|
||||
*/
|
||||
|
||||
#pragma warning disable
|
||||
|
||||
using System;
|
||||
using System.CodeDom.Compiler;
|
||||
|
||||
namespace TouchSocket.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// 标识源生成<see cref="IPackage"/>的实现。
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
|
||||
/*GeneratedCode*/
|
||||
internal class GeneratorPackageAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标识源生成<see cref="IPackage"/>成员的特性。
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
|
||||
/*GeneratedCode*/
|
||||
internal class PackageMemberAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// 生成行为。一般来说,对于非只读、非重写、且同时拥有get,set(可以私有)访问器的属性,会自动生成。
|
||||
/// 对于字段,均不会自动生成。所以可以使用该设置,来指示生成器的生成行为。
|
||||
/// </summary>
|
||||
public PackageBehavior Behavior { get; set; }
|
||||
public int Index { get; set; }
|
||||
public Type Converter { get; set; }
|
||||
}
|
||||
|
||||
/*GeneratedCode*/
|
||||
internal enum PackageBehavior : byte
|
||||
{
|
||||
Ignore,
|
||||
Include
|
||||
}
|
||||
}
|
||||
1131
src/TouchSocket.Core.SourceGenerator/_Package/PackageCodeBuilder.cs
Normal file
1131
src/TouchSocket.Core.SourceGenerator/_Package/PackageCodeBuilder.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace TouchSocket;
|
||||
|
||||
[Generator]
|
||||
public class PackageSourceGenerator : ISourceGenerator
|
||||
{
|
||||
private readonly string m_generatorPackageAttribute = @"
|
||||
|
||||
|
||||
/*
|
||||
此代码由SourceGenerator工具直接生成,非必要请不要修改此处代码
|
||||
*/
|
||||
|
||||
#pragma warning disable
|
||||
|
||||
using System;
|
||||
using System.CodeDom.Compiler;
|
||||
|
||||
namespace TouchSocket.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// 标识源生成<see cref=""IPackage""/>的实现。
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
|
||||
/*GeneratedCode*/
|
||||
internal class GeneratorPackageAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标识源生成<see cref=""IPackage""/>成员的特性。
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
|
||||
/*GeneratedCode*/
|
||||
internal class PackageMemberAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// 生成行为。一般来说,对于非只读、非重写、且同时拥有get,set(可以私有)访问器的属性,会自动生成。
|
||||
/// 对于字段,均不会自动生成。所以可以使用该设置,来指示生成器的生成行为。
|
||||
/// </summary>
|
||||
public PackageBehavior Behavior { get; set; }
|
||||
public int Index { get; set; }
|
||||
public Type Converter { get; set; }
|
||||
}
|
||||
|
||||
/*GeneratedCode*/
|
||||
internal enum PackageBehavior:byte
|
||||
{
|
||||
Ignore,
|
||||
Include
|
||||
}
|
||||
}
|
||||
|
||||
";
|
||||
|
||||
public void Initialize(GeneratorInitializationContext context)
|
||||
{
|
||||
context.RegisterForPostInitialization(a =>
|
||||
{
|
||||
var sourceCode = this.m_generatorPackageAttribute.Replace("/*GeneratedCode*/", $"[global::System.CodeDom.Compiler.GeneratedCode(\"TouchSocket.SourceGenerator\",\"{Assembly.GetExecutingAssembly().GetName().Version.ToString()}\")]");
|
||||
a.AddSource(nameof(this.m_generatorPackageAttribute), sourceCode);
|
||||
});
|
||||
context.RegisterForSyntaxNotifications(() => new PackageSyntaxReceiver());
|
||||
}
|
||||
|
||||
public void Execute(GeneratorExecutionContext context)
|
||||
{
|
||||
var s = context.Compilation.GetMetadataReference(context.Compilation.Assembly);
|
||||
|
||||
if (context.SyntaxReceiver is PackageSyntaxReceiver receiver)
|
||||
{
|
||||
var builders = receiver
|
||||
.GetPackageClassTypes(context.Compilation)
|
||||
.Select(i => new PackageCodeBuilder(i, context))
|
||||
.Distinct(CodeBuilderEqualityComparer<PackageCodeBuilder>.Default);
|
||||
//Debugger.Launch();
|
||||
foreach (var builder in builders)
|
||||
{
|
||||
if (builder.TryToSourceText(out var sourceText))
|
||||
{
|
||||
var tree = CSharpSyntaxTree.ParseText(sourceText);
|
||||
var root = tree.GetRoot().NormalizeWhitespace();
|
||||
var ret = root.ToFullString();
|
||||
context.AddSource($"{builder.GetFileName()}.g.cs", ret);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TouchSocket;
|
||||
|
||||
internal sealed class PackageSyntaxReceiver : ISyntaxReceiver
|
||||
{
|
||||
public const string GeneratorPackageAttributeTypeName = "TouchSocket.Core.GeneratorPackageAttribute";
|
||||
public const string IPackageTypeName = "TouchSocket.Core.IPackage";
|
||||
|
||||
public static INamedTypeSymbol GeneratorPackageAttribute { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 接口列表
|
||||
/// </summary>
|
||||
private readonly List<TypeDeclarationSyntax> m_classSyntaxList = new List<TypeDeclarationSyntax>();
|
||||
|
||||
/// <summary>
|
||||
/// 访问语法树
|
||||
/// </summary>
|
||||
/// <param name="syntaxNode"></param>
|
||||
void ISyntaxReceiver.OnVisitSyntaxNode(SyntaxNode syntaxNode)
|
||||
{
|
||||
if (syntaxNode is ClassDeclarationSyntax syntax)
|
||||
{
|
||||
this.m_classSyntaxList.Add(syntax);
|
||||
}
|
||||
else if (syntaxNode is StructDeclarationSyntax @struct)
|
||||
{
|
||||
this.m_classSyntaxList.Add(@struct);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有Package符号
|
||||
/// </summary>
|
||||
/// <param name="compilation"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<INamedTypeSymbol> GetPackageClassTypes(Compilation compilation)
|
||||
{
|
||||
// Debugger.Launch();
|
||||
GeneratorPackageAttribute = compilation.GetTypeByMetadataName(GeneratorPackageAttributeTypeName);
|
||||
if (GeneratorPackageAttribute == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
foreach (var classSyntax in this.m_classSyntaxList)
|
||||
{
|
||||
var @class = compilation.GetSemanticModel(classSyntax.SyntaxTree).GetDeclaredSymbol(classSyntax);
|
||||
if (@class != null && IsPackageClass(@class))
|
||||
{
|
||||
yield return @class;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否为容器生成
|
||||
/// </summary>
|
||||
/// <param name="class"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsPackageClass(INamedTypeSymbol @class)
|
||||
{
|
||||
if (GeneratorPackageAttribute is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
//Debugger.Launch();
|
||||
|
||||
if (!@class.HasAttribute(GeneratorPackageAttributeTypeName, out _))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (@class.IsInheritFrom(IPackageTypeName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 Microsoft.CodeAnalysis;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TouchSocket;
|
||||
|
||||
internal sealed class DmtpRpcClientCodeBuilder : RpcClientCodeBuilder
|
||||
{
|
||||
public const string DmtpRpcAttribute = "TouchSocket.Dmtp.Rpc.DmtpRpcAttribute";
|
||||
|
||||
public DmtpRpcClientCodeBuilder(INamedTypeSymbol rpcApi) : base(rpcApi, DmtpRpcAttribute)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override string RpcAttributeName => "DmtpRpc";
|
||||
|
||||
protected override string GetInheritedClassName(INamedTypeSymbol rpcApi)
|
||||
{
|
||||
return new DmtpRpcClientCodeBuilder(rpcApi).GetClassName();
|
||||
}
|
||||
|
||||
protected override string[] GetGenericConstraintTypes(IMethodSymbol method, Dictionary<string, TypedConstant> namedArguments)
|
||||
{
|
||||
var strings = new List<string>();
|
||||
strings.AddRange(base.GetGenericConstraintTypes(method, namedArguments));
|
||||
|
||||
strings.Add("TouchSocket.Dmtp.Rpc.IDmtpRpcActor");
|
||||
return strings.ToArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using System.Linq;
|
||||
|
||||
namespace TouchSocket;
|
||||
|
||||
[Generator]
|
||||
public class DmtpRpcClientSourceGenerator : ISourceGenerator
|
||||
{
|
||||
public void Initialize(GeneratorInitializationContext context)
|
||||
{
|
||||
context.RegisterForSyntaxNotifications(() => new DmtpRpcClientSyntaxReceiver());
|
||||
}
|
||||
|
||||
public void Execute(GeneratorExecutionContext context)
|
||||
{
|
||||
var s = context.Compilation.GetMetadataReference(context.Compilation.Assembly);
|
||||
|
||||
if (context.SyntaxReceiver is DmtpRpcClientSyntaxReceiver receiver)
|
||||
{
|
||||
var builders = receiver
|
||||
.GetRpcApiTypes(context.Compilation)
|
||||
.Select(i => new DmtpRpcClientCodeBuilder(i))
|
||||
.Distinct(CodeBuilderEqualityComparer<DmtpRpcClientCodeBuilder>.Default);
|
||||
//Debugger.Launch();
|
||||
foreach (var builder in builders)
|
||||
{
|
||||
var tree = CSharpSyntaxTree.ParseText(builder.ToSourceText());
|
||||
var root = tree.GetRoot().NormalizeWhitespace();
|
||||
var ret = root.ToFullString();
|
||||
context.AddSource($"{builder.GetFileName()}.g.cs", ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using System.Collections.Generic;
|
||||
using TouchSocket.Rpc;
|
||||
|
||||
namespace TouchSocket;
|
||||
|
||||
internal sealed class DmtpRpcClientSyntaxReceiver : ISyntaxReceiver
|
||||
{
|
||||
|
||||
private readonly List<InterfaceDeclarationSyntax> interfaceSyntaxList = new List<InterfaceDeclarationSyntax>();
|
||||
|
||||
void ISyntaxReceiver.OnVisitSyntaxNode(SyntaxNode syntaxNode)
|
||||
{
|
||||
if (syntaxNode is InterfaceDeclarationSyntax syntax)
|
||||
{
|
||||
this.interfaceSyntaxList.Add(syntax);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<INamedTypeSymbol> GetRpcApiTypes(Compilation compilation)
|
||||
{
|
||||
//Debugger.Launch();
|
||||
var generatorRpcProxyAttribute = RpcUtils.GetGeneratorRpcProxyAttribute(compilation);
|
||||
if (generatorRpcProxyAttribute == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
foreach (var interfaceSyntax in this.interfaceSyntaxList)
|
||||
{
|
||||
var @interface = compilation.GetSemanticModel(interfaceSyntax.SyntaxTree).GetDeclaredSymbol(interfaceSyntax);
|
||||
if (@interface != null && RpcUtils.IsRpcApiInterface(@interface))
|
||||
{
|
||||
yield return @interface;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<NoPackageAnalysis>true</NoPackageAnalysis>
|
||||
<ImportDirectoryBuildProps>false</ImportDirectoryBuildProps>
|
||||
<BaseOutputPath>bin</BaseOutputPath>
|
||||
<GenerateDocumentationFile>False</GenerateDocumentationFile>
|
||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||
<SignAssembly>false</SignAssembly>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\TouchSocket.SourceGenerator.SharedProject\Rpc\GeneratorFlag.cs" Link="GeneratorFlag.cs" />
|
||||
<Compile Include="..\TouchSocket.SourceGenerator.SharedProject\Rpc\RpcClientCodeBuilder.cs" Link="RpcClientCodeBuilder.cs" />
|
||||
<Compile Include="..\TouchSocket.SourceGenerator.SharedProject\Rpc\RpcUtils.cs" Link="RpcUtils.cs" />
|
||||
<Compile Include="..\TouchSocket.SourceGenerator.SharedProject\Utils.cs" Link="Utils.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.9.0" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,42 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 Microsoft.CodeAnalysis;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TouchSocket;
|
||||
|
||||
internal sealed class JsonRpcClientCodeBuilder : RpcClientCodeBuilder
|
||||
{
|
||||
public const string JsonRpcAttribute = "TouchSocket.JsonRpc.JsonRpcAttribute";
|
||||
|
||||
public JsonRpcClientCodeBuilder(INamedTypeSymbol rpcApi) : base(rpcApi, JsonRpcAttribute)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override string RpcAttributeName => "JsonRpc";
|
||||
|
||||
protected override string GetInheritedClassName(INamedTypeSymbol rpcApi)
|
||||
{
|
||||
return new JsonRpcClientCodeBuilder(rpcApi).GetClassName();
|
||||
}
|
||||
|
||||
protected override string[] GetGenericConstraintTypes(IMethodSymbol method, Dictionary<string, TypedConstant> namedArguments)
|
||||
{
|
||||
var strings = new List<string>();
|
||||
strings.AddRange(base.GetGenericConstraintTypes(method, namedArguments));
|
||||
|
||||
strings.Add("TouchSocket.JsonRpc.IJsonRpcClient");
|
||||
return strings.ToArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using System.Linq;
|
||||
|
||||
namespace TouchSocket;
|
||||
|
||||
[Generator]
|
||||
public class JsonRpcClientSourceGenerator : ISourceGenerator
|
||||
{
|
||||
public void Initialize(GeneratorInitializationContext context)
|
||||
{
|
||||
context.RegisterForSyntaxNotifications(() => new JsonRpcClientSyntaxReceiver());
|
||||
}
|
||||
|
||||
public void Execute(GeneratorExecutionContext context)
|
||||
{
|
||||
var s = context.Compilation.GetMetadataReference(context.Compilation.Assembly);
|
||||
|
||||
if (context.SyntaxReceiver is JsonRpcClientSyntaxReceiver receiver)
|
||||
{
|
||||
var builders = receiver
|
||||
.GetRpcApiTypes(context.Compilation)
|
||||
.Select(i => new JsonRpcClientCodeBuilder(i))
|
||||
.Distinct(CodeBuilderEqualityComparer<JsonRpcClientCodeBuilder>.Default);
|
||||
//Debugger.Launch();
|
||||
foreach (var builder in builders)
|
||||
{
|
||||
var tree = CSharpSyntaxTree.ParseText(builder.ToSourceText());
|
||||
var root = tree.GetRoot().NormalizeWhitespace();
|
||||
var ret = root.ToFullString();
|
||||
context.AddSource($"{builder.GetFileName()}.g.cs", ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using System.Collections.Generic;
|
||||
using TouchSocket.Rpc;
|
||||
|
||||
namespace TouchSocket;
|
||||
|
||||
internal sealed class JsonRpcClientSyntaxReceiver : ISyntaxReceiver
|
||||
{
|
||||
|
||||
private readonly List<InterfaceDeclarationSyntax> interfaceSyntaxList = new List<InterfaceDeclarationSyntax>();
|
||||
|
||||
void ISyntaxReceiver.OnVisitSyntaxNode(SyntaxNode syntaxNode)
|
||||
{
|
||||
if (syntaxNode is InterfaceDeclarationSyntax syntax)
|
||||
{
|
||||
this.interfaceSyntaxList.Add(syntax);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<INamedTypeSymbol> GetRpcApiTypes(Compilation compilation)
|
||||
{
|
||||
//Debugger.Launch();
|
||||
var generatorRpcProxyAttribute = RpcUtils.GetGeneratorRpcProxyAttribute(compilation);
|
||||
if (generatorRpcProxyAttribute == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
foreach (var interfaceSyntax in this.interfaceSyntaxList)
|
||||
{
|
||||
var @interface = compilation.GetSemanticModel(interfaceSyntax.SyntaxTree).GetDeclaredSymbol(interfaceSyntax);
|
||||
if (@interface != null && RpcUtils.IsRpcApiInterface(@interface))
|
||||
{
|
||||
yield return @interface;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<NoPackageAnalysis>true</NoPackageAnalysis>
|
||||
<ImportDirectoryBuildProps>false</ImportDirectoryBuildProps>
|
||||
<BaseOutputPath>bin</BaseOutputPath>
|
||||
<GenerateDocumentationFile>False</GenerateDocumentationFile>
|
||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||
<SignAssembly>false</SignAssembly>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\TouchSocket.SourceGenerator.SharedProject\Rpc\GeneratorFlag.cs" Link="GeneratorFlag.cs" />
|
||||
<Compile Include="..\TouchSocket.SourceGenerator.SharedProject\Rpc\RpcClientCodeBuilder.cs" Link="RpcClientCodeBuilder.cs" />
|
||||
<Compile Include="..\TouchSocket.SourceGenerator.SharedProject\Rpc\RpcUtils.cs" Link="RpcUtils.cs" />
|
||||
<Compile Include="..\TouchSocket.SourceGenerator.SharedProject\Utils.cs" Link="Utils.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.9.0" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
439
src/TouchSocket.Mqtt/Actors/MqttActor.cs
Normal file
439
src/TouchSocket.Mqtt/Actors/MqttActor.cs
Normal file
@@ -0,0 +1,439 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
public abstract class MqttActor : DisposableObject, IOnlineClient
|
||||
{
|
||||
#region 字段
|
||||
|
||||
private readonly Dictionary<ushort, MqttArrivedMessage> m_qos2MqttArrivedMessage = new();
|
||||
private readonly CancellationTokenSource m_tokenSource = new();
|
||||
private readonly WaitHandlePool<MqttIdentifierMessage> m_waitHandlePool = new();
|
||||
|
||||
#endregion 字段
|
||||
|
||||
public MqttActor()
|
||||
{
|
||||
this.m_waitHandlePool.MaxSign = 1;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (this.DisposedValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
this.m_tokenSource.Cancel();
|
||||
this.m_tokenSource.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Task PublishMessageArrivedAsync(MqttArrivedMessage message)
|
||||
{
|
||||
if (this.MessageArrived != null)
|
||||
{
|
||||
var args = new MqttReceivedEventArgs(message);
|
||||
return this.MessageArrived(this, args);
|
||||
}
|
||||
else
|
||||
{
|
||||
return EasyTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
#region 属性
|
||||
|
||||
public string Id { get; protected set; }
|
||||
|
||||
public bool Online { get; protected set; }
|
||||
|
||||
public CancellationTokenSource TokenSource => this.m_tokenSource;
|
||||
|
||||
public WaitHandlePool<MqttIdentifierMessage> WaitHandlePool => this.m_waitHandlePool;
|
||||
|
||||
#endregion 属性
|
||||
|
||||
#region Publish
|
||||
|
||||
public async Task PublishAsync(MqttPublishMessage message)
|
||||
{
|
||||
switch (message.QosLevel)
|
||||
{
|
||||
case QosLevel.AtMostOnce:
|
||||
await this.PublishAtMostOnceMessageAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
break;
|
||||
|
||||
case QosLevel.AtLeastOnce:
|
||||
await this.PublishAtLeastOnceMessageAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
break;
|
||||
|
||||
case QosLevel.ExactlyOnce:
|
||||
await this.PublishExactlyOnceMessageAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task PublishAtLeastOnceMessageAsync(MqttPublishMessage message)
|
||||
{
|
||||
var waitDataAsync = this.m_waitHandlePool.GetWaitDataAsync(message);
|
||||
|
||||
try
|
||||
{
|
||||
await this.ProtectedOutputSendAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
|
||||
var waitDataStatus = await waitDataAsync.WaitAsync(1000 * 10).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
waitDataStatus.ThrowIfNotRunning();
|
||||
|
||||
if (waitDataAsync.WaitResult is MqttPubAckMessage pubAckMessage)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.m_waitHandlePool.Destroy(waitDataAsync);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task PublishAtMostOnceMessageAsync(MqttPublishMessage message)
|
||||
{
|
||||
await this.ProtectedOutputSendAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
private async Task PublishExactlyOnceMessageAsync(MqttPublishMessage message)
|
||||
{
|
||||
var waitData_1_Async = this.m_waitHandlePool.GetWaitDataAsync(message);
|
||||
|
||||
try
|
||||
{
|
||||
await this.ProtectedOutputSendAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
var waitDataStatus = await waitData_1_Async.WaitAsync(1000 * 10).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
waitDataStatus.ThrowIfNotRunning();
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.m_waitHandlePool.Destroy(waitData_1_Async);
|
||||
}
|
||||
|
||||
var mqttPubRelMessage = new MqttPubRelMessage()
|
||||
{
|
||||
MessageId = message.MessageId
|
||||
};
|
||||
|
||||
var waitData_2_Async = this.m_waitHandlePool.GetWaitDataAsync(mqttPubRelMessage, false);
|
||||
|
||||
try
|
||||
{
|
||||
await this.ProtectedOutputSendAsync(mqttPubRelMessage).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
var waitDataStatus = await waitData_2_Async.WaitAsync(1000 * 10).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
waitDataStatus.ThrowIfNotRunning();
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.m_waitHandlePool.Destroy(waitData_2_Async);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Publish
|
||||
|
||||
#region InputMqttMessage
|
||||
|
||||
public async Task InputMqttMessageAsync(MqttMessage mqttMessage)
|
||||
{
|
||||
switch (mqttMessage)
|
||||
{
|
||||
case MqttConnectMessage message:
|
||||
await this.InputMqttConnectMessageAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
break;
|
||||
|
||||
case MqttConnAckMessage message:
|
||||
await this.InputMqttConnAckMessageAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
break;
|
||||
|
||||
case MqttPingReqMessage message:
|
||||
await this.InputMqttPingReqMessageAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
break;
|
||||
|
||||
case MqttPingRespMessage message:
|
||||
await this.InputMqttPingRespMessageAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
break;
|
||||
|
||||
case MqttPublishMessage message:
|
||||
await this.InputMqttPublishMessageAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
break;
|
||||
|
||||
case MqttPubAckMessage message:
|
||||
await this.InputMqttPubAckMessageAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
break;
|
||||
|
||||
case MqttPubRecMessage message:
|
||||
await this.InputMqttPubRecMessageAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
break;
|
||||
|
||||
case MqttPubRelMessage message:
|
||||
await this.InputMqttPubRelMessageAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
break;
|
||||
|
||||
case MqttPubCompMessage message:
|
||||
await this.InputMqttPubCompMessageAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
break;
|
||||
|
||||
case MqttSubscribeMessage message:
|
||||
await this.InputMqttSubscribeMessageAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
break;
|
||||
|
||||
case MqttSubAckMessage message:
|
||||
await this.InputMqttSubAckMessageAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
break;
|
||||
|
||||
case MqttUnsubscribeMessage message:
|
||||
await this.InputMqttUnsubscribeMessageAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
break;
|
||||
|
||||
case MqttUnsubAckMessage message:
|
||||
await this.InputMqttUnsubAckMessageAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
break;
|
||||
|
||||
case MqttDisconnectMessage message:
|
||||
await this.InputMqttDisconnectMessageAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理连接确认消息
|
||||
/// </summary>
|
||||
/// <param name="message">连接确认消息</param>
|
||||
/// <returns>任务</returns>
|
||||
protected abstract Task InputMqttConnAckMessageAsync(MqttConnAckMessage message);
|
||||
|
||||
/// <summary>
|
||||
/// 处理连接消息
|
||||
/// </summary>
|
||||
/// <param name="message">连接消息</param>
|
||||
/// <returns>任务</returns>
|
||||
protected abstract Task InputMqttConnectMessageAsync(MqttConnectMessage message);
|
||||
|
||||
/// <summary>
|
||||
/// 处理PING响应消息
|
||||
/// </summary>
|
||||
/// <param name="message">PING响应消息</param>
|
||||
/// <returns>任务</returns>
|
||||
protected abstract Task InputMqttPingRespMessageAsync(MqttPingRespMessage message);
|
||||
|
||||
/// <summary>
|
||||
/// 处理订阅消息
|
||||
/// </summary>
|
||||
/// <param name="message">订阅消息</param>
|
||||
/// <returns>任务</returns>
|
||||
protected abstract Task InputMqttSubscribeMessageAsync(MqttSubscribeMessage message);
|
||||
|
||||
/// <summary>
|
||||
/// 处理取消订阅消息
|
||||
/// </summary>
|
||||
/// <param name="message">取消订阅消息</param>
|
||||
/// <returns>任务</returns>
|
||||
protected abstract Task InputMqttUnsubscribeMessageAsync(MqttUnsubscribeMessage message);
|
||||
|
||||
/// <summary>
|
||||
/// 处理断开连接消息
|
||||
/// </summary>
|
||||
/// <param name="message">断开连接消息</param>
|
||||
/// <returns>任务</returns>
|
||||
private Task InputMqttDisconnectMessageAsync(MqttDisconnectMessage message)
|
||||
{
|
||||
return this.ProtectedMqttOnClosing(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理PING请求消息
|
||||
/// </summary>
|
||||
/// <param name="message">PING请求消息</param>
|
||||
/// <returns>任务</returns>
|
||||
private async Task InputMqttPingReqMessageAsync(MqttPingReqMessage message)
|
||||
{
|
||||
var contentForAck = new MqttPingRespMessage();
|
||||
await this.ProtectedOutputSendAsync(contentForAck).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理发布确认消息
|
||||
/// </summary>
|
||||
/// <param name="message">发布确认消息</param>
|
||||
/// <returns>任务</returns>
|
||||
private Task InputMqttPubAckMessageAsync(MqttPubAckMessage message)
|
||||
{
|
||||
this.m_waitHandlePool.SetRun(message);
|
||||
return EasyTask.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理发布完成消息
|
||||
/// </summary>
|
||||
/// <param name="message">发布完成消息</param>
|
||||
/// <returns>任务</returns>
|
||||
private Task InputMqttPubCompMessageAsync(MqttPubCompMessage message)
|
||||
{
|
||||
this.m_waitHandlePool.SetRun(message);
|
||||
return EasyTask.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理发布消息
|
||||
/// </summary>
|
||||
/// <param name="message">发布消息</param>
|
||||
/// <returns>任务</returns>
|
||||
private async Task InputMqttPublishMessageAsync(MqttPublishMessage message)
|
||||
{
|
||||
//Console.WriteLine($"TopicName:{message.TopicName}");
|
||||
//Console.WriteLine($"Payload:{Encoding.UTF8.GetString(message.Payload.ToArray())}");
|
||||
|
||||
if (message.QosLevel == QosLevel.AtMostOnce)
|
||||
{
|
||||
await this.PublishMessageArrivedAsync(new MqttArrivedMessage(message)).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
else if (message.QosLevel == QosLevel.AtLeastOnce)
|
||||
{
|
||||
await this.PublishMessageArrivedAsync(new MqttArrivedMessage(message)).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
var pubAckMessage = new MqttPubAckMessage()
|
||||
{
|
||||
MessageId = message.MessageId
|
||||
};
|
||||
await this.ProtectedOutputSendAsync(pubAckMessage).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
else if (message.QosLevel == QosLevel.ExactlyOnce)
|
||||
{
|
||||
this.m_qos2MqttArrivedMessage.Add(message.MessageId, new MqttArrivedMessage(message));
|
||||
var pubRecMessage = new MqttPubRecMessage()
|
||||
{
|
||||
MessageId = message.MessageId
|
||||
};
|
||||
await this.ProtectedOutputSendAsync(pubRecMessage).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理发布接收消息
|
||||
/// </summary>
|
||||
/// <param name="message">发布接收消息</param>
|
||||
/// <returns>任务</returns>
|
||||
private Task InputMqttPubRecMessageAsync(MqttPubRecMessage message)
|
||||
{
|
||||
this.m_waitHandlePool.SetRun(message);
|
||||
return EasyTask.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理发布释放消息
|
||||
/// </summary>
|
||||
/// <param name="message">发布释放消息</param>
|
||||
/// <returns>任务</returns>
|
||||
private async Task InputMqttPubRelMessageAsync(MqttPubRelMessage message)
|
||||
{
|
||||
if (this.m_qos2MqttArrivedMessage.TryGetValue(message.MessageId, out var mqttArrivedMessage))
|
||||
{
|
||||
this.m_qos2MqttArrivedMessage.Remove(message.MessageId);
|
||||
await this.PublishMessageArrivedAsync(mqttArrivedMessage).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
var pubCompMessage = new MqttPubCompMessage()
|
||||
{
|
||||
MessageId = message.MessageId
|
||||
};
|
||||
await this.ProtectedOutputSendAsync(pubCompMessage).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理订阅确认消息
|
||||
/// </summary>
|
||||
/// <param name="message">订阅确认消息</param>
|
||||
/// <returns>任务</returns>
|
||||
private Task InputMqttSubAckMessageAsync(MqttSubAckMessage message)
|
||||
{
|
||||
this.m_waitHandlePool.SetRun(message);
|
||||
return EasyTask.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理取消订阅确认消息
|
||||
/// </summary>
|
||||
/// <param name="message">取消订阅确认消息</param>
|
||||
/// <returns>任务</returns>
|
||||
private Task InputMqttUnsubAckMessageAsync(MqttUnsubAckMessage message)
|
||||
{
|
||||
this.m_waitHandlePool.SetRun(message);
|
||||
return EasyTask.CompletedTask;
|
||||
}
|
||||
|
||||
#endregion InputMqttMessage
|
||||
|
||||
#region 委托
|
||||
|
||||
public Func<MqttActor, MqttClosingEventArgs, Task> Closing { get; set; }
|
||||
public Func<MqttActor, MqttConnectedEventArgs, Task> Connected { get; set; }
|
||||
public Func<MqttActor, MqttConnectingEventArgs, Task> Connecting { get; set; }
|
||||
public Func<MqttActor, MqttReceivedEventArgs, Task> MessageArrived { get; set; }
|
||||
public Func<MqttActor, MqttMessage, Task> OutputSendAsync { get; set; }
|
||||
|
||||
#endregion 委托
|
||||
|
||||
#region 委托方法
|
||||
|
||||
protected async Task ProtectedMqttOnClosing(MqttDisconnectMessage message)
|
||||
{
|
||||
if (this.Closing != null)
|
||||
{
|
||||
await this.Closing.Invoke(this, new MqttClosingEventArgs(message)).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task ProtectedMqttOnConnected(object o)
|
||||
{
|
||||
var e = (MqttConnectedEventArgs)o;
|
||||
if (this.Connected != null)
|
||||
{
|
||||
await this.Connected.Invoke(this, e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task ProtectedMqttOnConnecting(MqttConnectingEventArgs e)
|
||||
{
|
||||
if (this.Connecting != null)
|
||||
{
|
||||
await this.Connecting.Invoke(this, e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
}
|
||||
|
||||
protected Task ProtectedOutputSendAsync(MqttMessage message)
|
||||
{
|
||||
return this.OutputSendAsync(this, message);
|
||||
}
|
||||
|
||||
#endregion 委托方法
|
||||
}
|
||||
143
src/TouchSocket.Mqtt/Actors/MqttClientActor.cs
Normal file
143
src/TouchSocket.Mqtt/Actors/MqttClientActor.cs
Normal file
@@ -0,0 +1,143 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
public class MqttClientActor : MqttActor
|
||||
{
|
||||
private readonly WaitDataAsync<MqttConnAckMessage> m_waitForConnect = new();
|
||||
private readonly WaitDataAsync<MqttPingRespMessage> m_waitForPing = new();
|
||||
|
||||
public async Task DisconnectAsync()
|
||||
{
|
||||
if (!this.Online)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
await this.ProtectedOutputSendAsync(new MqttDisconnectMessage()).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.Online = false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task PingAsync(int timeout, CancellationToken token)
|
||||
{
|
||||
var contentForAck = new MqttPingReqMessage();
|
||||
this.m_waitForPing.Reset();
|
||||
this.m_waitForPing.SetCancellationToken(token);
|
||||
await this.ProtectedOutputSendAsync(contentForAck);
|
||||
var waitDataStatus = await this.m_waitForPing.WaitAsync(timeout).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
waitDataStatus.ThrowIfNotRunning();
|
||||
}
|
||||
|
||||
#region 连接
|
||||
|
||||
public async Task<MqttConnAckMessage> ConnectAsync(MqttConnectMessage message, int millisecondsTimeout, CancellationToken token)
|
||||
{
|
||||
this.m_waitForConnect.Reset();
|
||||
this.m_waitForConnect.SetCancellationToken(token);
|
||||
|
||||
await this.ProtectedOutputSendAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
var waitDataStatus = await this.m_waitForConnect.WaitAsync(millisecondsTimeout);
|
||||
waitDataStatus.ThrowIfNotRunning();
|
||||
this.Online = true;
|
||||
return this.m_waitForConnect.WaitResult;
|
||||
}
|
||||
|
||||
#endregion 连接
|
||||
|
||||
#region 重写
|
||||
|
||||
protected override Task InputMqttConnAckMessageAsync(MqttConnAckMessage message)
|
||||
{
|
||||
this.m_waitForConnect.Set(message);
|
||||
return EasyTask.CompletedTask;
|
||||
}
|
||||
|
||||
protected override Task InputMqttConnectMessageAsync(MqttConnectMessage message)
|
||||
{
|
||||
throw ThrowHelper.CreateNotSupportedException($"遇到无法处理的数据报文,Message={message}");
|
||||
}
|
||||
|
||||
protected override Task InputMqttPingRespMessageAsync(MqttPingRespMessage message)
|
||||
{
|
||||
this.m_waitForPing.Set(message);
|
||||
return EasyTask.CompletedTask;
|
||||
}
|
||||
|
||||
protected override Task InputMqttSubscribeMessageAsync(MqttSubscribeMessage message)
|
||||
{
|
||||
throw ThrowHelper.CreateNotSupportedException($"遇到无法处理的数据报文,Message={message}");
|
||||
}
|
||||
|
||||
protected override Task InputMqttUnsubscribeMessageAsync(MqttUnsubscribeMessage message)
|
||||
{
|
||||
throw ThrowHelper.CreateNotSupportedException($"遇到无法处理的数据报文,Message={message}");
|
||||
}
|
||||
|
||||
#endregion 重写
|
||||
|
||||
public async Task<MqttSubAckMessage> SubscribeAsync(MqttSubscribeMessage message, int timeout = 5000, CancellationToken token = default)
|
||||
{
|
||||
var waitDataAsync = this.WaitHandlePool.GetWaitDataAsync(message);
|
||||
|
||||
try
|
||||
{
|
||||
waitDataAsync.SetCancellationToken(token);
|
||||
|
||||
await this.ProtectedOutputSendAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
|
||||
var waitDataStatus = await waitDataAsync.WaitAsync(timeout).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
waitDataStatus.ThrowIfNotRunning();
|
||||
var subAckMessage = (MqttSubAckMessage)waitDataAsync.WaitResult;
|
||||
return subAckMessage;
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.WaitHandlePool.Destroy(waitDataAsync);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task<MqttUnsubAckMessage> UnsubscribeAsync(MqttUnsubscribeMessage message, int timeout = 5000, CancellationToken token = default)
|
||||
{
|
||||
var waitDataAsync = this.WaitHandlePool.GetWaitDataAsync(message);
|
||||
|
||||
try
|
||||
{
|
||||
waitDataAsync.SetCancellationToken(token);
|
||||
|
||||
await this.ProtectedOutputSendAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
|
||||
var waitDataStatus = await waitDataAsync.WaitAsync(timeout).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
waitDataStatus.ThrowIfNotRunning();
|
||||
var subAckMessage = (MqttUnsubAckMessage)waitDataAsync.WaitResult;
|
||||
return subAckMessage;
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.WaitHandlePool.Destroy(waitDataAsync);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
252
src/TouchSocket.Mqtt/Actors/MqttSessionActor.cs
Normal file
252
src/TouchSocket.Mqtt/Actors/MqttSessionActor.cs
Normal file
@@ -0,0 +1,252 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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.Threading;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个 MQTT 会话的 Actor。
|
||||
/// </summary>
|
||||
public class MqttSessionActor : MqttActor
|
||||
{
|
||||
private readonly AsyncResetEvent m_asyncResetEvent = new(false, false);
|
||||
|
||||
private readonly Channel<DistributeMessage> m_mqttArrivedMessageQueue = Channel.CreateBounded<DistributeMessage>(new BoundedChannelOptions(1000)
|
||||
{
|
||||
FullMode = BoundedChannelFullMode.DropNewest,
|
||||
SingleReader = true,
|
||||
SingleWriter = false,
|
||||
AllowSynchronousContinuations = false,
|
||||
Capacity = 1000
|
||||
});
|
||||
|
||||
private readonly MqttBroker m_mqttBroker;
|
||||
private bool m_sessionPresent;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="MqttSessionActor"/> 类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="messageCenter">MQTT 消息中心。</param>
|
||||
public MqttSessionActor(MqttBroker messageCenter)
|
||||
{
|
||||
this.m_mqttBroker = messageCenter;
|
||||
Task.Run(this.WaitForReadAsync);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取会话是否存在。
|
||||
/// </summary>
|
||||
public bool SessionPresent => this.m_sessionPresent;
|
||||
|
||||
/// <summary>
|
||||
/// 激活会话。
|
||||
/// </summary>
|
||||
public void Activate()
|
||||
{
|
||||
this.m_asyncResetEvent.Set();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取消激活会话。
|
||||
/// </summary>
|
||||
public void Deactivate()
|
||||
{
|
||||
this.m_asyncResetEvent.Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步分发消息。
|
||||
/// </summary>
|
||||
/// <param name="message">要分发的消息。</param>
|
||||
public async Task DistributeMessagesAsync(DistributeMessage message)
|
||||
{
|
||||
var tokenClosed = this.TokenSource.Token;
|
||||
if (tokenClosed.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await this.m_mqttArrivedMessageQueue.Writer.WriteAsync(message, tokenClosed).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Console.WriteLine("DistributeMessagesAsync:" + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置会话存在标志为 true。
|
||||
/// </summary>
|
||||
public void MakeSessionPresent()
|
||||
{
|
||||
this.m_sessionPresent = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
this.m_asyncResetEvent.Dispose();
|
||||
//this.m_mqttArrivedMessageQueue.Reader.();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region 属性
|
||||
|
||||
/// <summary>
|
||||
/// 获取 MQTT 消息中心。
|
||||
/// </summary>
|
||||
public MqttBroker MqttBroker => this.m_mqttBroker;
|
||||
|
||||
#endregion 属性
|
||||
|
||||
protected override Task InputMqttConnAckMessageAsync(MqttConnAckMessage message)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override async Task InputMqttConnectMessageAsync(MqttConnectMessage message)
|
||||
{
|
||||
var mqttConnAckMessage = new MqttConnAckMessage(this.m_sessionPresent)
|
||||
{
|
||||
ReturnCode = MqttReasonCode.ConnectionAccepted,
|
||||
AssignedClientIdentifier = message.ClientId,
|
||||
AuthenticationData = message.AuthenticationData,
|
||||
AuthenticationMethod = message.AuthenticationMethod,
|
||||
MaximumPacketSize = message.MaximumPacketSize,
|
||||
ReceiveMaximum = message.ReceiveMaximum,
|
||||
SessionExpiryInterval = message.SessionExpiryInterval,
|
||||
ReasonCode = MqttReasonCode.Success,
|
||||
ReasonString = "Success",
|
||||
ResponseInformation = "Success"
|
||||
};
|
||||
|
||||
var e = new MqttConnectingEventArgs(message, mqttConnAckMessage);
|
||||
await this.ProtectedMqttOnConnecting(e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
|
||||
if (mqttConnAckMessage.ReturnCode != MqttReasonCode.ConnectionAccepted)
|
||||
{
|
||||
await this.ProtectedOutputSendAsync(mqttConnAckMessage).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
return;
|
||||
}
|
||||
|
||||
this.Id = message.ClientId;
|
||||
|
||||
await this.ProtectedOutputSendAsync(mqttConnAckMessage).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
|
||||
this.Online = true;
|
||||
|
||||
_ = EasyTask.Run(this.ProtectedMqttOnConnected, new MqttConnectedEventArgs(message, mqttConnAckMessage));
|
||||
}
|
||||
|
||||
protected override Task InputMqttPingRespMessageAsync(MqttPingRespMessage message)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override async Task InputMqttSubscribeMessageAsync(MqttSubscribeMessage message)
|
||||
{
|
||||
var contentForAck = new MqttSubAckMessage()
|
||||
{
|
||||
MessageId = message.MessageId
|
||||
};
|
||||
foreach (var item in message.SubscribeRequests)
|
||||
{
|
||||
this.m_mqttBroker.RegisterActor(this.Id, item.Topic, item.QosLevel);
|
||||
contentForAck.AddReturnCode(item.QosLevel);
|
||||
}
|
||||
await this.ProtectedOutputSendAsync(contentForAck).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
protected override async Task InputMqttUnsubscribeMessageAsync(MqttUnsubscribeMessage message)
|
||||
{
|
||||
foreach (var topic in message.TopicFilters)
|
||||
{
|
||||
this.m_mqttBroker.UnregisterActor(this.Id, topic);
|
||||
}
|
||||
var contentForAck = new MqttUnsubAckMessage()
|
||||
{
|
||||
MessageId = message.MessageId
|
||||
};
|
||||
await this.ProtectedOutputSendAsync(contentForAck).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
protected override async Task PublishMessageArrivedAsync(MqttArrivedMessage message)
|
||||
{
|
||||
await this.m_mqttBroker.ForwardMessageAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
await base.PublishMessageArrivedAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
private async Task PublishDistributeMessageAsync(DistributeMessage distributeMessage)
|
||||
{
|
||||
var token = this.TokenSource.Token;
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var message = distributeMessage.Message;
|
||||
var qosLevel = distributeMessage.QosLevel;
|
||||
|
||||
var publishMessage = new MqttPublishMessage(message.TopicName, message.Retain, qosLevel, message.Payload);
|
||||
|
||||
await this.m_asyncResetEvent.WaitOneAsync(token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
|
||||
try
|
||||
{
|
||||
await this.PublishAsync(publishMessage).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("PublishDistributeMessageAsync:"+ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task WaitForReadAsync()
|
||||
{
|
||||
var token = this.TokenSource.Token;
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
Console.WriteLine("WaitForReadAsync IsCancellationRequested");
|
||||
return;
|
||||
}
|
||||
|
||||
var distributeMessage = await this.m_mqttArrivedMessageQueue.Reader.ReadAsync(token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
if (distributeMessage is null)
|
||||
{
|
||||
Console.WriteLine("WaitForReadAsync distributeMessage is null");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
await this.PublishDistributeMessageAsync(distributeMessage).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
91
src/TouchSocket.Mqtt/Adapter/MqttAdapter.cs
Normal file
91
src/TouchSocket.Mqtt/Adapter/MqttAdapter.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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.Threading.Tasks;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
internal class MqttAdapter : CustomDataHandlingAdapter<MqttMessage>
|
||||
{
|
||||
public override bool CanSendRequestInfo => true;
|
||||
|
||||
public MqttProtocolVersion Version { get; private set; } = MqttProtocolVersion.Unknown;
|
||||
|
||||
protected override FilterResult Filter<TByteBlock>(ref TByteBlock byteBlock, bool beCached, ref MqttMessage request, ref int tempCapacity)
|
||||
{
|
||||
if (byteBlock.CanReadLength < 2)
|
||||
{
|
||||
return FilterResult.Cache;
|
||||
}
|
||||
var position = byteBlock.Position;
|
||||
var firstByte = byteBlock.ReadByte();
|
||||
var mqttControlPacketType = (MqttMessageType)firstByte.GetHeight4();
|
||||
//var fixHeaderFlags = new FixHeaderFlags(firstByte);
|
||||
|
||||
var remainingLength = MqttExtension.ReadVariableByteInteger(ref byteBlock);
|
||||
if (byteBlock.CanReadLength < remainingLength)
|
||||
{
|
||||
byteBlock.Position = position;
|
||||
return FilterResult.Cache;
|
||||
}
|
||||
|
||||
var mqttMessage = MqttMessage.CreateMqttMessage(mqttControlPacketType);
|
||||
|
||||
//Console.WriteLine("Rev:"+ mqttMessage.MessageType);
|
||||
|
||||
if (mqttMessage is not MqttConnectMessage)
|
||||
{
|
||||
mqttMessage.InternalSetVersion(this.Version);
|
||||
}
|
||||
|
||||
byteBlock.Position = position;
|
||||
mqttMessage.Unpack(ref byteBlock);
|
||||
if (byteBlock.Position != position + remainingLength + 2)
|
||||
{
|
||||
throw new Exception("存在没有读取的数据");
|
||||
}
|
||||
|
||||
if (mqttMessage is MqttConnectMessage connectMessage)
|
||||
{
|
||||
this.Version = connectMessage.Version;
|
||||
}
|
||||
|
||||
request = mqttMessage;
|
||||
return FilterResult.Success;
|
||||
}
|
||||
|
||||
protected override async Task PreviewSendAsync(IRequestInfo requestInfo)
|
||||
{
|
||||
if (requestInfo is MqttMessage mqttMessage)
|
||||
{
|
||||
//Console.WriteLine("Send:" + mqttMessage.MessageType);
|
||||
|
||||
if (mqttMessage.MessageType == MqttMessageType.Connect)
|
||||
{
|
||||
this.Version = ((MqttConnectMessage)mqttMessage).Version;
|
||||
}
|
||||
var byteBlock = new ValueByteBlock(mqttMessage.MaxLength);
|
||||
try
|
||||
{
|
||||
mqttMessage.InternalSetVersion(this.Version);
|
||||
mqttMessage.Build(ref byteBlock);
|
||||
await this.GoSendAsync(byteBlock.Memory).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
finally
|
||||
{
|
||||
byteBlock.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
src/TouchSocket.Mqtt/Broker/DistributeMessage.cs
Normal file
40
src/TouchSocket.Mqtt/Broker/DistributeMessage.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个分发消息的类。
|
||||
/// </summary>
|
||||
public sealed class DistributeMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="DistributeMessage"/> 类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="message">MQTT 到达的消息。</param>
|
||||
/// <param name="qosLevel">服务质量级别。</param>
|
||||
public DistributeMessage(MqttArrivedMessage message, QosLevel qosLevel)
|
||||
{
|
||||
this.Message = message;
|
||||
this.QosLevel = qosLevel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取 MQTT 到达的消息。
|
||||
/// </summary>
|
||||
public MqttArrivedMessage Message { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取服务质量级别。
|
||||
/// </summary>
|
||||
public QosLevel QosLevel { get; }
|
||||
}
|
||||
197
src/TouchSocket.Mqtt/Broker/MqttBroker.cs
Normal file
197
src/TouchSocket.Mqtt/Broker/MqttBroker.cs
Normal file
@@ -0,0 +1,197 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个MQTT代理。
|
||||
/// </summary>
|
||||
public class MqttBroker
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, MqttSessionActor> m_sessionActors = new();
|
||||
|
||||
private readonly Dictionary<string, HashSet<Subscription>> m_topicToActors = new();
|
||||
|
||||
/// <summary>
|
||||
/// 异步转发消息。
|
||||
/// </summary>
|
||||
/// <param name="message">到达的MQTT消息。</param>
|
||||
public async Task ForwardMessageAsync(MqttArrivedMessage message)
|
||||
{
|
||||
var topic = message.TopicName;
|
||||
if (this.m_topicToActors.TryGetValue(topic, out var actors))
|
||||
{
|
||||
foreach (var subscription in actors)
|
||||
{
|
||||
var actorId = subscription.ClientId;
|
||||
var actorTarget = this.FindMqttActor(actorId);
|
||||
if (actorTarget == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var qosLevel = MqttExtension.MinQosLevel(subscription.QosLevel, message.QosLevel);
|
||||
await actorTarget.DistributeMessagesAsync(new DistributeMessage(message, qosLevel)).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取或创建MQTT会话参与者。
|
||||
/// </summary>
|
||||
/// <param name="clientId">客户端ID。</param>
|
||||
/// <returns>MQTT会话参与者。</returns>
|
||||
public MqttSessionActor GetOrCreateMqttSessionActor(string clientId)
|
||||
{
|
||||
if (this.m_sessionActors.TryGetValue(clientId, out var actor))
|
||||
{
|
||||
actor.MakeSessionPresent();
|
||||
return actor;
|
||||
}
|
||||
else
|
||||
{
|
||||
var newActor = new MqttSessionActor(this);
|
||||
if (this.m_sessionActors.TryAdd(clientId, newActor))
|
||||
{
|
||||
return newActor;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.GetOrCreateMqttSessionActor(clientId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册参与者。
|
||||
/// </summary>
|
||||
/// <param name="clientId">客户端ID。</param>
|
||||
/// <param name="topic">主题。</param>
|
||||
/// <param name="qosLevel">服务质量等级。</param>
|
||||
public void RegisterActor(string clientId, string topic, QosLevel qosLevel)
|
||||
{
|
||||
var subscription = new Subscription(clientId, qosLevel);
|
||||
if (this.m_topicToActors.TryGetValue(topic, out var actors))
|
||||
{
|
||||
actors.Remove(subscription);
|
||||
actors.Add(subscription);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.m_topicToActors.Add(topic, new HashSet<Subscription> { subscription });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除MQTT会话参与者。
|
||||
/// </summary>
|
||||
/// <param name="clientId">客户端ID。</param>
|
||||
/// <returns>是否成功移除。</returns>
|
||||
public bool RemoveMqttSessionActor(string clientId)
|
||||
{
|
||||
if (this.m_sessionActors.TryRemove(clientId, out var mqttSessionActor))
|
||||
{
|
||||
mqttSessionActor.Dispose();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除MQTT会话参与者。
|
||||
/// </summary>
|
||||
/// <param name="m_mqttActor">MQTT会话参与者。</param>
|
||||
/// <returns>是否成功移除。</returns>
|
||||
public bool RemoveMqttSessionActor(MqttSessionActor m_mqttActor)
|
||||
{
|
||||
if (this.m_sessionActors.TryRemove(m_mqttActor.Id, out var mqttSessionActor))
|
||||
{
|
||||
mqttSessionActor.Dispose();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注销参与者。
|
||||
/// </summary>
|
||||
/// <param name="clientId">客户端ID。</param>
|
||||
/// <param name="topic">主题。</param>
|
||||
/// <returns>是否成功注销。</returns>
|
||||
public bool UnregisterActor(string clientId, string topic)
|
||||
{
|
||||
if (this.m_topicToActors.TryGetValue(topic, out var subscriptions))
|
||||
{
|
||||
return subscriptions.Remove(new Subscription(clientId));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private MqttSessionActor FindMqttActor(string actorId)
|
||||
{
|
||||
if (this.m_sessionActors.TryGetValue(actorId, out var actor))
|
||||
{
|
||||
return actor;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
#region Class
|
||||
|
||||
private readonly struct Subscription : IEquatable<Subscription>
|
||||
{
|
||||
public Subscription(string clientId, QosLevel qosLevel) : this(clientId)
|
||||
{
|
||||
this.QosLevel = qosLevel;
|
||||
}
|
||||
|
||||
public Subscription(string clientId)
|
||||
{
|
||||
this.ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
|
||||
}
|
||||
|
||||
public string ClientId { get; }
|
||||
public QosLevel QosLevel { get; }
|
||||
|
||||
public static bool operator !=(Subscription left, Subscription right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
public static bool operator ==(Subscription left, Subscription right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public bool Equals(Subscription other)
|
||||
{
|
||||
return this.ClientId.Equals(other.ClientId);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is Subscription subscription && this.Equals(subscription);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return this.ClientId.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Class
|
||||
}
|
||||
53
src/TouchSocket.Mqtt/Common/MqttArrivedMessage.cs
Normal file
53
src/TouchSocket.Mqtt/Common/MqttArrivedMessage.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个到达的 MQTT 消息。
|
||||
/// </summary>
|
||||
public sealed class MqttArrivedMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="MqttArrivedMessage"/> 类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="message">MQTT 发布消息。</param>
|
||||
public MqttArrivedMessage(MqttPublishMessage message)
|
||||
{
|
||||
this.QosLevel = message.QosLevel;
|
||||
this.Retain = message.Retain;
|
||||
this.TopicName = message.TopicName;
|
||||
this.Payload = new ReadOnlyMemory<byte>(message.Payload.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取消息的服务质量级别。
|
||||
/// </summary>
|
||||
public QosLevel QosLevel { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取消息的有效负载。
|
||||
/// </summary>
|
||||
public ReadOnlyMemory<byte> Payload { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示消息是否被保留。
|
||||
/// </summary>
|
||||
public bool Retain { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取消息的主题名称。
|
||||
/// </summary>
|
||||
public string TopicName { get; }
|
||||
}
|
||||
24
src/TouchSocket.Mqtt/Common/MqttSessionActorResult.cs
Normal file
24
src/TouchSocket.Mqtt/Common/MqttSessionActorResult.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
public struct MqttSessionActorResult
|
||||
{
|
||||
public MqttSessionActorResult(MqttSessionActor sessionActor, bool isNew)
|
||||
{
|
||||
this.SessionActor = sessionActor;
|
||||
this.IsNew = isNew;
|
||||
}
|
||||
public MqttSessionActor SessionActor { get; }
|
||||
public bool IsNew { get; }
|
||||
}
|
||||
@@ -5,29 +5,21 @@
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
//namespace TouchSocket.Core
|
||||
//{
|
||||
// /// <summary>
|
||||
// /// 使用源生成插件的调用。
|
||||
// /// </summary>
|
||||
// internal class GeneratorPluginAttribute:Attribute
|
||||
// {
|
||||
// public string PluginName { get; set; }
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
// /// <summary>
|
||||
// /// 使用源生成插件的调用。
|
||||
// /// </summary>
|
||||
// /// <param name="pluginName">插件名称,一般建议使用nameof()解决。</param>
|
||||
// public GeneratorPluginAttribute(string pluginName)
|
||||
// {
|
||||
// this.PluginName = pluginName;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
public static class MqttUtility
|
||||
{
|
||||
public const string MqttProtocolName = "MQTT";
|
||||
public static ReadOnlySpan<byte> MqttProtocolNameSpan => Encoding.UTF8.GetBytes("MQTT");
|
||||
|
||||
public static IReadOnlyList<MqttUserProperty> EmptyUserProperties { get; } = [];
|
||||
}
|
||||
81
src/TouchSocket.Mqtt/Common/SubscribeRequest.cs
Normal file
81
src/TouchSocket.Mqtt/Common/SubscribeRequest.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 TouchSocket.Mqtt;
|
||||
|
||||
public readonly record struct SubscribeRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="SubscribeRequest"/> 类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="topic">订阅的主题。</param>
|
||||
/// <param name="qosLevel">服务质量等级。</param>
|
||||
public SubscribeRequest(string topic, QosLevel qosLevel)
|
||||
{
|
||||
this.Topic = topic;
|
||||
this.m_options.SetQosLevel(0, qosLevel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="SubscribeRequest"/> 类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="topic">订阅的主题。</param>
|
||||
/// <param name="options">选项字节。</param>
|
||||
public SubscribeRequest(string topic, byte options)
|
||||
{
|
||||
this.Topic = topic;
|
||||
this.m_options = options;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="SubscribeRequest"/> 类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="topic">订阅的主题。</param>
|
||||
/// <param name="qosLevel">服务质量等级。</param>
|
||||
/// <param name="noLocal">是否不接收本地发布的消息。</param>
|
||||
/// <param name="retainAsPublished">是否保留消息的发布状态。</param>
|
||||
/// <param name="retainHandling">保留消息的处理方式。</param>
|
||||
public SubscribeRequest(string topic, QosLevel qosLevel, bool noLocal, bool retainAsPublished, MqttRetainHandling retainHandling) : this(topic, qosLevel)
|
||||
{
|
||||
this.m_options.SetBit(2, noLocal);
|
||||
this.m_options.SetBit(3, retainAsPublished);
|
||||
this.m_options.SetRetainHandling(4, retainHandling);
|
||||
}
|
||||
|
||||
private readonly byte m_options;
|
||||
|
||||
/// <summary>
|
||||
/// 获取服务质量等级。
|
||||
/// </summary>
|
||||
public QosLevel QosLevel => this.m_options.GetQosLevel(0);
|
||||
/// <summary>
|
||||
/// 获取订阅的主题。
|
||||
/// </summary>
|
||||
public string Topic { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取是否不接收本地发布的消息。
|
||||
/// </summary>
|
||||
public bool NoLocal => this.m_options.GetBit(2);
|
||||
|
||||
/// <summary>
|
||||
/// 获取是否保留消息的发布状态。
|
||||
/// </summary>
|
||||
public bool RetainAsPublished => this.m_options.GetBit(3);
|
||||
|
||||
/// <summary>
|
||||
/// 获取保留消息的处理方式。
|
||||
/// </summary>
|
||||
public MqttRetainHandling RetainHandling => this.m_options.GetRetainHandling(4);
|
||||
}
|
||||
69
src/TouchSocket.Mqtt/Common/VariableByteIntegerRecorder.cs
Normal file
69
src/TouchSocket.Mqtt/Common/VariableByteIntegerRecorder.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 TouchSocket.Core;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
internal ref struct VariableByteIntegerRecorder
|
||||
{
|
||||
private int m_dataPosition;
|
||||
private int m_minimumCount;
|
||||
private int m_startPosition;
|
||||
|
||||
public void CheckOut<TByteBlock>(ref TByteBlock byteBlock, int minimum = 0)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
this.m_minimumCount = MqttExtension.GetVariableByteIntegerCount(minimum);
|
||||
this.m_startPosition = byteBlock.Position;
|
||||
byteBlock.Position += this.m_minimumCount;
|
||||
this.m_dataPosition = byteBlock.Position;
|
||||
}
|
||||
|
||||
public readonly int CheckIn<TByteBlock>(ref TByteBlock byteBlock)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
var endPosition = byteBlock.Position;
|
||||
|
||||
var len = endPosition - this.m_dataPosition;
|
||||
var lenCount = MqttExtension.GetVariableByteIntegerCount(len);
|
||||
if (lenCount > this.m_minimumCount)
|
||||
{
|
||||
var moveCount = lenCount - this.m_minimumCount;
|
||||
byteBlock.ExtendSize(moveCount);
|
||||
var span = byteBlock.TotalMemory.Span.Slice(this.m_dataPosition);
|
||||
ShiftWithRight(span, moveCount);
|
||||
|
||||
byteBlock.Position = this.m_startPosition;
|
||||
MqttExtension.WriteVariableByteInteger(ref byteBlock, (uint)len);
|
||||
byteBlock.SetLength(endPosition);
|
||||
byteBlock.SeekToEnd();
|
||||
}
|
||||
else
|
||||
{
|
||||
byteBlock.Position = this.m_startPosition;
|
||||
MqttExtension.WriteVariableByteInteger(ref byteBlock, (uint)len);
|
||||
byteBlock.SeekToEnd();
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
private static void ShiftWithRight(Span<byte> span, int shiftCount)
|
||||
{
|
||||
var length = span.Length;
|
||||
for (var i = length - 1; i >= shiftCount; i--)
|
||||
{
|
||||
span[i] = span[i - shiftCount];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个MQTT客户端接口。
|
||||
/// </summary>
|
||||
public interface IMqttClient : IMqttSession, IConnectableClient
|
||||
{
|
||||
Task PingAsync(int timeout = 5000, CancellationToken token = default);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个MQTT会话接口。
|
||||
/// </summary>
|
||||
public interface IMqttSession : IDependencyClient, IResolverConfigObject, IOnlineClient, IClosableClient
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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
|
||||
// 感谢您的下载和使用
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个 MQTT 会话客户端接口。
|
||||
/// </summary>
|
||||
public interface IMqttSessionClient : IMqttSession
|
||||
{
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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.Sockets;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个 MQTT TCP 客户端接口。
|
||||
/// </summary>
|
||||
public interface IMqttTcpClient : IMqttClient, ITcpSession
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
public interface IMqttTcpService : ITcpServiceBase<MqttTcpSessionClient>
|
||||
{
|
||||
MqttBroker MqttBroker { get; }
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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.Sockets;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个MQTT TCP会话客户端接口。
|
||||
/// </summary>
|
||||
public interface IMqttTcpSessionClient : IMqttSessionClient, ITcpSession, ITcpListenableClient, IIdClient
|
||||
{
|
||||
}
|
||||
|
||||
160
src/TouchSocket.Mqtt/Components/Tcp/MqttTcpClient.cs
Normal file
160
src/TouchSocket.Mqtt/Components/Tcp/MqttTcpClient.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
public class MqttTcpClient : TcpClientBase, IMqttTcpClient
|
||||
{
|
||||
private readonly MqttClientActor m_mqttActor;
|
||||
|
||||
public MqttTcpClient()
|
||||
{
|
||||
var actor = new MqttClientActor
|
||||
{
|
||||
OutputSendAsync = this.PrivateMqttOnSend,
|
||||
Connecting = this.PrivateMqttOnConnecting,
|
||||
Connected = this.PrivateMqttOnConnected,
|
||||
Closing = this.PrivateMqttOnClosing,
|
||||
MessageArrived = this.PrivateMqttOnMessageArrived
|
||||
};
|
||||
this.m_mqttActor = actor;
|
||||
}
|
||||
|
||||
#region MqttActor
|
||||
|
||||
private async Task PrivateMqttOnClosing(MqttActor actor, MqttClosingEventArgs e)
|
||||
{
|
||||
await this.PluginManager.RaiseAsync(typeof(IMqttClosingPlugin), this.Resolver, this, e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
private async Task PrivateMqttOnConnected(MqttActor mqttActor, MqttConnectedEventArgs e)
|
||||
{
|
||||
await this.PluginManager.RaiseAsync(typeof(IMqttConnectedPlugin), this.Resolver, this, e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
private async Task PrivateMqttOnConnecting(MqttActor mqttActor, MqttConnectingEventArgs e)
|
||||
{
|
||||
await this.PluginManager.RaiseAsync(typeof(IMqttConnectingPlugin), this.Resolver, this, e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
private async Task PrivateMqttOnMessageArrived(MqttActor actor, MqttReceivedEventArgs e)
|
||||
{
|
||||
await this.PluginManager.RaiseAsync(typeof(IMqttReceivedPlugin), this.Resolver, this, e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
private Task PrivateMqttOnSend(MqttActor mqttActor, MqttMessage message)
|
||||
{
|
||||
return base.ProtectedSendAsync(message);
|
||||
}
|
||||
|
||||
#endregion MqttActor
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Online => base.Online && this.m_mqttActor.Online;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<Result> CloseAsync(string msg, CancellationToken token = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (this.Online)
|
||||
{
|
||||
await this.m_mqttActor.DisconnectAsync().ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
await base.CloseAsync(msg,token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
return Result.FromException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task ConnectAsync(int millisecondsTimeout, CancellationToken token)
|
||||
{
|
||||
if (this.Online)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var mqttConnectOptions = this.Config.GetValue(MqttConfigExtension.MqttConnectOptionsProperty);
|
||||
ThrowHelper.ThrowArgumentNullExceptionIf(mqttConnectOptions, nameof(mqttConnectOptions));
|
||||
|
||||
var connectMessage = new MqttConnectMessage(mqttConnectOptions);
|
||||
|
||||
await this.PluginManager.RaiseAsync(typeof(IMqttConnectingPlugin), this.Resolver, this, new MqttConnectingEventArgs(connectMessage, default));
|
||||
|
||||
await base.TcpConnectAsync(millisecondsTimeout, token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
|
||||
var connAckMessage = await this.m_mqttActor.ConnectAsync(connectMessage, millisecondsTimeout, token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
|
||||
await this.PluginManager.RaiseAsync(typeof(IMqttConnectedPlugin), this.Resolver, this, new MqttConnectedEventArgs(connectMessage, connAckMessage)).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task PingAsync(int timeout = 5000, CancellationToken token = default)
|
||||
{
|
||||
await this.m_mqttActor.PingAsync(timeout, token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task PublishAsync(MqttPublishMessage mqttMessage)
|
||||
{
|
||||
await this.m_mqttActor.PublishAsync(mqttMessage).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<MqttSubAckMessage> SubscribeAsync(MqttSubscribeMessage message)
|
||||
{
|
||||
//订阅
|
||||
return await this.m_mqttActor.SubscribeAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<MqttUnsubAckMessage> UnsubscribeAsync(MqttUnsubscribeMessage message)
|
||||
{
|
||||
return await this.m_mqttActor.UnsubscribeAsync(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OnTcpClosed(ClosedEventArgs e)
|
||||
{
|
||||
await this.PluginManager.RaiseAsync(typeof(IMqttClosedPlugin), this.Resolver, this, new MqttClosedEventArgs(e.Manual, e.Message)).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
await base.OnTcpClosed(e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OnTcpConnecting(ConnectingEventArgs e)
|
||||
{
|
||||
await base.OnTcpConnecting(e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
this.SetAdapter(new MqttAdapter());
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OnTcpReceived(ReceivedDataEventArgs e)
|
||||
{
|
||||
if (e.RequestInfo is MqttMessage mqttMessage)
|
||||
{
|
||||
await this.PluginManager.RaiseAsync(typeof(IMqttReceivingPlugin), this.Resolver, this, new MqttReceivingEventArgs(mqttMessage)).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
|
||||
await this.m_mqttActor.InputMqttMessageAsync(mqttMessage).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
await base.OnTcpReceived(e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
}
|
||||
27
src/TouchSocket.Mqtt/Components/Tcp/MqttTcpService.cs
Normal file
27
src/TouchSocket.Mqtt/Components/Tcp/MqttTcpService.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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.Sockets;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
public class MqttTcpService : TcpServiceBase<MqttTcpSessionClient>, IMqttTcpService
|
||||
{
|
||||
private readonly MqttBroker m_mqttBroker = new();
|
||||
|
||||
public MqttBroker MqttBroker => this.m_mqttBroker;
|
||||
|
||||
protected override MqttTcpSessionClient NewClient()
|
||||
{
|
||||
return new MqttTcpSessionClient(this.m_mqttBroker);
|
||||
}
|
||||
}
|
||||
129
src/TouchSocket.Mqtt/Components/Tcp/MqttTcpSessionClient.cs
Normal file
129
src/TouchSocket.Mqtt/Components/Tcp/MqttTcpSessionClient.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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.Threading.Tasks;
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
public class MqttTcpSessionClient : TcpSessionClientBase, IMqttTcpSessionClient
|
||||
{
|
||||
private readonly MqttBroker m_mqttBroker;
|
||||
private MqttSessionActor m_mqttActor;
|
||||
|
||||
public MqttTcpSessionClient(MqttBroker mqttBroker)
|
||||
{
|
||||
this.m_mqttBroker = mqttBroker;
|
||||
}
|
||||
|
||||
#region MqttActor
|
||||
|
||||
private async Task PrivateMqttOnClosing(MqttActor actor, MqttClosingEventArgs e)
|
||||
{
|
||||
await this.PluginManager.RaiseAsync(typeof(IMqttClosingPlugin), this.Resolver, this, e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
private async Task PrivateMqttOnConnected(MqttActor mqttActor, MqttConnectedEventArgs args)
|
||||
{
|
||||
await this.PluginManager.RaiseAsync(typeof(IMqttConnectedPlugin), this.Resolver, this, args).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
private async Task PrivateMqttOnConnecting(MqttActor mqttActor, MqttConnectingEventArgs e)
|
||||
{
|
||||
if (e.ConnAckMessage.ReturnCode == MqttReasonCode.ConnectionAccepted)
|
||||
{
|
||||
await this.ResetIdAsync(e.ConnectMessage.ClientId).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
await this.PluginManager.RaiseAsync(typeof(IMqttConnectingPlugin), this.Resolver, this, e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
private async Task PrivateMqttOnMessageArrived(MqttActor actor, MqttReceivedEventArgs e)
|
||||
{
|
||||
await this.PluginManager.RaiseAsync(typeof(IMqttReceivedPlugin), this.Resolver, this, e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
private Task PrivateMqttOnSend(MqttActor mqttActor, MqttMessage message)
|
||||
{
|
||||
return base.ProtectedSendAsync(message);
|
||||
}
|
||||
|
||||
#endregion MqttActor
|
||||
|
||||
public bool CleanSession { get; private set; }
|
||||
public MqttSessionActor MqttActor => this.m_mqttActor;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OnTcpClosed(ClosedEventArgs e)
|
||||
{
|
||||
var mqttActor = this.m_mqttActor;
|
||||
if (mqttActor is not null)
|
||||
{
|
||||
mqttActor.OutputSendAsync = null;
|
||||
mqttActor.Connecting = null;
|
||||
mqttActor.Connected = null;
|
||||
mqttActor.MessageArrived = null;
|
||||
mqttActor.Closing = null;
|
||||
if (this.CleanSession)
|
||||
{
|
||||
this.m_mqttBroker.RemoveMqttSessionActor(mqttActor);
|
||||
}
|
||||
else
|
||||
{
|
||||
mqttActor.Deactivate();
|
||||
}
|
||||
|
||||
await this.PluginManager.RaiseAsync(typeof(IMqttClosedPlugin), this.Resolver, this, new MqttClosedEventArgs(e.Manual, e.Message)).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
await base.OnTcpClosed(e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OnTcpConnecting(ConnectingEventArgs e)
|
||||
{
|
||||
await base.OnTcpConnecting(e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
base.SetAdapter(new MqttAdapter());
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OnTcpReceived(ReceivedDataEventArgs e)
|
||||
{
|
||||
if (e.RequestInfo is MqttMessage mqttMessage)
|
||||
{
|
||||
if (this.m_mqttActor is null)
|
||||
{
|
||||
if (mqttMessage is not MqttConnectMessage mqttConnectMessage)
|
||||
{
|
||||
ThrowHelper.ThrowInvalidOperationException("在未初始化时,必须先进行初始化");
|
||||
return;
|
||||
}
|
||||
|
||||
this.CleanSession = mqttConnectMessage.CleanSession;
|
||||
var actor = this.m_mqttBroker.GetOrCreateMqttSessionActor(mqttConnectMessage.ClientId);
|
||||
|
||||
actor.OutputSendAsync = this.PrivateMqttOnSend;
|
||||
actor.Connecting = this.PrivateMqttOnConnecting;
|
||||
actor.Connected = this.PrivateMqttOnConnected;
|
||||
actor.MessageArrived = this.PrivateMqttOnMessageArrived;
|
||||
actor.Closing = this.PrivateMqttOnClosing;
|
||||
actor.Activate();
|
||||
this.m_mqttActor = actor;
|
||||
}
|
||||
|
||||
await this.PluginManager.RaiseAsync(typeof(IMqttReceivingPlugin), this.Resolver, this, new MqttReceivingEventArgs(mqttMessage)).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
|
||||
await this.m_mqttActor.InputMqttMessageAsync(mqttMessage).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
await base.OnTcpReceived(e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
}
|
||||
99
src/TouchSocket.Mqtt/Enums/MqttMessageType.cs
Normal file
99
src/TouchSocket.Mqtt/Enums/MqttMessageType.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示 MQTT 协议中的控制报文类型。
|
||||
/// </summary>
|
||||
public enum MqttMessageType
|
||||
{
|
||||
/// <summary>
|
||||
/// 0 - 禁止使用,保留。
|
||||
/// </summary>
|
||||
Reserved = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 1 - 客户端到服务端:客户端请求连接服务端。
|
||||
/// </summary>
|
||||
Connect = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 2 - 服务端到客户端:连接报文确认。
|
||||
/// </summary>
|
||||
ConnAck = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 3 - 双向:发布消息。
|
||||
/// </summary>
|
||||
Publish = 3,
|
||||
|
||||
/// <summary>
|
||||
/// 4 - 双向:QoS 1 消息发布收到确认。
|
||||
/// </summary>
|
||||
PubAck = 4,
|
||||
|
||||
/// <summary>
|
||||
/// 5 - 双向:发布收到(保证交付第一步)。
|
||||
/// </summary>
|
||||
PubRec = 5,
|
||||
|
||||
/// <summary>
|
||||
/// 6 - 双向:发布释放(保证交付第二步)。
|
||||
/// </summary>
|
||||
PubRel = 6,
|
||||
|
||||
/// <summary>
|
||||
/// 7 - 双向:QoS 2 消息发布完成(保证交互第三步)。
|
||||
/// </summary>
|
||||
PubComp = 7,
|
||||
|
||||
/// <summary>
|
||||
/// 8 - 客户端到服务端:客户端订阅请求。
|
||||
/// </summary>
|
||||
Subscribe = 8,
|
||||
|
||||
/// <summary>
|
||||
/// 9 - 服务端到客户端:订阅请求报文确认。
|
||||
/// </summary>
|
||||
SubAck = 9,
|
||||
|
||||
/// <summary>
|
||||
/// 10 - 客户端到服务端:客户端取消订阅请求。
|
||||
/// </summary>
|
||||
Unsubscribe = 10,
|
||||
|
||||
/// <summary>
|
||||
/// 11 - 服务端到客户端:取消订阅报文确认。
|
||||
/// </summary>
|
||||
UnsubAck = 11,
|
||||
|
||||
/// <summary>
|
||||
/// 12 - 客户端到服务端:心跳请求。
|
||||
/// </summary>
|
||||
PingReq = 12,
|
||||
|
||||
/// <summary>
|
||||
/// 13 - 服务端到客户端:心跳响应。
|
||||
/// </summary>
|
||||
PingResp = 13,
|
||||
|
||||
/// <summary>
|
||||
/// 14 - 客户端到服务端:客户端断开连接。
|
||||
/// </summary>
|
||||
Disconnect = 14,
|
||||
|
||||
/// <summary>
|
||||
/// 15 - 禁止使用,保留。表示此枚举中保留值的最大值。
|
||||
/// </summary>
|
||||
ReservedMax = 15
|
||||
}
|
||||
@@ -5,21 +5,25 @@
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
namespace TouchSocket.Rpc
|
||||
/// <summary>
|
||||
/// 表示 MQTT 有效负载格式指示符。
|
||||
/// </summary>
|
||||
public enum MqttPayloadFormatIndicator : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// 标识将通过源生成器生成Rpc服务的调用委托。
|
||||
/// 未指定格式。
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
[Obsolete("该配置已被弃用,内部会尽可能得使用委托调用",true)]
|
||||
internal class GeneratorRpcServerAttribute : Attribute
|
||||
{
|
||||
}
|
||||
Unspecified = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 字符数据格式。
|
||||
/// </summary>
|
||||
CharacterData = 1
|
||||
}
|
||||
39
src/TouchSocket.Mqtt/Enums/MqttProtocolVersion.cs
Normal file
39
src/TouchSocket.Mqtt/Enums/MqttProtocolVersion.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示 MQTT 协议版本。
|
||||
/// </summary>
|
||||
public enum MqttProtocolVersion
|
||||
{
|
||||
/// <summary>
|
||||
/// 未知版本。
|
||||
/// </summary>
|
||||
Unknown = 0,
|
||||
|
||||
/// <summary>
|
||||
/// MQTT 3.1.0 版本。
|
||||
/// </summary>
|
||||
V310 = 3,
|
||||
|
||||
/// <summary>
|
||||
/// MQTT 3.1.1 版本。
|
||||
/// </summary>
|
||||
V311 = 4,
|
||||
|
||||
/// <summary>
|
||||
/// MQTT 5.0.0 版本。
|
||||
/// </summary>
|
||||
V500 = 5
|
||||
}
|
||||
405
src/TouchSocket.Mqtt/Enums/MqttReasonCode.cs
Normal file
405
src/TouchSocket.Mqtt/Enums/MqttReasonCode.cs
Normal file
@@ -0,0 +1,405 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// MQTT协议中的原因码枚举,用于表示不同 MQTT 报文处理结果或错误情况。
|
||||
/// </summary>
|
||||
public enum MqttReasonCode
|
||||
{
|
||||
/// <summary>
|
||||
/// 此 ReasonCode 可以用在所有存在 ReasonCode 的报文中,例如 <see cref="MqttConnAckMessage"/>、<see cref="MqttDisconnectMessage"/> 报文等等。
|
||||
/// 它通常用于表示成功,比如连接成功、取消订阅成功、消息接收成功和认证成功等等。
|
||||
/// 适用报文:<see cref="MqttConnAckMessage"/>, <see cref="MqttPubAckMessage"/> <see cref="MqttPubAckMessage"/>, <see cref="MqttPubRecMessage"/> <see cref="MqttPubRecMessage"/>, <see cref="MqttPubRelMessage"/>, <see cref="MqttPubCompMessage"/> , <see cref="MqttUnsubAckMessage"/>, AUTH
|
||||
/// </summary>
|
||||
Success = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 在 <see cref="MqttDisconnectMessage"/> 报文中,该值表示连接正常断开,这种情况下遗嘱消息不会被发布。
|
||||
/// 适用报文:<see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
NormalDisconnection = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 连接已接受。
|
||||
/// </summary>
|
||||
ConnectionAccepted = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 在 <see cref="MqttSubAckMessage"/> 订阅确认报文中,该值用来指示订阅结果,表示订阅成功,
|
||||
/// 同时向订阅端指示最终被授予的最大 QoS 等级为 0。
|
||||
/// 因为服务端最终授予的最大 QoS 等级,可能小于订阅时请求的最大 QoS 等级。
|
||||
/// 适用报文:<see cref="MqttSubAckMessage"/>
|
||||
/// </summary>
|
||||
GrantedQoS0 = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 在 <see cref="MqttSubAckMessage"/> 订阅确认报文中,该值用来指示订阅结果,表示订阅成功,
|
||||
/// 同时向订阅端指示最终被授予的最大 QoS 等级为 1。
|
||||
/// 因为服务端最终授予的最大 QoS 等级,可能小于订阅时请求的最大 QoS 等级。
|
||||
/// 适用报文:<see cref="MqttSubAckMessage"/>
|
||||
/// </summary>
|
||||
GrantedQoS1 = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 连接被拒绝,不可接受的协议版本。
|
||||
/// </summary>
|
||||
ConnectionRefusedUnacceptableProtocolVersion = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 在 <see cref="MqttSubAckMessage"/> 订阅确认报文中,该值用来指示订阅结果,表示订阅成功,
|
||||
/// 同时向订阅端指示最终被授予的最大 QoS 等级为 2。
|
||||
/// 因为服务端最终授予的最大 QoS 等级,可能小于订阅时请求的最大 QoS 等级。
|
||||
/// 适用报文:<see cref="MqttSubAckMessage"/>
|
||||
/// </summary>
|
||||
GrantedQoS2 = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 连接被拒绝,标识符被拒绝。
|
||||
/// </summary>
|
||||
ConnectionRefusedIdentifierRejected = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 连接被拒绝,服务器不可用。
|
||||
/// </summary>
|
||||
ConnectionRefusedServerUnavailable = 3,
|
||||
|
||||
/// <summary>
|
||||
/// 仅用于 <see cref="MqttDisconnectMessage"/> 报文,适用于客户端希望正常断开连接但服务端仍然需要发布遗嘱消息的情况,
|
||||
/// 比如客户端希望会话过期时可以对外发出通知。
|
||||
/// 适用报文:<see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
DisconnectWithWillMessage = 4,
|
||||
|
||||
/// <summary>
|
||||
/// 连接被拒绝,用户名或密码错误。
|
||||
/// </summary>
|
||||
ConnectionRefusedBadUsernameOrPassword = 4,
|
||||
|
||||
/// <summary>
|
||||
/// 连接被拒绝,未授权。
|
||||
/// </summary>
|
||||
ConnectionRefusedNotAuthorized = 5,
|
||||
|
||||
/// <summary>
|
||||
/// 该 ReasonCode 用于向发送方指示,消息已经收到,但是当前没有匹配的订阅者,所以只有服务端可以使用这个 ReasonCode。
|
||||
/// 可以通过收到 ReasonCode 为该值的响应报文得知当前没有人会收到自己的消息,
|
||||
/// 但不能通过没有收到该值的响应报文来假定所有人都会收到自己的消息,除非最多只会存在一个订阅者。
|
||||
/// 注意,没有匹配的订阅者时使用该值替代 0,并不是一个必须实现的行为,这取决于服务端的具体实现。
|
||||
/// 适用报文:<see cref="MqttPubAckMessage"/>, <see cref="MqttPubRecMessage"/>
|
||||
/// </summary>
|
||||
NoMatchingSubscribers = 16,
|
||||
|
||||
/// <summary>
|
||||
/// 仅用于 <see cref="MqttUnsubAckMessage"/> 报文,表示取消订阅时没有发现匹配的订阅。
|
||||
/// 适用报文:<see cref="MqttUnsubAckMessage"/>
|
||||
/// </summary>
|
||||
NoSubscriptionExisted = 17,
|
||||
|
||||
/// <summary>
|
||||
/// 仅用于 AUTH 报文,表示继续认证,通过这个 ReasonCode,客户端和服务端之间可以进行任意次数的 AUTH 报文交换,
|
||||
/// 以满足不同的认证方法的需要。
|
||||
/// 适用报文:AUTH
|
||||
/// </summary>
|
||||
ContinueAuthentication = 24,
|
||||
|
||||
/// <summary>
|
||||
/// 仅用于 AUTH 报文,在增强认证成功后客户端可以随时通过发送 ReasonCode 为该值的 AUTH 报文发起重新认证。
|
||||
/// 重新认证期间,其他报文收发会正常继续,如果重新认证失败,连接就会被关闭。
|
||||
/// 适用报文:AUTH
|
||||
/// </summary>
|
||||
ReAuthenticate = 25,
|
||||
|
||||
/// <summary>
|
||||
/// 表示未指明的错误。当一方不希望向另一方透露错误的具体原因,
|
||||
/// 或者协议规范中没有能够匹配当前情况的 ReasonCode 时,那么它可以在报文中使用这个 ReasonCode。
|
||||
/// 适用报文:<see cref="MqttConnAckMessage"/>, <see cref="MqttPubAckMessage"/>, <see cref="MqttPubRecMessage"/>, <see cref="MqttSubAckMessage"/>, <see cref="MqttUnsubAckMessage"/>, <see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
UnspecifiedError = 128,
|
||||
|
||||
/// <summary>
|
||||
/// 当收到了无法根据协议规范正确解析的控制报文时,接收方需要发送 ReasonCode 为该值的 <see cref="MqttDisconnectMessage"/> 报文来断开连接。
|
||||
/// 如果是 CONNECT 报文存在问题,那么服务端应该使用 <see cref="MqttConnAckMessage"/> 报文。
|
||||
/// 当控制报文中出现固定报头中的保留位没有按照协议要求置 0、QoS 被指定为 3、UTF - 8 字符串中包含了一个空字符等等这些情况时,
|
||||
/// 都将被认为是一个畸形的报文。
|
||||
/// 适用报文:<see cref="MqttConnAckMessage"/>, <see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
MalformedPacket = 129,
|
||||
|
||||
/// <summary>
|
||||
/// 在控制报文被按照协议规范解析后检测到的错误,比如包含协议不允许的数据,行为与协议要求不符等等,
|
||||
/// 都会被认为是协议错误。接收方需要发送 ReasonCode 为该值的 <see cref="MqttDisconnectMessage"/> 报文来断开连接。
|
||||
/// 如果是 CONNECT 报文存在问题,那么服务端应该使用 <see cref="MqttConnAckMessage"/> 报文。
|
||||
/// 常见的协议错误包括,客户端在一个连接内发送了两个 CONNECT 报文、一个报文中包含了多个相同的属性,
|
||||
/// 以及某个属性被设置成了一个协议不允许的值等等。
|
||||
/// 但是当有其他更具体的 ReasonCode 时,就不会使用该值 (Malformed Packet) 或者 130 (Protocol Error) 了。
|
||||
/// 例如,服务端已经声明自己不支持保留消息,但客户端仍然向服务端发送保留消息,
|
||||
/// 这本质上也属于协议错误,但会选择使用 154 (Retain not supported) 这个能够更清楚指明错误原因的 ReasonCode。
|
||||
/// 适用报文:<see cref="MqttConnAckMessage"/>, <see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
ProtocolError = 130,
|
||||
|
||||
/// <summary>
|
||||
/// 报文有效,但是不被当前接收方的实现所接受。
|
||||
/// 适用报文:<see cref="MqttConnAckMessage"/>, <see cref="MqttPubAckMessage"/>, <see cref="MqttPubRecMessage"/>, <see cref="MqttSubAckMessage"/>, <see cref="MqttUnsubAckMessage"/>, <see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
ImplementationSpecificError = 131,
|
||||
|
||||
/// <summary>
|
||||
/// 仅用于 <see cref="MqttConnAckMessage"/> 报文。对于支持了 MQTT 5.0 的服务端来说,如果不支持客户端当前使用的 MQTT 协议版本,
|
||||
/// 或者客户端指定了一个错误的协议版本或协议名。例如,客户端将协议版本设置为 6,
|
||||
/// 那么服务端可以发送 ReasonCode 为该值的 <see cref="MqttConnAckMessage"/> 报文,表示不支持该协议版本并且表明自己 MQTT 服务端的身份,
|
||||
/// 然后关闭网络连接。当然服务端也可以选择直接关闭网络连接,因为使用 MQTT 3.1 或 3.1.1 的 MQTT 客户端
|
||||
/// 可能并不能理解该值这个 ReasonCode 的含义。这两个版本都是在 <see cref="MqttConnAckMessage"/> 报文使用 1 来表示不支持客户端指定的协议版本。
|
||||
/// 适用报文:<see cref="MqttConnAckMessage"/>
|
||||
/// </summary>
|
||||
UnsupportedProtocolVersion = 132,
|
||||
|
||||
/// <summary>
|
||||
/// 仅用于 <see cref="MqttConnAckMessage"/> 报文,表示 Client ID 是有效的字符串,但是服务端不允许。
|
||||
/// 可能的情形有 Clean Start 为 0 但 Client ID 为空、或者 Client ID 超出了服务端允许的最大长度等等。
|
||||
/// 适用报文:<see cref="MqttConnAckMessage"/>
|
||||
/// </summary>
|
||||
ClientIdentifierNotValid = 133,
|
||||
|
||||
/// <summary>
|
||||
/// 仅用于 <see cref="MqttConnAckMessage"/> 报文,表示客户端使用了错误的用户名或密码,这也意味着客户端将被拒绝连接。
|
||||
/// 适用报文:<see cref="MqttConnAckMessage"/>
|
||||
/// </summary>
|
||||
BadUserNameOrPassword = 134,
|
||||
|
||||
/// <summary>
|
||||
/// 当客户端使用 Token 认证或者增强认证时,使用该值来表示客户端没有被授权连接会比 134 更加合适。
|
||||
/// 当客户端进行发布、订阅等操作时,如果没有通过服务端的授权检查,
|
||||
/// 那么服务端也可以在 <see cref="MqttPubAckMessage"/> 等应答报文中指定该值这个 ReasonCode 来指示授权结果。
|
||||
/// 适用报文:<see cref="MqttConnAckMessage"/>, <see cref="MqttPubAckMessage"/>, <see cref="MqttPubRecMessage"/>, <see cref="MqttSubAckMessage"/>, <see cref="MqttUnsubAckMessage"/>, <see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
NotAuthorized = 135,
|
||||
|
||||
/// <summary>
|
||||
/// 仅用于 <see cref="MqttConnAckMessage"/> 报文,向客户端指示当前服务端不可用。比如当前服务端认证服务异常无法接入新客户端等等。
|
||||
/// 适用报文:<see cref="MqttConnAckMessage"/>
|
||||
/// </summary>
|
||||
ServerUnavailable = 136,
|
||||
|
||||
/// <summary>
|
||||
/// 向客户端指示服务端正忙,请稍后再试。
|
||||
/// 适用报文:<see cref="MqttConnAckMessage"/>, <see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
ServerBusy = 137,
|
||||
|
||||
/// <summary>
|
||||
/// 仅用于 <see cref="MqttConnAckMessage"/> 报文,表示客户端被禁止登录。
|
||||
/// 例如服务端检测到客户端的异常连接行为,所以将这个客户端的 Client ID 或者 IP 地址加入到了黑名单列表中,
|
||||
/// 又或者是后台管理人员手动封禁了这个客户端,当然以上这些通常需要视服务端的具体实现而定。
|
||||
/// 适用报文:<see cref="MqttConnAckMessage"/>
|
||||
/// </summary>
|
||||
Banned = 138,
|
||||
|
||||
/// <summary>
|
||||
/// 仅用于 <see cref="MqttDisconnectMessage"/> 报文,并且只有服务端可以使用。
|
||||
/// 如果服务端正在或即将关闭,它可以通过主动发送 ReasonCode 为该值的 <see cref="MqttDisconnectMessage"/> 报文的方式告知客户端连接因为服务端正在关闭而被终止。
|
||||
/// 这可以帮助客户端避免在连接关闭后继续向此服务端发起连接请求。
|
||||
/// 适用报文:<see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
ServerShuttingDown = 139,
|
||||
|
||||
/// <summary>
|
||||
/// 当服务端不支持客户端指定的增强认证方法,或者客户端在重新认证时使用了和之前认证不同的认证方法时,
|
||||
/// 那么服务端就会发送 ReasonCode 为该值的 <see cref="MqttConnAckMessage"/> 或者 <see cref="MqttDisconnectMessage"/> 报文。
|
||||
/// 适用报文:<see cref="MqttConnAckMessage"/>, <see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
BadAuthenticationMethod = 140,
|
||||
|
||||
/// <summary>
|
||||
/// 仅用于 <see cref="MqttDisconnectMessage"/> 报文,并且只有服务端可以使用。
|
||||
/// 如果客户端没能在 1.5 倍的 Keep Alive 时间内保持通信,服务端将会发送 ReasonCode 为该值的 <see cref="MqttDisconnectMessage"/> 报文然后关闭网络连接。
|
||||
/// 适用报文:<see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
KeepAliveTimeout = 141,
|
||||
|
||||
/// <summary>
|
||||
/// 仅用于 <see cref="MqttDisconnectMessage"/> 报文,并且只有服务端可以使用。
|
||||
/// 当客户端连接到服务端时,如果服务端中已经存在使用相同 Client ID 的客户端连接,
|
||||
/// 那么服务端就会向原有的客户端发送 ReasonCode 为该值的 <see cref="MqttDisconnectMessage"/> 报文,表示会话被新的客户端连接接管,
|
||||
/// 然后关闭原有的网络连接。不管新的客户端连接中的 Clean Start 是 0 还是 1,
|
||||
/// 服务端都会使用这个 ReasonCode 向原有客户端指示会话被接管。
|
||||
/// 适用报文:<see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
SessionTakenOver = 142,
|
||||
|
||||
/// <summary>
|
||||
/// 主题过滤器的格式正确,但是不被服务端接受。
|
||||
/// 比如主题过滤器的层级超过了服务端允许的最大数量限制,或者主题过滤器中包含了空格等不被当前服务端接受的字符。
|
||||
/// 适用报文:<see cref="MqttSubAckMessage"/>, <see cref="MqttUnsubAckMessage"/>, <see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
TopicFilterInvalid = 143,
|
||||
|
||||
/// <summary>
|
||||
/// 主题名的格式正确,但是不被客户端或服务端接受。
|
||||
/// 适用报文:<see cref="MqttConnAckMessage"/>, <see cref="MqttPubAckMessage"/>, <see cref="MqttPubRecMessage"/>, <see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
TopicNameInvalid = 144,
|
||||
|
||||
/// <summary>
|
||||
/// 表示收到报文中的 Packet ID 正在被使用,例如发送方发送了一个 Packet ID 为 100 的 QoS 1 消息,
|
||||
/// 但是接收方认为当前有一个使用相同 Packet ID 的 QoS 2 消息还没有按成它的报文流程。
|
||||
/// 这通常意味着当前客户端和服务端之前的会话状态不匹配,可能需要通过设置 Clean Start 为 1 重新连接来重置会话状态。
|
||||
/// 适用报文:<see cref="MqttPubAckMessage"/>, <see cref="MqttPubRecMessage"/>, <see cref="MqttSubAckMessage"/>, <see cref="MqttUnsubAckMessage"/>
|
||||
/// </summary>
|
||||
PacketIdentifierInUse = 145,
|
||||
|
||||
/// <summary>
|
||||
/// 表示未找到对应的 Packet ID,这只会在 QoS 2 的报文交互流程中发生。
|
||||
/// 比如当接收方回复 <see cref="MqttPubRecMessage"/> 报文时,发送方未找到使用相同 Packet ID 的等待确认的 PUBLISH 报文,
|
||||
/// 或者当发送方发送 <see cref="MqttPubRelMessage"/> 报文时,接收方未找到使用相同 Packet ID 的 <see cref="MqttPubRecMessage"/> 报文。
|
||||
/// 这通常意味着当前客户端和服务端之间的会话状态不匹配,可能需要通过设置 Clean Start 为 1 重新连接来重置会话状态。
|
||||
/// 适用报文:<see cref="MqttPubRelMessage"/>, <see cref="MqttPubCompMessage"/>
|
||||
/// </summary>
|
||||
PacketIdentifierNotFound = 146,
|
||||
|
||||
/// <summary>
|
||||
/// 仅用于 <see cref="MqttDisconnectMessage"/> 报文,表示超出了接收最大值。
|
||||
/// MQTT 5.0 增加了流控机制,客户端和服务端在连接时通过 Receive Maximum 属性约定它们愿意并发处理的可靠消息数(QoS > 0)。
|
||||
/// 所以一旦发送方发送的没有完成确认的消息超过了这一数量限制,接收方就会发送 ReasonCode 为该值的 <see cref="MqttDisconnectMessage"/> 报文然后关闭网络连接。
|
||||
/// 适用报文:<see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
ReceiveMaximumExceeded = 147,
|
||||
|
||||
/// <summary>
|
||||
/// 仅用于 <see cref="MqttDisconnectMessage"/> 报文,表示主题别名不合法。
|
||||
/// 如果 PUBLISH 报文中的主题别名值为 0 或者大于连接时约定的最大主题别名,
|
||||
/// 接收方会将此视为协议错误,它将发送 ReasonCode 为该值的 <see cref="MqttDisconnectMessage"/> 报文然后关闭网络连接。
|
||||
/// 适用报文:<see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
TopicAliasInvalid = 148,
|
||||
|
||||
/// <summary>
|
||||
/// 用于表示报文超过了最大允许长度。
|
||||
/// 客户端和服务端各自允许的最大报文长度,可以在 CONNECT 和 <see cref="MqttConnAckMessage"/> 报文中通过 Maximum Packet Size 属性约定。
|
||||
/// 当一方发送了过大的报文,那么另一方将发送 ReasonCode 为该值的 <see cref="MqttDisconnectMessage"/> 报文,然后关闭网络连接。
|
||||
/// 由于客户端可以在连接时设置遗嘱消息,因此 CONNECT 报文也有可能超过服务端能够处理的最大报文长度限制,
|
||||
/// 此时服务端需要在 <see cref="MqttConnAckMessage"/> 报文中使用这个 ReasonCode。
|
||||
/// 适用报文:<see cref="MqttConnAckMessage"/>, <see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
PacketTooLarge = 149,
|
||||
|
||||
/// <summary>
|
||||
/// 仅用于 <see cref="MqttDisconnectMessage"/> 报文,表示超过了允许的最大消息发布速率。
|
||||
/// 需要注意它与 Quota exceeded 的区别,Message rate 限制消息的发布速率,比如每秒最高可发布多少消息,
|
||||
/// Quota 限制的是资源的配额,比如客户端每天可以发布的消息数量,但客户端可能在一小时内耗尽它的配额。
|
||||
/// 适用报文:<see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
MessageRateTooHigh = 150,
|
||||
|
||||
/// <summary>
|
||||
/// 用于表示超出了配额限制。
|
||||
/// 服务端可能会对发布端的发送配额进行限制,比如每天最多为其转发 1000 条消息。
|
||||
/// 当发布端的配额耗尽,服务端就会在 <see cref="MqttPubAckMessage"/> 等确认报文中使用这个 ReasonCode 提醒对方。
|
||||
/// 另一方面,服务端还可能限制客户端的连接数量和订阅数量,当超出这一限制时,
|
||||
/// 服务端就会通过 <see cref="MqttConnAckMessage"/> 或者 <see cref="MqttSubAckMessage"/> 报文向客户端指示当前超出了配额。
|
||||
/// 一些严格的客户端和服务端,在发现对端超出配额时,可能会选择发送 <see cref="MqttDisconnectMessage"/> 报文然后关闭连接。
|
||||
/// 适用报文:<see cref="MqttConnAckMessage"/>, <see cref="MqttPubAckMessage"/>, <see cref="MqttPubRecMessage"/>, <see cref="MqttSubAckMessage"/>, <see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
QuotaExceeded = 151,
|
||||
|
||||
/// <summary>
|
||||
/// 仅用于 <see cref="MqttDisconnectMessage"/> 报文,向客户端指示连接因为管理操作而被关闭,例如运维人员在后台踢除了这个客户端连接等等。
|
||||
/// 适用报文:<see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
AdministrativeAction = 152,
|
||||
|
||||
/// <summary>
|
||||
/// 当消息中包含 Payload Format Indicator 属性时,接收方可以检查消息中 Payload 的格式与该属性是否匹配。
|
||||
/// 如果不匹配,接收方需要发送 ReasonCode 为该值的确认报文。一些严格的客户端或者服务器,
|
||||
/// 可能会直接发送 <see cref="MqttDisconnectMessage"/> 报文然后关闭网络连接。如果是 CONNECT 报文中的遗嘱消息存在问题,
|
||||
/// 服务端将发送 ReasonCode 为该值的 <see cref="MqttConnAckMessage"/> 报文然后关闭网络连接。
|
||||
/// 适用报文:<see cref="MqttConnAckMessage"/>, <see cref="MqttPubAckMessage"/>, <see cref="MqttPubRecMessage"/>, <see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
PayloadFormatInvalid = 153,
|
||||
|
||||
/// <summary>
|
||||
/// 当服务端不支持保留消息,但是客户端发送了保留消息时,服务端就会向它发送 ReasonCode 为该值的 <see cref="MqttDisconnectMessage"/> 报文然后关闭网络连接。
|
||||
/// 由于客户端还可以在连接时将遗嘱消息设置为保留消息,所以服务端也可能在 <see cref="MqttConnAckMessage"/> 报文中使用这个 ReasonCode。
|
||||
/// 适用报文:<see cref="MqttConnAckMessage"/>, <see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
RetainNotSupported = 154,
|
||||
|
||||
/// <summary>
|
||||
/// 用于表示不支持当前的 QoS 等级。如果客户端在消息(包括遗嘱消息)中指定的 QoS 大于服务端支持的最大 QoS,
|
||||
/// 服务端将会发送 ReasonCode 为该值的 <see cref="MqttDisconnectMessage"/> 或者 <see cref="MqttConnAckMessage"/> 报文然后关闭网络连接。
|
||||
/// 在大部份情况下,这个 ReasonCode 都是由服务端使用。但是在客户端收到不是来自订阅的消息,
|
||||
/// 并且消息的 QoS 大于它支持的最大 QoS 时,它也会发送 ReasonCode 为该值的 <see cref="MqttDisconnectMessage"/> 报文然后关闭网络连接。
|
||||
/// 这种情况通常意味着服务端的实现可能存在问题。
|
||||
/// 适用报文:<see cref="MqttConnAckMessage"/>, <see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
QoSNotSupported = 155,
|
||||
|
||||
/// <summary>
|
||||
/// 服务端在 <see cref="MqttConnAckMessage"/> 或者 <see cref="MqttDisconnectMessage"/> 报文中通过这个 ReasonCode 告知客户端应该临时切换到另一个服务端。
|
||||
/// 如果另一个服务端不是客户端已知的,那么这个 ReasonCode 还需要配合 Server Reference 属性一起使用,
|
||||
/// 以告知客户端新的服务端的地址。
|
||||
/// 适用报文:<see cref="MqttConnAckMessage"/>, <see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
UseAnotherServer = 156,
|
||||
|
||||
/// <summary>
|
||||
/// 服务端在 <see cref="MqttConnAckMessage"/> 或者 <see cref="MqttDisconnectMessage"/> 报文中通过这个 ReasonCode 告知客户端应该永久切换到另一个服务端。
|
||||
/// 如果另一个服务端不是客户端已知的,那么这个 ReasonCode 还需要配合 Server Reference 属性一起使用,
|
||||
/// 以告知客户端新的服务端的地址。
|
||||
/// 适用报文:<see cref="MqttConnAckMessage"/>, <see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
ServerMoved = 157,
|
||||
|
||||
/// <summary>
|
||||
/// 当服务端不支持共享订阅,但是客户端尝试建立共享订阅时,服务端可以发送 ReasonCode 为该值的 <see cref="MqttSubAckMessage"/> 报文拒绝这次订阅请求,
|
||||
/// 也可以直接发送 ReasonCode 为该值的 <see cref="MqttDisconnectMessage"/> 报文然后关闭网络连接。
|
||||
/// 适用报文:<see cref="MqttSubAckMessage"/>, <see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
SharedSubscriptionsNotSupported = 158,
|
||||
|
||||
/// <summary>
|
||||
/// 用于表示客户端已超过连接速率限制。服务端可以对客户端的连接速率做出限制,
|
||||
/// 客户端连接过快时,服务端可以发送 ReasonCode 为该值的<see cref="MqttConnAckMessage"/>报文来拒绝新的连接。
|
||||
/// 当然这并不是绝对的情况,考虑到不是所有的客户端都会等待一段时间再重新发起连接,
|
||||
/// 一些服务端实现可能会选择暂时挂起连接而不是返回 <see cref="MqttConnAckMessage"/>。
|
||||
/// 适用报文:<see cref="MqttConnAckMessage"/>, <see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
ConnectionRateExceeded = 159,
|
||||
|
||||
/// <summary>
|
||||
/// 仅用于 <see cref="MqttDisconnectMessage"/> 报文,并且只有服务端可以使用。出于安全性的考虑,
|
||||
/// 服务端可以限制单次授权中客户端的最大连接时间,比如在使用 JWT 认证时,
|
||||
/// 客户端连接不应在 JWT 过期后继续保持。这种情况下,服务端可以发送 ReasonCode 为该值的 <see cref="MqttDisconnectMessage"/> 报文,
|
||||
/// 向客户端指示连接因为超过授权的最大连接时间而被关闭。客户端可以在收到包含这个 ReasonCode 的 <see cref="MqttDisconnectMessage"/> 报文后,
|
||||
/// 重新获取认证凭据然后再次请求连接。
|
||||
/// 适用报文:<see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
MaximumConnectTime = 160,
|
||||
|
||||
/// <summary>
|
||||
/// 当服务端不支持订阅标识符,但是客户端的订阅请求中包含了订阅标识符时,
|
||||
/// 服务端可以发送 ReasonCode 为该值的 <see cref="MqttSubAckMessage"/> 报文拒绝这次订阅请求,
|
||||
/// 也可以直接发送 ReasonCode 为该值的 <see cref="MqttDisconnectMessage"/> 报文然后关闭网络连接。
|
||||
/// 适用报文:<see cref="MqttSubAckMessage"/>, <see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
SubscriptionIdentifiersNotSupported = 161,
|
||||
|
||||
/// <summary>
|
||||
/// 当服务端不支持通配符订阅,但是客户端的订阅请求中包含了主题通配符时,
|
||||
/// 服务端可以发送 ReasonCode 为该值的 <see cref="MqttSubAckMessage"/> 报文拒绝这次订阅请求,
|
||||
/// 也可以直接发送 ReasonCode 为该值的 <see cref="MqttDisconnectMessage"/> 报文然后关闭网络连接。
|
||||
/// 适用报文:<see cref="MqttSubAckMessage"/>, <see cref="MqttDisconnectMessage"/>
|
||||
/// </summary>
|
||||
WildcardSubscriptionsNotSupported = 162
|
||||
}
|
||||
34
src/TouchSocket.Mqtt/Enums/MqttRetainHandling.cs
Normal file
34
src/TouchSocket.Mqtt/Enums/MqttRetainHandling.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示 MQTT 保留消息处理方式的枚举。
|
||||
/// </summary>
|
||||
public enum MqttRetainHandling : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// 在订阅时发送保留消息。
|
||||
/// </summary>
|
||||
SendAtSubscribe = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 仅在新的订阅时发送保留消息。
|
||||
/// </summary>
|
||||
SendAtSubscribeIfNewSubscriptionOnly = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 在订阅时不发送保留消息。
|
||||
/// </summary>
|
||||
DoNotSendOnSubscribe = 2
|
||||
}
|
||||
34
src/TouchSocket.Mqtt/Enums/QosLevel.cs
Normal file
34
src/TouchSocket.Mqtt/Enums/QosLevel.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示MQTT消息的服务质量(QoS)级别。
|
||||
/// </summary>
|
||||
public enum QosLevel : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// 最多一次。消息发布完全依赖底层网络的能力。消息可能到达一次也可能根本没到达。
|
||||
/// </summary>
|
||||
AtMostOnce = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 至少一次。确保消息至少到达一次,但消息可能会重复。
|
||||
/// </summary>
|
||||
AtLeastOnce = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 只有一次。确保消息到达一次且仅到达一次。
|
||||
/// </summary>
|
||||
ExactlyOnce = 2
|
||||
}
|
||||
29
src/TouchSocket.Mqtt/EventArgs/MqttClosedEventArgs.cs
Normal file
29
src/TouchSocket.Mqtt/EventArgs/MqttClosedEventArgs.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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.Sockets;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
/// <summary>
|
||||
/// 表示 MQTT 关闭事件的参数。
|
||||
/// </summary>
|
||||
public class MqttClosedEventArgs : ClosedEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="MqttClosedEventArgs"/> 类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="manual">指示关闭是否是手动的。</param>
|
||||
/// <param name="mes">关闭的消息。</param>
|
||||
public MqttClosedEventArgs(bool manual, string mes) : base(manual, mes)
|
||||
{
|
||||
}
|
||||
}
|
||||
41
src/TouchSocket.Mqtt/EventArgs/MqttClosingEventArgs.cs
Normal file
41
src/TouchSocket.Mqtt/EventArgs/MqttClosingEventArgs.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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.Sockets;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
/// <summary>
|
||||
/// 表示 MQTT 关闭事件的参数。
|
||||
/// </summary>
|
||||
public class MqttClosingEventArgs : ClosingEventArgs
|
||||
{
|
||||
private readonly MqttDisconnectMessage m_message;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="MqttClosingEventArgs"/> 类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="msg">关闭消息。</param>
|
||||
public MqttClosingEventArgs(string msg) : base(msg)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="MqttClosingEventArgs"/> 类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="message">MQTT 断开连接消息。</param>
|
||||
public MqttClosingEventArgs(MqttDisconnectMessage message) : base(message.ReasonString)
|
||||
{
|
||||
this.m_message = message;
|
||||
}
|
||||
|
||||
public MqttDisconnectMessage MqttMessage => this.m_message;
|
||||
}
|
||||
42
src/TouchSocket.Mqtt/EventArgs/MqttConnectedEventArgs.cs
Normal file
42
src/TouchSocket.Mqtt/EventArgs/MqttConnectedEventArgs.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示 MQTT 连接事件参数。
|
||||
/// </summary>
|
||||
public class MqttConnectedEventArgs : PluginEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取连接消息。
|
||||
/// </summary>
|
||||
public MqttConnectMessage ConnectMessage { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取连接确认消息。
|
||||
/// </summary>
|
||||
public MqttConnAckMessage ConnAckMessage { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="MqttConnectedEventArgs"/> 类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="connectMessage">连接消息。</param>
|
||||
/// <param name="connAckMessage">连接确认消息。</param>
|
||||
public MqttConnectedEventArgs(MqttConnectMessage connectMessage, MqttConnAckMessage connAckMessage)
|
||||
{
|
||||
this.ConnectMessage = connectMessage;
|
||||
this.ConnAckMessage = connAckMessage;
|
||||
}
|
||||
}
|
||||
29
src/TouchSocket.Mqtt/EventArgs/MqttConnectingEventArgs.cs
Normal file
29
src/TouchSocket.Mqtt/EventArgs/MqttConnectingEventArgs.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示 MQTT 连接事件参数。
|
||||
/// </summary>
|
||||
public class MqttConnectingEventArgs : MqttConnectedEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="MqttConnectingEventArgs"/> 类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="connectMessage">连接消息。</param>
|
||||
/// <param name="connAckMessage">连接确认消息。</param>
|
||||
public MqttConnectingEventArgs(MqttConnectMessage connectMessage, MqttConnAckMessage connAckMessage)
|
||||
: base(connectMessage, connAckMessage)
|
||||
{
|
||||
}
|
||||
}
|
||||
35
src/TouchSocket.Mqtt/EventArgs/MqttReceivedEventArgs.cs
Normal file
35
src/TouchSocket.Mqtt/EventArgs/MqttReceivedEventArgs.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示接收到的 MQTT 消息事件参数。
|
||||
/// </summary>
|
||||
public class MqttReceivedEventArgs : PluginEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="MqttReceivedEventArgs"/> 类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="message">接收到的 MQTT 消息。</param>
|
||||
public MqttReceivedEventArgs(MqttArrivedMessage message)
|
||||
{
|
||||
this.Message = message;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取接收到的 MQTT 消息。
|
||||
/// </summary>
|
||||
public MqttArrivedMessage Message { get; }
|
||||
}
|
||||
35
src/TouchSocket.Mqtt/EventArgs/MqttReceivingEventArgs.cs
Normal file
35
src/TouchSocket.Mqtt/EventArgs/MqttReceivingEventArgs.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示接收 MQTT 消息的事件参数。
|
||||
/// </summary>
|
||||
public class MqttReceivingEventArgs : PluginEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取接收到的 MQTT 消息。
|
||||
/// </summary>
|
||||
public MqttMessage MqttMessage { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="MqttReceivingEventArgs"/> 类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="mqttMessage">接收到的 MQTT 消息。</param>
|
||||
public MqttReceivingEventArgs(MqttMessage mqttMessage)
|
||||
{
|
||||
this.MqttMessage = mqttMessage;
|
||||
}
|
||||
}
|
||||
41
src/TouchSocket.Mqtt/Extensions/MqttConfigExtension.cs
Normal file
41
src/TouchSocket.Mqtt/Extensions/MqttConfigExtension.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 TouchSocket.Core;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 提供用于配置MQTT选项的扩展方法。
|
||||
/// </summary>
|
||||
public static class MqttConfigExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// MQTT连接选项的依赖属性。
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty<MqttConnectOptions> MqttConnectOptionsProperty = new DependencyProperty<MqttConnectOptions>("MqttConnectOptions", null);
|
||||
|
||||
/// <summary>
|
||||
/// 设置MQTT连接选项。
|
||||
/// </summary>
|
||||
/// <param name="config">TouchSocket配置。</param>
|
||||
/// <param name="options">用于配置MQTT连接选项的操作。</param>
|
||||
/// <returns>更新后的TouchSocket配置。</returns>
|
||||
public static TouchSocketConfig SetMqttConnectOptions(this TouchSocketConfig config, Action<MqttConnectOptions> options)
|
||||
{
|
||||
var mqttConnectOptions = new MqttConnectOptions();
|
||||
options?.Invoke(mqttConnectOptions);
|
||||
config.SetValue(MqttConnectOptionsProperty, mqttConnectOptions);
|
||||
return config;
|
||||
}
|
||||
}
|
||||
694
src/TouchSocket.Mqtt/Extensions/MqttExtension.cs
Normal file
694
src/TouchSocket.Mqtt/Extensions/MqttExtension.cs
Normal file
@@ -0,0 +1,694 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 TouchSocket.Core;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 提供用于 MQTT 操作的扩展方法。
|
||||
/// </summary>
|
||||
public static class MqttExtension
|
||||
{
|
||||
#region ByteBlock
|
||||
|
||||
private const uint VariableByteIntegerMaxValue = 268435455;
|
||||
|
||||
/// <summary>
|
||||
/// 从字节块中读取 MQTT Int16 字符串。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块的类型。</typeparam>
|
||||
/// <param name="byteBlock">要读取的字节块。</param>
|
||||
/// <returns>读取的字符串。</returns>
|
||||
public static string ReadMqttInt16String<TByteBlock>(ref TByteBlock byteBlock)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
return ReadMqttInt16String(ref byteBlock, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从字节块中读取 MQTT Int16 字符串。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块的类型。</typeparam>
|
||||
/// <param name="byteBlock">要读取的字节块。</param>
|
||||
/// <param name="length">读取的字符串长度。</param>
|
||||
/// <returns>读取的字符串。</returns>
|
||||
public static string ReadMqttInt16String<TByteBlock>(ref TByteBlock byteBlock, out ushort length)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
length = byteBlock.ReadUInt16(EndianType.Big);
|
||||
return byteBlock.ReadToSpan(length).ToString(Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从字节块中读取可变字节整数。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块的类型。</typeparam>
|
||||
/// <param name="byteBlock">要读取的字节块。</param>
|
||||
/// <returns>读取的可变字节整数。</returns>
|
||||
public static uint ReadVariableByteInteger<TByteBlock>(ref TByteBlock byteBlock)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
var multiplier = 1;
|
||||
var value = 0;
|
||||
byte encodedByte;
|
||||
|
||||
do
|
||||
{
|
||||
encodedByte = byteBlock.ReadByte();
|
||||
value += (encodedByte & 0x7F) * multiplier;
|
||||
multiplier *= 128;
|
||||
} while ((encodedByte & 0x80) == 0x80);
|
||||
|
||||
return (uint)value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将 MQTT 固定报头写入字节块。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块的类型。</typeparam>
|
||||
/// <param name="byteBlock">要写入的字节块。</param>
|
||||
/// <param name="packetType">MQTT 消息类型。</param>
|
||||
/// <param name="flags">要写入的标志。</param>
|
||||
public static void WriteMqttFixedHeader<TByteBlock>(ref TByteBlock byteBlock, MqttMessageType packetType, byte flags = 0)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
var fixedHeader = (int)packetType << 4;
|
||||
fixedHeader |= flags;
|
||||
byteBlock.WriteByte((byte)fixedHeader);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将 MQTT Int16 字符串写入字节块。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块的类型。</typeparam>
|
||||
/// <param name="byteBlock">要写入的字节块。</param>
|
||||
/// <param name="value">要写入的字符串值。</param>
|
||||
/// <returns>写入的字符串长度。</returns>
|
||||
public static ushort WriteMqttInt16String<TByteBlock>(ref TByteBlock byteBlock, string value)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
var pos = byteBlock.Position;
|
||||
byteBlock.Position += 2;
|
||||
byteBlock.WriteNormalString(value, Encoding.UTF8);
|
||||
var lastPos = byteBlock.Position;
|
||||
var len = byteBlock.Position - pos - 2;
|
||||
byteBlock.Position = pos;
|
||||
byteBlock.WriteUInt16((ushort)len, EndianType.Big);
|
||||
byteBlock.Position = lastPos;
|
||||
return (ushort)len;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将可变字节整数写入字节块。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块的类型。</typeparam>
|
||||
/// <param name="byteBlock">要写入的字节块。</param>
|
||||
/// <param name="value">要写入的值。</param>
|
||||
public static void WriteVariableByteInteger<TByteBlock>(ref TByteBlock byteBlock, uint value)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
if (value > VariableByteIntegerMaxValue)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value), value, $"The value must be less than or equal to {VariableByteIntegerMaxValue}.");
|
||||
}
|
||||
do
|
||||
{
|
||||
var encodedByte = (byte)(value % 128);
|
||||
value /= 128;
|
||||
if (value > 0)
|
||||
{
|
||||
encodedByte |= 128;
|
||||
}
|
||||
byteBlock.WriteByte(encodedByte);
|
||||
} while (value > 0);
|
||||
}
|
||||
|
||||
#endregion ByteBlock
|
||||
|
||||
/// <summary>
|
||||
/// 获取字节的高 4 位。
|
||||
/// </summary>
|
||||
/// <param name="byte">要获取高 4 位的字节。</param>
|
||||
/// <returns>高 4 位。</returns>
|
||||
public static int GetHeight4(this byte @byte)
|
||||
{
|
||||
int height;
|
||||
height = (@byte & 0xf0) >> 4;
|
||||
return height;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取字节的低 4 位。
|
||||
/// </summary>
|
||||
/// <param name="byte">要获取低 4 位的字节。</param>
|
||||
/// <returns>低 4 位。</returns>
|
||||
public static int GetLow4(this byte @byte)
|
||||
{
|
||||
int low;
|
||||
low = @byte & 0x0f;//0x0f(00001111)
|
||||
return low;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取 MQTT Int16 字符串的长度。
|
||||
/// </summary>
|
||||
/// <param name="value">字符串值。</param>
|
||||
/// <returns>字符串的长度。</returns>
|
||||
public static ushort GetMqttInt16StringLength(string value)
|
||||
{
|
||||
if (value.IsNullOrEmpty())
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
return (ushort)(Encoding.UTF8.GetByteCount(value) + 2);
|
||||
}
|
||||
|
||||
public static int GetVariableByteIntegerCount(int value)
|
||||
{
|
||||
if (value > VariableByteIntegerMaxValue)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value), value, $"The value must be less than or equal to {VariableByteIntegerMaxValue}.");
|
||||
}
|
||||
|
||||
var count = 0;
|
||||
do
|
||||
{
|
||||
var encodedByte = (byte)(value % 128);
|
||||
value /= 128;
|
||||
if (value > 0)
|
||||
{
|
||||
encodedByte |= 128;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
while (value > 0);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
#region RetainHandling
|
||||
|
||||
public static MqttRetainHandling GetRetainHandling(this byte @byte, int index)
|
||||
{
|
||||
return (MqttRetainHandling)((@byte.GetBit(index + 1) ? 1 : 0) * 2 + (@byte.GetBit(index) ? 1 : 0));
|
||||
}
|
||||
|
||||
public static byte SetRetainHandling(this ref byte b, int bitNumber, MqttRetainHandling value)
|
||||
{
|
||||
b = (byte)(b & ~(3 << bitNumber) | (byte)value << bitNumber);
|
||||
return b;
|
||||
}
|
||||
|
||||
#endregion RetainHandling
|
||||
|
||||
/// <summary>
|
||||
/// 获取最大 QoS 级别。
|
||||
/// </summary>
|
||||
/// <param name="qosLevel1">第一个 QoS 级别。</param>
|
||||
/// <param name="qosLevel2">第二个 QoS 级别。</param>
|
||||
/// <returns>最大 QoS 级别。</returns>
|
||||
public static QosLevel MaxQosLevel(QosLevel qosLevel1, QosLevel qosLevel2)
|
||||
{
|
||||
return qosLevel1 > qosLevel2 ? qosLevel1 : qosLevel2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取最小 QoS 级别。
|
||||
/// </summary>
|
||||
/// <param name="qosLevel1">第一个 QoS 级别。</param>
|
||||
/// <param name="qosLevel2">第二个 QoS 级别。</param>
|
||||
/// <returns>最小 QoS 级别。</returns>
|
||||
public static QosLevel MinQosLevel(QosLevel qosLevel1, QosLevel qosLevel2)
|
||||
{
|
||||
return qosLevel1 < qosLevel2 ? qosLevel1 : qosLevel2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从字节块中读取 MQTT 二进制数据。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块的类型。</typeparam>
|
||||
/// <param name="byteBlock">要读取的字节块。</param>
|
||||
/// <returns>读取的二进制数据。</returns>
|
||||
public static byte[] ReadMqttBinaryData<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlock
|
||||
{
|
||||
var length = byteBlock.ReadUInt16(EndianType.Big);
|
||||
var data = new byte[length];
|
||||
var r = byteBlock.Read(data);
|
||||
if (r != length)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
#region QosLevel
|
||||
|
||||
/// <summary>
|
||||
/// 从字节的指定索引获取 QoS 级别。
|
||||
/// </summary>
|
||||
/// <param name="byte">要获取 QoS 级别的字节。</param>
|
||||
/// <param name="index">要获取 QoS 级别的索引。</param>
|
||||
/// <returns>QoS 级别。</returns>
|
||||
public static QosLevel GetQosLevel(this byte @byte, int index)
|
||||
{
|
||||
return (QosLevel)((@byte.GetBit(index + 1) ? 1 : 0) * 2 + (@byte.GetBit(index) ? 1 : 0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定位号设置字节中的 QoS 级别。
|
||||
/// </summary>
|
||||
/// <param name="b">要设置 QoS 级别的字节。</param>
|
||||
/// <param name="bitNumber">要设置 QoS 级别的位号。</param>
|
||||
/// <param name="value">要设置的 QoS 级别。</param>
|
||||
public static byte SetQosLevel(this ref byte b, int bitNumber, QosLevel value)
|
||||
{
|
||||
b = (byte)(b & ~(3 << bitNumber) | (byte)value << bitNumber);
|
||||
return b;
|
||||
}
|
||||
|
||||
#endregion QosLevel
|
||||
|
||||
/// <summary>
|
||||
/// 将缓冲区转换为 MQTT Int16 值。
|
||||
/// </summary>
|
||||
/// <param name="buffer">要转换的缓冲区。</param>
|
||||
/// <param name="offset">开始转换的偏移量。</param>
|
||||
/// <returns>转换后的 Int16 值。</returns>
|
||||
public static short ToMqttInt16(this byte[] buffer, int offset)
|
||||
{
|
||||
var data = new byte[] { buffer[offset + 1], buffer[offset] };
|
||||
return BitConverter.ToInt16(data, 0);
|
||||
}
|
||||
|
||||
#region MqttV5Properties
|
||||
|
||||
public static void WriteAssignedClientIdentifier<TByteBlock>(ref TByteBlock byteBlock, string value) where TByteBlock : IByteBlock
|
||||
{
|
||||
WriteStringProperty(ref byteBlock, MqttPropertyId.AssignedClientIdentifier, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入认证数据。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块的类型。</typeparam>
|
||||
/// <param name="byteBlock">要写入的字节块。</param>
|
||||
/// <param name="value">认证数据的值。</param>
|
||||
public static void WriteAuthenticationData<TByteBlock>(ref TByteBlock byteBlock, ReadOnlySpan<byte> value)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
if (value.IsEmpty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
byteBlock.WriteByte((byte)MqttPropertyId.AuthenticationData);
|
||||
byteBlock.WriteUInt16((ushort)value.Length, EndianType.Big);
|
||||
byteBlock.Write(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入认证方法。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块的类型。</typeparam>
|
||||
/// <param name="byteBlock">要写入的字节块。</param>
|
||||
/// <param name="value">认证方法的值。</param>
|
||||
public static void WriteAuthenticationMethod<TByteBlock>(ref TByteBlock byteBlock, string value)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
WriteStringProperty(ref byteBlock, MqttPropertyId.AuthenticationMethod, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入内容类型。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块的类型。</typeparam>
|
||||
/// <param name="byteBlock">要写入的字节块。</param>
|
||||
/// <param name="value">内容类型的值。</param>
|
||||
public static void WriteContentType<TByteBlock>(ref TByteBlock byteBlock, string value)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
WriteStringProperty(ref byteBlock, MqttPropertyId.ContentType, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入相关性数据。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块的类型。</typeparam>
|
||||
/// <param name="byteBlock">要写入的字节块。</param>
|
||||
/// <param name="value">相关性数据的值。</param>
|
||||
public static void WriteCorrelationData<TByteBlock>(ref TByteBlock byteBlock, ReadOnlySpan<byte> value)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
if (value.IsEmpty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
byteBlock.WriteByte((byte)MqttPropertyId.CorrelationData);
|
||||
byteBlock.WriteUInt16((ushort)value.Length, EndianType.Big);
|
||||
byteBlock.Write(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入最大数据包大小。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块的类型。</typeparam>
|
||||
/// <param name="byteBlock">要写入的字节块。</param>
|
||||
/// <param name="value">最大数据包大小的值。</param>
|
||||
public static void WriteMaximumPacketSize<TByteBlock>(ref TByteBlock byteBlock, uint value)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
byteBlock.WriteByte((byte)MqttPropertyId.MaximumPacketSize);
|
||||
byteBlock.WriteUInt32(value, EndianType.Big);
|
||||
}
|
||||
|
||||
public static void WriteMaximumQoS<TByteBlock>(ref TByteBlock byteBlock, QosLevel value) where TByteBlock : IByteBlock
|
||||
{
|
||||
if (value == QosLevel.ExactlyOnce)
|
||||
{
|
||||
return;
|
||||
}
|
||||
byteBlock.WriteByte((byte)MqttPropertyId.MaximumQoS);
|
||||
byteBlock.WriteByte((byte)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入消息过期间隔。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块的类型。</typeparam>
|
||||
/// <param name="byteBlock">要写入的字节块。</param>
|
||||
/// <param name="value">消息过期间隔的值。</param>
|
||||
public static void WriteMessageExpiryInterval<TByteBlock>(ref TByteBlock byteBlock, uint value)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
byteBlock.WriteByte((byte)MqttPropertyId.MessageExpiryInterval);
|
||||
byteBlock.WriteUInt32(value, EndianType.Big);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入负载格式指示符。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块的类型。</typeparam>
|
||||
/// <param name="byteBlock">要写入的字节块。</param>
|
||||
/// <param name="value">负载格式指示符的值。</param>
|
||||
public static void WritePayloadFormatIndicator<TByteBlock>(ref TByteBlock byteBlock, MqttPayloadFormatIndicator value)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
if (value == MqttPayloadFormatIndicator.Unspecified)
|
||||
{
|
||||
return;
|
||||
}
|
||||
byteBlock.WriteByte((byte)MqttPropertyId.PayloadFormatIndicator);
|
||||
byteBlock.WriteByte((byte)value);
|
||||
}
|
||||
|
||||
public static void WriteReasonString<TByteBlock>(ref TByteBlock byteBlock, string value) where TByteBlock : IByteBlock
|
||||
{
|
||||
WriteStringProperty(ref byteBlock, MqttPropertyId.ReasonString, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入接收最大值。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块的类型。</typeparam>
|
||||
/// <param name="byteBlock">要写入的字节块。</param>
|
||||
/// <param name="value">接收最大值的值。</param>
|
||||
public static void WriteReceiveMaximum<TByteBlock>(ref TByteBlock byteBlock, ushort value)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
byteBlock.WriteByte((byte)MqttPropertyId.ReceiveMaximum);
|
||||
byteBlock.WriteUInt16(value, EndianType.Big);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入请求问题信息。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块的类型。</typeparam>
|
||||
/// <param name="byteBlock">要写入的字节块。</param>
|
||||
/// <param name="value">请求问题信息的值。</param>
|
||||
public static void WriteRequestProblemInformation<TByteBlock>(ref TByteBlock byteBlock, bool value)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
byteBlock.WriteByte((byte)MqttPropertyId.RequestProblemInformation);
|
||||
byteBlock.WriteByte(1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入请求问题信息。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块的类型。</typeparam>
|
||||
/// <param name="byteBlock">要写入的字节块。</param>
|
||||
/// <param name="value">请求问题信息的值。</param>
|
||||
public static void WriteRequestProblemInformation<TByteBlock>(ref TByteBlock byteBlock, uint value)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
byteBlock.WriteByte((byte)MqttPropertyId.RequestProblemInformation);
|
||||
byteBlock.WriteUInt32(value, EndianType.Big);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入请求响应信息。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块的类型。</typeparam>
|
||||
/// <param name="byteBlock">要写入的字节块。</param>
|
||||
/// <param name="value">请求响应信息的值。</param>
|
||||
public static void WriteRequestResponseInformation<TByteBlock>(ref TByteBlock byteBlock, bool value)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
byteBlock.WriteByte((byte)MqttPropertyId.RequestResponseInformation);
|
||||
byteBlock.WriteByte(1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入请求响应信息。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块的类型。</typeparam>
|
||||
/// <param name="byteBlock">要写入的字节块。</param>
|
||||
/// <param name="value">请求响应信息的值。</param>
|
||||
public static void WriteRequestResponseInformation<TByteBlock>(ref TByteBlock byteBlock, uint value)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
byteBlock.WriteByte((byte)MqttPropertyId.RequestResponseInformation);
|
||||
byteBlock.WriteUInt32(value, EndianType.Big);
|
||||
}
|
||||
|
||||
public static void WriteResponseInformation<TByteBlock>(ref TByteBlock byteBlock, string value) where TByteBlock : IByteBlock
|
||||
{
|
||||
WriteStringProperty(ref byteBlock, MqttPropertyId.ResponseInformation, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入响应主题。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块的类型。</typeparam>
|
||||
/// <param name="byteBlock">要写入的字节块。</param>
|
||||
/// <param name="value">响应主题的值。</param>
|
||||
public static void WriteResponseTopic<TByteBlock>(ref TByteBlock byteBlock, string value)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
WriteStringProperty(ref byteBlock, MqttPropertyId.ResponseTopic, value);
|
||||
}
|
||||
|
||||
public static void WriteRetainAvailable<TByteBlock>(ref TByteBlock byteBlock, bool value) where TByteBlock : IByteBlock
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
byteBlock.WriteByte((byte)MqttPropertyId.RetainAvailable);
|
||||
byteBlock.WriteByte(0);
|
||||
}
|
||||
|
||||
public static void WriteServerKeepAlive<TByteBlock>(ref TByteBlock byteBlock, ushort value) where TByteBlock : IByteBlock
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
byteBlock.WriteByte((byte)MqttPropertyId.ServerKeepAlive);
|
||||
byteBlock.WriteUInt16(value);
|
||||
}
|
||||
|
||||
public static void WriteServerReference<TByteBlock>(ref TByteBlock byteBlock, string value) where TByteBlock : IByteBlock
|
||||
{
|
||||
WriteStringProperty(ref byteBlock, MqttPropertyId.ServerReference, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入会话过期间隔。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块的类型。</typeparam>
|
||||
/// <param name="byteBlock">要写入的字节块。</param>
|
||||
/// <param name="value">会话过期间隔的值。</param>
|
||||
public static void WriteSessionExpiryInterval<TByteBlock>(ref TByteBlock byteBlock, uint value)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
byteBlock.WriteByte((byte)MqttPropertyId.SessionExpiryInterval);
|
||||
byteBlock.WriteUInt32(value, EndianType.Big);
|
||||
}
|
||||
|
||||
public static void WriteSharedSubscriptionAvailable<TByteBlock>(ref TByteBlock byteBlock, bool value) where TByteBlock : IByteBlock
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
byteBlock.WriteByte((byte)MqttPropertyId.SharedSubscriptionAvailable);
|
||||
byteBlock.WriteByte(0);
|
||||
}
|
||||
|
||||
public static void WriteSubscriptionIdentifier<TByteBlock>(ref TByteBlock byteBlock, uint subscriptionIdentifier) where TByteBlock : IByteBlock
|
||||
{
|
||||
byteBlock.WriteByte((byte)MqttPropertyId.SubscriptionIdentifier);
|
||||
WriteVariableByteInteger(ref byteBlock, subscriptionIdentifier);
|
||||
}
|
||||
|
||||
public static void WriteSubscriptionIdentifiersAvailable<TByteBlock>(ref TByteBlock byteBlock, bool value) where TByteBlock : IByteBlock
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
byteBlock.WriteByte((byte)MqttPropertyId.SubscriptionIdentifiersAvailable);
|
||||
byteBlock.WriteByte(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入主题别名最大值。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块的类型。</typeparam>
|
||||
/// <param name="byteBlock">要写入的字节块。</param>
|
||||
/// <param name="value">主题别名最大值的值。</param>
|
||||
public static void WriteTopicAliasMaximum<TByteBlock>(ref TByteBlock byteBlock, ushort value)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
byteBlock.WriteByte((byte)MqttPropertyId.TopicAliasMaximum);
|
||||
byteBlock.WriteUInt16(value, EndianType.Big);
|
||||
}
|
||||
|
||||
public static void WriteUserProperties<TByteBlock>(ref TByteBlock byteBlock, IReadOnlyList<MqttUserProperty> userProperties) where TByteBlock : IByteBlock
|
||||
{
|
||||
if (userProperties is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (var userProperty in userProperties)
|
||||
{
|
||||
WriteUserProperty(ref byteBlock, userProperty.Name, userProperty.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入用户属性。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块的类型。</typeparam>
|
||||
/// <param name="byteBlock">要写入的字节块。</param>
|
||||
/// <param name="name">用户属性的名称。</param>
|
||||
/// <param name="value">用户属性的值。</param>
|
||||
public static void WriteUserProperty<TByteBlock>(ref TByteBlock byteBlock, string name, string value)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
if (name.IsNullOrEmpty() || value.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
byteBlock.WriteByte((byte)MqttPropertyId.UserProperty);
|
||||
WriteMqttInt16String(ref byteBlock, name);
|
||||
WriteMqttInt16String(ref byteBlock, value);
|
||||
}
|
||||
|
||||
public static void WriteWildcardSubscriptionAvailable<TByteBlock>(ref TByteBlock byteBlock, bool value) where TByteBlock : IByteBlock
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
byteBlock.WriteByte((byte)MqttPropertyId.WildcardSubscriptionAvailable);
|
||||
byteBlock.WriteByte(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入遗嘱延迟时间间隔。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块的类型。</typeparam>
|
||||
/// <param name="byteBlock">要写入的字节块。</param>
|
||||
/// <param name="value">遗嘱延迟时间间隔的值。</param>
|
||||
public static void WriteWillDelayInterval<TByteBlock>(ref TByteBlock byteBlock, uint value)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
byteBlock.WriteByte((byte)MqttPropertyId.WillDelayInterval);
|
||||
byteBlock.WriteUInt32(value, EndianType.Big);
|
||||
}
|
||||
|
||||
private static void WriteStringProperty<TByteBlock>(ref TByteBlock byteBlock, MqttPropertyId propertyId, string value)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
if (value.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
byteBlock.WriteByte((byte)propertyId);
|
||||
WriteMqttInt16String(ref byteBlock, value);
|
||||
}
|
||||
|
||||
public static void WriteTopicAlias<TByteBlock>(ref TByteBlock byteBlock, ushort value) where TByteBlock : IByteBlock
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
byteBlock.WriteByte((byte)MqttPropertyId.TopicAlias);
|
||||
byteBlock.WriteUInt16(value, EndianType.Big);
|
||||
}
|
||||
|
||||
#endregion MqttV5Properties
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个带有标识符的 MQTT 消息。
|
||||
/// </summary>
|
||||
public abstract class MqttIdentifierMessage : MqttUserPropertiesMessage, IWaitHandle
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取或设置消息的标识符。
|
||||
/// </summary>
|
||||
public ushort MessageId { get; set; }
|
||||
|
||||
int IWaitHandle.Sign
|
||||
{
|
||||
get => this.MessageId;
|
||||
set => this.MessageId = (ushort)value;
|
||||
}
|
||||
}
|
||||
216
src/TouchSocket.Mqtt/MqttMessages/Base/MqttMessage.cs
Normal file
216
src/TouchSocket.Mqtt/MqttMessages/Base/MqttMessage.cs
Normal file
@@ -0,0 +1,216 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个MQTT消息的抽象基类。
|
||||
/// </summary>
|
||||
public abstract class MqttMessage : IRequestInfo, IRequestInfoBuilder
|
||||
{
|
||||
private int m_endPosition;
|
||||
|
||||
/// <summary>
|
||||
/// 获取消息的最大长度。
|
||||
/// </summary>
|
||||
public virtual int MaxLength => 1024 * 64;
|
||||
|
||||
/// <summary>
|
||||
/// 获取消息类型。
|
||||
/// </summary>
|
||||
public abstract MqttMessageType MessageType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置协议版本号。
|
||||
/// </summary>
|
||||
public MqttProtocolVersion Version { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取结束位置。
|
||||
/// </summary>
|
||||
protected int EndPosition => this.m_endPosition;
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置标志位。
|
||||
/// </summary>
|
||||
protected byte Flags { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置可变数据的剩余长度。
|
||||
/// </summary>
|
||||
protected uint RemainingLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建MQTT消息实例。
|
||||
/// </summary>
|
||||
/// <param name="mqttDataType">MQTT数据类型。</param>
|
||||
/// <returns>MQTT消息实例。</returns>
|
||||
public static MqttMessage CreateMqttMessage(MqttMessageType mqttDataType)
|
||||
{
|
||||
//Console.WriteLine(mqttDataType);
|
||||
return mqttDataType switch
|
||||
{
|
||||
MqttMessageType.Connect => new MqttConnectMessage(),
|
||||
MqttMessageType.ConnAck => new MqttConnAckMessage(),
|
||||
MqttMessageType.Publish => new MqttPublishMessage(),
|
||||
MqttMessageType.PubAck => new MqttPubAckMessage(),
|
||||
MqttMessageType.PubRec => new MqttPubRecMessage(),
|
||||
MqttMessageType.PubRel => new MqttPubRelMessage(),
|
||||
MqttMessageType.PubComp => new MqttPubCompMessage(),
|
||||
MqttMessageType.Subscribe => new MqttSubscribeMessage(),
|
||||
MqttMessageType.SubAck => new MqttSubAckMessage(),
|
||||
MqttMessageType.Unsubscribe => new MqttUnsubscribeMessage(),
|
||||
MqttMessageType.UnsubAck => new MqttUnsubAckMessage(),
|
||||
MqttMessageType.PingReq => new MqttPingReqMessage(),
|
||||
MqttMessageType.PingResp => new MqttPingRespMessage(),
|
||||
MqttMessageType.Disconnect => new MqttDisconnectMessage(),
|
||||
//_ => new MqttMessage()
|
||||
_ => throw ThrowHelper.CreateInvalidEnumArgumentException(mqttDataType)
|
||||
};
|
||||
}
|
||||
|
||||
#region Build
|
||||
|
||||
/// <summary>
|
||||
/// 构建MQTT消息。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块类型。</typeparam>
|
||||
/// <param name="byteBlock">字节块引用。</param>
|
||||
public void Build<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlock
|
||||
{
|
||||
MqttExtension.WriteMqttFixedHeader(ref byteBlock, this.MessageType, this.Flags);
|
||||
var variableByteIntegerRecorder = new VariableByteIntegerRecorder();
|
||||
variableByteIntegerRecorder.CheckOut(ref byteBlock, this.GetMinimumRemainingLength());
|
||||
|
||||
this.BuildVariableBody(ref byteBlock);
|
||||
this.RemainingLength = (uint)variableByteIntegerRecorder.CheckIn(ref byteBlock);
|
||||
}
|
||||
|
||||
protected abstract void BuildVariableBodyWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
where TByteBlock : IByteBlock;
|
||||
|
||||
protected abstract void BuildVariableBodyWithMqtt5<TByteBlock>(ref TByteBlock byteBlock)
|
||||
where TByteBlock : IByteBlock;
|
||||
|
||||
/// <summary>
|
||||
/// 获取最小剩余长度。
|
||||
/// </summary>
|
||||
/// <returns>剩余长度。</returns>
|
||||
protected abstract int GetMinimumRemainingLength();
|
||||
|
||||
private void BuildVariableBody<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlock
|
||||
{
|
||||
switch (this.Version)
|
||||
{
|
||||
case MqttProtocolVersion.V310:
|
||||
case MqttProtocolVersion.V311:
|
||||
this.BuildVariableBodyWithMqtt3(ref byteBlock);
|
||||
break;
|
||||
|
||||
case MqttProtocolVersion.V500:
|
||||
this.BuildVariableBodyWithMqtt5(ref byteBlock);
|
||||
break;
|
||||
|
||||
case MqttProtocolVersion.Unknown:
|
||||
default:
|
||||
ThrowHelper.ThrowInvalidEnumArgumentException(this.Version);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Build
|
||||
|
||||
#region Unpack
|
||||
|
||||
/// <summary>
|
||||
/// 解包MQTT消息。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块类型。</typeparam>
|
||||
/// <param name="byteBlock">字节块引用。</param>
|
||||
public virtual void Unpack<TByteBlock>(ref TByteBlock byteBlock)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
var firstByte = byteBlock.ReadByte();
|
||||
this.SetFlags((byte)firstByte.GetLow4());
|
||||
this.RemainingLength = MqttExtension.ReadVariableByteInteger(ref byteBlock);
|
||||
this.m_endPosition = (int)(byteBlock.Position + this.RemainingLength);
|
||||
switch (this.Version)
|
||||
{
|
||||
case MqttProtocolVersion.V310:
|
||||
case MqttProtocolVersion.V311:
|
||||
this.UnpackWithMqtt3(ref byteBlock);
|
||||
break;
|
||||
|
||||
case MqttProtocolVersion.V500:
|
||||
this.UnpackWithMqtt5(ref byteBlock);
|
||||
break;
|
||||
|
||||
case MqttProtocolVersion.Unknown:
|
||||
default:
|
||||
ThrowHelper.ThrowInvalidEnumArgumentException(this.Version);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool EndOfByteBlock<TByteBlock>(in TByteBlock byteBlock)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
return this.m_endPosition == byteBlock.Position;
|
||||
}
|
||||
|
||||
protected abstract void UnpackWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
where TByteBlock : IByteBlock;
|
||||
|
||||
protected abstract void UnpackWithMqtt5<TByteBlock>(ref TByteBlock byteBlock)
|
||||
where TByteBlock : IByteBlock;
|
||||
|
||||
#endregion Unpack
|
||||
|
||||
internal void InternalSetVersion(MqttProtocolVersion version)
|
||||
{
|
||||
this.Version = version;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置标志位为0010。
|
||||
/// </summary>
|
||||
protected void SetFixedFlagsWith0010()
|
||||
{
|
||||
this.SetFlags(2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置标志位。
|
||||
/// </summary>
|
||||
/// <param name="value">标志位值。</param>
|
||||
protected virtual void SetFlags(byte value)
|
||||
{
|
||||
this.Flags = value;
|
||||
}
|
||||
|
||||
#region Throw
|
||||
|
||||
/// <summary>
|
||||
/// 如果标志位不为0010则抛出异常。
|
||||
/// </summary>
|
||||
protected void ThrowIfFlagsNot0010()
|
||||
{
|
||||
if (this.Flags != 2)
|
||||
{
|
||||
ThrowHelper.ThrowException($"{this.MessageType}消息的固定头的flags不合法");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Throw
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个包含用户属性的 MQTT 消息。
|
||||
/// </summary>
|
||||
public abstract class MqttUserPropertiesMessage : MqttMessage
|
||||
{
|
||||
private List<MqttUserProperty> m_userProperties;
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户属性的只读列表。
|
||||
/// </summary>
|
||||
[NotNull]
|
||||
public IReadOnlyList<MqttUserProperty> UserProperties => this.m_userProperties ?? MqttUtility.EmptyUserProperties;
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个用户属性。
|
||||
/// </summary>
|
||||
/// <param name="name">属性名称。</param>
|
||||
/// <param name="value">属性值。</param>
|
||||
public void AddUserProperty(string name, string value)
|
||||
{
|
||||
this.AddUserProperty(new MqttUserProperty(name, value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个用户属性。
|
||||
/// </summary>
|
||||
/// <param name="userProperty">用户属性。</param>
|
||||
public void AddUserProperty(MqttUserProperty userProperty)
|
||||
{
|
||||
this.m_userProperties ??= new List<MqttUserProperty>();
|
||||
this.m_userProperties.Add(userProperty);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示 MQTT 连接确认消息。
|
||||
/// </summary>
|
||||
public sealed partial class MqttConnAckMessage : MqttUserPropertiesMessage
|
||||
{
|
||||
private byte m_connectAcknowledgeFlags;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="MqttConnAckMessage"/> 类的新实例。
|
||||
/// </summary>
|
||||
public MqttConnAckMessage()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用指定的连接确认标志和返回代码初始化 <see cref="MqttConnAckMessage"/> 类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="m_connectAcknowledgeFlags">连接确认标志。</param>
|
||||
public MqttConnAckMessage(byte m_connectAcknowledgeFlags)
|
||||
{
|
||||
this.m_connectAcknowledgeFlags = m_connectAcknowledgeFlags;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用指定的会话存在标志和返回代码初始化 <see cref="MqttConnAckMessage"/> 类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="sessionPresent">会话存在标志。</param>
|
||||
public MqttConnAckMessage(bool sessionPresent)
|
||||
{
|
||||
this.m_connectAcknowledgeFlags = this.m_connectAcknowledgeFlags.SetBit(0, sessionPresent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取消息类型。
|
||||
/// </summary>
|
||||
public override MqttMessageType MessageType => MqttMessageType.ConnAck;
|
||||
|
||||
/// <summary>
|
||||
/// 获取返回代码。
|
||||
/// </summary>
|
||||
public MqttReasonCode ReturnCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取会话存在标志。
|
||||
/// </summary>
|
||||
public bool SessionPresent => this.m_connectAcknowledgeFlags.GetBit(0);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void BuildVariableBodyWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
byteBlock.WriteByte(this.m_connectAcknowledgeFlags);
|
||||
byteBlock.WriteByte((byte)this.ReturnCode);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override int GetMinimumRemainingLength()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnpackWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
this.m_connectAcknowledgeFlags = byteBlock.ReadByte();
|
||||
this.ReturnCode = (MqttReasonCode)byteBlock.ReadByte();
|
||||
}
|
||||
}
|
||||
259
src/TouchSocket.Mqtt/MqttMessages/V311/MqttConnectMessage_v3.cs
Normal file
259
src/TouchSocket.Mqtt/MqttMessages/V311/MqttConnectMessage_v3.cs
Normal file
@@ -0,0 +1,259 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示MQTT连接消息。
|
||||
/// </summary>
|
||||
public sealed partial class MqttConnectMessage : MqttUserPropertiesMessage
|
||||
{
|
||||
private byte m_connectFlags;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="MqttConnectMessage"/> 类的新实例。
|
||||
/// </summary>
|
||||
public MqttConnectMessage()
|
||||
{
|
||||
this.ProtocolName = MqttUtility.MqttProtocolName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="MqttConnectMessage"/> 类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="options">MQTT连接选项。</param>
|
||||
public MqttConnectMessage(MqttConnectOptions options)
|
||||
{
|
||||
this.ProtocolName = MqttUtility.MqttProtocolName;
|
||||
this.Version = options.Version;
|
||||
this.ClientId = options.ClientId;
|
||||
this.KeepAlive = options.KeepAlive;
|
||||
this.UserName = options.UserName;
|
||||
this.Password = options.Password;
|
||||
this.m_connectFlags = options.BuildConnectFlags();
|
||||
|
||||
//MQTT5
|
||||
this.AuthenticationData = options.AuthenticationData;
|
||||
this.AuthenticationMethod = options.AuthenticationMethod;
|
||||
this.MaximumPacketSize = options.MaximumPacketSize;
|
||||
this.ReceiveMaximum = options.ReceiveMaximum;
|
||||
this.RequestProblemInformation = options.RequestProblemInformation;
|
||||
this.RequestResponseInformation = options.RequestResponseInformation;
|
||||
this.SessionExpiryInterval = options.SessionExpiryInterval;
|
||||
this.TopicAliasMaximum = options.TopicAliasMaximum;
|
||||
|
||||
this.WillContentType = options.WillContentType;
|
||||
this.WillCorrelationData = options.WillCorrelationData;
|
||||
this.WillDelayInterval = options.WillDelayInterval;
|
||||
|
||||
this.WillDelayInterval = options.WillDelayInterval;
|
||||
this.WillMessageExpiryInterval = options.WillMessageExpiryInterval;
|
||||
this.WillPayloadFormatIndicator = options.WillPayloadFormatIndicator;
|
||||
|
||||
this.WillMessage = options.WillMessage;
|
||||
this.WillTopic = options.WillTopic;
|
||||
|
||||
foreach (var item in options.UserProperties.GetSafeEnumerator())
|
||||
{
|
||||
this.AddUserProperty(item);
|
||||
}
|
||||
|
||||
foreach (var item in options.WillUserProperties.GetSafeEnumerator())
|
||||
{
|
||||
this.AddWillUserProperties(item);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置客户端Id。
|
||||
/// </summary>
|
||||
public string ClientId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示是否为重复消息。
|
||||
/// </summary>
|
||||
public bool DUP => base.Flags.GetBit(3);
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置保活时长。
|
||||
/// </summary>
|
||||
public ushort KeepAlive { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取MQTT数据包类型。
|
||||
/// </summary>
|
||||
public override MqttMessageType MessageType => MqttMessageType.Connect;
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置密码。
|
||||
/// </summary>
|
||||
public string Password { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置协议名称。
|
||||
/// </summary>
|
||||
public string ProtocolName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取服务质量级别。
|
||||
/// </summary>
|
||||
public QosLevel QosLevel => base.Flags.GetQosLevel(1);
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示是否保留消息。
|
||||
/// </summary>
|
||||
public bool Retain => base.Flags.GetBit(0);
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置用户名。
|
||||
/// </summary>
|
||||
public string UserName { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Unpack<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
var firstByte = byteBlock.ReadByte();
|
||||
this.SetFlags((byte)firstByte.GetLow4());
|
||||
this.RemainingLength = MqttExtension.ReadVariableByteInteger(ref byteBlock);
|
||||
|
||||
this.ProtocolName = MqttExtension.ReadMqttInt16String(ref byteBlock);
|
||||
|
||||
this.Version = (MqttProtocolVersion)byteBlock.ReadByte();
|
||||
|
||||
this.m_connectFlags = byteBlock.ReadByte();
|
||||
this.KeepAlive = byteBlock.ReadUInt16(EndianType.Big);
|
||||
|
||||
switch (this.Version)
|
||||
{
|
||||
case MqttProtocolVersion.V310:
|
||||
case MqttProtocolVersion.V311:
|
||||
this.UnpackWithMqtt3(ref byteBlock);
|
||||
break;
|
||||
|
||||
case MqttProtocolVersion.V500:
|
||||
this.UnpackWithMqtt5(ref byteBlock);
|
||||
break;
|
||||
|
||||
case MqttProtocolVersion.Unknown:
|
||||
default:
|
||||
ThrowHelper.ThrowInvalidEnumArgumentException(this.Version);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void BuildVariableBodyWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
MqttExtension.WriteMqttInt16String(ref byteBlock, this.ProtocolName);
|
||||
byteBlock.WriteByte((byte)this.Version);
|
||||
byteBlock.WriteByte(this.m_connectFlags);
|
||||
byteBlock.WriteUInt16(this.KeepAlive, EndianType.Big);
|
||||
|
||||
MqttExtension.WriteMqttInt16String(ref byteBlock, this.ClientId);
|
||||
if (this.WillFlag)
|
||||
{
|
||||
MqttExtension.WriteMqttInt16String(ref byteBlock, this.WillTopic);
|
||||
MqttExtension.WriteMqttInt16String(ref byteBlock, this.WillMessage);
|
||||
}
|
||||
|
||||
if (this.UserNameFlag)
|
||||
{
|
||||
MqttExtension.WriteMqttInt16String(ref byteBlock, this.UserName);
|
||||
}
|
||||
if (this.PasswordFlag)
|
||||
{
|
||||
MqttExtension.WriteMqttInt16String(ref byteBlock, this.Password);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override int GetMinimumRemainingLength()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#region ConnectFlags
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示是否清理会话。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// MQTT 5.0.0以下
|
||||
/// </remarks>
|
||||
public bool CleanSession => this.m_connectFlags.GetBit(1);
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示是否包含密码标志。
|
||||
/// </summary>
|
||||
public bool PasswordFlag => this.m_connectFlags.GetBit(6);
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示是否为保留位。
|
||||
/// </summary>
|
||||
public bool Reserved => this.m_connectFlags.GetBit(0);
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示是否包含用户名标志。
|
||||
/// </summary>
|
||||
public bool UserNameFlag => this.m_connectFlags.GetBit(7);
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示是否包含遗嘱标志。
|
||||
/// </summary>
|
||||
public bool WillFlag => this.m_connectFlags.GetBit(2);
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置遗嘱消息。
|
||||
/// </summary>
|
||||
public string WillMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取遗嘱服务质量级别。
|
||||
/// </summary>
|
||||
public QosLevel WillQos => this.m_connectFlags.GetQosLevel(3);
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示是否保留遗嘱消息。
|
||||
/// </summary>
|
||||
public bool WillRetain => this.m_connectFlags.GetBit(5);
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置遗嘱主题。
|
||||
/// </summary>
|
||||
public string WillTopic { get; set; }
|
||||
|
||||
#endregion ConnectFlags
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnpackWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
this.ClientId = MqttExtension.ReadMqttInt16String(ref byteBlock);
|
||||
if (this.WillFlag)
|
||||
{
|
||||
this.WillTopic = MqttExtension.ReadMqttInt16String(ref byteBlock);
|
||||
this.WillMessage = MqttExtension.ReadMqttInt16String(ref byteBlock);
|
||||
}
|
||||
|
||||
if (this.UserNameFlag)
|
||||
{
|
||||
this.UserName = MqttExtension.ReadMqttInt16String(ref byteBlock);
|
||||
}
|
||||
|
||||
if (this.PasswordFlag)
|
||||
{
|
||||
this.Password = MqttExtension.ReadMqttInt16String(ref byteBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
public sealed partial class MqttDisconnectMessage : MqttUserPropertiesMessage
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override MqttMessageType MessageType => MqttMessageType.Disconnect;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void BuildVariableBodyWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override int GetMinimumRemainingLength()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnpackWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个MQTT Ping请求消息。
|
||||
/// </summary>
|
||||
public sealed partial class MqttPingReqMessage : MqttMessage
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override MqttMessageType MessageType => MqttMessageType.PingReq;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void BuildVariableBodyWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override int GetMinimumRemainingLength()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnpackWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示MQTT Ping响应消息。
|
||||
/// </summary>
|
||||
public sealed partial class MqttPingRespMessage : MqttMessage
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override MqttMessageType MessageType => MqttMessageType.PingResp;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void BuildVariableBodyWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override int GetMinimumRemainingLength()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnpackWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示 MQTT PubAck 消息。
|
||||
/// </summary>
|
||||
public sealed partial class MqttPubAckMessage : MqttIdentifierMessage
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override MqttMessageType MessageType => MqttMessageType.PubAck;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnpackWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
this.MessageId = byteBlock.ReadUInt16(EndianType.Big);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void BuildVariableBodyWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
byteBlock.WriteUInt16(this.MessageId, EndianType.Big);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override int GetMinimumRemainingLength()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示 MQTT PubComp 消息。
|
||||
/// </summary>
|
||||
public sealed partial class MqttPubCompMessage : MqttIdentifierMessage
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override MqttMessageType MessageType => MqttMessageType.PubComp;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void BuildVariableBodyWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
byteBlock.WriteUInt16(this.MessageId, EndianType.Big);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override int GetMinimumRemainingLength()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnpackWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
this.MessageId = byteBlock.ReadUInt16(EndianType.Big);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示 MQTT PubRec 消息。
|
||||
/// </summary>
|
||||
public sealed partial class MqttPubRecMessage : MqttIdentifierMessage
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override MqttMessageType MessageType => MqttMessageType.PubRec;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void BuildVariableBodyWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
byteBlock.WriteUInt16(this.MessageId, EndianType.Big);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnpackWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
this.MessageId = byteBlock.ReadUInt16(EndianType.Big);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
protected override int GetMinimumRemainingLength()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示 MQTT PubRel 消息。
|
||||
/// </summary>
|
||||
public sealed partial class MqttPubRelMessage : MqttIdentifierMessage
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override MqttMessageType MessageType => MqttMessageType.PubRel;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="MqttPubRelMessage"/> 类的新实例。
|
||||
/// </summary>
|
||||
public MqttPubRelMessage()
|
||||
{
|
||||
base.SetFlags(0x02);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override int GetMinimumRemainingLength()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void BuildVariableBodyWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
byteBlock.WriteUInt16(this.MessageId, EndianType.Big);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnpackWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
this.MessageId = byteBlock.ReadUInt16(EndianType.Big);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void SetFlags(byte value)
|
||||
{
|
||||
base.SetFlags(value);
|
||||
this.ThrowIfFlagsNot0010();
|
||||
}
|
||||
}
|
||||
132
src/TouchSocket.Mqtt/MqttMessages/V311/MqttPublishMessage_v3.cs
Normal file
132
src/TouchSocket.Mqtt/MqttMessages/V311/MqttPublishMessage_v3.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 TouchSocket.Core;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个MQTT发布消息。
|
||||
/// </summary>
|
||||
public sealed partial class MqttPublishMessage : MqttIdentifierMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="MqttPublishMessage"/> 类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="topicName">主题名称。</param>
|
||||
/// <param name="retain">是否保留消息。</param>
|
||||
/// <param name="qosLevel">服务质量级别。</param>
|
||||
/// <param name="payload">消息负载。</param>
|
||||
public MqttPublishMessage(string topicName, bool retain, QosLevel qosLevel, ReadOnlyMemory<byte> payload)
|
||||
{
|
||||
this.TopicName = topicName;
|
||||
this.Payload = payload;
|
||||
this.SetFlags(retain, qosLevel, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="MqttPublishMessage"/> 类的新实例。
|
||||
/// </summary>
|
||||
public MqttPublishMessage()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="MqttPublishMessage"/> 类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="topicName">主题名称。</param>
|
||||
/// <param name="payload">消息负载。</param>
|
||||
internal MqttPublishMessage(string topicName, ReadOnlyMemory<byte> payload)
|
||||
{
|
||||
this.TopicName = topicName;
|
||||
this.Payload = payload;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示是否为重复消息。
|
||||
/// </summary>
|
||||
public bool DUP => base.Flags.GetBit(3);
|
||||
|
||||
/// <summary>
|
||||
/// 获取消息类型。
|
||||
/// </summary>
|
||||
public override MqttMessageType MessageType => MqttMessageType.Publish;
|
||||
|
||||
/// <summary>
|
||||
/// 获取消息负载。
|
||||
/// </summary>
|
||||
public ReadOnlyMemory<byte> Payload { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取服务质量级别。
|
||||
/// </summary>
|
||||
public QosLevel QosLevel => base.Flags.GetQosLevel(1);
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示是否保留消息。
|
||||
/// </summary>
|
||||
public bool Retain => base.Flags.GetBit(0);
|
||||
|
||||
/// <summary>
|
||||
/// 获取主题名称。
|
||||
/// </summary>
|
||||
public string TopicName { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnpackWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
this.TopicName = MqttExtension.ReadMqttInt16String(ref byteBlock);
|
||||
if (this.QosLevel != QosLevel.AtMostOnce)
|
||||
{
|
||||
this.MessageId = byteBlock.ReadUInt16(EndianType.Big);
|
||||
}
|
||||
|
||||
this.Payload = this.ReadPayload(ref byteBlock);
|
||||
}
|
||||
|
||||
private ReadOnlyMemory<byte> ReadPayload<TByteBlock>(ref TByteBlock byteBlock)
|
||||
where TByteBlock : IByteBlock
|
||||
{
|
||||
var payloadLength = this.EndPosition - byteBlock.Position;
|
||||
var payload = byteBlock.Memory.Slice(byteBlock.Position, payloadLength);
|
||||
byteBlock.Position = this.EndPosition;
|
||||
return payload;
|
||||
}
|
||||
|
||||
|
||||
internal void SetFlags(bool retain, QosLevel qosLevel, bool dup)
|
||||
{
|
||||
byte flags = 0;
|
||||
flags = flags.SetBit(0, retain);
|
||||
flags = flags.SetQosLevel(1, qosLevel);
|
||||
flags = flags.SetBit(3, dup);
|
||||
base.SetFlags(flags);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void BuildVariableBodyWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
MqttExtension.WriteMqttInt16String(ref byteBlock, this.TopicName);
|
||||
if (this.QosLevel != QosLevel.AtMostOnce)
|
||||
{
|
||||
byteBlock.WriteUInt16(this.MessageId, EndianType.Big);
|
||||
}
|
||||
byteBlock.Write(this.Payload.Span);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override int GetMinimumRemainingLength()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 TouchSocket.Core;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示 MQTT 订阅确认消息。
|
||||
/// </summary>
|
||||
public sealed partial class MqttSubAckMessage : MqttIdentifierMessage
|
||||
{
|
||||
private readonly List<MqttReasonCode> m_returnCodes = new List<MqttReasonCode>();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override MqttMessageType MessageType => MqttMessageType.SubAck;
|
||||
|
||||
/// <summary>
|
||||
/// 获取返回码的只读列表。
|
||||
/// </summary>
|
||||
public IReadOnlyList<MqttReasonCode> ReturnCodes => this.m_returnCodes;
|
||||
|
||||
/// <summary>
|
||||
/// 添加返回码。
|
||||
/// </summary>
|
||||
/// <param name="returnCode">返回码。</param>
|
||||
public void AddReturnCode(MqttReasonCode returnCode)
|
||||
{
|
||||
this.m_returnCodes.Add(returnCode);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override int GetMinimumRemainingLength()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据 QoS 级别添加返回码。
|
||||
/// </summary>
|
||||
/// <param name="qosLevel">QoS 级别。</param>
|
||||
public void AddReturnCode(QosLevel qosLevel)
|
||||
{
|
||||
switch (qosLevel)
|
||||
{
|
||||
case QosLevel.AtMostOnce:
|
||||
this.AddReturnCode(MqttReasonCode.GrantedQoS0);
|
||||
break;
|
||||
|
||||
case QosLevel.AtLeastOnce:
|
||||
this.AddReturnCode(MqttReasonCode.GrantedQoS1);
|
||||
break;
|
||||
|
||||
case QosLevel.ExactlyOnce:
|
||||
this.AddReturnCode(MqttReasonCode.GrantedQoS2);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(qosLevel), qosLevel, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void BuildVariableBodyWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
byteBlock.WriteUInt16(this.MessageId, EndianType.Big);
|
||||
foreach (var item in this.ReturnCodes)
|
||||
{
|
||||
byteBlock.WriteByte((byte)item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnpackWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
this.MessageId = byteBlock.ReadUInt16(EndianType.Big);
|
||||
while (!this.EndOfByteBlock(byteBlock))
|
||||
{
|
||||
this.m_returnCodes.Add((MqttReasonCode)byteBlock.ReadByte());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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.Collections.Generic;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个MQTT订阅消息。
|
||||
/// </summary>
|
||||
public sealed partial class MqttSubscribeMessage : MqttIdentifierMessage
|
||||
{
|
||||
private readonly List<SubscribeRequest> m_subscribeRequests = new List<SubscribeRequest>();
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="MqttSubscribeMessage"/> 类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="subscribes">订阅请求的数组。</param>
|
||||
public MqttSubscribeMessage(params SubscribeRequest[] subscribes)
|
||||
{
|
||||
this.m_subscribeRequests.AddRange(subscribes);
|
||||
this.SetFixedFlagsWith0010();
|
||||
}
|
||||
|
||||
internal MqttSubscribeMessage()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override MqttMessageType MessageType => MqttMessageType.Subscribe;
|
||||
|
||||
/// <summary>
|
||||
/// 获取订阅请求的只读列表。
|
||||
/// </summary>
|
||||
public IReadOnlyList<SubscribeRequest> SubscribeRequests => this.m_subscribeRequests;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void BuildVariableBodyWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
byteBlock.WriteUInt16(this.MessageId, EndianType.Big);
|
||||
foreach (var item in this.m_subscribeRequests)
|
||||
{
|
||||
MqttExtension.WriteMqttInt16String(ref byteBlock, item.Topic);
|
||||
byteBlock.WriteByte((byte)item.QosLevel);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override int GetMinimumRemainingLength()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void SetFlags(byte value)
|
||||
{
|
||||
base.SetFlags(value);
|
||||
|
||||
this.ThrowIfFlagsNot0010();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnpackWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
this.MessageId = byteBlock.ReadUInt16(EndianType.Big);
|
||||
|
||||
while (!this.EndOfByteBlock(byteBlock))
|
||||
{
|
||||
var topic = MqttExtension.ReadMqttInt16String(ref byteBlock);
|
||||
var options = byteBlock.ReadByte();
|
||||
this.m_subscribeRequests.Add(new SubscribeRequest(topic, options));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示 MQTT 协议的取消订阅确认消息。
|
||||
/// </summary>
|
||||
public sealed partial class MqttUnsubAckMessage : MqttIdentifierMessage
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override MqttMessageType MessageType => MqttMessageType.UnsubAck;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void BuildVariableBodyWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
byteBlock.WriteUInt16(this.MessageId, EndianType.Big);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override int GetMinimumRemainingLength()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnpackWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
this.MessageId = byteBlock.ReadUInt16(EndianType.Big);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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.Collections.Generic;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示 MQTT 卸载消息。
|
||||
/// </summary>
|
||||
public sealed partial class MqttUnsubscribeMessage : MqttIdentifierMessage
|
||||
{
|
||||
private readonly List<string> m_topicFilters = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="MqttUnsubscribeMessage"/> 类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="topics">要取消订阅的主题。</param>
|
||||
public MqttUnsubscribeMessage(params string[] topics)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentNullExceptionIf(topics, nameof(topics));
|
||||
|
||||
if (topics.Length < 1)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentOutOfRangeException_LessThan(nameof(topics), topics.Length, 1);
|
||||
}
|
||||
|
||||
this.m_topicFilters.AddRange(topics);
|
||||
this.SetFixedFlagsWith0010();
|
||||
}
|
||||
|
||||
internal MqttUnsubscribeMessage()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override MqttMessageType MessageType => MqttMessageType.Unsubscribe;
|
||||
|
||||
/// <summary>
|
||||
/// 获取要取消订阅的主题过滤器列表。
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> TopicFilters => this.m_topicFilters;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void BuildVariableBodyWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
byteBlock.WriteUInt16(this.MessageId, EndianType.Big);
|
||||
foreach (var topicFilter in this.TopicFilters)
|
||||
{
|
||||
MqttExtension.WriteMqttInt16String(ref byteBlock, topicFilter);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override int GetMinimumRemainingLength()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void SetFlags(byte value)
|
||||
{
|
||||
base.SetFlags(value);
|
||||
this.ThrowIfFlagsNot0010();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnpackWithMqtt3<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
this.MessageId = byteBlock.ReadUInt16(EndianType.Big);
|
||||
while (!this.EndOfByteBlock(byteBlock))
|
||||
{
|
||||
this.m_topicFilters.Add(MqttExtension.ReadMqttInt16String(ref byteBlock));
|
||||
}
|
||||
}
|
||||
}
|
||||
238
src/TouchSocket.Mqtt/MqttMessages/V5/MqttConnAckMessage_v5.cs
Normal file
238
src/TouchSocket.Mqtt/MqttMessages/V5/MqttConnAckMessage_v5.cs
Normal file
@@ -0,0 +1,238 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 TouchSocket.Mqtt;
|
||||
|
||||
public partial class MqttConnAckMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取或设置分配的客户端标识符。
|
||||
/// </summary>
|
||||
public string AssignedClientIdentifier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置认证数据。
|
||||
/// </summary>
|
||||
public byte[] AuthenticationData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置认证方法。
|
||||
/// </summary>
|
||||
public string AuthenticationMethod { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置最大数据包大小。
|
||||
/// </summary>
|
||||
public uint MaximumPacketSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置最大服务质量(QoS)级别。
|
||||
/// </summary>
|
||||
public QosLevel MaximumQoS { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置原因代码。
|
||||
/// </summary>
|
||||
public MqttReasonCode ReasonCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置原因字符串。
|
||||
/// </summary>
|
||||
public string ReasonString { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置接收最大值。
|
||||
/// </summary>
|
||||
public ushort ReceiveMaximum { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置响应信息。
|
||||
/// </summary>
|
||||
public string ResponseInformation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置一个值,该值指示是否可用保留。
|
||||
/// </summary>
|
||||
public bool RetainAvailable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置服务器保持连接时间。
|
||||
/// </summary>
|
||||
public ushort ServerKeepAlive { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置服务器引用。
|
||||
/// </summary>
|
||||
public string ServerReference { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置会话过期时间间隔。
|
||||
/// </summary>
|
||||
public uint SessionExpiryInterval { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置一个值,该值指示是否可用共享订阅。
|
||||
/// </summary>
|
||||
public bool SharedSubscriptionAvailable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置一个值,该值指示是否可用订阅标识符。
|
||||
/// </summary>
|
||||
public bool SubscriptionIdentifiersAvailable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置主题别名最大值。
|
||||
/// </summary>
|
||||
public ushort TopicAliasMaximum { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置一个值,该值指示是否可用通配符订阅。
|
||||
/// </summary>
|
||||
public bool WildcardSubscriptionAvailable { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void BuildVariableBodyWithMqtt5<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
byteBlock.WriteByte(this.m_connectAcknowledgeFlags);
|
||||
byteBlock.WriteByte((byte)this.ReturnCode);
|
||||
|
||||
var variableByteIntegerRecorder = new VariableByteIntegerRecorder();
|
||||
variableByteIntegerRecorder.CheckOut(ref byteBlock);
|
||||
|
||||
MqttExtension.WriteSessionExpiryInterval(ref byteBlock, this.SessionExpiryInterval);
|
||||
MqttExtension.WriteReceiveMaximum(ref byteBlock, this.ReceiveMaximum);
|
||||
MqttExtension.WriteMaximumQoS(ref byteBlock, this.MaximumQoS);
|
||||
MqttExtension.WriteRetainAvailable(ref byteBlock, this.RetainAvailable);
|
||||
MqttExtension.WriteMaximumPacketSize(ref byteBlock, this.MaximumPacketSize);
|
||||
MqttExtension.WriteAssignedClientIdentifier(ref byteBlock, this.AssignedClientIdentifier);
|
||||
MqttExtension.WriteTopicAliasMaximum(ref byteBlock, this.TopicAliasMaximum);
|
||||
MqttExtension.WriteReasonString(ref byteBlock, this.ReasonString);
|
||||
MqttExtension.WriteWildcardSubscriptionAvailable(ref byteBlock, this.WildcardSubscriptionAvailable);
|
||||
MqttExtension.WriteSubscriptionIdentifiersAvailable(ref byteBlock, this.SubscriptionIdentifiersAvailable);
|
||||
MqttExtension.WriteSharedSubscriptionAvailable(ref byteBlock, this.SharedSubscriptionAvailable);
|
||||
MqttExtension.WriteServerKeepAlive(ref byteBlock, this.ServerKeepAlive);
|
||||
MqttExtension.WriteResponseInformation(ref byteBlock, this.ResponseInformation);
|
||||
MqttExtension.WriteServerReference(ref byteBlock, this.ServerReference);
|
||||
MqttExtension.WriteAuthenticationMethod(ref byteBlock, this.AuthenticationMethod);
|
||||
MqttExtension.WriteAuthenticationData(ref byteBlock, this.AuthenticationData);
|
||||
MqttExtension.WriteUserProperties(ref byteBlock, this.UserProperties);
|
||||
|
||||
variableByteIntegerRecorder.CheckIn(ref byteBlock);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnpackWithMqtt5<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
this.m_connectAcknowledgeFlags = byteBlock.ReadByte();
|
||||
this.ReturnCode = (MqttReasonCode)byteBlock.ReadByte();
|
||||
|
||||
var propertiesReader = new MqttV5PropertiesReader<TByteBlock>(ref byteBlock);
|
||||
|
||||
while (propertiesReader.TryRead(ref byteBlock, out var mqttPropertyId))
|
||||
{
|
||||
switch (mqttPropertyId)
|
||||
{
|
||||
case MqttPropertyId.SessionExpiryInterval:
|
||||
{
|
||||
this.SessionExpiryInterval = propertiesReader.ReadSessionExpiryInterval(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.ReceiveMaximum:
|
||||
{
|
||||
this.ReceiveMaximum = propertiesReader.ReadReceiveMaximum(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.MaximumQoS:
|
||||
{
|
||||
this.MaximumQoS = propertiesReader.ReadMaximumQoS(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.RetainAvailable:
|
||||
{
|
||||
this.RetainAvailable = propertiesReader.ReadRetainAvailable(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.MaximumPacketSize:
|
||||
{
|
||||
this.MaximumPacketSize = propertiesReader.ReadMaximumPacketSize(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.AssignedClientIdentifier:
|
||||
{
|
||||
this.AssignedClientIdentifier = propertiesReader.ReadAssignedClientIdentifier(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.TopicAliasMaximum:
|
||||
{
|
||||
this.TopicAliasMaximum = propertiesReader.ReadTopicAliasMaximum(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.ReasonString:
|
||||
{
|
||||
this.ReasonString = propertiesReader.ReadReasonString(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.WildcardSubscriptionAvailable:
|
||||
{
|
||||
this.WildcardSubscriptionAvailable = propertiesReader.ReadWildcardSubscriptionAvailable(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.SubscriptionIdentifiersAvailable:
|
||||
{
|
||||
this.SubscriptionIdentifiersAvailable = propertiesReader.ReadSubscriptionIdentifiersAvailable(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.SharedSubscriptionAvailable:
|
||||
{
|
||||
this.SharedSubscriptionAvailable = propertiesReader.ReadSharedSubscriptionAvailable(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
|
||||
case MqttPropertyId.ServerKeepAlive:
|
||||
{
|
||||
this.ServerKeepAlive = propertiesReader.ReadServerKeepAlive(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.ResponseInformation:
|
||||
{
|
||||
this.ResponseInformation = propertiesReader.ReadResponseInformation(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.ServerReference:
|
||||
{
|
||||
this.ServerReference = propertiesReader.ReadServerReference(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.AuthenticationMethod:
|
||||
{
|
||||
this.AuthenticationMethod = propertiesReader.ReadAuthenticationMethod(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.AuthenticationData:
|
||||
{
|
||||
this.AuthenticationData = propertiesReader.ReadAuthenticationData(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.UserProperty:
|
||||
{
|
||||
this.AddUserProperty(propertiesReader.ReadUserProperty(ref byteBlock));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ThrowHelper.ThrowInvalidEnumArgumentException(mqttPropertyId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
348
src/TouchSocket.Mqtt/MqttMessages/V5/MqttConnectMessage_v5.cs
Normal file
348
src/TouchSocket.Mqtt/MqttMessages/V5/MqttConnectMessage_v5.cs
Normal file
@@ -0,0 +1,348 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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.Collections.Generic;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
public partial class MqttConnectMessage
|
||||
{
|
||||
private List<MqttUserProperty> m_willUserProperties;
|
||||
|
||||
#region Mqtt Properties
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置认证数据。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// MQTT 5.0.0以上
|
||||
/// </remarks>
|
||||
public byte[] AuthenticationData { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置认证方法。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// MQTT 5.0.0以上
|
||||
/// </remarks>
|
||||
public string AuthenticationMethod { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置最大数据包大小。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// MQTT 5.0.0以上
|
||||
/// </remarks>
|
||||
public uint MaximumPacketSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置接收最大值。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// MQTT 5.0.0以上
|
||||
/// </remarks>
|
||||
public ushort ReceiveMaximum { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置请求问题信息标志。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// MQTT 5.0.0以上
|
||||
/// </remarks>
|
||||
public bool RequestProblemInformation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置请求响应信息标志。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// MQTT 5.0.0以上
|
||||
/// </remarks>
|
||||
public bool RequestResponseInformation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置会话过期时间间隔。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// MQTT 5.0.0以上
|
||||
/// </remarks>
|
||||
public uint SessionExpiryInterval { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置主题别名最大值。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// MQTT 5.0.0以上
|
||||
/// </remarks>
|
||||
public ushort TopicAliasMaximum { get; private set; }
|
||||
|
||||
#endregion Mqtt Properties
|
||||
|
||||
#region Will Properties
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置遗嘱内容类型。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// MQTT 5.0.0以上
|
||||
/// </remarks>
|
||||
public string WillContentType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置遗嘱关联数据。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// MQTT 5.0.0以上
|
||||
/// </remarks>
|
||||
public byte[] WillCorrelationData { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置遗嘱延迟时间间隔。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// MQTT 5.0.0以上
|
||||
/// </remarks>
|
||||
public uint WillDelayInterval { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置遗嘱消息过期时间间隔。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// MQTT 5.0.0以上
|
||||
/// </remarks>
|
||||
public uint WillMessageExpiryInterval { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置遗嘱负载格式指示符。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// MQTT 5.0.0以上
|
||||
/// </remarks>
|
||||
public MqttPayloadFormatIndicator WillPayloadFormatIndicator { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置遗嘱响应主题。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// MQTT 5.0.0以上
|
||||
/// </remarks>
|
||||
public string WillResponseTopic { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取遗嘱用户属性列表。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// MQTT 5.0.0以上
|
||||
/// </remarks>
|
||||
public IReadOnlyList<MqttUserProperty> WillUserProperties => this.m_willUserProperties ?? MqttUtility.EmptyUserProperties;
|
||||
|
||||
#endregion Will Properties
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示是否新开会话。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// MQTT 5.0.0以上
|
||||
/// </remarks>
|
||||
public bool CleanStart => this.m_connectFlags.GetBit(1);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void BuildVariableBodyWithMqtt5<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
MqttExtension.WriteMqttInt16String(ref byteBlock, this.ProtocolName);
|
||||
byteBlock.WriteByte((byte)this.Version);
|
||||
byteBlock.WriteByte(this.m_connectFlags);
|
||||
byteBlock.WriteUInt16(this.KeepAlive, EndianType.Big);
|
||||
|
||||
#region Properties
|
||||
|
||||
MqttExtension.WriteSessionExpiryInterval(ref byteBlock, this.SessionExpiryInterval);
|
||||
MqttExtension.WriteAuthenticationMethod(ref byteBlock, this.AuthenticationMethod);
|
||||
MqttExtension.WriteAuthenticationData(ref byteBlock, this.AuthenticationData);
|
||||
MqttExtension.WriteReceiveMaximum(ref byteBlock, this.ReceiveMaximum);
|
||||
MqttExtension.WriteTopicAliasMaximum(ref byteBlock, this.TopicAliasMaximum);
|
||||
MqttExtension.WriteMaximumPacketSize(ref byteBlock, this.MaximumPacketSize);
|
||||
MqttExtension.WriteRequestResponseInformation(ref byteBlock, this.RequestResponseInformation);
|
||||
MqttExtension.WriteRequestProblemInformation(ref byteBlock, this.RequestProblemInformation);
|
||||
MqttExtension.WriteUserProperties(ref byteBlock, this.UserProperties);
|
||||
|
||||
#endregion Properties
|
||||
|
||||
MqttExtension.WriteMqttInt16String(ref byteBlock, this.ClientId);
|
||||
if (this.WillFlag)
|
||||
{
|
||||
MqttExtension.WritePayloadFormatIndicator(ref byteBlock, this.WillPayloadFormatIndicator);
|
||||
MqttExtension.WriteMessageExpiryInterval(ref byteBlock, this.WillMessageExpiryInterval);
|
||||
MqttExtension.WriteResponseTopic(ref byteBlock, this.WillResponseTopic);
|
||||
MqttExtension.WriteCorrelationData(ref byteBlock, this.WillCorrelationData);
|
||||
MqttExtension.WriteContentType(ref byteBlock, this.WillContentType);
|
||||
MqttExtension.WriteWillDelayInterval(ref byteBlock, this.WillDelayInterval);
|
||||
MqttExtension.WriteMqttInt16String(ref byteBlock, this.WillTopic);
|
||||
MqttExtension.WriteMqttInt16String(ref byteBlock, this.WillMessage);
|
||||
MqttExtension.WriteUserProperties(ref byteBlock, this.WillUserProperties);
|
||||
}
|
||||
|
||||
if (this.UserNameFlag)
|
||||
{
|
||||
MqttExtension.WriteMqttInt16String(ref byteBlock, this.UserName);
|
||||
}
|
||||
if (this.PasswordFlag)
|
||||
{
|
||||
MqttExtension.WriteMqttInt16String(ref byteBlock, this.Password);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnpackWithMqtt5<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
#region Properties
|
||||
|
||||
var propertiesReader = new MqttV5PropertiesReader<TByteBlock>(ref byteBlock);
|
||||
|
||||
while (propertiesReader.TryRead(ref byteBlock, out var mqttPropertyId))
|
||||
{
|
||||
switch (mqttPropertyId)
|
||||
{
|
||||
case MqttPropertyId.SessionExpiryInterval:
|
||||
{
|
||||
this.SessionExpiryInterval = propertiesReader.ReadSessionExpiryInterval(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.AuthenticationMethod:
|
||||
{
|
||||
this.AuthenticationMethod = propertiesReader.ReadAuthenticationMethod(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.AuthenticationData:
|
||||
{
|
||||
this.AuthenticationData = propertiesReader.ReadAuthenticationData(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.ReceiveMaximum:
|
||||
{
|
||||
this.ReceiveMaximum = propertiesReader.ReadReceiveMaximum(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.TopicAliasMaximum:
|
||||
{
|
||||
this.TopicAliasMaximum = propertiesReader.ReadTopicAliasMaximum(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.MaximumPacketSize:
|
||||
{
|
||||
this.MaximumPacketSize = propertiesReader.ReadMaximumPacketSize(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.RequestResponseInformation:
|
||||
{
|
||||
this.RequestResponseInformation = propertiesReader.ReadRequestResponseInformation(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.RequestProblemInformation:
|
||||
{
|
||||
this.RequestProblemInformation = propertiesReader.ReadRequestProblemInformation(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.UserProperty:
|
||||
{
|
||||
this.AddUserProperty(propertiesReader.ReadUserProperty(ref byteBlock));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ThrowHelper.ThrowInvalidEnumArgumentException(mqttPropertyId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//this.SessionExpiryInterval = propertiesReader.SessionExpiryInterval;
|
||||
//this.AuthenticationMethod = propertiesReader.AuthenticationMethod;
|
||||
//this.AuthenticationData = propertiesReader.AuthenticationData;
|
||||
//this.ReceiveMaximum = propertiesReader.ReceiveMaximum;
|
||||
//this.TopicAliasMaximum = propertiesReader.TopicAliasMaximum;
|
||||
//this.MaximumPacketSize = propertiesReader.MaximumPacketSize;
|
||||
//this.RequestResponseInformation = propertiesReader.RequestResponseInformation;
|
||||
//this.RequestProblemInformation = propertiesReader.RequestProblemInformation;
|
||||
//this.UserProperties = propertiesReader.UserProperties;
|
||||
|
||||
#endregion Properties
|
||||
|
||||
this.ClientId = MqttExtension.ReadMqttInt16String(ref byteBlock);
|
||||
if (this.WillFlag)
|
||||
{
|
||||
var willPropertiesReader = new MqttV5PropertiesReader<TByteBlock>(ref byteBlock);
|
||||
|
||||
while (willPropertiesReader.TryRead(ref byteBlock, out var mqttPropertyId))
|
||||
{
|
||||
switch (mqttPropertyId)
|
||||
{
|
||||
case MqttPropertyId.PayloadFormatIndicator:
|
||||
{
|
||||
this.WillPayloadFormatIndicator = willPropertiesReader.ReadPayloadFormatIndicator(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.MessageExpiryInterval:
|
||||
{
|
||||
this.WillMessageExpiryInterval = willPropertiesReader.ReadMessageExpiryInterval(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.ResponseTopic:
|
||||
{
|
||||
this.WillResponseTopic = willPropertiesReader.ReadResponseTopic(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.CorrelationData:
|
||||
{
|
||||
this.WillCorrelationData = willPropertiesReader.ReadCorrelationData(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.ContentType:
|
||||
{
|
||||
this.WillContentType = willPropertiesReader.ReadContentType(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.WillDelayInterval:
|
||||
{
|
||||
this.WillDelayInterval = willPropertiesReader.ReadWillDelayInterval(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
case MqttPropertyId.UserProperty:
|
||||
{
|
||||
this.AddWillUserProperties(willPropertiesReader.ReadUserProperty(ref byteBlock));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ThrowHelper.ThrowInvalidEnumArgumentException(mqttPropertyId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.WillTopic = MqttExtension.ReadMqttInt16String(ref byteBlock);
|
||||
this.WillMessage = MqttExtension.ReadMqttInt16String(ref byteBlock);
|
||||
}
|
||||
|
||||
if (this.UserNameFlag)
|
||||
{
|
||||
this.UserName = MqttExtension.ReadMqttInt16String(ref byteBlock);
|
||||
}
|
||||
|
||||
if (this.PasswordFlag)
|
||||
{
|
||||
this.Password = MqttExtension.ReadMqttInt16String(ref byteBlock);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddWillUserProperties(MqttUserProperty mqttUserProperty)
|
||||
{
|
||||
this.m_willUserProperties ??= new List<MqttUserProperty>();
|
||||
this.m_willUserProperties.Add(mqttUserProperty);
|
||||
}
|
||||
}
|
||||
101
src/TouchSocket.Mqtt/MqttMessages/V5/MqttDisconnectMessage_v5.cs
Normal file
101
src/TouchSocket.Mqtt/MqttMessages/V5/MqttDisconnectMessage_v5.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
/// <summary>
|
||||
/// 表示MQTT断开连接消息。
|
||||
/// </summary>
|
||||
public partial class MqttDisconnectMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取或设置断开连接的原因代码。
|
||||
/// </summary>
|
||||
public MqttReasonCode ReasonCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置断开连接的原因字符串。
|
||||
/// </summary>
|
||||
public string ReasonString { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置服务器引用。
|
||||
/// </summary>
|
||||
public string ServerReference { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置会话到期时间间隔。
|
||||
/// </summary>
|
||||
public uint SessionExpiryInterval { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void BuildVariableBodyWithMqtt5<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
byteBlock.WriteByte((byte)this.ReasonCode);
|
||||
|
||||
var variableByteIntegerRecorder = new VariableByteIntegerRecorder();
|
||||
variableByteIntegerRecorder.CheckOut(ref byteBlock);
|
||||
|
||||
MqttExtension.WriteServerReference(ref byteBlock, this.ServerReference);
|
||||
MqttExtension.WriteReasonString(ref byteBlock, this.ReasonString);
|
||||
MqttExtension.WriteSessionExpiryInterval(ref byteBlock, this.SessionExpiryInterval);
|
||||
MqttExtension.WriteUserProperties(ref byteBlock, this.UserProperties);
|
||||
|
||||
variableByteIntegerRecorder.CheckIn(ref byteBlock);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnpackWithMqtt5<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
this.ReasonCode = (MqttReasonCode)byteBlock.ReadByte();
|
||||
|
||||
var propertiesReader = new MqttV5PropertiesReader<TByteBlock>(ref byteBlock);
|
||||
|
||||
while (propertiesReader.TryRead(ref byteBlock, out var mqttPropertyId))
|
||||
{
|
||||
switch (mqttPropertyId)
|
||||
{
|
||||
case MqttPropertyId.ServerReference:
|
||||
{
|
||||
this.ServerReference = propertiesReader.ReadServerReference(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
|
||||
case MqttPropertyId.ReasonString:
|
||||
{
|
||||
this.ReasonString = propertiesReader.ReadReasonString(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
|
||||
case MqttPropertyId.SessionExpiryInterval:
|
||||
{
|
||||
this.SessionExpiryInterval = propertiesReader.ReadSessionExpiryInterval(ref byteBlock);
|
||||
break;
|
||||
}
|
||||
|
||||
case MqttPropertyId.UserProperty:
|
||||
{
|
||||
this.AddUserProperty(propertiesReader.ReadUserProperty(ref byteBlock));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//this.ServerReference = propertiesReader.ServerReference;
|
||||
//this.ReasonString = propertiesReader.ReasonString;
|
||||
//this.SessionExpiryInterval = propertiesReader.SessionExpiryInterval;
|
||||
//this.UserProperties = propertiesReader.UserProperties;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
public partial class MqttPingReqMessage
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
protected override void BuildVariableBodyWithMqtt5<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnpackWithMqtt5<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace TouchSocket.Mqtt;
|
||||
|
||||
public partial class MqttPingRespMessage
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
protected override void BuildVariableBodyWithMqtt5<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnpackWithMqtt5<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
81
src/TouchSocket.Mqtt/MqttMessages/V5/MqttPubAckMessage_v5.cs
Normal file
81
src/TouchSocket.Mqtt/MqttMessages/V5/MqttPubAckMessage_v5.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 TouchSocket.Mqtt;
|
||||
|
||||
public partial class MqttPubAckMessage
|
||||
{
|
||||
public MqttReasonCode ReasonCode { get; set; }
|
||||
|
||||
public string ReasonString { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void BuildVariableBodyWithMqtt5<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
byteBlock.WriteUInt16(this.MessageId, EndianType.Big);
|
||||
if (this.ReasonCode != MqttReasonCode.Success || this.ReasonString.HasValue() || this.UserProperties.Count > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
byteBlock.WriteByte((byte)this.ReasonCode);
|
||||
|
||||
var variableByteIntegerRecorder = new VariableByteIntegerRecorder();
|
||||
variableByteIntegerRecorder.CheckOut(ref byteBlock);
|
||||
MqttExtension.WriteReasonString(ref byteBlock, this.ReasonString);
|
||||
MqttExtension.WriteUserProperties(ref byteBlock, this.UserProperties);
|
||||
variableByteIntegerRecorder.CheckIn(ref byteBlock);
|
||||
|
||||
//this.ReasonString = propertiesReader.ReasonString;
|
||||
//this.UserProperties = propertiesReader.UserProperties;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UnpackWithMqtt5<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
this.MessageId = byteBlock.ReadUInt16(EndianType.Big);
|
||||
if (this.EndOfByteBlock(byteBlock))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.ReasonCode = (MqttReasonCode)byteBlock.ReadByte();
|
||||
|
||||
if (this.EndOfByteBlock(byteBlock))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var propertiesReader = new Mqtt.MqttV5PropertiesReader<TByteBlock>(ref byteBlock);
|
||||
|
||||
while (propertiesReader.TryRead(ref byteBlock, out var mqttPropertyId))
|
||||
{
|
||||
switch (mqttPropertyId)
|
||||
{
|
||||
case MqttPropertyId.ReasonString:
|
||||
this.ReasonString = propertiesReader.ReadReasonString(ref byteBlock);
|
||||
break;
|
||||
|
||||
case MqttPropertyId.UserProperty:
|
||||
this.AddUserProperty(propertiesReader.ReadUserProperty(ref byteBlock));
|
||||
break;
|
||||
|
||||
default:
|
||||
ThrowHelper.ThrowInvalidEnumArgumentException(mqttPropertyId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user