简体   繁体   English

通过本地 TCP 套接字发送大字节数组太慢

[英]Sending large byte array over local TCP socket is too slow

I'm streaming my desktop from one device to another by using DirectX desktop duplication on the sending device, copying the datastream of the resulting bitmap to a byte array, and then sending that byte array over a socket to a locally connected device over a WiFi connection.我通过在发送设备上使用 DirectX 桌面复制将我的桌面从一台设备流式传输到另一台设备,将生成的 bitmap 的数据流复制到一个字节数组,然后通过套接字将该字节数组通过 WiFi 发送到本地连接的设备联系。

The other device receives the byte array, copies it to a datastream to create a bitmap from it and then renders that to a window.另一个设备接收字节数组,将其复制到数据流中以从中创建 bitmap,然后将其呈现给 window。

The duplicating/rendering only takes <20ms.复制/渲染只需要 <20ms。

The time it takes to send each byte array is around 900ms - 1400ms.发送每个字节数组所需的时间约为 900ms - 1400ms。 But if I connect to the socket as server/client on the same device, it's very faster.但是,如果我在同一设备上作为服务器/客户端连接到套接字,它会非常快。 So the issue is with actually sending it to the other connected device.所以问题在于实际将其发送到其他连接的设备。

Ideally I'd want this send/receive process to take <100ms.理想情况下,我希望这个发送/接收过程花费 <100 毫秒。

If I send simple things like a single int, it gets received instantly.如果我发送像单个 int 这样简单的东西,它会立即收到。

I should also note the DataStream object is an array of colour data per pixel, so this is a constant size per resolution (ARGB 1920x1080 = 8294400 bytes).我还应该注意 DataStream object 是每个像素的颜色数据数组,因此这是每个分辨率的恒定大小(ARGB 1920x1080 = 8294400 字节)。

The wifi adapter of the device I'm sending from has max link speed of 400/400 Mbps and the receiving device is 240/240, so I don't think this is the problem either.我发送的设备的 wifi 适配器的最大链接速度为 400/400 Mbps,接收设备为 240/240,所以我认为这也不是问题。

Here is my code for socket:这是我的套接字代码:

using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;

namespace StreamSocket {
    public class Instance {
        private Socket Host;
        private Socket Socket;
        
        public string InstanceType { get; set; }
        public int BufferSize { get; set; }
        public bool IsConnected { get { return Socket != null && Socket.Connected; } }

        public event EventHandler<EventArgs> Connected = delegate { };
        public event EventHandler<EventArgs> Disconnected = delegate { };
        public event EventHandler<EventArgs> ConnectionFailed = delegate { };
        public event EventHandler<DataReceivedEventArgs> DataReceived = delegate { };

        public class DataReceivedEventArgs : EventArgs {
            public int Code { get; private set; }
            public byte[] Data { get; private set; }

            public DataReceivedEventArgs(int code, byte[] data) {
                Code = code; Data = data;
            }
        }

        public void Start(int bufferSize, string ip = "") {
            BufferSize = bufferSize;

            if(ip == string.Empty) { //Start as Server
                Host = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                Host.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
                Host.Bind(new IPEndPoint(IPAddress.Any, 8080));
                Host.Listen(1);
                Host.BeginAccept(new AsyncCallback(AcceptCallback), Host);
            } else { //Start as Client
                Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
                Socket.BeginConnect(ip, 8080, new AsyncCallback(ConnectCallback), Socket);
            }
        }

        public void Disconnect() {
            if(Socket == null) { return; }

            Disconnected(this, new EventArgs());
            if(Host != null) {
                if(Socket != null) {
                    Socket.Disconnect(true);
                }
                Host.BeginAccept(new AsyncCallback(AcceptCallback), Host);
            } else {
                Socket.Close();
            }
        }

        private void AcceptCallback(IAsyncResult result) {
            Socket = ((Socket)result.AsyncState).EndAccept(result);
            Connected(this, new EventArgs());
            BeginReceive();
        }

        private void ConnectCallback(IAsyncResult result) {
            try {
                Socket.EndConnect(result);
                Connected(this, new EventArgs());
                BeginReceive();
            } catch {
                ConnectionFailed(this, new EventArgs());
            }
        }

        public void Send(byte[] data) {
            if(!IsConnected) { return; }

            try {
                Socket.BeginSend(data, 0, data.Length, 0, new AsyncCallback(SendCallback), Socket);
            } catch { Disconnect(); }
        }

        private void SendCallback(IAsyncResult result) {
            try {
                Socket.EndSend(result);
            } catch { Disconnect(); }
        }

        private void BeginReceive() {
            if(!IsConnected) { return; }

            SocketData data = new SocketData() { Buffer = new byte[BufferSize] };
            Socket.BeginReceive(data.Buffer, 0, BufferSize, 0, new AsyncCallback(ReceiveCallback), data);
        }

