c# socket tcp 通信 结构体 字节流 大端序列 小端序列

发布时间 2023-09-02 15:27:31作者: 虎虎生威啊

SeerAGV_2/SeerMessage.cs

using System.Reflection;
using System.Runtime.InteropServices;

namespace SeerAGV
{
    public struct SeerMessageHead
    {
        public byte sync;
        public byte version;
        public ushort number;
        public uint length;
        public ushort type;
#pragma warning disable CS0169 // 禁用 CS0169 警告
        private readonly byte ref0;      //保留
        private readonly byte ref1;      //保留
        private readonly byte ref2;      //保留
        private readonly byte ref3;      //保留
        private readonly byte ref4;      //保留
        private readonly byte ref5;      //保留
#pragma warning restore CS0169 // 启用 CS0169 警告

        public readonly byte[] ToBytes()
        {

            int size = Marshal.SizeOf(this);
            byte[] bytes = new byte[size];

            IntPtr ptr = Marshal.AllocHGlobal(size);
            Marshal.StructureToPtr(this, ptr, true);
            Marshal.Copy(ptr, bytes, 0, size);
            Marshal.FreeHGlobal(ptr);

            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(bytes, 2, 2);
                Array.Reverse(bytes, 4, 4);
                Array.Reverse(bytes, 8, 2);
            }

            return bytes;
        }


    };


    public struct SeerMessage
    {
        public byte[] head;
        public byte[] body;

        public SeerMessage(SeerMessageHead head, string body)
        {
            this.body = SetBody(body);
            // head中的lenght是body的长度
            head.length = (uint)this.body.Length;
            this.head = head.ToBytes();
        }


        public readonly int Length()
        {
            return body.Length + Marshal.SizeOf(head);
        }


        private static byte[] SetBody(string str)
        {
            byte[] result = new byte[str.Length];
            byte[] buffer = System.Text.Encoding.UTF8.GetBytes(str);
            for (int i = 0; i < buffer.Length; i++)
            {
                result[i] = Convert.ToByte(buffer[i].ToString("X2"), 16);
            }
            return result;
        }
    }
}

SeerAGV_2/SeerAGV.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

SeerAGV_2/Program.cs

using System.Net.Sockets;
using System.Runtime.InteropServices;

namespace SeerAGV
{

    /// <summary>
    /// 
    /// </summary>
    public class SeerSocket
    {

        /// <summary>
        /// 该方法接受一个十六进制字符串作为参数,并返回一个字节数组。
        /// 它首先使用 `Replace` 方法删除字符串中的所有空格,
        /// 然后检查字符串的长度是否为偶数。如果不是,它会在字符串末尾添加一个空格。
        /// 接下来,它创建一个新的字节数组,并使用 `for` 循环遍历字符串中的每两个字符。
        /// 在循环中,它使用 `Substring` 方法获取当前两个字符,然后使用 `Convert.ToByte` 方法将它们转换为一个字节,并将该字节存储在字节数组中。
        /// 最后,它返回字节数组。
        /// </summary>
        /// <param name="hexString"></param>
        /// <returns></returns>
        private static byte[] HexStrTobyte(string hexString)
        {
            hexString = hexString.Replace(" ", "");
            if (hexString.Length % 2 != 0)
                hexString += " ";

            byte[] returnBytes = new byte[hexString.Length / 2];

            for (int i = 0; i < returnBytes.Length; i++)
            {
                returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2).Trim(), 16);
            }
            return returnBytes;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="bytesBuffer"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static T BytesToStructure<T>(byte[] bytesBuffer)
        {
            if (bytesBuffer.Length < Marshal.SizeOf(typeof(T)))
            {
                throw new ArgumentException("size error");
            }

            nint bufferHandler = Marshal.AllocHGlobal(bytesBuffer.Length);

            for (int index = 0; index < bytesBuffer.Length; index++)
            {
                Marshal.WriteByte(bufferHandler, index, bytesBuffer[index]);
            }

            // T? structObject = (T)Marshal.PtrToStructure(bufferHandler, typeof(T));
            T? structObject = Marshal.PtrToStructure<T>(bufferHandler);

            Marshal.FreeHGlobal(bufferHandler);

            if (structObject == null)
            {
                throw new InvalidOperationException("Failed to convert bytes to structure.");
            }

            return structObject!;
        }

        /// <summary>
        ///该方法接受一个字符串作为参数,并返回一个字节数组。
        ///它首先创建一个新的字节数组,其长度与输入字符串的长度相同。
        ///然后,它使用 `System.Text.Encoding.UTF8.GetBytes` 方法将输入字符串转换为 UTF-8 编码的字节数组。
        ///接下来,它使用 `for` 循环遍历该字节数组中的每个字节。
        ///在循环中,它使用 `ToString` 方法将当前字节转换为十六进制字符串,并使用 `Convert.ToByte` 方法将该字符串转换为字节,并将该字节存储在结果字节数组中。
        ///最后,它返回结果字节数组。希望这对您有所帮助!
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private static byte[] NormalStrToHexByte(string str)
        {
            byte[] result = new byte[str.Length];
            byte[] buffer = System.Text.Encoding.UTF8.GetBytes(str);
            for (int i = 0; i < buffer.Length; i++)
            {
                result[i] = Convert.ToByte(buffer[i].ToString("X2"), 16);
            }
            return result;
        }


