[英]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.