程式扎記: [ NP in MS ] Internet Protocol (Part4 : Writing IP Version–Independent Programs)

標籤

2011年4月28日 星期四

[ NP in MS ] Internet Protocol (Part4 : Writing IP Version–Independent Programs)


Preface :
In this section, we'll cover how to develop applications that work seamlessly over IPv4 and IPv6. This method requires using the new name resolution APIs getaddrinfo and getnameinfo and requires a bit of rearranging Winsock calls from what you are probably used to.
Before we get into the specifics, let's cover some of the basic practices that you should follow. First, applications should not allocate the socket address structures specific to each protocol (such as SOCKADDR_IN and SOCKADDR_IN6 for IPv4 and IPv6, respectivelybecause they can be different sizes. Instead, a new socket address structure SOCKADDR_STORAGE has been introduced that is as large as the largest possible protocol specific address structure and includes padding for 64-bit alignment issues. The following code uses a SOCKADDR_STORAGE structure to store the destination IPv6 address :
  1. SOCKADDR_STORAGE                    saDestination;  
  2. SOCKET                  s;  
  3. int                 addrlen,  
  4.                     rc;  
  5.   
  6. s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);  
  7. if (s == INVALID_SOCKET) {  
  8.         // socket failed  
  9. }  
  10. addrlen = sizeof(saDestination);  
  11. rc = WSAStringToAddress(  
  12.             "3ffe:2900:d005:f28d:250:8bff:fea0:92ed",  
  13.             AF_INET6,  
  14.             NULL,  
  15.             (SOCKADDR *)&saDestination,  
  16.             &addrlen  
  17.             );  
  18. if (rc == SOCKET_ERROR) {  
  19.         // conversion failed  
  20. }  
  21. rc = connect(s, (SOCKADDR *)&saDestination, sizeof(saDestination));  
  22. if (rc == SOCKET_ERROR) {  
  23.         // connect failed  
  24. }  
Second, functions that take an address as a parameter should pass the entire socket address structure and not the protocol specific types like struct in_addror struct in6_addr. This is important for IPv6, which might require the scope ID information to successfully connect. The SOCKADDR_STORAGE structure containing the address should be passed instead.
Third, avoid hardcode addresses regardless of whether they are IPv4 or IPv6. The Winsock header files define constants for all the address that are hard coded such as the loopback address and the wildcard address used for binding.
Now that some of the basic issues are out of the way, let's move to discussing how an application should be structured to be IP independent. We will divide our discussion into two sections: the client and the server.

Client :
For both TCP and UDP clients, the application typically possesses the server (or recipient's) IP address or hostname. Whether it resolves to an IPv4 address or IPv6 address doesn't matter. The client should follow these three steps :
1. Resolve the address using the getaddrinfo function. The hints should contain AF_UNSPEC as well as the socket type and protocol depending on whether the client uses TCP or UDP to communicate.

2. Create the socket using the ai_familyai_socktype, and ai_protocol fields from the addrinfo structure returned in step 1.

3. Call connect or sendto with the ai_addr member of the addrinfo structure.

The following code sample illustrates these principles :
  1. SOCKET               s;  
  2. struct addrinfo hints,  
  3.                 *res=NULL  
  4. char            *szRemoteAddress=NULL,  
  5.                 *szRemotePort=NULL;  
  6. int         rc;  
  7.   
  8. // Parse the command line to obtain the remote server's  
  9. // hostname or address along with the port number, which are contained  
  10. // in szRemoteAddress and szRemotePort.  
  11. memset(&hints, 0, sizeof(hints));  
  12. hints.ai_family = AF_UNSPEC;  
  13. hints.ai_socktype = SOCK_STREAM;  
  14. hints.ai_protocol = IPPROTO_TCP;  
  15. // first resolve assuming string is a string literal address  
  16. rc = getaddrinfo(  
  17.             szRemoteAddress,  
  18.             szRemotePort,  
  19.             &hints,  
  20.             &res  
  21.          );  
  22. if (rc == WSANO_DATA) {  
  23.         // Unable to resolve name - bail out  
  24.    }  
  25. s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);  
  26. if (s == INVALID_SOCKET) {  
  27.         // socket failed  
  28. }  
  29. rc = connect(s, res->ai_addr, res->ai_addrlen);  
  30. if (rc == SOCKET_ERROR) {  
  31.         // connect failed  
  32. }  
  33. freeaddrinfo(res);  
First, you will notice that there are no explicit references to AF_INET or AF_INET6. Also, there's no need to manipulate the underlying SOCKADDR_IN orSOCKADDR_IN6 addresses. The getaddrinfo call fully initializes the returned socket address structure with all the required information—address family, binary address, etc.—that is necessary for connecting or sending datagrams.
If the client application needs to explicitly bind the socket to a local port after socket creation but before connect or sendto, then another getaddrinfo call can be made. This call would specify the address family, socket type, and protocol returned from the first call along with the AI_PASSIVE flag and desired local port, which will return another socket address structure initialized to the necessary bind address (such as 0.0.0.0 for IPv4 and :: for IPv6).

Server :
The server side is a bit more involved than the client side. This is because the Windows IPv6 stack is a dual stack. That is, there is a separate stack for IPv4 and IPv6, so if a server wishes to accept both IPv4 and IPv6 connections, it must create a socket for each one. The two steps for creating an IP independent server are the following :
1. Call getaddrinfo with hints containing AI_PASSIVEAF_UNSPEC, and the desired socket type and protocol along with the desired local port to listen or receive data on. This will return two addrinfo structures: one containing the listening address for IPv4 and the other containing the listening address for IPv6.

2. For every addrinfo structure returned, create a socket with the ai_familyai_socktype, and ai_protocol fields followed by calling bind with the ai_addrand ai_addrlen members.

The following code illustrates this principle :
  1. SOCKET              slisten[16];  
  2. char                *szPort="5150";  
  3. struct addrinfo               hints,  
  4.                 * res=NULL,  
  5.                 * ptr=NULL;  
  6. int               count=0,  
  7.                   rc;  
  8.   
  9. memset(&hints, 0, sizeof(hints));  
  10. hints.ai_family = AF_UNSPEC;  
  11. hints.ai_socktype = SOCK_STREAM;  
  12. hints.ai_protocol = IPPROTO_TCP;  
  13. hints.ai_flags = AI_PASSIVE;  
  14. rc = getaddrinfo(NULL, szPort, &hints, &res);  
  15. if (rc != 0) {  
  16.         // failed for some reason  
  17. }  
  18. ptr = res;  
  19. while (ptr)  
  20. {  
  21.         slisten[count] = socket(ptr->ai_family,   
  22.         ptr->ai_socktype, ptr->ai_protocol);  
  23.         if (slisten[count] == INVALID_SOCKET) {  
  24.             // socket failed  
  25.         }  
  26.         rc = bind(slisten[count], ptr->ai_addr, ptr->ai_addrlen);  
  27.         if (rc == SOCKET_ERROR) {  
  28.             // bind failed  
  29.         }  
  30.         rc = listen(slisten[count], 7);  
  31.         if (rc == SOCKET_ERROR) {  
  32.             // listen failed  
  33.         }  
  34.         count++;  
  35.         ptr = ptr->ai_next;  
  36. }  
Once the sockets are created and bound, the application simply needs to wait for incoming connections on each. Chapter 5 covers the various I/O models available in Winsock and provides fully functioning client and server samples that are written using the principles covered in this section.
This message was edited 4 times. Last update was at 16/04/2011 15:55:25

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!