Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

CAsyncSocket::OnReceive() notifications missing

680 views
Skip to first unread message

danceswi...@hotmail.com

unread,
Mar 14, 2005, 10:04:16 PM3/14/05
to
I'm having a problem where a transfer is stalling because OnReceive()
is not being called when there is data available. I've got debug
routines that confirm that for each OnReceive() call, I am making one
call to CAsyncSocket::Receive(). Should this not ensure that data will
not stall in the receive buffer? Furthermore, I have a timer (the CWnd
timer) that triggers every 5 seconds. In the timer handler I have
socket.Receive(&c, 1, MSG_PEEK). When the handler is triggered, the
notifications start up again. Can anyone think of a reason why this is
happening if Receive() is called with each OnReceive() notification?
I've been told that the MSDN documentation for FD_READ notifications
handled by WSAAsyncSelect() would tell me all I need to know about
criteria for OnReceive() calls. On the page I'm looking at it tells me
that if a recv() or recvfrom() is called, but doesn't get all the data,
an FD_READ will be reissued. It seems to me that I'm doing just that.
Am I? So why do I need this latent Receive() call with MSG_PEEK to
restart the notification process?
This problem is clearly related (indirectly) to timing. If I put
TRACEs everywhere, or other things that slow my program down, I don't
experience the problem. It's as if the problem only occurs when I read
the data fast enough.

Scott McPhillips [MVP]

unread,
Mar 14, 2005, 11:31:52 PM3/14/05
to

One possibility would be if you pump messages while in a OnReceive call.
For example, calling MessageBox does this. If the timing is just
wrong, pumping messages while in OnReceive will cause OnReceive to
reenter. Depending on your code, the reentered version might never call
Receive, so notifications stop. Check for OnReceive reentry.

--
Scott McPhillips [VC++ MVP]

danceswi...@hotmail.com

unread,
Mar 15, 2005, 1:00:03 AM3/15/05
to
Ah yes, that tricky message pump. It would make my code so much simpler
if I could just popup a message box w/out having to worry about what
blow up beneath it, but that's not the problem. The debug version makes
sure that each entrance to OnReceive() is followed by an exit, and it
asserts if it enters twice before leaving, so that's not it.

Michael K. O'Neill

