2012-03-09 10 views
12

Mam serwer gry Java TCP, używam java.net.ServerSocket i wszystko działa dobrze, ale ostatnio mój ISP zrobił pewien rodzaj aktualizacji, gdzie, jeśli wyślesz dwa pakiety bardzo szybko dla tego samego połączenia TCP, zamykają je siłą.Mój ISP zmusza mnie do buforowania danych tcp przed wysłaniem

To dlatego wiele moich zawodników są odłączone losowo kiedy jest dużo ruchu w grze (gdy nie jest dużo szansy, że serwer będzie wysyłał 2 pakiety w tym samym czasie do tej samej osoby)

Oto przykład tego, co mam na myśli: Jeśli zrobię coś takiego, mój ISP zamknie connexion bez powodu zarówno klienta jak i serwer strony:

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

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

Ale to będzie działać dobrze, jeśli mogę coś zrobić tak:

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

Thread.sleep(200); 

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

Albo to:

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

tylko ten rozpoczął kilka tygodni temu, gdy oni (ISP) zrobił kilka zmian do usług i sieci. Zauważyłem, używając Wireshark, że musisz mieć co najmniej ~ 150ms czasu między dwoma pakietami dla tego samego połączenia TCP, inaczej to się zamknie.

1) Czy wiecie, jak się nazywa? czy to nawet ma imię? Czy to jest legalne?

Teraz muszę ponownie napisać serwer gry, wiedząc, że mogę użyć metody nazywanej: send(PrintWriter out, String packetData);

2) Czy istnieje proste rozwiązanie, aby zapytać java do buforowania danych przed wysłaniem go do klienta? Lub czekać 150 ms przed każdym wysłaniem bez konieczności przepisywania całej rzeczy? Zrobiłem trochę googlowania, ale nie mogę znaleźć niczego, co poradziłoby sobie z tym problemem. Wszelkie porady lub informacje pomocne w tym zakresie będą bardzo doceniane, optymalizacja prędkości jest bardzo ważna. Dziękuję Ci.

+1

To nie brzmi dobrze. Twój lokalny stos TCP/IP będzie buforował i opóźniał wychodzące dane zgodnie z postrzeganą szybkością sieci, i chyba że będziesz się bał z tcpSetNoDelay, generalnie opóźni pakiety dalej, aby je połączyć. Co widzisz, co sprawia, że ​​myślisz o przymusowym zamknięciu? Czy twoi użytkownicy naprawdę mogą wpisać 6 linii na sekundę? czy jest to po prostu twoja aplikacja niepotrzebnie spłukująca po każdym newline? – EJP

+1

Dziękuję EJP za komentarz. Jestem w 100% pozytywna, że ​​dostawca Internetu zamyka połączenie, widzę to na Wireshark, kiedy dostaję czerwony pakiet resetu. Zrobiłem też kilka testów. Czasami dane są dołączane do jednego pakietu, jak właśnie powiedziałeś (lokalny stos TCP/IP), ale czasami nie, gdy mój serwer ma dużo do robienia flushu. Jest to gra MMORPG, więc kiedy użytkownicy mówią, przemieszczają się, atakują itp. W tym samym czasie na mapie, jest dużo do zrobienia, więc w pewnym momencie dwa pakiety są wysyłane w tym samym czasie do jednego gracza, a mój ISP zamyka się to gniazdo. – Reacen

+0

I przez "ten sam czas" mam na myśli z opóźnieniem mniej niż 100ms. – Reacen

Odpowiedz

3

Można utworzyć implementację opakowania w formacie Writer, aby śledzić ostatni znacznik czasu wywołania flush. Szybka implementacja polega na dodaniu wywołania wait w celu uwzględnienia opóźnienia wynoszącego 150 ms między dwoma kolejnymi opróżnieniami.

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(); 
    } 

} 

Teraz powinno wystarczyć, aby owinąć istniejącego PrintWriter z tym ControlledFlushWriter pracować wokół Twojego ISP QoS bez ponownego pisania całą swoją aplikację.

Brzmi rozsądnie, aby uniemożliwić połączenie z flagą któregokolwiek z jego pakietów jako pilne ... W takim przypadku trudno jest wdrożyć uczciwe udostępnianie linków QoS.

+0

Dziękuję bardzo! – Reacen

5

Jeśli twój ISP nakłada takie zasady jakości usług i nie masz możliwości ich negocjowania, proponuję, abyś egzekwował te zasady również po swojej stronie z konfiguracją QoS stosu TCP/IP.

Spłukanie oznacza, że ​​pakiet TCP jest pilny (flaga URG), tak aby był wysyłany niezależnie od stanu okna bufora/TCP. Teraz trzeba powiedzieć systemu operacyjnego lub innego sprzętu sieciowego na linii albo

  • ignorować (lub po prostu reset) pilna flaga gdy poprzedni pakiet został wysłany w ciągu ostatnich 150 ms i zrobić buforowania jeśli to konieczne
  • opóźnić dostarczenie kolejnych pilnych pakietów dla dotrzymania ograniczenia 150 ms.

Prawdopodobnie istnieje drogie oprogramowanie dla systemu Windows. Osobiście uważam, że umieszczenie Linux-a jako routera pomiędzy stacjami roboczymi Windows i modemem z appropriate QoS settings w iptables i qdisc to załatwi sprawę.

Powiązane problemy