随笔 - 17  文章 - 0 评论 - 4 
<2009年5月>
262728293012
3456789
10111213141516
17181920212223
24252627282930
31123456

常用链接

留言簿(1)

随笔档案(17)

文章分类

搜索

  •  

最新评论

阅读排行榜

评论排行榜

In the last chapter, we provided a brief (简短) overview of the origins (起源) of the Internet and examined (审查) the

evolution (演变) of BSD Sockets and the technology that gave birth to the Internet and provided the basis (基础) for

Windows's Internet technology, Widnows Socket.

In this chapter, we'll learn how to write a simple Winsock application that essentially (基本上) does nothing useful.

However, it does demonstrate how to load and unload Windsock correctly (正确). We'll also learn how to detect (检测) Winsock

errors properly (正确).

Start and Closing Winsock

In this chapter, we'll build a simple application that demonstrates the two most fundamental (基本) functions in the Winsock

stable (固定), WSAStartUp() and WSACleanup(). Without exception, your application must always call WSAStartUp() before

calling any other Winsock API function. If you neglect (忽视) this essential (基本) step, your appliation will fail,

sometimes in spectacular (壮观) fashion(时尚) (惊人地, 壮观地). Similarly, when your application ends, it should always

call WSACleanup().

At invocation,WSAStartup() performs several essential tasks, as follows:
■ Loads Winsock into memory
■ Registers the calling application
■ Allocates resources for the calling application
■ Obtains (获得) the implementation details for Winsock

You can use the implementation details returned by WSAStartup() to determine if the version of Winsock is compatible with

the version requested by the calling application. Ideally (理想), any application should run using any version of Winsock.

winsock 1.1 applications can run unchanged using Winsock  2 because Winsock 2 seamlessly (无缝) maps the Winsock 1.1

functions to their equivalents (等价物) in Winsock 2.

To maintain this backward compatibility, WSAStartup() performs a negotiation (谈判) phase (阶段) with the calling

application. In this phase, the Winsock DLL and the calling application negotiate the highest version that they both can

support.

If Winsock supports the version requested by the application, the call succeeds and Winsock returns the highest version that

it supports. In other words, if a Winsock 1.1 application makes a request to load Winsock 1.1, and if Winsock 2 is present,

the application will work with Winsock 2 because it supports all version up to 2, include 1.1.

This negotiation phase allows Winsock and the application to support a range of Winsock versions. Table 2-1 shows the range

of Winsock versions that an application can use.

Table 2-1: Different version of Winsock

It is only necessary for an application to call WSAStartup() and WSACleanup() once. Sometimes, though, an application may

call WSAStartup() more than once. The golden rule is to make certain that the number of calls to WSAStartup() matches the

number of calls to WSACleanup(). For example, if an application calls WSAStartup() three times, it must call WSACleanup()

three times. That is, the first two calls to WSACleanup() do nothing except decrement an internal counter in Winsock; the

final call to WSACleanup() for the task frees any resource.

Unlike Winsock 1.1 (which only supports one provider), the architecture of Winsock2 supports multiple providers, which we

will discuss in Chapter 4.

function WSAStartup Winsock2.pas

Syntax
WSAStartup(wVersionRequired:WORD; var lpWSAData:TWSAData):Integer; stdcall;

Description
This function initializes the Winsock DLL, registers the calling application, and allocates resources. It allows the

application to specify the minimum version of Winsock it requires. The function also returns implementation information that

the calling application should examine for version compatibility. After successful invocation of WSAStartup(), the

application can call other Winsock functions.

Parameters
wVersionRequired: The highest version that the calling application requires. The high-order byte specifies the minor version

and low-order byte the major version. Under Windows 95, the highest version that is supported si 1.1. At the tiem of

publication, the current version is 2.2. Table 2-2 presents which version of Winsock is available for all Windows operating

systems.

Table 2-2 Winsock versions for all Windows platforms
Windows 3.1                         1.1
Windows 95    1.1(2.2) See Tip
Windows 98    2.2
Windows Milennium   2.2
windows NT 4.0    2.2
Windows XP    2.2

TIP: If you belong to that unique tribe of developers that still uses Win95 as a development platform, and you want to

develop Winsock 2 applications for Windows 95, you will have to upgrade Winsock 1.1. The upgrade is avaiable from the

Microsoft web site (www.microsoft.com).

wsDATA: This is a placeholder for the WSAData record that contains implementataion details for Winsock. When we call

