Transmisja po UDP a NAT

Transmisja po UDP a NAT
AN
  • Rejestracja:prawie 19 lat
  • Ostatnio:około godziny
0

Robię na własne potrzeby eksperymenty z transmisją UDP w C++/Qt.

Napisałem sobie aplikację bazując na https://stackoverflow.com/questions/20125641/udp-server-client-chat-in-c-qt (jest prawie kopią przedstawionego kodu) i ona działa poprawnie. W moim przypadku, aplikacja ma jedno gniazdo, a jak chcę zrealizować test połączenia, to uruchamiam dwie instancje.

U mnie wszystko działa, jednak stwierdziłem, żeby działało, to na obu aplikacjach muszę uruchomić socket i zbindować na innym porcie, a przy wysyłaniu lub odbieraniu muszę znać adres IP i numer drugiego portu komputera.

UDP ma tą cechę, że powoduje mniejsze obciążenie sieci i komputerów w zamian za brak gwarancji dotarcia każdego komunikatu i brak kontroli zachowania kolejności komunikatów.

Załóżmy, że chciałbym zrealizować komunikację UDP między dwoma komputerami w takiej kolejności:
Jest router 1, który ma serwer DHCP, do niego podłączony jest komputer A i router 2. Natomiast router dwa ma swój serwer DHCP i do siebie podłączony komputer B. Router 1 rozdaje adresy z puli 192.168.1.x i komputer A dostał 192.168.1.2, a router 2 dostał 192.168.1.3. Router 2 rozdaje adresy z puli 192.168.2.z i komputer B dostał adres 192.168.2.2. Na routerach nie są ustawione żadne przekierowania transmisji.

W przypadku TCP, przy takiej konfiguracji, jak komputer A jest serwerem, to komputer B może się podłączyć posługując się adresem 192.168.1.2, natomiast jak komputer B jest serwerem, to komputer A nie może się podłączyć, bo komputer A nie dotrze pod adres 192.168.2.2, a pod adres 192.168.1.3 nie dotrze do komputera B, bo na routerze nie ma przekierowania, nie ma ograniczeń puli otwartych portów.

Jednak po uzyskaniu połączenia można wysyłać komunikaty w obie strony.

Natomiast przy UDP nie ma czegoś takiego, jak połączenie. W tej konfiguracji udało mi się wysłać pakiet z B do A, ale z A do B nie udało mi się wysłać.

Skoro wszystkie media strumieniowe działają w oparciu o UDP, to musi dać się zrealizować komunikację UDP w obie strony przy przedstawionej konfiguracji, po poprzez analogię, przy odbiorze radia internetowego, komputer A będzie serwerem rozgłośni, a komputer B będzie moim komputerem, na którym słucham radia, więc musi dać się wysłać UDP z A do B.

W jaki sposób można zrealizować transmisje UDP w obie strony w podanej konfiguracji bez żadnych zmian w połączeniach i konfiguracji sieci?

Zobacz pozostały 1 komentarz
AK
2. łaczenie UDP komputerów z dwóch różnych NAT odbywa się z pomocą "trzeciego hosta" na etapie "przedstawienia się", potem już nie.. Nazwa takiego scenariusza mi uleciała z głowy, ale to klasyka. Odpowiednik w TCP jest za to nowością
nalik
To co mówisz, to zupełnie inny mechanizm. Służy od obchodzenia NAT, gdy oba hosty za takim stoją. Nazywa się to hole punching. Ale W przypadku, gdy jeden z adresów jest bezpośrednio dostępny, nie ma to sensu.
AK
dzieki za nazwę. Widocznie coś mi w poście umknęło, masz rację
AN
Za tym NAT chodzi tylko i wyłącznie o to, że problem zaczyna się w momencie, gdy jedna strona jest za NAT. Jak obie strony są za NAT, to potrzebny jest trzeci host, a to już inny problem i to nie o to mi chodzi. A jak obie strony nie są za NAT (należą do tej samej sieci), to nie ma żadnego problemu.
nalik
O matko ... , powtórzę jeszcze raz, jak jeden jest za NAT to też nie ma problemu. Nie rozumiesz jak działa NAT. Telcomy korzystają z carrier-grade-nat na wielką skalę i jakoś im to działa ...
nalik
  • Rejestracja:około 9 lat
  • Ostatnio:prawie 2 lata
  • Postów:1039
