2012-05-13 11 views
9

Attualmente sto scrivendo codice di rete naive per un progetto e un compagno mi ha accennato alla possibilità che quando invio un pacchetto di informazioni dal server a tutti i client in modo iterativo, potrei ottenere un lag intenso quando uno dei i clienti non rispondono correttamente.È un OutputStream nel blocco di Java? (Prese)

È noto per il trolling, quindi ero un po 'scettico quando implementavo un thread secondario che ora è responsabile di inviare dati a un client, avendo una coda che il server aggiunge semplicemente i pacchetti su quella che viene poi letta dal thread per inviare dati.

La domanda che ora ho dopo averci pensato su è tempo o non OutputStream di un Socket Java in realtà mette in coda la roba che vuole inviare da solo, eliminando così la necessità di una coda in anticipo. La possibilità di avere problemi intensi si verifica solo quando il server sta bloccando finché non riceve una risposta da un client che l'oggetto inviato è stato ricevuto.

Grazie.

+0

Ho riscontrato un problema simile se si tenta di ottenere l'output o il flusso di input nella funzione di esecuzione di una discussione.Il problema con il blocco (sia per getInputStream che per getOutputStream) del socket è perché è nella funzione di esecuzione ... la soluzione sembra essere quella di inserirla nel costruttore, salvare la variabile e quindi fare riferimento alla variabile in esecuzione. – Zimm3r

risposta

4

Ovviamente, quando si scrive su un socket, questa scrittura viene memorizzata nel buffer. L'oggetto socket ha un metodo setSendBufferSize() per l'impostazione di questa dimensione del buffer. Se la tua scrittura può essere memorizzata nella cache in questo buffer, allora, naturalmente, puoi eseguire l'iterazione immediatamente sul seguente socket. Altrimenti questo buffer deve essere scaricato immediatamente dal client. Quindi, durante il flushing sarai bloccato. Se si desidera evitare di essere bloccato durante il lavaggio del buffer, è necessario utilizzare un SocketChannel in I/O non bloccante. In ogni caso, l'opzione migliore per scrivere contemporaneamente su più socket è gestire ciascun socket con un thread diverso, in modo che tutte le scritture possano essere eseguite contemporaneamente.

+5

"Altrimenti questo buffer deve essere scaricato immediatamente dal client" non è un modo corretto di inserirlo. Il buffer viene sempre scritto in rete, in modo asincrono, ma non può essere inviato fino a quando non c'è spazio nel buffer di ricezione del peer. Se il peer non sta leggendo, o sta leggendo il rallentamento, il suo buffer di ricezione si riempirà, quindi il buffer di invio si riempirà, così le tue scritture successive si bloccheranno. – EJP

+0

Anche se il post di Tomasz è stato un esempio migliore, questo è esattamente quello che stavo cercando in termini di spiegazione EJP. Grazie mille. – salbeira

8

Il tuo amico ha ragione, ma ha più a che fare con il protocollo . È necessario confermare in modo sostanziale i pacchetti inviati al cliente. Se il client non risponde (non riesce a leggere i dati in arrivo, il computer è sotto carico pesante, ecc.) Il server non riceverà i riconoscimenti e interromperà l'invio dei dati. Questo meccanismo integrato in TCP/IP impedisce a una parte della comunicazione di inviare grandi quantità di dati senza assicurarsi che l'altra parte li abbia ricevuti. A sua volta questo evita il requisito di inviare nuovamente grandi quantità di dati.

In Java questo si manifesta come scrittura di blocco su OutputStream. Lo stack TCP/IP sottostante/sistema operativo non consente l'invio di più dati finché il client non è pronto a riceverli.

Si può facilmente testare questo! Ho implementato semplice server che accetta connessione, ma non riesce a leggere i dati in entrata:

new Thread(new Runnable() { 
    @Override 
    public void run() { 
     try { 
      final ServerSocket serverSocket = new ServerSocket(4444); 
      final Socket clientSocket = serverSocket.accept(); 
      final InputStream inputStream = clientSocket.getInputStream(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 

    } 
}).start(); 

E un semplice client che proprio invia quanti più dati si può in 4K lotti:

final Socket client = new Socket("localhost", 4444); 
final OutputStream outputStream = client.getOutputStream(); 
int packet = 0; 
while(true) { 
    System.out.println(++packet); 
    outputStream.write(new byte[1024 * 4]); 
} 

Il ciclo cliente si blocca su il mio computer dopo 95 iterazioni (il tuo chilometraggio può variare). Tuttavia, se leggo da inputStream nel thread del server, il ciclo continua e continua.

+0

Ho riscontrato un problema simile se provo a ottenere l'output o il flusso di input nella funzione di esecuzione di un thread. Il problema con il blocco (sia per getInputStream che per getOutputStream) del socket è perché è nella funzione di esecuzione ... la soluzione sembra essere quella di inserirla nel costruttore, salvare la variabile e quindi fare riferimento alla variabile in esecuzione. – Zimm3r

0

Un OutputStream sta bloccando. Probabilmente ha del buffering, ma questo non ti aiuta molto se il server non consuma mai byte (qualsiasi buffer fisso finirà per riempire). Quindi il tuo amico ha ragione, devi scrivere in un thread separato o usare qualcosa di più avanzato, come nio.

Dal lato di lettura, è possibile utilizzare disponibile() per evitare il blocco. Nessuna chiamata corrispondente esiste sul lato scrittura. Vorrei che ci fosse.