[英]Sending large byte array over local TCP socket is too slow
我通过在发送设备上使用 DirectX 桌面复制将我的桌面从一台设备流式传输到另一台设备,将生成的 bitmap 的数据流复制到一个字节数组,然后通过套接字将该字节数组通过 WiFi 发送到本地连接的设备联系。
另一个设备接收字节数组,将其复制到数据流中以从中创建 bitmap,然后将其呈现给 window。
复制/渲染只需要 <20ms。
发送每个字节数组所需的时间约为 900ms - 1400ms。 但是,如果我在同一设备上作为服务器/客户端连接到套接字,它会非常快。 所以问题在于实际将其发送到其他连接的设备。
理想情况下,我希望这个发送/接收过程花费 <100 毫秒。
如果我发送像单个 int 这样简单的东西,它会立即收到。
我还应该注意 DataStream object 是每个像素的颜色数据数组,因此这是每个分辨率的恒定大小(ARGB 1920x1080 = 8294400 字节)。
我发送的设备的 wifi 适配器的最大链接速度为 400/400 Mbps,接收设备为 240/240,所以我认为这也不是问题。
这是我的套接字代码:
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(); }
}
}
}
以上由其他 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;
}
}
以下是在 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 };
}
}
}
我认为问题可能与我的套接字代码的效率或 TCP 的限制有关? 如何提高发送大字节 arrays 的速度?
240 Mbit/s 表示的速度可以在 240/2 *95% = 14 MB/s 左右移动(Wi-Fi 开销为 /2,TCP 在 IPv4 开销上为 95%),大约 1.7 个屏幕价值或 580 ms/屏幕 - 前提是附近网络(或蓝牙或......)没有网络拥塞。
您需要提高链接速度 [*1] - 千兆以太网约为。 快 8 倍 - 或通过压缩(周围有更快的压缩器)或智能缓存(仅传输更改的像素)来减少数据量。 对于任意视频,会想到像 H.264 这样的视频编解码器,大多数 GPU 或强大的 CPU 可以实时压缩。
顺便说一句,HDMI 不会无缘无故地使用高达 48 Gbit/s 的传输速度。
[*1] 较低的速度很重要,因为您无法以比接收速度更快的速度传输数据。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.