Many of you know the original "send()" will not write to the wire the amount of bytes you ask it to. Easily you can use a pointer and a loop to make sure your data is all sent.
However, I don't see how in WSASend() and completion ports work in this case. It returns immediately and you have no control over how much was sent (except in a lpLength which you have access in the routine). How does this get solved?
Do you have to call WSASend() in the routine multiple times in order the get all the data out? Doesn't this seem like a great disadvantage, especially if you want your data out in a particular order and multiple threads access the routines?
When you call
WSASend
with a socket that is associated with anIOCP
and anOVERLAPPED
structure you effectively pass off your data to the network stack to send. The network stack will give you a "completion" once the data buffer that you used is no longer required by the network stack. At that point you are free to reuse or release the memory used for your data buffer.Note that the data is unlikely to have reached the peer at the point the completion is generated and the generation of the completion means nothing more than the network stack has taken ownership of the contents of the buffer.
This is different to how
send
operates. Withsend
in blocking mode the call tosend
will block until the network stack has used all of the data that you have supplied. For calls tosend
in non-blocking mode the network stack takes as much data as it can from your buffer and then returns to you with details of how much it used; this means that some of your data has been used. WithWSASend
, generally, all of your data is used before you are notified.It's possible for an overlapped
WSASend
to fail due to resource limits or network errors. It's unusual to get a failure which indicates that some data has been send but not all. Usually it's all sent OK or none sent at all. However it IS possible to get a completion with an error which indicates that some data has been used but not all. How you proceed from this point depends on the error (temporary resource limit or hard network fault) and how many otherWSASend
s you have pending on that socket (zero or non-zero). You can only try and send the rest of the data if you have a temporary resource error and no other outstandingWSASend
calls for this socket; and this is made more complicated by the fact that you don't know when the temporary resource limit situation will pass... If you ever have a temporary resource limit induced partial send and you DO have otherWSASend
calls pending then you should probably abort the connection as you may have garbled your data stream by sending part of the buffer from thisWSASend
call and then all (or part) of a subsequentWSASend
call.Note that it's a) useful and b) efficient to have multiple
WSASend
calls outstanding on a socket. It's the only way to keep the connection fully utilised. You should, however, be aware of the memory and resource usage implications of having multiple overlappedWSASend
calls pending at one time (see here) as effectively you are handing control of the lifetime of your buffers (and thus the amount of memory and resources that your code uses) to the peer due to TCP flow control issues). SeeSIO_IDEAL_SEND_BACKLOG_QUERY
andSIO_IDEAL_SEND_BACKLOG_CHANGE
if you want to get really clever...WSASend()
on a completion port does not notify you until all of the requested data has been accepted by the socket, or until an error occurs, whichever happens first. It keeps working in the background until all of the data has been accepted (or errored). Until it notifies you, that buffer has to remain active in memory, but your code is free to move on to do other things whileWSASend()
is busy. There is no notification when the data is actually transmitted to the peer. IF you need that, then you have to implement an ACK in your data protocol so the peer can notify you when it receives the data.First regarding
send
. Actually there may happen 2 different things, depending on how the socket is configured.If socket is in so-called blocking mode (the default) - the call to
send
will block the calling thread, until all the input buffer is consumed by the underlying network driver. (Note that this doesn't mean that the data has already arrived at the peer).If the socket is transferred to a non-blocking mode - the call to
send
will fail if the underlying driver may not consume all the input immediately. TheGetLastError
returnsWSAEWOULDBLOCK
in such a case. The application should wait until it may retry to send. Instead of callingsend
in a loop the application should get the notification from the system about the socket state change. Functions such asWSAEventSelect
orWSAAsyncSelect
may be used for this (as well as legacyselect
).Now, with I/O completion ports and
WSASend
the story is somewhat different. When the socket is associated with the completion port - it's automatically transferred to a non-blocking mode.If the call to
WSASend
can't be completed immediately (i.e. the network driver can't consume all the input) - theWSASend
returns an error andGetLastError
returnsSTATUS_PENDING
. This actually means that an asynchronous operation has started but not finished yet**.That is, you should not call
WSASend
repeatedly, because the send operation is already in the progress. When it's finished (either successfully or not) you'll get the notification on the I/O completion port, but meanwhile the calling thread is free to do other things.