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) 编辑 收藏 引用