2

Nie wiem czy do końca zrozumiałem, co chcesz zrobić, ale załóżmy, ze tak.

andrzejlisek napisał(a):

U mnie wszystko działa, jednak stwierdziłem, żeby działało, to na obu aplikacjach muszę uruchomić socket i zbindować na innym porcie, a przy wysyłaniu lub odbieraniu muszę znać adres IP i numer drugiego portu komputera.

Tu jest moim zdaniem problem. Otóż w takiej konfiguracji masz 2 serwery. Czyli komunikację peer-to-peer, a nie klient-serwer. Chyba, że źle rozumiałem, i ty bindujesz ten sam socket, którym wysyłasz.

Jednak po uzyskaniu połączenia (TCP) można wysyłać komunikaty w obie strony.

Natomiast przy UDP nie ma czegoś takiego, jak połączenie. W tej konfiguracji udało mi się wysłać pakiet z B do A, ale z A do B nie udało mi się wysłać.

A tu jest błąd w rozumowaniu. To nie dzięki połączeniu TCP możesz wysyłać komunikaty w obie strony. To zasługa mapowania pomiędzy siecią wewnętrzną a zewnętrzną, którą dynamicznie robi moduł NAT twojego routera.

W przypadku TCP, ale także UDP, NAT działający na routerze zapamiętuje krotkę (ip_src, port_src, ip_dst, port_dst, proto) z pakietu wychodzącego ze swojej sieci. W przypadku pakietów pofragmentowanych będzie musiał skorzystać z pola identification nagłówka pakietu ip aby poskłądać dane warstwy 4. Następnie router podmienia adres źródłowy oraz port w pakietach wychodzących na swój własny, czyli wychodzące dane mają wpisane (ip_r, port_r, ip_dst, port_dst, proto).

Krotki (ip_src, port_src, ip_dst, port_dst, proto) i (ip_r, port_r, ip_dst, port_dst, proto) są ze sobą połączone, router będzie je trzymał w tablicy, aby potem w łatwy sposób zidentyfikować hosta w sieci wewnętrznej. Odbiorca będzie odsyłał pakiety na adres routera. Router widząc pakiety od odbiorcy zidentyfikuje prawdziwego hosta w swojej sieci wewnętrznej, wystarczy że sprawdzi wszystkie połączenia (tzw. flowtable) i znajdzie to odpowiednie. Prosta podmiana adresów i portów, i dane są przekazywane do hosta w sieci wewnętrznej.

Teraz, jeżeli A i B nasłuchują na swoich adresach, to niestety, ale podwoiłeś ilość portów potrzebnych do tej komunikacji. Host A używa jednego portu dla swojego serwera, ale też łącząc się do hosta B, otrzymuje następny port, który używany jest w komunikacji zainicjowanej z A do B . I to samo po stronie B. Przez to NAT nie ma pełnego mapowania, bo ruch jest niesymetryczn ... tzn posługujesz się jedną parę (ip, port1) do wysyłania, a inną, nadaną automatycznie, tj (ip, port2), do odbierania. To nie będzie działać zarówno na UDP jak i TCP jeżeli w grę wchodzi NAT.

Skoro wszystkie media strumieniowe działają w oparciu o UDP, to musi dać się zrealizować komunikację UDP w obie strony przy przedstawionej konfiguracji, po poprzez analogię, przy odbiorze radia internetowego, komputer A będzie serwerem rozgłośni, a komputer B będzie moim komputerem, na którym słucham radia, więc musi dać się wysłać UDP z A do B.

Działają, ponieważ masz komunikację klient-serwer. Przy takim schemacie NAT jest w stanie zmapować połączenia zewnętrze i wewnętrzne.