unread,
Mar 15, 2005, 1:31:26 AM3/15/05
to
FD_READ notifications don't work like a message that notifies your
application that new data has arrived and is ready for reading. You get a
FD_READ notification only after you have tried to read but failed with
WSAEWOULDBLOCK. So, to get new FD_READ notifications, you need to start the
whole process with an attempt to read, which might successfully return
everything in the Winsock buffer (in which case you're done and you won't
get another FD_READ) or might not (WSAGetLastError returns WSAEWOULDBLOCK
and you'll get FD_READ later).

Are you certain that your last call to Receive is returning SOCKET_ERROR
with a WSAGetLastError of WSAEWOULDBLOCK? If it's not, then you won't be
getting any further FD_READs unless you somehow re-trigger the entire
mechanism.

Sometimes you can be too smart about recv's: you know exactly how much data
you expect to receive and you ask only for that precise amount. This causes
a problem in that a call to recv will not result in an error of
WSAEWOULDBLOCK, and you will not get a FD_READ notification when new data
arrives later.

<danceswi...@hotmail.com> wrote in message
news:1110853800.5...@z14g2000cwz.googlegroups.com...

Scott McPhillips [MVP]

unread,
Mar 15, 2005, 6:42:00 AM3/15/05
to
Michael K. O'Neill wrote:
> FD_READ notifications don't work like a message that notifies your
> application that new data has arrived and is ready for reading. You get a
> FD_READ notification only after you have tried to read but failed with
> WSAEWOULDBLOCK.

That's not correct. From MSDN WSAAsyncSelect:
"With these semantics, an application need not read all available data
in response to an FD_READ message—a single recv in response to each
FD_READ message is appropriate."

Michael K. O'Neill

unread,
Mar 15, 2005, 10:41:22 AM3/15/05
to
"Scott McPhillips [MVP]" <org-dot-mvps-at-scottmcp> wrote in message
news:%23WA4YSV...@TK2MSFTNGP15.phx.gbl...

You're reading only part of the docs. (For the benefit of other, see
http://msdn.microsoft.com/library/en-us/winsock/winsock/recv_2.asp ). In
the section you quoted, the reason that the app continues to get FD_READs is
because a previous WSAEWOULDBLOCK is still valid. This is explained
slightly above the section you quoted, as follows (with a few editorial
comments and capitalizations inserted):

"For FD_READ, FD_OOB, and FD_ACCEPT events, message posting is
level-triggered [ed: as opposed to edge-triggered; this is important]. This
means that if the reenabling routine is called and the relevant condition is
STILL MET AFTER THE CALL [ed: important], a WSAAsyncSelect message is posted
to the application. This allows an application to be event-driven and not be
concerned with the amount of data that arrives at any one time. Consider the
following sequence:

"1. Network transport stack receives 100 bytes of data on socket s and
causes Windows Sockets 2 to post an FD_READ message.
"2. The application issues recv( s, buffptr, 50, 0) to read 50 bytes.
"3. Another FD_READ message is posted because there is STILL data to be
read.

"With these semantics, an application need not read all available data in
response to an FD_READ message—a single recv in response to each FD_READ

message is appropriate. If an application issues multiple recv calls in
response to a single FD_READ, it can receive multiple FD_READ messages. Such
an application can require disabling FD_READ messages before starting the
recv calls by calling WSAAsyncSelect with the FD_READ event not set."

So, FD_READ is level-triggered as opposed to FD_QOS (and others) which is
edge-triggered. To see the distinction, here's what the docs say about
FD_QOS:

"The FD_QOS and FD_GROUP_QOS events are considered edge triggered. A message
will be posted exactly once when a quality of service CHANGE [ed] occurs.
Further messages will not be forthcoming until either the provider detects a
further CHANGE [ed] in quality of service or the application renegotiates
the quality of service for the socket."

But you're correct that my post might have been so general that it was
misleading. It should have stated that an FD_READ notification is sent
"only after you have tried to read but failed with WSAEWOULDBLOCK, or after
a succesful read but only if the conditions that caused a prior
WSAEWOULDBLOCK are still valid".


danceswi...@hotmail.com

unread,
Mar 15, 2005, 12:26:01 PM3/15/05
to

Michael K. O'Neill wrote:
> "Scott McPhillips [MVP]" <org-dot-mvps-at-scottmcp> wrote in message
> news:%23WA4YSV...@TK2MSFTNGP15.phx.gbl...
> > Michael K. O'Neill wrote:
> > > FD_READ notifications don't work like a message that notifies
your
> > > application that new data has arrived and is ready for reading.
You get
> a
> > > FD_READ notification only after you have tried to read but failed
with
> > > WSAEWOULDBLOCK.
> >
> > That's not correct. From MSDN WSAAsyncSelect:
> > "With these semantics, an application need not read all available
data
> > in response to an FD_READ message-a single recv in response to

These criteria are *very* slippery, but I THINK I get what you are
saying. If 100 bytes arrive, but after a read some of those 100 bytes
are still there, another notification will be posted. However, if all
those 100 bytes are read, and no other read attempts are made, no more
FD_READ notifications are posted, is that right?

>
> "With these semantics, an application need not read all available
data in

> response to an FD_READ message-a single recv in response to each

But this I don't understand. The "if the conditions that caused a
prior WSAEWOULDBLOCK are still valid" is confusing. It appears to
conflict with "after a succesful read". There's only one condition I'm
aware of that would cause a WSAEWOULDBLOCK on a read, and that's if
there is nothing available. So if a read is successful (assuming that
"successful" means reading bytes, without error) and empties the
receive buffer, then a notification would be posted since the condition
that causes WSAEWOULDBLOCK (nothing in the buffer) is valid. Naturally,
a read would be attempted, WSAEWOULDBLOCK would be returned, which you
say is sufficient to cause a future notification when new data comes
in. This is obviously not what's happening, since my transfers stall.

Michael K. O'Neill

unread,
Mar 15, 2005, 11:34:44 PM3/15/05
to
I did some web searching in an effort to find useful information to help
resolve this issue.

Unfortunately, I could not find a definitive answer.

So, the upshot is that while I *think* I'm correct, I also found enough
contradictory information so that I could be persuaded that I'm wrong.

For example, look at pages 20-22 of the following pdf file:
http://course.ie.cuhk.edu.hk/~ieg4180/lecture/Lecture%202004-2005/04-message_driven_sockets_programming.pdf .
It shows examples of level triggering and edge triggering, for FD_READ and
FD_WRITE, that act exactly the way that Scott McPhillips said, and are
exactly opposite to the way that I understand it.

Sorry for the controversy.

Mike


<danceswi...@hotmail.com> wrote in message
news:1110907561.0...@l41g2000cwc.googlegroups.com...

danceswi...@hotmail.com

unread,
Mar 16, 2005, 3:49:32 PM3/16/05
to

Michael K. O'Neill wrote:
> I did some web searching in an effort to find useful information to
help
> resolve this issue.
>
> Unfortunately, I could not find a definitive answer.
>
> So, the upshot is that while I *think* I'm correct, I also found
enough
> contradictory information so that I could be persuaded that I'm
wrong.
>
> For example, look at pages 20-22 of the following pdf file:
>
http://course.ie.cuhk.edu.hk/~ieg4180/lecture/Lecture%202004-2005/04-message_driven_sockets_programming.pdf
.
> It shows examples of level triggering and edge triggering, for
FD_READ and
> FD_WRITE, that act exactly the way that Scott McPhillips said, and
are
> exactly opposite to the way that I understand it.
>
> Sorry for the controversy.
>
> Mike

It's hard to believe that information so crucial would be so
unavailable. I think the last time I tried to Receive() until I got a
WSAEWOULDBLOCK, I had problems. Then I was told that anytime Receive()
is called, a notification would be posted the next time there is data
available (or immediately, if there is data left).

So I'm back to asking anyone if there is a better solution than my
workaround? This workaround being a CWnd timer that triggers every few
seconds, and does a MSG_PEEK on the socket, which wakes up the sleeping
socket.

Scott McPhillips [MVP]

unread,
Mar 16, 2005, 7:40:16 PM3/16/05
to
danceswi...@hotmail.com wrote:
> It's hard to believe that information so crucial would be so
> unavailable. I think the last time I tried to Receive() until I got a
> WSAEWOULDBLOCK, I had problems. Then I was told that anytime Receive()
> is called, a notification would be posted the next time there is data
> available (or immediately, if there is data left).
>
> So I'm back to asking anyone if there is a better solution than my
> workaround? This workaround being a CWnd timer that triggers every few
> seconds, and does a MSG_PEEK on the socket, which wakes up the sleeping
> socket.

I have built lots of CAsyncSockets, both heavily and lightly loaded, and
have not ever had this problem. The symptoms you have correspond to
missing one FD_READ notification (somehow). If you miss one the
back-and-forth handshake stops.

(One FD_READ should be generated after every call to Read.
WSAEWOULDBLOCK should not occur, since OnReceive is only called if data
is available.)

Since you ruled out reentry to OnReceive, all I can suggest is to
simplify your code to a drastically cut down test version in an attempt
to isolate the source of the problem.

danceswi...@hotmail.com

unread,
Mar 16, 2005, 10:21:23 PM3/16/05
to

Simplifying the code isn't possible. It's already simple enough
that I can be sure that for every OnReceive() call a Receive() is also
called. I guess I could look into the possibility that I'm trying to
Receive() 0 bytes (if such a thing could jam the socket up) but I doubt
that's it. I guess it's time to concede that I have more important
things to attend to, and accept my workaround. Thanks anyway.

Singh@discussions.microsoft.com Harpreet Singh

unread,
May 4, 2005, 11:03:07 PM5/4/05
to
I was going through similar problems like our "dancer" friend . Looked closly
at Michaels's and Scott's posts and answer is right there. Actually if you
try to read we a preconcieved notion about the size of the incoming packet
.i.e . you tailor the size of buffer according to your size of request
WSAEWOULDBLOCK never happens and in turn FD_READ also is not posted to MFC
message queue. Actually what i feel that if the size of the buffer and size
of packet match somehow during a receive FD_READ notifications stop coming.
This also explains some of the other posts where people are having a problem
with FD_READ succeeding some times and failing at others.
0 new messages