网络代码中.Net中结构的不安全自动填充

时间:2011-09-18 02:52:44

标签: c# pointers unsafe

这个想法:能够获取任何结构的字节,通过TcpClient(或通过我的客户端包装器)发送这些字节,然后让接收客户端加载这些字节并使用指针将它们“绘制”到新结构上

问题:它完全将字节读入缓冲区;它完美地读取另一端的字节数组。然而,“油漆”操作失败了。我写了一个新的Vector3(1F,2F,3F);我读了一个Vector3(0F,0F,0F)......显然,不理想。

不幸的是,我没有看到错误 - 如果它以一种方式工作,它应该反过来 - 并且值正在被填充。

写/读功能如下:

    public static unsafe void Write<T>(Client client, T value) where T : struct
    {
        int n = System.Runtime.InteropServices.Marshal.SizeOf(value);
        byte[] buffer = new byte[n];
        {
            var handle = System.Runtime.InteropServices.GCHandle.Alloc(value, System.Runtime.InteropServices.GCHandleType.Pinned);
            void* ptr = handle.AddrOfPinnedObject().ToPointer();
            byte* bptr = (byte*)ptr;

            for (int t = 0; t < n; ++t)
            {
                buffer[t] = *(bptr + t);
            }
            handle.Free();
        }

        client.Writer.Write(buffer);
    }

换行

    public static unsafe T Read<T>(Client client) where T : struct
    {
        T r = new T();
        int n = System.Runtime.InteropServices.Marshal.SizeOf(r);
        {
            byte[] buffer = client.Reader.ReadBytes(n);
            var handle = System.Runtime.InteropServices.GCHandle.Alloc(r, System.Runtime.InteropServices.GCHandleType.Pinned);
            void* ptr = handle.AddrOfPinnedObject().ToPointer();
            byte* bptr = (byte*)ptr;

            for (int t = 0; t < n; ++t)
            {
                *(bptr + t) = buffer[t];
            }
            handle.Free();
        }
        return r;
    }

请帮助,谢谢。

编辑:

好吧,一个主要问题是我得到了传递struct值时创建的临时副本的句柄。

EDIT2:

更改“T r = new T();” to“object r = new T();”并且“返回r”到“return(T)r”框并取消框结构,同时使它成为引用,所以指针实际指向它。

然而,它很慢。我每秒得到13,500 - 14,500次写入/读取。

EDIT3:

OTOH,通过BinaryFormatter序列化/反序列化Vector3每秒可获得大约750次写入/读取。所以 Lot 比我使用的更快。 :)

Edit4:

单独发送花车的速度为8,400 RW /秒。突然之间,我对此感觉好多了。 :)

Edit5:

经过测试的GCHandle分配固定和释放;每秒28,000,000 ops(相比于大约1,000,000,000 Int32 / int add + assign / second。因此与整数相比,它慢了35倍。但是,这仍然相当快)。请注意,即使GCHandle自动装箱结构正常(GCHandle接受“object”类型的值),您似乎也无法固定类。

现在,如果C#人员将约束更新到指针分配识别出“T”是结构的点,我可以直接指向一个指针,这是......,非常快。

接下来:可能使用单独的线程测试写入/读取。 :)看看GCHandle实际影响发送/接收延迟的程度。

事实证明:

Edit6:

        double start = Timer.Elapsed.TotalSeconds;
        for (t = 0; t < count; ++t)
        {
            Vector3 from = new Vector3(1F, 2F, 3F);
            // Vector3* ptr = &test;
            // Vector3* ptr2 = &from;
            int n = sizeof(Vector3);
            if (n / 4 * 4 != n)
            {
                // This gets 9,000,000 ops/second;
                byte* bptr1 = (byte*)&test;
                byte* bptr2 = (byte*)&from;
                // int n = 12;
                for (int t2 = 0; t2 < n; ++t2)
                {
                    *(bptr1 + t2) = *(bptr2 + t2);
                }
            }
            else
            {
                // This speedup gets 24,000,000 ops/second.
                int n2 = n / 4;
                int* iptr1 = (int*)&test;
                int* iptr2 = (int*)&from;
                // int n = 12;
                for (int t2 = 0; t2 < n2; ++t2)
                {
                    *(iptr1 + t2) = *(iptr2 + t2);
                }
            }
        }

所以,总的来说,我认为GCHandle并没有真正放慢速度。 (那些认为这是将一个Vector3分配给另一个的慢速方法的人,请记住,目的是将结构序列化为byte []缓冲区以通过网络发送。而且,虽然这不是我们在这里做的,但它用第一种方法很容易做到。)

Edit7:

以下获得6,900,000 ops /秒:

        for (t = 0; t < count; ++t)
        {
            Vector3 from = new Vector3(1F, 2F, 3F);
            int n = sizeof(Vector3);
            byte* bptr2 = (byte*)&from;
            byte[] buffer = new byte[n];
            for (int t2 = 0; t2 < n; ++t2)
            {
                buffer[t2] = *(bptr2 + t2);
            }
        }

...帮助!我有IntruigingPuzzlitus! :d

0 个答案:

没有答案