W jaki sposób można zrealizować transmisje UDP w obie strony w podanej konfiguracji bez żadnych zmian w połączeniach i konfiguracji sieci?

Nie bindować osobnych adresów dla każdego hosta. Albo raczej posługiwać się jedną parę.
Tzn jeżeli A wysłał coś do B, to niech używa tego samego socketa do odbierania wiadomości od A. Bind jest tutaj zbędny by odbierać pakiety od B. Przyda się jedynie w przypadku gdy ktoś inny łączy się bezpośrednio do A.

W praktyce, jeżeli oba hosty są za NAT, to należy skorzystać z pomocy trzeciego hosta, który pomoże nawiązać "połączenie" (tak, nawet w przypadku UDP ...). Tak naprawdę rolą takiego trzeciego hosta jest zadbanie o to, by oba routery miały odpowiednie wpisy w NAT. Technika taka nazywa się **hole punching **.

edytowany 7x, ostatnio: nalik
Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
0

Skoro wszystkie media strumieniowe działają w oparciu o UDP, to musi dać się zrealizować komunikację UDP w obie strony

A to niby czemu? Od kiedy strumieniowanie potrzebuje dwustronnej komunikacji? :) Ba, czasem robi się to w ogóle multicastem nawet!


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
nowyworek
  • Rejestracja:prawie 5 lat
  • Ostatnio:ponad 4 lata
  • Lokalizacja:świat
  • Postów:174
1

No nie skomunikują się bo nie są w tej samej sieci. Jeśli na prawde nie chcesz tam wcisnąć jakiegoś protokołu rutowania dynamicznego, to ja to ciężko widzę. @Shalom juz wspomniał o multicast może to jest to czego szukasz.


Julian
AN
  • Rejestracja:prawie 19 lat
  • Ostatnio:około godziny
0
Shalom napisał(a):

Skoro wszystkie media strumieniowe działają w oparciu o UDP, to musi dać się zrealizować komunikację UDP w obie strony

A to niby czemu? Od kiedy strumieniowanie potrzebuje dwustronnej komunikacji? :) Ba, czasem robi się to w ogóle multicastem nawet!

Nie chodzi o strumieniowanie w dwie strony, tylko o to, że strumieniowanie jest zwykle w kierunku od komputera widocznego w sieci do komputera bezpośrednio niewidocznego w sieci.

nalik
No to wszystko będzie działać dzięki NAT, o ile to ten bez publicznego ip zainicjował komunikację. Przeczytaj mój post. TCP nie robi jakiejś szczególnej magi. To jest zasługa translacji połączeń/adresów.
AN
  • Rejestracja:prawie 19 lat
  • Ostatnio:około godziny
0

W tym wszystkim nie chodzi o studiowanie, jak działa TCP i UDP, co nie zmienia faktu, że lepiej wiedzieć niż nie wiedzieć, tylko realizację dwustronnej komunikacji w aplikacji korzystając z API udostępnianym przez QT. Porobiłem parę prób i mam to, co chciałem.

Udało mi się do tego dojść w następujący sposób:

  1. Na obu komputerach utworzyłem gniazdo, zbindowałem z adresem 0.0.0.0, na komputerze A port 8000, na komputerze B port 8001 (jak na obu dałem ten sam port, to robił się bałagan, komputer A przyjmował wysłane przez siebie komunikaty, które nie dochodziły do B).
  2. Komputer A wysyła komunikaty na adres routera 2 i port 8001, natomiast komputer B wysyła komunikaty na adres komputera A.
  3. Od momentu, gdy komputer B wyśle komunikat, wtedy zaczyna działać komunikacja dwustronna, czyli oba komputery mogą się porozumieć.
    Czy to właśnie tak się robi (oczywiście adresy i numery portów mogą być inne)?

Zrobiłem też taką próbę, że na obu komputerach wpisałem adres siebie (dla komputera B próbowałem zarówno adres kompa, jak i adres routera 2) i jedynie udało mi się uzyskać komunikację od A do B, w drugą stronę już nie.

