We're using MFC's CSocket, CSocketFile, CArchive in our socket application.
The test PCs are running Windows 2K and Windows XP SP1. We've built our
applications using VC 7.1 (Visual Studio .NET 2003). We're currently running
a debug/unicode build of our projects.
The CSocket-derived object is instantiated in a dedicated CWinThread-derived
thread. The thread's message loop uses the
PeekMessage(...,PM_REMOVE)/DispatchMessage() pair and
MsgWaitForMultipleObjectsEx(...,INFINITE, QS_ALLINPUT, MWMO_INPUTAVAILABLE)
to wakeup for event objects and waitable timer objects. The client app and
the server app link to the same DLL, and therefore both use the mechanism
described above.
The client and server applications behave beautifully up to a point and then
the socket's OnReceive() is no longer called. Using Spy++, I see that
WM_SOCKET_NOTIFY messages stop arriving at the socket window. Sometimes, the
server doesn't receive the last request sent by the client. Sometimes, the
client doesn't receive the last response sent by the server.
With the Debugger I've stepped through MFC's CAsyncSocket::DoCallBack(), in
the FD_READ case to watch select() being called and returned, just to gain a
better understanding of how WM_SOCKET_NOTIFY is handled. I set a breakpoint
on nErrorCode = WSAGetLastError(), but never hit it. For grins, I added a
call to WSAGetLastError() to my thread's message loop, immediately after
DispatchMessage() and before MWOMEx(), so that I could trace an error if
detected. An error wasn't detected. But in that case, I can't be sure that
the Last Error isn't reset in MFC before my call is made. So, it may not be
a good test.
I decided to try calling CSocket::AsyncSelect() immediately after MWMOEx()
returns. I picked up on this idea thanks to information found in a separate
discussion group posting in July 2004: "CAsyncThread::Send does not appear
to send a WM_SOCKET_NOTIFY."
So far this works.
But I have the nagging feeling I am fixing the symptom and not fixing the bug.
Any feedback is greatly appreciated.
-Mary
>We're using MFC's CSocket, CSocketFile, CArchive in our socket application.
>The test PCs are running Windows 2K and Windows XP SP1. We've built our
[quoted text clipped - 36 lines]
>Any feedback is greatly appreciated.
>-Mary
Hi Mary,
This sounds all too familiar 8-|
I haven't tried using CSockets in MFC 7.x but they were notoriously
buggy in previous versions. CSockets are based on Winsock 1.1 which
had a variety of problems with non-blocking operations. Current
systems use Winsock 2.x, but still emulate 1.1's broken behavior for
applications that request only 1.1 level functionality.
Previous CSocket implementations also had integration problems with
the application framework. A few really egregious bugs were fixed in
MFC 4.2, but CSockets have never been reliable in non-blocking mode.
Although unrelated socket events are supposed to be queued, certain
events are related in subtle ways (open and write, read and close,
ioctl and pretty much anything ... see a POSIX socket description for
details) and Winsock 1.1 (and therefore CSockets) had problems in
certain conditions. One trick that usually works is to make certain
the event is reenabled before returning from the handler - if there is
activity on the socket and the message loop runs before the event gets
reenabled, you may lose further notifications. Also, if the
connection is broken rather than shut down gracefully, you might never
get the close notification so be prepared to interpret "readable but
zero bytes available" as a close event.
I'm sorry I can't give you better advice without seeing code that
demonstrates the problem.
If your schedule can tolerate it, my advice is to go back and use
Winsock 2.0 calls directly. If you can get by with blocking calls (in
a worker thread perhaps), that is the simplest model. For
non-blocking behavior it is easiest to associate a pair of events
(read and write completion) with each socket and use the overlapped IO
versions of Send and Recv. You can then control dispatch normally
with WaitForMultipleObjects() or whatever.
Win9x clients (if you support them) may require service packs to
upgrade Winsock to 2.0 or later. This will be necessary to use the
overlapped IO calls which are implemented by multithreading on Win9x.
Also consider whether you really need to stream archive data or
whether you can pack it into discrete messages. You can always
archive into a CMemFile, detach and send the buffer, and reverse the
process on the receiving end. It's a bit slower than streaming but is
safer IMO. If you really need to stream, you can subclass CArchive
and get access to its internal buffers ... having personally done this
for something similar, I don't recommend it because CArchive is quite
intricate and it's really easy to screw everything up.
George

Signature
for email reply remove "/" from address
MWFox - 10 Aug 2004 01:09 GMT
Hi, George;
Thank you very much for your detailed reply.
> One trick that usually works is to make certain
> the event is reenabled before returning from the handler - if there is
> activity on the socket and the message loop runs before the event gets
> reenabled, you may lose further notifications.
Yes, this loophole had occurred to me as well.
> If your schedule can tolerate it, my advice is to go back and use
> Winsock 2.0 calls directly.
I was afraid I was going to hear that. But, thanks again for your advice.
-Mary