Ho creato un'applicazione di controllo desktop remoto. Ovviamente, si compone di client e server parti:Ottimizzazione delle prestazioni delle prese Java
Server:
- Ricevere le azioni del mouse/tastiera dal client;
- Invio screenshot del desktop al client.
Cliente:
- Ricevere screenshot dal server;
- Invio di azioni del mouse/tastiera;
Considerare l'invio dello screenshot. Quando uso il mio PC di casa come server, finisco con una dimensione di screenshot di 1920x1080. Utilizzando JAI Image I/O Tools e la codifica in formato PNG sono stato in grado di raggiungere i seguenti statistiche per una grande immagine, quali:
- scrivere a tempo ~ 0,2 s; (non nel socket, ma in un flusso di output "regolare", i. e. tempo di codifica)
- Tempo di lettura ~ 0.05 s; (non dal socket, ma da un flusso di input "normale", i. e. tempo di decodifica)
- Dimensioni ~ 250 KB;
- Qualità perfetta.
Di conseguenza, in base al numero 1, l'FPS ideale dovrebbe essere ~ 5.
Sfortunatamente, non sono in grado di raggiungere nemmeno quei 5 FPS, nemmeno 2 FPS. Ho cercato il collo di bottiglia e ho scoperto che scrivere/leggere su/da socket I/O stream richiede fino a ~ 2 s (Vedere l'appendice 1 e 2 per chiarimenti). Sicuramente questo è inaccettabile.
Ho studiato un po 'l'argomento e aggiunto il buffering dei flussi I/O del socket (con BufferedInputStream
e BufferedOutputStream
) su entrambi i lati. Ho iniziato con 64 KB di dimensioni. Questo ha effettivamente migliorato le prestazioni. Ma ancora non può avere almeno 2 FPS! Inoltre, ho sperimentato con Socket#setReceiveBufferSize
e Socket#setSendBufferSize
e ci sono stati alcuni cambiamenti di velocità, ma non so come si comportano esattamente, quindi non so quali valori utilizzare.
sguardo al codice di inizializzazione:
Server:
ServerSocket serverSocket = new ServerSocket();
serverSocket.setReceiveBufferSize(?); // #1
serverSocket.bind(new InetSocketAddress(...));
Socket clientSocket = serverSocket.accept();
clientSocket.setSendBufferSize(?); // #2
clientSocket.setReceiveBufferSize(?); // #3
OutputStream outputStream = new BufferedOutputStream(
clientSocket.getOutputStream(), ?); // #4
InputStream inputStream = new BufferedInputStream(
clientSocket.getInputStream(), ?); // #5
client:
Socket socket = new Socket(...);
socket.setSendBufferSize(?); // #6
socket.setReceiveBufferSize(?); // #7
OutputStream outputStream = new BufferedOutputStream(
socket.getOutputStream(), ?); // #8
InputStream inputStream = new BufferedInputStream(
socket.getInputStream(), ?); // #9
Domande:
- Quali valori consiglieresti (per migliorare le prestazioni) per tutti gli questi casi e perché?
- Si prega di chiarire il comportamento
Socket#setReceiveBufferSize
eSocket#setSendBufferSize
. - Quali altri metodi/tecniche è possibile consigliare per migliorare le prestazioni di tale applicazione?
- Skype offre una trasmissione desktop in tempo reale di buona qualità - come fanno?
Appendice 1: Aggiunta della pseudo-codice srotolato di socket client lettura (@mcfinnigan):
while(true) {
// objectInputStream is wrapping socket's buffered input stream.
Object object = objectInputStream.readObject(); // <--- Bottleneck (without setting proper buffer sizes is ~2 s)
if(object == null)
continue;
if(object.getClass() == ImageCapsule.class) {
ImageCapsule imageCapsule = (ImageCapsule)object;
screen = imageCapsule.read(); // <--- Decode PNG (~0.05 s)
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
repaint();
}
});
}
}
Appendice 2: Aggiunta del pseudo-codice srotolato del socket server scrittura (@ EJP):
while(true) {
// objectOutputStream is wrapping socket's buffered output stream.
BufferedImage screen = ... // obtaining screenshot
ImageCapsule imageCapsule = new ImageCapsule();
imageCapsule.write(screen, formatName()); // <--- Encode PNG (~0.2 s)
try {
objectOutputStream.writeObject(imageCapsule); // <--- Bottleneck (without setting proper buffer sizes is ~2 s)
}
finally {
objectOutputStream.flush();
objectOutputStream.reset(); // Reset to free written objects.
}
}
Conclusione:
01.235.164,106174 millionsGrazie per le vostre risposte, specialmente EJP - mi ha reso le cose un po 'più chiare. Se siete come me - cercando le risposte su come ottimizzare le prestazioni dei socket dovreste assolutamente controllare TCP/IP Sockets in Java, Second Edition: Practical Guide for Programmers, in particolare il capitolo 6 "Under the Hood" che descrive cosa succede dietro le quinte delle classi *Socket
, come vengono gestiti e utilizzati i buffer di invio e ricezione (quali sono le chiavi primarie per le prestazioni).
Sei sicuro che le impostazioni della presa sono i tuoi fattori limitanti? Che tipo di connessione hai tra il tuo client e il tuo server? Qual è la velocità del tuo server? –
Puoi pubblicare il codice dove leggi dalle prese sul lato client? – mcfinnigan
http://en.wikipedia.org/wiki/Delta_encoding – Krrose27