Wcześniej bawiłem się z TCP, również w Qt i tam zdarzało się, że przy przesyłaniu pakietów większych od ok. 64kB, komputer odbierający odbierał dwa komunikaty zamiast jednego. Oczywiście kolejność została zachowana i dotarły wszystkie części, bo TCP to gwarantuje. Przy częstym wysyłaniu, komputer odbierający sklejał dwa otrzymane komunikaty w jeden. To już można sobie programowo poradzić z obydwoma przypadkami traktując odbierane komunikaty jako niekończący się strumień bajtowy doładowywany kolejnymi komunikatami, z którego trzeba wyłowić poszczególne komunikaty i odpowiednio zareagować.

Doczytałem, że komunikat UDP ma długość maksymalnie 65535, ale danych może mieć 65527, a jak się doposaży w nagłówek IP (komunikat między hostami), to dane mogą mieć tylko 65507 bajtów. Czy to prawda, że jak jeden komunikat będzie mieć maksymalnie 65500 bajtów danych, to mam gwarancję, ze albo dotrze cały, albo nie dotrze wcale, a jak spróbuje się wysłać większy komunikat, to część może dotrzeć w innej kolejności, a część nie dotrze wcale i informacja po drugiej stronie będzie zniekształcona lub niepełna?

nalik
  • Rejestracja:około 9 lat
  • Ostatnio:prawie 2 lata
  • Postów:1039
2
andrzejlisek napisał(a):

W tym wszystkim nie chodzi o studiowanie, jak działa TCP i UDP, co nie zmienia faktu, że lepiej wiedzieć niż nie wiedzieć, tylko realizację dwustronnej komunikacji w aplikacji korzystając z API udostępnianym przez QT. Porobiłem parę prób i mam to, co chciałem.

Tyle, że to ma znaczenie. Albo wiesz co robisz, albo nie.

Udało mi się do tego dojść w następujący sposób:

  1. Na obu komputerach utworzyłem gniazdo, zbindowałem z adresem 0.0.0.0, na komputerze A port 8000, na komputerze B port 8001 (jak na obu dałem ten sam port, to robił się bałagan, komputer A przyjmował wysłane przez siebie komunikaty, które nie dochodziły do B).
  2. Komputer A wysyła komunikaty na adres routera 2 i port 8001, natomiast komputer B wysyła komunikaty na adres komputera A.

routera 2 i port 8001 ? Nie znasz tego adresu dopóki B się nie skomunikuje z A. Nie powinieneś wysyłać nic bezpośrednio na adres rutera. (chyba, że zrobiłeś ręczne mapowanie w NAT/DMZ)
Jakby A w tym momencie wysłał coś na adres B, to by się "odbił". Jakby A wysłał na adres routera B (2), zanim B się połączył z A to też by się "odbił".
Dopiero gdy B się podłączy do A, to A może wysyłać coś na ten adres. I tu jedna uwaga, to mapowanie i port wygenerowany przez ruter są tymczasowe. Jak komunikacja zaniknie, to router w końcu sprzątnie tymczasowe połączenie. W przypadku TCP pomaga keep-alive, które utrzyma mapowanie na NAT. W przypadku UDP trzeba by co jakiś czas coś wysyłać.

  1. Od momentu, gdy komputer B wyśle komunikat, wtedy zaczyna działać komunikacja dwustronna, czyli oba komputery mogą się porozumieć.
    Czy to właśnie tak się robi (oczywiście adresy i numery portów mogą być inne)?

Tak, bo wysłanie tego pakietu UDP z B do A skutkuje stworzeniem mapowania pomiędzy adresem zewnętrznym a wewnętrznym na routerze, za którym stoi B. Od tego momentu router wie, że gdy dochodzą wiadomości na adres, który stworzył na potrzeby mapowania, to ma go przekazać hostowi w sieci prywatnej.

Zrobiłem też taką próbę, że na obu komputerach wpisałem adres siebie (dla komputera B próbowałem zarówno adres kompa, jak i adres routera 2) i jedynie udało mi się uzyskać komunikację od A do B, w drugą stronę już nie.

