This chapter is dedicated to learning the basic techniques for writing successful Winsock applications. 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.
In this chapter, we'll look at the fundamentals of setting up communication from one machine on a network to another, along with how to send and receive data. 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. Chapter 5 presents non-blocking and other advanced I/O methods available in Winsock, including code examples.
Winsock Headers and Libraries :
Winsock is available in two major versions—Winsock 1 and Winsock 2—on all Windows platforms except Windows CE (Windows CE has only Winsock 1). 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.
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 are using MS Visual Studio and Winsock 2, you can either use below two way to include WS2_32.lib :
- Link from Source file
You just declare in source code in below line :
- #pragma comment(lib, "WS2_32.lib")
Or you can use project to include additional dependencies :
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 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;
Table 1-1 lists the versions of Winsock that the various Microsoft Windows platforms support. What's important to remember is the difference between major versions. WINSOCK 1.x does not support many of the advanced Winsock features detailed in this section :
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. 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.
Error Checking and Handling :
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.
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 WSAGetLastErrorall have predefined constant values that are declared in either WINSOCK.H or WINSOCK2.H, depending on the version of Winsock. The following program demonstrates how to construct a skeleton Winsock application based on the discussion so far :
- #include
- void main(void)
- {
- WSADATA wsaData;
- // Initialize Winsock version 2.2
- if ((Ret = WSAStartup(MAKEWORD(2,2), &wsaData)) != 0)
- {
- // NOTE: Since Winsock failed to load we cannot use
- // WSAGetLastError to determine the specific error for
- // why it failed. Instead we can rely on the return
- // status of WSAStartup.
- printf("WSAStartup failed with error %d\n", Ret);
- 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());
- }
- }
Addressing a Protocol :
For simplicity's sake, and to avoid repetition, the remaining discussion in this chapter is limited to describing how to make fundamental Winsock calls to set up communication using the Internet Protocol (IP). We chose IP because most Winsock applications developed today use it because it is widely available due to the popularity of the Internet.
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_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
- );
- 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 :
- u_long htonl(u_long hostlong);
- int WSAHtonl(
- SOCKET s,
- u_long hostlong,
- u_long FAR * lpnetlong
- );
- u_short htons(u_short hostshort);
- int WSAHtons(
- SOCKET s,
- u_short hostshort,
- u_short FAR * lpnetshort
- );
The next four functions are the opposite of the preceding four functions; they convert network-byte order to host-byte order :
- u_long ntohl(u_long netlong);
- int WSANtohl(
- SOCKET s,
- u_long netlong,
- u_long FAR * lphostlong
- );
- u_short ntohs(u_short netshort);
- int WSANtohs(
- SOCKET s,
- u_short netshort,
- u_short FAR * lphostshort
- );
- 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);
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. The next three chapters describe socket creation for each of the available protocols in great detail. For simplicity, we will briefly describe socket :
- SOCKET socket (
- int af,
- int type,
- int protocol
- );
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. Chapter 7 describes each of these functions and all the available options. 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.
沒有留言:
張貼留言