3

The documentation for WSARecv() says the following about the lpNumberOfBytesRecvd parameter:

A pointer to the number, in bytes, of data received by this call if the receive operation completes immediately.

Use NULL for this parameter if the lpOverlapped parameter is not NULL to avoid potentially erroneous results. This parameter can be NULL only if the lpOverlapped parameter is not NULL.

I am using IOCP and so the lpOverlapped cannot be NULL, and at the same time WSARecv() can be completed immediately. So if the WSARecv() completed immediately, how am I suppose to know how many bytes were read if lpNumberOfBytesRecvd is NULL?! Or does the documentation just means that I can use NULL for lpNumberOfBytesRecvd but I don't have to?

2 Answers2

2

When an overlapped WSARecv() completes with IO_PENDING or SUCCESS a completion packet is queued to the IOCP see this MSDN article for details.

With Vista or later you can change this by calling SetFileCompletionNotificationModes() for the socket and passing FILE_SKIP_COMPLETION_PORT_ON_SUCCESS (note you can read File as Socket in the docs, the concept of a file handle translates directly to a socket).

If you DO enable completion port skipping then when a WSARecv() returns immediately with data (i.e. a SUCCESS return rather than an IO_PENDING return) then you MUST handle it directly at the WSARecv() call site as you WILL NOT get a completion packet.

Note that enabling "skip completion port" processing is great for reducing context switching, but you DO now need to handle completions in two ways, either directly (whenWSARecv() returns SUCCESS) or in your normal completion handler (when WSARecv() returns an error and the error is IO_PENDING). Whereas before both results generated a completion packet.

So, in answer to your question...

UNLESS you have enabled "skip completion port" processing there is no reason to use the value of lpNumberOfBytesRecvd at the call site for WSARecv() because even if the call returns SUCCESS because data is already available you will STILL get a completion queued to the IOCP which you MUST handle in the usual way.

If and ONLY if you HAVE enabled "skip completion port" processing should you process the data returned by a call to WSARecv() that returns SUCCESS at the point where you get the SUCCESS result.

Len Holgate
  • 21,282
  • 4
  • 45
  • 92
  • So if I enabled "skip completion port", then there is no problem if I pass a non-`NULL` value for `lpNumberOfBytesRecvd` and a non-`NULL` for `lpOverlapped` for the same function call. However, it would be a problem if I did not enable "skip completion port"? –  Feb 27 '15 at 13:25
  • There's never a problem to pass non-null. I'm pretty sure the specific detail of the docs that you refer to is very new. All of my code passes `lpNumberOfBytesRecvd` to all calls to `WSARecv()`, I just don't use the result unless I've enabled "skip port on success" (and that's a run time decision as it needs to be based on there not being any inappropriate LSPs in the chain). – Len Holgate Feb 27 '15 at 13:31
  • But EJP says: "It can give misleading results. See the documentation. You should get the transfer count from the lpOverlapped data only." –  Feb 27 '15 at 13:34
  • Read my answer and the comment again. The only time you should ever USE the value is when there will NOT BE A COMPLETION because you have turned them off by enabling "skip completion port on success" processing. In this scenario the value MUST be correct (if it's not then you're in big trouble and so is MS as the API can't be used!). You can ALWAYS pass `lpNumberOfBytesRecvd` but there's NEVER a reason to look at it unless you have turned on "skip completion port on success" processing. This really is NOT that complex. – Len Holgate Feb 27 '15 at 13:51
  • I understood your answer, and this is why I did not understand why EJP would say: "It can give misleading results". –  Feb 27 '15 at 13:56
  • If you were to try and use the value in any other situation then the value could be incorrect and therefore is not to be relied on. However to be able to build code that can optionally enable "skip completion port on success" processing based on run time decisions as to the suitability of the optimisation for the platform on which the code is running you need to be able to pass the address of a variable to capture the bytes recvd on all calls but only use it if your code is set up to guarantee that the value is valid. So it's down to you as to whether it could give misleading results. – Len Holgate Feb 27 '15 at 14:02
  • I am assuming that `WSARecv()` can be used instead of `recv()` (for non-Overlapped IO). So I guess when they say: "This parameter can be NULL only if the lpOverlapped parameter is not NULL", this means that when `lpOverlapped` is `NULL` then `WSARecv()` is used in non-Overlapped mode, and so `lpNumberOfBytesRecvd` cannot be `NULL` because it is the only way the number of bytes received can be returned, is this correct? –  Feb 27 '15 at 14:14
  • Well, as the documentation page that you're referring to says "If both lpOverlapped and lpCompletionRoutine are NULL, the socket in this function will be treated as a nonoverlapped socket.". So yes. – Len Holgate Feb 27 '15 at 14:18
0

The documentation is just stating some rules for the utilization of NULL on those parameters, its not stating that you must use NULL in any situation whatsoever. You can always have those parameters properly pointing to a valid variable according to your needs.

Havenard
  • 27,022
  • 5
  • 36
  • 62
  • So it is not a problem if I pass a non-`NULL` value for `lpNumberOfBytesRecvd` and a non-`NULL` for `lpOverlapped` for the same function call? –  Feb 27 '15 at 04:01
  • @user4592590 It can give misleading results. See the documentation. You should get the transfer count from the `lpOverlapped` data only. – user207421 Feb 27 '15 at 04:07
  • 1
    @EJP But the `OVERLAPPED` structure does not have a member for the number of bytes transferred. –  Feb 27 '15 at 04:11