Nie rozumiem, co to znaczy adres siebie.

Wcześniej bawiłem się z TCP, również w Qt i tam zdarzało się, że przy przesyłaniu pakietów większych od ok. 64kB, komputer odbierający odbierał dwa komunikaty zamiast jednego. Oczywiście kolejność została zachowana i dotarły wszystkie części, bo TCP to gwarantuje.

Mylisz się. Na poziomie aplikacji nie wiesz ile segmentów TCP otrzymałeś. Wiesz jedynie ile razy odczytałeś część strumienia z jadra systemu.

Przy częstym wysyłaniu, komputer odbierający sklejał dwa otrzymane komunikaty w jeden. To już można sobie programowo poradzić z obydwoma przypadkami traktując odbierane komunikaty jako niekończący się strumień bajtowy doładowywany kolejnymi komunikatami, z którego trzeba wyłowić poszczególne komunikaty i odpowiednio zareagować.

Bo taka jest istota TCP. Wysyłasz ciągły strumień bajtów. Jak chcesz mieć podział na wiadomości, to robisz to ponad TCP korzystając z jakiegoś sposobu kodowania wiadomości.

Doczytałem, że komunikat UDP ma długość maksymalnie 65535, ale danych może mieć 65527, a jak się doposaży w nagłówek IP (komunikat między hostami), to dane mogą mieć tylko 65507 bajtów.

Trochę pomyliłeś pojęcia. 65535 to maksymalny rozmiar pakietu ip v4. Wynika z tego, że pole Total Length z nagłówku pakietu ipv4 ma 16 bitów. Pakiet ip v4 w sekcji danych zawiera nagłówek UDP wraz z danymi warstwy 4. Wszystko musi się zmieścić, bo UDP nie dzieli wiadomości (wiadomości dzieli warstwa 3, jeden pakiet ip może być rozbity na wiele fragmentów, ale łączny limit jest taki jak wyżej).

Czy to prawda, że jak jeden komunikat będzie mieć maksymalnie 65500 bajtów danych, to mam gwarancję, ze albo dotrze cały, albo nie dotrze wcale, a jak spróbuje się wysłać większy komunikat, to część może dotrzeć w innej kolejności, a część nie dotrze wcale i informacja po drugiej stronie będzie zniekształcona lub niepełna?

Po kolei:

  1. Nie da się wysłać większego komunikatu niż 65535 po UDP.
  2. Raczej starałbym się ograniczyć do MTU w sieci, albo nawet na ścieżce. Fragmentacja pakietów ip poważnie ogranicza transfer.
  3. Zawsze masz taką gwarancję, że segment UDP dotrze cały, albo wcale. Jakby część została stracona z powodu fragmentacji, to w aplikacji nie otrzymałbyś żadnej części tego segmentu.
  4. Zanim dane z segmentu UDP zostaną przekazane do aplikacji, to muszą być poskładane z fragmentów pakietu ip. Aplikacja nigdy nie dostanie tylko części danych wysłanych w pojedynczym segmencie.
  5. Co do tego limitu danych, wątpię. Jest mnóstwo innych czynników, które mogą pomniejszyć maksymalny rozmiar danych. Żeby nie szukać daleko, enkapsulacja ipv4 w ipv4.
edytowany 6x, ostatnio: nalik
AN
  • Rejestracja:prawie 19 lat
  • Ostatnio:około godziny
0
nalik napisał(a):
andrzejlisek napisał(a):

W tym wszystkim nie chodzi o studiowanie, jak działa TCP i UDP, co nie zmienia faktu, że lepiej wiedzieć niż nie wiedzieć, tylko realizację dwustronnej komunikacji w aplikacji korzystając z API udostępnianym przez QT. Porobiłem parę prób i mam to, co chciałem.

Tyle, że to ma znaczenie. Albo wiesz co robisz, albo nie.

Z tego właśnie powodu, jak napisałem, lepiej wiedzieć niż nie wiedzieć.