WSAStartup(), the function populates the WSAData record, which is defined in Winsock2.pas as follows:
WSADATA = record
 wVersion:WORD;
 wHighVersion:WORD;
 szDescription:array[0..WSADESCRIPTION_LEN] of Char;
 szSystemStatus:array[0..WSASYS_STATUS_LEN] of Char;
 iMaxSockets:Word;
 iMaxUdpDg:Word;
 lpVendorInfo: PChar;
end;

LPWSADATA=^WSAData;
TWsaData = WSAData;
PWsaData = LPWSADATA;

Table 2-3 describes these field of the WSAData data structure.

Table 2-3: Values for the main members of the WSAData structure
wVersion: The version of the Windows Sockets specification that the Windows Sockets DLL excepts the calling application to

use.

wHighVersion: The highest version of the Windows Sockets spcification that DLL that can support (also encoded as above).

Normally this will be the same as wVersion.

szDescription: A NULL-terminated ASCII string into which the Windows Sockets DLL copies a description of the Windows Sockets

implementation. The text may be up to 256 characters in length and contain any characters except control and formatting

characters. The information in this field is often used by an application to provide a status message.

szSystemStatus: A NULL-terminated ASCII string into which the Windows Sockets DLL copies relevant status or configuration

information. The Windows Sockets DLL should use this field only if the information might be useful to the user or support

staff; it should not be considered as an extension of the szDescription field.

iMaxSockets: This field is retained for backward compatibility but should be ignored for version 2 and later, as no single

value can be appropriate for all underlying service providers.

iMaxUdpDg: This value should be ignored for version 2 and onward. It is retained for backward compatibility with Windows

sockets specification 1.1 but should not be used when develping new applications. For the actual maximum message size

specific to a particular Windows sockets service provider and socket type, applications should use getsockopt() to retrieve

the value of option SO_MAX_MSG_SIZE after a socket has been created.

lpVendorInfo: This value should be ignored for version 2 and onward. It is retained for backward compatibility with Windows

Sockets specification 1.1. Applications needing to access vendor-specific configuration information should use getsockopt()

to retrieve the value of option PVD_CONFIG. The definition of this value (if utilized) is beyond the scope of this

specification.

Return Value
If successful, WSAStartup() will return zero. As we'll see when we cover other Winsock functions, WSAStartup() is the

exception to the rule in that it does not return a Winsock error that we can use to determine the cause of that error. Since

WSAStartup() is a function that initializes the Winsock DLL, which includes the WSAGetLastError() function to report

Winsock-specific errors, it cannot call WSAGetLastError() because the DLL is not loaded. It is a conundrum (难题) like the

proverbial chicken and egg problem. Therefore, to test for the success or failure to initialize Winsock, we just check for

the return value of WSAStartup().

Returning to the WSAData data structure, as far as programming Winsock applications goes, the most important fields that you

should always read or check are wVersion and wHighVersion.

The WSAData structure in Winsock 2 no longer necessarily applies to a single vendor's stack. This means that Winsock 2

applications should ignore iMaxSockets, iMaxUdpDg, and lpVendorInfo, as these are irrelevant (无关). However, you can

retrieve provider-specific information by calling the getsockopt() function. We'll discuss this function briefly in Chapter

6, "Socket Options."

See Also
getsockopt, send, sendto, WSACleanup

Example
Listing 2-1 (program EX21 on the companion CD) shows how to load Winsock using WSAStartup() and how to verify version

compatibility. It also shows how to close a Winsock application properly using WSACleanup().

function WSACleanup Winsock2.pas
Syntax
 WSACleanup:Integer;stdcall;
Description
 This function unloads the Winsock DLL. A call to WSACleanup() will cancel the following operations: blocking and

asynchronous calls, overlapped send and receive operations, and close and free any open sockets. Please note that any data

pending (紧迫的) may be lost.

Parameters
 None
Return Value
 If successful, the function will return a value of zero. Otherwist, the function returns a value of SOCKET_ERROR. To

retrieve information about the error, call the WSAGetLastError() function. Possible error codes are WSANOTINITIALISED,

WSAENETDOWN, and WSAEINPROGRESS. See Appendix B for a detailed description of the error codes.

See Also
 closesocket, shutdown, WSAStartup
Example
Listing 2-1 shows how to load and unload the Winsock DLL by calling WSAStartup() and WSACleanup(), respectively (分别).

Handling Winsock Errors
Like any application, a winsock application can fail. You cannot always prevent (防止) an application error, but you can at

least detect (检测) and handle any winsock error. There are two classes of Winsock errors. One is an error caused by

inappropriate (不适当) calls to the Winsock function. A classic example of this is calling any other Winsock function

without first calling the WSAStartup() function. The other is a network error, which is completely unpredictable (难以预测),

hence (从此) the importance of trapping (捕获) this type of error.this type of error.

