How to make sure that WSASend() will send the data

2019-07-20 13:35发布

问题:

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.

回答1:

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.



回答2:

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;
}


标签: c++ sockets iocp