You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# TCP/IP服务开发:多结构体发送与接收实现方案求助

C# 多连接TCP服务:结构体收发与序列化实现指导

核心问题修正点

1. 结构体可空类型序列化问题

原ResponseMessage中的可空值类型(byte?、short?等)无法被Marshal正确序列化,因为可空类型本质是嵌套结构体,内存布局和定义的Size=12不匹配。需改为非可空类型,用约定默认值表示空状态(例如用0表示未设置)。

2. 服务器多连接并发处理

原服务器采用同步循环处理连接,同一时间只能处理一个客户端。需改为异步并行处理每个连接,避免阻塞后续请求。

3. TCP粘包/拆包处理

TCP是流式协议,连续发送的结构体可能被合并或拆分。需按结构体固定长度读取数据,确保每次反序列化一个完整的结构体。

4. 序列化/反序列化逻辑优化

确保序列化时仅处理结构体对应长度的字节,反序列化时只使用读取到的有效数据,避免内存越界或数据错乱。


修正后的客户端代码

using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace TcpStructClient
{
    class Program
    {
        static async Task Main(string[] args)
        {
            List<ResponseMessage> messages = new List<ResponseMessage>();

            messages.Add(
                new ResponseMessage()
                {
                    cmd = 2,
                    cmd_num = 4,
                    client_id = 5,
                    local_part_num = 1,
                    end_buffer_address = 100500,
                    group_id = 1
                }
            );

            messages.Add(
                new ResponseMessage()
                {
                    cmd = 2,
                    cmd_num = 24,
                    client_id = 11,
                    local_part_num = 25,
                    end_buffer_address = 300500,
                    group_id = 33
                }
            );

            using TcpClient tcpClient = new TcpClient();
            try
            {
                await tcpClient.ConnectAsync("127.0.0.1", 8888);
                var stream = tcpClient.GetStream();

                foreach (var msg in messages)
                {
                    var byteArray = StructureToByteArray(msg);
                    await stream.WriteAsync(byteArray, 0, byteArray.Length);
                    // 可选:添加小延迟避免粘包(生产环境建议用长度前缀)
                    await Task.Delay(10);
                }

                Console.WriteLine("所有消息发送完成");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"发送失败:{ex.Message}");
            }
        }

        /// <summary>
        /// 将结构体转换为字节数组
        /// </summary>
        static byte[] StructureToByteArray(object obj)
        {
            int size = Marshal.SizeOf(obj);
            byte[] arr = new byte[size];
            IntPtr ptr = Marshal.AllocHGlobal(size);

            try
            {
                Marshal.StructureToPtr(obj, ptr, false);
                Marshal.Copy(ptr, arr, 0, size);
            }
            finally
            {
                Marshal.FreeHGlobal(ptr);
            }
            return arr;
        }

        [StructLayout(LayoutKind.Sequential, Size = 12)]
        struct RequestMessage
        {
            public sbyte cmd;
            public byte cmd_num;
            public short rsvd;
            public int request_address;
            public int request_size;

            public RequestMessage()
            {
                cmd = 1;
                cmd_num = 200;
                rsvd = 0;
                request_address = 192133333;
                request_size = 350350333;
            }
        }

        [StructLayout(LayoutKind.Sequential, Size = 12, Pack = 1)]
        struct ResponseMessage
        {
            public sbyte cmd;
            public byte cmd_num;
            public short local_part_num;
            public short group_id;
            public short client_id;
            public int end_buffer_address;

            public ResponseMessage()
            {
                cmd = 2;
                cmd_num = 200;
                local_part_num = 1;
                group_id = 1;
                client_id = 4;
                end_buffer_address = 123213123;
            }
        }
    }
}

修正后的服务器代码

