Windows Network Programming Chapter 3 Internet Protocol Reading Notes

Source: Internet
Author: User
1. This chapter describes IP addresses and explains IPv4 and IPv6. The following two chapters describe address and name resolution and how to write an IPv4 and IPv6 adaptive program.

2. brief excerpt from the IPv4 section:

(1) IP addresses that can be used as private addresses include:

10.0.0.0? 10.255.255.255 (10.0.0.0/8)
172.16.0.0? 172.31.255.255 (172.16.0.0/12)
192.168.0.0? 192.168.255.255 (192.168.0.0/16)

When writing IP segments, there are often methods such as/16 and/24. This indicates the mask./16 indicates that each of the first 16 bits is 1, that is, 255.255.0.0.

(2) If you want to obtain the NIC and IP Address Configuration of the local machine in the program, you must use the wsaioctl function and use the sio_address_list_query command. Chapter 7 and chapter 16th are introduced.

(3) There are two types of IP addresses: DHCP and manual configuration. If DHCP is configured but the DHCP server cannot reach the IP address, the system assigns a 169.254.0.0/16 address to the NIC. This is based on the apipa protocol (automatic private IP address)

(4) The IPv4 management protocols. IPv4 protocol also requires support from many other protocols. The three most common protocols are ARP, ICMP, and IGMP. IGMP, which are unfamiliar. IGMP (Internet Group Management Protocol) is used for multicast. When an application on a machine wants to join a multicast group, it sends an IGMP Membership Reports message, which notifies the router so that the router records the request, when multicast information is sent out in the future, the vro forwards multicast information to each member of the multicast group. Chapter 9 will discuss multicast in detail.

3. IPv6. This section does not show.

4. address and name resolution. This section describes two functions: getaddrinfo and getnameinfo. The getaddrinfo function is mainly used to convert the given IP address, host name, and port into a sockaddr structure, that is, the binary addressing that is often mentioned in this book. Getnameinfo is the opposite of getaddrinfo. getnameinfo is a sockaddr instance and generates IP Address/host name and port information.

Here we need to explain why these two functions are used, because we have seen in chapter 1 that we can manually apply a sockaddr_in structure, enter the IP address, host name, and port information in it, and then send the information to connect, sendto, and bind functions that require addressing. There are several reasons:

(1) using these two functions can adapt to IPv4 and IPv6. Previously, sockaddr_in was intended for IPv4. To support IPv6, you need to write additional code. For example, when you enter the host name and port connected to the program using these two functions, we do not know whether the IP address corresponding to the host name entered by the user is IPv4 or IPv6, the address of V4 and V6 on this host is still available. In the past, our code had to adapt itself to this situation. With these two functions, the code can be adaptive.

(2) using these two functions does not care about the host order or network order. That is to say, traditional functions such as inet_addr and gethostbyname do not need to be written.

(3) With these two functions, the code is actually better understood. We only need to pass in the IP address/host name and port information, and then use getaddrinfo to get the structure of addrinfo by setting different hint. The content in this structure can be used to create a socket, call bind, connect, sendto, etc.

Therefore, we should try to use these two functions to perform addressing operations. Previously, Code such as inet_addr, gethostbyname, and gethostbyaddr should be overwritten, these functions are retained in WinSock to be compatible with old code.

5. OK. Now let's look at these two functions. First, declare that the two functions are defined in ws2tcpip. h, but this is only true in WINXP. to use these two functions in other Windows systems that support WinSock 2, you also need to include ws2tcpip. before H, include wspiapi. H (mainly include in ws2tcpip. h ).

Code: select all
int getaddrinfo(
      const char FAR *nodename,
      const char FAR *servname,
      const struct addrinfo FAR *hints,
      struct addrinfo FAR *FAR *res
);

Nodename -- Host Name or IP address
Servname -- service name, in fact, is to specify the port, or fill in a string such as ftp. In a system such as Windows NT, there is a service file in the % WINDOWS %/system32/Drivers/etc directory, which contains the correspondence between the port and service.
Hints -- a pointer to the addrinfo structure.
Res -- returned result data. However, this data may be an array. Depending on the content entered in hints, the function may return multiple sockaddr data.

If getaddrinfo is successfully executed, 0 is returned. If no value is returned, an error code is returned. (wsagetlasterror is not required)

Therefore, the key is the hints filling method. The addrinfo structure is as follows:

Code: select all
struct addrinfo {
      int      ai_flags;
      int      ai_family;
      int      ai_socktype;
      int      ai_protocol;
      size_t      ai_addrlen;
      char      *ai_canonname;
      struct sockaddr *ai_addr;
      struct addrinfo *ai_next;
};

Ai_flags -- only one of the following three values can be taken: ai_passive, ai_canonname, ai_numerichost. ai_canonname indicates that in the getaddrinfo function, the nodename field is the host name, for example, www.microsoft.com; ai_numerichost indicates that in the getaddrinfo function, nodename is filled with an IP address. ai_passive will be followed later, mainly used for bind

Ai_family -- af_inet, af_inet6, af_unspec. If af_unspec is entered, getaddrinfo may return an IPv4 sockaddr or IPv6 sockaddr, or both (So RES is an array structure). The key is whether the host supports IPv6.

