TCP 套接字客户端向本地主机写入输出缓慢
TCP socket client writing output slow to localhost
我的设备上有一个应用程序可以进行屏幕截图并将位图保存到内存中。将内存中的最后一个位图流式传输到服务器。
每次我的套接字客户端将位图的字节写入服务器时,将原始 8 MB 数据发送到 127.0.0.1:9090(在本例中是我的 python 服务器)大约需要 400 毫秒。假设它在本地主机上,它不应该更快吗?我需要每秒将尽可能多的帧流式传输到我的本地主机 TCP 服务器。我尝试在客户端将 Bitmap 压缩为 PNG,但大约需要 1000 毫秒,这就是我不想这样做的原因。我需要原始像素,因此 JPEG 也无法工作,因为它是有损压缩。
OutputStream.write() 的执行时间:
OutputStream wrote 8355840 bytes to localhoost in 414ms
OutputStream wrote 8355840 bytes to localhoost in 386ms
OutputStream wrote 8355840 bytes to localhoost in 475ms
OutputStream wrote 8355840 bytes to localhoost in 413ms
OutputStream wrote 8355840 bytes to localhoost in 409ms
OutputStream wrote 8355840 bytes to localhoost in 394ms
OutputStream wrote 8355840 bytes to localhoost in 463ms
OutputStream wrote 8355840 bytes to localhoost in 411ms
OutputStream wrote 8355840 bytes to localhoost in 434ms
OutputStream wrote 8355840 bytes to localhoost in 407ms
OutputStream wrote 8355840 bytes to localhoost in 403ms
OutputStream wrote 8355840 bytes to localhoost in 478ms
OutputStream wrote 8355840 bytes to localhoost in 434ms
OutputStream wrote 8355840 bytes to localhoost in 417ms
package com.genymobile.scrcpy;
import android.graphics.Bitmap;
import java.net.Socket;
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
public final class Main {
static Bitmap mFrame = null;
public static void main(String... args) {
new Thread(new Client()).start();
while(true) {
mFrame = ScreenCaptorUtils.screenshot(1080, 1920);
}
}
private static class Client implements Runnable {
public void run() {
try {
Socket socket = new Socket("127.0.0.1", 9090);
OutputStream out = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(out);
while(mFrame == null) {
Thread.sleep(100);
}
while(true) {
ByteBuffer buffer = ByteBuffer.allocate(mFrame.getByteCount());
mFrame.copyPixelsToBuffer(buffer);
byte[] frameBytes = buffer.array();
out.write(ByteBuffer.allocate(4).putInt(frameBytes.length).array()); // Write header - 4 bytes integer with the bitmap size
long start = System.currentTimeMillis();
out.write(frameBytes); // Write raw bitmap bytes
System.out.println("OutputStream wrote " + String.valueOf(frameBytes.length) + " bytes to localhoost in " + String.valueOf(System.currentTimeMillis() - start) + "ms");
}
} catch(Exception e) { e.printStackTrace(); }
}
}
}
设备通过USB连接,WIFI被禁用。我用这个命令打开了 9090 端口:
adb reverse tcp:9090 tcp:9090
这也是 python 服务器:
import socket
import sys
import time
from threading import Thread
from struct import *
from io import BytesIO
from PIL import Image
class BitmapStreamSocket:
LAST_FRAME = None
def __init__(self, host, port):
self.host = host
self.port = port
self.last_frame = None
self.start_server()
def client_thread(self, sock):
print("Client thread started")
while True:
barr = self.recv_msg(sock)
if barr:
stream = BytesIO(bytes(barr))
print("Just got ", len(barr), "bytes: ", time.time())
else:
print("Client just disconnected")
return
def send_msg(self, sock, msg):
# Prefix each message with a 4-byte length (network byte order)
msg = pack('>I', len(msg)) + msg
sock.sendall(msg)
def recv_msg(self, sock):
# Read message length and unpack it into an integer
raw_msglen = self.recvall(sock, 4)
if not raw_msglen:
return None
msglen = unpack('>I', raw_msglen)[0]
# Read the message data
recvall = self.recvall(sock, msglen)
return recvall
def recvall(self, sock, n):
# Helper function to recv n bytes or return None if EOF is hit
data = b''
while len(data) < n:
packet = sock.recv(n - len(data))
if not packet:
return None
data += packet
return data
def start_server(self):
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # SO_REUSEADDR flag tells the kernel to reuse a local socket in TIME_WAIT state, without waiting for its natural timeout to expire
try:
soc.bind((self.host, self.port))
except:
print("Bind failed. Error : " + str(sys.exc_info()))
sys.exit()
soc.listen(5) # queue up to 5 requests
print("Socket now listening")
# infinite loop- do not reset for every requests
while True:
connection, address = soc.accept()
print("Connected with " + str(address[0]) + ":" + str(address[1]))
t = Thread(target=self.client_thread, args=(connection,))
t.start()
soc.close()
客户端没有从服务器接收输入,因为服务器没有发送任何输入。客户端只发送数据,服务器接收。
基本上我想在最短的时间内将尽可能多的数据流式传输到本地主机服务器,这样我就可以获得更多的 FPS。一张 8 MB 的图像需要 400 毫秒,这会很慢。
8355000 Bytes / 0.4 seconds = 20887500 Bytes/second
相当于大约 200 MBit/second,光纤互联网访问速度不错,USB 连接速度惊人。
这个速度还不错
我的设备上有一个应用程序可以进行屏幕截图并将位图保存到内存中。将内存中的最后一个位图流式传输到服务器。 每次我的套接字客户端将位图的字节写入服务器时,将原始 8 MB 数据发送到 127.0.0.1:9090(在本例中是我的 python 服务器)大约需要 400 毫秒。假设它在本地主机上,它不应该更快吗?我需要每秒将尽可能多的帧流式传输到我的本地主机 TCP 服务器。我尝试在客户端将 Bitmap 压缩为 PNG,但大约需要 1000 毫秒,这就是我不想这样做的原因。我需要原始像素,因此 JPEG 也无法工作,因为它是有损压缩。
OutputStream.write() 的执行时间:
OutputStream wrote 8355840 bytes to localhoost in 414ms
OutputStream wrote 8355840 bytes to localhoost in 386ms
OutputStream wrote 8355840 bytes to localhoost in 475ms
OutputStream wrote 8355840 bytes to localhoost in 413ms
OutputStream wrote 8355840 bytes to localhoost in 409ms
OutputStream wrote 8355840 bytes to localhoost in 394ms
OutputStream wrote 8355840 bytes to localhoost in 463ms
OutputStream wrote 8355840 bytes to localhoost in 411ms
OutputStream wrote 8355840 bytes to localhoost in 434ms
OutputStream wrote 8355840 bytes to localhoost in 407ms
OutputStream wrote 8355840 bytes to localhoost in 403ms
OutputStream wrote 8355840 bytes to localhoost in 478ms
OutputStream wrote 8355840 bytes to localhoost in 434ms
OutputStream wrote 8355840 bytes to localhoost in 417ms
package com.genymobile.scrcpy;
import android.graphics.Bitmap;
import java.net.Socket;
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
public final class Main {
static Bitmap mFrame = null;
public static void main(String... args) {
new Thread(new Client()).start();
while(true) {
mFrame = ScreenCaptorUtils.screenshot(1080, 1920);
}
}
private static class Client implements Runnable {
public void run() {
try {
Socket socket = new Socket("127.0.0.1", 9090);
OutputStream out = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(out);
while(mFrame == null) {
Thread.sleep(100);
}
while(true) {
ByteBuffer buffer = ByteBuffer.allocate(mFrame.getByteCount());
mFrame.copyPixelsToBuffer(buffer);
byte[] frameBytes = buffer.array();
out.write(ByteBuffer.allocate(4).putInt(frameBytes.length).array()); // Write header - 4 bytes integer with the bitmap size
long start = System.currentTimeMillis();
out.write(frameBytes); // Write raw bitmap bytes
System.out.println("OutputStream wrote " + String.valueOf(frameBytes.length) + " bytes to localhoost in " + String.valueOf(System.currentTimeMillis() - start) + "ms");
}
} catch(Exception e) { e.printStackTrace(); }
}
}
}
设备通过USB连接,WIFI被禁用。我用这个命令打开了 9090 端口:
adb reverse tcp:9090 tcp:9090
这也是 python 服务器:
import socket
import sys
import time
from threading import Thread
from struct import *
from io import BytesIO
from PIL import Image
class BitmapStreamSocket:
LAST_FRAME = None
def __init__(self, host, port):
self.host = host
self.port = port
self.last_frame = None
self.start_server()
def client_thread(self, sock):
print("Client thread started")
while True:
barr = self.recv_msg(sock)
if barr:
stream = BytesIO(bytes(barr))
print("Just got ", len(barr), "bytes: ", time.time())
else:
print("Client just disconnected")
return
def send_msg(self, sock, msg):
# Prefix each message with a 4-byte length (network byte order)
msg = pack('>I', len(msg)) + msg
sock.sendall(msg)
def recv_msg(self, sock):
# Read message length and unpack it into an integer
raw_msglen = self.recvall(sock, 4)
if not raw_msglen:
return None
msglen = unpack('>I', raw_msglen)[0]
# Read the message data
recvall = self.recvall(sock, msglen)
return recvall
def recvall(self, sock, n):
# Helper function to recv n bytes or return None if EOF is hit
data = b''
while len(data) < n:
packet = sock.recv(n - len(data))
if not packet:
return None
data += packet
return data
def start_server(self):
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # SO_REUSEADDR flag tells the kernel to reuse a local socket in TIME_WAIT state, without waiting for its natural timeout to expire
try:
soc.bind((self.host, self.port))
except:
print("Bind failed. Error : " + str(sys.exc_info()))
sys.exit()
soc.listen(5) # queue up to 5 requests
print("Socket now listening")
# infinite loop- do not reset for every requests
while True:
connection, address = soc.accept()
print("Connected with " + str(address[0]) + ":" + str(address[1]))
t = Thread(target=self.client_thread, args=(connection,))
t.start()
soc.close()
客户端没有从服务器接收输入,因为服务器没有发送任何输入。客户端只发送数据,服务器接收。
基本上我想在最短的时间内将尽可能多的数据流式传输到本地主机服务器,这样我就可以获得更多的 FPS。一张 8 MB 的图像需要 400 毫秒,这会很慢。
8355000 Bytes / 0.4 seconds = 20887500 Bytes/second
相当于大约 200 MBit/second,光纤互联网访问速度不错,USB 连接速度惊人。
这个速度还不错