using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace TcpStructServer
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var tcpListener = new TcpListener(IPAddress.Any, 8888);
            try
            {
                tcpListener.Start();
                Console.WriteLine("服务器已启动,等待客户端连接...");

                int clientCounter = 0;
                while (true)
                {
                    var tcpClient = await tcpListener.AcceptTcpClientAsync();
                    clientCounter++;
                    // 异步处理客户端连接,不阻塞后续连接
                    _ = HandleClientAsync(tcpClient, clientCounter);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"服务器异常:{ex.Message}");
                tcpListener.Stop();
            }
        }

        static async Task HandleClientAsync(TcpClient tcpClient, int clientId)
        {
            Console.WriteLine($"客户端 {clientId} 已连接");
            var stream = tcpClient.GetStream();
            int structSize = Marshal.SizeOf(typeof(ResponseMessage));
            byte[] buffer = new byte[structSize];

            try
            {
                while (true)
                {
                    // 按结构体固定长度读取数据
                    int bytesRead = await stream.ReadAsync(buffer, 0, structSize);
                    if (bytesRead == 0)
                    {
                        Console.WriteLine($"客户端 {clientId} 断开连接");
                        break;
                    }

                    // 确保读取到完整的结构体
                    if (bytesRead == structSize)
                    {
                        ResponseMessage msg = FromBytes(buffer);
                        StringBuilder sb = new StringBuilder();
                        sb.Append($"客户端 {clientId} 收到消息:");
                        sb.Append($"cmd={msg.cmd}, ");
                        sb.Append($"cmd_num={msg.cmd_num}, ");
                        sb.Append($"local_part_num={msg.local_part_num}, ");
                        sb.Append($"group_id={msg.group_id}, ");
                        sb.Append($"client_id={msg.client_id}, ");
                        sb.Append($"end_buffer_address={msg.end_buffer_address}");
                        Console.WriteLine(sb.ToString());
                    }
                    else
                    {
                        Console.WriteLine($"客户端 {clientId} 数据不完整,已丢弃");
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"客户端 {clientId} 异常:{ex.Message}");
            }
            finally
            {
                tcpClient.Close();
            }
        }

        static ResponseMessage FromBytes(byte[] arr)
        {
            ResponseMessage msg = new ResponseMessage();
            int size = Marshal.SizeOf(msg);
            IntPtr ptr = IntPtr.Zero;

            try
            {
                ptr = Marshal.AllocHGlobal(size);
                Marshal.Copy(arr, 0, ptr, size);
                msg = (ResponseMessage)Marshal.PtrToStructure(ptr, typeof(ResponseMessage));
            }
            finally
            {
                Marshal.FreeHGlobal(ptr);
            }
            return msg;
        }

        [StructLayout(LayoutKind.Sequential, Size = 12)]
        struct RequestMessage
        {
            public sbyte cmd;
            public byte cmd_num;
            public short rsvd;
            public int request_address;
            public int request_size;

            public RequestMessage()
            {
                cmd = 1;
                cmd_num = 200;
                rsvd = 0;
                request_address = 192133333;
                request_size = 350350333;
            }
        }

        [StructLayout(LayoutKind.Sequential, Size = 12, Pack = 1)]
        struct ResponseMessage
        {
            public sbyte cmd;
            public byte cmd_num;
            public short local_part_num;
            public short group_id;
            public short client_id;
            public int end_buffer_address;

            public ResponseMessage()
            {
                cmd = 2;
                cmd_num = 200;
                local_part_num = 1;
                group_id = 1;
                client_id = 4;
                end_buffer_address = 123213123;
            }
        }
    }
}

关键说明

  • 结构体内存布局:添加Pack=1确保结构体按1字节对齐,避免不同平台下内存布局差异。
  • 多连接处理:服务器用_ = HandleClientAsync(...)异步处理每个客户端,实现并发连接支持。
  • 粘包处理:按结构体固定长度读取,确保每次获取完整的结构体数据。如果结构体长度不固定,可先发送4字节的长度前缀,再发送结构体数据。
  • 序列化可靠性:移除可空类型,改用非可空类型+默认值的方式,确保Marshal能正确序列化/反序列化。

内容的提问来源于stack exchange,提问作者RxNsx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 08:25:30