Zrobiłem też taką próbę, że na obu komputerach wpisałem adres siebie (dla komputera B próbowałem zarówno adres kompa, jak i adres routera 2) i jedynie udało mi się uzyskać komunikację od A do B, w drugą stronę już nie.

Nie rozumiem, co to znaczy adres siebie.

Adres siebie, czyli na komputerze A binduję z adresem IP komputera A, a na komputerze B binduję z adresem IP komputera B lub adresem IP routera 2 po stronie WAN.

edytowany 1x, ostatnio: andrzejlisek
nalik
  • Rejestracja:około 9 lat
  • Ostatnio:prawie 2 lata
  • Postów:1039
0
andrzejlisek napisał(a):

Zrobiłem też taką próbę, że na obu komputerach wpisałem adres siebie (dla komputera B próbowałem zarówno adres kompa, jak i adres routera 2) i jedynie udało mi się uzyskać komunikację od A do B, w drugą stronę już nie.

Nie rozumiem, co to znaczy adres siebie.

Adres siebie, czyli na komputerze A binduję z adresem IP komputera A, a na komputerze B binduję z adresem IP komputera B lub adresem IP routera 2 po stronie WAN.

Do bindowania zawsze powinieneś podawać adres siebie. Jak podajesz "0.0.0.0", to tak naprawdę nasłuchujesz na wszystkich możliwych adresach hosta (a więc także localhost).

Podawanie adresu rutera podczas bindowania jest bez sensu. Bindowanie to wywołanie systemowe, które informuje jądro, że aplikacja spodziewa się otrzymywać wiadomości pod wskazanym (adresem, portem, protokołem). Dzięki temu jądro wie, że w momencie gdy z karty sieciowej zostanie odczytany pakiet zaadresowany takimi wartościami, to jest aplikacja, która potrzebuje tych pakietów. Ruter nie ma tutaj nic do rzeczy.

edytowany 1x, ostatnio: nalik
AN
  • Rejestracja:prawie 19 lat
  • Ostatnio:około godziny
0

W takim razie, jeśli chodzi o UDP to temat łączności mam ogarnięty. Jednak w wątku przewinął się jeszcze jedna sprawa, jaką jest hole punching. Załóżmy, że teraz mam bardziej rozbudowaną konfigurację:

Kopiuj
                ╔═════════╗
                ║Router 1 ║
                ║10.10.0.2║
                ╚══╤═╤═╤══╝
                   │ │ │
      ┌────────────┘ │ └────────────┐
      │              │              │
╔═════╧═════╗  ╔═════╧═════╗  ╔═════╧═════╗
║192.168.1.3║  ║192.168.1.2║  ║192.168.1.4║
║ Router 2  ║  ║  Host A   ║  ║ Router 3  ║
╚═════╤═════╝  ╚═══════════╝  ╚═════╤═════╝
      │                             │
      │                             │
╔═════╧═════╗                 ╔═════╧═════╗
║192.168.2.2║                 ║192.168.3.2║
║  Host B   ║                 ║  Host C   ║
╚═══════════╝                 ╚═══════════╝

W przypadku TCP i UDP to połączenie A z B oraz z A do C nie stanowi żadnego problemu pod warunkiem, że w przypadku TCP serwer będzie na A, a klient na B lub C, a w przypadku UDP, pierwszy komunikat będzie z B do A lub z C do A, wtedy komunikacja w obie strony będzie działać.

W takim razie w jaki sposób nawiązuje się połączenie między B i C? Rozumiem, że do takiego celu stosuje się hole punching. Z tego, co wyczytałem, to polega to na tym, że na A musi być uruchomiona aplikacja, która uruchamia dwa serwery, na jednym przyjmuje połączenie od B, na drugim przyjmuje połączenie od C. Na podstawie tych połączeń ta aplikacja wie, jaki jest adres i port hostów B i C z perspektywy hostu A i tym samym routera 1, w międzyczasie routery 2 i 3 ustawiły translację NAT stosownie do tych połączeń. Potem A przekazuje do B informację o adresie C, a do C przekazuje informacje o B. W drugiej kolejności, jak na B będzie uruchomiony serwer, to C będzie mógł się podłączyć. Czy tak to właśnie wygląda?

