How to make sure that WSASend() will send the data?
Asked Answered
S

2

2

WSASend() will return immediately whether the data will be sent or not. But how to make sure that data will be sent, for example I have a button in my UI that will send "Hello World!" when pressed. Now I want to make sure that when the user click on this button the "Hello World!" will be sent at some point, but WSASend() could return WSAEWOULDBLOCK indicating that data will not be sent, so should I enclose WSASend() in a loop that does not exit until WSASend() returns 0 (success).

Note: I am using IOCP.

Supramolecular answered 26/2, 2015 at 20:27 Comment(7)
Since you are using IOCP, you should have already been aware that (like any other IOCP operation) WSASend() will take the entire data and notify you with an IOCP completion status when the send is fully finished, even if the send completes immediately (unless you use SetFileCompletionNotificationModes(FILE_SKIP_COMPLETION_PORT_ON_SUCCESS) to disable it). WSAEWOULDBLOCK does not apply to IOCP, ERROR_IO_PENDING/WSA_IO_PENDING is used instead.Repugnant
Right, asynchronous operations (whether IOCP, overlapped event, or completion callback model) use WSA_IO_PENDING rather than WSAEWOULDBLOCK.Zarf
WSAEWOULDBLOCK applies only to nonblocking sockets.Chauvin
But MSDN says about WSAEWOULDBLOCK: "Overlapped sockets: There are too many outstanding overlapped I/O requests."Supramolecular
That should not apply to you since hopefully you will only have one outstanding IO at a time. This is the standard way to go about things.Chauvin
@Chauvin So when I press the button, I should disable its functionality until I get a notification that the operation was successful?Supramolecular
You could enqueue the data into your own data structure. That's what I'd do. On the other hand this would be a tolerable case for multiple outstanding sends since it would be unexpected to have a great number of them.Chauvin
S
3

should I enclose WSASend() in a loop that does not exit until WSASend() returns 0 (success)

Err.. NO!

Have the UI issue an overlapped WSASend request, complete with buffer/s and OVERLAPPED/s. If, by some miracle, it does actually return success immedately, (and I've never seen it), you're good.

If, (when:), it returns WSA_IO_PENDING, you can do nothing in your UI button-handler because GUI event-handlers cannot wait. Graphical UI's are state-machines - you must exit the button-handler and return to the message input queue in prompt manner. You can do some GUI stuff, if you want. Maybe disable the 'Send' button, or add some 'Message sent' text to a memo component. That's about it - you must then exit.

Some time later, the successful completion notification, (or failure notification), will get posted to the IOCP completion queue and a handler thread will get hold of it. Use PostMessage, QueueUserAPC or similar inter-thread comms mechanism to signal 'something', (eg. the buffer object used in the original WSASend), back to the UI thread so that it can take action/s on the returned result, eg. re-enabling the 'Send' button.

Yes, it can be seen as messy, but it is the only way you can do it that will work well.

Other approaches - polling loops, Application.DoEvents, timers etc are all horrible bodges.

Selfridge answered 26/2, 2015 at 20:44 Comment(2)
"or failure notification" Can an IO operation fail after it has been initiated?Supramolecular
@Supramolecular sure - it's asynchronous.Selfridge
O
1

Overlapped Socket I/O

If an overlapped operation completes immediately, WSASend returns a value of zero and the lpNumberOfBytesSent parameter is updated with the number of bytes sent. If the overlapped operation is successfully initiated and will complete later, WSASend returns SOCKET_ERROR and indicates error code WSA_IO_PENDING.

...

The error code WSA_IO_PENDING indicates that the overlapped operation has been successfully initiated and that completion will be indicated at a later time. Any other error code indicates that the overlapped operation was not successfully initiated and no completion indication will occur.

...

So as demonstrated in docs, you don't need to enclose in a loop, just check for a SOCKET_ERROR and if the last error is not equal to WSA_IO_PENDING, everything is fine:

rc = WSASend(AcceptSocket, &DataBuf, 1,
             &SendBytes, 0, &SendOverlapped, NULL);
if ((rc == SOCKET_ERROR) &&
    (WSA_IO_PENDING != (err = WSAGetLastError()))) {
    printf("WSASend failed with error: %d\n", err);
    break;
}
Otto answered 26/2, 2015 at 20:45 Comment(1)
Yes - I just edit my answer from WSAEWOULDBLOCK to WSA_IO_PENDING. Thanks, have an upvote:)Selfridge

© 2022 - 2024 — McMap. All rights reserved.