Using WSAAsync Select

Calling WSAAsyncSelect() notifies Winsock to send a message to a nominated window whenever a network event occurs. You should specify which network events to detect when you make a call to WSAAsyncSelect(). Calling WSA-AsyncSelect() automatically sets the socket in non-blocking mode. The prototype for WSAAsyncSelect() is defined in Winsock2.pas as follows:

function WSAAsyncSelect(s: TSocket; hWnd: HWND; wMsg: u_int; lEvent: Longint): Integer; stdcall;

The first parameter, s, is the socket that you want to put into non-blocking or, more correctly, asynchronous mode. The second parameter, hWnd, specifies the handle to the window for notification. The wMsg parameter identifies the message that the window handle is to receive when a network event occurs. The value of the wMsg parameter must be greater than the value of WM_USER to avoid message conflicts. The last parameter, lEvent, specifies which network events to monitor. The network events you may specify are listed in Table 5-6. To monitor more than one network event, you should call WSAAsyncSelect(), like this:

WSAAsyncSelect(s, hwnd, WM_SOCKET, FD_CONNECT or FD_READ or FD_WRITE or FD_CLOSE)

This tells Winsock to monitor the following network events that occur when a connection is made, pending read I/O, pending write I/O, or a connection is closed, respectively. Table 5-6: Network events

Value

Meaning

FD_READ

Required to receive notification of readiness for reading

FD_WRITE

Required to receive notification of readiness for writing

FD_OOB

Required to receive notification of the arrival of out-of-band data

FD_ACCEPT

Required to receive notification of incoming connections

FD_CONNECT

Required to receive notification of completed connection or multipoint join operation

FD_CLOSE

Required to receive notification of socket closure

FD_QOS

Required to receive notification of socket Quality of Service (QOS) changes

FD_GROUP_QOS

Reserved for future use with socket groups; required to receive notification of socket group Quality of Service (QOS) changes

FD_ROUTING_INTERFACE_CHANGE

Required to receive notification of routing interface changes for the specified destination(s)

FD_ADDRESS_LIST_CHANGE

Required to receive notification of local address list changes for the socket's protocol family

During the lifetime of an application, you will often call WSAAsyncSelect() for a socket more than once. A new call to WSAAsyncSelect() will cancel any previous WSAAsyncSelect() or WSAEventSelect() calls for the same socket. For example, to receive notification for reading and writing, the application must call WSAAsyncSelect() with both FD_READ and FD_WRITE, as the following code snippet illustrates:

WSAAsyncSelect(s, hWnd, wMsg, FD_READ OR FD_WRITE);

It is not possible to specify different messages for different events. The following code will not work properly because the second call to WSAAsyncSelect() will cancel the effects of the first call, and only FD_WRITE events will be reported with message wMsg2:

WSAAsyncSelect(s, hWnd, wMsgl, FD_READ); // first call

WSAAsyncSelect(s, hWnd, wMsg2, FD_WRITE); // second call overwrites original event notification

To cancel all notifications, you need to set lEvent to zero, like this:

WSAAsyncSelect(s, hWnd, 0, 0);

Although in this case, calling WSAAsyncSelect() immediately disables event message notification for the socket s, it is possible that messages may still be waiting in the application's message queue. The application must still receive network event messages even after cancellation. Closing a socket with closesocket() also cancels WSAAsyncSelect() message sending, but the same caveat about messages in the queue prior to calling the socket() function still applies.

When you call WSAAsyncSelect(), you must always check for any result from the function. It is nearly always the case that the function could return a nonfatal error of WSAEWOULDBLOCK, which means that the socket has no pending data for reading or writing. The code that you write with WSAAsyncSelect() must handle this error as well as other errors. The code in Listing 5-8 shows how you should handle the WSAEWOULDBLOCK error.

So far, we have discussed the notification of events, but we must complete the puzzle by associating a procedure to handle the events themselves. We usually declare a message procedure somewhere in the interface section or in a class or component like this:

procedure SomeEvent(var Mess : TMessage); message NETWORK_EVENT;

When you call WSAAsyncSelect(), you link this message procedure with the message NETWORK_EVENT like this:

WSAAsyncSelect(s, hwnd, NETWORK_EVENT, FD_CONNECT or FD_READ or FD_WRITE or FD_CLOSE);

When you get a network event that you have requested Winsock to monitor on your behalf, you must check for any errors on that event. To do this vital check, you should call WSAGetSelectError() to evaluate the LParam field of the Mess parameter returned in the SomeEvent procedure. If WSAGetSelectError() returns zero, the network event is normal; otherwise, if there is a network error, call WSAGetSelectError() again to determine the actual error. Whatever error you get, your code must handle it gracefully. The prototype of WSAGet-SelectError() is defined in Winsock2.pas as follows:

function WSAGetSelectError(Param: Longint): Word;

After verifying that the event has no errors, call WSAGetEventSelect() to determine which event has occurred. The prototype is:

function WSAGetSelectEvent(Param: Longint): Word;

Pass the LParam field of the Mess parameter for inspection. The function returns a network event. When you get an FD_READ event, the socket has pending data ready to receive. Likewise, with FD_WRITE, the socket is ready to send data.

Was this article helpful?

+1 0

Post a comment