2013-05-10 491 views
6

我正在試驗IPv6套接字,尤其是在Windows Vista和更高版本上提供的「雙棧」功能,顯然在Unix上默認情況下。我發現當我將服務器綁定到特定的IP地址或本地計算機的主機名解析時,我無法接受來自IPv4客戶端的連接。當我綁定到INADDR_ANY但是,我可以。將IPv4客戶端連接到IPv6服務器:連接被拒絕

請爲我的服務器考慮下面的代碼。你可以看到,我按照創建一個IPv6套接字,那麼IPV6_V6ONLY標誌設置爲零的微軟的建議是:

addrinfo* result, *pCurrent, hints; 

memset(&hints, 0, sizeof hints); // Must do this! 
hints.ai_family = AF_INET6; 
hints.ai_socktype = SOCK_STREAM; 
hints.ai_flags = AI_PASSIVE;  // We intend to use the addrinfo in a call to connect(). (I know it is ignored if we specify a server to connect to...) 

int nRet = getaddrinfo("powerhouse", "82", &hints, &result); 

SOCKET sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); 

int no = 0; 
if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&no, sizeof(no)) != 0) 
    return -1; 

if (bind(sock, result->ai_addr, result->ai_addrlen) == SOCKET_ERROR) 
    return -1; 

if (listen(sock, SOMAXCONN) == SOCKET_ERROR) 
    return -1; 

SOCKET sockClient = accept(sock, NULL, NULL); 

這裏是我的客戶端代碼。你可以看到我創建IPv4套接字,並嘗試連接到我的服務器:從我的連接調用

addrinfo* result, *pCurrent, hints; 

memset(&hints, 0, sizeof hints); // Must do this! 
hints.ai_family = AF_INET; 
hints.ai_socktype = SOCK_STREAM; 

if (getaddrinfo("powerhouse", "82", &hints, &result) != 0) 
    return -1; 

SOCKET sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol); 
int nRet = connect(sock, result->ai_addr, result->ai_addrlen); 

結果總是10061:連接被拒絕。

如果我將服務器代碼更改爲綁定到::(或將NULL主機傳遞給getaddrinfo()(同樣的事情)),並更改我的客戶端代碼以在getaddrinfo()調用中指定NULL主機,則V4客戶端可以很好地連接。

任何人都可以解釋爲什麼請嗎?如果我們需要雙套接字行爲,我沒有讀過任何必須指定NULL主機的東西(因此使用INADDR_ANY)。這不是一個要求,因爲我有一個多宿主主機,並且我只想在某些可用的IP上接受IPv4?

編輯15/05/2013:

這是有關文件已經得到了我的困惑,爲什麼我的代碼失敗:

Dual-Stack Sockets for IPv6 Winsock Applications

「Windows Vista和後來提供了創建一個可以同時處理IPv6和IPv4流量的單個IPv6套接字的功能,例如,創建一個用於IPv6的TCP偵聽套接字,將其插入雙協議棧K色模式,和 綁定到端口5001。這種雙棧插座可以接受來自 IPv6的TCP客戶端的連接連接到端口5001,並從連接到端口5001。「

」的IPv4 TCP客戶 缺省情況下,IPv6套接字在Windows Vista上創建,後來只有 在IPv6協議上運行。爲了在 中創建一個雙棧套接字的IPv6套接字,必須使用IPV6_V6ONLY套接字選項調用setsockopt函數,以便在將套接字 綁定到IP地址之前將此值設置爲零。 當IPV6_V6ONLY套接字選項設置爲 爲零時,可以使用爲AF_INET6地址系列創建的套接字 來發送和接收來自IPv6地址或IPv4地址的數據包和從IPv4地址或IPv4 映射的地址發送和接收數據包。(重點煤礦)」

+0

您需要一個用於IPv4連接的IPv4端點,「綁定」顯式是IPv6端點而不是通配符。 – 2013-05-10 12:51:08

+0

@史蒂夫o我不明白你的意思,對不起。你能澄清嗎? – Wad 2013-05-10 12:57:52

+0

綁定實現了一個過濾器,只接受與綁定地址匹配的目標地址的數據。 IPv4數據包將具有IPv4目標地址,因此將被拒絕。 – 2013-05-10 17:43:44

回答

7

IPv4和IPv6是兩個獨立的協議中的一個協議的報文無法使用其他協議來處理這就是爲什麼雙協議棧的概念存在:。您的系統同時運行IPv4和IPv6協議棧,同時具有IPv4和IPv6地址等。

操作系統有一個技巧,你可以擁有一個偵聽所有IPv4和IPv6地址的IPv6套接字。您仍然需要在主機上同時使用兩個地址族,並且只有在綁定到通配符地址時才能使用。一旦你將該套接字綁定到一個不再工作的固定地址,它將只適用於你綁定的地址。

所以,如果你想監聽所有可用的地址,那麼將IPV6_V6ONLY設置爲0,並監聽通配符地址。 IPv4客戶端將顯示爲使用IPv6地址,從::ffff:開始,最後32位包含IPv4地址。

當你想綁定到特定的地址,你將需要綁定到每個你想聽的地址的套接字。然後,您需要使用select(...)來監視這些套接字並響應那些因爲某人連接到這些套接字而變得活躍的套接字。

+0

感謝您評論Sander,您的評論很有意義。你能否提供一個鏈接到這個記錄的位置 - 雖然我一直找不到。例如,http://msdn.microsoft.com/en-us/library/windows/desktop/bb513665%28v=vs.85%29.aspx似乎表明我們需要做的唯一事情是將IPV6_V6ONLY設置爲零... – Wad 2013-05-10 12:43:11

+0

官方文檔是RFC 3493:http://tools.ietf.org/html/rfc3493.html。但是這種情況在這裏沒有描述,因爲它在技術上是不可能的。如果將服務器綁定到IPv6地址,則只有在使用該確切地址作爲目標地址時,客戶端才能連接。僅限IPv4的客戶端永遠不能連接到IPv6地址,就像僅限IPv6的客戶端永遠不能連接到IPv4地址一樣。源和目標必須始終具有相同的協議。 IPv6不會向後兼容IPv4上的線路...... – 2013-05-10 17:06:17

+0

感謝Sander。我綁定到僅IPv6地址,是的,但是正如上面通過設置IPPROTO_IPV6向Steve-o所述,文檔聲稱我的IPv6套接字也可以接受IPv4客戶端:IPv4地址將映射到IPv6地址。事實上,當我運行我的服務器並查看TCPView時,在setsockopt()調用之後,我看到了每個IPv4和IPv6協議之一*上的兩個*綁定套接字。這就是爲什麼我不明白爲什麼這仍然失敗...... – Wad 2013-05-13 11:43:13

2

此鏈接http://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch12lev1sec2.html給出了關於IPv4和IPv6連接的更多信息,

大多數雙棧主機應該在處理 監聽套接字使用以下規則:

  • 聽音的IPv4套接字可以接受來自僅IPv4客戶端的傳入連接。
  • 如果服務器有已綁定的通配符地址,IPV6_V6ONLY套接字選項(7.8節)未設置監聽IPv6套接字, 該插座可以接受來自任何IPv4的客戶 或IPv6客戶端的連接。對於來自IPv4客戶端的連接,連接的服務器的 本地地址將是相應的IPv4映射的 IPv6地址。
  • 如果服務器具有偵聽的IPv6套接字,該套接字綁定了除IPv4映射的IPv6地址以外的IPv6地址,或綁定了通配符地址但已設置IPv6_V6ONLY套接字選項(第 7.8節),則該套接字可以接受僅來自IPv6客戶端的傳入連接。
相關問題