2013-05-10 59 views
6

Tôi đang thử nghiệm với ổ cắm IPv6, đặc biệt là khả năng "ngăn xếp kép" được cung cấp trên Windows Vista và sau đó, và dường như trên Unix theo mặc định. Tôi thấy rằng khi tôi liên kết máy chủ của tôi với một địa chỉ IP cụ thể, hoặc đến độ phân giải tên máy của máy cục bộ của tôi, tôi không thể chấp nhận kết nối từ máy khách IPv4. Tuy nhiên, khi tôi liên kết với INADDR_ANY, tôi có thể.Kết nối máy khách IPv4 với máy chủ IPv6: kết nối bị từ chối

Vui lòng xem xét mã sau cho máy chủ của tôi. Bạn có thể thấy rằng tôi làm theo lời khuyên của Microsoft tạo ra một ổ cắm IPv6, sau đó thiết lập các cờ IPV6_V6ONLY để zero:

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

Đây là mã cho khách hàng của tôi. Bạn có thể thấy tôi tạo một ổ cắm IPv4 và cố gắng kết nối với máy chủ của tôi:

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

Kết quả từ cuộc gọi kết nối của tôi luôn là 10061: kết nối bị từ chối.

Nếu tôi thay đổi mã máy chủ của mình thành ràng buộc :: (hoặc chuyển một máy chủ NULL tới getaddrinfo() (cùng một điều)) và thay đổi mã máy khách của tôi để chỉ định máy chủ NULL trong cuộc gọi getaddrinfo(), thì Máy khách V4 có thể kết nối tốt.

Có ai giải thích tại sao không? Tôi đã không đọc bất cứ điều gì mà chúng ta phải chỉ định một máy chủ NULL (do đó sử dụng INADDR_ANY) nếu chúng ta muốn hành vi dual-socket. Điều này không thể là một yêu cầu, bởi vì những gì tôi có một máy chủ multihomed và tôi muốn chấp nhận IPv4 trên chỉ một số các IP có sẵn?

EDIT 15/05/2013:

Đây là tài liệu có liên quan mà đã nhận được tôi bối rối là tại sao mã của tôi thất bại:

Từ Dual-Stack Sockets for IPv6 Winsock Applications

"Windows Vista và sau đó cung cấp khả năng tạo một ổ cắm IPv6 duy nhất có thể xử lý cả lưu lượng IPv6 và IPv4.Ví dụ, một ổ cắm nghe TCP cho IPv6 được tạo ra, được đặt vào hai điểm dừng k mode, và ràng buộc để cổng 5001. Đây dual-stack ổ cắm có thể chấp nhận các kết nối từ khách hàng IPv6 TCP kết nối với cổng 5001 và từ IPv4 TCP client kết nối với cổng 5001. "

" Theo mặc định, một ổ cắm IPv6 được tạo trên Windows Vista và sau đó chỉ hoạt động trên giao thức IPv6. Để tạo ổ cắm IPv6 thành ổ cắm ngăn xếp kép, chức năng setsockopt phải được gọi với tùy chọn ổ cắm IPV6_V6ONLY để đặt giá trị này về 0 trước khi socket bị ràng buộc vào địa chỉ IP. Khi tùy chọn ổ cắm IPV6_V6ONLY được đặt về 0, ổ cắm được tạo cho họ địa chỉ AF_INET6 có thể được sử dụng để gửi và nhận gói đến và từ địa chỉ IPv6 hoặc địa chỉ IPv4 được ánh xạ. (nhấn mạnh mỏ) "

+0

Bạn cần điểm cuối IPv4 cho kết nối IPv4, 'bind' rõ ràng là điểm cuối IPv6 chứ không phải là ký tự đại diện. –

+0

@ Steve-o Tôi không hiểu ý anh là gì, xin lỗi. Bạn có thể làm rõ? – Wad

+0

Binding thực hiện một bộ lọc để chỉ chấp nhận dữ liệu với địa chỉ đích khớp với địa chỉ bị ràng buộc. Các gói IPv4 sẽ có địa chỉ đích IPv4 do đó sẽ bị từ chối. –

Trả lời

7

IPv4 và IPv6 là hai giao thức riêng biệt. Đó là lý do tại sao khái niệm Dual Stack tồn tại: hệ thống của bạn chạy cả IPv4 và IPv6 ngăn xếp giao thức, có cả địa chỉ IPv4 và IPv6, v.v.