        private void ReceiveCallback(IAsyncResult result) {
            if(!IsConnected) { return; }

            try {
                SocketData data = (SocketData)result.AsyncState;
                int bytesRead = Socket.EndReceive(result);
                if(bytesRead > 0) {
                    if(data.Length == 0) {
                        //Get headers from start of first buffer
                        using(MemoryStream ms = new MemoryStream(data.Buffer)) {
                            using(BinaryReader br = new BinaryReader(ms)) {
                                data.Code = br.ReadByte();
                                data.Length = br.ReadInt32();
                            }
                        }
                        //Write the rest of the buffer
                        if(data.Buffer.Length > sizeof(byte) + sizeof(int)) {
                            data.Stream.Write(data.Buffer, sizeof(byte) + sizeof(int), bytesRead - (sizeof(byte) + sizeof(int)));
                        }
                    } else {
                        //Writer buffer chunk that doesn't start with header
                        data.Stream.Write(data.Buffer, 0, bytesRead);
                    }

                    if(data.Stream.Length != data.Length) {
                        //Expected to receive more chunks
                        Socket.BeginReceive(data.Buffer, 0, BufferSize, 0, new AsyncCallback(ReceiveCallback), data);
                    } else {
                        //All data received
                        DataReceived(this, new DataReceivedEventArgs(data.Code, data.Stream.ToArray()));
                        data.Stream.Dispose();
                        
                        BeginReceive();
                    }
                } else {
                    Disconnect();
                }
            } catch { Disconnect(); }
        }
    }
}

The above is used by 2 other classes Server/Client that start instance of socket and receive the events like when receiving data:以上由其他 2 个类 Server/Client 使用,它们启动套接字实例并接收接收数据时的事件:

private void Client_DataReceived(object sender, Instance.DataReceivedEventArgs e) {
    switch((Code)e.Code) {
        case Code.BitmapReceived:
            //This sends the result to the DX Renderer
            BytesToBitmapData(e.Data);
            break;
    }
}

Here are the functions for converting between BitmapData & byte array: (These only take around <10ms so I don't think they're a problem)以下是在 BitmapData 和字节数组之间进行转换的函数:(这些只需要大约 <10ms,所以我认为它们不是问题)

public static byte[] BitmapDataToBytes(SerialCode code, BitmapData bitmapData) {
    using(MemoryStream ms1 = new MemoryStream()) {
        using(BinaryWriter bw1 = new BinaryWriter(ms1)) {
            bw1.Write((byte)code); //Header - Code
            bw1.Write((sizeof(int) * 4) + bitmapData.Data.Length); //Header - Length
            //Body:
            bw1.Write(bitmapData.Size.Width);
            bw1.Write(bitmapData.Size.Height);
            bw1.Write(bitmapData.Pitch);
            bw1.Write(bitmapData.Data.Length);
            bw1.Write(bitmapData.Data);

            return ms1.ToArray();
        }
    }
}

public static BitmapData BytesToBitmapData(byte[] bytes) {
    using(MemoryStream ms = new MemoryStream(bytes)) {
        using(BinaryReader br = new BinaryReader(ms)) {
            Size size = new Size(br.ReadInt32(), br.ReadInt32());
            int pitch = br.ReadInt32();
            int length = br.ReadInt32();
            byte[] data = br.ReadBytes(length);

            return new BitmapData() { Size = size, Pitch = pitch, Data = data };
        }
    }
}

I assume the issue may be with efficiency of my socket code or a limitation of TCP?我认为问题可能与我的套接字代码的效率或 TCP 的限制有关? What could I do to improve the speed of sending large byte arrays?如何提高发送大字节 arrays 的速度?

240 Mbit/s indicated speed could move around 240/2 *95% = 14 MB/s (/2 for Wi-Fi overhead, 95% for TCP over IPv4 overhead), roughly 1.7 screens worth or 580 ms/screen - provided that there's no network congestion by nearby networks (or Bluetooth or...). 240 Mbit/s 表示的速度可以在 240/2 *95% = 14 MB/s 左右移动(Wi-Fi 开销为 /2,TCP 在 IPv4 开销上为 95%),大约 1.7 个屏幕价值或 580 ms/屏幕 - 前提是附近网络(或蓝牙或......)没有网络拥塞。

You need to either increase the link speed [*1] - Gigabit Ethernet is appr.您需要提高链接速度 [*1] - 千兆以太网约为。 8 times faster - or decrease the amount of data by compressing (there are faster compressors around) or smart caching (only transmit changed pixels).快 8 倍 - 或通过压缩(周围有更快的压缩器)或智能缓存(仅传输更改的像素)来减少数据量。 For arbitrary video, a video codec like H.264 comes to mind which most GPUs or a beefy CPU could compress in realtime.对于任意视频,会想到像 H.264 这样的视频编解码器,大多数 GPU 或强大的 CPU 可以实时压缩。

Btw, HDMI doesn't use a transmission speed of up to 48 Gbit/s without a cause.顺便说一句,HDMI 不会无缘无故地使用高达 48 Gbit/s 的传输速度。

[*1] The lower speed counts as you can't transmit data faster than it an be received. [*1] 较低的速度很重要,因为您无法以比接收速度更快的速度传输数据。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM