mikejsavage.co.uk • About • Archive • RSS • Thanks for blocking ads! Blocking ads owns: AdGuard for Safari / uBlock Origin for everything else
This API is garbage, the docs are garbage, and every piece of code I could find that uses it is garbage. It works roughly like this:
WSAAsyncSelect
on itWndProc
gets called with FD_READ
every frame if there's still data
on the socketWndProc
gets called with FD_CLOSE
when it closeswhich looks totally reasonable. But, FD_CLOSE
happens when the socket
closes and not when you're done reading it, so you can keep getting
FD_READ
s after the FD_CLOSE
. On top of that the API seems to be
buggy shit and you can get random garbage FD_READ
s out of nowhere.
(the Microsoft sample
code
runs into this too (key word in that comment being "usually") so it's
not just me using it wrong)
So the correct usage code looks like this:
switch( WSAGETSELECTEVENT( lParam ) ) {
case FD_READ:
case FD_CLOSE: {
SOCKET sock = ( SOCKET ) wParam;
// check this isn't a random FD_READ
if( !open_sockets.contains( sock ) )
break;
while( true ) {
char buf[ 2048 ];
int n = recv( fd, buf, sizeof( buf ), 0 );
if( n > 0 ) {
// do stuff with buf
}
else if( n == 0 ) {
closesocket( sock );
open_sockets.remove( sock );
break;
}
else {
int err = WSAGetLastError();
if( err != WSAEWOULDBLOCK )
abort();
break;
}
}
} break;
// ...
}
At the top we have a check to make sure it's not a garbage FD_READ
,
and then we have a while loop to read until WSAEWOULDBLOCK
. You need
the loop so you know when to actually close the socket. You can't close
it in FD_CLOSE
because you can get FD_READ
s after that. Without the
loop you'll stop receiving FD_READ
s after you read everything off the
socket, which means you never see the zero length recv
and you won't
know when to close it. Technically you only need to start looping after
FD_CLOSE
, but it simplifies the code a bit if you treat both events in
the same way.
Lots of samples you see just ignore any errors from recv
. Don't do
this, you should explicitly handle expected errors (in this case that's
just WSAEWOULDBLOCK
), and kill the program on anything else because it
means your code is wrong.