c#TCP发送结构体数据

byte数组与结构体数据相互转换

using System.Collections;
using System.Collections.Generic;
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Reflection;
using System.Runtime.InteropServices;

namespace ConsoleApplication13
{
    public class ByteConvertHelper
    {

        /// <summary>
        /// 将结构体对象转换为byte数组
        /// </summary>
        /// <param name="obj">被转换对象</param>
        /// <returns>转换后byte数组</returns>
        public static byte[] Object2Bytes(object obj)
        {

            int size = Marshal.SizeOf(obj);//获取对象的非托管大小
            byte[] bytes = new byte[1024];
            IntPtr structPtr = Marshal.AllocHGlobal(size);//从进程的非托管内存中分配内存
            Marshal.StructureToPtr(obj, structPtr, false);//将数据从托管内存中封送到非托管内存中
            Marshal.Copy(structPtr, bytes, 0, size);//将数据从非托管内存指针复制托管整数数组中
            Marshal.FreeHGlobal(structPtr);//释放内存
            return bytes;
        }


        /// <summary>
        /// 将byte数组转换成对象
        /// </summary>
        /// <param name="buff">被转换byte数组</param>
        /// <param name="typ">转换成的类名</param>
        /// <returns>转换完成后的对象</returns>
        public static object Bytes2Object(byte[] buff, Type type)
        {

            int size = Marshal.SizeOf(type);
            if (size > buff.Length)
            {
                return null;
            }
            IntPtr structPtr = Marshal.AllocHGlobal(size);//开辟内存
            Marshal.Copy(buff, 0, structPtr, size);//
            object obj = Marshal.PtrToStructure(structPtr, type);//将数据从非托管内存块封送到新分配的指定类型的托管对象。
            Marshal.FreeHGlobal(structPtr);//释放内存
            return obj;
        }
    }
}

结构体数据定义

//下面是结构体的类定义

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;


namespace ConsoleApplication12
{
    public enum CurrentStatus
    {
        Serching,
        Playing,
        Free,
    }


    public enum PlayerSide
    {
        Left,
        Right,
    }


    public class PlayerData
    {
        [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]              //必须要写,保证结构体的保存在内存中的方式
        public struct PlayerStat
        {
            public float PositionX;
            public float PositionY;


            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]                  //必须要写,不然字符串没法解析
            public string Name;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
            public string SendId;
            public CurrentStatus status;
            public PlayerSide side;
        }
    }
}

服务端

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;

namespace ConsoleApplication13
{
    class Program
    {
        static void Main(string[] args)
        {
            Socket Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint sIPEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8000);
            Server.Bind(sIPEndPoint);
            Server.Listen(10);
            Socket client = Server.Accept();
            byte[] Buff = new byte[1024];
            client.Receive(Buff);
            PlayerData.PlayerStat data =  (PlayerData.PlayerStat)ByteConvertHelper.Bytes2Object(Buff, typeof(PlayerData.PlayerStat));
            float x = data.PositionX;
        }
    }
}

客户端

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Reflection;
using System.Runtime.InteropServices;


namespace ConsoleApplication12
{
    class Program
    {
        struct SendMsg
        {
            public SendMsg(string Begion, double cTestItem1, double cTestItem2, string End)
            {
                begin = Begion;
                TestItem1 = cTestItem1;
                TestItem2 = cTestItem2;
                end = End;
            }
            public string begin;
            public double TestItem1;
            public double TestItem2;
            public string end;

        }
        static void Main(string[] args)
        {
            Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint theIPEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8000);
            client.Connect(theIPEndPoint);
           
            PlayerData.PlayerStat data = new PlayerData.PlayerStat();
            data.PositionX =3.15f;
            data.PositionY = 3.25f;
            data.Name = "Name";
            data.SendId = "123";
            data.status = CurrentStatus.Free;
            data.side = PlayerSide.Right;         
            byte[] byteData = ByteConvertHelper.Object2Bytes(data);
            client.Send(byteData);
          
        }
    }
}

