2012-03-09 8 views
12

Ho un server di giochi Java TCP, io uso java.net.ServerSocket e tutto funziona bene, ma recentemente il mio ISP ha fatto una sorta di aggiornamento, dove, se si inviano due pacchetti molto veloci per la stessa connessione TCP, li chiudono con la forza.Il mio ISP mi obbliga a bufferizzare i dati tcp prima di inviarlo

Questo è il motivo per cui un sacco di miei giocatori sono disconnesso in modo casuale quando c'è un sacco di traffico in gioco (quando c'è un sacco di possibilità che il server invierà 2 pacchetti al tempo stesso per la stessa persona)

Ecco un esempio di ciò che intendo: Se faccio qualcosa di simile, il mio ISP chiuderà il collegamento per alcun motivo per client e server side:

tcpOut.print("Hello."); 
tcpOut.flush(); 

tcpOut.print("How are you?"); 
tcpOut.flush(); 

ma funzionerà bene se faccio qualcosa così:

tcpOut.print("Hello."); 
tcpOut.flush(); 

Thread.sleep(200); 

tcpOut.print("How are you?"); 
tcpOut.flush(); 

O questo:

tcpOut.print("Hello."); 
tcpOut.print("How are you?"); 
tcpOut.flush(); 

Questo ha iniziato solo un paio di settimane fa, quando hanno (ISP) ha fatto alcune modifiche al servizio e la rete. Ho notato usando Wireshark che devi avere almeno ~ 150ms di tempo tra due pacchetti per la stessa connessione TCP altrimenti si chiuderà.

1) Ragazzi, sapete come si chiama? non ha nemmeno un nome? È legale?

Ora devo riscrivere il mio server di gioco sapendo che io uso un metodo chiamato: send(PrintWriter out, String packetData);

2) C'è qualche soluzione facile chiedere Java per tamponare i dati prima che lo invia ai clienti? O aspettare 150ms prima di ogni invio senza dover riscrivere il tutto? Ho fatto qualche ricerca su google, ma non riesco a trovare nulla che si occupi di questo problema. Qualche suggerimento o informazione per aiutare questo sarebbe molto apprezzato, btw l'ottimizzazione della velocità è molto cruciale. Grazie.

+1

Questo non suona giusto. Lo stack TCP/IP locale bufferizzerà e ritarderà i dati in uscita in base alla velocità percepita della rete e, a meno che non stiate scherzando con tcpSetNoDelay, in genere ritarderà ulteriormente i pacchetti in modo da coalizzarli. Cosa vedi che ti fa pensare che ci sia una chiusura forzata? E i tuoi utenti possono realmente digitare 6 righe al secondo? o è solo la tua applicazione a svuotarsi inutilmente dopo ogni nuova riga? – EJP

+1

Grazie EJP per il tuo commento. Sono sicuro al 100% che l'ISP stia chiudendo la connessione, lo vedo su Wireshark quando ottengo un pacchetto di reset rosso. Ho anche fatto alcuni test. A volte i dati vengono aggiunti in un pacchetto come hai appena detto (stack TCP/IP locale) ma a volte non succede quando il mio server ha un sacco di flushing da fare. È un gioco MMORPG, quindi quando gli utenti parlano, muovono, attaccano, ecc. Contemporaneamente sulla mappa, c'è un sacco di flushings da fare, quindi ad un certo punto due pacchetti vengono inviati contemporaneamente a un giocatore, e il mio ISP si chiude quella presa. – Reacen

+0

E per "stesso tempo" intendo con un ritardo inferiore a 100 ms. – Reacen

risposta

3

È possibile creare un'implementazione del wrapper Writer per tenere traccia dell'ora dello flush ultima chiamata. Una rapida implementazione consiste nell'aggiungere una chiamata di attesa per onorare il ritardo di 150 ms tra due flush consecutivi.

public class ControlledFlushWriter extends Writer { 
    private long enforcedDelay = 150; 
    private long lastFlush = 0; 
    private Writer delegated; 

    public ControlledFlushWriter(Writer writer, long flushDelay) { 
     this.delegated = writer: 
     this.enforcedDelay = flushDelay; 
    } 

    /* simple delegation for other abstract methods... */ 

    public void flush() { 
     long now = System.currentTimeMillis(); 
     if (now < lastFlush + enforcedDelay) { 
      try { 
       Thread.sleep(lastFlush + enforcedDelay - now); 
      } catch (InterruptedException e) { 
       // probably prefer to give up flushing 
       // instead of risking a connection reset ! 
       return; 
      } 
     } 
     lastFlush = System.currentTimeMillis(); 
     this.delegated.flush(); 
    } 

} 

Ora dovrebbe essere sufficiente per avvolgere il vostro attuale PrintWriter con questo ControlledFlushWriter a lavorare intorno i vostri ISP QoS senza ri-scrittura di tutto l'applicazione.

Dopo tutto, sembra ragionevole impedire a una connessione di contrassegnare qualsiasi pacchetto come urgente ... In tale condizione, è difficile implementare una condivisione di collegamento QoS equo.

+0

Grazie mille! – Reacen

5

Se il tuo ISP impone tali politiche di qualità del servizio e non hai modo di negoziarlo con esso, ti propongo di applicare anche quelle regole dalla tua parte con la configurazione QoS dello stack TCP/IP.

Un flush contrassegna il pacchetto TCP come urgente (flag URG) in modo che venga inviato indipendentemente dallo stato della finestra buffer/TCP. Ora dovete dire al vostro sistema operativo o di qualsiasi apparecchiatura di rete sulla linea a uno

  • ignorare (o semplicemente reset) la bandiera urgente quando il pacchetto precedente è stato inviato negli ultimi 150 ms e fare un po 'di buffering, se necessario
  • ritardare la consegna di pacchetti urgenti consecutivi per rispettare il vincolo di 150 ms.

Probabilmente esiste un software costoso per Windows. Personalmente, penso che mettere un Linux box come router tra le workstation Windows e il modem con lo appropriate QoS settings in iptables e qdisc farà il trucco.

Problemi correlati