程式扎記: [ Network Programming in MS ] Introduction to WinSock (Part1)

標籤

2011年4月11日 星期一

[ Network Programming in MS ] Introduction to WinSock (Part1)

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

  1. #pragma comment(lib, "WS2_32.lib")  
- Link from project 
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 : 
  1. int WSAStartup(  
  2.     WORD wVersionRequested,  
  3.     LPWSADATA lpWSAData  
  4. );  
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. 
  1. typedef struct WSAData   
  2. {  
  3.     WORD           wVersion;  
  4.     WORD           wHighVersion;  
  5.     char           szDescription[WSADESCRIPTION_LEN + 1];   
  6.     char           szSystemStatus[WSASYS_STATUS_LEN + 1];  
  7.     unsigned short iMaxSockets;  
  8.     unsigned short iMaxUdpDg;  
  9.     char FAR *     lpVendorInfo;  
  10. } 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. For more, you can refer to msdn structure WSADATA. 
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 : 
  1. #include   
  2.   
  3. void main(void)  
  4. {  
  5.    WSADATA wsaData;  
  6.   
  7.    // Initialize Winsock version 2.2  
  8.   
  9.    if ((Ret = WSAStartup(MAKEWORD(2,2), &wsaData)) != 0)  
  10.    {  
  11.       // NOTE: Since Winsock failed to load we cannot use   
  12.       // WSAGetLastError to determine the specific error for  
  13.       // why it failed. Instead we can rely on the return   
  14.       // status of WSAStartup.  
  15.   
  16.       printf("WSAStartup failed with error %d\n", Ret);  
  17.       return;  
  18.    }  
  19.   
  20.    // Setup Winsock communication code here   
  21.   
  22.    // When your application is finished call WSACleanup  
  23.    if (WSACleanup() == SOCKET_ERROR)  
  24.    {  
  25.       printf("WSACleanup failed with error %d\n", WSAGetLastError());  
  26.    }  
  27. }  
Now we are ready to describe how to set up communication using a network protocol. 

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 : 
  1. struct sockaddr_in  
  2. {  
  3.     short           sin_family;  
  4.     u_short         sin_port;  
  5.     struct in_addr  sin_addr;  
  6.     char            sin_zero[8];  
  7. };  
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. 
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 : 
  1. unsigned long inet_addr(  
  2.     const char FAR *cp   
  3. );   
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. 

- 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 : 
  1. u_long htonl(u_long hostlong);  
  2.   
  3. int WSAHtonl(  
  4.     SOCKET s,  
  5.     u_long hostlong,  
  6.     u_long FAR * lpnetlong  
  7. );  
  8.   
  9. u_short htons(u_short hostshort);  
  10.   
  11. int WSAHtons(  
  12.     SOCKET s,  
  13.     u_short hostshort,  
  14.     u_short FAR * lpnetshort  
  15. );  
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 andWSAHtons 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 theWSAHtons 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 : 
  1. u_long ntohl(u_long netlong);  
  2.   
  3. int WSANtohl(  
  4.     SOCKET s,  
  5.     u_long netlong,  
  6.     u_long FAR * lphostlong  
  7. );  
  8.   
  9. u_short ntohs(u_short netshort);  
  10.   
  11. int WSANtohs(  
  12.     SOCKET s,  
  13.     u_short netshort,  
  14.     u_short FAR * lphostshort  
  15. );  
We will now demonstrate how to address IPv4 by creating a SOCKADDR_IN structure using the inet_addr and htons functions described previously : 
  1. SOCKADDR_IN InternetAddr;  
  2. INT nPortId = 5150;  
  3.   
  4. InternetAddr.sin_family = AF_INET;  
  5.   
  6. // Convert the proposed dotted Internet address 136.149.3.29  
  7. // to a four-byte integer, and assign it to sin_addr  
  8.   
  9. InternetAddr.sin_addr.s_addr = inet_addr("136.149.3.29");  
  10.   
  11. // The nPortId variable is stored in host-byte order. Convert  
  12. // nPortId to network-byte order, and assign it to sin_port.  
  13.   
  14. 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. Chapter 3 describes useful address and name resolution functions that can help you resolve a host name. 
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 : 
  1. SOCKET socket (  
  2.     int af,  
  3.     int type,  
  4.     int protocol  
  5. );  
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. 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. 

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!