Ai_socktype -- enter the socket type, such as sock_dgram and sock_stream. When the servname field in the getaddrinfo function is a service name rather than a port number, different ports are returned based on this field, because we know that some services can use TCP or UDP, their ports are different.

Ai_protocol -- specify protocol, such as ipproto_tcp. It works when the servname entered in getaddrinfo is the name of a service.

Ai_next -- when multiple addressing information is returned, This is a pointer to the next addrinfo. Note that res returned by the getaddrinfo function is also a pointer to the addrinfo structure array.

Ai_addr -- Return sockaddr Information

If the hint is not set when getaddrinfo is called, getaddrinfo considers it to be an empty hint structure and the ai_family parameter is set to af_unspec.

The following code is used as an example:

Code: select all
SOCKET            s;
struct addrinfo      hints,
            *result;
int            rc;

memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_CANONNAME;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
rc = getaddrinfo("foobar", "5001", &hints, &result);
if (rc != 0) {
      // unable to resolve the name
}
s = socket(result->ai_family, result->ai_socktype,
result->ai_protocol);
if (s == INVALID_SOCKET) {
      // socket API failed
}
rc = connect(s, result->ai_addr, result->ai_addrlen);
if (rc == SOCKET_ERROR) {
      // connect API failed
}
freeaddrinfo(result);

Note the following points in the above Code:

(1) The above code tries to connect to port 5001 of the foobar machine. Here we can see that we don't care about whether the foobar machine is IPv4 or IPv6. It depends entirely on the machine on which the foobar name is resolved in the network. If we only want to connect to the IPv4 foobar machine, we can set the ai_family to af_inet when setting the hint structure.

(2) Pay attention to the fact that the result returned by getaddrinfo is dynamically allocated. Therefore, we must call the freeaddrinfo function to release the result after use.

In the above example, we try to connect to the foobar machine. We can also try to connect an IP address (either IPv4 or IPv6 ):

Code: select all
struct addrinfo      hints,
                 *result;
int            rc;

memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_NUMERICHOST;
hints.ai_family = AI_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
rc = getaddrinfo("172.17.7.1", "5001", &hints, &result);
if (rc != 0) {
      // invalid literal address
}
// Use the result
freeaddrinfo(result);

