wanshenmean
2026-02-03 d8a9b76a6bb2824c1e9fb0d17938c926472dd78b
支持帧消息协议并改进 TCP 服务器

添加消息帧(头/尾)支持和稳健的接收逻辑,以及客户端管理和并发性改进。SocketServerOptions:将默认 Backlog 增加至 1000,并添加 MessageHeader/MessageFooter 选项。TcpSocketServer:添加客户端跟踪集合、同步对象和 CancellationTokenSource;引入 GetClientIds/GetClientIdByDevice、SendToDeviceAsync、SendMessageAsync、BroadcastAsync 及其他辅助方法。消息/接收路径:将 ReadLineAsync 替换为 ReceiveFullMessageAsync,该函数处理头/尾帧和编码,并在触发事件前检测 JSON 负载。更新 WriteToClientAsync 及其他发送路径以使用帧消息。更新 appsettings.json 注释并将 SocketServer.Backlog 改为 1000。更新 .gitignore 以忽略 .vs 和 .db 文件。此外,还包括 Visual Studio 工作空间布局和 Copilot 索引数据库更改(IDE 元数据),并添加一个 xlsx 文件。
已添加1个文件
已修改11个文件
416 ■■■■ 文件已修改
.gitignore 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/CopilotIndices/18.0.988.22099/CodeChunks.db 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/CopilotIndices/18.0.988.22099/SemanticSymbols.db 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/v18/DocumentLayout.json 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/appsettings.json 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/SocketServerOptions.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Clients.cs 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Dispose.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Messaging.cs 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Server.cs 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.cs 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目资料/设备协议/拘束机对接协议/插拔钉机对接协议.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore
@@ -405,3 +405,8 @@
/Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/copilot-chat
/Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/CopilotIndices
/Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/CopilotIndices
/Code/WCS/WIDESEAWCS_Server/.vs
/Code/WCS/WIDESEAWCS_Server/.vs
*.db
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/CopilotIndices/18.0.988.22099/CodeChunks.db
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/CopilotIndices/18.0.988.22099/SemanticSymbols.db
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/CopilotIndices/18.0.988.22099/CodeChunks.db
Binary files differ
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/CopilotIndices/18.0.988.22099/SemanticSymbols.db
Binary files differ
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/v18/DocumentLayout.json
@@ -3,24 +3,32 @@
  "WorkspaceRootPath": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\",
  "Documents": [
    {
      "AbsoluteMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_tasks\\robotjob\\robotjob.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|solutionrelative:wideseawcs_tasks\\robotjob\\robotjob.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_tasks\\socketserver\\tcpsocketserver.messaging.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|solutionrelative:wideseawcs_tasks\\socketserver\\tcpsocketserver.messaging.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_tasks\\socketserver\\tcpsocketserver.server.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|solutionrelative:wideseawcs_tasks\\socketserver\\tcpsocketserver.server.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
      "AbsoluteMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_tasks\\socketserver\\socketserveroptions.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|solutionrelative:wideseawcs_tasks\\socketserver\\socketserveroptions.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_tasks\\socketserver\\tcpsocketserver.clients.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|solutionrelative:wideseawcs_tasks\\socketserver\\tcpsocketserver.clients.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_tasks\\robotjob\\robotjob.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|solutionrelative:wideseawcs_tasks\\robotjob\\robotjob.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{487FA45B-EA1A-4ACA-BB5B-0F6708F462C0}|WIDESEAWCS_Server\\WIDESEAWCS_Server.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_server\\appsettings.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}",
      "RelativeMoniker": "D:0:0:{487FA45B-EA1A-4ACA-BB5B-0F6708F462C0}|WIDESEAWCS_Server\\WIDESEAWCS_Server.csproj|solutionrelative:wideseawcs_server\\appsettings.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_tasks\\socketserver\\tcpsocketserver.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|solutionrelative:wideseawcs_tasks\\socketserver\\tcpsocketserver.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_tasks\\socketserver\\tcpsocketserver.clients.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|solutionrelative:wideseawcs_tasks\\socketserver\\tcpsocketserver.clients.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
      "AbsoluteMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_tasks\\socketserver\\tcpsocketserver.server.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|solutionrelative:wideseawcs_tasks\\socketserver\\tcpsocketserver.server.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{83F18A31-5983-4587-A0B2-414BF70E50B5}|WIDESEAWCS_TaskInfoService\\WIDESEAWCS_TaskInfoService.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_taskinfoservice\\taskservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
@@ -62,7 +70,7 @@
      "DocumentGroups": [
        {
          "DockedWidth": 200,
          "SelectedChildIndex": 11,
          "SelectedChildIndex": 14,
          "Children": [
            {
              "$type": "Bookmark",
@@ -78,7 +86,33 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 8,
              "DocumentIndex": 1,
              "Title": "SocketServerOptions.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\SocketServer\\SocketServerOptions.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Tasks\\SocketServer\\SocketServerOptions.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\SocketServer\\SocketServerOptions.cs",
              "RelativeToolTip": "WIDESEAWCS_Tasks\\SocketServer\\SocketServerOptions.cs",
              "ViewState": "AgIAACEAAAAAAAAAAAAQwCAAAAA7AAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-03T03:20:50.458Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 4,
              "Title": "appsettings.json",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Server\\appsettings.json",
              "RelativeDocumentMoniker": "WIDESEAWCS_Server\\appsettings.json",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Server\\appsettings.json",
              "RelativeToolTip": "WIDESEAWCS_Server\\appsettings.json",
              "ViewState": "AgIAABUAAAAAAAAAAAAAADYAAAAbAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001642|",
              "WhenOpened": "2026-02-03T03:54:20.54Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 10,
              "Title": "TaskTypeEnum.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Common\\TaskEnum\\TaskTypeEnum.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Common\\TaskEnum\\TaskTypeEnum.cs",
@@ -90,7 +124,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 7,
              "DocumentIndex": 9,
              "Title": "TaskEnumHelper.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Common\\TaskEnum\\TaskEnumHelper.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Common\\TaskEnum\\TaskEnumHelper.cs",
@@ -102,7 +136,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 5,
              "DocumentIndex": 7,
              "Title": "TaskService.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_TaskInfoService\\TaskService.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_TaskInfoService\\TaskService.cs",
@@ -114,7 +148,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 11,
              "DocumentIndex": 13,
              "Title": "FormationCommonStackerCraneJob.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\FormationStackerCraneJob\\FormationCommonStackerCraneJob.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Tasks\\FormationStackerCraneJob\\FormationCommonStackerCraneJob.cs",
@@ -126,7 +160,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 10,
              "DocumentIndex": 12,
              "Title": "ITaskService.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_ITaskInfoService\\ITaskService.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_ITaskInfoService\\ITaskService.cs",
@@ -138,7 +172,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 6,
              "DocumentIndex": 8,
              "Title": "TaskStatusEnum.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Common\\TaskEnum\\TaskStatusEnum.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Common\\TaskEnum\\TaskStatusEnum.cs",
@@ -150,7 +184,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 9,
              "DocumentIndex": 11,
              "Title": "CommonStackerCraneJob.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\StackerCraneJob\\CommonStackerCraneJob.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Tasks\\StackerCraneJob\\CommonStackerCraneJob.cs",
@@ -163,7 +197,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 12,
              "DocumentIndex": 14,
              "Title": "QuartzNetExtension.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_QuartzJob\\QuartzNet\\QuartzNetExtension.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_QuartzJob\\QuartzNet\\QuartzNetExtension.cs",
@@ -175,65 +209,65 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 0,
              "DocumentIndex": 3,
              "Title": "RobotJob.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\RobotJob\\RobotJob.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Tasks\\RobotJob\\RobotJob.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\RobotJob\\RobotJob.cs",
              "RelativeToolTip": "WIDESEAWCS_Tasks\\RobotJob\\RobotJob.cs",
              "ViewState": "AgIAACMAAAAAAAAAAAAAwD4AAABRAAAAAAAAAA==",
              "ViewState": "AgIAAFYAAAAAAAAAAAAAAGwAAAAMAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-02T03:52:06.502Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 1,
              "DocumentIndex": 0,
              "Title": "TcpSocketServer.Messaging.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Messaging.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Messaging.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Messaging.cs",
              "RelativeToolTip": "WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Messaging.cs",
              "ViewState": "AgIAACMAAAAAAAAAAAAkwD4AAAAQAAAAAAAAAA==",
              "ViewState": "AgIAALYAAAAAAAAAAAAIwLsAAAAWAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-02T03:38:35.325Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 3,
              "DocumentIndex": 5,
              "Title": "TcpSocketServer.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.cs",
              "RelativeToolTip": "WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.cs",
              "ViewState": "AgIAACQAAAAAAAAAAAAowDYAAAA/AAAAAAAAAA==",
              "ViewState": "AgIAAB8AAAAAAAAAAAAmwDsAAABaAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-02T03:37:56.495Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 4,
              "DocumentIndex": 2,
              "Title": "TcpSocketServer.Clients.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Clients.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Clients.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Clients.cs",
              "RelativeToolTip": "WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Clients.cs",
              "ViewState": "AgIAAAAAAAAAAAAAAAAAAE0AAAAhAAAAAAAAAA==",
              "ViewState": "AgIAAEQAAAAAAAAAAAAkwGAAAAAAAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-02T03:37:53.636Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 2,
              "DocumentIndex": 6,
              "Title": "TcpSocketServer.Server.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Server.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Server.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Server.cs",
              "RelativeToolTip": "WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Server.cs",
              "ViewState": "AgIAAFMAAAAAAAAAAAAQwGwAAAAAAAAAAAAAAA==",
              "ViewState": "AgIAAAAAAAAAAAAAAAAAAGwAAAAAAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-02T03:37:49.526Z",
              "EditorCaption": ""
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/appsettings.json
@@ -46,14 +46,14 @@
  "WebSocketEnable": true, //是否开启WebSocket服务
  "WebSocketPort": 9296, //WebSocket服务端口
  "SocketServer": {
    "Enabled": true,
    "Port": 2000,
    "IpAddress": "0.0.0.0",
    "Backlog": 100,
    "EncodingName": "utf-8",
    "AutoDetectEncoding": true,
    "IdleTimeoutSeconds": 300,
    "EnableHeartbeat": true,
    "LogFilePath": "socketserver.log"
    "Enabled": true, //是否启用Socket服务器
    "Port": 2000,  //监听端口
    "IpAddress": "0.0.0.0", //监听地址,
    "Backlog": 1000,   //最大连接数
    "EncodingName": "utf-8",  //编码方式
    "AutoDetectEncoding": true,   //是否自动检测编码
    "IdleTimeoutSeconds": 300,   //空闲超时时间,单位秒,0表示不超时
    "EnableHeartbeat": true,   //是否启用心跳检测
    "LogFilePath": "socketserver.log"  //日志文件路径
  }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/SocketServerOptions.cs
@@ -25,7 +25,7 @@
        /// <summary>
        /// è¿žæŽ¥é˜Ÿåˆ—长度
        /// </summary>
        public int Backlog { get; set; } = 100;
        public int Backlog { get; set; } = 1000;
        /// <summary>
        /// æ–‡æœ¬ç¼–码名称(例如: utf-8, gbk)
@@ -51,5 +51,15 @@
        /// æ—¥å¿—文件路径(相对于程序运行目录)
        /// </summary>
        public string LogFilePath { get; set; } = "socketserver.log";
        /// <summary>
        /// æ¶ˆæ¯å¤´æ ‡è®°
        /// </summary>
        public string MessageHeader { get; set; } = "<START>";
        /// <summary>
        /// æ¶ˆæ¯å°¾æ ‡è®°
        /// </summary>
        public string MessageFooter { get; set; } = "<END>";
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Clients.cs
@@ -9,6 +9,11 @@
{
    public partial class TcpSocketServer
    {
        /// <summary>
        /// æ£€ç´¢å½“前在服务中注册的所有客户端标识符的只读列表。
        /// </summary>
        /// <remarks>返回的列表表示调用时刻客户端ID的快照。后续对客户端集合的更改不会影响返回的列表。此方法是线程安全的。</remarks>
        /// <returns>包含客户端ID的<see cref="IReadOnlyList{String}"/>。如果没有客户端注册,列表将为空。</returns>
        public IReadOnlyList<string> GetClientIds()
        {
            lock (_syncRoot)
@@ -17,6 +22,12 @@
            }
        }
        /// <summary>
        /// æ£€ç´¢ä¸ŽæŒ‡å®šè®¾å¤‡æ ‡è¯†ç¬¦å…³è”的客户端标识符。
        /// </summary>
        /// <remarks>此方法是线程安全的。如果未找到设备标识符,方法将返回null而不是抛出异常。</remarks>
        /// <param name="deviceId">要检索客户端标识符的设备的唯一标识符。不能为null。</param>
        /// <returns>与指定设备标识符关联的客户端标识符,如果不存在关联则返回null。</returns>
        public string? GetClientIdByDevice(string deviceId)
        {
            lock (_syncRoot)
@@ -25,6 +36,14 @@
            }
        }
        /// <summary>
        /// å¼‚步向指定设备发送消息。
        /// </summary>
        /// <remarks>如果指定设备未注册或无法找到,则返回 <see langword="false"/>。</remarks>
        /// <param name="deviceId">目标设备的唯一标识符。不能为null或空。</param>
        /// <param name="message">要发送给设备的消息。不能为null。</param>
        /// <returns>表示异步操作的任务。如果消息成功发送,任务结果为 <see langword="true"/>;
        /// å¦åˆ™ä¸º <see langword="false"/>。</returns>
        public Task<bool> SendToDeviceAsync(string deviceId, string message)
        {
            var clientId = GetClientIdByDevice(deviceId);
@@ -32,6 +51,16 @@
            return SendToClientAsync(clientId, message);
        }
        /// <summary>
        /// é€šè¿‡TCP连接异步向指定客户端发送带帧的文本消息。
        /// </summary>
        /// <remarks>如果客户端未连接或不存在,此方法将返回 <see langword="false"/> ä¸”不发送消息。
        /// æ¶ˆæ¯å°†ä¼˜å…ˆä½¿ç”¨å®¢æˆ·ç«¯é¦–选的文本编码进行编码;否则使用默认编码。
        /// æ­¤æ–¹æ³•对于向不同客户端的并发调用是线程安全的。</remarks>
        /// <param name="clientId">要发送消息到的客户端的唯一标识符。必须对应已连接的客户端。</param>
        /// <param name="message">要发送给客户端的文本消息。不能为null。</param>
        /// <returns>表示异步操作的任务。如果消息成功发送,任务结果为 <see langword="true"/>;
        /// å¦åˆ™ï¼Œå¦‚果客户端未连接或不存在,结果为 <see langword="false"/>。</returns>
        public async Task<bool> SendToClientAsync(string clientId, string message)
        {
            TcpClient? client;
@@ -55,7 +84,8 @@
            try
            {
                var ns = client.GetStream();
                var data = enc.GetBytes((message ?? string.Empty) + "\n");
                var framedMessage = BuildFramedMessage(message);
                var data = enc.GetBytes(framedMessage);
                await ns.WriteAsync(data, 0, data.Length);
            }
            finally
@@ -65,6 +95,13 @@
            return true;
        }
        /// <summary>
        /// å¼‚步向所有已连接的客户端发送指定的消息。
        /// </summary>
        /// <remarks>如果向某个客户端发送消息时发生错误,异常将被抑制并继续向其他客户端广播。
        /// å½“所有发送操作完成后,此方法结束。</remarks>
        /// <param name="message">要广播给所有客户端的消息。不能为null。</param>
        /// <returns>表示异步广播操作的任务。</returns>
        public async Task BroadcastAsync(string message)
        {
            List<TcpClient> clients;
@@ -75,11 +112,19 @@
            await Task.WhenAll(clients.Select(c => Task.Run(async () =>
            {
                try { await SendAsync(c, message); } catch { }
                try { await SendMessageAsync(c, message); } catch { }
            })));
        }
        public static async Task SendAsync(TcpClient client, string message)
        /// <summary>
        /// é€šè¿‡ç½‘络流异步向指定的TCP客户端发送带帧的文本消息。
        /// </summary>
        /// <remarks>如果客户端为null或未连接,此方法将立即返回而不发送消息。
        /// æ¶ˆæ¯å°†ä½¿ç”¨é…ç½®çš„æ–‡æœ¬ç¼–码进行编码并添加帧头后通过网络流发送。</remarks>
        /// <param name="client">要发送消息到的TCP客户端。必须处于连接状态;否则方法不执行任何操作。</param>
        /// <param name="message">要发送给客户端的文本消息。消息在传输前将被编码并添加帧头。</param>
        /// <returns>表示异步发送操作的任务。</returns>
        private async Task SendMessageAsync(TcpClient client, string message)
        {
            if (client == null || !client.Connected)
            {
@@ -87,7 +132,8 @@
            }
            NetworkStream stream = client.GetStream();
            var data = Encoding.UTF8.GetBytes((message ?? string.Empty) + "\n");
            var framedMessage = BuildFramedMessage(message);
            var data = _textEncoding.GetBytes(framedMessage);
            await stream.WriteAsync(data, 0, data.Length);
        }
    }
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Dispose.cs
@@ -5,6 +5,11 @@
{
    public partial class TcpSocketServer
    {
        /// <summary>
        /// é‡Šæ”¾æœåŠ¡å™¨ä½¿ç”¨çš„æ‰€æœ‰èµ„æºå¹¶åœæ­¢ç›‘å¬ä¼ å…¥è¿žæŽ¥ã€‚
        /// </summary>
        /// <remarks>当不再需要服务器时调用此方法,以确保所有相关资源(如网络监听器和同步原语)被正确释放。
        /// è°ƒç”¨ <see cref="Dispose"/> åŽï¼ŒæœåŠ¡å™¨æ— æ³•é‡æ–°å¯åŠ¨æˆ–å†æ¬¡ä½¿ç”¨ã€‚</remarks>
        public void Dispose()
        {
            _cts?.Cancel();
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Messaging.cs
@@ -1,16 +1,23 @@
using HslCommunication.Core.IMessage;
using System;
using System.Net.Sockets;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using WIDESEAWCS_QuartzJob;
using System.IO;
namespace WIDESEAWCS_Tasks.SocketServer
{
    public partial class TcpSocketServer
    {
        /// <summary>
        /// å¼‚步处理与已连接的TCP客户端的通信,处理机器人起重机会话中的传入消息和客户端状态更新。
        /// </summary>
        /// <remarks>此方法管理客户端连接的生命周期,包括读取消息、更新客户端状态和调用相关事件。
        /// å½“处理结束时,客户端和相关的网络资源将被释放。如果启用心跳或空闲超时选项,
        /// å°†åº”用额外的取消逻辑。事件调用期间的异常将被捕获并抑制,以确保会话处理的鲁棒性。</remarks>
        /// <param name="client">表示要处理的远程连接的TCP客户端。方法完成后将释放此对象。</param>
        /// <param name="clientId">已连接客户端的唯一标识符。用于在整个会话中跟踪和更新客户端状态。</param>
        /// <param name="cancellationToken">可用于取消客户端处理操作的取消令牌。如果请求取消,方法将立即终止处理。</param>
        /// <param name="robotCrane">表示与客户端关联的机器人起重机的当前状态对象。用于为消息处理和事件调用提供上下文。</param>
        /// <returns>表示处理客户端连接的异步操作的任务。当客户端断开连接或请求取消时任务完成。</returns>
        public async Task HandleClientAsync(TcpClient client, string clientId, CancellationToken cancellationToken, RobotSocketState robotCrane)
        {
            using (client)
@@ -32,7 +39,8 @@
                        try
                        {
                            var ct = localCts?.Token ?? cancellationToken;
                            message = await reader.ReadLineAsync().WaitAsync(ct);
                            message = await ReceiveFullMessageAsync(networkStream, _textEncoding, ct);
                            //message = await reader.ReadLineAsync().WaitAsync(ct);
                        }
                        catch (OperationCanceledException)
                        {
@@ -55,7 +63,13 @@
                        if (MessageReceived != null)
                        {
                            try { _ = MessageReceived.Invoke(message, false, client, robotCrane); } catch { }
                            try
                            {
                                // åˆ¤æ–­æ˜¯å¦ä¸º JSON æ ¼å¼
                                bool isJsonFormat = TryParseJsonSilent(message);
                                _ = MessageReceived.Invoke(message, isJsonFormat, client, robotCrane);
                            }
                            catch { }
                        }
                    }
                }
@@ -68,6 +82,18 @@
            }
        }
        /// <summary>
        /// å°è¯•处理来自客户端的设备注册请求。返回一个值指示该消息是否被作为注册请求处理。
        /// </summary>
        /// <remarks>如果消息是有效的注册请求且包含非空的设备标识符,
        /// åˆ™å°†è®¾å¤‡ç»‘定到客户端并发送确认信息。此方法不会因无效消息而抛出异常;
        /// å®ƒä»…返回 false。</remarks>
        /// <param name="messageLower">客户端消息的小写版本,用于判断消息是否为注册请求。</param>
        /// <param name="message">包含注册命令和设备标识符的原始客户端消息。</param>
        /// <param name="clientId">发送注册请求的客户端的唯一标识符。</param>
        /// <param name="client">与客户端通信的TCP客户端连接。</param>
        /// <param name="cancellationToken">可用于取消注册操作的取消令牌。</param>
        /// <returns>如果消息被识别并作为注册请求处理,则返回 true;否则返回 false。</returns>
        private bool TryHandleRegister(string messageLower, string message, string clientId, NetworkStream networkStream, CancellationToken cancellationToken)
        {
            if (!messageLower.StartsWith("register,"))
@@ -89,6 +115,11 @@
            return true;
        }
        /// <summary>
        /// æ›´æ–°å®¢æˆ·ç«¯çŠ¶æ€
        /// </summary>
        /// <param name="clientId"></param>
        /// <param name="message"></param>
        private void UpdateClientStatus(string clientId, string message)
        {
            lock (_syncRoot)
@@ -110,6 +141,14 @@
            }
        }
        /// <summary>
        /// å†™å…¥æ¶ˆæ¯åˆ°å®¢æˆ·ç«¯
        /// </summary>
        /// <param name="clientId"></param>
        /// <param name="networkStream"></param>
        /// <param name="message"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        private async Task WriteToClientAsync(string clientId, NetworkStream networkStream, string message, CancellationToken cancellationToken)
        {
            SemaphoreSlim? sem = null;
@@ -125,7 +164,8 @@
            if (sem != null) await sem.WaitAsync(cancellationToken);
            try
            {
                var data = enc.GetBytes(message + "\n");
                var framedMessage = BuildFramedMessage(message);
                var data = enc.GetBytes(framedMessage);
                await networkStream.WriteAsync(data, 0, data.Length, cancellationToken);
            }
            finally
@@ -134,6 +174,23 @@
            }
        }
        /// <summary>
        /// æ·»åŠ æ¶ˆæ¯å¸§å¤´å°¾
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        private string BuildFramedMessage(string message)
        {
            var header = _options.MessageHeader ?? string.Empty;
            var footer = _options.MessageFooter ?? string.Empty;
            return header + (message ?? string.Empty) + footer;
        }
        /// <summary>
        /// JSON格式尝试解析(静默失败)
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        private static bool TryParseJsonSilent(string message)
        {
            if (string.IsNullOrWhiteSpace(message)) return false;
@@ -142,6 +199,11 @@
            try { JsonDocument.Parse(message); return true; } catch { return false; }
        }
        /// <summary>
        /// utf-8 å¯èƒ½æ€§æ£€æµ‹
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private static bool IsLikelyUtf8(byte[] data)
        {
            int i = 0;
@@ -171,5 +233,58 @@
            }
            return true;
        }
        /// <summary>
        /// è¯»å–完整消息
        /// </summary>
        /// <param name="networkStream">字节流</param>
        /// <param name="encoding">编码格式</param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        private async Task<string?> ReceiveFullMessageAsync(NetworkStream networkStream, Encoding encoding, CancellationToken cancellationToken)
        {
            var header = _options.MessageHeader ?? string.Empty;
            var footer = _options.MessageFooter ?? string.Empty;
            var buffer = new byte[1024];
            var builder = new StringBuilder();
            while (true)
            {
                int bytesRead = await networkStream.ReadAsync(buffer.AsMemory(0, buffer.Length), cancellationToken);
                if (bytesRead <= 0)
                {
                    if (builder.Length == 0) return null;
                    return string.IsNullOrEmpty(header) && string.IsNullOrEmpty(footer) ? builder.ToString() : null;
                }
                builder.Append(encoding.GetString(buffer, 0, bytesRead));
                if (string.IsNullOrEmpty(header) && string.IsNullOrEmpty(footer))
                {
                    if (!networkStream.DataAvailable)
                    {
                        break;
                    }
                    continue;
                }
                var data = builder.ToString();
                var headerIndex = string.IsNullOrEmpty(header) ? 0 : data.IndexOf(header, StringComparison.Ordinal);
                if (headerIndex < 0)
                {
                    continue;
                }
                var startIndex = headerIndex + header.Length;
                var footerIndex = string.IsNullOrEmpty(footer) ? data.Length : data.IndexOf(footer, startIndex, StringComparison.Ordinal);
                if (footerIndex >= 0)
                {
                    return data.Substring(startIndex, footerIndex - startIndex);
                }
            }
            return builder.ToString();
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Server.cs
@@ -10,6 +10,13 @@
{
    public partial class TcpSocketServer
    {
        /// <summary>
        /// å¼‚步启动TCP服务器,使其开始接受传入的客户端连接。
        /// </summary>
        /// <remarks>如果服务器已在运行或通过配置禁用,此方法将立即返回而不启动服务器。
        /// åŽç»­çš„客户端监控和接受操作在后台任务中运行。此方法不会阻塞调用线程。</remarks>
        /// <param name="cancellationToken">可用于请求取消服务器启动及后续后台操作的取消令牌。</param>
        /// <returns>表示异步启动操作的任务。当服务器开始监听连接时任务完成。</returns>
        public Task StartAsync(CancellationToken cancellationToken)
        {
            if (IsRunning || !_options.Enabled)
@@ -34,6 +41,13 @@
            return Task.CompletedTask;
        }
        //// <summary>
        /// å¼‚步停止服务器并等待所有活动客户端连接完成。
        /// </summary>
        /// <remarks>如果服务器未运行,此方法将立即返回而不执行任何操作。
        /// æ­¤æ–¹æ³•确保所有客户端任务完成后才将服务器标记为已停止。</remarks>
        /// <param name="cancellationToken">可用于在完成前取消停止操作的取消令牌。</param>
        /// <returns>表示异步停止操作的任务。</returns>
        public async Task StopAsync(CancellationToken cancellationToken)
        {
            if (!IsRunning)
@@ -58,6 +72,13 @@
            IsRunning = false;
        }
        /// <summary>
        /// æŒç»­æŽ¥å—传入的TCP客户端连接,直到请求取消。
        /// </summary>
        /// <remarks>此方法旨在后台运行以处理新的客户端连接。
        /// å¦‚果监听器被释放或通过提供的令牌请求取消,循环将退出。</remarks>
        /// <param name="cancellationToken">可用于请求取消接受循环的令牌。当请求取消时,循环将立即终止。</param>
        /// <returns>表示异步接受循环操作的任务。</returns>
        private async Task AcceptLoopAsync(CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
@@ -97,6 +118,12 @@
            }
        }
        /// <summary>
        /// ä»Žå†…部集合中移除指定标识符的客户端,并释放相关资源。
        /// </summary>
        /// <remarks>此方法关闭客户端连接,释放任何关联的锁,并移除对客户端的所有引用,
        /// åŒ…括设备绑定和编码信息。通过对内部同步对象加锁确保线程安全。</remarks>
        /// <param name="clientId">要移除的客户端的唯一标识符。不能为null或空。</param>
        private void RemoveClient(string clientId)
        {
            lock (_syncRoot)
@@ -124,6 +151,13 @@
            }
        }
        /// <summary>
        /// å¼‚步监控已连接的客户端,并断开超过配置超时时间闲置的客户端连接。
        /// </summary>
        /// <remarks>此方法持续检查闲置客户端,如果其不活动时间超过指定的空闲超时,则断开连接。
        /// ç›‘控循环将持续运行,直到通过提供的令牌请求取消。</remarks>
        /// <param name="cancellationToken">可用于请求终止监控循环的取消令牌。</param>
        /// <returns>表示异步监控操作的任务。</returns>
        private async Task MonitorClientsAsync(CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
@@ -156,6 +190,13 @@
            }
        }
        /// <summary>
        /// åŸºäºŽè¿œç¨‹ç»ˆç«¯ç‚¹èŽ·å–æŒ‡å®šTCP客户端的唯一标识符字符串。
        /// </summary>
        /// <remarks>返回的标识符适用于在日志记录或跟踪场景中区分客户端。
        /// å¦‚果客户端的远程终端点不可用,将生成GUID以确保唯一性。</remarks>
        /// <param name="client">要获取标识符的TCP客户端。不能为null。</param>
        /// <returns>表示客户端远程终端点的字符串(如果可用);否则为生成的新GUID字符串。</returns>
        public static string GetClientId(TcpClient client)
        {
            return client.Client.RemoteEndPoint?.ToString() ?? Guid.NewGuid().ToString();
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.cs
@@ -16,20 +16,81 @@
    public partial class TcpSocketServer : IDisposable
    {
        private readonly SocketServerOptions _options;
        /// <summary>
        /// æä¾›ä¸€ä¸ªå¯ç”¨äºŽåŒæ­¥å¯¹åŒ…含实例的访问的对象。
        /// </summary>
        /// <remarks>在对实例实现线程安全操作时,可将此对象用作锁定目标。此模式通常用于避免死锁并确保一致的同步。</remarks>
        public readonly object _syncRoot = new();
        private TcpListener? _listener;
        /// <summary>
        /// è¡¨ç¤ºç”¨äºŽå‘出进行中操作的取消请求的取消令牌源。
        /// </summary>
        /// <remarks>如果当前没有活动的取消机制,此字段可能为null。使用此令牌源取消支持取消的任务或操作。</remarks>
        public CancellationTokenSource? _cts;
        /// <summary>
        /// æä¾›è¡¨ç¤ºæ´»åŠ¨å®¢æˆ·ç«¯æ“ä½œçš„ä»»åŠ¡åˆ—è¡¨ã€‚
        /// </summary>
        /// <remarks>此字段用于内部跟踪异步客户端活动。它是只读的,不应在包含类外部直接修改。</remarks>
        public readonly List<Task> _clientTasks = new();
        /// <summary>
        /// æä¾›ä»Žå®¢æˆ·ç«¯æ ‡è¯†ç¬¦åˆ°å…¶å…³è”çš„TCP客户端连接的映射。
        /// </summary>
        /// <remarks>此字典允许通过唯一字符串标识符访问活动的TCP客户端。在多线程场景中,对集合的修改应小心进行以避免并发问题。</remarks>
        public readonly Dictionary<string, TcpClient> _clients = new();
        /// <summary>
        /// æä¾›ä»Žè®¾å¤‡æ ‡è¯†ç¬¦åˆ°å…¶å¯¹åº”绑定值的映射。
        /// </summary>
        /// <remarks>此字段是只读的,用于包含类内部使用。应通过指定的方法或属性对字典进行修改以确保一致性。</remarks>
        public readonly Dictionary<string, string> _deviceBindings = new();
        /// <summary>
        /// æä¾›ä»Žå®¢æˆ·ç«¯æ ‡è¯†ç¬¦åˆ°å…¶å…³è”锁的映射,用于同步对客户端特定资源的访问。
        /// </summary>
        /// <remarks>字典中的每个条目将一个唯一的客户端ID与一个<see cref="SemaphoreSlim"/>实例关联,实现每个客户端的线程安全操作。此集合用于内部协调并发访问,不应直接修改。</remarks>
        public readonly Dictionary<string, SemaphoreSlim> _clientLocks = new();
        /// <summary>
        /// æä¾›ä»Žå®¢æˆ·ç«¯æ ‡è¯†ç¬¦åˆ°å…¶å…³è”文本编码的映射。
        /// </summary>
        /// <remarks>此字典用于内部跟踪已连接客户端的编码偏好。键表示客户端标识符,值指定用于文本操作的对应<see cref="System.Text.Encoding"/>。</remarks>
        public readonly Dictionary<string, Encoding> _clientEncodings = new();
        /// <summary>
        /// å­˜å‚¨æ¯ä¸ªå®¢æˆ·ç«¯æœ€åŽæ´»åŠ¨çš„æ—¶é—´æˆ³ï¼Œä»¥å®¢æˆ·ç«¯æ ‡è¯†ç¬¦ä¸ºé”®ã€‚
        /// </summary>
        /// <remarks>此字段用于内部跟踪客户端活动。字典将客户端标识符映射到对应的最后活动时间(UTC)。直接修改此集合可能影响客户端会话管理逻辑。</remarks>
        public readonly Dictionary<string, DateTime> _clientLastActive = new();
        /// <summary>
        /// æŒ‡å®šåŒ…含类型中字符数据使用的文本编码。
        /// </summary>
        /// <remarks>使用此字段确定处理字符数据时如何编码或解码文本。编码影响字节如何被解释为字符,反之亦然。常见的编码包括UTF8、ASCII和Unicode。</remarks>
        public readonly Encoding _textEncoding;
        /// <summary>
        /// è¡¨ç¤ºè‡ªåŠ¨æ£€æµ‹åˆ°çš„GB2312编码(如果可用)。
        /// </summary>
        /// <remarks>通常从输入数据确定编码时设置此字段。如果检测失败或未执行检测,值可能为null。</remarks>
        public readonly Encoding? _autoDetectedGb2312;
        private readonly string _logFile;
        private Task? _monitorTask;
        /// <summary>
        /// ä½¿ç”¨æŒ‡å®šçš„æœåŠ¡å™¨é€‰é¡¹åˆå§‹åŒ– TcpSocketServer ç±»çš„æ–°å®žä¾‹ã€‚
        /// </summary>
        /// <remarks>如果启用了 AutoDetectEncoding é€‰é¡¹ï¼ŒæœåŠ¡å™¨å°†é»˜è®¤ä½¿ç”¨ UTF-8 ç¼–码,
        /// å¹¶å°è¯•支持 GBK ç¼–码进行自动检测。如果编码检测失败或提供了无效的编码名称,
        /// å°†å›žé€€ä½¿ç”¨ UTF-8 ç¼–码。日志文件路径由 LogFilePath é€‰é¡¹å†³å®šï¼Œ
        /// å¦‚果未指定,则默认为应用程序基目录下的 'socketserver.log' æ–‡ä»¶ã€‚</remarks>
        /// <param name="options">套接字服务器的配置选项。不能为 null。提供编码设置、日志文件路径和自动检测行为等配置。</param>
        public TcpSocketServer(IOptions<SocketServerOptions> options)
        {
            _options = options.Value;
@@ -52,6 +113,7 @@
        public bool IsRunning { get; private set; }
        public event Func<string, bool, TcpClient, RobotSocketState, Task<string?>>? MessageReceived;
        public event Func<string, Task<string?>>? RobotReceived;
        private void Log(string message)
ÏîÄ¿×ÊÁÏ/É豸ЭÒé/¾ÐÊø»ú¶Ô½ÓЭÒé/²å°Î¶¤»ú¶Ô½ÓЭÒé.xlsx
Binary files differ