< Winsock2 Headers & Libraries | Winsock2 Main | Win32 Program Examples | Client APIs & TCP States >


 

 

An Intro to Windows Socket Programming with C

Part 2

 

 

What do we have in this chapter 1 part 2?

  1. Addressing a Protocol

  2. Addressing IPv4

  3. Internet Addresses

  4. Some Note

  5. inet_ntoa() Function

  6. InetNtop() Function

  7. InetPton() Function

  8. Byte Ordering

  9. Creating a Socket

  10. Connection-Oriented Communication

  11. Server API Functions

  12. Binding, bind()

  13. Listening, listen()

  14. 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.

 

Addressing 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 sockaddr_in

{

    short           sin_family;

    u_short         sin_port;

    struct in_addr  sin_addr;

    char            sin_zero[8];

};

 

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:

 

unsigned long inet_addr(const char 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.

 

Internet Addresses

 

Values specified using the ".'' notation (dot notation) takes one of the following forms:

 

  1. a.b.c.d

  2. a.b.c

  3. a.b

  4. a

 

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

Meaning

"4.3.2.16"

Decimal

"004.003.002.020"

Octal

"0x4.0x3.0x2.0x10"

Hexadecimal

"4.003.002.0x10"

Mix

 

Some Note

 

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.

 

inet_ntoa() Function

 

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.

 

InetNtop() Function

 

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.

 

Value

Meaning

AF_INET (2)

The Internet Protocol version 4 (IPv4) address family. When this parameter is specified, this function returns an IPv4 address string.

AF_INET6 (23)

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.

 

Error code

Meaning

WSAEAFNOSUPPORT

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.

ERROR_INVALID_PARAMETER

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.

 

InetPton() Function

 

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.

 

Value

Meaning

AF_INET (2)

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.

AF_INET6 (23)

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.

 

Error code

Meaning

WSAEAFNOSUPPORT

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.

WSAEFAULT

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.

 

Byte Ordering

 

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

1

u_long htonl(u_long hostlong);

2

int WSAHtonl(

    SOCKET s,

    u_long hostlong,

    u_long FAR * lpnetlong);

3

u_short htons(u_short hostshort);

4

int WSAHtons(

    SOCKET s,

    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

1

u_long ntohl(u_long netlong);

2

int WSANtohl(

    SOCKET s,

    u_long netlong,

    u_long FAR * lphostlong);

3

u_short ntohs(u_short netshort);

4

int WSANtohs(

    SOCKET s,

    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.

 

SOCKADDR_IN InternetAddr;

INT nPortId = 5150;

 

InternetAddr.sin_family = AF_INET;

 

// Convert the proposed dotted Internet address 136.149.3.29

// to a four-byte integer, and assign it to sin_addr

InternetAddr.sin_addr.s_addr = inet_addr("136.149.3.29");

 

// 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:

 

  1. WSAAsyncGetHostByAddr(),

  2. WSAAsyncGetHostByName(),

  3. WSAAsyncGetProtoByName(),

  4. WSAAsyncGetProtoByNumber(),

  5. WSAAsyncGetServByName(), and

  6. WSAAsyncGetServByPort().

 

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 (

    int af,

    int type,

    int protocol

);

 

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.

 

Connection-Oriented 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.

 

Binding, bind()

 

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:

 

int bind(

    SOCKET    s,

    const struct sockaddr FAR* name,

    int    namelen

);

 

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:

 

SOCKET    s;

SOCKADDR_IN    tcpaddr;

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.

 

Listening, listen()

 

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:

 

SOCKET accept(

    SOCKET s,

    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.

 

Windows socket and C programming: creating a new Win32 console application project

 

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.

 

Windows socket and C programming: selecting an empty console application

 

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.

 

----------------------------------------------------------------------

Windows socket and C programming: adding C++ source file to the existing Visual C++ project

 

5.      Now, add the source code as given below.

 

#include <winsock2.h>

#include <stdio.h>

 

int main(void)

{

     WSADATA            wsaData;

     SOCKET             ListeningSocket;

     SOCKET             NewConnection;

     SOCKADDR_IN        ServerAddr;

     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

          return -1;

     }

     else

     {

          printf("Server: The Winsock dll found!\n");

          printf("Server: The current status is %s.\n", wsaData.szSystemStatus);

     }

    

     if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2 )

     {

          //Tell the user that we could not find a usable WinSock DLL

          printf("Server: The dll do not support the Winsock version %u.%u!\n",

                    LOBYTE(wsaData.wVersion),HIBYTE(wsaData.wVersion));

          // Do the clean up

          WSACleanup();

          // and exit with error

          return -1;

     }

     else

     {

          printf("Server: The dll supports the Winsock version %u.%u!\n", LOBYTE(wsaData.wVersion),

                    HIBYTE(wsaData.wVersion));

          printf("Server: The highest version this dll can support is %u.%u\n",

                    LOBYTE(wsaData.wHighVersion), HIBYTE(wsaData.wHighVersion));

     }

    

     // Create a new socket to listen for client connections.

     ListeningSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    

     // Check for errors to ensure that the socket is a valid socket.

     if (ListeningSocket == INVALID_SOCKET)

     {

          printf("Server: Error at socket(), error code: %ld\n", WSAGetLastError());

          // Clean up

          WSACleanup();

          // and exit with error

          return -1;

     }

     else

          printf("Server: socket() is OK!\n");

 

     // Set up a SOCKADDR_IN structure that will tell bind that we

     // want to listen for connections on all interfaces using port 5150.

 

     // The IPv4 family

     ServerAddr.sin_family = AF_INET;

     // host-to-network byte order

     ServerAddr.sin_port = htons(Port);

     // Listen on all interface, host-to-network byte order

     ServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);

 

     // Associate the address information with the socket using bind.

     // Call the bind function, passing the created socket and the sockaddr_in

     // structure as parameters. Check for general errors.

     if (bind(ListeningSocket, (SOCKADDR *)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR)

     {

          printf("Server: bind() failed! Error code: %ld.\n", WSAGetLastError());

          // Close the socket

          closesocket(ListeningSocket);

          // Do the clean up

          WSACleanup();

          // and exit with error

          return -1;

     }

     else

          printf("Server: bind() is OK!\n");

 

     // Listen for client connections. We used a backlog of 5, which

     // is normal for many applications.

     if (listen(ListeningSocket, 5) == SOCKET_ERROR)

     {

          printf("Server: listen(): Error listening on socket %ld.\n", WSAGetLastError());

          // Close the socket

          closesocket(ListeningSocket);

          // Do the clean up

          WSACleanup();

          // Exit with error

          return -1;

     }

     else

          printf("Server: listen() is OK, I'm waiting for connections...\n");

 

     printf("Server: accept() is ready...\n");

     // Accept a new connection when one arrives.

     while(1)

     {

          NewConnection = SOCKET_ERROR;

          while(NewConnection == SOCKET_ERROR)

          {

              NewConnection = accept(ListeningSocket, NULL, NULL);

          }

 

          printf("Server: accept() is OK...\n");

          printf("Server: Client connected, ready for receiving and sending data...\n");

          // Transfer the connected socket to the listening socket

          ListeningSocket = NewConnection;

          break;

     }

 

     // At this point you can do two things with these sockets. Wait

     // for more connections by calling accept again on ListeningSocket

     // and start sending or receiving data on NewConnection (need a loop). We will

     // describe how to send and receive data later in the chapter.

 

     // When you are finished sending and receiving data on the

     // NewConnection socket and are finished accepting new connections

     // on ListeningSocket, you should close the sockets using the closesocket API.

     if(closesocket(NewConnection) != 0)

          printf("Server: Cannot close \"NewConnection\" socket. Error code: %ld\n", WSAGetLastError());

     else

          printf("Server: Closing \"NewConnection\" socket...\n");

 

     // When your application is finished handling the connections,

     // call WSACleanup.

     if(WSACleanup() != 0)

          printf("Server: WSACleanup() failed! Error code: %ld\n", WSAGetLastError());

     else

          printf("Server: WSACleanup() is OK...\n");

     return 0;

}

 

6.      Before we can build this Winsock C Win32 console application project, we need to set the project to be compiled as C code and link to ws2_32.lib, the Winsock2 library. Invoke the project property page.

7.      Expand the Configuration folder > Expand the C/C++ sub folder. Select the Advanced link and for the Compile As option, select Compile as C Code (/TC).

8.      Next, expand the Linker folder and select the Input link. For the Additional Dependencies option, click the ellipses at the end of the empty field on the right side.

9.      Manually, type the library name, ws2_32.lib and click OK. Or you can just directly type the library name in the empty field on the right of the Additional Dependencies. Click OK.

 

If the previous steps not done, you will see the following error during the program compilation.

 

1>------ Build started: Project: TCPServer, Configuration: Debug Win32 ------

1>Compiling...

1>TCPServersrc.cpp

1>Compiling manifest to resources...

1>Microsoft (R) Windows (R) Resource Compiler Version 6.1.6723.1

1>Copyright (C) Microsoft Corporation.  All rights reserved.

1>Linking...

1>TCPServersrc.obj : error LNK2019: unresolved external symbol __imp__select@20 referenced in function "int __cdecl recvTimeOutTCP(unsigned int,long,long)" (?recvTimeOutTCP@@YAHIJJ@Z)

1>TCPServersrc.obj : error LNK2019: unresolved external symbol __imp__shutdown@8 referenced in function _main

1>TCPServersrc.obj : error LNK2019: unresolved external symbol __imp__getpeername@12 referenced in function _main

1>TCPServersrc.obj : error LNK2019: unresolved external symbol __imp__inet_ntoa@4 referenced in function _main

1>TCPServersrc.obj : error LNK2019: unresolved external symbol __imp__getsockname@12 referenced in function _main

1>TCPServersrc.obj : error LNK2019: unresolved external symbol __imp__recv@16 referenced in function _main

1>TCPServersrc.obj : error LNK2019: unresolved external symbol __imp__accept@12 referenced in function _main

1>TCPServersrc.obj : error LNK2019: unresolved external symbol __imp__listen@8 referenced in function _main

1>TCPServersrc.obj : error LNK2019: unresolved external symbol __imp__closesocket@4 referenced in function _main

1>TCPServersrc.obj : error LNK2019: unresolved external symbol __imp__bind@12 referenced in function _main

1>TCPServersrc.obj : error LNK2019: unresolved external symbol __imp__htonl@4 referenced in function _main

1>TCPServersrc.obj : error LNK2019: unresolved external symbol __imp__htons@4 referenced in function _main

1>TCPServersrc.obj : error LNK2019: unresolved external symbol __imp__socket@12 referenced in function _main

1>TCPServersrc.obj : error LNK2019: unresolved external symbol __imp__WSACleanup@0 referenced in function _main

1>TCPServersrc.obj : error LNK2019: unresolved external symbol __imp__WSAGetLastError@0 referenced in function _main

1>TCPServersrc.obj : error LNK2019: unresolved external symbol __imp__WSAStartup@8 referenced in function _main

1>C:\Mywinsockproject\TCPServer\Debug\TCPServer.exe : fatal error LNK1120: 16 unresolved externals

1>Build log was saved at "file://c:\Mywinsockproject\TCPServer\TCPServer\Debug\BuildLog.htm"

1>TCPServer - 17 error(s), 0 warning(s)

========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

 

10. Build the project and make sure there is no error which can be seen (if any) in the Output window normally docked at the bottom of the IDE by default.

11. Run the project. Unblock the Windows firewall if any.

 

Windows socket and C programming: unblocking the Windows firewall alert message box

 

12. If there is no error, a sample output is shown below.

 

Windows socket and C programming: a sample Winsock2 server/receiver console application output which is ready to accept connection

 

Now that you understand how to construct a server that can receive a client connection, we will describe how to construct a client.

 

 

 


< Winsock2 Headers & Libraries | Winsock2 Main | Client APIs & TCP States >