People don’t like changes. IPv6 could have help to solve a lot of the burden in networks deployed today, which are still mostly based on the original version of the Internet Protocol, aka version 4. But time has come, and even the old tricks like throwing network address translation (NAT) everywhere are not going to help anymore, simply because we are out of IP addresses. It may take some more time, and people will do everything they can to (continue and) delay it, but believe me – there is no other way around – IPv6 is here to replace IPv4. IPv6 is also a critical part of the promise of the cloud and the Internet of Things (IoT). If you want to connect everything to the network, you better plan for massive scale and have enough addresses to use.
One of the trickiest things with IPv6 though is the fact that it’s pretty different from IPv4. While some of the concepts remains the same, there are some fundamental differences between IPv4 and IPv6, and it’s definitely takes some time to get used into some of the IPv6 basics, including the terms being used. Experienced IPv4 engineers will probably need to change their mindset, and as I stated before, people don’t really like changes…
In this post, I want to highlight the address assignment options available with IPv6, which is in my view one of the most fundamental things in IP networking, and where things are pretty different comparing to IPv4. I am going to assume you have some basic background on IPv6, and while I will cover the theory part I will also show the command line interface and demonstrate some of the configuration options, focusing on SLAAC and stateless DHCPv6. I am going to use a simple topology with two Cisco routers directly connected to each other using their GigabitEthernet 1/0 interface. Both routers are running IOS 15.2(4).
Let the party started
With IPv6 an interface can have multiple prefixes and IP addresses, and unlike IPv4, all of them are primary. All interfaces will have a Link-Local address which is the address used to implement many of the control plane functions. If you don’t manually set the Link-Local address, one will automatically be generated for you. Note that the IPv6 protocol stack will not become operational on an interface until a Link-Local address was assigned or generated and it passed Duplicate Address Detection (DAD) verification.In Cisco IOS, we will first need to enable IPv6 on the router which is done globally using the ipv6 unicast-routing command. We will then enable IPv6 on the interface using the ipv6 enable command:ipv6 unicast-routing ! interface GigabitEthernet1/0 ipv6 enable !
Now IPv6 in enabled on the interface, and we should get a Link-Local address assigned automatically:show ipv6 interface g1/0 | include link IPv6 is enabled, link-local address is FE80::C800:51FF:FE2F:1C
IPv6 address assignment options
A little bit of theory as promised. When it comes to IPv6 address assignment there are several options you can use:
- Static (manual) address assignment – exactly like with IPv4, you can go on and apply the address yourself. I believe this is straight forward and therefore I am not going to demonstrate that.
- Stateless Address Auto Configuration (SLAAC) – nodes listen for ICMPv6 Router Advertisements (RA) messages periodically sent out by routers on the local link, or requested by the node using an RA solicitation message. They can then create a Global unicast IPv6 address by combining its interface EUI-64 (based on the MAC address on Ethernet interfaces) plus the Link Prefix obtained via the Router Advertisement. This is a unique feature only to IPv6 which provides simple “plug & play” networking. By default, SLAAC does not provide anything to the client outside of an IPv6 address and a default gateway. SLAAC is greatly discussed in RFC 4862.
- Stateless DHCPv6 – with this option SLAAC is still used to get the IP address, but DHCP is used to obtain “other” configuration options, usually things like DNS, NTP, etc. The advantage here is that the DHCP server is not required to store any dynamic state information about any individual clients. In case of large networks which has huge number of end points attached to it, implementing stateless DHCPv6 will highly reduce the number of DHCPv6 messages that are needed for address state refreshment.
- Stateful DHCPv6 – functions exactly the same as IPv4 DHCP in which hosts receive both their IPv6 address and additional parameters from the DHCP server. Like DHCP for IPv4, the components of a DHCPv6 infrastructure consist of DHCPv6 clients that request configuration, DHCPv6 servers that provide configuration, and DHCPv6 relay agents that convey messages between clients and servers when clients are on subnets that do not have a DHCPv6 server. You can learn more about DHCP for IPv6 in RFC 3315.
NOTE: The only way to get a default gateway in IPv6 is via a RA message. DHCPv6 does not carry default route information at this time.
Putting it all together
An IPv6 host performs stateless address autoconfiguration (SLAAC) by default and uses a configuration protocol such as DHCPv6 based on the following flags in the Router Advertisement message sent by a neighboring router:
- Managed Address Configuration Flag, the ‘M’ flag. When set to 1, this flag instructs the host to use a configuration protocol to obtain stateful IPv6 addresses
- Other Stateful Configuration Flag, the ‘O’ flag. When set to 1, this flag instructs the host to use a configuration protocol to obtain other configuration settings, e.g., DNS, NTP, etc.
Combining the values of the M and O flags can yield the following:
- Both M and O Flags are set to 0. This combination corresponds to a network without a DHCPv6 infrastructure. Hosts use Router Advertisements for non-link-local addresses and other methods (such as manual configuration) to configure other parameters.
Both M and O Flags are set to 1. DHCPv6 is used for both addresses and other configuration settings, aka stateful DHCPv6.
The M Flag is set to 0 and the O Flag is set to 1. DHCPv6 is not used to assign addresses, only to assign other configuration settings. Neighboring routers are configured to advertise non-link-local address prefixes from which IPv6 hosts derive stateless addresses. This combination is known as statless DHCPv6.
Examining the configuration
Client configuration:interface GigabitEthernet1/0 ipv6 address autoconfig ipv6 enable
Server configuration:interface GigabitEthernet1/0 ipv6 address 2001:1111:1111::1/64 ipv6 enable
We can see the server sending the RA message with the prefix that was configured:ICMPv6-ND: Request to send RA for FE80::C801:51FF:FE2F:1C ICMPv6-ND: Setup RA from FE80::C801:51FF:FE2F:1C to FF02::1 on GigabitEthernet1/0 ICMPv6-ND: MTU = 1500 ICMPv6-ND: prefix = 2001:1111:1111::/64 onlink autoconfig ICMPv6-ND: 2592000/604800 (valid/preferred)
And the client receiving the message and calculating an address using EUI-64:ICMPv6-ND: Received RA from FE80::C801:51FF:FE2F:1C on GigabitEthernet1/0 ICMPv6-ND: Prefix : 2001:1111:1111::ICMPv6-ND: Update on-link prefix 2001:1111:1111::/64 on GiabitEthernet1/0 IPV6ADDR: Generating IntfID for 'eui64', prefix 2001:1111:1111::/64 ICMPv6-ND: IPv6 Address Autoconfig 2001:1111:1111:0:C800:51FF:FE2F:1C
R1#show ipv6 interface brief
No changes are required on the client side. The client is configured to use SLAAC by setting the “auto-config” option.interface GigabitEthernet1/0 ipv6 address autoconfig ipv6 enable
Server configuration:ipv6 dhcp pool STATELESS_DHCP dns-server 2001:1111:1111::10 domain-name test.com ! interface GigabitEthernet1/0 ipv6 address 2001:1111:11111::1/64 ipv6 enable ipv6 nd other-config-flag ipv6 dhcp server STATELESS_DHCP
We can see the client keeping the same IP address, but now obtaining DNS settings through DHCP:IPv6 DHCP: Adding server FE80::C801:51FF:FE2F:1C IPv6 DHCP: Processing options IPv6 DHCP: Configuring DNS server 2001:1111:1111::10 IPv6 DHCP: Configuring domain name test.com
< Winsock2 Headers & Libraries | Winsock2 Main | Win32 Program Examples | Client APIs & TCP States >
An Intro to Windows Socket Programming with C
What do we have in this chapter 1 part 2?
Addressing a Protocol
Creating a Socket
Server API Functions
Accepting Connections, accept()
Addressing a Protocol
This chapter is limited to describing how to make fundamental Winsock calls to set up communication using the Internet Protocol (IP) because most Winsock applications developed today use it because it is widely available due to the popularity of the Internet. However, Winsock is a protocol-independent interface. Also, our discussion of IP in this chapter is limited to briefly describing IP version 4 (IPv4).
Throughout the remainder of this chapter, we will demonstrate the basics of how to set up Winsock communication using the IPv4 protocol. IP is widely available on most computer operating systems and can be used on most local area networks (LANs), such as a small network in your office, and on wide area networks (WANs), such as the Internet. By design, IP is a connectionless protocol and doesn't guarantee data delivery. Two higher-level protocols, Transmission Control Protocol (TCP) and User Datagram Protocol (UDP) are used for connection-oriented and connectionless data communication over IP, which we will describe later. Both TCP and UDP use IP for data transmission and are normally referred to as TCP/IP and UDP/IP. To use IPv4 in Winsock, you need understand how to address IPv4.
In IPv4, computers are assigned an address that is represented as a 32-bit quantity. When a client wants to communicate with a server through TCP or UDP, it must specify the server's IP address along with a service port number. Also, when servers want to listen for incoming client requests, they must specify an IP address and a port number. In Winsock, applications specify IP addresses and service port information through the SOCKADDR_IN structure, which is defined as:
struct in_addr sin_addr;
The sin_family field must be set to AF_INET, which tells Winsock we are using the IP address family.
The sin_port field defines which TCP or UDP communication port will be used to identify a server service. Applications should be particularly careful in choosing a port because some of the available port numbers are reserved for well-known services, such as File Transfer Protocol (FTP) and Hypertext Transfer Protocol (HTTP).
The sin_addr field of the SOCKADDR_IN structure is used for storing an IPv4 address as a four-byte quantity, which is an unsigned long integer data type. Depending on how this field is used, it can represent a local or a remote IP address. IP addresses are normally specified in Internet standard dotted notation as "a.b.c.d." Each letter represents a number (in decimal, octal, or hexadecimal format) for each byte and is assigned, from left to right, to the four bytes of the unsigned long integer.
The final field, sin_zero, functions only as padding to make the SOCKADDR_IN structure the same size as the SOCKADDR structure.
A useful support function named inet_addr() converts a dotted IP address to a 32-bit unsigned long integer quantity. The inet_addr() function is defined as:
unsignedlong inet_addr(constchar FAR *cp);
The cp field is a null-terminated character string that accepts an IP address in dotted notation. Note that this function returns an IP address as a 32-bit unsigned long integer in network-byte order.
If no error occurs, inet_addr() returns an unsigned long value containing a suitable binary representation of the Internet address given. On Windows Server 2003 and later if the string in the cp parameter does not contain a legitimate Internet address, for example if a portion of an "a.b.c.d" address exceeds 255, then inet_addr() returns the value INADDR_NONE. This error is also returned if an empty string or NULL is passed in the cp parameter. On Windows XP and earlier if the string in the cp parameter does not contain a legitimate Internet address, for example if a portion of an "a.b.c.d" address exceeds 255, then inet_addr() returns the value INADDR_NONE. If the string in the cp parameter is an empty string or NULL, then inet_addr() returns the value INADDR_ANY.
The inet_addr() function interprets the character string specified by the cp parameter. This string represents a numeric Internet address expressed in the Internet standard ".'' notation. The value returned is a number suitable for use as an Internet address. All Internet addresses are returned in IP's network order (bytes ordered from left to right). If you pass in " " (a space) to the inet_addr() function, inet_addr() returns zero.
On Windows Vista and later, the RtlIpv4StringToAddress() function can be used to convert a string representation of an IPv4 address to a binary IPv4 address represented as an IN_ADDR structure. While the RtlIpv6StringToAddress() function can be used to convert a string representation of an IPv6 address to a binary IPv6 address represented as an IN6_ADDR structure.
Values specified using the ".'' notation (dot notation) takes one of the following forms:
When four parts are specified, each is interpreted as a byte of data and assigned, from left to right, to the 4 bytes of an Internet address. When an Internet address is viewed as a 32-bit integer quantity on the Intel architecture, the bytes referred to above appear as "d.c.b.a''. That is, the bytes on an Intel processor are ordered from right to left.
The parts that make up an address in "." notation can be decimal, octal or hexadecimal as specified in the C language. Numbers that start with "0x" or "0X" imply hexadecimal. Numbers that start with "0" imply octal. All other numbers are interpreted as decimal.
Internet address value
The inet_addr() function supports the decimal, octal, hexadecimal, and mixed notations for the string passed in the cp parameter. The following para explains the notations which are only used by Berkeley software, and nowhere else on the Internet.
For compatibility with Berkeley software, the inet_addr() function also supports the additional notations specified in the following paragraph. When a three-part address is specified, the last part is interpreted as a 16-bit quantity and placed in the right-most 2 bytes of the network address. This makes the three-part address format convenient for specifying Class B network addresses as "128.net.host''. When a two-part address is specified, the last part is interpreted as a 24-bit quantity and placed in the right-most 3 bytes of the network address. This makes the two-part address format convenient for specifying Class A network addresses as "net.host''. When only one part is given, the value is stored directly in the network address without any byte rearrangement.
The inet_ntoa() function converts an (Ipv4) Internet network address into a string in Internet standard dotted-decimal format. The syntax is:
char* FAR inet_ntoa(__in struct in_addr in);
The in is an in_addr() structure that represents an Internet host address. If no error occurs, inet_ntoa() returns a character pointer to a static buffer containing the text address in standard ".'' notation. Otherwise, it returns NULL.
The inet_ntoa() function takes an Internet address structure specified by the in parameter and returns a NULL-terminated ASCII string that represents the address in "." (dot) notation as in "192.168.16.0", an example of an IPv4 address in dotted-decimal notation. The string returned by inet_ntoa() resides in memory that is allocated by Windows Sockets. The application should not make any assumptions about the way in which the memory is allocated. The string returned is guaranteed to be valid only until the next Windows Sockets function call is made within the same thread. Therefore, the data should be copied before another Windows Sockets call is made.
On Windows Vista and later, the RtlIpv4AddressToString() function can be used to convert an IPv4 address represented as an IN_ADDR structure to a string representation of an IPv4 address in Internet standard dotted-decimal notation. While, the RtlIpv6AddressToString() function can be used to convert an IPv6 address represented as an IN6_ADDR structure to a string representation of an IPv6 address.
The InetNtop() function converts an IPv4 or IPv6 Internet network address into a string in Internet standard format. The ANSI version of this function is inet_ntop(). The syntax is:
PCTSTR WSAAPI InetNtop(
__in INT Family,
__in PVOID pAddr,
__out PTSTR pStringBuf,
__in size_t StringBufSize
The Family is an address family. The possible values for the address family are defined in the Ws2def.h header file. Note that the Ws2def.h header file is automatically included in Winsock2.h, and should never be used directly. Note that the values for the AF_ address family and PF_ protocol family constants are identical (for example, AF_INET and PF_INET), so either constant can be used.
The values currently supported are AF_INET and AF_INET6.
The Internet Protocol version 4 (IPv4) address family. When this parameter is specified, this function returns an IPv4 address string.
The Internet Protocol version 6 (IPv6) address family. When this parameter is specified, this function returns an IPv6 address string.
The pAddr is a pointer to the IP address in network byte to convert to a string. When the Family parameter is AF_INET, then the pAddr parameter must point to an IN_ADDR structure with the IPv4 address to convert. When the Family parameter is AF_INET6, then the pAddr parameter must point to an IN6_ADDR structure with the IPv6 address to convert.
The pStringBuf is a pointer to a buffer in which to store the NULL-terminated string representation of the IP address. For an IPv4 address, this buffer should be large enough to hold at least 16 characters. For an IPv6 address, this buffer should be large enough to hold at least 46 characters.
The final parameter, StringBufSize which is if on input, the length, in characters, of the buffer pointed to by the pStringBuf parameter. On the output, this parameter contains the number of characters actually written to the buffer pointed to by the pStringBuf parameter.
If no error occurs, InetNtop() function returns a pointer to a buffer containing the string representation of IP address in standard format. Otherwise, a value of NULL is returned, and a specific error code can be retrieved by calling the WSAGetLastError() for extended error information. If the function fails, the extended error code returned by WSAGetLastError() can be one of the following values.
The address family specified in the Family parameter is not supported. This error is returned if the Family parameter specified was not AF_INET or AF_INET6.
An invalid parameter was passed to the function. This error is returned if a NULL pointer is passed in the pStringBuf or the StringBufSize parameter is zero. This error is also returned if the length of the buffer pointed to by the pStringBuf parameter is not large enough to receive the string representation of the IP address.
The InetNtop() function is supported on Windows Vista and later. The InetNtop() function provides a protocol-independent address-to-string translation. The InetNtop() function takes an Internet address structure specified by the pAddr parameter and returns a NULL-terminated string that represents the IP address. While the inet_ntoa function works only with IPv4 addresses, the InetNtop() function works with either IPv4 or IPv6 addresses.
The ANSI version of this function is inet_ntop as defined in RFC 2553. The InetNtop() function does not require that the Windows Sockets DLL be loaded to perform IP address to string conversion. If the Family parameter specified is AF_INET, then the pAddr parameter must point to an IN_ADDR structure with the IPv4 address to convert. The address string returned in the buffer pointed to by the pStringBuf parameter is in dotted-decimal notation as in "192.168.16.0", an example of an IPv4 address in dotted-decimal notation.
If the Family parameter specified is AF_INET6, then the pAddr parameter must point to an IN6_ADDR structure with the IPv6 address to convert. The address string returned in the buffer pointed to by the pStringBuf parameter is in Internet standard format. The basic string representation consists of 8 hexadecimal numbers separated by colons. A string of consecutive zero numbers is replaced with a double-colon. There can only be one double-colon in the string representation of the IPv6 address. The last 32 bits are represented in IPv4-style dotted-octet notation if the address is an IPv4-compatible address.
If the length of the buffer pointed to by the pStringBuf parameter is not large enough to receive the string representation of the IP address, InetNtop returns ERROR_INVALID_PARAMETER.
When UNICODE or _UNICODE is defined, InetNtop() is defined to InetNtopW(), the Unicode version of this function. The pStringBuf parameter is defined to the PSTR data type. When UNICODE or _UNICODE is not defined, InetNtop() is defined to InetNtopA(), the ANSI version of this function. The ANSI version of this function is always defined as inet_ntop(). The pStringBuf parameter is defined to the PWSTR data type. The IN_ADDR structure is defined in the Inaddr.h header file. The IN6_ADDR structure is defined in the In6addr.h header file.
On Windows Vista and later, the RtlIpv4AddressToString() and RtlIpv4AddressToStringEx() functions can be used to convert an IPv4 address represented as an IN_ADDR structure to a string representation of an IPv4 address in Internet standard dotted-decimal notation. While, the RtlIpv6AddressToString() and RtlIpv6AddressToStringEx() functions can be used to convert an IPv6 address represented as an IN6_ADDR structure to a string representation of an IPv6 address. The RtlIpv6AddressToStringEx() function is more flexible since it also converts an IPv6 address, scope ID, and port to an IPv6 string in standard format.
The InetPton() function converts an IPv4 or IPv6 Internet network address in its standard text presentation form into its numeric binary form. The ANSI version of this function is inet_pton(). The syntax is:
PCTSTR WSAAPI inet_pton(
__in INT Family,
__in PCTSTR pszAddrString,
__out PVOID pAddrBuf
The Family is the address family. Possible values for the address family are defined in the Ws2def.h header file. Take note that the Ws2def.h header file is automatically included in Winsock2.h, and should never be used directly. Note that the values for the AF_ address family and PF_ protocol family constants are identical (for example, AF_INET and PF_INET), so either constant can be used. The values currently supported are AF_INET and AF_INET6.
The Internet Protocol version 4 (IPv4) address family. When this parameter is specified, the pszAddrString parameter must point to a text representation of an IPv4 address and the pAddrBuf parameter returns a pointer to an IN_ADDR structure that represents the IPv4 address.
The Internet Protocol version 6 (IPv6) address family. When this parameter is specified, the pszAddrString parameter must point to a text representation of an IPv6 address and the pAddrBuf parameter returns a pointer to an IN6_ADDR structure that represents the IPv6 address.
The pszAddrString is a pointer to the NULL-terminated string that contains the text representation of the IP address to convert to numeric binary form. When the Family parameter is AF_INET, then the pszAddrString parameter must point to a text representation of an IPv6 address in standard notation. When the Family parameter is AF_INET6, then the pszAddrString parameter must point to a text representation of an IPv4 address in standard dotted-decimal notation.
The pAddrBuf is a pointer to a buffer in which to store the numeric binary representation of the IP address. The IP address is returned in network byte order. When the Family parameter is AF_INET, this buffer should be large enough to hold an IN_ADDR structure. When the Family parameter is AF_INET6, this buffer should be large enough to hold an IN6_ADDR structure.
If no error occurs, the InetPton() function returns a value of 1 and the buffer pointed to by the pAddrBuf parameter contains the binary numeric IP address in network byte order. The InetPton() function returns a value of 0 if the pAddrBuf parameter points to a string that is not a valid IPv4 dotted-decimal string or a valid IPv6 address string. Otherwise, a value of -1 is returned, and a specific error code can be retrieved by calling the WSAGetLastError() for extended error information. If the function has an error, the extended error code returned by WSAGetLastError() can be one of the following values.
The address family specified in the Family parameter is not supported. This error is returned if the Family parameter specified was not AF_INET or AF_INET6.
The pszAddrString or pAddrBuf parameters are NULL or are not part of the user address space.
The InetPton() function is supported on Windows Vista and later. The InetPton() function provides a protocol-independent conversion of an Internet network address in its standard text presentation form into its numeric binary form. The InetPton() function takes a text representation of an Internet address pointed to by the pszAddrString parameter and returns a pointer to the numeric binary IP address in the pAddrBuf parameter. While the inet_addr function works only with IPv4 address strings, the InetPton function works with either IPv4 or IPv6 address strings.
The ANSI version of this function is inet_pton() as defined in RFC 2553. The InetPton() function does not require that the Windows Sockets DLL be loaded to perform conversion of a text string that represents an IP address to a numeric binary IP address.
If the Family parameter specified is AF_INET, then the pszAddrString parameter must point a text string of an IPv4 address in dotted-decimal notation as in "192.168.16.0", an example of an IPv4 address in dotted-decimal notation. If the Family parameter specified is AF_INET6, then the pszAddrString parameter must point a text string of an IPv6 address in Internet standard format. The basic string representation consists of 8 hexadecimal numbers separated by colons. A string of consecutive zero numbers may be replaced with a double-colon. There can only be one double-colon in the string representation of the IPv6 address. The last 32 bits may be represented in IPv4-style dotted-octet notation if the address is an IPv4-compatible address.
When UNICODE or _UNICODE is defined, InetPton() is defined to InetPtonW(), the Unicode version of this function. The pszAddrString parameter is defined to the PCWSTR data type.
When UNICODE or _UNICODE is not defined, InetPton() is defined to InetPtonA(), the ANSI version of this function. The ANSI version of this function is always defined as inet_pton. The pszAddrString parameter is defined to the PCSTR data type.
The IN_ADDR structure is defined in the Inaddr.h header file. The IN6_ADDR structure is defined in the In6addr.h header file. On Windows Vista and later, the RtlIpv4StringToAddress() and RtlIpv4StringToAddressEx() functions can be used to convert a text representation of an IPv4 address in Internet standard dotted-decimal notation to a numeric binary address represented as an IN_ADDR structure. While, the RtlIpv6StringToAddress() and RtlIpv6StringToAddressEx() functions can be used to convert a string representation of an IPv6 address to a numeric binary IPv6 address represented as an IN6_ADDR structure. The RtlIpv6StringToAddressEx() function is more flexible since it also converts a string representation of an IPv6 address that can include a scope ID and port in standard notation to a numeric binary form.
Other functions that are supported on Windows Vista and later include RtlIpv4AddressToString(), RtlIpv4StringToAddress(), RtlIpv4StringToAddressEx(), RtlIpv6AddressToString(), RtlIpv6AddressToStringEx(), RtlIpv6StringToAddress(), RtlIpv6StringToAddressEx().
The header file need to be included is Mstcpip.h and the library is Ntdll.dll. As the Windows Win32 legacy convention used by Microsoft, the RtlIpv4AddressToStringExW() is for Unicode) and RtlIpv4AddressToStringEx() is for ANSI.
Different computer processors represent numbers in big-endian and little-endian form, depending on how they are designed. For example, on Intel x86 processors, multibyte numbers are represented in little-endian form: the bytes are ordered from least significant to most significant. When an IP address and port number are specified as multibyte quantities in a computer, they are represented in host-byte order. However, when IP addresses and port numbers are specified over a network, Internet networking standards specify that multibyte values must be represented in big-endian form (most significant byte to least significant), normally referred to as network-byte order.
A series of functions can be used to convert a multibyte number from host-byte order to network-byte order and vice versa. The following four API functions convert a number from host-byte to network-byte order:
Host to Network-byte converter API Functions
u_long htonl(u_long hostlong);
u_long FAR * lpnetlong);
u_short htons(u_short hostshort);
u_short FAR * lpnetshort);
The hostlong parameter of htonl() and WSAHtonl() is a four-byte number in host-byte order. The htonl() function returns the number in network-byte order, whereas the WSAHtonl() function returns the number in network-byte order through the lpnetlong parameter. The hostshort parameter of htons() and WSAHtons() is a two-byte number in host-byte order. The htons() function returns the number as a two-byte value in network-byte order, whereas the WSAHtons() function returns the number through the lpnetshort parameter.
The next four functions are the opposite of the preceding four functions; they convert network-byte order to host-byte order.
Network to Host-byte Converter API Functions
u_long ntohl(u_long netlong);
u_long FAR * lphostlong);
u_short ntohs(u_short netshort);
u_short FAR * lphostshort);
We will now demonstrate how to address IPv4 by creating a SOCKADDR_IN structure using the inet_addr() and htons() functions described previously.
INT nPortId = 5150;
InternetAddr.sin_family = AF_INET;
// Convert the proposed dotted Internet address 188.8.131.52
// to a four-byte integer, and assign it to sin_addr
InternetAddr.sin_addr.s_addr = inet_addr("184.108.40.206");
// The nPortId variable is stored in host-byte order. Convert
// nPortId to network-byte order, and assign it to sin_port.
InternetAddr.sin_port = htons(nPortId);
As you can probably tell, IP addresses aren't easy to remember. Most people would much rather refer to a machine (or host) by using an easy-to-remember, user-friendly host name instead of an IP address. Hopefully, in other chapters we will describe useful address and name resolution functions that can help you resolve a host name, such as www.bodo.com, to an IP address and a service name, such as FTP, to a port number using functions such as getaddrinfo(), getnameinfo(), gethostbyaddr(), gethostbyname(), gethostname(), getprotobyname(), getprotobynumber(), get-servbyname(), and getservbyport(). There are also some asynchronous (non-blocking) versions of some of these functions:
Keep in mind that some of the functions may already deprecated. Now that you have the basics of addressing a protocol such as IPv4, you can prepare to set up communication by creating a socket.
Creating a Socket
If you're familiar with Winsock, you know that the API is based on the concept of a socket. A socket is a handle to a transport provider. In Windows, a socket is not the same thing as a file descriptor and therefore is a separate type: SOCKET in WINSOCK2.H. There are two functions that can be used to create a socket: socket and WSASocket. In the simplest form, we will briefly describe socket as:
SOCKET socket (
The first parameter, af, is the protocol's address family. Since we describe Winsock in this chapter using only the IPv4 protocol, you should set this field to AF_INET.
The second parameter, type, is the protocol's socket type. When you are creating a socket to use TCP/IP, set this field to SOCK_STREAM, for UDP/IP use SOCK_DGRAM.
The third parameter is protocol and is used to qualify a specific transport if there are multiple entries for the given address family and socket type. For TCP you should set this field to IPPROTO_TCP; for UDP use IPPROTO_UDP. Chapter 2 describes socket creation in greater detail for all protocols, including the WSASocket API.
Winsock features four useful functions to control various socket options and socket behaviors: setsockopt(), getsockopt(), ioctlsocket(), and WSAIoctl(). For simple Winsock programming, you will not need to use them specifically. Once you have successfully created a socket, you are ready to set up communication on the socket to prepare it for sending and receiving data. In Winsock there are two basic communication techniques: connection-oriented and connectionless communication.
In this section, we'll cover the Winsock functions necessary for both receiving connections and establishing connections. We'll first discuss how to develop a server by listening for client connections and explore the process for accepting or rejecting a connection. Then we'll describe how to develop a client by initiating a connection to a server. Finally, we'll discuss how data is transferred in a connection-oriented session.
In IP, connection-oriented communication is accomplished through the TCP/IP protocol. TCP provides reliable error-free data transmission between two computers. When applications communicate using TCP, a virtual connection is established between the source computer and the destination computer. Once a connection is established, data can be exchanged between the computers as a two-way stream of bytes.
Server API Functions
A server is a process that waits for any number of client connections with the purpose of servicing their requests. A server must listen for connections on a well-known name. In TCP/IP, this name is the IP address of the local interface and a port number. Every protocol has a different addressing scheme and therefore a different naming method. The first step in Winsock is to create a socket with either the socket() or WSASocket() call and bind the socket of the given protocol to its well-known name, which is accomplished with the bind() API call. The next step is to put the socket into listening mode, which is performed (appropriately enough) with the listen() API function. Finally, when a client attempts a connection, the server must accept the connection with either the accept() or WSAAccept() call. In the next few sections, we will discuss each API call that is required for binding, listening, and accepting a client connection.
Once the socket of a particular protocol is created, you must bind it to a well-known address. The bind() function associates the given socket with a well-known address. This function is declared as:
conststruct sockaddr FAR* name,
The first parameter, s, is the socket on which you want to wait for client connections.
The second parameter is of type struct sockaddr, which is simply a generic buffer. You must actually fill out an address buffer specific to the protocol you are using and cast that as a struct sockaddr when calling bind(). The Winsock header file defines the type SOCKADDR as struct sockaddr. We'll use this type throughout the chapter for brevity.
The third parameter is simply the size of the protocol-specific address structure being passed. For example, the following code illustrates how this is done on a TCP connection:
int port = 5150;
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
tcpaddr.sin_family = AF_INET;
tcpaddr.sin_port = htons(port);
tcpaddr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(s, (SOCKADDR *)&tcpaddr, sizeof(tcpaddr));
From the example, you'll see a stream socket being created, followed by setting up the TCP/IP address structure on which client connections will be accepted. In this case, the socket is being bound to the default IP interface by using a special address, INADDR_ANY, and occupies port number 5150. We could have specified an explicit IP address available on the system, but INADDR_ANY allows us to bind to all available interfaces on the system so that any incoming client connection on any interface (but the correct port) will be accepted by our listening socket. The call to bind() formally establishes this association of the socket with the local IP interface and port.
On error, bind() returns SOCKET_ERROR. The most common error encountered with bind is WSAEADDRINUSE. With TCP/IP, the WSAEADDRINUSE error indicates that another process is already bound to the local IP interface and port number or that the IP interface and port number are in the TIME_WAIT state. If you call bind again on a socket that is already bound, WSAEFAULT will be returned.
The next piece of the equation is to put the socket into listening mode. The bind() function merely associates the socket with a given address. The API function that tells a socket to wait for incoming connections is listen(), which is defined as:
int listen(SOCKET s, int backlog);
Again, the first parameter is a bound socket.
The backlog parameter specifies the maximum queue length for pending connections. This is important when several simultaneous requests are made to the server. For example, let's say the backlog parameter is set to two. If three client requests are made at the same time, the first two will be placed in a “pending” queue so that the application can service their requests. The third connection request will fail with WSAECONNREFUSED. Note that once the server accepts a connection, the request is removed from the queue so that others can make a request. The backlog parameter is silently limited to a value that the underlying protocol provider determines. Illegal values are replaced with their nearest legal values. In addition, there is no standard provision for finding the actual backlog value.
The errors associated with listen() are fairly straightforward. By far the most common is WSAEINVAL, which usually indicates that you forgot to call bind() before listen(). Otherwise, it is possible to receive the WSAEADDRINUSE error on the listen() call as opposed to the bind() call. This error occurs most often on the bind() call.
Accepting Connections, accept()
Now you're ready to accept client connections. This is accomplished with the accept(), WSAAccept(), or AcceptEx() function. (AcceptEx(), an extended version of accept similar to other Win32 Ex versions) The prototype for accept is:
struct sockaddr FAR* addr,
int FAR* addrlen
The parameter s is the bound socket that is in a listening state.
The second parameter should be the address of a valid SOCKADDR_IN structure.
While addrlen should be a reference to the length of the SOCKADDR_IN structure. For a socket of another protocol, substitute the SOCKADDR_IN with the SOCKADDR structure corresponding to that protocol. A call to accept() services the first connection request in the queue of pending connections. When the accept() function returns, the addr structure contains the IPv4 address information of the client making the connection request, and the addrlen parameter indicates the size of the structure. In addition, accept() returns a new socket descriptor that corresponds to the accepted client connection. For all subsequent operations with this client, the new socket should be used. The original listening socket is still open to accept other client connections and is still in listening mode.
If an error occurs, INVALID_SOCKET is returned. The most common error encountered is WSAEWOULDBLOCK if the listening socket is in asynchronous or non-blocking mode and there is no connection to be accepted. Block, non-blocking, and other socket modes are covered in another chapter. Winsock 2 introduced the function WSAAccept(), which has the capability to conditionally accept a connection based on the return value of a condition function.
At this point, we have described all the necessary elements to construct a simple Winsock TCP/IP server application. The following program example demonstrates how to write a simple server that can accept one TCP/IP connection. We did not perform full error checking on the calls to make reading the code less confusing.
1. Click File menu > Project sub menu to create a new project. Select Win32 for the Project types: and Win32 Console Application for the Templates:. Put the project and solution name. Adjust the project location if needed and click OK.
2. Click Next for the Win32 Application Wizard Overview page. We will remove all the unnecessary project items. In the Application page, select Empty project for the Additional options:. Leave others as given and click Finish.
3. Next, we need to add new source file. Click Project menu > Add New Item sub menu or select the project folder in the Solution Explorer > Select Add menu > Select New Item sub menu.
4. Select C++ File (.cpp) for the Templates:. Put the source file name and click Add. Although the extension is .cpp, Visual C++ IDE will recognize that the source code used is C based on the Compile as C Code (/TC) option which will be set in the project property page later.
5. Now, add the source code as given below.
int Port = 5150;
// Initialize Winsock version 2.2
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
// MSDN: An application can call the WSAGetLastError() function to determine the
// extended error code for other Windows sockets functions as is normally
// done in Windows Sockets even if the WSAStartup function fails or the WSAStartup
// function was not called to properly initialize Windows Sockets before calling a
// Windows Sockets function. The WSAGetLastError() function is one of the only functions
// in the Winsock 2.2 DLL that can be called in the case of a WSAStartup failure
printf("Server: WSAStartup failed with error %ld\n", WSAGetLastError());
// Exit with error