在使用 C# 通过 OPC UA 协议读写结构体数据时,需要利用 OPC UA 的复杂数据类型支持机制。OPC UA 提供了对结构体(`Structure`)和数据类型(`DataType`)的支持,允许客户端与服务器之间交换复杂数据结构。这种机制基于 OPC UA 的元数据(`DataTypeDictionary`)和编码规则(如 UA 二进制编码)实现 [^1]。 ### 数据结构定义 OPC UA 中的结构体通常通过 `StructureDescription` 定义,并在地址空间中注册为 `DataType`。每个结构体由多个字段组成,每个字段具有名称、数据类型和描述等属性。 ### 使用 C# 实现结构体读写 #### 1. OPC UA 客户端初始化 首先,需要创建 OPC UA 客户端实例,并连接到 OPC UA 服务器。 ```csharp using Opc.Ua; using Opc.Ua.Client; ApplicationConfiguration config = new ApplicationConfiguration(); config.ApplicationName = "OPC UA Structure Reader"; config.SecurityConfiguration = new SecurityConfiguration(); config.TransportConfigurations.Add(new TransportConfiguration()); config.ClientConfiguration = new ClientConfiguration(); var client = ClientFactory.Create(config, new ConfiguredEndpoint(null, new Uri("opc.tcp://localhost:4840"))); client.Connect(); ``` #### 2. 读取结构体数据 读取结构体数据时,需要确保服务器端已定义该结构体类型,并在地址空间中存在。使用 `ReadValue` 方法可以读取节点的值,其中结构体数据会以 `ExtensionObject` 的形式返回。 ```csharp NodeId nodeId = new NodeId("ns=2;s=My.StructureNode"); DataValue dataValue = client.ReadValue(nodeId); object rawValue = dataValue.Value; if (rawValue is ExtensionObject extensionObject) { object decodedValue = extensionObject.DecodeBody(client.MessageContext); Console.WriteLine(decodedValue); } ``` #### 3. 写入结构体数据 写入结构体数据时,需要构造一个符合 OPC UA 编码规范的 `ExtensionObject`。可以使用 `EncodeableFactory` 来创建结构体实例。 ```csharp MyStructure structure = new MyStructure { Field1 = 123, Field2 = "Test" }; ExtensionObject extension = new ExtensionObject( new NodeId("ns=2;i=1001"), // DataType ID structure ); WriteValueCollection writes = new WriteValueCollection(); writes.Add(new WriteValue { NodeId = nodeId, AttributeId = Attributes.Value, Value = new DataValue(new Variant(extension)) }); StatusCodeCollection results; DiagnosticInfoCollection diagnosticInfos; client.Write( null, 0, TimestampsToReturn.Both, writes, out results, out diagnosticInfos ); ``` #### 4. 注册结构体类型 如果客户端需要处理结构体类型,必须注册 `EncodeableFactory`,以便 OPC UA 客户端能够正确解析结构体。 ```csharp EncodeableFactory factory = new EncodeableFactory(); factory.RegisterType(typeof(MyStructure), "ns=2;i=1001"); client.MessageContext.EncodeableFactory = factory; ``` ### 数据类型定义与编码 OPC UA 支持两种主要的编码方式:UA 二进制编码和 XML 编码。UA 二进制编码是默认选项,适用于高效的数据传输。结构体的编码依赖于 OPC UA 的 `DataValue` 和 `ExtensionObject` 类型,确保数据能够在客户端与服务器之间正确传输 [^1]。 ### 结构体数据格式示例 ```csharp public class MyStructure : IEncodeable { public uint Field1 { get; set; } public string Field2 { get; set; } public NodeId TypeId => new NodeId("ns=2;i=1001"); public void Encode(IEncoder encoder) { encoder.WriteUInt32("Field1", Field1); encoder.WriteString("Field2", Field2); } public void Decode(IDecoder decoder) { Field1 = decoder.ReadUInt32("Field1"); Field2 = decoder.ReadString("Field2"); } public bool IsEqual(IEncodeable another) { if (another is MyStructure other) { return Field1 == other.Field1 && Field2 == other.Field2; } return false; } } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值