To help you detect and handle errors, Winsock provides two functions, WSAGetLastError() and WSASetLastError(). When an error

occurs, your application should determine the error by calling WSAGetLastError() and take appropriate (适当) action,

depending on the contex of the error. For example, when an application makes an inappropriate call to an API, it should

report the error and retire gracefully (优雅). For a network error, the application should handle it in context. For

example, if a connection breaks, the application should report the error and perform another task or retire altogether.

WSAGetLastError() is a wrapper for GetLastError(), which is a standard function for reporting errors in Windows, and because

GetLastError() uses a TLS (thread local storage) entry in the calling threads context, WSAGetLastError() is thread safe.

(For more information on threads, consult The Tomes of Delphi: Win32 Core API-Windows 2000 Edition by John Ayres (ISBN 1-

55622-750-7) from Wordware Publishing, inc.).

For a robust (健全) Winsock application, the strategy (战略) to employ is as follows: After each call to a Winsock API, you

must check the resul of the function (which is usually SOCKET_ERROR, though INVALID_SOCKET is used for certain function

calls such as socket()). If there is an error, you clal WSAGetLastError() to determine the cause of the error. The

application code should always provide a means of handling the error gracefully and retiring, if necessary. You can use the

WSASetLastError() function to set an error code that your application can use in certain situations. This function is

similar to SetLastError(), which, like GetlastError(), is also a member of the Win32 API.

WSAGetLastError() is not the only function to return a Winsok error code. The other reporting functions are getsockopt(),

WSAGetAsyncError(), and WSAGetSelectError(). WSAGetAsyncError() and WSAGetSelectError() are functions that extract

additional error information whenever an error occurs. You should use WSAGetAsyncError and WSAGetSelectError() rather than

WSAGetLastError() when you use Microsoft's family of asynchronous functions, which we will cover in Chapters 3 and 5.

The WSASetLastError() function is a useful function to deploy, provided you are aware of the caveat emptor (购者自慎) of

using this function inappropriately. You use WSASetLastError() to set a virtual error that your application can retrieve

with a call to WSAGetLastError(). However, any subsequent call to WSAGetLastError() will wipe out the artificial error,

which is where the caveat emptor comes in if your program logic is incorrect. To explain the use of WSASetLastError(), I

have developed a rather contrived example in Listing 2-3.

Errors and errors

As you would expect, error codes, like socket functions, have a UNIX pedigree (系谱). The list of errors and their brief (简

要) descriptions are in Appendix B. As well as that pedigree, we have Winsock-specific error codes resulting in a hybrid (混

合). If you examine Winsock2.pas, you will see two blocks of error codes that begin with WSA and E prefixes. These refer to

Winsock and Berkeley error codes, respectively (分别). The Berkeley error codes are mapped to their Winsock equivalent (等价

物). this mapping is rather useful or UNIX developers porting their socket applications to Windows. Thankfully, this detail

is irrelevant (无关) to Delph develpers.

Rather than listing what's common to Winsock and UNIX socket error codes, the following list shows Winsock-specific error

codes not found in UNIX. We will describ some of these errors in detail when we discuss the Winsock functions in the

chapters to follow. Note that we will not discuss Quality of Service (error codes from WSA_QOS_RECEIVERS to and including

WSA_QOS_RESERVED_PETYPE), as this is a topic for another tome.

WSASYSNOTREADY
WSAVERNOTSUPPORTED
WSANOTINITIALISED
WSAEDISCON
WSAENOMORE
WSAECANCELLED
WSAEINVALIDPROCTABLE
WSAEINVALIDPROVIDER
WSAEPROVIDERFAILEDINIT
WSASYSCALLFAILURE
WSASERVICE_NOT_FOUND
WSATYPE_NOT_FOUND
WSA_E_NO_MORE
WSA_E_CANCELLED
WSAEREFUSED
WSA_QOS_RECEIVERS
WSA_QOS_EOBJLENGTH
WSA_QOS_EFLOWCOUNT
WSA_QOS_EUNKOWNPSOBJ
WSA_QOS_EPOLICYOBJ
WSA_QOS_EFLOWDESC
WSA_QOS_SENDERS
WSA_QOS_NO_SENDERS
WSA_QOS_NO_RECEIVERS
WSA_QOS_REQUEST_CONFIRMED
WSA_QOS_ADMISSION_FAILURE
WSA_QOS_POLICY_FAILURE
WSA_QOS_BAD_STYLE
WSA_QOS_BAD_OBJECT
WSA_QOS_TRAFFIC_CTRL_ERROR
WSA_QOS_GENERIC_ERROR
WSA_QOS_ESERVICETYPE
WSA_QOS_EFILTERCOUNT
WSA_QOS_EPSFLOWSPEC
WSA_QOS_EPSFILTERSPEC
WSA_QOS_ESDMODEOBJ
WSA_QOS_ESHAPERATEOBJ
WSA_QOS_RESERVED_PETYPE