The example is simple. If the hint is not provided when we call the getaddrinfo function, we can view the flags value in the returned result data (that is, the addrinfo structure, determine whether the returned sockaddr structure is a host name or an IP address.

In flags of addrinfo, there is another one that is not introduced, namely ai_passive. This flag is used to obtain the information required by the BIND function. For IPv4, bind requires inaddr_any (0.0.0.0). For IPv6, bind requires in6addr_any (::). OK, so when we call getaddrinfo, set nodename to null and servname to the port or service name we need to bind. In the hint, set ai_family, whether it is IPv4 or IPv6, or set it to af_unspec. In this case, getaddrinfo returns the information of both versions.

6. getnameinfo:

Code: select all
int getnameinfo(
      const struct sockaddr FAR *sa,
      socklen_t salen,
      char FAR *host,
      DWORD hostlen,
      char FAR *serv,
      DWORD servlen,
      int flags
);

Given the sockaddr data, this function returns the hostname and servname. The parameters are well understood. Sa is the given sockaddr information. Host and serv are hostname and port, and hostname is FQDN. The last flags has the following values:

Ni_nofqdn -- the returned hostname does not contain a domain name
Ni_numerichost -- the returned IP address instead of the Host Name
Ni_namereqd -- If sockaddr cannot parse the hostname of the FQDN, an error is returned.
Ni_numericserv -- return a digital port instead of a service name. Note: if we do not specify this flag, if the port cannot be parsed into a service name, the function will return an error wsano_data
Ni_dgram -- used to differentiate datemediservice and Stream Services

7. Simple address conversion.

Code: select all
INT WSAStringToAddress(
      LPTSTR AddressString,
      INT AddressFamily,
      LPWSAPROTOCOL_INFO lpProtocolInfo,
      LPSOCKADDR lpAddress,
      LPINT lpAddressLength
);

INT WSAAddressToString(
      LPSOCKADDR lpsaAddress,
      DWORD dwAddressLength,
      LPWSAPROTOCOL_INFO lpProtocolInfo,
      LPTSTR lpszAddressString,
      LPDWORD lpdwAddressStringLength
);

These two functions are used to convert IP addresses and addressing information. That is to say, only one IP address + port can be converted into one sockaddr or vice versa. For example, wsastringtoaddress can accept strings like "192.168.0.1: 1200" and convert them to addressing data. Wsastringtoaddress is not as smart as the getaddrinfo function. It must specify whether the IP address is IPv4 or IPv6.

8. The traditional IPv4 address and name processing functions.

Inet_addr -- converts an IPv4 address into a 32-bit long number in the network order
Inet_ntoa -- converts the number of long-type network order into an IPv4 address

Gethostbyname, wsaasyncgethostbyname, gethostbyaddr, and wsaasyncgethostbyaddr. For more information about these functions, see msdn. A function such as wsaasyncgetxxx is very interesting and asynchronous. When calling this function, you need to specify a buffer (this buffer will be filled by the function into what we want ), in addition, an hwnd and MSG should be given, so that when the function is complete, the MSG message will be sent to the specified hwnd window, so that we can process it.

9. Writing IP version-independent programs.

This section is a sample code for the getaddrinfo and getnameinfo functions in the previous section. In the code, there is nothing to mention, because of the use of these two functions, we do not need to care about IPv4, IPv6, and we do not need to manually declare a sockaddr_in, sockaddr_in6 is a structure variable, because the addrinfo structure returned by the getaddrinfo function contains the sockaddr variable, which can be used directly. If we must manually declare variables of the sockaddr type, do not use a sockaddr_in or sockaddr_in6 structure. Instead, use the sockaddr_storage structure, this structure is designed to be compatible with the sockaddr structure of any protocol. With this structure, we can ensure that the program we write is not bound to a specific network protocol. In addition, the address constants used when using functions such as bind are fixed in the header file of WinSock and do not need to be manually hardcode. Here, I wrote an IPv6 program example. I used the simple function wsastringtoaddress in the previous section to demonstrate the IP version-independent program:

Code: select all
SOCKADDR_STORAGE            saDestination;
SOCKET               s;
int               addrlen,
               rc;

s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (s == INVALID_SOCKET) {
      // socket failed
}
addrlen = sizeof(saDestination);
rc = WSAStringToAddress(
         "3ffe:2900:d005:f28d:250:8bff:fea0:92ed",
         AF_INET6,
         NULL,
         (SOCKADDR *)&saDestination,
         &addrlen
         );
if (rc == SOCKET_ERROR) {
      // conversion failed
}
rc = connect(s, (SOCKADDR *)&saDestination, sizeof(saDestination));
if (rc == SOCKET_ERROR) {
      // connect failed
}

The program is not hard to understand. Let's look at the TCP program we have previously written. This time, we use the getaddrinfo function to rewrite the client and server programs into IP version-independent code. First, let's look at the client code:

Code: select all
SOCKET             s;
struct addrinfo hints,
               *res=NULL
char         *szRemoteAddress=NULL,
               *szRemotePort=NULL;
int         rc;

// Parse the command line to obtain the remote server's
// hostname or address along with the port number, which are contained
// in szRemoteAddress and szRemotePort.
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
// first resolve assuming string is a string literal address
rc = getaddrinfo(
         szRemoteAddress,
         szRemotePort,
           &hints,
           &res
         );
if (rc == WSANO_DATA) {
      // Unable to resolve name - bail out
   }
s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (s == INVALID_SOCKET) {
      // socket failed
}
rc = connect(s, res->ai_addr, res->ai_addrlen);
if (rc == SOCKET_ERROR) {
      // connect failed
}
freeaddrinfo(res);

In the above code, we can see that if this is a complete program, we can get the szremoteaddress and szremoteport information from the command line, in addition, we don't need to worry about whether the two items are IPv4 or IPv6. We only need to set the family in the hint to af_unspec and then call getaddrinfo. Very convenient.

In addition, if we need bind before connect or sendto, it is also very simple. As mentioned above, the only thing bind needs is the description of the local address and port. We only need to set the family, socket type, and protocol in the addrinfo structure generated by calling getaddrinfo to a new hint (manual setting is not allowed, you must use the returned value of the last getaddrinfo operation. If you set it manually, it will involve IPv4 and IPv6 family settings. Use the information returned last time, this is the correct family generated according to our szremoteaddress) and hint. set ai_flags to ai_passive, and call getaddrinfo again. Call getaddrinfo this time, set nodename to null, set servname to the desired port, and call getaddrinfo, the addressing information required by BIND is returned.

Server code:

Code: select all
SOCKET            slisten[16];
char            *szPort="5150";
struct addrinfo              hints,
            * res=NULL,
            * ptr=NULL;
int              count=0,
              rc;

memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
rc = getaddrinfo(NULL, szPort, &hints, &res);
if (rc != 0) {
      // failed for some reason
}
ptr = res;
while (ptr)
{
      slisten[count] = socket(ptr->ai_family,
      ptr->ai_socktype, ptr->ai_protocol);
      if (slisten[count] == INVALID_SOCKET) {
         // socket failed
      }
      rc = bind(slisten[count], ptr->ai_addr, ptr->ai_addrlen);
      if (rc == SOCKET_ERROR) {
         // bind failed
      }
      rc = listen(slisten[count], 7);
      if (rc == SOCKET_ERROR) {
         // listen failed
      }
      count++;
      ptr = ptr->ai_next;
}

OK. The above code is very understandable. We can see that the server does not need to connect others, but only needs to bind itself and then listen (TCP ). Therefore, we set hint. ai_flags to ai_passive. Then, because hint. ai_family is set to af_unspec, getaddrinfo returns IPv4 and IPv6 addressing information. In this case, we simply use a loop. In the two addresses returned by getaddrinfo, socket, bind, and listen are created.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.