W takim razie, czy używając interfejsów gniazd TCP i UDP dostępne w Qt i większości innych API, można zrealizować połączenie P2P pomiędzy B i C? Chodzi o coś na wzór popularnych niegdyś sieci wymiany plików P2P i tu należy skorzystać z hole punching.

edytowany 1x, ostatnio: andrzejlisek
nalik
  • Rejestracja:około 9 lat
  • Ostatnio:prawie 2 lata
  • Postów:1039
0
andrzejlisek napisał(a):

W takim razie, jeśli chodzi o UDP to temat łączności mam ogarnięty. Jednak w wątku przewinął się jeszcze jedna sprawa, jaką jest hole punching. Załóżmy, że teraz mam bardziej rozbudowaną konfigurację:

Kopiuj
                ╔═════════╗
                ║Router 1 ║
                ║10.10.0.2║
                ╚══╤═╤═╤══╝
                   │ │ │
      ┌────────────┘ │ └────────────┐
      │              │              │
╔═════╧═════╗  ╔═════╧═════╗  ╔═════╧═════╗
║192.168.1.3║  ║192.168.1.2║  ║192.168.1.4║
║ Router 2  ║  ║  Host A   ║  ║ Router 3  ║
╚═════╤═════╝  ╚═══════════╝  ╚═════╤═════╝
      │                             │
      │                             │
╔═════╧═════╗                 ╔═════╧═════╗
║192.168.2.2║                 ║192.168.3.2║
║  Host B   ║                 ║  Host C   ║
╚═══════════╝                 ╚═══════════╝

(...)
W takim razie w jaki sposób nawiązuje się połączenie między B i C? Rozumiem, że do takiego celu stosuje się hole punching. Z tego, co wyczytałem, to polega to na tym, że na A musi być uruchomiona aplikacja, która uruchamia dwa serwery, na jednym przyjmuje połączenie od B, na drugim przyjmuje połączenie od C.

Jedna aplikacja, które zadaniem jest rozesłanie znanych zewnętrznych ip i zewnetrznych portów, użwanych przez innych klientów, którzy się połączyli. Na jednym adresie i porcie przecież można obsłużyć wiele połączeń.

Na podstawie tych połączeń ta aplikacja wie, jaki jest adres i port hostów B i C z perspektywy hostu A i tym samym routera 1, w międzyczasie routery 2 i 3 ustawiły translację NAT stosownie do tych połączeń.

Tu się zgadza.

Potem A przekazuje do B informację o adresie C, a do C przekazuje informacje o B. W drugiej kolejności, jak na B będzie uruchomiony serwer, to C będzie mógł się podłączyć. Czy tak to właśnie wygląda?

Tu się nie zgadza. B nie tyle przekazuje informację o adresie, co próbuje się połączyć, zakładając, że ip i port zewnętrzne, którym posługiwała się aplikacja B nie uległy zmianie.
W momencie kiedy pakiet opuszcza sieć wewnętrzną, NAT tworzy odpowiednie mapowania. Gdy mapowania są już utworzone na obu NAT, to pakiety będą przechodzić granicę urządzeń robiących translację.

Niestety wiele zależy od typu NAT. Jeżeli mapowanie portu zewnętrznego i wewnętrznego jest niezmienne to jest OK. Jeżeli port jest randomizowany per połączenie albo jest wbity na stałe na zasadzie DMZ, to prawdopodobnie technika nie zadziała. Tutaj nie jestem pewien, ponieważ nigdy nie widziałem implementacji tego mechanizmu.

Za to odnalazłem materiały, które być może Ci pomogą: https://bford.info/pub/net/p2pnat/

W takim razie, czy używając interfejsów gniazd TCP i UDP dostępne w Qt i większości innych API, można zrealizować połączenie P2P pomiędzy B i C? Chodzi o coś na wzór popularnych niegdyś sieci wymiany plików P2P i tu należy skorzystać z hole punching.

Tak.

edytowany 2x, ostatnio: nalik
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)