Before concluding (结论) this section, here is a final word to the wise about error codes: It is all very well for your

application handle Winsock exceptions and report error codes as they arise. Your Winsock application should also present

exceptions in plain language as well as the actual error code for ease of error reporting for the user. In example in this

book, we use sysErrorMessage(), a function that translates error codes into plain language that your user will hopefully

understand. The string in the tail (尾巴) with this function is that it doesn't work across all Windows platforms. The

SysErrorMessage() function work fine on windows 2000 but reports an empty string on Windows NT 4.0.

TIP: Use SysErrorMessage() to present a meaningful explanation (解释) of Winsock errors to your user.

Listing 2-3 demonstrates how to use SysErrorMessage().

function WSAGetLastError Winsock2.pas
Syntax
 WSAGetLastError:Integer;stdcall;
Description
 This function retrieves the error status for the last network operation that failed.
Parameters
 None
Return Value
 The return value indicates the error code for the last operation that failed.
See Also
 getsockopt, WSASetLastError
Example
 Listing 2-2 (program EX22) shows how to use WSAGetLastError().

procedure  WSASetLastError Winsock2.pas
Syntax
 WSASetLastError(iError:Integer);stdcall;
Description
 The function sets the error code that can be retrieved through the WSAGetLastError() fucntion.
Parameters
 iError: Integer that specifies the error code to be returned by a subsequent WSAGetLastError() call.
Return Value
 There is no return value.
See Also
 getsockopt, WSAGetLastError
Example
 Listing 2-3 (program EX23) shows how to use WSASetLastError() and WSAGetLastError().

The Many Faces of the Winsock Dll
By this stage (阶段), you might have the impression (印象) that Winsock 2 is a monolithic (单片) API wrapped (包裹) in a

DLL. Not so! At least, it is no longer true for Winsock 2. Unlike Winsock 1.1, which had only one transport protocol to

content (内容) with, namely TCP/IP, Winsock 2 is designed to handle transport protocols other than TCP/IP. (If you cast your

mind back to Chapter 1, Winsock is an integral (完整的) component of WOSA.) Complicating matters, Winsock 2 also has to

handle different name space for the resolution of names, services, and ports. (Don't worry; we will cover these topic in

Chapter 4.) this complexity, which permits Winsock 2 to be multilingual, is reflected in how Winsock 2 is structured across
DLLs. this sharing of tasks by DLLs becomes clear if you take a look at Table 2-4. As split u/+p as Winsock 2 is, the main

DLL for the Winsock 2 API resides in the Ws2_32.DLL. those applications that require winsock 1.1 are handled by the Winsock

and WSock32 DLLs, which are 16-bit and 32-bit, respectively. When an application calls the Winsock 1.1 API, Winsock 2

intercepts these calls and passes then to the Winsock and Wsock32 DLLs as appropriate. This is known as thunking. Winsock 2

delegates tasks to the appropriately called helper DLLs. For example, Wshatm handles funcitons specific to the ATM transport

protocol.

Table 2-4: How Winsock2 is shared across DLLs

Winsock Files           Function
Winsock.dll  16-bit Winsock 1.1
Wsock32.dll  32-bit Winsock 1.1
Ws2_32.dll  Main Winsock2.0
Mswsock.dll   Microsoft extensions to Winsock. Mswsock.dll is
                      an API that supplies services that are not part os Winsock.
Wshtcpip.dll  Helper for TCP
Wshnetbs.dll  Helper for NetBT
Wshirda.dll  Helper for IrDA (infrared sockets)
Wshatm.dll  Helper for ATM
Wshisn.dll  Helper for Netware
Wshisotp.dll  Helper for OSI transports
Sfmwshat.dll  Helper for Macintosh
Nwprovau.dll  Name resolution provider for IPX
Rnr20.dll  Main name resolution
Winrnr.dll  LDAP name resolution
Msafd.dll  Winsock interface to kernel
Afd.sys   Winsock kernel interface to TDI transport protocols

Summary
We have learned how to load and unload Winsock. We also learned how to detect Winsock and handle errors. In the next

chapter, we'll learn how to use the various functions for resolving hosts and services. Resolution of hosts, ports, and

services is an essential step to perform before communication can occur between peer applications.

 


 

posted on 2009-05-25 22:49 鸡蛋捞面 阅读(620) 评论(0)  编辑 收藏 引用
只有注册用户登录后才能发表评论。