2012-01-12 19 views
19

Próbuję zrobić kodowanie agnostyczne IP i jak sugerują różne źródła próbowałem użyć sockaddr_storage. Jednak wszystkie wywołania API (getaddrinfo, getnameinfo) nadal zależą od struct sockaddr. A rzucanie między nimi nie jest dobrym rozwiązaniem, gale powodują wiele innych problemów.API przy użyciu sockaddr_storage

Odlewanie do sockaddr_in i sockaddr_in6 osobno prowadzi do tego, że próbuję użyć sockaddr_storage.

Każdy, kto skutecznie użył sockaddr_storage w tworzeniu aplikacji na proste gniazdo klienta.

Odpowiedz

23

Problem ze wspólną obsługą programowania IPV6 i IPV4 polega na tym, że sama struktura sockaddr nie jest wystarczająco duża, aby pomieścić sockaddr_in6. Więc jeśli musisz ślepo omijać adres, który może być sockaddr_in lub sockaddr_in6, sockaddr_storage jest nieco łatwiejszy w użyciu.

Pod koniec dnia, niezależnie od tego, czy używasz sockaddr_in, sockaddr_in6, czy sockaddr_storage, będziesz musiał rzucić te wskaźniki, aby nawiązać połączenie z sendto, recvfrom, connect, accept i wieloma innymi funkcjami gniazda. To tylko znany niuans programowania gniazd. Po prostu puść poczucie, że robisz coś niebezpiecznego. Twój kod będzie w porządku.

Teraz, pisząc kod sieciowy, który ma działać zarówno w IPV4, jak i IPV6, możesz łatwo wpaść w pułapkę posiadania dużej liczby instrukcji switchowych do obsługi różnych typów sieci. Kod następnie dostaje bałagan tak:

if (addr.ss_family == AF_INET) 
    sendto(sock, buffer, len, 0, (sockaddr*)&addr, sizeof(sockaddr_in)) 
else (addr.ss_family == AF_INET6) 
    sendto(sock, buffer, len, 0, (sockaddr*)&addr, sizeof(sockaddr_in6)); 

I wtedy ten rodzaj „jeśli rodzina == AF_INET” ekspresja może łatwo zacząć powtarzać w kółko. Tego właśnie chcesz uniknąć.

Zakładając, że korzystasz z C++, okaże się, że klasa abstrakcji dla obiektu adresu gniazda jest niesamowicie przydatna. Mam przykład na github here i here. Klasa CSocketAddress jest wspierana przez powiązanie {sockaddr, sockaddr_in, sockaddr_in6} i może być zbudowana z sockaddr_storage. Gdybym wiedział o sockaddr_storage zanim zacząłem tę lekcję, użyłbym tego zamiast rzeczy związkowej. W każdym razie, to pozwala mi napisać kod w następujący sposób:

CSocketAddress addr; 
... 
sendto(sock, buffer, len, 0, addr.GetSockAddr(), addr.GetSockAddrLength()); 

Podobnie, „przyjąć” zestawienie wygląda następująco:

sockaddr_storage addrstorage = {}; 
int len = sizeof(sockaddr_storage); 
accept(sock, (sockaddr*)&addrstorage, &len); 

CSocketAdddress addr(addrstorage); // construct an address object to pass around everywhere else 

To było niezwykle pomocne dla ścieżek kodowych, które wymagają wiązania, wyślij i przywróć. Teraz mój serwer STUN i ścieżki kodu klienta nie muszą już nic wiedzieć o typie rodziny adresu gniazda.Działają tylko z obiektami "CSocketAddress". Jedyny specyficzny kod IPV4 i IPV6 jest podczas inicjowania klienta i serwera - kiedy obiekty adresów są faktycznie konstruowane. Na szczęście zostało to częściowo wyabstrahowane.

Możesz także chcieć zapoznać się z funkcjami pomocnika here. Jest więcej przydatnych rzeczy do rozwiązywania nazw hostów, wyliczania kart itp. ... w sposób niepodatny na IP. Jest to kod Linuksa, ale niektóre z nich powinny być dobrze odwzorowane na Windows i winsock.

Już prawie skończyłem dodawać obsługę TCP do tej bazy kodu. W procesie dodawania obsługi SOCK_STREAM nie musiałem wprowadzać jednej zmiany ani dodawać nowego kodu, aby poradzić sobie z różnicami w strukturach adresów IPV4 i IPV6.

3

Nie widzę potrzeby na struct sockaddr_storage. Jego celem jest przydzielenie wystarczającej ilości miejsca dla struktury sockaddr dowolnego protokołu, ale jak często trzeba to zrobić w agnostycznym kodzie IP? Zwykle wywołujesz getaddrinfo() i daje ci to mnóstwo struct sockaddr * s, i nie obchodzi cię, czy są one sockaddr_in lub , po prostu przekazujesz je tak, jak to jest bind() i connect() (nie jest wymagane rzutowanie).

W typowym kodzie klienta/serwera, głównym miejscem, w którym mogę myśleć o tym, gdzie jest użyteczny struct sockaddr_storage, jest zarezerwowanie miejsca dla drugiego parametru na accept(). W tym przypadku zgadzam się, że brzydkim jest rzucenie go do struct sockaddr * raz dla accept() i ponownie dla getnameinfo(). Ale nie widzę sposobu na obejście tych odlewów. To jest C. Dziedziczenie struktury zawsze wiąże się z wieloma rzutami.

+0

Należy zauważyć, że funkcja gethostbyname() zwraca niezgodność z sockaddr_in - zwraca wskaźniki do czterobajtowych adresów hostów, a nie do pełnych instancji sockaddr. W ten sposób skopiujesz pole h_addr_list [0] do pola sin_addr pliku sockaddr_in dla IPV6 i do pola sin6_addr pliku sockaddr_ipv6. –

+0

@JonWatte: true, a to jeszcze jeden powód, dla którego nie należy używać 'gethostbyname()'. Zamiast tego użyj 'getaddrinfo()'. – Celada

+0

Tak, w bazach kodu, w których mam tę wolność, to jest lepsze. –

Powiązane problemy