2012-05-16 21 views
17

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:

  1. scrivere a tempo ~ 0,2 s; (non nel socket, ma in un flusso di output "regolare", i. e. tempo di codifica)
  2. Tempo di lettura ~ 0.05 s; (non dal socket, ma da un flusso di input "normale", i. e. tempo di decodifica)
  3. Dimensioni ~ 250 KB;
  4. 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:

  1. Quali valori consiglieresti (per migliorare le prestazioni) per tutti gli questi casi e perché?
  2. Si prega di chiarire il comportamento Socket#setReceiveBufferSize e Socket#setSendBufferSize.
  3. Quali altri metodi/tecniche è possibile consigliare per migliorare le prestazioni di tale applicazione?
  4. 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 millions

Grazie 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).

+0

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? –

+0

Puoi pubblicare il codice dove leggi dalle prese sul lato client? – mcfinnigan

+2

http://en.wikipedia.org/wiki/Delta_encoding – Krrose27

risposta

11
  • Write tempo ~ 0,2 s;
  • Tempo di lettura ~ 0,05 s;

Tali obiettivi sono completamente insignificante senza considerare la latenza e larghezza di banda della rete intervenire.

Dimensioni ~ 250 KB;

Off topic. La dimensione dell'immagine spetta a te e non ha alcun rapporto con la programmazione effettiva, che è lo scopo di questo sito.

Qualità perfetta.

'Qualità perfetta' richiede solo che non vengano rilasciati bit, che non si otterranno comunque via TCP.

serverSocket.setReceiveBufferSize(?); // #1 

Imposta la dimensione del buffer di ricezione per tutte le prese accettate. Impostalo il più grande che puoi, oltre 64k se possibile.

socket.setSendBufferSize(?); // #6 

Impostare questo grande come è possibile, oltre 64k se possibile.

socket.setReceiveBufferSize(?); // #7 

Poiché si tratta di una presa accettata, l'avete già fatto. Rimuovere.

OutputStream outputStream = new BufferedOutputStream(
      socket.getOutputStream(), ?); // #8 
    InputStream inputStream = new BufferedInputStream(
      socket.getInputStream(), ?); // #9 

I valori predefiniti per questi sono 8k; a patto di avere dimensioni di buffer socket decenti che dovrebbero essere sufficienti.

Quali valori consiglieresti (per migliorare le prestazioni) per tutti questi casi e perché?

Vedere sopra.

Si prega di chiarire il comportamento Socket#setReceiveBufferSize() e Socket#setSendBufferSize().

Essi controllano la dimensione della "finestra" TCP. È un argomento abbastanza astruso, ma l'idea è di ottenere la dimensione almeno uguale al prodotto con ritardo di banda della rete, cioè larghezza di banda in byte/secondo ritardo in secondi> = dimensione del buffer in byte.

Quali altri metodi/tecniche è possibile consigliare per migliorare le prestazioni di tale applicazione?

Non andare in giro con il sonno e svolgere altre attività durante l'invio dei dati. Mandalo il più velocemente possibile nel ciclo più stretto possibile che puoi organizzare.

Skype offre una trasmissione desktop in tempo reale di buona qualità - come fanno?

Off topic e probabilmente inconoscibile a meno che un dipendente Skype capita di voler dare via segreti aziendali qui.

+0

Grazie per questa informazione. Tuttavia, non sono d'accordo con te sui tempi di scrittura/lettura delle immagini. Guarda nel post originale - ho aggiunto dei chiarimenti lì. Si tratta di scritture/letture non in socket stesso, ma in flussi di I/O "regolari". Pertanto questa volta mostra il tempo medio richiesto per codificare/decodificare il PNG. Pensaci: non posso inviare PNG più velocemente di ogni ~ 0,2 s, indipendentemente dalla velocità della connessione; Non riesco a leggere PNG più velocemente di ogni ~ 0.05 s - indipendentemente dalla velocità della connessione; –

+0

@Haroogan Il mio punto è che non puoi leggere o scrivere più velocemente della rete, quindi specificare i tempi per ciascuno è inutile. – EJP

+0

Lo stesso vale per la dimensione dell'immagine - mi sono sentito come se fosse importante per considerare le dimensioni dei buffer, vero? –

1

penso che il più grande dei rifiuti larghezza di banda si troverà nel trasferimento completo del desktop. Dovresti trattare questo più come un film e fare la codifica differenziale dei fotogrammi.

Lo svantaggio è la gestione più complicata. Forse ci sono alcune semplici API/implementazioni di codec video là fuori?

5
  • Se client apre nuova connessione TCP per ogni immagine, quindi TCP slow start può provocare la lentezza. -> Usa la stessa connessione TCP per tutti i frame.

  • TCP dispone di buffer propri che vengono utilizzati in modo ottimale per TCP ->BufferedOutputStream fornisce talvolta vantaggi e alcune volte no.

  • Di solito solo una piccola parte dello schermo viene cambiata tra gli screenshot. -> Trasferisci solo le parti modificate.

+0

So che il # 3 è un ottimo acceleratore, ma è qualcosa che è difficile da implementare - e non sono esperto in questo tipo di problemi (è per questo che sarà la mia ultima considerazione). # 1 non è discussione. # 2 è interessante - sei sicuro? –

+0

# 2 non è corretto secondo le mie osservazioni. L'utilizzo di flussi bufferizzati è ESTREMAMENTE importante: fornisce un notevole incremento delle prestazioni per le operazioni di I/O sui socket. –

+0

@Haroogan: Ok, rimuoverò il # 2. Ma ricorda di usare flush() dopo aver scritto l'immagine su BufferedOutputStream. – SKi

0

La domanda è chiara. Qualcuno sopra di me suggerisce l'uso di più canali di nio, preferisco usare più socket di blocco (nio non è più veloce). Tuttavia questo non sta migliorando la comunicazione di rete, questa è la programmazione parallela che sicuramente sarebbe una buona soluzione nel tuo caso. Mi permetto di suggerire il seguente

http://docs.oracle.com/javase/1.4.2/docs/api/java/net/Socket.html#setTcpNoDelay(boolean)

impostandola a true, sarete in grado di accelerare la presa comm fino a costo di un aumento del consumo di larghezza di banda.

+0

Ma ci sarà un impatto sulle prestazioni a livello di nodo. – navaltiger

Problemi correlati