打包任意坐标数的udp包
Packing udp packet of arbitrary number of coordinates
所以我得到了可变数量的坐标(纬度和经度)。我想以一种直接的方式打包这些坐标,可以通过 UDP 轻松发送和解包。我如何使用 C# 解决这个问题?
我假设我声明了一个 struct
,然后使用内置的编组来获取要发送的字节数组。当涉及的点数可变时,如何做到这一点?
我的大部分编程经验都在 Python,但我需要用 C# 来完成,我对它的经验有限。
编辑:我将添加一些我正在测试的代码,因为我觉得没有人只响应文本。
namespace ConsoleApplication1
{
class Testing
{
static void Main(string[] args)
{
// Console.WriteLine("Starting");
// string text = "Hello";
// byte[] data = Encoding.ASCII.GetBytes(text);
StartPacket test = new StartPacket();
test.len = 3;
List<double> points = new List<double>();
points.Add(3.14);
points.Add(5);
points.Add(-1023.1231311);
test.points = points;
byte[] data = StructureToByteArray(test);
SendUdp(65456, "192.168.20.100", data);
}
static void SendUdp(int srcPort, string dstIp, byte[] data)
{
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,
ProtocolType.Udp);
IPAddress dst = IPAddress.Parse(dstIp);
IPEndPoint endPoint = new IPEndPoint(dst, srcPort);
sock.SendTo(data, endPoint);
}
public struct StartPacket
{
public uint len;
public List<double> points;
}
public static byte[] StructureToByteArray(object obj)
{
int len = Marshal.SizeOf(obj);
byte[] arr = new byte[len];
IntPtr ptr = Marshal.AllocHGlobal(len);
Marshal.StructureToPtr(obj, ptr, true);
Marshal.Copy(ptr, arr, 0, len);
Marshal.FreeHGlobal(ptr);
return arr;
}
}
}
此代码失败:
类型 'ConsoleApplication1.Testing+StartPacket' 不能作为非托管结构进行编组;无法计算有意义的大小或偏移量。
这似乎更像是一项 serialization/deserialization 任务。最简单的方法是用 Serializable
标记 class,然后像这样使用 BinaryFormatter
:
public class Program
{
public static void Main(string[] args)
{
var startPacket = new StartPacket();
startPacket.len = 3;
startPacket.points = new List<double>() { 3.14, 5, -1023.1231311 };
// serialize into a byte array for Socket.SendTo()
var formatter = new BinaryFormatter();
var ms = new MemoryStream();
formatter.Serialize(ms, startPacket);
var bytes = ms.ToArray();
// assuming we received bytes[] from a socket, deserialize into StartPacket object
ms = new MemoryStream(bytes);
formatter = new BinaryFormatter();
startPacket = (StartPacket)formatter.Deserialize(ms);
}
}
[Serializable()]
public struct StartPacket
{
public uint len;
public List<double> points;
}
然而,这在内存方面不是很有效——这个例子生成了一个 524 字节的序列化 StartPacket
,可能是因为 List<>
的容量超过3 double
秒。使 points
成为具有特定大小的数组只会让我们减少到 211 字节。如果不仔细研究,我猜想在结构的序列化版本中有很多开销(比如变量的名称和类型等)。但是,如果您不太关心数据包的大小,那么这可能适合您。
如果你想要更高效的东西,那么你可以像这样向 StartPacket
添加方法:
public class Program
{
public static void Main(string[] args)
{
var startPacket = new StartPacket();
startPacket.len = 3;
startPacket.points = new List<double> { 3.14, 5, -1023.1231311 };
// create an array to send through the socket
var arr = startPacket.ToArray();
// create a StartPacket from an array we received from a socket
var newStartPacket = StartPacket.FromArray(arr);
}
}
public struct StartPacket
{
public uint len;
public List<double> points;
public byte[] ToArray()
{
var arr = BitConverter.GetBytes(len);
foreach (var point in points)
{
arr = arr.Concat(BitConverter.GetBytes(point)).ToArray();
}
return arr;
}
public static StartPacket FromArray(byte[] array)
{
var sp = new StartPacket();
sp.len = BitConverter.ToUInt32(array, 0);
sp.points = new List<double>();
for (int i = 0; i < sp.len; i++)
{
sp.points.Add(BitConverter.ToDouble(array, 4 + i * 8));
}
return sp;
}
}
请注意,这些都没有考虑系统的字节顺序。希望这会有所帮助。
所以我得到了可变数量的坐标(纬度和经度)。我想以一种直接的方式打包这些坐标,可以通过 UDP 轻松发送和解包。我如何使用 C# 解决这个问题?
我假设我声明了一个 struct
,然后使用内置的编组来获取要发送的字节数组。当涉及的点数可变时,如何做到这一点?
我的大部分编程经验都在 Python,但我需要用 C# 来完成,我对它的经验有限。
编辑:我将添加一些我正在测试的代码,因为我觉得没有人只响应文本。
namespace ConsoleApplication1
{
class Testing
{
static void Main(string[] args)
{
// Console.WriteLine("Starting");
// string text = "Hello";
// byte[] data = Encoding.ASCII.GetBytes(text);
StartPacket test = new StartPacket();
test.len = 3;
List<double> points = new List<double>();
points.Add(3.14);
points.Add(5);
points.Add(-1023.1231311);
test.points = points;
byte[] data = StructureToByteArray(test);
SendUdp(65456, "192.168.20.100", data);
}
static void SendUdp(int srcPort, string dstIp, byte[] data)
{
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,
ProtocolType.Udp);
IPAddress dst = IPAddress.Parse(dstIp);
IPEndPoint endPoint = new IPEndPoint(dst, srcPort);
sock.SendTo(data, endPoint);
}
public struct StartPacket
{
public uint len;
public List<double> points;
}
public static byte[] StructureToByteArray(object obj)
{
int len = Marshal.SizeOf(obj);
byte[] arr = new byte[len];
IntPtr ptr = Marshal.AllocHGlobal(len);
Marshal.StructureToPtr(obj, ptr, true);
Marshal.Copy(ptr, arr, 0, len);
Marshal.FreeHGlobal(ptr);
return arr;
}
}
}
此代码失败: 类型 'ConsoleApplication1.Testing+StartPacket' 不能作为非托管结构进行编组;无法计算有意义的大小或偏移量。
这似乎更像是一项 serialization/deserialization 任务。最简单的方法是用 Serializable
标记 class,然后像这样使用 BinaryFormatter
:
public class Program
{
public static void Main(string[] args)
{
var startPacket = new StartPacket();
startPacket.len = 3;
startPacket.points = new List<double>() { 3.14, 5, -1023.1231311 };
// serialize into a byte array for Socket.SendTo()
var formatter = new BinaryFormatter();
var ms = new MemoryStream();
formatter.Serialize(ms, startPacket);
var bytes = ms.ToArray();
// assuming we received bytes[] from a socket, deserialize into StartPacket object
ms = new MemoryStream(bytes);
formatter = new BinaryFormatter();
startPacket = (StartPacket)formatter.Deserialize(ms);
}
}
[Serializable()]
public struct StartPacket
{
public uint len;
public List<double> points;
}
然而,这在内存方面不是很有效——这个例子生成了一个 524 字节的序列化 StartPacket
,可能是因为 List<>
的容量超过3 double
秒。使 points
成为具有特定大小的数组只会让我们减少到 211 字节。如果不仔细研究,我猜想在结构的序列化版本中有很多开销(比如变量的名称和类型等)。但是,如果您不太关心数据包的大小,那么这可能适合您。
如果你想要更高效的东西,那么你可以像这样向 StartPacket
添加方法:
public class Program
{
public static void Main(string[] args)
{
var startPacket = new StartPacket();
startPacket.len = 3;
startPacket.points = new List<double> { 3.14, 5, -1023.1231311 };
// create an array to send through the socket
var arr = startPacket.ToArray();
// create a StartPacket from an array we received from a socket
var newStartPacket = StartPacket.FromArray(arr);
}
}
public struct StartPacket
{
public uint len;
public List<double> points;
public byte[] ToArray()
{
var arr = BitConverter.GetBytes(len);
foreach (var point in points)
{
arr = arr.Concat(BitConverter.GetBytes(point)).ToArray();
}
return arr;
}
public static StartPacket FromArray(byte[] array)
{
var sp = new StartPacket();
sp.len = BitConverter.ToUInt32(array, 0);
sp.points = new List<double>();
for (int i = 0; i < sp.len; i++)
{
sp.points.Add(BitConverter.ToDouble(array, 4 + i * 8));
}
return sp;
}
}
请注意,这些都没有考虑系统的字节顺序。希望这会有所帮助。