< Winsock 2 C Programming Index | IP Address & Winsock2 APIs >
|
|
Winsock is a standard application programming interface (API) that allows two or more applications (or processes) to communicate either on the same machine or across a network and is primarily designed to foster data communication over a network. It is important to understand that Winsock is a network programming interface and not a protocol. Winsock provides the programming interface for applications to communicate using popular network protocols such as Transmission Control Protocol/Internet Protocol (TCP/IP) and Internetwork Packet Exchange (IPX). The Winsock interface inherits a great deal from the BSD Sockets implementation on UNIX platforms. In Windows environments, the interface has evolved into a truly protocol-independent interface, especially with the release of Winsock 2. The examples presented in this chapter help to provide an understanding of the Winsock calls that are required for accepting connections, establishing connections, and sending and receiving data. Because the purpose of this chapter is to learn these fundamental Winsock calls, the examples presented use straight blocking Winsock calls. You can differentiate the two functions with the WSA prefix. If Winsock 2 updated or added a new API function in its specification, the function name is prefixed with WSA. For example, the Winsock 1 function to create a socket is simply socket(). Winsock 2 introduces a newer version named WSASocket() that is capable of using some of the new features made available in Winsock 2. There are a few exceptions to this naming rule. WSAStartup(), WSACleanup(), WSARecvEx(), and WSAGetLastError() are in the Winsock 1.1 specification. If you fail to find the related Winsock headers in your machine or programming environment, you may want to read Visual .NET doc 1 and Visual .NET doc 2 articles first. Those articles also show how to add the Winsock link library, ws2_32.lib to the Visual C++ project.
Winsock Headers and Libraries
When developing new applications you should target the Winsock 2 specification by including WINSOCK2.H in your application. For compatibility with older Winsock applications and when developing on Windows CE platforms, WINSOCK.H is available. There is also an additional header file: MSWSOCK.H, which targets Microsoft-specific programming extensions that are normally used for developing high performance Winsock applications. When compiling your application with WINSOCK2.H, you should link with WS2_32.LIB library. When using WINSOCK.H (as on Windows CE) you should use WSOCK32.LIB. If you use extension APIs from MSWSOCK.H, you must also link with MSWSOCK.LIB (get the idea here - VC++ .NET or here - VC++ 6). Once you have included the necessary header files and link environment, you are ready to begin coding your application, which requires initializing Winsock. |
Every Winsock application must load the appropriate version of the Winsock DLL. If you fail to load the Winsock library before calling a Winsock function, the function returns a SOCKET_ERROR; the error will be WSANOTINITIALISED. Loading the Winsock library is accomplished by calling the WSAStartup() function, which is defined as:
int WSAStartup(
WORD wVersionRequested,
LPWSADATA lpWSAData
);
The wVersionRequested parameter is used to specify the version of the Winsock library you want to load. The high-order byte specifies the minor version of the requested Winsock library, while the low-order byte is the major version. You can use the handy macro MAKEWORD(x, y), in which x is the high byte and y is the low byte, to obtain the correct value for wVersionRequested. The lpWSAData parameter is a pointer to a LPWSADATA structure that WSAStartup() fills with information related to the version of the library it loads:
typedef struct WSAData
{
WORD wVersion;
WORD wHighVersion;
char szDescription[WSADESCRIPTION_LEN + 1];
char szSystemStatus[WSASYS_STATUS_LEN + 1];
unsigned short iMaxSockets;
unsigned short iMaxUdpDg;
char FAR * lpVendorInfo;
} WSADATA, * LPWSADATA;
WSAStartup() sets the first field, wVersion, to the Winsock version you will be using. The wHighVersion parameter holds the highest version of the Winsock library available. Remember that in both of these fields, the high-order byte represents the Winsock minor version, and the low-order byte is the major version. The szDescription and szSystemStatus fields are set by the particular implementation of Winsock and aren't really useful. Do not use the next two fields, iMaxSockets and iMaxUdpDg. They are supposed to be the maximum number of concurrently open sockets and the maximum datagram size; however, to find the maximum datagram size you should query the protocol information through WSAEnumProtocols().
The maximum number of concurrent sockets isn't some magic number, it depends more on the physical resources available. Finally, the lpVendorInfo field is reserved for vendor-specific information regarding the implementation of Winsock. This field is not used on any Windows platforms.
For the most part, when writing new applications you will load the latest version of the Winsock library currently available. Remember that if, for example, Winsock 3 is released, your application that loads version 2.2 should run as expected. If you request a Winsock version later than that which the platform supports, WSAStartup() will fail. Upon return, the wHighVersion of the WSADATA structure will be the latest version supported by the library on the current system. When your application is completely finished using the Winsock interface, you should call WSACleanup(), which allows Winsock to free up any resources allocated by Winsock and cancel any pending Winsock calls that your application made. WSACleanup() is defined as:
int WSACleanup(void);
Failure to call WSACleanup when your application exits is not harmful because the operating system will free up resources automatically; however, your application will not be following the Winsock specification. Also, you should call WSACleanup for each call that is made to WSAStartup.
We'll first cover error checking and handling, as they are vital to writing a successful Winsock application. It is actually common for Winsock functions to return an error; however, there are some cases in which the error is not critical and communication can still take place on that socket. The most common return value for an unsuccessful Winsock call is SOCKET_ERROR, although this is certainly not always the case. When covering each API call in detail, we'll point out the return value corresponding to an error. The constant SOCKET_ERROR actually is -1. If you make a call to a Winsock function and an error condition occurs, you can use the function WSAGetLastError() to obtain a code that indicates specifically what happened. This function is defined as:
int WSAGetLastError (void);
A call to the function after an error occurs will return an integer code for the particular error that occurred. These error codes returned from WSAGetLastError() all have predefined constant values that are declared in either WINSOCK.H or WINSOCK2.H, depending on the version of Winsock. The only difference between the two header files is that WINSOCK2.H contains more error codes for some of the newer API functions and capabilities introduced in Winsock 2. The constants defined for the various error codes (with #define directives) generally begin with WSAE. On the flip side of WSAGetLastError(), there is WSASetLastError(), which allows you to manually set error codes that WSAGetLastError() retrieves.
The following program demonstrates how to construct a skeleton Winsock application based on the discussion so far:
#include <winsock2.h>
void main(void)
{
WSADATA wsaData;
// Initialize Winsock version 2.2
if ((Ret = WSAStartup(MAKEWORD(2,2), &wsaData)) != 0)
{
printf("WSAStartup failed with error %ld\n", WSAGetLastError());
return;
}
// Setup Winsock communication code here
// When your application is finished call WSACleanup
if (WSACleanup() == SOCKET_ERROR)
{
printf("WSACleanup failed with error %d\n", WSAGetLastError());
}
}
Let try this program using Visual C++ 2008 Express Edition. First and foremost let change the newly installed VC++ startup page to last loaded solution. You can skip this ‘optional’ step. Click Tools menu > Options sub menu.

Expand Environment folder > Select Startup link > Set the At Startup: to Load last loaded solution > Click OK.

1. Then we can start creating the Win32 console application project. Click File menu > Project sub menu to create a new project.

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

3. Click Next for the Win32 Application Wizard Overview page. We will remove all the unnecessary project items.

4. In the Application page, select Empty project for the Additional options:. Leave others as given and click Finish.

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


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

7. Now, add the source code as given below.
#include <winsock2.h>
#include <stdio.h>
int main(void)
{
WSADATA wsaData;
int RetCode;
// Initialize Winsock version 2.2
if ((RetCode = WSAStartup(MAKEWORD(2,2), &wsaData)) != 0)
{
printf("WSAStartup failed with error %d\n", RetCode);
return 1;
}
else
{
printf("The Winsock dll found!\n");
printf("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("The dll do not support the Winsock version %u.%u!\n",
LOBYTE(wsaData.wVersion),HIBYTE(wsaData.wVersion));
// When your application is finished call WSACleanup
WSACleanup();
// and exit
return 0;
}
else
{
printf("The dll supports the Winsock version %u.%u!\n", LOBYTE(wsaData.wVersion),
HIBYTE(wsaData.wVersion));
printf("The highest version this dll can support: %u.%u\n", LOBYTE(wsaData.wHighVersion),
HIBYTE(wsaData.wHighVersion));
// Setup Winsock communication code here
// When your application is finished call WSACleanup
if (WSACleanup() == SOCKET_ERROR)
printf("WSACleanup failed with error %d\n", WSAGetLastError());
// and exit
return 1;
}
}
8. 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.
------------------------------------------------------------

9. 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).

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

11. Manually, type the library name and click OK.

12. Or you can just directly type the library name in the empty field on the right of the Additional Dependencies. Click OK.

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

14. Run the project.

15. If there is no error, a sample of expected output is shown below.

Well, after completing this exercise you should be familiar with the steps to create an empty Win32 console application project. Those steps will be repeated for almost all the Winsock2 projects in this tutorial. Now we are ready to describe how to set up communication using a network protocol.
< Winsock 2 C Programming Index | IP Address & Winsock2 APIs >