2012-01-12 38 views
19

我試圖做一些IP不可知編碼,並建議由各種來源,我試圖使用sockaddr_storage。但是所有的API調用(getaddrinfo,getnameinfo)仍然依賴於struct sockaddr。而在他們之間鑄造並不是一個好的選擇,gves引發了很多其他問題。API使用sockaddr_storage

分別鑄造到sockaddr_in和sockaddr_in6有點失敗了我嘗試使用sockaddr_storage的目的。

任何在devloping一個簡單的客戶端服務器套接字應用程序中有效使用sockaddr_storage的人。

回答

23

聯合完成IPV6和IPV4編程的問題在於純粹的sockaddr結構本身並不足以容納sockaddr_in6。因此,如果您需要盲目傳遞可能是sockaddr_in或sockaddr_in6的地址,則sockaddr_storage更容易使用。

在一天結束時,無論您是使用sockaddr_in,sockaddr_in6還是sockaddr_storage,您都必須強制轉換這些指針來調用sendto,recvfrom,connect,accept和其他許多套接字函數。這只是套接字編程的已知細微差別。只是放棄做不安全的感覺。你的代碼會好的。

現在編寫網絡代碼可以同時適用於IPV4和IPV6時,您可以很容易陷入擁有大量交換語句來處理不同網絡類型的陷阱。然後,代碼就會變得混亂類似如下:

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

然後該類型的:「如果家庭== AF_INET」表達可以輕鬆地開始一遍又一遍地重演。這就是你想要避免的。

假設您使用的是C++,您會發現套接字地址對象的抽象類非常有用。我在github herehere上有一個例子。 CSocketAddress類由{sockaddr,sockaddr_in,sockaddr_in6}的聯合支持,並且可以使用sockaddr_storage構造。如果我在開始這個課程之前就已經瞭解了sockaddr_storage,我會用它來代替union這個東西。在任何情況下,它可以讓我寫出如下代碼:

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

同樣,「接受」的聲明看起來是這樣的:

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 

這是爲調用bind的代碼路徑難以置信的幫助,發送和接收。現在我的STUN服務器和客戶端代碼路徑不再需要知道任何有關套接字地址的系列類型。他們只使用「CSocketAddress」對象。唯一的IPV4和IPV6特定代碼是在客戶端和服務器初始化期間 - 地址對象實際構建時。幸運的是,這也被部分抽象出來了。

您可能還想仔細閱讀幫助函數here。有一些更有用的東西來解決主機名,枚舉適配器等...以IP不可知的方式。這是Linux代碼,但其中一些應該可以映射到Windows和winsock。

我幾乎完成添加TCP支持這個代碼庫。在添加對SOCK_STREAM的支持的過程中,我不必進行單個更改,也不需要添加任何新代碼來處理IPV4和IPV6地址結構中的差異。

3

我通常不會看到需要struct sockaddr_storage。它的目的是爲任何給定的協議的sockaddr結構分配足夠的空間,但是在IP版本不可知的代碼中,你需要多久執行一次?通常你打電話getaddrinfo(),它給你一堆struct sockaddr * s,你不在乎他們是sockaddr_in還是,你只是將它們按原樣傳遞給bind()connect()(不需要強制轉換)。

在典型的客戶端/服務器代碼中,我能想到struct sockaddr_storage有用的主要位置是爲第二個參數預留空間至accept()。在這種情況下,我同意將它投射到struct sockaddr *一次爲accept(),再次爲getnameinfo()是醜陋的。但我無法看到圍繞這些演員的方式。這是C.結構繼承總是涉及到大量的演員。

+0

請注意,gethostbyname()返回的內容與sockaddr_in不兼容 - 它返回指向4字節主機地址的指針,而不是完整的sockaddr實例。因此,將h_addr_list [0]字段複製到IPV6的sockaddr_in的sin_addr字段中,並複製到sockaddr_ipv6的sin6_addr字段中。 – 2016-08-06 21:27:07

+0

@JonWatte:是的,這只是另一個不使用'gethostbyname()'的理由。改用'getaddrinfo()'。 – Celada 2016-08-07 08:58:44

+0

是的,在我擁有那種自由的代碼基礎上,那更好。 – 2016-08-12 18:15:34