Handling Notification MessagesNotification Messages is really a misleading term. In reality, WinSock only generates one message that it posts to the queue, and attached to it is the event that occurred. This is shown by the example below. switch(msg) { case WM_WSAASYNC: { // what word? switch(WSAGETSELECTEVENT(lParam)) { case FD_ACCEPT: { // check for an error if (!WSAGETSELECTERROR(lParam)) return(FALSE); // process message sAccept = accept(wParam, NULL, NULL); return(0); } } } } Whoa! Talk about nests! And what are all these macros doing? LParam and wParam? What the heck? Let’s now take a look at the notification message structure
Ahhh, now it’s beginning to make some sense. Let’s cover where each parameter plays its part. We’ll start with wParam. Where is it? Right here: ... sAccept = accept(wParam, NULL, NULL); ... And here’s what I was telling you earlier. Notice I used the variable wParam in place of the socket instance. Why? So that if I had more than one socket (s1 and s2) I could use the same message for both. So whichever socket invokes the message is passed along with it. For instance, say it was s2 that needs to receive data. Well instead of having a switch statement inside the message handler to decide which socket to place in the accept() call, I just use wParam. Translation: sAccept = accept(wParam, NULL, NULL); is equal to sAccept = accept(s2, NULL, NULL); Now that is cool. Saves you the time and the trouble now doesn’t it? I know I probably repeated myself a bit there but it’s an important point to remember and understand because it’s very useful, as you can see. Now then, lParam is no more complicated. But there is a difference. The lParam variable has a high field and a low field, and both fields can be set to separate values. Normally we would extract these with the Windows’ API HIWORD() and LOWORD() macros. But Winsock makes our lives even easier and has two of its own macros, WSAGETSELECTEVENT() and WSAGETSELECTERROR(). These do the exact same thing as the Windows’ macros, except they relieve us of the need to remember which field holds what. If you look back up at the example above, you can see how both macros are used. The WSAGETSELECTEVENT() is your ticket to figuring out just what WinSock event was triggered. Here is the full case statement inside the WM_WSAASYNC message handler: switch(WSAGETSELECTEVENT(lParam)) { case FD_READ: { } break; case FD_WRITE: { } break; case FD_CONNECT: // or case FD_ACCEPT: { } break; case FD_CLOSE: { } break; } Hope I didn’t trip you up with the FD_CONNECT and the FD_ACCEPT events - just remember that they will always depend on whether it’s the server or the client. And finally we have the WSAGETSELECTERROR() macro, which we can use to see if an error has occurred before we decide to do anything. Usually this is not necessary, but there is one good time to error check - and that’s when the FD_CLOSE event is called. As I said earlier, there are many reasons a socket could be shut down. Here are the error codes:
It’s always best to check for these error codes when processing an FD_CLOSE event to determine just what went wrong. All other times you should just use the WSAGetLastError() function to determine what went bad. |