Hệ điều hành có thể có ổ cắm IPv6 nghe trên tất cả địa chỉ IPv4 và IPv6.Bạn vẫn cần phải có cả hai gia đình địa chỉ trên máy chủ lưu trữ và nó chỉ hoạt động khi bạn liên kết với địa chỉ ký tự đại diện. Khi bạn liên kết ổ cắm đó với một địa chỉ cố định không hoạt động nữa và nó sẽ chỉ hoạt động đối với địa chỉ mà bạn đã ràng buộc.

Vì vậy, nếu bạn muốn nghe trên tất cả các địa chỉ có sẵn thì hãy đặt IPV6_V6ONLY thành 0 và nghe trên địa chỉ ký tự đại diện. Các máy khách IPv4 sẽ được hiển thị khi sử dụng các địa chỉ IPv6 bắt đầu bằng ::ffff: với 32 bit cuối cùng chứa địa chỉ IPv4.

Khi bạn muốn liên kết với các địa chỉ cụ thể, bạn sẽ cần các ổ cắm được liên kết với từng địa chỉ bạn muốn nghe. Sau đó, bạn cần sử dụng số select(...) để theo dõi các ổ cắm đó và để phản hồi những ổ cắm hoạt động vì ai đó kết nối với chúng.

+0

Cảm ơn bạn đã bình luận Sander, bình luận của bạn có ý nghĩa. Bạn có thể cung cấp một liên kết đến nơi mà tài liệu này được mặc dù không - Tôi đã không thể tìm thấy một. Ví dụ: http://msdn.microsoft.com/en-us/library/windows/desktop/bb513665%28v=vs.85%29.aspx dường như nêu rõ rằng điều duy nhất chúng tôi cần làm là đặt IPV6_V6ONLY thành 0 ... – Wad

+0

Tài liệu chính thức là RFC 3493: http://tools.ietf.org/html/rfc3493.html. Nhưng kịch bản này không được mô tả ở đó bởi vì nó là không thể về mặt kỹ thuật. Nếu bạn liên kết máy chủ với địa chỉ IPv6 thì máy khách chỉ có thể kết nối khi sử dụng địa chỉ chính xác đó làm địa chỉ đích. Một máy khách chỉ IPv4 không bao giờ có thể kết nối với một địa chỉ IPv6, giống như một máy khách chỉ IPv6 không bao giờ có thể kết nối với một địa chỉ IPv4. Nguồn và đích phải luôn là cùng một giao thức. IPv6 không tương thích ngược với IPv4 trên dây ... –

+0

Cảm ơn Sander. Tôi liên kết với các địa chỉ IPv6, vâng, nhưng như đã nói ở trên với Steve-o bằng cách thiết lập IPPROTO_IPV6 tài liệu cho rằng ổ cắm IPv6 của tôi cũng có thể chấp nhận một máy khách IPv4: địa chỉ IPv4 sẽ được ánh xạ tới một địa chỉ IPv6.Thật vậy, khi tôi chạy máy chủ của mình và nhìn vào TCPView, sau lệnh gọi setsockopt() tôi thấy * hai * ổ cắm bị ràng buộc trên powerhouse, một trong mỗi giao thức IPv4 và IPv6. Đó là lý do tại sao tôi không thể hiểu tại sao điều này vẫn không thành công ... – Wad

2

Liên kết này http://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch12lev1sec2.html cho biết thêm thông tin về các kết nối IPv4 và IPv6,

Hầu hết dual-stack host nên sử dụng các quy tắc sau đây trong việc đối phó với socket lắng nghe:

  • Một IPv4 nghe socket có thể chấp nhận các kết nối đến từ chỉ các máy khách IPv4.
  • Nếu máy chủ có ổ cắm IPv6 đang nghe đã kết nối địa chỉ đại diện và tùy chọn ổ cắm IPV6_V6ONLY (Mục 7.8) không được đặt, ổ cắm đó có thể chấp nhận kết nối đến từ máy khách IPv4 hoặc máy khách IPv6. Đối với kết nối từ máy khách IPv4, địa chỉ cục bộ của máy chủ cho kết nối sẽ là địa chỉ IPv6 tương ứng IPv4 được ánh xạ địa chỉ IPvIPv4 tương ứng.
  • Nếu máy chủ có ổ cắm IPv6 đang nghe có địa chỉ IPv6 khác với địa chỉ IPv6 được ánh xạ IPv4 hoặc đã gắn địa chỉ đại diện nhưng đã đặt tùy chọn socket IPv6_V6ONLY (Mục 7.8), ổ cắm đó có thể chấp nhận các kết nối đến từ các máy khách IPv6.
Các vấn đề liên quan