        public static byte[] SeerMessageHeadToBytes(SeerMessageHead msg)
        {
            var hsize = Marshal.SizeOf(msg);
            byte[] bytes = new byte[hsize];
            nint structPtr = Marshal.AllocHGlobal(hsize);
            Marshal.StructureToPtr(msg, structPtr, false);
            Marshal.Copy(structPtr, bytes, 0, hsize);
            Marshal.FreeHGlobal(structPtr);
            return bytes;
        }




        public static void Send()
        {
            try
            {
                // 创建tcp连接
                var client = new TcpClient("127.0.0.1", 19204);

                if (client.Connected)
                {
                    NetworkStream serverStream = client.GetStream();

                    // 创建一些变量
                    var seerMsg = new SeerMessage(new SeerMessageHead
                    {
                        sync = 0x5a,
                        version = 0x01,
                        number = 0x0001,
                        type = 2002,
                    }, $"{{\"x\":10.0,\"y\":3.0,\"angle\":0}}");
                    serverStream.Write(seerMsg.head, 0, seerMsg.head.Length);
                    serverStream.Write(seerMsg.body, 0, seerMsg.body.Length);
                    serverStream.Flush();



                    byte[] inStream = new byte[16];
                    while (16 != serverStream.Read(inStream, 0, 16))
                    {
                        Thread.Sleep(20);
                    }

                    var recv_head = BytesToStructure<SeerMessageHead>(inStream);
                    byte[] recvbyte = BitConverter.GetBytes(recv_head.length);
                    Array.Reverse(recvbyte);
                    var dsize = BitConverter.ToUInt32(recvbyte, 0);

                    const int bufferSize = 512;
                    var datalist = new List<byte>();
                    int count = 0;

                    while (true)
                    {
                        byte[] buffer = new byte[bufferSize];
                        int readSize = serverStream.Read(buffer, 0, bufferSize);

                        count += readSize;
                        datalist.AddRange(buffer);

                        if (count == dsize)
                        {
                            break;
                        }

                        Thread.Sleep(10);
                    }

                    var content = BitConverter.ToString(SeerMessageHeadToBytes(recv_head)).Replace("-", " ");//normalStrToHexStr(Encoding.UTF8.GetString(seerMessageHeadToBytes(recv_head)));
                    Console.WriteLine(content);


                    string str = System.Text.Encoding.UTF8.GetString(datalist.ToArray());


                    client.Close();
                }
            }
            catch (SocketException)
            {

            }
            catch (IOException)
            {
            }
        }

    }

    /// <summary>
    /// 将socket封装成一个类
    /// </summary>
    public class SocketClient
    {
        // 定义一些私有字段,用于存储客户端、网络流和缓冲区大小等信息
        private TcpClient client;
        private NetworkStream stream;
        private const int bufferSize = 512;

        // 定义一个构造函数,用于创建一个socket客户端,并连接到指定的服务器地址和端口
        public SocketClient(string address, int port)
        {
            client = new TcpClient(address, port);
            if (client.Connected)
            {
                stream = client.GetStream();
            }
            else
            {
                throw new Exception("Failed to connect to the server.");
            }
        }

        // 定义一个Send方法,用于发送一个SeerMessage类型的结构体到服务器
        public void Send(SeerMessage message)
        {
            if (stream != null)
            {
                stream.Write(message.head, 0, message.head.Length);
                stream.Write(message.body, 0, message.body.Length);
                stream.Flush();
            }
            else
            {
                throw new Exception("The network stream is null.");
            }
        }

        // 定义一个Receive方法,用于接收服务器返回的一个SeerMessage类型的结构体
        public string Receive()
        {
            if (stream != null)
            {
                // 接收消息头
                byte[] headBytes = new byte[16];
                while (16 != stream.Read(headBytes, 0, 16))
                {
                    Thread.Sleep(20);
                }
                var head = BytesToStructure<SeerMessageHead>(headBytes);

                // 接收消息体
                byte[] lengthBytes = BitConverter.GetBytes(head.length);
                Array.Reverse(lengthBytes);
                var bodyLength = BitConverter.ToUInt32(lengthBytes, 0);

                var bodyList = new List<byte>();
                int count = 0;

                while (true)
                {
                    byte[] buffer = new byte[bufferSize];
                    int readSize = stream.Read(buffer, 0, bufferSize);

                    count += readSize;
                    bodyList.AddRange(buffer);

                    if (count == bodyLength)
                    {
                        break;
                    }

                    Thread.Sleep(10);
                }

                var body = bodyList.ToArray();


                // 返回消息
                return BitConverter.ToString(body);

            }
            else
            {
                throw new Exception("The network stream is null.");
            }
        }

        public static T BytesToStructure<T>(byte[] bytesBuffer)
        {
            if (bytesBuffer.Length < Marshal.SizeOf(typeof(T)))
            {
                throw new ArgumentException("size error");
            }

            nint bufferHandler = Marshal.AllocHGlobal(bytesBuffer.Length);

            for (int index = 0; index < bytesBuffer.Length; index++)
            {
                Marshal.WriteByte(bufferHandler, index, bytesBuffer[index]);
            }

            // T? structObject = (T)Marshal.PtrToStructure(bufferHandler, typeof(T));
            T? structObject = Marshal.PtrToStructure<T>(bufferHandler);

            Marshal.FreeHGlobal(bufferHandler);

            if (structObject == null)
            {
                throw new InvalidOperationException("Failed to convert bytes to structure.");
            }

            return structObject!;
        }


    }



    class Program
    {
        static void Main(string[] args)
        {
            var myObject = new SeerSocket();
            SeerSocket.Send();

        }
    }

}