diff --git a/BootstrapBlazor.Extensions.sln b/BootstrapBlazor.Extensions.sln
deleted file mode 100644
index c5d4f29b..00000000
--- a/BootstrapBlazor.Extensions.sln
+++ /dev/null
@@ -1,655 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 18
-VisualStudioVersion = 18.0.10828.68
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "components", "components", "{FF1089BE-C704-4374-B629-C57C08E1798F}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.BarcodeGenerator", "src\components\BootstrapBlazor.BarcodeGenerator\BootstrapBlazor.BarcodeGenerator.csproj", "{0E6F09A3-61C4-4945-9DC8-5E00CE547DBD}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.FileSystem", "src\components\BootstrapBlazor.FileSystem\BootstrapBlazor.FileSystem.csproj", "{203B9DE4-201C-4223-AD0E-4823777AB9A7}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.FileViewer", "src\components\BootstrapBlazor.FileViewer\BootstrapBlazor.FileViewer.csproj", "{3EBD90E6-5237-4BB0-9C14-CC1FC5956A1B}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.ImageCropper", "src\components\BootstrapBlazor.ImageCropper\BootstrapBlazor.ImageCropper.csproj", "{079E4289-E2C6-47A9-98C2-9193F8A5D415}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.ImageHelper", "src\components\BootstrapBlazor.ImageHelper\BootstrapBlazor.ImageHelper.csproj", "{BF6F0A50-93DA-4F40-9936-933FEBF3B5CA}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.Maps", "src\components\BootstrapBlazor.Maps\BootstrapBlazor.Maps.csproj", "{A6FE7E0D-04EB-4E74-9C28-0B0920BEF7D4}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.MindMap", "src\components\BootstrapBlazor.MindMap\BootstrapBlazor.MindMap.csproj", "{72D0DB26-0437-499F-ACD6-4F90EA8513ED}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.OCR", "src\components\BootstrapBlazor.OCR\BootstrapBlazor.OCR.csproj", "{F979AFA9-E3CF-43DF-A7EA-3C63213D3C72}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.OfdReader", "src\components\BootstrapBlazor.OfdReader\BootstrapBlazor.OfdReader.csproj", "{C1BD1FA6-BFD5-42E3-ADB5-4D4BA51653BA}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.OnScreenKeyboard", "src\components\BootstrapBlazor.OnScreenKeyboard\BootstrapBlazor.OnScreenKeyboard.csproj", "{21A163D4-DC81-4B87-BA1B-9B5F48D66587}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.OpenAI", "src\components\BootstrapBlazor.OpenAI.GPT3\BootstrapBlazor.OpenAI.csproj", "{4E88785F-AD6A-4B57-BEC1-89F63A51BB63}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.PdfReader", "src\components\BootstrapBlazor.PdfReader\BootstrapBlazor.PdfReader.csproj", "{D84EF7E9-2A5E-4EA8-BC2B-54289276A16D}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.SignaturePad", "src\components\BootstrapBlazor.SignaturePad\BootstrapBlazor.SignaturePad.csproj", "{CC30AFF5-305E-43C8-B230-1A82AE3041F2}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.VideoPlayer", "src\components\BootstrapBlazor.VideoPlayer\BootstrapBlazor.VideoPlayer.csproj", "{CE85D4DD-6F4A-49AF-B575-2429FC545E7B}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.Viewer", "src\components\BootstrapBlazor.Viewer\BootstrapBlazor.Viewer.csproj", "{98BF777B-3AC1-4169-8633-F69D9A6EB275}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.WebAPI", "src\components\BootstrapBlazor.WebAPI\BootstrapBlazor.WebAPI.csproj", "{AB6EE78F-B778-42BA-91AB-234AC4F60C16}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.AntDesignIcon", "src\components\BootstrapBlazor.AntDesignIcon\BootstrapBlazor.AntDesignIcon.csproj", "{7FFCC03C-147B-4257-A035-2A31A807F173}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.AzureOpenAI", "src\components\BootstrapBlazor.AzureOpenAI\BootstrapBlazor.AzureOpenAI.csproj", "{C546E031-1A92-409F-8E9C-4FD9A15FC042}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.AzureSpeech", "src\components\BootstrapBlazor.AzureSpeech\BootstrapBlazor.AzureSpeech.csproj", "{26DEC31C-40C2-4F3B-9A9D-2BEA313AA59C}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.AzureTranslator", "src\components\BootstrapBlazor.AzureTranslator\BootstrapBlazor.AzureTranslator.csproj", "{65181D1B-AD7C-4223-B230-6AB95EC1DA19}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.BaiduOcr", "src\components\BootstrapBlazor.BaiduOcr\BootstrapBlazor.BaiduOcr.csproj", "{FBC02566-6622-43DC-B105-A0C95E9D94EB}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.BaiduSpeech", "src\components\BootstrapBlazor.BaiduSpeech\BootstrapBlazor.BaiduSpeech.csproj", "{14EBAC86-E192-4BFA-80C1-91F91BDDE6A4}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.BarCode", "src\components\BootstrapBlazor.BarCode\BootstrapBlazor.BarCode.csproj", "{A5F1444E-D86F-4740-BADD-EF042193FD92}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.BootstrapIcon", "src\components\BootstrapBlazor.BootstrapIcon\BootstrapBlazor.BootstrapIcon.csproj", "{C78E90E8-47EC-4A1E-A108-D0CD018CE417}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.Chart", "src\components\BootstrapBlazor.Chart\BootstrapBlazor.Chart.csproj", "{A9D480F3-7D56-4E82-A71A-3378DE18A2D2}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.CherryMarkdown", "src\components\BootstrapBlazor.CherryMarkdown\BootstrapBlazor.CherryMarkdown.csproj", "{A6B947E9-450C-45D7-BDC0-AE3C12D3FBA3}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.CodeEditor", "src\components\BootstrapBlazor.CodeEditor\BootstrapBlazor.CodeEditor.csproj", "{80A0135B-6F5B-4CB0-90F5-0060D241136A}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.Dock", "src\components\BootstrapBlazor.Dock\BootstrapBlazor.Dock.csproj", "{B0D2BBC7-D1BB-4F7C-ADE0-59BEBF3BB5D0}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.DockView", "src\components\BootstrapBlazor.DockView\BootstrapBlazor.DockView.csproj", "{4B71D8F7-CF9C-44CE-B371-45FBFEE0FB6E}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.DriverJs", "src\components\BootstrapBlazor.DriverJs\BootstrapBlazor.DriverJs.csproj", "{F538D58D-58B1-4BA2-A933-E91AC168F568}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.ElementIcon", "src\components\BootstrapBlazor.ElementIcon\BootstrapBlazor.ElementIcon.csproj", "{BB03EAB9-9B5F-49E2-87CC-E61A06AF2DE7}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.FloatingUI", "src\components\BootstrapBlazor.FloatingUI\BootstrapBlazor.FloatingUI.csproj", "{309D35E1-8B17-4488-93BC-D6B301B1F853}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.FontAwesome", "src\components\BootstrapBlazor.FontAwesome\BootstrapBlazor.FontAwesome.csproj", "{CC7C4278-BC2E-49D0-BE18-BF8D28EFE6E7}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.Gantt", "src\components\BootstrapBlazor.Gantt\BootstrapBlazor.Gantt.csproj", "{BB7FBAC6-0F98-4443-B3A0-B7D0F4164D20}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.Holiday", "src\components\BootstrapBlazor.Holiday\BootstrapBlazor.Holiday.csproj", "{162A2C8E-767C-41A7-9020-729E79C4F1F9}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.Html2Pdf", "src\components\BootstrapBlazor.Html2Pdf\BootstrapBlazor.Html2Pdf.csproj", "{E386EBD8-0028-42B7-8473-F26014C0E326}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.IconPark", "src\components\BootstrapBlazor.IconPark\BootstrapBlazor.IconPark.csproj", "{8B374E31-BBC3-4765-82C7-E124C4E1E731}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.Live2DDisplay", "src\components\BootstrapBlazor.Live2DDisplay\BootstrapBlazor.Live2DDisplay.csproj", "{496554A7-52B0-4E01-9CF3-08F0A2B2FCD8}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.Markdown", "src\components\BootstrapBlazor.Markdown\BootstrapBlazor.Markdown.csproj", "{7C875492-41A2-401D-90B1-161BCD5103DC}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.MaterialDesign", "src\components\BootstrapBlazor.MaterialDesign\BootstrapBlazor.MaterialDesign.csproj", "{126D5C86-2C33-4E79-A762-DBEA6E7A779E}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.MouseFollower", "src\components\BootstrapBlazor.MouseFollower\BootstrapBlazor.MouseFollower.csproj", "{7DD55FF1-54BF-4B4C-9B74-603C79D9EC07}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.Player", "src\components\BootstrapBlazor.Player\BootstrapBlazor.Player.csproj", "{2D0F834A-522F-4CF3-A6BB-BC2585D8505E}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.Sortable", "src\components\BootstrapBlazor.Sortable\BootstrapBlazor.Sortable.csproj", "{9F61C73D-EFF4-4226-ADEF-4B32F03AA371}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.Splitting", "src\components\BootstrapBlazor.Splitting\BootstrapBlazor.Splitting.csproj", "{4D0A145A-029E-4DD9-841F-302E8B2AB143}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.SummerNote", "src\components\BootstrapBlazor.SummerNote\BootstrapBlazor.SummerNote.csproj", "{150033CC-28E8-47B2-9704-F6203964B5F1}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.TableExport", "src\components\BootstrapBlazor.TableExport\BootstrapBlazor.TableExport.csproj", "{AC89886C-4216-4D20-B8DD-CFEAFC6AB2C8}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.TagHelper", "src\components\BootstrapBlazor.TagHelper\BootstrapBlazor.TagHelper.csproj", "{45F1D4EB-AFB6-484B-90D3-DB684B76E693}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.Topology", "src\components\BootstrapBlazor.Topology\BootstrapBlazor.Topology.csproj", "{2D702BA7-AB23-46BD-9F54-7F7E28322790}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.WinBox", "src\components\BootstrapBlazor.WinBox\BootstrapBlazor.WinBox.csproj", "{A8113292-7E73-417A-8E23-9F69F5577816}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "configurations", "configurations", "{3A0D5F7E-6701-437A-9EE7-05516FBCEA1B}"
- ProjectSection(SolutionItems) = preProject
- Directory.Build.props = Directory.Build.props
- exclusion.dic = exclusion.dic
- EndProjectSection
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "extensions", "extensions", "{7B29E81D-92DE-46C8-8EDC-1B48C8F12BC2}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.DataAccess.EntityFrameworkCore", "src\extensions\BootstrapBlazor.DataAccess.EntityFrameworkCore\BootstrapBlazor.DataAccess.EntityFrameworkCore.csproj", "{2662E3C2-F511-4DE0-AA72-FE453031993F}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.DataAccess.FreeSql", "src\extensions\BootstrapBlazor.DataAccess.FreeSql\BootstrapBlazor.DataAccess.FreeSql.csproj", "{9BFFB509-9B57-4FE7-B2A1-4D6AFEEE0067}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.DataAccess.PetaPoco", "src\extensions\BootstrapBlazor.DataAccess.PetaPoco\BootstrapBlazor.DataAccess.PetaPoco.csproj", "{2E338270-C302-4E13-A681-7F80E1BEE158}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.DataAccess.SqlSugar", "src\extensions\BootstrapBlazor.DataAccess.SqlSugar\BootstrapBlazor.DataAccess.SqlSugar.csproj", "{750E2849-EA95-4849-A7E7-09920E30765D}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "middelware", "middelware", "{BB06AD5B-3F2B-4B59-A135-36AF999A614F}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B6A98ADE-D26A-4D0B-8978-AB7AC915F5AE}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTestEditor", "test\UnitTestEditor\UnitTestEditor.csproj", "{37979C83-5E5F-4525-85D7-DA20B27F831F}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTestHoliday", "test\UnitTestHoliday\UnitTestHoliday.csproj", "{22594AD2-3E36-4984-949C-8B578B402EDE}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A48E5EAB-6EFB-4A78-810E-A3E79CF749F5}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "props", "props", "{FF8B3A19-60DC-4802-9FA0-9FE670C7D565}"
- ProjectSection(SolutionItems) = preProject
- src\.gitignore = src\.gitignore
- src\Directory.Build.props = src\Directory.Build.props
- src\Directory.Build.targets = src\Directory.Build.targets
- src\Frameworks.props = src\Frameworks.props
- src\Logo.props = src\Logo.props
- src\readme.md = src\readme.md
- src\SourceLink.targets = src\SourceLink.targets
- EndProjectSection
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Middleware", "src\middleware\BootstrapBlazor.Middleware\BootstrapBlazor.Middleware.csproj", "{2D783293-CEBD-4365-B8A1-0F9E76D73295}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Html2Pdf.Select", "src\components\BootstrapBlazor.Html2Pdf.Select\BootstrapBlazor.Html2Pdf.Select.csproj", "{11FD9DF2-3F3F-4AF1-8C85-446190F7D887}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.SvgEditor", "src\components\BootstrapBlazor.SVGEditor\BootstrapBlazor.SvgEditor.csproj", "{29E47F4A-CE03-42B5-BDAA-FB4B40D4C897}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Mermaid", "src\components\BootstrapBlazor.Mermaid\BootstrapBlazor.Mermaid.csproj", "{DA2198E1-2CA9-EE53-926B-7950AB4B5EBF}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.MeiliSearch", "src\components\BootstrapBlazor.MeiliSearch\BootstrapBlazor.MeiliSearch.csproj", "{4B086A62-5F5A-47BC-921F-35803F26DD68}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.RDKit", "src\components\BootstrapBlazor.RDKit\BootstrapBlazor.RDKit.csproj", "{7328E464-AE3C-4277-BEC3-422C56637066}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.SmilesDrawer", "src\components\BootstrapBlazor.SmilesDrawer\BootstrapBlazor.SmilesDrawer.csproj", "{84823875-1B07-4CCE-A009-29AEF90C6C10}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.OctIcon", "src\components\BootstrapBlazor.OctIcon\BootstrapBlazor.OctIcon.csproj", "{AA4EDA37-1D81-4235-A7F6-F1C112B364EF}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.IP2Region", "src\components\BootstrapBlazor.IP2Region\BootstrapBlazor.IP2Region.csproj", "{8A46FEDF-FD76-48AB-9BB2-47254C7623A4}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.JuHeIpLocatorProvider", "src\components\BootstrapBlazor.JuHeIpLocatorProvider\BootstrapBlazor.JuHeIpLocatorProvider.csproj", "{C882891F-4A6A-C9BE-AC96-227764A4A46A}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Html2Image", "src\components\BootstrapBlazor.Html2Image\BootstrapBlazor.Html2Image.csproj", "{F184D96E-7855-4E3B-B447-D09DBC1C91C6}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.UniverSheet", "src\components\BootstrapBlazor.UniverSheet\BootstrapBlazor.UniverSheet.csproj", "{E30AAB64-BF28-4960-89C1-1F521025F531}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.UniverIcon", "src\components\BootstrapBlazor.UniverIcon\BootstrapBlazor.UniverIcon.csproj", "{A657E04C-1495-439E-BC2E-1EEAB2D1B4DA}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.ChatBot", "src\components\BootstrapBlazor.ChatBot\BootstrapBlazor.ChatBot.csproj", "{2F37FBF4-5C1C-4493-B614-0E8361432621}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Authenticator", "src\components\BootstrapBlazor.Authenticator\BootstrapBlazor.Authenticator.csproj", "{1FDDF0AD-7AB6-4706-A183-26C680817BB4}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Graph", "src\components\BootstrapBlazor.Graph\BootstrapBlazor.Graph.csproj", "{CED55D86-57CF-CB0D-E880-370C44C0DB1F}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Html2Pdf.Playwright", "src\components\BootstrapBlazor.Html2Pdf.Playwright\BootstrapBlazor.Html2Pdf.Playwright.csproj", "{F3043A78-1942-4524-BDC4-7E88F56DF3D5}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.JitsiMeet", "src\components\BootstrapBlazor.JitsiMeet\BootstrapBlazor.JitsiMeet.csproj", "{08458CA3-BF81-48E8-870D-9389DC037808}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.PdfViewer", "src\components\BootstrapBlazor.PdfViewer\BootstrapBlazor.PdfViewer.csproj", "{4757B038-70E4-40B0-9B73-700EE5632B07}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Vditor", "src\components\BootstrapBlazor.Vditor\BootstrapBlazor.Vditor.csproj", "{D417E1B9-D146-4983-81D0-79F3193B322B}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.OfficeViewer", "src\components\BootstrapBlazor.OfficeViewer\BootstrapBlazor.OfficeViewer.csproj", "{2436940C-5920-D801-8A81-721F4C20A355}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Socket", "src\extensions\BootstrapBlazor.Socket\BootstrapBlazor.Socket.csproj", "{965F1512-57DC-4621-9C74-E059A14BB866}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.TcpSocket", "src\extensions\BootstrapBlazor.TcpSocket\BootstrapBlazor.TcpSocket.csproj", "{3F7C6E3F-5AC2-4B13-A57F-9329E34C1F5C}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestTcpSocket", "test\UnitTestTcpSocket\UnitTestTcpSocket.csproj", "{10D35EE5-FA31-4C80-B113-CD7A0FB76B4E}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestSvgIcon", "test\UnitTestSvgIcon\UnitTestSvgIcon.csproj", "{7CAD5915-CE3E-31ED-B1AC-15C61C3ED8C3}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.FluentSystemIcon", "src\components\BootstrapBlazor.FluentSystemIcon\BootstrapBlazor.FluentSystemIcon.csproj", "{30449D30-0B4E-40FD-85BE-C9BAAC820162}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.OpcUa", "src\components\BootstrapBlazor.OpcUa\BootstrapBlazor.OpcUa.csproj", "{6114A9DF-9DF5-474E-A5B0-25CDF0268B52}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestOpcUa", "test\UnitTestOpcUa\UnitTestOpcUa.csproj", "{98373A64-E224-4715-AE02-A8C6DAFF3338}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.OpcDa", "src\extensions\BootstrapBlazor.OpcDa\BootstrapBlazor.OpcDa.csproj", "{01007B10-7C3C-4136-83FF-981CA39AD3D4}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestOpcDa", "test\UnitTestOpcDa\UnitTestOpcDa.csproj", "{835C8BA9-A9CC-4EA0-9002-34A20F8B2E86}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Tasks.Dashboard", "src\components\BootstrapBlazor.Tasks.Dashboard\BootstrapBlazor.Tasks.Dashboard.csproj", "{30C57119-C564-401C-AE3A-6203E2733E1A}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {0E6F09A3-61C4-4945-9DC8-5E00CE547DBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {0E6F09A3-61C4-4945-9DC8-5E00CE547DBD}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {0E6F09A3-61C4-4945-9DC8-5E00CE547DBD}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {0E6F09A3-61C4-4945-9DC8-5E00CE547DBD}.Release|Any CPU.Build.0 = Release|Any CPU
- {203B9DE4-201C-4223-AD0E-4823777AB9A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {203B9DE4-201C-4223-AD0E-4823777AB9A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {203B9DE4-201C-4223-AD0E-4823777AB9A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {203B9DE4-201C-4223-AD0E-4823777AB9A7}.Release|Any CPU.Build.0 = Release|Any CPU
- {3EBD90E6-5237-4BB0-9C14-CC1FC5956A1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {3EBD90E6-5237-4BB0-9C14-CC1FC5956A1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {3EBD90E6-5237-4BB0-9C14-CC1FC5956A1B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {3EBD90E6-5237-4BB0-9C14-CC1FC5956A1B}.Release|Any CPU.Build.0 = Release|Any CPU
- {079E4289-E2C6-47A9-98C2-9193F8A5D415}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {079E4289-E2C6-47A9-98C2-9193F8A5D415}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {079E4289-E2C6-47A9-98C2-9193F8A5D415}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {079E4289-E2C6-47A9-98C2-9193F8A5D415}.Release|Any CPU.Build.0 = Release|Any CPU
- {BF6F0A50-93DA-4F40-9936-933FEBF3B5CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {BF6F0A50-93DA-4F40-9936-933FEBF3B5CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {BF6F0A50-93DA-4F40-9936-933FEBF3B5CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {BF6F0A50-93DA-4F40-9936-933FEBF3B5CA}.Release|Any CPU.Build.0 = Release|Any CPU
- {A6FE7E0D-04EB-4E74-9C28-0B0920BEF7D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A6FE7E0D-04EB-4E74-9C28-0B0920BEF7D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A6FE7E0D-04EB-4E74-9C28-0B0920BEF7D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A6FE7E0D-04EB-4E74-9C28-0B0920BEF7D4}.Release|Any CPU.Build.0 = Release|Any CPU
- {72D0DB26-0437-499F-ACD6-4F90EA8513ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {72D0DB26-0437-499F-ACD6-4F90EA8513ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {72D0DB26-0437-499F-ACD6-4F90EA8513ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {72D0DB26-0437-499F-ACD6-4F90EA8513ED}.Release|Any CPU.Build.0 = Release|Any CPU
- {F979AFA9-E3CF-43DF-A7EA-3C63213D3C72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F979AFA9-E3CF-43DF-A7EA-3C63213D3C72}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F979AFA9-E3CF-43DF-A7EA-3C63213D3C72}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F979AFA9-E3CF-43DF-A7EA-3C63213D3C72}.Release|Any CPU.Build.0 = Release|Any CPU
- {C1BD1FA6-BFD5-42E3-ADB5-4D4BA51653BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C1BD1FA6-BFD5-42E3-ADB5-4D4BA51653BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C1BD1FA6-BFD5-42E3-ADB5-4D4BA51653BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C1BD1FA6-BFD5-42E3-ADB5-4D4BA51653BA}.Release|Any CPU.Build.0 = Release|Any CPU
- {21A163D4-DC81-4B87-BA1B-9B5F48D66587}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {21A163D4-DC81-4B87-BA1B-9B5F48D66587}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {21A163D4-DC81-4B87-BA1B-9B5F48D66587}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {21A163D4-DC81-4B87-BA1B-9B5F48D66587}.Release|Any CPU.Build.0 = Release|Any CPU
- {4E88785F-AD6A-4B57-BEC1-89F63A51BB63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {4E88785F-AD6A-4B57-BEC1-89F63A51BB63}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {4E88785F-AD6A-4B57-BEC1-89F63A51BB63}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {4E88785F-AD6A-4B57-BEC1-89F63A51BB63}.Release|Any CPU.Build.0 = Release|Any CPU
- {D84EF7E9-2A5E-4EA8-BC2B-54289276A16D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D84EF7E9-2A5E-4EA8-BC2B-54289276A16D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D84EF7E9-2A5E-4EA8-BC2B-54289276A16D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D84EF7E9-2A5E-4EA8-BC2B-54289276A16D}.Release|Any CPU.Build.0 = Release|Any CPU
- {CC30AFF5-305E-43C8-B230-1A82AE3041F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {CC30AFF5-305E-43C8-B230-1A82AE3041F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {CC30AFF5-305E-43C8-B230-1A82AE3041F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {CC30AFF5-305E-43C8-B230-1A82AE3041F2}.Release|Any CPU.Build.0 = Release|Any CPU
- {CE85D4DD-6F4A-49AF-B575-2429FC545E7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {CE85D4DD-6F4A-49AF-B575-2429FC545E7B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {CE85D4DD-6F4A-49AF-B575-2429FC545E7B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {CE85D4DD-6F4A-49AF-B575-2429FC545E7B}.Release|Any CPU.Build.0 = Release|Any CPU
- {98BF777B-3AC1-4169-8633-F69D9A6EB275}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {98BF777B-3AC1-4169-8633-F69D9A6EB275}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {98BF777B-3AC1-4169-8633-F69D9A6EB275}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {98BF777B-3AC1-4169-8633-F69D9A6EB275}.Release|Any CPU.Build.0 = Release|Any CPU
- {AB6EE78F-B778-42BA-91AB-234AC4F60C16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {AB6EE78F-B778-42BA-91AB-234AC4F60C16}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {AB6EE78F-B778-42BA-91AB-234AC4F60C16}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {AB6EE78F-B778-42BA-91AB-234AC4F60C16}.Release|Any CPU.Build.0 = Release|Any CPU
- {7FFCC03C-147B-4257-A035-2A31A807F173}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7FFCC03C-147B-4257-A035-2A31A807F173}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7FFCC03C-147B-4257-A035-2A31A807F173}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7FFCC03C-147B-4257-A035-2A31A807F173}.Release|Any CPU.Build.0 = Release|Any CPU
- {C546E031-1A92-409F-8E9C-4FD9A15FC042}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C546E031-1A92-409F-8E9C-4FD9A15FC042}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C546E031-1A92-409F-8E9C-4FD9A15FC042}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C546E031-1A92-409F-8E9C-4FD9A15FC042}.Release|Any CPU.Build.0 = Release|Any CPU
- {26DEC31C-40C2-4F3B-9A9D-2BEA313AA59C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {26DEC31C-40C2-4F3B-9A9D-2BEA313AA59C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {26DEC31C-40C2-4F3B-9A9D-2BEA313AA59C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {26DEC31C-40C2-4F3B-9A9D-2BEA313AA59C}.Release|Any CPU.Build.0 = Release|Any CPU
- {65181D1B-AD7C-4223-B230-6AB95EC1DA19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {65181D1B-AD7C-4223-B230-6AB95EC1DA19}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {65181D1B-AD7C-4223-B230-6AB95EC1DA19}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {65181D1B-AD7C-4223-B230-6AB95EC1DA19}.Release|Any CPU.Build.0 = Release|Any CPU
- {FBC02566-6622-43DC-B105-A0C95E9D94EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {FBC02566-6622-43DC-B105-A0C95E9D94EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {FBC02566-6622-43DC-B105-A0C95E9D94EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {FBC02566-6622-43DC-B105-A0C95E9D94EB}.Release|Any CPU.Build.0 = Release|Any CPU
- {14EBAC86-E192-4BFA-80C1-91F91BDDE6A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {14EBAC86-E192-4BFA-80C1-91F91BDDE6A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {14EBAC86-E192-4BFA-80C1-91F91BDDE6A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {14EBAC86-E192-4BFA-80C1-91F91BDDE6A4}.Release|Any CPU.Build.0 = Release|Any CPU
- {A5F1444E-D86F-4740-BADD-EF042193FD92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A5F1444E-D86F-4740-BADD-EF042193FD92}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A5F1444E-D86F-4740-BADD-EF042193FD92}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A5F1444E-D86F-4740-BADD-EF042193FD92}.Release|Any CPU.Build.0 = Release|Any CPU
- {C78E90E8-47EC-4A1E-A108-D0CD018CE417}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C78E90E8-47EC-4A1E-A108-D0CD018CE417}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C78E90E8-47EC-4A1E-A108-D0CD018CE417}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C78E90E8-47EC-4A1E-A108-D0CD018CE417}.Release|Any CPU.Build.0 = Release|Any CPU
- {A9D480F3-7D56-4E82-A71A-3378DE18A2D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A9D480F3-7D56-4E82-A71A-3378DE18A2D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A9D480F3-7D56-4E82-A71A-3378DE18A2D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A9D480F3-7D56-4E82-A71A-3378DE18A2D2}.Release|Any CPU.Build.0 = Release|Any CPU
- {A6B947E9-450C-45D7-BDC0-AE3C12D3FBA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A6B947E9-450C-45D7-BDC0-AE3C12D3FBA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A6B947E9-450C-45D7-BDC0-AE3C12D3FBA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A6B947E9-450C-45D7-BDC0-AE3C12D3FBA3}.Release|Any CPU.Build.0 = Release|Any CPU
- {80A0135B-6F5B-4CB0-90F5-0060D241136A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {80A0135B-6F5B-4CB0-90F5-0060D241136A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {80A0135B-6F5B-4CB0-90F5-0060D241136A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {80A0135B-6F5B-4CB0-90F5-0060D241136A}.Release|Any CPU.Build.0 = Release|Any CPU
- {B0D2BBC7-D1BB-4F7C-ADE0-59BEBF3BB5D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {B0D2BBC7-D1BB-4F7C-ADE0-59BEBF3BB5D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {B0D2BBC7-D1BB-4F7C-ADE0-59BEBF3BB5D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {B0D2BBC7-D1BB-4F7C-ADE0-59BEBF3BB5D0}.Release|Any CPU.Build.0 = Release|Any CPU
- {4B71D8F7-CF9C-44CE-B371-45FBFEE0FB6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {4B71D8F7-CF9C-44CE-B371-45FBFEE0FB6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {4B71D8F7-CF9C-44CE-B371-45FBFEE0FB6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {4B71D8F7-CF9C-44CE-B371-45FBFEE0FB6E}.Release|Any CPU.Build.0 = Release|Any CPU
- {F538D58D-58B1-4BA2-A933-E91AC168F568}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F538D58D-58B1-4BA2-A933-E91AC168F568}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F538D58D-58B1-4BA2-A933-E91AC168F568}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F538D58D-58B1-4BA2-A933-E91AC168F568}.Release|Any CPU.Build.0 = Release|Any CPU
- {BB03EAB9-9B5F-49E2-87CC-E61A06AF2DE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {BB03EAB9-9B5F-49E2-87CC-E61A06AF2DE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {BB03EAB9-9B5F-49E2-87CC-E61A06AF2DE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {BB03EAB9-9B5F-49E2-87CC-E61A06AF2DE7}.Release|Any CPU.Build.0 = Release|Any CPU
- {309D35E1-8B17-4488-93BC-D6B301B1F853}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {309D35E1-8B17-4488-93BC-D6B301B1F853}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {309D35E1-8B17-4488-93BC-D6B301B1F853}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {309D35E1-8B17-4488-93BC-D6B301B1F853}.Release|Any CPU.Build.0 = Release|Any CPU
- {CC7C4278-BC2E-49D0-BE18-BF8D28EFE6E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {CC7C4278-BC2E-49D0-BE18-BF8D28EFE6E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {CC7C4278-BC2E-49D0-BE18-BF8D28EFE6E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {CC7C4278-BC2E-49D0-BE18-BF8D28EFE6E7}.Release|Any CPU.Build.0 = Release|Any CPU
- {BB7FBAC6-0F98-4443-B3A0-B7D0F4164D20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {BB7FBAC6-0F98-4443-B3A0-B7D0F4164D20}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {BB7FBAC6-0F98-4443-B3A0-B7D0F4164D20}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {BB7FBAC6-0F98-4443-B3A0-B7D0F4164D20}.Release|Any CPU.Build.0 = Release|Any CPU
- {162A2C8E-767C-41A7-9020-729E79C4F1F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {162A2C8E-767C-41A7-9020-729E79C4F1F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {162A2C8E-767C-41A7-9020-729E79C4F1F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {162A2C8E-767C-41A7-9020-729E79C4F1F9}.Release|Any CPU.Build.0 = Release|Any CPU
- {E386EBD8-0028-42B7-8473-F26014C0E326}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {E386EBD8-0028-42B7-8473-F26014C0E326}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {E386EBD8-0028-42B7-8473-F26014C0E326}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {E386EBD8-0028-42B7-8473-F26014C0E326}.Release|Any CPU.Build.0 = Release|Any CPU
- {8B374E31-BBC3-4765-82C7-E124C4E1E731}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {8B374E31-BBC3-4765-82C7-E124C4E1E731}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {8B374E31-BBC3-4765-82C7-E124C4E1E731}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {8B374E31-BBC3-4765-82C7-E124C4E1E731}.Release|Any CPU.Build.0 = Release|Any CPU
- {496554A7-52B0-4E01-9CF3-08F0A2B2FCD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {496554A7-52B0-4E01-9CF3-08F0A2B2FCD8}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {496554A7-52B0-4E01-9CF3-08F0A2B2FCD8}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {496554A7-52B0-4E01-9CF3-08F0A2B2FCD8}.Release|Any CPU.Build.0 = Release|Any CPU
- {7C875492-41A2-401D-90B1-161BCD5103DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7C875492-41A2-401D-90B1-161BCD5103DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7C875492-41A2-401D-90B1-161BCD5103DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7C875492-41A2-401D-90B1-161BCD5103DC}.Release|Any CPU.Build.0 = Release|Any CPU
- {126D5C86-2C33-4E79-A762-DBEA6E7A779E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {126D5C86-2C33-4E79-A762-DBEA6E7A779E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {126D5C86-2C33-4E79-A762-DBEA6E7A779E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {126D5C86-2C33-4E79-A762-DBEA6E7A779E}.Release|Any CPU.Build.0 = Release|Any CPU
- {7DD55FF1-54BF-4B4C-9B74-603C79D9EC07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7DD55FF1-54BF-4B4C-9B74-603C79D9EC07}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7DD55FF1-54BF-4B4C-9B74-603C79D9EC07}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7DD55FF1-54BF-4B4C-9B74-603C79D9EC07}.Release|Any CPU.Build.0 = Release|Any CPU
- {2D0F834A-522F-4CF3-A6BB-BC2585D8505E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2D0F834A-522F-4CF3-A6BB-BC2585D8505E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2D0F834A-522F-4CF3-A6BB-BC2585D8505E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2D0F834A-522F-4CF3-A6BB-BC2585D8505E}.Release|Any CPU.Build.0 = Release|Any CPU
- {9F61C73D-EFF4-4226-ADEF-4B32F03AA371}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {9F61C73D-EFF4-4226-ADEF-4B32F03AA371}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {9F61C73D-EFF4-4226-ADEF-4B32F03AA371}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {9F61C73D-EFF4-4226-ADEF-4B32F03AA371}.Release|Any CPU.Build.0 = Release|Any CPU
- {4D0A145A-029E-4DD9-841F-302E8B2AB143}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {4D0A145A-029E-4DD9-841F-302E8B2AB143}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {4D0A145A-029E-4DD9-841F-302E8B2AB143}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {4D0A145A-029E-4DD9-841F-302E8B2AB143}.Release|Any CPU.Build.0 = Release|Any CPU
- {150033CC-28E8-47B2-9704-F6203964B5F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {150033CC-28E8-47B2-9704-F6203964B5F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {150033CC-28E8-47B2-9704-F6203964B5F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {150033CC-28E8-47B2-9704-F6203964B5F1}.Release|Any CPU.Build.0 = Release|Any CPU
- {AC89886C-4216-4D20-B8DD-CFEAFC6AB2C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {AC89886C-4216-4D20-B8DD-CFEAFC6AB2C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {AC89886C-4216-4D20-B8DD-CFEAFC6AB2C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {AC89886C-4216-4D20-B8DD-CFEAFC6AB2C8}.Release|Any CPU.Build.0 = Release|Any CPU
- {45F1D4EB-AFB6-484B-90D3-DB684B76E693}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {45F1D4EB-AFB6-484B-90D3-DB684B76E693}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {45F1D4EB-AFB6-484B-90D3-DB684B76E693}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {45F1D4EB-AFB6-484B-90D3-DB684B76E693}.Release|Any CPU.Build.0 = Release|Any CPU
- {2D702BA7-AB23-46BD-9F54-7F7E28322790}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2D702BA7-AB23-46BD-9F54-7F7E28322790}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2D702BA7-AB23-46BD-9F54-7F7E28322790}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2D702BA7-AB23-46BD-9F54-7F7E28322790}.Release|Any CPU.Build.0 = Release|Any CPU
- {A8113292-7E73-417A-8E23-9F69F5577816}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A8113292-7E73-417A-8E23-9F69F5577816}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A8113292-7E73-417A-8E23-9F69F5577816}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A8113292-7E73-417A-8E23-9F69F5577816}.Release|Any CPU.Build.0 = Release|Any CPU
- {2662E3C2-F511-4DE0-AA72-FE453031993F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2662E3C2-F511-4DE0-AA72-FE453031993F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2662E3C2-F511-4DE0-AA72-FE453031993F}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2662E3C2-F511-4DE0-AA72-FE453031993F}.Release|Any CPU.Build.0 = Release|Any CPU
- {9BFFB509-9B57-4FE7-B2A1-4D6AFEEE0067}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {9BFFB509-9B57-4FE7-B2A1-4D6AFEEE0067}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {9BFFB509-9B57-4FE7-B2A1-4D6AFEEE0067}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {9BFFB509-9B57-4FE7-B2A1-4D6AFEEE0067}.Release|Any CPU.Build.0 = Release|Any CPU
- {2E338270-C302-4E13-A681-7F80E1BEE158}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2E338270-C302-4E13-A681-7F80E1BEE158}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2E338270-C302-4E13-A681-7F80E1BEE158}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2E338270-C302-4E13-A681-7F80E1BEE158}.Release|Any CPU.Build.0 = Release|Any CPU
- {750E2849-EA95-4849-A7E7-09920E30765D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {750E2849-EA95-4849-A7E7-09920E30765D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {750E2849-EA95-4849-A7E7-09920E30765D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {750E2849-EA95-4849-A7E7-09920E30765D}.Release|Any CPU.Build.0 = Release|Any CPU
- {37979C83-5E5F-4525-85D7-DA20B27F831F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {37979C83-5E5F-4525-85D7-DA20B27F831F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {37979C83-5E5F-4525-85D7-DA20B27F831F}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {37979C83-5E5F-4525-85D7-DA20B27F831F}.Release|Any CPU.Build.0 = Release|Any CPU
- {22594AD2-3E36-4984-949C-8B578B402EDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {22594AD2-3E36-4984-949C-8B578B402EDE}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {22594AD2-3E36-4984-949C-8B578B402EDE}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {22594AD2-3E36-4984-949C-8B578B402EDE}.Release|Any CPU.Build.0 = Release|Any CPU
- {2D783293-CEBD-4365-B8A1-0F9E76D73295}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2D783293-CEBD-4365-B8A1-0F9E76D73295}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2D783293-CEBD-4365-B8A1-0F9E76D73295}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2D783293-CEBD-4365-B8A1-0F9E76D73295}.Release|Any CPU.Build.0 = Release|Any CPU
- {11FD9DF2-3F3F-4AF1-8C85-446190F7D887}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {11FD9DF2-3F3F-4AF1-8C85-446190F7D887}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {11FD9DF2-3F3F-4AF1-8C85-446190F7D887}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {11FD9DF2-3F3F-4AF1-8C85-446190F7D887}.Release|Any CPU.Build.0 = Release|Any CPU
- {29E47F4A-CE03-42B5-BDAA-FB4B40D4C897}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {29E47F4A-CE03-42B5-BDAA-FB4B40D4C897}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {29E47F4A-CE03-42B5-BDAA-FB4B40D4C897}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {29E47F4A-CE03-42B5-BDAA-FB4B40D4C897}.Release|Any CPU.Build.0 = Release|Any CPU
- {DA2198E1-2CA9-EE53-926B-7950AB4B5EBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {DA2198E1-2CA9-EE53-926B-7950AB4B5EBF}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {DA2198E1-2CA9-EE53-926B-7950AB4B5EBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {DA2198E1-2CA9-EE53-926B-7950AB4B5EBF}.Release|Any CPU.Build.0 = Release|Any CPU
- {4B086A62-5F5A-47BC-921F-35803F26DD68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {4B086A62-5F5A-47BC-921F-35803F26DD68}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {4B086A62-5F5A-47BC-921F-35803F26DD68}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {4B086A62-5F5A-47BC-921F-35803F26DD68}.Release|Any CPU.Build.0 = Release|Any CPU
- {7328E464-AE3C-4277-BEC3-422C56637066}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7328E464-AE3C-4277-BEC3-422C56637066}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7328E464-AE3C-4277-BEC3-422C56637066}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7328E464-AE3C-4277-BEC3-422C56637066}.Release|Any CPU.Build.0 = Release|Any CPU
- {84823875-1B07-4CCE-A009-29AEF90C6C10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {84823875-1B07-4CCE-A009-29AEF90C6C10}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {84823875-1B07-4CCE-A009-29AEF90C6C10}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {84823875-1B07-4CCE-A009-29AEF90C6C10}.Release|Any CPU.Build.0 = Release|Any CPU
- {AA4EDA37-1D81-4235-A7F6-F1C112B364EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {AA4EDA37-1D81-4235-A7F6-F1C112B364EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {AA4EDA37-1D81-4235-A7F6-F1C112B364EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {AA4EDA37-1D81-4235-A7F6-F1C112B364EF}.Release|Any CPU.Build.0 = Release|Any CPU
- {8A46FEDF-FD76-48AB-9BB2-47254C7623A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {8A46FEDF-FD76-48AB-9BB2-47254C7623A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {8A46FEDF-FD76-48AB-9BB2-47254C7623A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {8A46FEDF-FD76-48AB-9BB2-47254C7623A4}.Release|Any CPU.Build.0 = Release|Any CPU
- {C882891F-4A6A-C9BE-AC96-227764A4A46A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C882891F-4A6A-C9BE-AC96-227764A4A46A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C882891F-4A6A-C9BE-AC96-227764A4A46A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C882891F-4A6A-C9BE-AC96-227764A4A46A}.Release|Any CPU.Build.0 = Release|Any CPU
- {F184D96E-7855-4E3B-B447-D09DBC1C91C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F184D96E-7855-4E3B-B447-D09DBC1C91C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F184D96E-7855-4E3B-B447-D09DBC1C91C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F184D96E-7855-4E3B-B447-D09DBC1C91C6}.Release|Any CPU.Build.0 = Release|Any CPU
- {E30AAB64-BF28-4960-89C1-1F521025F531}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {E30AAB64-BF28-4960-89C1-1F521025F531}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {E30AAB64-BF28-4960-89C1-1F521025F531}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {E30AAB64-BF28-4960-89C1-1F521025F531}.Release|Any CPU.Build.0 = Release|Any CPU
- {A657E04C-1495-439E-BC2E-1EEAB2D1B4DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A657E04C-1495-439E-BC2E-1EEAB2D1B4DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A657E04C-1495-439E-BC2E-1EEAB2D1B4DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A657E04C-1495-439E-BC2E-1EEAB2D1B4DA}.Release|Any CPU.Build.0 = Release|Any CPU
- {2F37FBF4-5C1C-4493-B614-0E8361432621}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2F37FBF4-5C1C-4493-B614-0E8361432621}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2F37FBF4-5C1C-4493-B614-0E8361432621}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2F37FBF4-5C1C-4493-B614-0E8361432621}.Release|Any CPU.Build.0 = Release|Any CPU
- {1FDDF0AD-7AB6-4706-A183-26C680817BB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {1FDDF0AD-7AB6-4706-A183-26C680817BB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {1FDDF0AD-7AB6-4706-A183-26C680817BB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {1FDDF0AD-7AB6-4706-A183-26C680817BB4}.Release|Any CPU.Build.0 = Release|Any CPU
- {CED55D86-57CF-CB0D-E880-370C44C0DB1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {CED55D86-57CF-CB0D-E880-370C44C0DB1F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {CED55D86-57CF-CB0D-E880-370C44C0DB1F}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {CED55D86-57CF-CB0D-E880-370C44C0DB1F}.Release|Any CPU.Build.0 = Release|Any CPU
- {F3043A78-1942-4524-BDC4-7E88F56DF3D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F3043A78-1942-4524-BDC4-7E88F56DF3D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F3043A78-1942-4524-BDC4-7E88F56DF3D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F3043A78-1942-4524-BDC4-7E88F56DF3D5}.Release|Any CPU.Build.0 = Release|Any CPU
- {08458CA3-BF81-48E8-870D-9389DC037808}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {08458CA3-BF81-48E8-870D-9389DC037808}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {08458CA3-BF81-48E8-870D-9389DC037808}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {08458CA3-BF81-48E8-870D-9389DC037808}.Release|Any CPU.Build.0 = Release|Any CPU
- {4757B038-70E4-40B0-9B73-700EE5632B07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {4757B038-70E4-40B0-9B73-700EE5632B07}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {4757B038-70E4-40B0-9B73-700EE5632B07}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {4757B038-70E4-40B0-9B73-700EE5632B07}.Release|Any CPU.Build.0 = Release|Any CPU
- {D417E1B9-D146-4983-81D0-79F3193B322B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D417E1B9-D146-4983-81D0-79F3193B322B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D417E1B9-D146-4983-81D0-79F3193B322B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D417E1B9-D146-4983-81D0-79F3193B322B}.Release|Any CPU.Build.0 = Release|Any CPU
- {2436940C-5920-D801-8A81-721F4C20A355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2436940C-5920-D801-8A81-721F4C20A355}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2436940C-5920-D801-8A81-721F4C20A355}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2436940C-5920-D801-8A81-721F4C20A355}.Release|Any CPU.Build.0 = Release|Any CPU
- {965F1512-57DC-4621-9C74-E059A14BB866}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {965F1512-57DC-4621-9C74-E059A14BB866}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {965F1512-57DC-4621-9C74-E059A14BB866}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {965F1512-57DC-4621-9C74-E059A14BB866}.Release|Any CPU.Build.0 = Release|Any CPU
- {3F7C6E3F-5AC2-4B13-A57F-9329E34C1F5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {3F7C6E3F-5AC2-4B13-A57F-9329E34C1F5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {3F7C6E3F-5AC2-4B13-A57F-9329E34C1F5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {3F7C6E3F-5AC2-4B13-A57F-9329E34C1F5C}.Release|Any CPU.Build.0 = Release|Any CPU
- {10D35EE5-FA31-4C80-B113-CD7A0FB76B4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {10D35EE5-FA31-4C80-B113-CD7A0FB76B4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {10D35EE5-FA31-4C80-B113-CD7A0FB76B4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {10D35EE5-FA31-4C80-B113-CD7A0FB76B4E}.Release|Any CPU.Build.0 = Release|Any CPU
- {7CAD5915-CE3E-31ED-B1AC-15C61C3ED8C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7CAD5915-CE3E-31ED-B1AC-15C61C3ED8C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7CAD5915-CE3E-31ED-B1AC-15C61C3ED8C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7CAD5915-CE3E-31ED-B1AC-15C61C3ED8C3}.Release|Any CPU.Build.0 = Release|Any CPU
- {30449D30-0B4E-40FD-85BE-C9BAAC820162}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {30449D30-0B4E-40FD-85BE-C9BAAC820162}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {30449D30-0B4E-40FD-85BE-C9BAAC820162}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {30449D30-0B4E-40FD-85BE-C9BAAC820162}.Release|Any CPU.Build.0 = Release|Any CPU
- {6114A9DF-9DF5-474E-A5B0-25CDF0268B52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {6114A9DF-9DF5-474E-A5B0-25CDF0268B52}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {6114A9DF-9DF5-474E-A5B0-25CDF0268B52}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {6114A9DF-9DF5-474E-A5B0-25CDF0268B52}.Release|Any CPU.Build.0 = Release|Any CPU
- {98373A64-E224-4715-AE02-A8C6DAFF3338}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {98373A64-E224-4715-AE02-A8C6DAFF3338}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {98373A64-E224-4715-AE02-A8C6DAFF3338}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {98373A64-E224-4715-AE02-A8C6DAFF3338}.Release|Any CPU.Build.0 = Release|Any CPU
- {01007B10-7C3C-4136-83FF-981CA39AD3D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {01007B10-7C3C-4136-83FF-981CA39AD3D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {01007B10-7C3C-4136-83FF-981CA39AD3D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {01007B10-7C3C-4136-83FF-981CA39AD3D4}.Release|Any CPU.Build.0 = Release|Any CPU
- {835C8BA9-A9CC-4EA0-9002-34A20F8B2E86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {835C8BA9-A9CC-4EA0-9002-34A20F8B2E86}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {835C8BA9-A9CC-4EA0-9002-34A20F8B2E86}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {835C8BA9-A9CC-4EA0-9002-34A20F8B2E86}.Release|Any CPU.Build.0 = Release|Any CPU
- {30C57119-C564-401C-AE3A-6203E2733E1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {30C57119-C564-401C-AE3A-6203E2733E1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {30C57119-C564-401C-AE3A-6203E2733E1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {30C57119-C564-401C-AE3A-6203E2733E1A}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- {FF1089BE-C704-4374-B629-C57C08E1798F} = {A48E5EAB-6EFB-4A78-810E-A3E79CF749F5}
- {0E6F09A3-61C4-4945-9DC8-5E00CE547DBD} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {203B9DE4-201C-4223-AD0E-4823777AB9A7} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {3EBD90E6-5237-4BB0-9C14-CC1FC5956A1B} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {079E4289-E2C6-47A9-98C2-9193F8A5D415} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {BF6F0A50-93DA-4F40-9936-933FEBF3B5CA} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {A6FE7E0D-04EB-4E74-9C28-0B0920BEF7D4} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {72D0DB26-0437-499F-ACD6-4F90EA8513ED} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {F979AFA9-E3CF-43DF-A7EA-3C63213D3C72} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {C1BD1FA6-BFD5-42E3-ADB5-4D4BA51653BA} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {21A163D4-DC81-4B87-BA1B-9B5F48D66587} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {4E88785F-AD6A-4B57-BEC1-89F63A51BB63} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {D84EF7E9-2A5E-4EA8-BC2B-54289276A16D} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {CC30AFF5-305E-43C8-B230-1A82AE3041F2} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {CE85D4DD-6F4A-49AF-B575-2429FC545E7B} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {98BF777B-3AC1-4169-8633-F69D9A6EB275} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {AB6EE78F-B778-42BA-91AB-234AC4F60C16} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {7FFCC03C-147B-4257-A035-2A31A807F173} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {C546E031-1A92-409F-8E9C-4FD9A15FC042} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {26DEC31C-40C2-4F3B-9A9D-2BEA313AA59C} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {65181D1B-AD7C-4223-B230-6AB95EC1DA19} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {FBC02566-6622-43DC-B105-A0C95E9D94EB} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {14EBAC86-E192-4BFA-80C1-91F91BDDE6A4} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {A5F1444E-D86F-4740-BADD-EF042193FD92} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {C78E90E8-47EC-4A1E-A108-D0CD018CE417} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {A9D480F3-7D56-4E82-A71A-3378DE18A2D2} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {A6B947E9-450C-45D7-BDC0-AE3C12D3FBA3} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {80A0135B-6F5B-4CB0-90F5-0060D241136A} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {B0D2BBC7-D1BB-4F7C-ADE0-59BEBF3BB5D0} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {4B71D8F7-CF9C-44CE-B371-45FBFEE0FB6E} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {F538D58D-58B1-4BA2-A933-E91AC168F568} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {BB03EAB9-9B5F-49E2-87CC-E61A06AF2DE7} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {309D35E1-8B17-4488-93BC-D6B301B1F853} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {CC7C4278-BC2E-49D0-BE18-BF8D28EFE6E7} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {BB7FBAC6-0F98-4443-B3A0-B7D0F4164D20} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {162A2C8E-767C-41A7-9020-729E79C4F1F9} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {E386EBD8-0028-42B7-8473-F26014C0E326} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {8B374E31-BBC3-4765-82C7-E124C4E1E731} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {496554A7-52B0-4E01-9CF3-08F0A2B2FCD8} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {7C875492-41A2-401D-90B1-161BCD5103DC} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {126D5C86-2C33-4E79-A762-DBEA6E7A779E} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {7DD55FF1-54BF-4B4C-9B74-603C79D9EC07} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {2D0F834A-522F-4CF3-A6BB-BC2585D8505E} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {9F61C73D-EFF4-4226-ADEF-4B32F03AA371} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {4D0A145A-029E-4DD9-841F-302E8B2AB143} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {150033CC-28E8-47B2-9704-F6203964B5F1} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {AC89886C-4216-4D20-B8DD-CFEAFC6AB2C8} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {45F1D4EB-AFB6-484B-90D3-DB684B76E693} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {2D702BA7-AB23-46BD-9F54-7F7E28322790} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {A8113292-7E73-417A-8E23-9F69F5577816} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {7B29E81D-92DE-46C8-8EDC-1B48C8F12BC2} = {A48E5EAB-6EFB-4A78-810E-A3E79CF749F5}
- {2662E3C2-F511-4DE0-AA72-FE453031993F} = {7B29E81D-92DE-46C8-8EDC-1B48C8F12BC2}
- {9BFFB509-9B57-4FE7-B2A1-4D6AFEEE0067} = {7B29E81D-92DE-46C8-8EDC-1B48C8F12BC2}
- {2E338270-C302-4E13-A681-7F80E1BEE158} = {7B29E81D-92DE-46C8-8EDC-1B48C8F12BC2}
- {750E2849-EA95-4849-A7E7-09920E30765D} = {7B29E81D-92DE-46C8-8EDC-1B48C8F12BC2}
- {BB06AD5B-3F2B-4B59-A135-36AF999A614F} = {A48E5EAB-6EFB-4A78-810E-A3E79CF749F5}
- {37979C83-5E5F-4525-85D7-DA20B27F831F} = {B6A98ADE-D26A-4D0B-8978-AB7AC915F5AE}
- {22594AD2-3E36-4984-949C-8B578B402EDE} = {B6A98ADE-D26A-4D0B-8978-AB7AC915F5AE}
- {FF8B3A19-60DC-4802-9FA0-9FE670C7D565} = {A48E5EAB-6EFB-4A78-810E-A3E79CF749F5}
- {2D783293-CEBD-4365-B8A1-0F9E76D73295} = {BB06AD5B-3F2B-4B59-A135-36AF999A614F}
- {11FD9DF2-3F3F-4AF1-8C85-446190F7D887} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {29E47F4A-CE03-42B5-BDAA-FB4B40D4C897} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {DA2198E1-2CA9-EE53-926B-7950AB4B5EBF} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {4B086A62-5F5A-47BC-921F-35803F26DD68} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {7328E464-AE3C-4277-BEC3-422C56637066} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {84823875-1B07-4CCE-A009-29AEF90C6C10} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {AA4EDA37-1D81-4235-A7F6-F1C112B364EF} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {8A46FEDF-FD76-48AB-9BB2-47254C7623A4} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {C882891F-4A6A-C9BE-AC96-227764A4A46A} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {F184D96E-7855-4E3B-B447-D09DBC1C91C6} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {E30AAB64-BF28-4960-89C1-1F521025F531} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {A657E04C-1495-439E-BC2E-1EEAB2D1B4DA} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {2F37FBF4-5C1C-4493-B614-0E8361432621} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {1FDDF0AD-7AB6-4706-A183-26C680817BB4} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {CED55D86-57CF-CB0D-E880-370C44C0DB1F} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {F3043A78-1942-4524-BDC4-7E88F56DF3D5} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {08458CA3-BF81-48E8-870D-9389DC037808} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {4757B038-70E4-40B0-9B73-700EE5632B07} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {D417E1B9-D146-4983-81D0-79F3193B322B} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {2436940C-5920-D801-8A81-721F4C20A355} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {965F1512-57DC-4621-9C74-E059A14BB866} = {7B29E81D-92DE-46C8-8EDC-1B48C8F12BC2}
- {3F7C6E3F-5AC2-4B13-A57F-9329E34C1F5C} = {7B29E81D-92DE-46C8-8EDC-1B48C8F12BC2}
- {10D35EE5-FA31-4C80-B113-CD7A0FB76B4E} = {B6A98ADE-D26A-4D0B-8978-AB7AC915F5AE}
- {7CAD5915-CE3E-31ED-B1AC-15C61C3ED8C3} = {B6A98ADE-D26A-4D0B-8978-AB7AC915F5AE}
- {30449D30-0B4E-40FD-85BE-C9BAAC820162} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {6114A9DF-9DF5-474E-A5B0-25CDF0268B52} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {98373A64-E224-4715-AE02-A8C6DAFF3338} = {B6A98ADE-D26A-4D0B-8978-AB7AC915F5AE}
- {01007B10-7C3C-4136-83FF-981CA39AD3D4} = {7B29E81D-92DE-46C8-8EDC-1B48C8F12BC2}
- {835C8BA9-A9CC-4EA0-9002-34A20F8B2E86} = {B6A98ADE-D26A-4D0B-8978-AB7AC915F5AE}
- {30C57119-C564-401C-AE3A-6203E2733E1A} = {FF1089BE-C704-4374-B629-C57C08E1798F}
- {98054DCC-A9AB-CB11-798F-424112EC8639} = {7B29E81D-92DE-46C8-8EDC-1B48C8F12BC2}
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {D5EB1960-6F30-4CE1-B375-EAE1F787D6FF}
- EndGlobalSection
-EndGlobal
diff --git a/BootstrapBlazor.Extensions.slnx b/BootstrapBlazor.Extensions.slnx
new file mode 100644
index 00000000..eb64485c
--- /dev/null
+++ b/BootstrapBlazor.Extensions.slnx
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/extensions/BootstrapBlazor.Socket/BootstrapBlazor.Socket.csproj b/src/extensions/BootstrapBlazor.Socket/BootstrapBlazor.Socket.csproj
deleted file mode 100644
index 0cee55f8..00000000
--- a/src/extensions/BootstrapBlazor.Socket/BootstrapBlazor.Socket.csproj
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
- 9.0.14
-
-
-
- BootstrapBlazor Socket
- BootstrapBlazor extensions of Socket
-
-
-
- 8.0.*
- 9.0.*
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/extensions/BootstrapBlazor.Socket/DataAdapter/DataPackageAdapter.cs b/src/extensions/BootstrapBlazor.Socket/DataAdapter/DataPackageAdapter.cs
deleted file mode 100644
index b84d9d55..00000000
--- a/src/extensions/BootstrapBlazor.Socket/DataAdapter/DataPackageAdapter.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-namespace BootstrapBlazor.Socket.DataAdapters;
-
-///
-/// Provides a base implementation for adapting data packages between different systems or formats.
-///
-/// 实例
-/// This abstract class serves as a foundation for implementing custom data package adapters. It defines
-/// common methods for sending, receiving, and handling data packages, as well as a property for accessing the
-/// associated data package handler. Derived classes should override the virtual methods to provide specific behavior
-/// for handling data packages.
-public class DataPackageAdapter(IDataPackageHandler? DataPackageHandler = null) : IDataPackageAdapter
-{
- ///
- ///
- ///
- public Func, ValueTask>? ReceivedCallBack { get; set; }
-
- ///
- ///
- ///
- ///
- ///
- ///
- public virtual async ValueTask HandlerAsync(ReadOnlyMemory data, CancellationToken token = default)
- {
- if (DataPackageHandler != null)
- {
- DataPackageHandler.ReceivedCallBack ??= OnHandlerReceivedCallBack;
-
- // 如果存在数据处理器则调用其处理方法
- await DataPackageHandler.HandlerAsync(data, token);
- }
- }
-
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public virtual bool TryConvertTo(ReadOnlyMemory data, IDataConverter socketDataConverter, out TEntity? entity)
- {
- entity = default;
- var ret = socketDataConverter.TryConvertTo(data, out var v);
- if (ret)
- {
- entity = v;
- }
- return ret;
- }
-
- ///
- /// Handles incoming data by invoking a callback method, if one is defined.
- ///
- /// This method is designed to be overridden in derived classes to provide custom handling of
- /// incoming data. If a callback method is assigned, it will be invoked asynchronously with the provided
- /// data.
- /// The incoming data to be processed, represented as a read-only memory block of bytes.
- ///
- protected virtual async ValueTask OnHandlerReceivedCallBack(ReadOnlyMemory data)
- {
- if (ReceivedCallBack != null)
- {
- // 调用接收回调方法处理数据
- await ReceivedCallBack(data);
- }
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/DataAdapter/IDataPackageAdapter.cs b/src/extensions/BootstrapBlazor.Socket/DataAdapter/IDataPackageAdapter.cs
deleted file mode 100644
index 382d8039..00000000
--- a/src/extensions/BootstrapBlazor.Socket/DataAdapter/IDataPackageAdapter.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-namespace BootstrapBlazor.Socket.DataAdapters;
-
-///
-/// Defines an adapter for handling and transmitting data packages to a target destination.
-///
-/// This interface provides methods for sending data asynchronously and configuring a data handler.
-/// Implementations of this interface are responsible for managing the interaction between the caller and the underlying
-/// data transmission mechanism.
-public interface IDataPackageAdapter
-{
- ///
- /// Gets or sets the callback function to be invoked when data is received.
- ///
- /// The callback function is expected to handle the received data asynchronously. Ensure that the
- /// implementation of the callback does not block the calling thread and completes promptly to avoid performance
- /// issues.
- Func, ValueTask>? ReceivedCallBack { get; set; }
-
- ///
- /// Asynchronously receives data from a source and processes it.
- ///
- /// This method does not return any result directly. It is intended for scenarios where data is received
- /// and processed asynchronously. Ensure that the parameter contains valid data before calling
- /// this method.
- /// A read-only memory region containing the data to be received. The caller must ensure the memory is valid and
- /// populated.
- /// An optional cancellation token that can be used to cancel the operation. Defaults to if
- /// not provided.
- /// A representing the asynchronous operation. The task completes when the data has been
- /// successfully received and processed.
- ValueTask HandlerAsync(ReadOnlyMemory data, CancellationToken token = default);
-
- ///
- /// Attempts to convert the specified byte data into an entity of type .
- ///
- /// This method does not throw an exception if the conversion fails. Instead, it returns and sets to its default value.
- /// The type of the entity to convert the data to.
- /// The byte data to be converted.
- /// The converter used to transform the byte data into an entity.
- /// When this method returns, contains the converted entity if the conversion was successful; otherwise, the default
- /// value for the type of the entity.
- /// if the conversion was successful; otherwise, .
- bool TryConvertTo(ReadOnlyMemory data, IDataConverter socketDataConverter, out TEntity? entity);
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/DataConverter/BinConverter.cs b/src/extensions/BootstrapBlazor.Socket/DataConverter/BinConverter.cs
deleted file mode 100644
index 0c8f3b7e..00000000
--- a/src/extensions/BootstrapBlazor.Socket/DataConverter/BinConverter.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// 二进制 与 Byte 数组转换方法
-///
-public static class BinConverter
-{
- ///
- /// 将 byte[] 转为 二进制字符串
- /// Converts a byte array to its hexadecimal string representation.
- ///
- /// The byte array to convert.
- ///
- /// A string containing the hexadecimal representation of the byte array.
- public static string ToString(byte[]? bytes, string? separator = "-")
- {
- if (bytes == null || bytes.Length == 0)
- {
- return string.Empty;
- }
-
- return string.Join(separator, bytes.Select(i => Convert.ToString(i, 2).PadLeft(8, '0')));
- }
-
- ///
- /// 将字符串转换为字节数组
- ///
- ///
- ///
- ///
- ///
- public static byte[] ToBytes(string str, string? separator = null, StringSplitOptions options = StringSplitOptions.None)
- {
- // 把 str 内的 separator 符号替换掉
- if (!string.IsNullOrEmpty(separator))
- {
- str = string.Join("", str.Split(separator, options).Select(i => i.PadLeft(8, '0')));
- }
-
- // 把 Hex 形式的 str 转化为 byte[]
- if (str.Length % 8 != 0)
- {
- throw new ArgumentException("The raw string cannot have an odd number of digits. 参数 str 位数不正确无法转化为 二进制字节数组", nameof(str));
- }
-
- var bytes = new byte[str.Length / 8];
- for (var i = 0; i < bytes.Length; i++)
- {
- bytes[i] = Convert.ToByte(str.Substring(i * 8, 8), 2);
- }
- return bytes;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/DataConverter/DataConverter.cs b/src/extensions/BootstrapBlazor.Socket/DataConverter/DataConverter.cs
deleted file mode 100644
index 672efdfc..00000000
--- a/src/extensions/BootstrapBlazor.Socket/DataConverter/DataConverter.cs
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using BootstrapBlazor.Socket.Logging;
-using System.Diagnostics;
-using System.Reflection;
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Provides a base class for converting socket data into a specified entity type.
-///
-/// The type of entity to convert the socket data into.
-public class DataConverter(DataConverterCollections converters) : IDataConverter
-{
- ///
- /// 构造函数
- ///
- public DataConverter() : this(new())
- {
- }
-
- ///
- ///
- ///
- ///
- ///
- ///
- public virtual bool TryConvertTo(ReadOnlyMemory data, [NotNullWhen(true)] out TEntity? entity)
- {
- var ret = false;
- entity = default;
- try
- {
- var v = CreateEntity();
- if (Parse(data, v))
- {
- entity = v;
- ret = true;
- }
- }
- catch (Exception ex)
- {
- SocketLogging.LogError(ex, $"DataConverter {nameof(TryConvertTo)} failed");
- }
- return ret;
- }
-
- ///
- /// 创建实体实例方法
- ///
- ///
- protected virtual TEntity CreateEntity() => Activator.CreateInstance();
-
- ///
- /// 将字节数据转换为指定实体类型的实例。
- ///
- ///
- ///
- protected virtual bool Parse(ReadOnlyMemory data, TEntity entity)
- {
- // 使用 SocketDataPropertyAttribute 特性获取数据转换规则
- var ret = false;
- if (entity != null)
- {
- // 通过 SocketDataPropertyConverterAttribute 特性获取属性转换器
- var properties = entity.GetType().GetProperties().Where(p => p.CanWrite).ToList();
- if (Debugger.IsAttached)
- {
- SocketLogging.LogDebug($"Data: {BitConverter.ToString(data.ToArray())}");
- }
- foreach (var p in properties)
- {
- var attr = p.GetCustomAttribute(false) ?? GetPropertyConverterAttribute(p);
- if (attr is { Type: not null })
- {
- var value = attr.ConvertTo(data);
- var valueType = value?.GetType();
- if (p.PropertyType.IsAssignableFrom(valueType))
- {
- p.SetValue(entity, value);
- }
- else
- {
- SocketLogging.LogInformation($"{nameof(Parse)} failed. Start: {attr.Offset}. Length: {attr.Length}. Can't convert value from {GetValueType(valueType)} to {p.Name}({p.PropertyType})");
- }
- }
- }
- ret = true;
- }
- return ret;
- }
-
- private static string GetValueType(Type? type) => type?.FullName ?? "NULL";
-
- private DataPropertyConverterAttribute? GetPropertyConverterAttribute(PropertyInfo propertyInfo)
- {
- DataPropertyConverterAttribute? attr = null;
- if (converters.TryGetPropertyConverter(propertyInfo, out var v))
- {
- attr = v;
- }
- return attr;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/DataConverter/DataConverterCollections.cs b/src/extensions/BootstrapBlazor.Socket/DataConverter/DataConverterCollections.cs
deleted file mode 100644
index d0fc4654..00000000
--- a/src/extensions/BootstrapBlazor.Socket/DataConverter/DataConverterCollections.cs
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Collections.Concurrent;
-using System.Linq.Expressions;
-using System.Reflection;
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// 数据转换器集合类
-///
-public sealed class DataConverterCollections
-{
- readonly ConcurrentDictionary _converters = new();
- readonly ConcurrentDictionary _propertyConverters = new();
-
- ///
- /// 增加指定 数据类型转换器方法
- ///
- ///
- ///
- public void AddTypeConverter(IDataConverter converter)
- {
- var type = typeof(TEntity);
- _converters.AddOrUpdate(type, t => converter, (t, v) => converter);
- }
-
- ///
- /// 增加默认数据类型转换器方法 转换器使用
- ///
- ///
- public void AddTypeConverter() => AddTypeConverter(new DataConverter(this));
-
- ///
- /// 添加属性类型转化器方法
- ///
- ///
- ///
- ///
- public void AddPropertyConverter(Expression> propertyExpression, DataPropertyConverterAttribute attribute)
- {
- if (propertyExpression.Body is MemberExpression memberExpression)
- {
- if (attribute.Type == null)
- {
- attribute.Type = memberExpression.Type;
- }
- _propertyConverters.AddOrUpdate(memberExpression.Member, m => attribute, (m, v) => attribute);
- }
- }
-
- ///
- /// 获得指定数据类型转换器方法
- ///
- ///
- public bool TryGetTypeConverter([NotNullWhen(true)] out IDataConverter? converter)
- {
- converter = null;
- var ret = false;
- if (_converters.TryGetValue(typeof(TEntity), out var v) && v is IDataConverter c)
- {
- converter = c;
- ret = true;
- }
- return ret;
- }
-
- ///
- /// 获得指定数据类型属性转换器方法
- ///
- ///
- public bool TryGetPropertyConverter(Expression> propertyExpression, [NotNullWhen(true)] out DataPropertyConverterAttribute? converterAttribute)
- {
- converterAttribute = null;
- var ret = false;
- if (propertyExpression.Body is MemberExpression memberExpression && TryGetPropertyConverter(memberExpression.Member, out var v))
- {
- converterAttribute = v;
- ret = true;
- }
- return ret;
- }
-
- ///
- /// 获得指定数据类型属性转换器方法
- ///
- ///
- public bool TryGetPropertyConverter(MemberInfo memberInfo, [NotNullWhen(true)] out DataPropertyConverterAttribute? converterAttribute)
- {
- converterAttribute = null;
- var ret = false;
- if (_propertyConverters.TryGetValue(memberInfo, out var v))
- {
- converterAttribute = v;
- ret = true;
- }
- return ret;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/DataConverter/DataPropertyConverterAttribute.cs b/src/extensions/BootstrapBlazor.Socket/DataConverter/DataPropertyConverterAttribute.cs
deleted file mode 100644
index 4f714946..00000000
--- a/src/extensions/BootstrapBlazor.Socket/DataConverter/DataPropertyConverterAttribute.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Represents an attribute used to mark a field as a socket data field.
-///
-/// This attribute can be applied to fields to indicate that they are part of the data transmitted over a
-/// socket connection. It is intended for use in scenarios where socket communication requires specific fields to be
-/// identified for processing.
-[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
-public class DataPropertyConverterAttribute : Attribute
-{
- ///
- /// 获得/设置 数据类型
- ///
- public Type? Type { get; set; }
-
- ///
- /// 获得/设置 数据偏移量
- ///
- public int Offset { get; set; }
-
- ///
- /// 获得/设置 数据长度
- ///
- public int Length { get; set; }
-
- ///
- /// 获得/设置 数据编码名称
- ///
- public string? EncodingName { get; set; }
-
- ///
- /// 获得/设置 数据转换器类型
- ///
- public Type? ConverterType { get; set; }
-
- ///
- /// 获得/设置 数据转换器构造函数参数
- ///
- public object?[]? ConverterParameters { get; set; }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/DataConverter/DataTypeConverterAttribute.cs b/src/extensions/BootstrapBlazor.Socket/DataConverter/DataTypeConverterAttribute.cs
deleted file mode 100644
index 37bc80bc..00000000
--- a/src/extensions/BootstrapBlazor.Socket/DataConverter/DataTypeConverterAttribute.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-///
-///
-[AttributeUsage(AttributeTargets.Class)]
-public class DataTypeConverterAttribute : Attribute
-{
- ///
- /// Gets or sets the type of the .
- ///
- public Type? Type { get; set; }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/DataConverter/HexConverter.cs b/src/extensions/BootstrapBlazor.Socket/DataConverter/HexConverter.cs
deleted file mode 100644
index 3b74ef3f..00000000
--- a/src/extensions/BootstrapBlazor.Socket/DataConverter/HexConverter.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// 十六进制 与 Byte 数组转换方法
-///
-public static class HexConverter
-{
- ///
- /// 将 byte[] 转为 16 进制字符串
- /// Converts a byte array to its hexadecimal string representation.
- ///
- /// The byte array to convert.
- ///
- ///
- /// A string containing the hexadecimal representation of the byte array.
- public static string ToString(byte[]? bytes, string? separator = "-", bool upper = true)
- {
- if (bytes == null || bytes.Length == 0)
- {
- return string.Empty;
- }
-
- if (separator == "-")
- {
- return BitConverter.ToString(bytes);
- }
-
- return string.Join(separator, bytes.Select(i => upper ? i.ToString("X2") : i.ToString("x2")));
- }
-
- ///
- /// 将 byte[] 转为 16 进制字符串
- /// Converts a byte array to its hexadecimal string representation.
- ///
- ///
- ///
- ///
- ///
- public static string ToString(ReadOnlySpan span, string? separator = "-", bool upper = true) => ToString(span.ToArray(), separator, upper);
-
- ///
- /// 将字符串转换为字节数组
- ///
- ///
- ///
- ///
- ///
- public static byte[] ToBytes(string str, string? separator = null, StringSplitOptions options = StringSplitOptions.None)
- {
- // 把 str 内的 delimiter 符号替换掉
- if (!string.IsNullOrEmpty(separator))
- {
- str = string.Join("", str.Split(separator, options));
- }
-
- // 把 Hex 形式的 str 转化为 byte[]
- if (str.Length % 2 != 0)
- {
- throw new ArgumentException("The raw string cannot have an odd number of digits. 参数 str 位数不正确无法转化为 16 进制字节数组", nameof(str));
- }
-
- var bytes = new byte[str.Length / 2];
- for (var i = 0; i < bytes.Length; i++)
- {
- bytes[i] = Convert.ToByte(str.Substring(i * 2, 2), 16);
- }
- return bytes;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/DataConverter/IDataConverter.cs b/src/extensions/BootstrapBlazor.Socket/DataConverter/IDataConverter.cs
deleted file mode 100644
index c62d6590..00000000
--- a/src/extensions/BootstrapBlazor.Socket/DataConverter/IDataConverter.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Socket 数据转换器接口
-///
-public interface IDataConverter
-{
-
-}
-
-///
-/// Defines a method to convert raw socket data into a specified entity type.
-///
-/// The type of entity to convert the data into.
-public interface IDataConverter : IDataConverter
-{
- ///
- /// Attempts to convert the specified data to an instance of .
- ///
- /// This method does not throw an exception if the conversion fails. Instead, it returns and sets to .
- /// The data to be converted, represented as a read-only memory block of bytes.
- /// When this method returns, contains the converted if the conversion succeeded;
- /// otherwise, .
- /// if the conversion was successful; otherwise, .
- bool TryConvertTo(ReadOnlyMemory data, [NotNullWhen(true)] out TEntity? entity);
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/DataHandler/DataPackageHandlerBase.cs b/src/extensions/BootstrapBlazor.Socket/DataHandler/DataPackageHandlerBase.cs
deleted file mode 100644
index 17b6237f..00000000
--- a/src/extensions/BootstrapBlazor.Socket/DataHandler/DataPackageHandlerBase.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-namespace BootstrapBlazor.Socket.DataHandlers;
-
-///
-/// Provides a base implementation for handling data packages in a communication system.
-///
-/// This abstract class defines the core contract for receiving and sending data packages. Derived
-/// classes should override and extend its functionality to implement specific data handling logic. The default
-/// implementation simply returns the provided data.
-public abstract class DataPackageHandlerBase : IDataPackageHandler
-{
- private Memory _lastReceiveBuffer = Memory.Empty;
-
- ///
- ///
- ///
- public Func, ValueTask>? ReceivedCallBack { get; set; }
-
- ///
- ///
- ///
- ///
- ///
- ///
- public abstract ValueTask HandlerAsync(ReadOnlyMemory data, CancellationToken token = default);
-
- ///
- /// Handles the processing of a sticky package by adjusting the provided buffer and length.
- ///
- /// This method processes the portion of the buffer beyond the specified length and updates the
- /// internal state accordingly. The caller must ensure that the contains sufficient data
- /// for the specified .
- /// The memory buffer containing the data to process.
- /// The length of the valid data within the buffer.
- protected void SlicePackage(ReadOnlyMemory buffer, int length)
- {
- _lastReceiveBuffer = buffer[length..].ToArray().AsMemory();
- }
-
- ///
- /// Concatenates the provided buffer with any previously stored data and returns the combined result.
- ///
- /// This method combines the provided buffer with any data stored in the internal buffer. After
- /// concatenation, the internal buffer is cleared. The returned memory block is allocated from a shared memory pool
- /// and should be used promptly to avoid holding onto pooled resources.
- /// The buffer to concatenate with the previously stored data. Must not be empty.
- /// A instance containing the concatenated data. If no previously stored data exists, the
- /// method returns the input .
- protected ReadOnlyMemory ConcatBuffer(ReadOnlyMemory buffer)
- {
- if (_lastReceiveBuffer.IsEmpty)
- {
- return buffer;
- }
-
- // 计算缓存区长度
- Memory merged = new byte[_lastReceiveBuffer.Length + buffer.Length];
- _lastReceiveBuffer.CopyTo(merged);
- buffer.CopyTo(merged[_lastReceiveBuffer.Length..]);
-
- // Clear the sticky buffer
- _lastReceiveBuffer = Memory.Empty;
- return merged;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/DataHandler/DelimiterDataPackageHandler.cs b/src/extensions/BootstrapBlazor.Socket/DataHandler/DelimiterDataPackageHandler.cs
deleted file mode 100644
index d988a189..00000000
--- a/src/extensions/BootstrapBlazor.Socket/DataHandler/DelimiterDataPackageHandler.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Buffers;
-using System.Text;
-
-namespace BootstrapBlazor.Socket.DataHandlers;
-
-///
-/// Handles data packages that are delimited by a specific sequence of bytes or characters.
-///
-/// This class provides functionality for processing data packages that are separated by a defined
-/// delimiter. The delimiter can be specified as a string with an optional encoding or as a byte array.
-public class DelimiterDataPackageHandler : DataPackageHandlerBase
-{
- private readonly ReadOnlyMemory _delimiter;
-
- ///
- /// Initializes a new instance of the class with the specified delimiter
- /// and optional encoding.
- ///
- /// The string delimiter used to separate data packages. This value cannot be null or empty.
- /// The character encoding used to convert the delimiter to bytes. If null, is used as
- /// the default.
- /// Thrown if is null or empty.
- public DelimiterDataPackageHandler(string delimiter, Encoding? encoding = null)
- {
- if (string.IsNullOrEmpty(delimiter))
- {
- throw new ArgumentNullException(nameof(delimiter), "Delimiter cannot be null or empty.");
- }
-
- encoding ??= Encoding.UTF8;
- _delimiter = encoding.GetBytes(delimiter);
- }
-
- ///
- /// Initializes a new instance of the class with the specified delimiters.
- ///
- /// An array of bytes representing the delimiters used to parse data packages. Cannot be .
- /// Thrown if is .
- public DelimiterDataPackageHandler(byte[] delimiter)
- {
- _delimiter = delimiter ?? throw new ArgumentNullException(nameof(delimiter), "Delimiter cannot be null.");
- }
-
- ///
- ///
- ///
- ///
- ///
- ///
- public override async ValueTask HandlerAsync(ReadOnlyMemory data, CancellationToken token = default)
- {
- data = ConcatBuffer(data);
-
- while (data.Length > 0)
- {
- var index = data.Span.IndexOfAny(_delimiter.Span);
- var segment = index == -1 ? data : data[..index];
- var length = segment.Length + _delimiter.Length;
- using var buffer = MemoryPool.Shared.Rent(length);
- segment.CopyTo(buffer.Memory);
-
- if (index != -1)
- {
- SlicePackage(data, index + _delimiter.Length);
-
- _delimiter.CopyTo(buffer.Memory[index..]);
- if (ReceivedCallBack != null)
- {
- await ReceivedCallBack(buffer.Memory[..length].ToArray());
- }
-
- data = data[(index + _delimiter.Length)..];
- }
- else
- {
- SlicePackage(data, 0);
- break;
- }
- }
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/DataHandler/FixLengthDataPackageHandler.cs b/src/extensions/BootstrapBlazor.Socket/DataHandler/FixLengthDataPackageHandler.cs
deleted file mode 100644
index d71ba95a..00000000
--- a/src/extensions/BootstrapBlazor.Socket/DataHandler/FixLengthDataPackageHandler.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-namespace BootstrapBlazor.Socket.DataHandlers;
-
-///
-/// Handles fixed-length data packages by processing incoming data of a specified length.
-///
-/// This class is designed to handle data packages with a fixed length, as specified during
-/// initialization. It extends and overrides its behavior to process fixed-length
-/// data.
-/// The data package total data length.
-public class FixLengthDataPackageHandler(int length) : DataPackageHandlerBase
-{
- private readonly Memory _data = new byte[length];
-
- private int _receivedLength;
-
- ///
- ///
- ///
- ///
- ///
- ///
- public override async ValueTask HandlerAsync(ReadOnlyMemory data, CancellationToken token = default)
- {
- while (data.Length > 0)
- {
- // 拷贝数据
- var len = length - _receivedLength;
- var segment = data.Length > len ? data[..len] : data;
- segment.CopyTo(_data[_receivedLength..]);
-
- // 更新数据
- data = data[segment.Length..];
-
- // 更新已接收长度
- _receivedLength += segment.Length;
-
- // 如果已接收长度等于总长度则触发回调
- if (_receivedLength == length)
- {
- // 重置已接收长度
- _receivedLength = 0;
- if (ReceivedCallBack != null)
- {
- await ReceivedCallBack(_data);
- }
- }
- }
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/DataHandler/IDataPackageHandler.cs b/src/extensions/BootstrapBlazor.Socket/DataHandler/IDataPackageHandler.cs
deleted file mode 100644
index 58627a31..00000000
--- a/src/extensions/BootstrapBlazor.Socket/DataHandler/IDataPackageHandler.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-namespace BootstrapBlazor.Socket.DataHandlers;
-
-///
-/// Defines an interface for adapting data packages to and from a TCP socket connection.
-///
-/// Implementations of this interface are responsible for converting raw data received from a TCP socket
-/// into structured data packages and vice versa. This allows for custom serialization and deserialization logic
-/// tailored to specific application protocols.
-public interface IDataPackageHandler
-{
- ///
- /// Gets or sets the callback function to be invoked when data is received asynchronously.
- ///
- Func, ValueTask>? ReceivedCallBack { get; set; }
-
- ///
- /// Asynchronously receives data and processes it.
- ///
- /// The method is designed for asynchronous operations and may be used in scenarios where
- /// efficient handling of data streams is required. Ensure that the parameter contains valid
- /// data for processing, and handle potential cancellation using the .
- /// The data to be received, represented as a read-only memory block of bytes.
- /// A cancellation token that can be used to cancel the operation. Defaults to if not
- /// provided.
- /// A containing if the data was successfully received and
- /// processed; otherwise, .
- ValueTask HandlerAsync(ReadOnlyMemory data, CancellationToken token = default);
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/Extensions/ActivatorExtensions.cs b/src/extensions/BootstrapBlazor.Socket/Extensions/ActivatorExtensions.cs
deleted file mode 100644
index 371e03f5..00000000
--- a/src/extensions/BootstrapBlazor.Socket/Extensions/ActivatorExtensions.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using BootstrapBlazor.Socket.Logging;
-using System.Reflection;
-
-namespace System;
-
-///
-/// Activator 扩展方法
-///
-public static class ActivatorExtensions
-{
- ///
- /// 通过指定类型与参数创建实例方法
- ///
- ///
- ///
- ///
- public static object? CreateInstance(this Type type, object?[]? args = null)
- {
- var bindings = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
-
- object? instance = null;
- try
- {
- instance = Activator.CreateInstance(type, bindings, null, args, null);
- }
- catch (Exception ex)
- {
- SocketLogging.LogError(ex, $"Create Instance {type.FullName} failed");
- }
- return instance;
- }
-
- ///
- /// 通过指定类型与参数创建实例方法
- ///
- ///
- ///
- ///
- public static TType? CreateInstance(this Type type, object?[]? args = null)
- {
- TType? ret = default;
- var value = type.CreateInstance(args);
- if (value is TType v)
- {
- ret = v;
- }
- return ret;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/Extensions/DataPropertyExtensions.cs b/src/extensions/BootstrapBlazor.Socket/Extensions/DataPropertyExtensions.cs
deleted file mode 100644
index 8f117a73..00000000
--- a/src/extensions/BootstrapBlazor.Socket/Extensions/DataPropertyExtensions.cs
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-static class DataPropertyExtensions
-{
- public static IDataPropertyConverter? GetConverter(this DataPropertyConverterAttribute attribute)
- {
- return attribute.GetConverterByType() ?? attribute.GetDefaultConverter();
- }
-
- private static IDataPropertyConverter? GetConverterByType(this DataPropertyConverterAttribute attribute)
- {
- IDataPropertyConverter? converter = null;
- var converterType = attribute.ConverterType;
- if (converterType != null)
- {
- var converterParameters = attribute.ConverterParameters;
- converter = converterType.CreateInstance(converterParameters);
- }
- return converter;
- }
-
- private static IDataPropertyConverter? GetDefaultConverter(this DataPropertyConverterAttribute attribute)
- {
- IDataPropertyConverter? converter = null;
- var type = attribute.Type;
- if (type != null)
- {
- if (type == typeof(byte))
- {
- converter = new DataByteConverter();
- }
- else if (type == typeof(byte[]))
- {
- converter = new DataByteArrayConverter();
- }
- else if (type == typeof(string))
- {
- converter = new DataStringConverter(attribute.EncodingName);
- }
- else if (type.IsEnum)
- {
- converter = new DataEnumConverter(attribute.Type);
- }
- else if (type == typeof(bool))
- {
- converter = new DataBoolConverter();
- }
- else if (type == typeof(short))
- {
- converter = new DataInt16BigEndianConverter();
- }
- else if (type == typeof(int))
- {
- converter = new DataInt32BigEndianConverter();
- }
- else if (type == typeof(long))
- {
- converter = new DataInt64BigEndianConverter();
- }
- else if (type == typeof(float))
- {
- converter = new DataSingleBigEndianConverter();
- }
- else if (type == typeof(double))
- {
- converter = new DataDoubleBigEndianConverter();
- }
- else if (type == typeof(ushort))
- {
- converter = new DataUInt16BigEndianConverter();
- }
- else if (type == typeof(uint))
- {
- converter = new DataUInt32BigEndianConverter();
- }
- else if (type == typeof(ulong))
- {
- converter = new DataUInt64BigEndianConverter();
- }
- }
- return converter;
- }
-
- public static object? ConvertTo(this DataPropertyConverterAttribute attribute, ReadOnlyMemory data)
- {
- object? ret = null;
- var start = attribute.Offset;
- var length = attribute.Length;
-
- if (data.Length >= start + length)
- {
- var buffer = data.Slice(start, length);
- var converter = attribute.GetConverter();
- if (converter != null)
- {
- ret = converter.Convert(buffer);
- }
- }
- return ret;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/Logging/SocketLogging.cs b/src/extensions/BootstrapBlazor.Socket/Logging/SocketLogging.cs
deleted file mode 100644
index 7f7a2ce1..00000000
--- a/src/extensions/BootstrapBlazor.Socket/Logging/SocketLogging.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using Microsoft.Extensions.Logging;
-
-namespace BootstrapBlazor.Socket.Logging;
-
-///
-/// Socket 日志记录类
-///
-public static class SocketLogging
-{
- private static ILogger? _logger;
- private static bool _inited;
-
- ///
- /// 返回 是否已经初始化
- ///
- public static bool Inited => _inited;
-
- ///
- /// 初始化 ILogger 实例
- ///
- ///
- public static void Init(ILogger logger)
- {
- _inited = true;
- _logger = logger;
- }
-
- ///
- /// 记录异常信息方法
- ///
- ///
- ///
- public static void LogError(Exception ex, string? message = null) => _logger?.LogError(ex, "{message}", message);
-
- ///
- /// 记录警告信息方法
- ///
- ///
- public static void LogWarning(string message) => _logger?.LogWarning("{message}", message);
-
- ///
- /// 记录信息方法
- ///
- ///
- public static void LogInformation(string message) => _logger?.LogInformation("{message}", message);
-
- ///
- /// 记录调试信息方法
- ///
- ///
- public static void LogDebug(string message) => _logger?.LogDebug("{message}", message);
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataBoolConverter.cs b/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataBoolConverter.cs
deleted file mode 100644
index c2593e28..00000000
--- a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataBoolConverter.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Socket 数据转换为 bool 数据转换器
-///
-public class DataBoolConverter : IDataPropertyConverter
-{
- ///
- ///
- ///
- ///
- public object? Convert(ReadOnlyMemory data)
- {
- var ret = false;
- if (data.Length > 0)
- {
- ret = data.Span[0] != 0x00;
- }
- return ret;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataByteArrayConverter.cs b/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataByteArrayConverter.cs
deleted file mode 100644
index f3b0c18e..00000000
--- a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataByteArrayConverter.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Socket 数据转换为 byte[] 数组转换器
-///
-public class DataByteArrayConverter : IDataPropertyConverter
-{
- ///
- ///
- ///
- ///
- public object? Convert(ReadOnlyMemory data)
- {
- return data.ToArray();
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataByteConverter.cs b/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataByteConverter.cs
deleted file mode 100644
index c16c9566..00000000
--- a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataByteConverter.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Socket 数据转换为 byte 转换器
-///
-public class DataByteConverter : IDataPropertyConverter
-{
- ///
- ///
- ///
- ///
- public object? Convert(ReadOnlyMemory data)
- {
- return data.Length > 0 ? data.Span[0] : byte.MinValue;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataDoubleBigEndianConverter.cs b/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataDoubleBigEndianConverter.cs
deleted file mode 100644
index daed3af2..00000000
--- a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataDoubleBigEndianConverter.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Buffers.Binary;
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Socket 数据转换为 double 数据大端转换器
-///
-public class DataDoubleBigEndianConverter : IDataPropertyConverter
-{
- ///
- ///
- ///
- ///
- public object? Convert(ReadOnlyMemory data)
- {
- double ret = 0;
- if (data.Length <= 8)
- {
- Span paddedSpan = stackalloc byte[8];
- data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);
- if (BinaryPrimitives.TryReadDoubleBigEndian(paddedSpan, out var v))
- {
- ret = v;
- }
- }
- return ret;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataDoubleLittleEndianConverter.cs b/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataDoubleLittleEndianConverter.cs
deleted file mode 100644
index 63d25727..00000000
--- a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataDoubleLittleEndianConverter.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Buffers.Binary;
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Socket 数据转换为 double 数据小端转换器
-///
-public class DataDoubleLittleEndianConverter : IDataPropertyConverter
-{
- ///
- ///
- ///
- ///
- public object? Convert(ReadOnlyMemory data)
- {
- double ret = 0;
- if (data.Length <= 8)
- {
- Span paddedSpan = stackalloc byte[8];
- data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);
- if (BinaryPrimitives.TryReadDoubleLittleEndian(paddedSpan, out var v))
- {
- ret = v;
- }
- }
- return ret;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataEnumConverter.cs b/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataEnumConverter.cs
deleted file mode 100644
index f636e19f..00000000
--- a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataEnumConverter.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Socket 数据转换为 Enum 数据转换器
-///
-public class DataEnumConverter(Type? type) : IDataPropertyConverter
-{
- ///
- ///
- ///
- ///
- public object? Convert(ReadOnlyMemory data)
- {
- object? ret = null;
- if (type != null)
- {
- if (data.Length == 1)
- {
- var v = data.Span[0];
- if (Enum.TryParse(type, v.ToString(), out var enumValue))
- {
- ret = enumValue;
- }
- }
- }
- return ret;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataInt16BigEndianConverter.cs b/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataInt16BigEndianConverter.cs
deleted file mode 100644
index bbc76ef4..00000000
--- a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataInt16BigEndianConverter.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Buffers.Binary;
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Socket 数据转换为 short 数据大端转换器
-///
-public class DataInt16BigEndianConverter : IDataPropertyConverter
-{
- ///
- ///
- ///
- ///
- public object? Convert(ReadOnlyMemory data)
- {
- short ret = 0;
- if (data.Length <= 2)
- {
- Span paddedSpan = stackalloc byte[2];
- data.Span.CopyTo(paddedSpan[(2 - data.Length)..]);
- if (BinaryPrimitives.TryReadInt16BigEndian(paddedSpan, out var v))
- {
- ret = v;
- }
- }
- return ret;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataInt16LittleEndianConverter.cs b/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataInt16LittleEndianConverter.cs
deleted file mode 100644
index 3452ed51..00000000
--- a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataInt16LittleEndianConverter.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Buffers.Binary;
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Socket 数据转换为 short 数据小端转换器
-///
-public class DataInt16LittleEndianConverter : IDataPropertyConverter
-{
- ///
- ///
- ///
- ///
- public object? Convert(ReadOnlyMemory data)
- {
- short ret = 0;
- if (data.Length <= 2)
- {
- Span paddedSpan = stackalloc byte[2];
- data.Span.CopyTo(paddedSpan[(2 - data.Length)..]);
- if (BinaryPrimitives.TryReadInt16LittleEndian(paddedSpan, out var v))
- {
- ret = v;
- }
- }
- return ret;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataInt32BigEndianConverter.cs b/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataInt32BigEndianConverter.cs
deleted file mode 100644
index 445b6f59..00000000
--- a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataInt32BigEndianConverter.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Buffers.Binary;
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Socket 数据转换为 int 数据大端转换器
-///
-public class DataInt32BigEndianConverter : IDataPropertyConverter
-{
- ///
- ///
- ///
- ///
- public object? Convert(ReadOnlyMemory data)
- {
- var ret = 0;
- if (data.Length <= 4)
- {
- Span paddedSpan = stackalloc byte[4];
- data.Span.CopyTo(paddedSpan[(4 - data.Length)..]);
-
- if (BinaryPrimitives.TryReadInt32BigEndian(paddedSpan, out var v))
- {
- ret = v;
- }
- }
- return ret;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataInt32LittleEndianConverter.cs b/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataInt32LittleEndianConverter.cs
deleted file mode 100644
index f2744e07..00000000
--- a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataInt32LittleEndianConverter.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Buffers.Binary;
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Socket 数据转换为 int 数据小端转换器
-///
-public class DataInt32LittleEndianConverter : IDataPropertyConverter
-{
- ///
- ///
- ///
- ///
- public object? Convert(ReadOnlyMemory data)
- {
- var ret = 0;
- if (data.Length <= 4)
- {
- Span paddedSpan = stackalloc byte[4];
- data.Span.CopyTo(paddedSpan[(4 - data.Length)..]);
-
- if (BinaryPrimitives.TryReadInt32LittleEndian(paddedSpan, out var v))
- {
- ret = v;
- }
- }
- return ret;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataInt64BigEndianConverter.cs b/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataInt64BigEndianConverter.cs
deleted file mode 100644
index b82d5044..00000000
--- a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataInt64BigEndianConverter.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Buffers.Binary;
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Socket 数据转换为 long 数据大端转换器
-///
-public class DataInt64BigEndianConverter : IDataPropertyConverter
-{
- ///
- ///
- ///
- ///
- public object? Convert(ReadOnlyMemory data)
- {
- long ret = 0;
- if (data.Length <= 8)
- {
- Span paddedSpan = stackalloc byte[8];
- data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);
-
- if (BinaryPrimitives.TryReadInt64BigEndian(paddedSpan, out var v))
- {
- ret = v;
- }
- }
- return ret;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataInt64LittleEndianConverter.cs b/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataInt64LittleEndianConverter.cs
deleted file mode 100644
index d78a104c..00000000
--- a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataInt64LittleEndianConverter.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Buffers.Binary;
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Socket 数据转换为 long 数据小端转换器
-///
-public class DataInt64LittleEndianConverter : IDataPropertyConverter
-{
- ///
- ///
- ///
- ///
- public object? Convert(ReadOnlyMemory data)
- {
- long ret = 0;
- if (data.Length <= 8)
- {
- Span paddedSpan = stackalloc byte[8];
- data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);
-
- if (BinaryPrimitives.TryReadInt64LittleEndian(paddedSpan, out var v))
- {
- ret = v;
- }
- }
- return ret;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataSingleBigEndianConverter.cs b/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataSingleBigEndianConverter.cs
deleted file mode 100644
index a047a201..00000000
--- a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataSingleBigEndianConverter.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Buffers.Binary;
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Socket 数据转换为 float 数据大端转换器
-///
-public class DataSingleBigEndianConverter : IDataPropertyConverter
-{
- ///
- ///
- ///
- ///
- public object? Convert(ReadOnlyMemory data)
- {
- float ret = 0;
- if (data.Length <= 4)
- {
- Span paddedSpan = stackalloc byte[4];
- data.Span.CopyTo(paddedSpan[(4 - data.Length)..]);
- if (BinaryPrimitives.TryReadSingleBigEndian(paddedSpan, out var v))
- {
- ret = v;
- }
- }
- return ret;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataSingleLittleEndianConverter.cs b/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataSingleLittleEndianConverter.cs
deleted file mode 100644
index c3387816..00000000
--- a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataSingleLittleEndianConverter.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Buffers.Binary;
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Socket 数据转换为 float 数据小端转换器
-///
-public class DataSingleLittleEndianConverter : IDataPropertyConverter
-{
- ///
- ///
- ///
- ///
- public object? Convert(ReadOnlyMemory data)
- {
- float ret = 0;
- if (data.Length <= 4)
- {
- Span paddedSpan = stackalloc byte[4];
- data.Span.CopyTo(paddedSpan[(4 - data.Length)..]);
- if (BinaryPrimitives.TryReadSingleLittleEndian(paddedSpan, out var v))
- {
- ret = v;
- }
- }
- return ret;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataStringConverter.cs b/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataStringConverter.cs
deleted file mode 100644
index 6515f823..00000000
--- a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataStringConverter.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Text;
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Socket 数据转换为 string 数据转换器
-///
-public class DataStringConverter(string? encodingName) : IDataPropertyConverter
-{
- ///
- ///
- ///
- ///
- public object? Convert(ReadOnlyMemory data)
- {
- var encoding = string.IsNullOrEmpty(encodingName) ? Encoding.UTF8 : Encoding.GetEncoding(encodingName);
- return encoding.GetString(data.Span);
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataUInt16BigEndianConverter.cs b/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataUInt16BigEndianConverter.cs
deleted file mode 100644
index ec8b82be..00000000
--- a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataUInt16BigEndianConverter.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Buffers.Binary;
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Socket 数据转换为 ushort 数据大端转换器
-///
-public class DataUInt16BigEndianConverter : IDataPropertyConverter
-{
- ///
- ///
- ///
- ///
- public object? Convert(ReadOnlyMemory data)
- {
- ushort ret = 0;
- if (data.Length <= 2)
- {
- Span paddedSpan = stackalloc byte[2];
- data.Span.CopyTo(paddedSpan[(2 - data.Length)..]);
- if (BinaryPrimitives.TryReadUInt16BigEndian(paddedSpan, out var v))
- {
- ret = v;
- }
- }
- return ret;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataUInt16LittleEndianConverter.cs b/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataUInt16LittleEndianConverter.cs
deleted file mode 100644
index dae01236..00000000
--- a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataUInt16LittleEndianConverter.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Buffers.Binary;
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Socket 数据转换为 ushort 数据小端转换器
-///
-public class DataUInt16LittleEndianConverter : IDataPropertyConverter
-{
- ///
- ///
- ///
- ///
- public object? Convert(ReadOnlyMemory data)
- {
- ushort ret = 0;
- if (data.Length <= 2)
- {
- Span paddedSpan = stackalloc byte[2];
- data.Span.CopyTo(paddedSpan[(2 - data.Length)..]);
- if (BinaryPrimitives.TryReadUInt16LittleEndian(paddedSpan, out var v))
- {
- ret = v;
- }
- }
- return ret;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataUInt32BigEndianConverter.cs b/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataUInt32BigEndianConverter.cs
deleted file mode 100644
index d3b1f0a1..00000000
--- a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataUInt32BigEndianConverter.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Buffers.Binary;
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Socket 数据转换为 uint 数据大端转换器
-///
-public class DataUInt32BigEndianConverter : IDataPropertyConverter
-{
- ///
- ///
- ///
- ///
- public object? Convert(ReadOnlyMemory data)
- {
- uint ret = 0;
- if (data.Length <= 4)
- {
- Span paddedSpan = stackalloc byte[4];
- data.Span.CopyTo(paddedSpan[(4 - data.Length)..]);
- if (BinaryPrimitives.TryReadUInt32BigEndian(paddedSpan, out var v))
- {
- ret = v;
- }
- }
- return ret;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataUInt32LittleEndianConverter.cs b/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataUInt32LittleEndianConverter.cs
deleted file mode 100644
index 482f8a8d..00000000
--- a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataUInt32LittleEndianConverter.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Buffers.Binary;
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Socket 数据转换为 uint 数据小端转换器
-///
-public class DataUInt32LittleEndianConverter : IDataPropertyConverter
-{
- ///
- ///
- ///
- ///
- public object? Convert(ReadOnlyMemory data)
- {
- uint ret = 0;
- if (data.Length <= 4)
- {
- Span paddedSpan = stackalloc byte[4];
- data.Span.CopyTo(paddedSpan[(4 - data.Length)..]);
- if (BinaryPrimitives.TryReadUInt32LittleEndian(paddedSpan, out var v))
- {
- ret = v;
- }
- }
- return ret;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataUInt64BigEndianConverter.cs b/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataUInt64BigEndianConverter.cs
deleted file mode 100644
index 737812a8..00000000
--- a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataUInt64BigEndianConverter.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Buffers.Binary;
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Socket 数据转换为 ulong 数据大端转换器
-///
-public class DataUInt64BigEndianConverter : IDataPropertyConverter
-{
- ///
- ///
- ///
- ///
- public object? Convert(ReadOnlyMemory data)
- {
- ulong ret = 0;
- if (data.Length <= 8)
- {
- Span paddedSpan = stackalloc byte[8];
- data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);
- if (BinaryPrimitives.TryReadUInt64BigEndian(paddedSpan, out var v))
- {
- ret = v;
- }
- }
- return ret;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataUInt64LittleEndianConverter.cs b/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataUInt64LittleEndianConverter.cs
deleted file mode 100644
index 51255bdc..00000000
--- a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/DataUInt64LittleEndianConverter.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Buffers.Binary;
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Socket 数据转换为 ulong 数据小端转换器
-///
-public class DataUInt64LittleEndianConverter : IDataPropertyConverter
-{
- ///
- ///
- ///
- ///
- public object? Convert(ReadOnlyMemory data)
- {
- ulong ret = 0;
- if (data.Length <= 8)
- {
- Span paddedSpan = stackalloc byte[8];
- data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);
- if (BinaryPrimitives.TryReadUInt64LittleEndian(paddedSpan, out var v))
- {
- ret = v;
- }
- }
- return ret;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/IDataPropertyConverter.cs b/src/extensions/BootstrapBlazor.Socket/PropertyConverter/IDataPropertyConverter.cs
deleted file mode 100644
index c95be6ac..00000000
--- a/src/extensions/BootstrapBlazor.Socket/PropertyConverter/IDataPropertyConverter.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-namespace BootstrapBlazor.Socket.DataConverters;
-
-///
-/// Socket 数据转换器接口
-///
-public interface IDataPropertyConverter
-{
- ///
- /// 将数据转换为指定类型的对象
- ///
- ///
- ///
- object? Convert(ReadOnlyMemory data);
-}
diff --git a/src/extensions/BootstrapBlazor.Socket/Utility/ModbusCrc16.cs b/src/extensions/BootstrapBlazor.Socket/Utility/ModbusCrc16.cs
deleted file mode 100644
index 74190383..00000000
--- a/src/extensions/BootstrapBlazor.Socket/Utility/ModbusCrc16.cs
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-namespace BootstrapBlazor.Socket.Algorithm;
-
-///
-/// Modubs CRC-16 查表算法
-///
-public static class ModbusCrc16
-{
- // 预计算的CRC表(256个条目)
- private static readonly ushort[] CrcTable =
- [
- 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
- 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
- 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
- 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
- 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
- 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
- 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
- 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
- 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
- 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
- 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
- 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
- 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
- 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
- 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
- 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
- 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
- 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
- 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
- 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
- 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
- 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
- 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
- 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
- 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
- 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
- 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
- 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
- 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
- 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
- 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
- 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
- ];
-
- ///
- /// 计算 Modbus CRC-16 校验码
- ///
- /// 要计算的数据字节数组
- /// CRC校验码(低字节在前)
- public static ushort Compute(ReadOnlySpan data)
- {
- ushort crc = 0xFFFF;
- foreach (var b in data)
- {
- crc = (ushort)((crc >> 8) ^ CrcTable[(crc ^ b) & 0xFF]);
- }
- return crc;
- }
-
- ///
- /// 计算 CRC 并将结果添加到消息末尾(Modbus格式)
- ///
- /// 原始数据
- /// 带 CRC 校验码的数据
- public static ReadOnlySpan Append(ReadOnlySpan data)
- {
- ushort crc = Compute(data);
-
- // 使用 stackalloc 避免堆分配(小数据时)
- if (data.Length <= 256)
- {
- Span result = stackalloc byte[data.Length + 2];
- data.CopyTo(result);
- result[data.Length] = (byte)(crc & 0xFF);
- result[data.Length + 1] = (byte)(crc >> 8);
- return result.ToArray();
- }
- else
- {
- // 大数据使用常规数组
- byte[] result = new byte[data.Length + 2];
- data.CopyTo(result);
- result[data.Length] = (byte)(crc & 0xFF);
- result[data.Length + 1] = (byte)(crc >> 8);
- return result;
- }
- }
-
- ///
- /// 验证带 CRC 的数据是否有效
- ///
- /// 包含 CRC 校验码的数据
- /// 验证结果
- public static bool Validate(ReadOnlySpan dataWithCrc)
- {
- if (dataWithCrc.Length < 2)
- {
- return false;
- }
-
- ushort receivedCrc = (ushort)(dataWithCrc[^1] << 8 | dataWithCrc[^2]);
- ushort calculatedCrc = Compute(dataWithCrc[..^2]);
- return receivedCrc == calculatedCrc;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.TcpSocket/BootstrapBlazor.TcpSocket.csproj b/src/extensions/BootstrapBlazor.TcpSocket/BootstrapBlazor.TcpSocket.csproj
deleted file mode 100644
index 69c92032..00000000
--- a/src/extensions/BootstrapBlazor.TcpSocket/BootstrapBlazor.TcpSocket.csproj
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
- 9.0.5
-
-
-
- BootstrapBlazor Socket TcpClient
- BootstrapBlazor extensions of TcpSocket
-
-
-
- 8.0.*
- 9.0.*
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/extensions/BootstrapBlazor.TcpSocket/DefaultTcpSocketClient.cs b/src/extensions/BootstrapBlazor.TcpSocket/DefaultTcpSocketClient.cs
deleted file mode 100644
index 52486cc5..00000000
--- a/src/extensions/BootstrapBlazor.TcpSocket/DefaultTcpSocketClient.cs
+++ /dev/null
@@ -1,440 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using System.Buffers;
-using System.Net;
-using System.Runtime.Versioning;
-
-namespace BootstrapBlazor.TcpSocket;
-
-[UnsupportedOSPlatform("browser")]
-class DefaultTcpSocketClient(TcpSocketClientOptions options) : IServiceProvider, ITcpSocketClient
-{
- ///
- /// Gets or sets the socket client provider used for managing socket connections.
- ///
- private ITcpSocketClientProvider? SocketClientProvider { get; set; }
-
- ///
- /// Gets or sets the logger instance used for logging messages and events.
- ///
- private ILogger? Logger { get; set; }
-
- ///
- /// Gets or sets the service provider used to resolve dependencies.
- ///
- [NotNull]
- public IServiceProvider? ServiceProvider { get; set; }
-
- ///
- ///
- ///
- public TcpSocketClientOptions Options => options;
-
- ///
- ///
- ///
- public bool IsConnected => SocketClientProvider?.IsConnected ?? false;
-
- ///
- ///
- ///
- public IPEndPoint LocalEndPoint => SocketClientProvider?.LocalEndPoint ?? new IPEndPoint(IPAddress.Any, 0);
-
- ///
- ///
- ///
- public Func, ValueTask>? ReceivedCallback { get; set; }
-
- ///
- ///
- ///
- public Func? OnConnecting { get; set; }
-
- ///
- ///
- ///
- public Func? OnConnected { get; set; }
-
- private IPEndPoint? _remoteEndPoint;
- private IPEndPoint? _localEndPoint;
- private CancellationTokenSource? _receiveCancellationTokenSource;
- private CancellationTokenSource? _autoConnectTokenSource;
-
- private readonly SemaphoreSlim _semaphoreSlim = new(1, 1);
-
- ///
- ///
- ///
- ///
- ///
- ///
- public async ValueTask ConnectAsync(IPEndPoint endPoint, CancellationToken token = default)
- {
- if (IsConnected)
- {
- return true;
- }
-
- var connectionToken = GenerateConnectionToken(token);
- try
- {
- await _semaphoreSlim.WaitAsync(connectionToken).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- // 如果信号量等待被取消,则直接返回 IsConnected
- // 不管是超时还是被取消,都不需要重连,肯定有其他线程在连接中
- return IsConnected;
- }
-
- if (IsConnected)
- {
- _semaphoreSlim.Release();
- return true;
- }
-
- var reconnect = true;
- var ret = false;
- SocketClientProvider = ServiceProvider?.GetRequiredService()
- ?? throw new InvalidOperationException("SocketClientProvider is not registered in the service provider.");
-
- try
- {
- if (OnConnecting != null)
- {
- await OnConnecting();
- }
- ret = await ConnectCoreAsync(SocketClientProvider, endPoint, connectionToken);
- if (OnConnected != null)
- {
- await OnConnected();
- }
- }
- catch (OperationCanceledException ex)
- {
- if (token.IsCancellationRequested)
- {
- Log(LogLevel.Warning, ex, $"TCP Socket connect operation was canceled from {LocalEndPoint} to {endPoint}");
- reconnect = false;
- }
- else
- {
- Log(LogLevel.Warning, ex, $"TCP Socket connect operation timed out from {LocalEndPoint} to {endPoint}");
- }
- }
- catch (Exception ex)
- {
- Log(LogLevel.Error, ex, $"TCP Socket connection failed from {LocalEndPoint} to {endPoint}");
- }
-
- // 释放信号量
- _semaphoreSlim.Release();
-
- if (reconnect)
- {
- _autoConnectTokenSource = new();
-
- if (!ret)
- {
- Reconnect();
- }
- }
- return ret;
- }
-
- private void Reconnect()
- {
- if (_autoConnectTokenSource != null && options.IsAutoReconnect && _remoteEndPoint != null)
- {
- Task.Run(async () =>
- {
- try
- {
- await Task.Delay(options.ReconnectInterval, _autoConnectTokenSource.Token).ConfigureAwait(false);
- await ConnectAsync(_remoteEndPoint, _autoConnectTokenSource.Token).ConfigureAwait(false);
- }
- catch { }
- }, CancellationToken.None).ConfigureAwait(false);
- }
- }
-
- private async ValueTask ConnectCoreAsync(ITcpSocketClientProvider provider, IPEndPoint endPoint, CancellationToken token)
- {
- // 释放资源
- await CloseCoreAsync();
-
- // 创建新的 TcpClient 实例
- provider.LocalEndPoint = Options.LocalEndPoint;
-
- _localEndPoint = Options.LocalEndPoint;
- _remoteEndPoint = endPoint;
-
- var ret = await provider.ConnectAsync(endPoint, token);
-
- if (ret)
- {
- _localEndPoint = provider.LocalEndPoint;
-
- if (options.IsAutoReceive)
- {
- _ = Task.Run(AutoReceiveAsync, CancellationToken.None).ConfigureAwait(false);
- }
- }
- return ret;
- }
-
- private CancellationToken GenerateConnectionToken(CancellationToken token)
- {
- var connectionToken = token;
- if (Options.ConnectTimeout > 0)
- {
- // 设置连接超时时间
- var connectTokenSource = new CancellationTokenSource(options.ConnectTimeout);
- connectionToken = CancellationTokenSource.CreateLinkedTokenSource(token, connectTokenSource.Token).Token;
- }
- return connectionToken;
- }
-
- ///
- ///
- ///
- ///
- ///
- ///
- public async ValueTask SendAsync(ReadOnlyMemory data, CancellationToken token = default)
- {
- if (SocketClientProvider is not { IsConnected: true })
- {
- throw new InvalidOperationException($"TCP Socket is not connected {LocalEndPoint}");
- }
-
- var ret = false;
- var reconnect = true;
- try
- {
- var sendToken = token;
- if (options.SendTimeout > 0)
- {
- // 设置发送超时时间
- var sendTokenSource = new CancellationTokenSource(options.SendTimeout);
- sendToken = CancellationTokenSource.CreateLinkedTokenSource(token, sendTokenSource.Token).Token;
- }
- ret = await SocketClientProvider.SendAsync(data, sendToken);
- }
- catch (OperationCanceledException ex)
- {
- if (token.IsCancellationRequested)
- {
- reconnect = false;
- Log(LogLevel.Warning, ex, $"TCP Socket send operation was canceled from {_localEndPoint} to {_remoteEndPoint}");
- }
- else
- {
- Log(LogLevel.Warning, ex, $"TCP Socket send operation timed out from {_localEndPoint} to {_remoteEndPoint}");
- }
- }
- catch (Exception ex)
- {
- Log(LogLevel.Error, ex, $"TCP Socket send failed from {_localEndPoint} to {_remoteEndPoint}");
- }
-
- Log(LogLevel.Information, null, $"Sending data from {_localEndPoint} to {_remoteEndPoint}, Data Length: {data.Length} Data Content: {BitConverter.ToString(data.ToArray())} Result: {ret}");
-
- if (!ret && reconnect)
- {
- // 如果发送失败并且需要重连则尝试重连
- Reconnect();
- }
- return ret;
- }
-
- ///
- ///
- ///
- ///
- ///
- public async ValueTask> ReceiveAsync(CancellationToken token = default)
- {
- if (SocketClientProvider is not { IsConnected: true })
- {
- throw new InvalidOperationException($"TCP Socket is not connected {LocalEndPoint}");
- }
-
- if (options.IsAutoReceive)
- {
- throw new InvalidOperationException("Cannot call ReceiveAsync when IsAutoReceive is true. Use the auto-receive mechanism instead.");
- }
-
- using var block = MemoryPool.Shared.Rent(options.ReceiveBufferSize);
- var buffer = block.Memory;
- var len = await ReceiveCoreAsync(SocketClientProvider, buffer, token);
- if (len == 0)
- {
- Reconnect();
- }
- return buffer[..len];
- }
-
- private async ValueTask AutoReceiveAsync()
- {
- // 自动接收方法
- _receiveCancellationTokenSource ??= new();
- while (_receiveCancellationTokenSource is { IsCancellationRequested: false })
- {
- if (SocketClientProvider is not { IsConnected: true })
- {
- throw new InvalidOperationException($"TCP Socket is not connected {LocalEndPoint}");
- }
-
- using var block = MemoryPool.Shared.Rent(options.ReceiveBufferSize);
- var buffer = block.Memory;
- var len = await ReceiveCoreAsync(SocketClientProvider, buffer, _receiveCancellationTokenSource.Token);
- if (len == 0)
- {
- // 远端关闭或者 DisposeAsync 方法被调用时退出
- break;
- }
- }
-
- Reconnect();
- }
-
- private async ValueTask ReceiveCoreAsync(ITcpSocketClientProvider client, Memory buffer, CancellationToken token)
- {
- var reconnect = true;
- var len = 0;
- try
- {
- var receiveToken = token;
- if (options.ReceiveTimeout > 0)
- {
- // 设置接收超时时间
- var receiveTokenSource = new CancellationTokenSource(options.ReceiveTimeout);
- receiveToken = CancellationTokenSource.CreateLinkedTokenSource(receiveToken, receiveTokenSource.Token).Token;
- }
-
- len = await client.ReceiveAsync(buffer, receiveToken);
- if (len == 0)
- {
- // 远端主机关闭链路
- Log(LogLevel.Information, null, $"TCP Socket {_localEndPoint} received 0 data closed by {_remoteEndPoint}");
- buffer = Memory.Empty;
- }
- else
- {
- buffer = buffer[..len];
- }
-
- if (ReceivedCallback != null)
- {
- // 如果订阅回调则触发回调
- await ReceivedCallback(buffer);
- }
- }
- catch (OperationCanceledException ex)
- {
- if (token.IsCancellationRequested)
- {
- Log(LogLevel.Warning, ex, $"TCP Socket receive operation canceled from {_localEndPoint} to {_remoteEndPoint}");
- reconnect = false;
- }
- else
- {
- Log(LogLevel.Warning, ex, $"TCP Socket receive operation timed out from {_localEndPoint} to {_remoteEndPoint}");
- }
- }
- catch (Exception ex)
- {
- Log(LogLevel.Error, ex, $"TCP Socket receive failed from {_localEndPoint} to {_remoteEndPoint}");
- }
-
- Log(LogLevel.Information, null, $"Receiving data from {_localEndPoint} to {_remoteEndPoint}, Data Length: {len} Data Content: {BitConverter.ToString(buffer.ToArray())}");
-
- if (len == 0 && reconnect)
- {
- // 如果接收数据长度为 0 并且需要重连则尝试重连
- Reconnect();
- }
- return len;
- }
-
- ///
- /// Logs a message with the specified log level, exception, and additional context.
- ///
- private void Log(LogLevel logLevel, Exception? ex, string? message)
- {
- if (options.EnableLog)
- {
- Logger ??= ServiceProvider?.GetRequiredService>();
- Logger?.Log(logLevel, ex, "{Message}", message);
- }
- }
-
- ///
- ///
- ///
- public async ValueTask CloseAsync()
- {
- // 取消重连任务
- if (_autoConnectTokenSource != null)
- {
- _autoConnectTokenSource.Cancel();
- _autoConnectTokenSource.Dispose();
- _autoConnectTokenSource = null;
- }
-
- await CloseCoreAsync();
- }
-
- private async ValueTask CloseCoreAsync()
- {
- // 取消接收数据的任务
- if (_receiveCancellationTokenSource != null)
- {
- _receiveCancellationTokenSource.Cancel();
- _receiveCancellationTokenSource.Dispose();
- _receiveCancellationTokenSource = null;
- }
-
- if (SocketClientProvider != null)
- {
- await SocketClientProvider.CloseAsync();
- }
- }
-
- ///
- ///
- ///
- ///
- ///
- public object? GetService(Type serviceType) => ServiceProvider.GetService(serviceType);
-
- ///
- /// Releases the resources used by the current instance of the class.
- ///
- /// This method is called to free both managed and unmanaged resources. If the parameter is , the method releases managed resources in addition to
- /// unmanaged resources. Override this method in a derived class to provide custom cleanup logic.
- /// to release both managed and unmanaged resources; to release only
- /// unmanaged resources.
- private async ValueTask DisposeAsync(bool disposing)
- {
- if (disposing)
- {
- await CloseAsync();
- }
- }
-
- ///
- ///
- ///
- public async ValueTask DisposeAsync()
- {
- await DisposeAsync(true);
- GC.SuppressFinalize(this);
- }
-}
diff --git a/src/extensions/BootstrapBlazor.TcpSocket/DefaultTcpSocketClientProvider.cs b/src/extensions/BootstrapBlazor.TcpSocket/DefaultTcpSocketClientProvider.cs
deleted file mode 100644
index 8fa3e886..00000000
--- a/src/extensions/BootstrapBlazor.TcpSocket/DefaultTcpSocketClientProvider.cs
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Net;
-using System.Net.Sockets;
-using System.Runtime.Versioning;
-
-namespace BootstrapBlazor.TcpSocket;
-
-///
-/// TcpSocket 客户端默认实现
-///
-[UnsupportedOSPlatform("browser")]
-class DefaultTcpSocketClientProvider : ITcpSocketClientProvider
-{
- private TcpClient? _client;
-
- ///
- ///
- ///
- public bool IsConnected => _client?.Connected ?? false;
-
- ///
- ///
- ///
- public IPEndPoint LocalEndPoint { get; set; } = new IPEndPoint(IPAddress.Any, 0);
-
- ///
- ///
- ///
- public async ValueTask ConnectAsync(IPEndPoint endPoint, CancellationToken token = default)
- {
- _client = new TcpClient(LocalEndPoint);
- await _client.ConnectAsync(endPoint, token).ConfigureAwait(false);
- if (_client.Connected)
- {
- if (_client.Client.LocalEndPoint is IPEndPoint localEndPoint)
- {
- LocalEndPoint = localEndPoint;
- }
- }
- return _client.Connected;
- }
-
- ///
- ///
- ///
- public async ValueTask SendAsync(ReadOnlyMemory data, CancellationToken token = default)
- {
- var ret = false;
- if (_client != null)
- {
- var stream = _client.GetStream();
- await stream.WriteAsync(data, token).ConfigureAwait(false);
- ret = true;
- }
- return ret;
- }
-
- ///
- ///
- ///
- public async ValueTask ReceiveAsync(Memory buffer, CancellationToken token = default)
- {
- var len = 0;
- if (_client is { Connected: true })
- {
- var stream = _client.GetStream();
- len = await stream.ReadAsync(buffer, token).ConfigureAwait(false);
-
- if (len == 0)
- {
- _client.Close();
- }
- }
- return len;
- }
-
- ///
- ///
- ///
- public ValueTask CloseAsync()
- {
- if (_client != null)
- {
- _client.Close();
- _client = null;
- }
- return ValueTask.CompletedTask;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.TcpSocket/DefaultTcpSocketFactory.cs b/src/extensions/BootstrapBlazor.TcpSocket/DefaultTcpSocketFactory.cs
deleted file mode 100644
index bd181254..00000000
--- a/src/extensions/BootstrapBlazor.TcpSocket/DefaultTcpSocketFactory.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using BootstrapBlazor.Socket.Logging;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using System.Collections.Concurrent;
-using System.Runtime.Versioning;
-
-namespace BootstrapBlazor.TcpSocket;
-
-[UnsupportedOSPlatform("browser")]
-sealed class DefaultTcpSocketFactory(IServiceProvider provider) : ITcpSocketFactory
-{
- private readonly ConcurrentDictionary _pool = new();
-
- public ITcpSocketClient GetOrCreate(string name, Action valueFactory)
- {
- if (!SocketLogging.Inited)
- {
- var logger = provider.GetService>();
- if (logger != null)
- {
- SocketLogging.Init(logger);
- }
- }
- return _pool.GetOrAdd(name, key =>
- {
- var options = new TcpSocketClientOptions();
- valueFactory(options);
- var client = new DefaultTcpSocketClient(options)
- {
- ServiceProvider = provider,
- };
- return client;
- });
- }
-
- public ITcpSocketClient? Remove(string name)
- {
- ITcpSocketClient? client = null;
- if (_pool.TryRemove(name, out var c))
- {
- client = c;
- }
- return client;
- }
-
- private async ValueTask DisposeAsync(bool disposing)
- {
- if (disposing)
- {
- // 释放托管资源
- foreach (var socket in _pool.Values)
- {
- await socket.DisposeAsync();
- }
- _pool.Clear();
- }
- }
-
- ///
- ///
- ///
- public async ValueTask DisposeAsync()
- {
- await DisposeAsync(true);
- GC.SuppressFinalize(this);
- }
-}
diff --git a/src/extensions/BootstrapBlazor.TcpSocket/Extensions/ITcpSocketClientExtensions.cs b/src/extensions/BootstrapBlazor.TcpSocket/Extensions/ITcpSocketClientExtensions.cs
deleted file mode 100644
index f229bd56..00000000
--- a/src/extensions/BootstrapBlazor.TcpSocket/Extensions/ITcpSocketClientExtensions.cs
+++ /dev/null
@@ -1,277 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Options;
-using System.Reflection;
-using System.Runtime.Versioning;
-using System.Text;
-
-namespace BootstrapBlazor.TcpSocket;
-
-///
-/// 扩展方法类
-///
-[UnsupportedOSPlatform("browser")]
-public static class ITcpSocketClientExtensions
-{
- ///
- /// Sends the specified string content to the connected TCP socket client asynchronously.
- ///
- /// This method converts the provided string content into a byte array using the specified
- /// encoding (or UTF-8 by default) and sends it to the connected TCP socket client. Ensure the client is connected
- /// before calling this method.
- /// The TCP socket client to which the content will be sent. Cannot be .
- /// The string content to send. Cannot be or empty.
- /// The character encoding to use for converting the string content to bytes. If , UTF-8
- /// encoding is used by default.
- /// A to observe while waiting for the operation to complete.
- /// A that represents the asynchronous operation. The result is if the content was sent successfully; otherwise, .
- public static ValueTask SendAsync(this ITcpSocketClient client, string content, Encoding? encoding = null, CancellationToken token = default)
- {
- var buffer = encoding?.GetBytes(content) ?? Encoding.UTF8.GetBytes(content);
- return client.SendAsync(buffer, token);
- }
-
- ///
- /// Establishes an asynchronous connection to the specified host and port.
- ///
- /// The TCP socket client to which the content will be sent. Cannot be .
- /// The hostname or IP address of the server to connect to. Cannot be null or empty.
- /// The port number on the server to connect to. Must be a valid port number between 0 and 65535.
- /// An optional to cancel the connection attempt. Defaults to if not provided.
- /// A task that represents the asynchronous operation. The task result is if the connection
- /// is successfully established; otherwise, .
- public static ValueTask ConnectAsync(this ITcpSocketClient client, string ipString, int port, CancellationToken token = default)
- {
- var endPoint = TcpSocketUtility.ConvertToIpEndPoint(ipString, port);
- return client.ConnectAsync(endPoint, token);
- }
-
- private static readonly Dictionary, ValueTask> Callback)>> Cache = [];
-
- ///
- /// 增加 数据适配器及其对应的回调方法
- ///
- ///
- ///
- ///
- public static void AddDataPackageAdapter(this ITcpSocketClient client, IDataPackageAdapter adapter, Func, ValueTask> callback)
- {
- async ValueTask ReceivedCallback(ReadOnlyMemory buffer)
- {
- // 将接收到的数据传递给 DataPackageAdapter 进行数据处理合规数据触发 ReceivedCallBack 回调
- await adapter.HandlerAsync(buffer);
- }
-
- if (Cache.TryGetValue(client, out var list))
- {
- list.Add((adapter, ReceivedCallback));
- }
- else
- {
- Cache.Add(client, [(adapter, ReceivedCallback)]);
- }
-
- client.ReceivedCallback += ReceivedCallback;
-
- // 设置 DataPackageAdapter 的回调函数
- adapter.ReceivedCallBack = callback;
- }
-
- ///
- /// 移除 数据适配器及其对应的回调方法
- ///
- ///
- ///
- public static void RemoveDataPackageAdapter(this ITcpSocketClient client, Func, ValueTask> callback)
- {
- if (Cache.TryGetValue(client, out var list))
- {
- var items = list.Where(i => i.Adapter.ReceivedCallBack == callback).ToList();
- foreach (var c in items)
- {
- client.ReceivedCallback -= c.Callback;
- list.Remove(c);
- }
- }
- }
-
- ///
- /// 通过指定 数据处理实例,设置数据适配器并配置回调方法,切记使用 移除数据处理委托防止内存泄露
- ///
- /// 实例
- /// 数据处理实例
- /// 回调方法
- public static void AddDataPackageAdapter(this ITcpSocketClient client, IDataPackageHandler handler, Func, ValueTask> callback)
- {
- client.AddDataPackageAdapter(new DataPackageAdapter(handler), callback);
- }
-
- private static readonly Dictionary, ValueTask> ReceivedCallback, Delegate EntityCallback)>> EntityCache = [];
-
- ///
- /// Configures the specified to use a data package adapter and a callback function
- /// for processing received data. 切记使用 移除数据处理委托防止内存泄露
- ///
- /// This method sets up the to process incoming data using the
- /// specified and . The is called with the converted entity whenever data is received.
- /// The type of the entity that the data will be converted to.
- /// The TCP socket client to configure.
- /// The data package adapter responsible for handling incoming data.
- /// The converter used to transform the received data into the specified entity type.
- /// The callback function to be invoked with the converted entity.
- public static void AddDataPackageAdapter(this ITcpSocketClient client, IDataPackageAdapter adapter, IDataConverter socketDataConverter, Func callback)
- {
- async ValueTask ReceivedCallback(ReadOnlyMemory buffer)
- {
- // 将接收到的数据传递给 DataPackageAdapter 进行数据处理合规数据触发 ReceivedCallBack 回调
- await adapter.HandlerAsync(buffer);
- }
-
- if (EntityCache.TryGetValue(client, out var list))
- {
- list.Add((ReceivedCallback, callback));
- }
- else
- {
- EntityCache.Add(client, [(ReceivedCallback, callback)]);
- }
-
- client.ReceivedCallback += ReceivedCallback;
-
- // 设置 DataPackageAdapter 的回调函数
- adapter.ReceivedCallBack = async buffer =>
- {
- TEntity? ret = default;
- if (socketDataConverter.TryConvertTo(buffer, out var t))
- {
- ret = t;
- }
- await callback(ret);
- };
- }
-
- ///
- /// 移除 数据适配器及其对应的回调方法
- ///
- ///
- ///
- public static void RemoveDataPackageAdapter(this ITcpSocketClient client, Func callback)
- {
- if (EntityCache.TryGetValue(client, out var list))
- {
- var items = list.Where(i => i.EntityCallback.Equals(callback)).ToList();
- foreach (var c in items)
- {
- client.ReceivedCallback -= c.ReceivedCallback;
- list.Remove(c);
- }
- }
- }
-
- ///
- /// 通过指定 数据处理实例,设置数据适配器并配置回调方法。切记使用 移除数据处理委托防止内存泄露
- ///
- ///
- ///
- ///
- ///
- ///
- public static void AddDataPackageAdapter(this ITcpSocketClient client, IDataPackageHandler handler, IDataConverter socketDataConverter, Func callback)
- {
- client.AddDataPackageAdapter(new DataPackageAdapter(handler), socketDataConverter, callback);
- }
-
- ///
- /// Configures the specified to use a custom data package adapter and callback
- /// function. 切记使用 移除数据处理委托防止内存泄露
- ///
- /// This method sets up the to use the specified for handling incoming data. If the type is decorated with a , the associated converter is used to transform the data before invoking
- /// the . The callback is called with the converted entity or if
- /// conversion fails.
- /// The type of entity that the data package adapter will handle.
- /// The TCP socket client to configure.
- /// The data package adapter responsible for processing incoming data.
- /// The callback function to invoke with the processed entity of type .
- public static void AddDataPackageAdapter(this ITcpSocketClient client, IDataPackageAdapter adapter, Func callback)
- {
- async ValueTask ReceivedCallback(ReadOnlyMemory buffer)
- {
- // 将接收到的数据传递给 DataPackageAdapter 进行数据处理合规数据触发 ReceivedCallBack 回调
- await adapter.HandlerAsync(buffer);
- }
-
- if (EntityCache.TryGetValue(client, out var list))
- {
- list.Add((ReceivedCallback, callback));
- }
- else
- {
- EntityCache.Add(client, [(ReceivedCallback, callback)]);
- }
-
- client.ReceivedCallback += ReceivedCallback;
-
- IDataConverter? converter = null;
-
- var type = typeof(TEntity);
- var converterType = type.GetCustomAttribute();
-
- // 如果类型上有 SocketDataTypeConverterAttribute 特性则使用特性中指定的转换器
- // 如果没有特性则从 ITcpSocketClient 中的服务容器获取转换器
- converter = converterType is { Type: not null }
- ? converterType.Type.CreateInstance>()
- : client.GetSocketDataConverter();
-
- if (converter == null)
- {
- // 未设置数据转换器返回 default 值
- adapter.ReceivedCallBack = async buffer => await callback(default);
- }
- else
- {
- // 设置转化器
- adapter.ReceivedCallBack = async buffer =>
- {
- TEntity? ret = default;
- if (converter.TryConvertTo(buffer, out var t))
- {
- ret = t;
- }
- await callback(ret);
- };
- }
- }
-
- ///
- /// 通过指定 数据处理实例,设置数据适配器并配置回调方法。切记使用 移除数据处理委托防止内存泄露
- ///
- /// 实例
- /// 数据处理实例
- /// 回调方法
- public static void AddDataPackageAdapter(this ITcpSocketClient client, IDataPackageHandler handler, Func callback)
- {
- client.AddDataPackageAdapter(new DataPackageAdapter(handler), callback);
- }
-
- private static IDataConverter? GetSocketDataConverter(this ITcpSocketClient client)
- {
- IDataConverter? converter = null;
- if (client is IServiceProvider provider)
- {
- var converters = provider.GetRequiredService>().Value;
- if (converters.TryGetTypeConverter(out var v))
- {
- converter = v;
- }
- }
- return converter;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.TcpSocket/Extensions/TcpSocketExtensions.cs b/src/extensions/BootstrapBlazor.TcpSocket/Extensions/TcpSocketExtensions.cs
deleted file mode 100644
index e3d2cbf9..00000000
--- a/src/extensions/BootstrapBlazor.TcpSocket/Extensions/TcpSocketExtensions.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
-using System.Runtime.Versioning;
-
-namespace BootstrapBlazor.TcpSocket;
-
-///
-/// TcpSocket 扩展方法
-///
-[UnsupportedOSPlatform("browser")]
-public static class TcpSocketExtensions
-{
- ///
- /// 增加 ITcpSocketFactory 服务
- ///
- ///
- ///
- public static IServiceCollection AddBootstrapBlazorTcpSocketFactory(this IServiceCollection services)
- {
- // 添加 ITcpSocketFactory 服务
- services.AddSingleton();
-
- // 增加 ISocketClientProvider 服务
- services.TryAddTransient();
-
- return services;
- }
-
- ///
- /// 配置第三方数据模型与 数据转换器集合配置扩展方法
- ///
- ///
- ///
- ///
- public static IServiceCollection ConfigureDataConverters(this IServiceCollection services, Action configureOptions)
- {
- services.Configure(configureOptions);
- return services;
- }
-}
diff --git a/src/extensions/BootstrapBlazor.TcpSocket/Extensions/TcpSocketUtility.cs b/src/extensions/BootstrapBlazor.TcpSocket/Extensions/TcpSocketUtility.cs
deleted file mode 100644
index 3c563331..00000000
--- a/src/extensions/BootstrapBlazor.TcpSocket/Extensions/TcpSocketUtility.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Net;
-using System.Net.Sockets;
-using System.Runtime.Versioning;
-
-namespace BootstrapBlazor.TcpSocket;
-
-///
-/// SocketUtility 帮助类
-///
-public static class TcpSocketUtility
-{
- ///
- /// Converts a string representation of an IP address or hostname into an object.
- ///
- /// This method handles common special cases for IP address strings, such as "localhost" and
- /// "any". For other inputs, it attempts to parse the string as an IP address using . If parsing fails, the method resolves the input as a
- /// hostname.
- /// A string containing the IP address or hostname to convert. Special values include:
- /// - "localhost" returns the loopback address ().
- "any" returns the wildcard address
- /// ().
For other values, the method attempts to parse the
- /// string as an IP address or resolve it as a hostname.
- /// An object representing the parsed or resolved IP address. If the input cannot be parsed
- /// or resolved, the method returns a default IP address.
- [UnsupportedOSPlatform("browser")]
- public static IPAddress ConvertToIPAddress(string ipString)
- {
- if (string.IsNullOrEmpty(ipString))
- {
- throw new ArgumentNullException(nameof(ipString), "IP address cannot be null or empty.");
- }
-
- if (ipString.Equals("localhost", StringComparison.OrdinalIgnoreCase))
- {
- return IPAddress.Loopback;
- }
- if (ipString.Equals("any", StringComparison.OrdinalIgnoreCase))
- {
- return IPAddress.Any;
- }
-
- return IPAddress.TryParse(ipString, out var ip) ? ip : IPAddressByHostName;
- }
-
- [ExcludeFromCodeCoverage]
- [UnsupportedOSPlatform("browser")]
- private static IPAddress IPAddressByHostName => Dns.GetHostAddresses(Dns.GetHostName(), AddressFamily.InterNetwork).FirstOrDefault() ?? IPAddress.Any;
-
- ///
- /// Converts a string representation of an IP address and a port number into an instance.
- ///
- /// This method is not supported on browser platforms.
- /// The string representation of the IP address. Must be a valid IPv4 or IPv6 address.
- /// The port number associated with the endpoint. Must be between 0 and 65535.
- /// An representing the specified IP address and port.
- /// Thrown if is less than 0 or greater than 65535.
- [UnsupportedOSPlatform("browser")]
- public static IPEndPoint ConvertToIpEndPoint(string ipString, int port)
- {
- if (port < 0 || port > 65535)
- {
- throw new ArgumentOutOfRangeException(nameof(port), "Port must be between 0 and 65535.");
- }
-
- var address = ConvertToIPAddress(ipString);
- return new IPEndPoint(address, port);
- }
-}
diff --git a/src/extensions/BootstrapBlazor.TcpSocket/ITcpSocketClient.cs b/src/extensions/BootstrapBlazor.TcpSocket/ITcpSocketClient.cs
deleted file mode 100644
index 5ad2f777..00000000
--- a/src/extensions/BootstrapBlazor.TcpSocket/ITcpSocketClient.cs
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Net;
-
-namespace BootstrapBlazor.TcpSocket;
-
-///
-/// Represents a TCP socket for network communication.
-///
-public interface ITcpSocketClient : IAsyncDisposable
-{
- ///
- /// Gets a value indicating whether the system is currently connected. Default is false.
- ///
- bool IsConnected { get; }
-
- ///
- /// Gets or sets the configuration options for the socket client.
- ///
- TcpSocketClientOptions Options { get; }
-
- ///
- /// Gets the local network endpoint that the socket is bound to.
- ///
- /// This property provides information about the local endpoint of the socket, which is typically
- /// used to identify the local address and port being used for communication. If the socket is not bound to a
- /// specific local endpoint, this property may return .
- IPEndPoint LocalEndPoint { get; }
-
- ///
- /// Gets or sets the callback function to handle received data.
- ///
- /// The callback function should be designed to handle the received data efficiently and
- /// asynchronously. Ensure that the implementation does not block or perform long-running operations, as this may
- /// impact performance.
- Func, ValueTask>? ReceivedCallback { get; set; }
-
- ///
- /// Gets or sets the callback function that is invoked when a connection attempt is initiated.
- ///
- Func? OnConnecting { get; set; }
-
- ///
- /// Gets or sets the delegate to be invoked when a connection is successfully established.
- ///
- Func? OnConnected { get; set; }
-
- ///
- /// Establishes an asynchronous connection to the specified endpoint.
- ///
- /// This method attempts to establish a connection to the specified endpoint. If the connection
- /// cannot be established, the method returns rather than throwing an exception.
- /// The representing the remote endpoint to connect to. Cannot be null.
- /// A that can be used to cancel the connection attempt. Defaults to if not provided.
- /// A task that represents the asynchronous operation. The task result is if the connection
- /// is successfully established; otherwise, .
- ValueTask ConnectAsync(IPEndPoint endPoint, CancellationToken token = default);
-
- ///
- /// Sends the specified data asynchronously to the target endpoint.
- ///
- /// This method performs a non-blocking operation to send data. If the operation is canceled via
- /// the , the task will complete with a canceled state. Ensure the connection is properly
- /// initialized before calling this method.
- /// The byte array containing the data to be sent. Cannot be null or empty.
- /// An optional to observe while waiting for the operation to complete.
- /// A task that represents the asynchronous operation. The task result is if the data was
- /// sent successfully; otherwise, .
- ValueTask SendAsync(ReadOnlyMemory data, CancellationToken token = default);
-
- ///
- /// Asynchronously receives a block of data from the underlying source.
- ///
- /// This method is non-blocking and completes when data is available or the operation is
- /// canceled. If the operation is canceled, the returned task will be in a faulted state with a .
- /// A cancellation token that can be used to cancel the operation. The default value is .
- /// A containing a of bytes representing the received data.
- /// The returned memory may be empty if no data is available.
- ValueTask> ReceiveAsync(CancellationToken token = default);
-
- ///
- /// Closes the current connection or resource, releasing any associated resources.
- ///
- /// Once the connection or resource is closed, it cannot be reopened. Ensure that all necessary
- /// operations are completed before calling this method. This method is typically used to clean up resources when
- /// they are no longer needed.
- ValueTask CloseAsync();
-}
diff --git a/src/extensions/BootstrapBlazor.TcpSocket/ITcpSocketClientProvider.cs b/src/extensions/BootstrapBlazor.TcpSocket/ITcpSocketClientProvider.cs
deleted file mode 100644
index 00b82018..00000000
--- a/src/extensions/BootstrapBlazor.TcpSocket/ITcpSocketClientProvider.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Net;
-
-namespace BootstrapBlazor.TcpSocket;
-
-///
-/// Defines the contract for a socket client that provides asynchronous methods for connecting, sending, receiving, and
-/// closing network connections.
-///
-/// This interface is designed to facilitate network communication using sockets. It provides methods for
-/// establishing connections, transmitting data, and receiving data asynchronously. Implementations of this interface
-/// should ensure proper resource management, including closing connections and releasing resources when no longer
-/// needed.
-public interface ITcpSocketClientProvider
-{
- ///
- /// Gets a value indicating whether the connection is currently active.
- ///
- bool IsConnected { get; }
-
- ///
- /// Gets the local network endpoint that the socket is bound to.
- ///
- IPEndPoint LocalEndPoint { get; set; }
-
- ///
- /// Establishes an asynchronous connection to the specified endpoint.
- ///
- /// This method attempts to establish a connection to the specified endpoint. If the connection
- /// fails, the method returns rather than throwing an exception. Ensure the endpoint is
- /// valid and reachable before calling this method.
- /// The representing the remote endpoint to connect to.
- /// An optional to observe while waiting for the connection to complete.
- /// A that represents the asynchronous operation. The result is if the connection was successfully established; otherwise, .
- ValueTask ConnectAsync(IPEndPoint endPoint, CancellationToken token = default);
-
- ///
- /// Sends the specified data asynchronously to the connected endpoint.
- ///
- /// This method performs a non-blocking operation to send data. If the operation is canceled via
- /// the , the returned task will not complete successfully. Ensure the connected endpoint
- /// is ready to receive data before calling this method.
- /// The data to send, represented as a read-only memory block of bytes.
- /// An optional cancellation token that can be used to cancel the operation.
- /// A representing the asynchronous operation. The result is if the data was sent successfully; otherwise, .
- ValueTask SendAsync(ReadOnlyMemory data, CancellationToken token = default);
-
- ///
- /// Asynchronously receives data from a source and writes it into the specified buffer.
- ///
- /// This method does not guarantee that the buffer will be completely filled. The caller should
- /// check the return value to determine the number of bytes received.
- /// The memory buffer where the received data will be stored. Must be large enough to hold the incoming data.
- /// A cancellation token that can be used to cancel the operation. Defaults to if not
- /// provided.
- /// A representing the asynchronous operation. The result is the number of bytes
- /// successfully received and written into the buffer. Returns 0 if the end of the data stream is reached.
- ValueTask ReceiveAsync(Memory buffer, CancellationToken token = default);
-
- ///
- /// Closes the current connection or resource, releasing any associated resources.
- ///
- /// Once the connection or resource is closed, it cannot be reopened. Ensure that all necessary
- /// operations are completed before calling this method. This method is typically used to clean up resources when
- /// they are no longer needed.
- ValueTask CloseAsync();
-}
diff --git a/src/extensions/BootstrapBlazor.TcpSocket/ITcpSocketFactory.cs b/src/extensions/BootstrapBlazor.TcpSocket/ITcpSocketFactory.cs
deleted file mode 100644
index c97065a3..00000000
--- a/src/extensions/BootstrapBlazor.TcpSocket/ITcpSocketFactory.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-namespace BootstrapBlazor.TcpSocket;
-
-///
-/// ITcpSocketFactory Interface
-///
-public interface ITcpSocketFactory : IAsyncDisposable
-{
- ///
- /// Retrieves an existing TCP socket client by name or creates a new one using the specified configuration.
- ///
- /// The unique name of the TCP socket client to retrieve or create. Cannot be null or empty.
- /// A delegate used to configure the for the new TCP socket client if it does not
- /// already exist. This delegate is invoked only when a new client is created.
- /// An instance of corresponding to the specified name. If the client already exists,
- /// the existing instance is returned; otherwise, a new instance is created and returned.
- ITcpSocketClient GetOrCreate(string name, Action valueFactory);
-
- ///
- /// Removes the TCP socket client associated with the specified name.
- ///
- /// The name of the TCP socket client to remove. Cannot be or empty.
- /// The removed instance if a client with the specified name exists; otherwise, .
- ITcpSocketClient? Remove(string name);
-}
diff --git a/src/extensions/BootstrapBlazor.TcpSocket/TcpSocketClientOptions.cs b/src/extensions/BootstrapBlazor.TcpSocket/TcpSocketClientOptions.cs
deleted file mode 100644
index 88907b1e..00000000
--- a/src/extensions/BootstrapBlazor.TcpSocket/TcpSocketClientOptions.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Net;
-
-namespace BootstrapBlazor.TcpSocket;
-
-///
-/// Represents configuration options for a socket client, including buffer sizes, timeouts, and endpoints.
-///
-/// Use this class to configure various settings for a socket client, such as connection timeouts,
-/// buffer sizes, and local or remote endpoints. These options allow fine-tuning of socket behavior to suit specific
-/// networking scenarios.
-public class TcpSocketClientOptions
-{
- ///
- /// Gets or sets the size, in bytes, of the receive buffer used by the connection.
- ///
- public int ReceiveBufferSize { get; set; } = 1024 * 64;
-
- ///
- /// Gets or sets a value indicating whether automatic receiving data is enabled. Default is true.
- ///
- public bool IsAutoReceive { get; set; } = true;
-
- ///
- /// Gets or sets the timeout duration, in milliseconds, for establishing a connection.
- ///
- public int ConnectTimeout { get; set; }
-
- ///
- /// Gets or sets the duration, in milliseconds, to wait for a send operation to complete before timing out.
- ///
- /// If the send operation does not complete within the specified timeout period, an exception may
- /// be thrown.
- public int SendTimeout { get; set; }
-
- ///
- /// Gets or sets the amount of time, in milliseconds, that the receiver will wait for a response before timing out.
- ///
- /// Use this property to configure the maximum wait time for receiving a response. Setting an
- /// appropriate timeout can help prevent indefinite blocking in scenarios where responses may be delayed or
- /// unavailable.
- public int ReceiveTimeout { get; set; }
-
- ///
- /// Gets or sets the local endpoint for the socket client. Default value is
- ///
- /// This property specifies the local network endpoint that the socket client will bind to when establishing a connection.
- public IPEndPoint LocalEndPoint { get; set; } = new(IPAddress.Any, 0);
-
- ///
- /// Gets or sets a value indicating whether logging is enabled. Default value is false.
- ///
- public bool EnableLog { get; set; }
-
- ///
- /// Gets or sets a value indicating whether the system should automatically attempt to reconnect after a connection is lost. Default value is false.
- ///
- public bool IsAutoReconnect { get; set; }
-
- ///
- /// Gets or sets the interval, in milliseconds, between reconnection attempts. Default value is 5000.
- ///
- public int ReconnectInterval { get; set; } = 5000;
-}
diff --git a/test/UnitTestTcpSocket/ActivationExtensionsTest.cs b/test/UnitTestTcpSocket/ActivationExtensionsTest.cs
deleted file mode 100644
index 3de407d1..00000000
--- a/test/UnitTestTcpSocket/ActivationExtensionsTest.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-namespace UnitTestTcpSocket;
-
-public class ActivationExtensionsTest
-{
- [Fact]
- public void Activation_Ok()
- {
- var type = typeof(Foo);
- var o = type.CreateInstance();
- Assert.NotNull(o);
-
- var foo = o as Foo;
- Assert.NotNull(foo);
-
- var foo1 = type.CreateInstance();
- Assert.NotNull(foo1);
- }
-
- [Fact]
- public void Activation_Nest()
- {
- var o = typeof(MockNestEntity).CreateInstance([0.01f]);
- Assert.Equal(0.01f, o?.Rate);
- }
-
- [Fact]
- public void Activation_Fail()
- {
- var type = typeof(string);
- var o = type.CreateInstance([123]);
- Assert.Null(o);
-
- var foo = type.CreateInstance();
- Assert.Null(foo);
- }
-
- class MockNestEntity(float rate)
- {
- public float Rate { get; } = rate;
- }
-}
diff --git a/test/UnitTestTcpSocket/Assembly.cs b/test/UnitTestTcpSocket/Assembly.cs
deleted file mode 100644
index 2eac8083..00000000
--- a/test/UnitTestTcpSocket/Assembly.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-[assembly: ExcludeFromCodeCoverage]
diff --git a/test/UnitTestTcpSocket/BinConverterTest.cs b/test/UnitTestTcpSocket/BinConverterTest.cs
deleted file mode 100644
index d8607c72..00000000
--- a/test/UnitTestTcpSocket/BinConverterTest.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-namespace UnitTestTcpSocket;
-
-public class BinConverterTest
-{
- [Fact]
- public void ToHexString_Null()
- {
- var actual = BinConverter.ToString(null);
- Assert.Equal(string.Empty, actual);
-
- actual = BinConverter.ToString([]);
- Assert.Equal(string.Empty, actual);
- }
-
- [Fact]
- public void ToBinString_Ok()
- {
- var data = new byte[] { 0x1A, 0x02 };
- var actual = BinConverter.ToString(data);
- Assert.Equal("00011010-00000010", actual);
-
- actual = BinConverter.ToString(data, " ");
- Assert.Equal("00011010 00000010", actual);
- }
-
- [Fact]
- public void ToHexString_Exception()
- {
- var data = "00011010-00000010";
- var ex = Assert.ThrowsAny(() => BinConverter.ToBytes(data));
- Assert.NotNull(ex);
- }
-
- [Fact]
- public void ToBytes_Ok()
- {
- var excepted = new byte[] { 0x1A, 0x02 };
-
- var data = "00011010-00000010";
- var actual = BinConverter.ToBytes(data, "-");
- Assert.Equal(excepted, actual);
-
- data = "00011010 00000010";
- actual = BinConverter.ToBytes(data, " ");
- Assert.Equal(excepted, actual);
-
- data = "0001101000000010";
- actual = BinConverter.ToBytes(data);
- Assert.Equal(excepted, actual);
- }
-}
diff --git a/test/UnitTestTcpSocket/DefaultSocketClientProviderTest.cs b/test/UnitTestTcpSocket/DefaultSocketClientProviderTest.cs
deleted file mode 100644
index 61658a22..00000000
--- a/test/UnitTestTcpSocket/DefaultSocketClientProviderTest.cs
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.Net;
-using System.Net.Sockets;
-
-namespace UnitTestTcpSocket;
-
-public class DefaultSocketClientProviderTest
-{
- [Fact]
- public async Task DefaultSocketClient_Ok()
- {
- var sc = new ServiceCollection();
- sc.AddBootstrapBlazorTcpSocketFactory();
- var provider = sc.BuildServiceProvider();
- var clientProvider = provider.GetRequiredService();
-
- // 未建立连接时 IsConnected 应为 false
- Assert.False(clientProvider.IsConnected);
-
- // 未建立连接直接调用 ReceiveAsync 方法
- var buffer = new byte[1024];
- var len = await clientProvider.ReceiveAsync(buffer);
- Assert.Equal(0, len);
- }
-
- [Fact]
- public async Task ReceiveAsync_Ok()
- {
- var port = 8100;
- // 测试接收数据时服务器断开未连接的情况
- StartTcpServer(port);
-
- var sc = new ServiceCollection();
- sc.AddBootstrapBlazorTcpSocketFactory();
- var provider = sc.BuildServiceProvider();
- var factory = provider.GetRequiredService();
- var client = factory.GetOrCreate("provider", op =>
- {
- op.LocalEndPoint = TcpSocketUtility.ConvertToIpEndPoint("localhost", 0);
- op.IsAutoReceive = false;
- op.EnableLog = false;
- });
-
- await client.ConnectAsync("127.0.0.1", port);
- Assert.True(client.IsConnected);
-
- var buffer = await client.ReceiveAsync();
- Assert.Equal(2, buffer.Length);
-
- await Task.Delay(50);
- buffer = await client.ReceiveAsync();
- Assert.False(client.IsConnected);
- }
-
- [Fact]
- public void SocketClientOptions_Ok()
- {
- var options = new TcpSocketClientOptions
- {
- ReceiveBufferSize = 1024 * 64,
- IsAutoReceive = true,
- ConnectTimeout = 1000,
- SendTimeout = 500,
- ReceiveTimeout = 500,
- LocalEndPoint = new IPEndPoint(IPAddress.Loopback, 0)
- };
- Assert.Equal(1024 * 64, options.ReceiveBufferSize);
- Assert.True(options.IsAutoReceive);
- Assert.Equal(1000, options.ConnectTimeout);
- Assert.Equal(500, options.SendTimeout);
- Assert.Equal(500, options.ReceiveTimeout);
- Assert.Equal(new IPEndPoint(IPAddress.Loopback, 0), options.LocalEndPoint);
- }
-
- private static TcpListener StartTcpServer(int port)
- {
- var server = new TcpListener(IPAddress.Loopback, port);
- server.Start();
- Task.Run(() => AcceptClientsAsync(server));
- return server;
- }
-
- private static async Task AcceptClientsAsync(TcpListener server)
- {
- while (true)
- {
- var client = await server.AcceptTcpClientAsync();
- _ = Task.Run(async () =>
- {
- using var stream = client.GetStream();
- while (true)
- {
- var buffer = new byte[1024];
-
- // 模拟拆包发送第二段数据
- await stream.WriteAsync(new byte[] { 0x3, 0x4 }, CancellationToken.None);
-
- // 等待 20ms
- await Task.Delay(20);
- client.Close();
- }
- });
- }
- }
-}
diff --git a/test/UnitTestTcpSocket/Foo.cs b/test/UnitTestTcpSocket/Foo.cs
deleted file mode 100644
index e244b923..00000000
--- a/test/UnitTestTcpSocket/Foo.cs
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using System.ComponentModel.DataAnnotations;
-
-namespace UnitTestTcpSocket;
-
-///
-/// Demo示例数据
-/// Demo sample data
-///
-public class Foo
-{
- // 列头信息支持 Display DisplayName 两种标签
-
- ///
- /// 主键
- ///
- [Key]
- [Display(Name = "主键")]
- public int Id { get; set; }
-
- ///
- /// 姓名
- ///
- [Required(ErrorMessage = "{0}不能为空")]
- [Display(Name = "姓名")]
- public string? Name { get; set; }
-
- ///
- /// 日期
- ///
- [Display(Name = "日期")]
- public DateTime? DateTime { get; set; }
-
- ///
- /// 地址
- ///
- [Display(Name = "地址")]
- [Required(ErrorMessage = "{0}不能为空")]
- public string? Address { get; set; }
-
- ///
- /// 数量
- ///
- [Display(Name = "数量")]
- [Required]
- public int Count { get; set; }
-
- ///
- /// 是/否
- ///
- [Display(Name = "是/否")]
- public bool Complete { get; set; }
-
- ///
- /// 学历
- ///
- [Required(ErrorMessage = "请选择学历")]
- [Display(Name = "学历")]
- public EnumEducation? Education { get; set; }
-
- ///
- /// 爱好
- ///
- [Required(ErrorMessage = "请选择一种{0}")]
- [Display(Name = "爱好")]
- public IEnumerable Hobby { get; set; } = new List();
-
- ///
- /// 只读列,模拟数据库计算列
- ///
- [Display(Name = "只读列")]
- public int ReadonlyColumn { get; init; }
-}
-
-///
-///
-///
-public enum EnumEducation
-{
- ///
- ///
- ///
- [Display(Name = "小学")]
- Primary,
-
- ///
- ///
- ///
- [Display(Name = "中学")]
- Middle
-}
diff --git a/test/UnitTestTcpSocket/HexConverterTest.cs b/test/UnitTestTcpSocket/HexConverterTest.cs
deleted file mode 100644
index 4c95a1db..00000000
--- a/test/UnitTestTcpSocket/HexConverterTest.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-namespace UnitTestTcpSocket;
-
-public class HexConverterTest
-{
- [Fact]
- public void ToHexString_Null()
- {
- var actual = HexConverter.ToString(null);
- Assert.Equal(string.Empty, actual);
-
- actual = HexConverter.ToString([]);
- Assert.Equal(string.Empty, actual);
- }
-
- [Fact]
- public void ToHexString_Exception()
- {
- var data = "1A021304FE1";
- var ex = Assert.ThrowsAny(() => HexConverter.ToBytes(data));
- Assert.NotNull(ex);
- }
-
- [Fact]
- public void ToHexString_Ok()
- {
- var data = new byte[] { 0x1A, 0x02, 0x13, 0x04, 0xFE };
- var actual = HexConverter.ToString(data);
- Assert.Equal("1A-02-13-04-FE", actual);
-
- actual = HexConverter.ToString(data, " ", false);
- Assert.Equal("1a 02 13 04 fe", actual);
-
- actual = HexConverter.ToString(data, " ", true);
- Assert.Equal("1A 02 13 04 FE", actual);
- }
-
- [Fact]
- public void ToBytes_Ok()
- {
- var excepted = new byte[] { 0x1A, 0x02, 0x13, 0x04, 0xFE };
-
- var data = "1A021304FE";
- var actual = HexConverter.ToBytes(data);
- Assert.Equal(excepted, actual);
-
- data = "1A-02-13-04-FE";
- actual = HexConverter.ToBytes(data, "-");
- Assert.Equal(excepted, actual);
-
- data = "1A 02 13 04 FE";
- actual = HexConverter.ToBytes(data, " ");
- Assert.Equal(excepted, actual);
- }
-}
diff --git a/test/UnitTestTcpSocket/ModbusCrcTest.cs b/test/UnitTestTcpSocket/ModbusCrcTest.cs
deleted file mode 100644
index fc1b5b7f..00000000
--- a/test/UnitTestTcpSocket/ModbusCrcTest.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using BootstrapBlazor.Socket.Algorithm;
-
-namespace UnitTestTcpSocket;
-
-public class ModbusCrcTest
-{
- [Fact]
- public void Computer_Ok()
- {
- // 06 00 00 01 7F C9 BA
- var data = new byte[] { 0x01, 0x06, 0x00, 0x00, 0x01, 0x7F };
-
- var crc = ModbusCrc16.Compute(data);
- Assert.Equal("BAC9", crc.ToString("X4"));
- Assert.Equal("01060000017FC9BA", HexConverter.ToString(ModbusCrc16.Append(data), ""));
- }
-
- [Fact]
- public void Validate_Ok()
- {
- var result = ModbusCrc16.Validate([0x01]);
- Assert.False(result);
-
- result = ModbusCrc16.Validate([0x01, 0x06, 0x00, 0x00, 0x01, 0x7F, 0xC9, 0xBA]);
- Assert.True(result);
-
- result = false;
- var data = Enumerable.Range(0, 300).Select(i => (byte)Random.Shared.Next(0, 255));
- result = ModbusCrc16.Validate(ModbusCrc16.Append(data.ToArray()));
- Assert.True(result);
- }
-}
diff --git a/test/UnitTestTcpSocket/SocketDataConverterCollectionsTest.cs b/test/UnitTestTcpSocket/SocketDataConverterCollectionsTest.cs
deleted file mode 100644
index 0be74daa..00000000
--- a/test/UnitTestTcpSocket/SocketDataConverterCollectionsTest.cs
+++ /dev/null
@@ -1,102 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the Apache 2.0 License
-// See the LICENSE file in the project root for more information.
-// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
-
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-
-namespace UnitTestTcpSocket;
-
-public class SocketDataConverterCollectionsTest
-{
- [Fact]
- public void TryGetConverter_Ok()
- {
- var sc = new ServiceCollection();
- sc.ConfigureDataConverters(options =>
- {
- options.AddTypeConverter();
- options.AddPropertyConverter(entity => entity.Header, new DataPropertyConverterAttribute()
- {
- Offset = 0,
- Length = 5
- });
- options.AddPropertyConverter(entity => entity.Body, new DataPropertyConverterAttribute()
- {
- Offset = 5,
- Length = 2
- });
-
- // 为提高代码覆盖率 重复添加转换器以后面的为准
- options.AddTypeConverter();
- options.AddPropertyConverter(entity => entity.Header, new DataPropertyConverterAttribute()
- {
- Offset = 0,
- Length = 5
- });
- });
-
- var provider = sc.BuildServiceProvider();
- var service = provider.GetRequiredService>();
- Assert.NotNull(service.Value);
-
- var ret = service.Value.TryGetTypeConverter(out var converter);
- Assert.True(ret);
- Assert.NotNull(converter);
-
- var fakeConverter = service.Value.TryGetTypeConverter(out var fooConverter);
- Assert.False(fakeConverter);
- Assert.Null(fooConverter);
-
- ret = service.Value.TryGetPropertyConverter(entity => entity.Header, out var propertyConverterAttribute);
- Assert.True(ret);
- Assert.NotNull(propertyConverterAttribute);
- Assert.True(propertyConverterAttribute is { Offset: 0, Length: 5 });
-
- ret = service.Value.TryGetPropertyConverter(entity => entity.Name, out var fooPropertyConverterAttribute);
- Assert.False(ret);
- Assert.Null(fooPropertyConverterAttribute);
-
- ret = service.Value.TryGetPropertyConverter(entity => entity.ToString(), out _);
- Assert.False(ret);
- }
-
- class MockEntity
- {
- public byte[]? Header { get; set; }
-
- public byte[]? Body { get; set; }
- }
-
- class MockLoggerProvider : ILoggerProvider
- {
- public ILogger CreateLogger(string categoryName)
- {
- return new MockLogger();
- }
-
- public void Dispose()
- {
-
- }
- }
-
- class MockLogger : ILogger
- {
- public IDisposable? BeginScope(TState state) where TState : notnull
- {
- return null;
- }
-
- public bool IsEnabled(LogLevel logLevel)
- {
- return true;
- }
-
- public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter)
- {
-
- }
- }
-}
diff --git a/test/UnitTestTcpSocket/SocketLoggingTest.cs b/test/UnitTestTcpSocket/SocketLoggingTest.cs
deleted file mode 100644
index 9fd34ec2..00000000
--- a/test/UnitTestTcpSocket/SocketLoggingTest.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using BootstrapBlazor.Socket.Logging;
-
-namespace UnitTestTcpSocket;
-
-public class SocketLoggingTest
-{
- [Fact]
- public void Logger_Ok()
- {
- SocketLogging.LogError(new Exception());
- SocketLogging.LogInformation("Information");
- SocketLogging.LogWarning("Warning");
- SocketLogging.LogDebug("Debug");
- }
-}
diff --git a/test/UnitTestTcpSocket/TcpSocketFactoryTest.cs b/test/UnitTestTcpSocket/TcpSocketFactoryTest.cs
deleted file mode 100644
index 108d0bc4..00000000
--- a/test/UnitTestTcpSocket/TcpSocketFactoryTest.cs
+++ /dev/null
@@ -1,1488 +0,0 @@
-// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-// Website: https://www.blazor.zone or https://argozhang.github.io/
-
-using BootstrapBlazor.Socket.Logging;
-using Microsoft.Extensions.Logging;
-using System.Buffers;
-using System.Net;
-using System.Net.Sockets;
-using System.Reflection;
-using System.Text;
-
-namespace UnitTestTcpSocket;
-
-public class TcpSocketFactoryTest
-{
- [Fact]
- public async Task GetOrCreate_Ok()
- {
- // 测试 GetOrCreate 方法创建的 Client 销毁后继续 GetOrCreate 得到的对象是否可用
- var sc = new ServiceCollection();
- sc.AddLogging(builder =>
- {
- builder.AddProvider(new MockLoggerProvider());
- });
- sc.AddBootstrapBlazorTcpSocketFactory();
- var provider = sc.BuildServiceProvider();
- var factory = provider.GetRequiredService();
- var client1 = factory.GetOrCreate("demo", op => op.LocalEndPoint = TcpSocketUtility.ConvertToIpEndPoint("localhost", 0));
- await client1.CloseAsync();
-
- var client2 = factory.GetOrCreate("demo", op => op.LocalEndPoint = TcpSocketUtility.ConvertToIpEndPoint("localhost", 0));
- Assert.Equal(client1, client2);
-
- var ip = Dns.GetHostAddresses(Dns.GetHostName(), AddressFamily.InterNetwork).FirstOrDefault() ?? IPAddress.Loopback;
- var client3 = factory.GetOrCreate("demo1", op => op.LocalEndPoint = TcpSocketUtility.ConvertToIpEndPoint(ip.ToString(), 0));
-
- // 测试不合格 IP 地址
- var client4 = factory.GetOrCreate("demo2", op => op.LocalEndPoint = TcpSocketUtility.ConvertToIpEndPoint("256.0.0.1", 0));
-
- var client5 = factory.Remove("demo2");
- Assert.Equal(client4, client5);
- Assert.NotNull(client5);
-
- await client5.DisposeAsync();
- await factory.DisposeAsync();
-
- SocketLogging.LogWarning("Warning");
- SocketLogging.LogDebug("Debug");
- SocketLogging.LogInformation("Information");
- }
-
- [Fact]
- public async Task ConnectAsync_Timeout()
- {
- var client = CreateClient(builder =>
- {
- // 增加发送报错 MockSocket
- builder.AddTransient();
- });
- client.Options.ConnectTimeout = 10;
-
- var connect = await client.ConnectAsync("localhost", 9999);
- Assert.False(connect);
- }
-
- [Fact]
- public async Task ConnectAsync_Cancel()
- {
- var client = CreateClient(builder =>
- {
- builder.AddTransient();
- },
- options =>
- {
- options.ConnectTimeout = 500;
- });
-
- // 测试 ConnectAsync 方法连接取消逻辑
- var cst = new CancellationTokenSource();
- cst.Cancel();
- var connect = await client.ConnectAsync("localhost", 9999, cst.Token);
-
- // 由于信号量被取消,所以连接会失败
- Assert.False(connect);
-
- // 测试真正的连接被取消逻辑
- cst = new CancellationTokenSource(200);
- connect = await client.ConnectAsync("localhost", 9999, cst.Token);
- Assert.False(connect);
- }
-
- [Fact]
- public async Task ConnectAsync_Failed()
- {
- var client = CreateClient();
-
- // 测试 ConnectAsync 方法连接失败
- var connect = await client.ConnectAsync("localhost", 9999);
- Assert.False(connect);
- }
-
- [Fact]
- public async Task ConnectAsync_Error()
- {
- var client = CreateClient();
-
- // 反射设置 SocketClientProvider 为空
- var propertyInfo = client.GetType().GetProperty("ServiceProvider", BindingFlags.Public | BindingFlags.Instance);
- Assert.NotNull(propertyInfo);
- propertyInfo.SetValue(client, null);
-
- // 测试 ConnectAsync 方法连接失败
- var ex = await Assert.ThrowsAsync(async () => await client.ConnectAsync("localhost", 9999));
- Assert.NotNull(ex);
-
- // 反射测试 Log 方法
- var methodInfo = client.GetType().GetMethod("Log", BindingFlags.NonPublic | BindingFlags.Instance);
- Assert.NotNull(methodInfo);
- methodInfo.Invoke(client, [LogLevel.Error, null!, "Test error log"]);
- }
-
- [Fact]
- public async Task ConnectAsync_Lock()
- {
- // 测试并发锁问题
- var provider = new MockAutoReconnectLockSocketProvider();
- var client = CreateClient(builder =>
- {
- builder.AddTransient(p => provider);
- });
-
- // 开 5 个线程同时连接
- _ = Task.Run(async () =>
- {
- // 延时 150 保证有一个连接失败
- await Task.Delay(150);
- provider.SetConnected(true);
- });
- var results = await Task.WhenAll(Enumerable.Range(1, 5).Select(i => client.ConnectAsync("localhost", 0).AsTask()));
- // 期望结果是 1个 false 4个 true
- Assert.Equal(1, results.Count(r => !r));
- Assert.Equal(4, results.Count(r => r));
- }
-
- [Fact]
- public async Task Send_Timeout()
- {
- var port = 8887;
- var server = StartTcpServer(port, MockSplitPackageAsync);
-
- var client = CreateClient(builder =>
- {
- // 增加发送报错 MockSocket
- builder.AddTransient();
- });
- client.Options.SendTimeout = 10;
-
- await client.ConnectAsync("localhost", port);
-
- var data = new ReadOnlyMemory([1, 2, 3, 4, 5]);
- var result = await client.SendAsync(data);
- Assert.False(result);
- }
-
- [Fact]
- public async Task SendAsync_Error()
- {
- var client = CreateClient(builder =>
- {
- // 增加发送报错 MockSocket
- builder.AddTransient();
- });
-
- // 测试未建立连接前调用 SendAsync 方法报异常逻辑
- var data = new ReadOnlyMemory([1, 2, 3, 4, 5]);
- var ex = await Assert.ThrowsAsync(async () => await client.SendAsync(data));
- Assert.NotNull(ex);
-
- // 测试发送失败
- var port = 8892;
- var server = StartTcpServer(port, MockSplitPackageAsync);
-
- await client.ConnectAsync("localhost", port);
- Assert.True(client.IsConnected);
-
- // 内部生成异常日志
- await client.SendAsync(data);
- }
-
- [Fact]
- public async Task SendAsync_Cancel()
- {
- var port = 8881;
- var server = StartTcpServer(port, MockSplitPackageAsync);
-
- var client = CreateClient();
- Assert.False(client.IsConnected);
-
- // 连接 TCP Server
- await client.ConnectAsync("localhost", port);
- Assert.True(client.IsConnected);
-
- // 测试 SendAsync 方法发送取消逻辑
- var cst = new CancellationTokenSource();
- cst.Cancel();
-
- var result = await client.SendAsync("test", null, cst.Token);
- Assert.False(result);
-
- result = await client.SendAsync("test", Encoding.UTF8, cst.Token);
- Assert.False(result);
-
- // 关闭连接
- StopTcpServer(server);
- }
-
- [Fact]
- public async Task ReceiveAsync_Timeout()
- {
- var port = 8888;
- var server = StartTcpServer(port, MockSplitPackageAsync);
-
- var client = CreateClient();
- client.Options.ReceiveTimeout = 100;
-
- await client.ConnectAsync("localhost", port);
-
- var data = new ReadOnlyMemory([1, 2, 3, 4, 5]);
- await client.SendAsync(data);
- await Task.Delay(220); // 等待接收超时
- }
-
- [Fact]
- public async Task ReceiveAsync_Cancel()
- {
- var port = 8889;
- var server = StartTcpServer(port, MockSplitPackageAsync);
-
- var client = CreateClient();
- await client.ConnectAsync("localhost", port);
-
- var data = new ReadOnlyMemory([1, 2, 3, 4, 5]);
- await client.SendAsync(data);
-
- // 通过反射取消令牌
- var type = client.GetType();
- Assert.NotNull(type);
-
- var fieldInfo = type.GetField("_receiveCancellationTokenSource", BindingFlags.NonPublic | BindingFlags.Instance);
- Assert.NotNull(fieldInfo);
- var tokenSource = fieldInfo.GetValue(client) as CancellationTokenSource;
- Assert.NotNull(tokenSource);
- tokenSource.Cancel();
- await Task.Delay(50);
- }
-
- [Fact]
- public async Task ReceiveAsync_InvalidOperationException()
- {
- // 未连接时调用 ReceiveAsync 方法会抛出 InvalidOperationException 异常
- var client = CreateClient();
- var ex = await Assert.ThrowsAsync(async () => await client.ReceiveAsync());
- Assert.NotNull(ex);
-
- // 已连接但是启用了自动接收功能时调用 ReceiveAsync 方法会抛出 InvalidOperationException 异常
- var port = 8893;
- var server = StartTcpServer(port, MockSplitPackageAsync);
-
- client.Options.IsAutoReceive = true;
- var connected = await client.ConnectAsync("localhost", port);
- Assert.True(connected);
-
- ex = await Assert.ThrowsAsync(async () => await client.ReceiveAsync());
- Assert.NotNull(ex);
- }
-
- [Fact]
- public async Task ReceiveAsync_Ok()
- {
- var onConnecting = false;
- var onConnected = false;
- var port = 8891;
- var server = StartTcpServer(port, MockSplitPackageAsync);
-
- var client = CreateClient();
- client.Options.IsAutoReceive = false;
- client.OnConnecting = () =>
- {
- onConnecting = true;
- return Task.CompletedTask;
- };
- client.OnConnected = () =>
- {
- onConnected = true;
- return Task.CompletedTask;
- };
- var connected = await client.ConnectAsync("localhost", port);
- Assert.True(connected);
- Assert.True(onConnecting);
- Assert.True(onConnected);
-
- var data = new ReadOnlyMemory([1, 2, 3, 4, 5]);
- var send = await client.SendAsync(data);
- Assert.True(send);
-
- // 未设置数据处理器未开启自动接收时,调用 ReceiveAsync 方法获取数据
- // 需要自己处理粘包分包和业务问题
- var payload = await client.ReceiveAsync();
- Assert.Equal([1, 2, 3, 4, 5], payload.ToArray());
-
- // 由于服务器端模拟了拆包发送第二段数据,所以这里可以再次调用 ReceiveAsync 方法获取第二段数据
- payload = await client.ReceiveAsync();
- Assert.Equal([3, 4], payload.ToArray());
- }
-
- [Fact]
- public async Task ReceiveAsync_Error()
- {
- var client = CreateClient();
-
- // 测试未建立连接前调用 ReceiveAsync 方法报异常逻辑
- var type = client.GetType();
- Assert.NotNull(type);
-
- var methodInfo = type.GetMethod("AutoReceiveAsync", BindingFlags.NonPublic | BindingFlags.Instance);
- Assert.NotNull(methodInfo);
-
- var task = (ValueTask)methodInfo.Invoke(client, null)!;
- var ex = await Assert.ThrowsAsync(async () => await task);
- Assert.NotNull(ex);
-
- var port = 8882;
- var server = StartTcpServer(port, MockSplitPackageAsync);
-
- Assert.Equal(1024 * 64, client.Options.ReceiveBufferSize);
-
- client.Options.ReceiveBufferSize = 1024 * 20;
- Assert.Equal(1024 * 20, client.Options.ReceiveBufferSize);
-
- ReadOnlyMemory buffer = ReadOnlyMemory.Empty;
- var tcs = new TaskCompletionSource();
-
- // 增加接收回调方法
- client.ReceivedCallback = b =>
- {
- buffer = b;
- tcs.SetResult();
- return ValueTask.CompletedTask;
- };
-
- await client.ConnectAsync("localhost", port);
-
- // 发送数据导致接收数据异常
- var data = new ReadOnlyMemory([1, 2, 3, 4, 5]);
- await client.SendAsync(data);
-
- await tcs.Task;
- Assert.Equal([1, 2, 3, 4, 5], buffer.ToArray());
-
- // 关闭连接
- StopTcpServer(server);
- }
-
- [Fact]
- public async Task AutoReconnect_Ok()
- {
- var client = CreateClient(optionConfigure: options =>
- {
- options.IsAutoReconnect = true;
- options.ReconnectInterval = 200;
- options.IsAutoReceive = true;
- });
-
- // 使用场景自动接收数据,短线后自动重连
- var port = 8894;
- var connect = await client.ConnectAsync("localhost", port);
- Assert.False(connect);
-
- // 开启服务端后,可以自动重连上
- var server = StartTcpServer(port, LoopSendPackageAsync);
- await Task.Delay(250);
- Assert.True(client.IsConnected);
-
- await client.DisposeAsync();
- }
-
- [Fact]
- public async Task AutoReconnect_False()
- {
- var provider = new MockAutoReconnectSocketProvider();
- var client = CreateClient(builder =>
- {
- // 增加发送报错 MockSocket
- builder.AddTransient(p => provider);
- },
- optionConfigure: options =>
- {
- options.IsAutoReconnect = true;
- options.ReconnectInterval = 200;
- options.IsAutoReceive = true;
- });
-
- // 使用场景自动接收数据,短线后自动重连
- var connect = await client.ConnectAsync("localhost", 0);
- Assert.False(connect);
-
- provider.SetConnected(true);
- await Task.Delay(250);
- Assert.True(client.IsConnected);
- }
-
- [Fact]
- public async Task AutoReconnect_Send_Ok()
- {
- // 发送数据时连接断开了,测试重连功能
- var provider = new MockAutoReconnectSocketProvider();
- var client = CreateClient(builder =>
- {
- // 增加发送报错 MockSocket
- builder.AddTransient(p => provider);
- }, optionConfigure: options =>
- {
- options.IsAutoReconnect = true;
- options.ReconnectInterval = 200;
- options.IsAutoReceive = true;
- });
-
- provider.SetConnected(true);
- var connect = await client.ConnectAsync("localhost", 0);
- Assert.True(connect);
-
- // 发送时断开连接
- provider.SetSend(false);
- var send = await client.SendAsync("test");
- Assert.False(send);
-
- await Task.Delay(250);
- Assert.True(client.IsConnected);
- }
-
- [Fact]
- public async Task AutoReconnect_Receive_Ok()
- {
- // 接收数据时连接断开了,测试重连功能
- var provider = new MockAutoReconnectSocketProvider();
- var client = CreateClient(builder =>
- {
- // 增加发送报错 MockSocket
- builder.AddTransient(p => provider);
- }, optionConfigure: options =>
- {
- options.IsAutoReconnect = true;
- options.ReconnectInterval = 200;
- options.IsAutoReceive = false;
- });
-
- provider.SetConnected(true);
- var connect = await client.ConnectAsync("localhost", 0);
- Assert.True(connect);
-
- // 发送时断开连接
- provider.SetReceive(false);
- var buffer = await client.ReceiveAsync();
- Assert.Equal(Memory.Empty, buffer);
-
- await Task.Delay(250);
- provider.SetReceive(true);
- buffer = await client.ReceiveAsync();
- Assert.Equal(5, buffer.Length);
- }
-
- [Fact]
- public async Task AutoReconnect_Cancel()
- {
- // 测试重连时取消逻辑
- var provider = new MockAutoReconnectSocketProvider();
- var client = CreateClient(builder =>
- {
- // 增加发送报错 MockSocket
- builder.AddTransient(p => provider);
- }, optionConfigure: options =>
- {
- options.IsAutoReconnect = true;
- options.ReconnectInterval = 2000;
- options.IsAutoReceive = false;
- });
-
- await client.ConnectAsync("localhost", 0);
- await Task.Delay(100);
- await client.DisposeAsync();
- }
-
- [Fact]
- public async Task FixLengthDataPackageHandler_Ok()
- {
- var port = 8884;
- var server = StartTcpServer(port, MockSplitPackageAsync);
- var client = CreateClient();
- var tcs = new TaskCompletionSource();
- var receivedBuffer = new byte[1024];
-
- // 设置数据适配器
- var adapter = new DataPackageAdapter(new FixLengthDataPackageHandler(7));
- var callback = new Func, ValueTask>(buffer =>
- {
- // buffer 即是接收到的数据
- buffer.CopyTo(receivedBuffer);
- receivedBuffer = receivedBuffer[..buffer.Length];
- tcs.SetResult();
- return ValueTask.CompletedTask;
- });
- client.AddDataPackageAdapter(adapter, callback);
-
- // 测试 ConnectAsync 方法
- var connect = await client.ConnectAsync("localhost", port);
- Assert.True(connect);
- Assert.True(client.IsConnected);
-
- // 测试 SendAsync 方法
- var data = new ReadOnlyMemory([1, 2, 3, 4, 5]);
- var result = await client.SendAsync(data);
- Assert.True(result);
-
- await tcs.Task;
- Assert.Equal([1, 2, 3, 4, 5, 3, 4], receivedBuffer.ToArray());
-
- // 关闭连接
- await client.CloseAsync();
- StopTcpServer(server);
- }
-
- [Fact]
- public async Task FixLengthDataPackageHandler_Sticky()
- {
- var port = 8885;
- var server = StartTcpServer(port, MockStickyPackageAsync);
- var client = CreateClient();
- var tcs = new TaskCompletionSource();
- var receivedBuffer = new byte[128];
-
- // 连接 TCP Server
- var connect = await client.ConnectAsync("localhost", port);
-
- // 设置数据适配器
- var adapter = new DataPackageAdapter(new FixLengthDataPackageHandler(7));
- client.AddDataPackageAdapter(adapter, buffer =>
- {
- // buffer 即是接收到的数据
- buffer.CopyTo(receivedBuffer);
- receivedBuffer = receivedBuffer[..buffer.Length];
- tcs.SetResult();
- return ValueTask.CompletedTask;
- });
-
- // 发送数据
- var data = new ReadOnlyMemory([1, 2, 3, 4, 5]);
- await client.SendAsync(data);
-
- // 等待接收数据处理完成
- await tcs.Task;
-
- // 验证接收到的数据
- Assert.Equal([1, 2, 3, 4, 5, 3, 4], receivedBuffer.ToArray());
-
- // 重置接收缓冲区
- receivedBuffer = new byte[1024];
- tcs = new TaskCompletionSource();
-
- // 等待第二次数据
- await tcs.Task;
-
- // 验证第二次收到的数据
- Assert.Equal([2, 2, 3, 4, 5, 6, 7], receivedBuffer.ToArray());
- tcs = new TaskCompletionSource();
- await tcs.Task;
-
- // 验证第三次收到的数据
- Assert.Equal([3, 2, 3, 4, 5, 6, 7], receivedBuffer.ToArray());
-
- // 关闭连接
- await client.CloseAsync();
- StopTcpServer(server);
- }
-
- [Fact]
- public async Task DelimiterDataPackageHandler_Ok()
- {
- var port = 8883;
- var server = StartTcpServer(port, MockDelimiterPackageAsync);
- var client = CreateClient();
- var tcs = new TaskCompletionSource();
- var receivedBuffer = new byte[128];
-
- // 设置数据适配器
- var adapter = new DataPackageAdapter(new DelimiterDataPackageHandler([13, 10]));
- client.AddDataPackageAdapter(adapter, buffer =>
- {
- // buffer 即是接收到的数据
- buffer.CopyTo(receivedBuffer);
- receivedBuffer = receivedBuffer[..buffer.Length];
- tcs.SetResult();
- return ValueTask.CompletedTask;
- });
-
- // 连接 TCP Server
- var connect = await client.ConnectAsync("localhost", port);
-
- // 发送数据
- var data = new ReadOnlyMemory([1, 2, 3, 4, 5]);
- await client.SendAsync(data);
-
- // 等待接收数据处理完成
- await tcs.Task;
-
- // 验证接收到的数据
- Assert.Equal([1, 2, 3, 4, 5, 13, 10], receivedBuffer.ToArray());
-
- // 等待第二次数据
- receivedBuffer = new byte[1024];
- tcs = new TaskCompletionSource();
- await tcs.Task;
-
- // 验证接收到的数据
- Assert.Equal([5, 6, 13, 10], receivedBuffer.ToArray());
-
- // 关闭连接
- await client.CloseAsync();
- StopTcpServer(server);
-
- var handler = new DelimiterDataPackageHandler("\r\n");
- var ex = Assert.Throws(() => new DelimiterDataPackageHandler(string.Empty));
- Assert.NotNull(ex);
-
- ex = Assert.Throws(() => new DelimiterDataPackageHandler(null!));
- Assert.NotNull(ex);
- }
-
- [Fact]
- public void TryConvertTo_Error()
- {
- var converter = new MockErrorDataConverter();
- var result = converter.TryConvertTo(new byte[] { 0x1, 0x2 }, out var entity);
- Assert.False(result);
- }
-
- [Fact]
- public async Task TryConvertTo_Ok()
- {
- var port = 8886;
- var server = StartTcpServer(port, MockEntityPackageAsync);
- var client = CreateClient();
- var tcs = new TaskCompletionSource();
- MockEntity? entity = null;
-
- // 设置数据适配器
- var adapter = new DataPackageAdapter(new FixLengthDataPackageHandler(29));
- var callback = new Func(t =>
- {
- entity = t;
- tcs.SetResult();
- return Task.CompletedTask;
- });
- client.AddDataPackageAdapter(adapter, new DataConverter(), callback);
-
- // 连接 TCP Server
- var connect = await client.ConnectAsync("localhost", port);
-
- // 发送数据
- var data = new ReadOnlyMemory([1, 2, 3, 4, 5]);
- await client.SendAsync(data);
- await tcs.Task;
-
- Assert.NotNull(entity);
- Assert.Equal([1, 2, 3, 4, 5], entity.Header);
- Assert.Equal([3, 4], entity.Body);
-
- // null
- Assert.Equal((byte)0x0, entity.Value16);
-
- // null
- Assert.Equal((byte)0x0, entity.Value17);
-
- // byte
- Assert.Equal(0x1, entity.Value15);
-
- // string
- Assert.Equal("1", entity.Value1);
-
- // string
- Assert.Equal("1", entity.Value14);
-
- // int
- Assert.Equal(9, entity.Value2);
-
- // long
- Assert.Equal(16, entity.Value3);
-
- // double
- Assert.Equal(3.14, entity.Value4);
-
- // single
- Assert.NotEqual(0, entity.Value5);
-
- // short
- Assert.Equal(0x23, entity.Value6);
-
- // ushort
- Assert.Equal(0x24, entity.Value7);
-
- // uint
- Assert.Equal((uint)0x25, entity.Value8);
-
- // ulong
- Assert.Equal((ulong)0x26, entity.Value9);
-
- // bool
- Assert.True(entity.Value10);
-
- // enum
- Assert.Equal(EnumEducation.Middle, entity.Value11);
-
- // foo
- Assert.NotNull(entity.Value12);
- Assert.Equal(0x29, entity.Value12.Id);
- Assert.Equal("test", entity.Value12.Name);
-
- // no attribute
- Assert.Null(entity.Value13);
- client.RemoveDataPackageAdapter(callback);
-
- // 测试 SocketDataConverter 标签功能
- tcs = new TaskCompletionSource();
- client.AddDataPackageAdapter(adapter, t =>
- {
- entity = t;
- tcs.SetResult();
- return Task.CompletedTask;
- });
- await client.SendAsync(data);
- await tcs.Task;
-
- Assert.NotNull(entity);
- Assert.Equal([1, 2, 3, 4, 5], entity.Header);
-
- // 测试数据适配器直接调用 TryConvertTo 方法转换数据
- var adapter2 = new DataPackageAdapter();
- var result = adapter2.TryConvertTo(data, new DataConverter(), out var t);
- Assert.True(result);
- Assert.NotNull(t);
- Assert.Equal([1, 2, 3, 4, 5], entity.Header);
-
- // 测试 SetDataPackageAdapter 泛型无标签情况
- tcs = new TaskCompletionSource();
- NoConvertEntity? noConvertEntity = null;
- client.AddDataPackageAdapter(adapter, t =>
- {
- noConvertEntity = t;
- tcs.SetResult();
- return Task.CompletedTask;
- });
- await client.SendAsync(data);
- await tcs.Task;
- Assert.Null(noConvertEntity);
-
- var converter = new MockSocketDataConverter();
- result = converter.TryConvertTo(new byte[] { 0x1, 0x2 }, out t);
- Assert.False(result);
-
- server.Stop();
- }
-
- [Fact]
- public async Task TryGetTypeConverter_Ok()
- {
- // 测试服务配置转换器
- var port = 8895;
- var server = StartTcpServer(port, MockSplitPackageAsync);
-
- var client = CreateClient(builder =>
- {
- builder.Configure(options =>
- {
- options.AddTypeConverter();
- options.AddPropertyConverter(entity => entity.Header, new DataPropertyConverterAttribute()
- {
- Offset = 0,
- Length = 5
- });
- options.AddPropertyConverter(entity => entity.Body, new DataPropertyConverterAttribute()
- {
- Offset = 5,
- Length = 2
- });
- });
- });
- var tcs = new TaskCompletionSource();
- var receivedBuffer = new byte[128];
-
- // 连接 TCP Server
- var connect = await client.ConnectAsync("localhost", port);
-
- // 设置数据适配器
- var adapter = new DataPackageAdapter(new FixLengthDataPackageHandler(7));
-
- OptionConvertEntity? entity = null;
- client.AddDataPackageAdapter(adapter, data =>
- {
- // buffer 即是接收到的数据
- entity = data;
- tcs.SetResult();
- return Task.CompletedTask;
- });
-
- // 发送数据
- var data = new ReadOnlyMemory([1, 2, 3, 4, 5]);
- await client.SendAsync(data);
-
- // 等待接收数据处理完成
- await tcs.Task;
- Assert.NotNull(entity);
- Assert.Equal([1, 2, 3, 4, 5], entity.Header);
- Assert.Equal([3, 4], entity.Body);
-
- server.Stop();
- }
-
- [Fact]
- public async Task AddDataPackageAdapter_Ok()
- {
- var port = 8896;
- var server = StartTcpServer(port, MockSplitPackageAsync);
-
- var client = CreateClient();
- var tcs = new TaskCompletionSource();
- var receivedBuffer = new byte[128];
- var receivedBuffer2 = new byte[128];
-
- // 连接 TCP Server
- var connect = await client.ConnectAsync("localhost", port);
-
- client.AddDataPackageAdapter(new DataPackageAdapter(new FixLengthDataPackageHandler(7)), ReceivedCallBack);
- client.AddDataPackageAdapter(new DataPackageAdapter(new FixLengthDataPackageHandler(7)), ReceivedCallBack2);
-
- var data = new ReadOnlyMemory([1, 2, 3, 4, 5]);
- await client.SendAsync(data);
-
- // 等待接收数据处理完成
- await tcs.Task;
- client.RemoveDataPackageAdapter(ReceivedCallBack);
- client.RemoveDataPackageAdapter(ReceivedCallBack2);
-
- ValueTask ReceivedCallBack(ReadOnlyMemory buffer)
- {
- // buffer 即是接收到的数据
- buffer.CopyTo(receivedBuffer);
- receivedBuffer = receivedBuffer[..buffer.Length];
- tcs.SetResult();
- return ValueTask.CompletedTask;
- }
-
- ValueTask ReceivedCallBack2(ReadOnlyMemory buffer)
- {
- // buffer 即是接收到的数据
- buffer.CopyTo(receivedBuffer2);
- receivedBuffer2 = receivedBuffer2[..buffer.Length];
- return ValueTask.CompletedTask;
- }
- }
-
- [Fact]
- public async Task SetDataPackageAdapter_Ok()
- {
- var port = 8897;
- var server = StartTcpServer(port, MockSplitPackageAsync);
-
- var client = CreateClient();
- var tcs = new TaskCompletionSource();
- var receivedBuffer = new byte[128];
-
- // 连接 TCP Server
- var connect = await client.ConnectAsync("localhost", port);
-
- client.AddDataPackageAdapter(new DataPackageAdapter(new FixLengthDataPackageHandler(7)), ReceivedCallBack);
- client.AddDataPackageAdapter(new FixLengthDataPackageHandler(7), ReceivedCallBack);
-
- var data = new ReadOnlyMemory([1, 2, 3, 4, 5]);
- await client.SendAsync(data);
-
- // 等待接收数据处理完成
- await tcs.Task;
-
- ValueTask ReceivedCallBack(ReadOnlyMemory buffer)
- {
- // buffer 即是接收到的数据
- buffer.CopyTo(receivedBuffer);
- receivedBuffer = receivedBuffer[..buffer.Length];
- tcs.SetResult();
- return ValueTask.CompletedTask;
- }
- }
-
- [Fact]
- public async Task SetDataPackageAdapter_Generic()
- {
- var port = 8898;
- var server = StartTcpServer(port, MockSplitPackageAsync);
-
- var client = CreateClient();
- var tcs = new TaskCompletionSource();
- var receivedBuffer = new byte[128];
-
- // 连接 TCP Server
- var connect = await client.ConnectAsync("localhost", port);
-
- client.AddDataPackageAdapter(new DataPackageAdapter(new FixLengthDataPackageHandler(7)), ReceivedCallBack);
- client.AddDataPackageAdapter(new FixLengthDataPackageHandler(7), ReceivedEntityCallBack);
-
- client.AddDataPackageAdapter(new DataPackageAdapter(new FixLengthDataPackageHandler(7)), ReceivedCallBack);
- client.AddDataPackageAdapter(new FixLengthDataPackageHandler(7), new MockSocketDataConverter(), ReceivedEntityCallBack);
-
- var data = new ReadOnlyMemory([1, 2, 3, 4, 5]);
- await client.SendAsync(data);
-
- // 等待接收数据处理完成
- await tcs.Task;
-
- ValueTask ReceivedCallBack(ReadOnlyMemory buffer)
- {
- // buffer 即是接收到的数据
- buffer.CopyTo(receivedBuffer);
- receivedBuffer = receivedBuffer[..buffer.Length];
- tcs.SetResult();
- return ValueTask.CompletedTask;
- }
-
- Task ReceivedEntityCallBack(MockEntity? entity)
- {
- tcs.SetResult();
- return Task.CompletedTask;
- }
- }
-
- [Fact]
- public async Task Convert_Ok()
- {
- var port = 8899;
- var server = StartTcpServer(port, MockSplitPackageAsync);
-
- var client = CreateClient();
- var tcs = new TaskCompletionSource();
- var receivedBuffer = new byte[128];
- MockConverterEntity? entity = null;
-
- // 连接 TCP Server
- var connect = await client.ConnectAsync("localhost", port);
-
- client.AddDataPackageAdapter(new FixLengthDataPackageHandler(7), ReceivedCallBack);
-
- var data = new ReadOnlyMemory([1, 2, 3, 4, 5]);
- await client.SendAsync(data);
-
- // 等待接收数据处理完成
- await tcs.Task;
-
- // 验证实体类不为空
- Assert.NotNull(entity);
- Assert.Equal("3.14", entity.Value1.ToString("#.##"));
-
- Task ReceivedCallBack(MockConverterEntity? data)
- {
- entity = data;
- tcs.SetResult();
- return Task.CompletedTask;
- }
- }
-
- [DataTypeConverter(Type = typeof(DataConverter))]
- class MockConverterEntity
- {
- [DataPropertyConverter(Type = typeof(byte[]), Offset = 0, Length = 5)]
- public byte[]? Header { get; set; }
-
- [DataPropertyConverter(Type = typeof(byte[]), Offset = 5, Length = 2)]
- public byte[]? Body { get; set; }
-
- [DataPropertyConverter(Type = typeof(float), Offset = 5, Length = 1, ConverterType = typeof(FloatConverter), ConverterParameters = [0.01f])]
- public float Value1 { get; set; }
- }
-
- class FloatConverter(float rate) : IDataPropertyConverter
- {
- public object? Convert(ReadOnlyMemory data)
- {
- return (float)Math.Round(314 * rate, 2);
- }
- }
-
- private static TcpListener StartTcpServer(int port, Func handler)
- {
- var server = new TcpListener(IPAddress.Loopback, port);
- server.Start();
- Task.Run(() => AcceptClientsAsync(server, handler));
- return server;
- }
-
- private static async Task AcceptClientsAsync(TcpListener server, Func handler)
- {
- while (true)
- {
- var client = await server.AcceptTcpClientAsync();
- _ = Task.Run(() => handler(client));
- }
- }
-
- private static async Task MockDelimiterPackageAsync(TcpClient client)
- {
- using var stream = client.GetStream();
- while (true)
- {
- var buffer = new byte[10240];
- var len = await stream.ReadAsync(buffer);
- if (len == 0)
- {
- break;
- }
-
- // 回写数据到客户端
- var block = new ReadOnlyMemory(buffer, 0, len);
- await stream.WriteAsync(block, CancellationToken.None);
-
- await Task.Delay(20);
-
- // 模拟拆包发送第二段数据
- await stream.WriteAsync(new byte[] { 13, 10, 0x5, 0x6, 13, 10 }, CancellationToken.None);
- }
- }
-
- private static async Task MockSplitPackageAsync(TcpClient client)
- {
- using var stream = client.GetStream();
- while (true)
- {
- var buffer = new byte[1024];
- var len = await stream.ReadAsync(buffer);
- if (len == 0)
- {
- break;
- }
-
- // 回写数据到客户端
- var block = new ReadOnlyMemory(buffer, 0, len);
- await stream.WriteAsync(block, CancellationToken.None);
-
- // 模拟延时
- await Task.Delay(50);
-
- // 模拟拆包发送第二段数据
- await stream.WriteAsync(new byte[] { 0x3, 0x4 }, CancellationToken.None);
- }
- }
-
- private static async Task MockEntityPackageAsync(TcpClient client)
- {
- using var stream = client.GetStream();
- while (true)
- {
- var buffer = new byte[1024];
- var len = await stream.ReadAsync(buffer);
- if (len == 0)
- {
- break;
- }
-
- // 回写数据到客户端
- await stream.WriteAsync(new byte[] { 0x1, 0x2, 0x3, 0x4, 0x5, 0x3, 0x4, 0x31, 0x09, 0x10, 0x40, 0x09, 0x1E, 0xB8, 0x51, 0xEB, 0x85, 0x1F, 0x40, 0x49, 0x0F, 0xDB, 0x23, 0x24, 0x25, 0x26, 0x01, 0x01, 0x29 }, CancellationToken.None);
- }
- }
-
- private static async Task MockStickyPackageAsync(TcpClient client)
- {
- using var stream = client.GetStream();
- while (true)
- {
- var buffer = new byte[10240];
- var len = await stream.ReadAsync(buffer);
- if (len == 0)
- {
- break;
- }
-
- // 回写数据到客户端
- var block = new ReadOnlyMemory(buffer, 0, len);
- await stream.WriteAsync(block, CancellationToken.None);
-
- // 模拟延时
- await Task.Delay(10);
-
- // 模拟拆包发送第二段数据
- await stream.WriteAsync(new byte[] { 0x3, 0x4, 0x2, 0x2 }, CancellationToken.None);
-
- // 模拟延时
- await Task.Delay(10);
-
- // 模拟粘包发送后续数据
- await stream.WriteAsync(new byte[] { 0x3, 0x4, 0x5, 0x6, 0x7, 0x3, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x1 }, CancellationToken.None);
- }
- }
-
- private static async Task LoopSendPackageAsync(TcpClient client)
- {
- using var stream = client.GetStream();
- while (true)
- {
- // 模拟发送数据
- var data = new byte[] { 1, 2, 3, 4, 5 };
- await stream.WriteAsync(data, CancellationToken.None);
- // 模拟延时
- await Task.Delay(500);
- }
- }
-
- private static void StopTcpServer(TcpListener server)
- {
- server?.Stop();
- }
-
- private static ITcpSocketClient CreateClient(Action? builder = null, Action? optionConfigure = null)
- {
- var sc = new ServiceCollection();
- sc.AddLogging(builder =>
- {
- builder.AddProvider(new MockLoggerProvider());
- });
- sc.AddBootstrapBlazorTcpSocketFactory();
- builder?.Invoke(sc);
-
- var provider = sc.BuildServiceProvider();
- var factory = provider.GetRequiredService();
- var client = factory.GetOrCreate("test", op =>
- {
- op.LocalEndPoint = TcpSocketUtility.ConvertToIpEndPoint("localhost", 0);
- op.EnableLog = true;
- optionConfigure?.Invoke(op);
- });
- return client;
- }
-
- class MockLoggerProvider : ILoggerProvider
- {
- public ILogger CreateLogger(string categoryName)
- {
- return new MockLogger();
- }
-
- public void Dispose()
- {
-
- }
- }
-
- class MockLogger : ILogger
- {
- public IDisposable? BeginScope(TState state) where TState : notnull
- {
- return null;
- }
-
- public bool IsEnabled(LogLevel logLevel)
- {
- return true;
- }
-
- public void Log