Autor Wątek: UDP Hole Punching - problem  (Przeczytany 1946 razy)

Offline nonGENIUS

  • Użytkownik

# Grudzień 29, 2015, 22:29:04
Cześć,
od jakiegoś czasu pracuję nad pewną grą sieciową. Zależy mi głównie na połączeniu peer to peer z wykorzystaniem zewnętrznego serwera, który służyłby do logowania i zapisywania pewnych informacji. Natknąłem się na artykuł odnośnie UDP Hole Punching i stwierdziłem, że jest to chyba najkorzystniejsze rozwiązanie w tym przypadku.

Najpierw próbowałem połączyć dwa komputery na zasadzie "zgadywania portów", jak można się domyślić - bezskutecznie (co mnie zresztą wcale nie dziwi). Zakupiłem więc do testów VPS'a, aby zbadać problem od środka. Stworzyłem prosty serwer i klienta, następnie wszystko uruchomiłem i zadziałało. Pomyślałem więc, że skoro mogę odczytać IP i port każdego klienta połączonego z serwerem to połączenie klient-klient nie powinno być skomplikowane. Okazało się, że jest.
Teoretycznie podpinając klienta do konkretnego portu na routerze jest wykonywane przekierowanie przez NAT do dowolnego wolnego portu, a następnie oczekuje na pakiet od adresu na który została wysłana wiadomość. Okazuje się, że w przypadku mojej sieci zabezpieczenie - NAT dla komunikacji z różnymi adresami tworzy połączenie na osobnym porcie.
Aby lepiej wyjaśnić problem podam konkretny przykład: Serwer obsługuje dwa porty (5000 i 5001) a klient jest powiązany z jakimś z portem na routerze (przykładowo 4000). Po wysłaniu wiadomości do obu portów serwera NAT klienta przydziela dwa osobne porty do komunikacji (18683 i 18684). Zazwyczaj nr portów są dobierane po kolei i każdy następny jest większy o 1 wyższy, jednak zdarzały się przypadki gdy różnica wynosiła więcej niż 100. Jak połączyć obu klientów, skoro ciężko stwierdzić na którym porcie została otwarta komunikacja?
Jak w takim przypadku należy postępować? Używam pythona, jednak nie jest to istotne gdyż interesuje sposób w jaki należy to rozwiązać.
Z góry dzięki za odpowiedź.

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline dzemeuksis

  • Użytkownik
    • Blog quodmeturbat

# Grudzień 29, 2015, 22:48:33
Tak na szybko, wydaje się, że pomocnym może okazać się zguglanie haseł takich, jak NAT traversal, STUN, TURN, ICE.

Offline nonGENIUS

  • Użytkownik

# Grudzień 30, 2015, 15:16:31
Zgooglowałem zaproponowane hasła i w sumie niewiele to dało w kwestii samego rozwiązania (były to dokładniejsze wyjaśnienia tego, co już wiedziałem). Przypadkowo natknąłem się na inne ciekawe ominięcie problemu, które wstępnie przetestowałem i wydaje się być ok.

rozumiem to w ten sposób:
Klient podpina się do portu routera (np. 12345), następuje przekierowanie przez NAT do innego portu (np. 4444), każda kolejna próba nawiązania połączenia z innym adresem otrzyma inny, kolejny wolny port NAT (np. 4456, 4500, 4501). Jeżeli klient wyłączy gniazdo to NAT przez ok. 20 sek. będzie trzymał te porty w pogotowiu (gniazdo zostało zamknięte w routerze, nie w sieci NAT, więc oczekują na timeout). Jeżeli w przeciągu 20 sek. znowu zostanie uruchomione gniazdo 12345 na routerze to za automatu NAT przyzna te same porty.
Także wymyślonym przeze mnie rozwiązaniem jest otwarcie na serwerze dodatkowych portów, które by służyły do nawiązywania wstępnego połączenia. Serwer znając dokładne IP i porty graczy rozsyła je między klientami, po czym u wszystkich następuje zamknięcie gniazda i ponowne otwarcie (tym razem próbują się połączyć z adresami klientów). Wtedy gracz wykorzystywałby wyłącznie jedno gniazdo swojej sieci wewnętrznej (np. routera) do komunikacji zarówno z serwerem jak i pozostałymi graczami. Te rozwiązanie ma duży minus - jeżeli klient po 20 sek. nie połączy się z jednym z graczy będzie musiał zaczynać wszystko od nowa (resetować gniazdo i przyznawać nowe adresy NAT). Dlatego jest druga opcja: klient nasłuchiwałby dla każdego klienta osobne gniazdo. Wtedy nie musi resetować połączenia dla wszystkich. Tylko teraz pytanie - czy nasłuchiwanie po jednym porcie (UDP) dla każdego graczy ma sens (dla rozgrywki 10 osobowej + serwer trzeba by było nasłuchiwać co najmniej 10-portów)?

Offline albireo

  • Użytkownik

# Grudzień 30, 2015, 15:55:47
Co dokładniej robisz? Bo widzę jakieś kombinacje alpejskie z zamykaniem/otwieraniem gniazda (czy też wielu gniazd), gdy spokojnie powinno wystarczyć otwarcie jednego gniazda na serwerze i jednego na każdym kliencie (niezależnie od tego z iloma klientami następuje komunikacja).

Offline nonGENIUS

  • Użytkownik

# Grudzień 30, 2015, 17:11:12
Powiedzmy, że chcę napisać grę multiplayer dla 8 osób (peer to peer). W przypadku mojej sieci każde wysłanie pakietu do nowego IP jest otwierane na nowym porcie NAT i ten port przyjmuje wyłącznie pakiety od odbiorcy. Testowałem to na VPSie. Postawiłem serwer który nasłuchiwał porty 5000, 5001 i 5002, klient natomiast był podpięty do jednego portu na routerze. Wysłanie osobnej wiadomości do serwera na każdy port (z tego samego gniazda) skutkowało tym, że NAT przydzielał różne porty dla połączenia z każdym z nich (zazwyczaj następny w kolejności, lecz nie zawsze). Jeżeli szybko (poniżej 20 sek.) wyłączyłem gniazdo klienta i włączyłem od nowa zmieniając kolejność wysyłania wiadomości na porty serwera - to zostały użyte te same porty NAT, lecz połączyły się w zmienionej kolejności. Podejrzewam, że jest to zabezpieczenie stosowane w mojej sieci, które pewnie da się jakoś objeść przekierowując porty, dzwoniąc do administratora sieci itp, lecz mija to się z celem.

Offline albireo

  • Użytkownik

# Grudzień 31, 2015, 09:07:18
No to w takiej sytuacji nie da się niestety niczego, działającego pewnie, zrobić, jeśli tylko jeden z klientów byłby za tak skonfigurowanym NATem (ewentualnie więcej niż jeden, ale wszystkie za tym samym NATem i w tej samej sieci lokalnej), to on mógłby inicjować połączenia do pozostałych i wtedy by działało, jeśli więcej niż jeden, to niestety trzeba użyć jakiegoś proxy (może być nim któryś z klientów albo serwer główny, jeśli wszyscy klienci są w takiej sytuacji).