处理WSARecv的立即返回

时间:2017-12-15 01:31:14

标签: c windows winapi winsock winsock2

我正在使用WSARecv实现异步服务器。根据文档,WSARecv可以立即返回0。这仍然最终调用我的完成例程,我再次调用WSARecv来获取其余数据。如果没有更多要抓取的数据,我预计随后对WSARecv的调用将导致其完成例程在第二个参数中接收0个字节。

所以,我打电话给接收:

// ret is zero after this call.
int ret = WSARecv(socket, buf, 1, &RecvBytes, &Flags, overlapped, WorkerRoutine);

最终,这会调用我的完成例程WorkerRoutine,我会对数据进行一些处理,并最终再次调用WSARecv以获取数据的其余部分。

void CALLBACK WorkerRoutine(DWORD recv_error, DWORD bytes, LPWSAOVERLAPPED overlapped, DWORD recv_flags)
{
    int         ret = 0;
    const char* error = 0;
    DWORD       num_bytes = 0;
    DWORD       flags = 0;

    if (recv_error != 0) {
        goto cleanup;
    }

    // This is non-zero on the first call to the completion routine.
    if (bytes == 0) {
        return;
    }

    //... Omitted some data handling code.

    WSABUF buf;
    buf.buf = buffer;
    buf.len = buff_size;
    SecureZeroMemory(overlapped, sizeof(WSAOVERLAPPED));

    // ret is -1 here and WSAGetLastError() == WSA_IO_PENDING
    ret = WSARecv(sock, &buf, 1, &num_bytes, &flags, overlapped, WorkerRoutine);

如果我发送少量数据(< buff_size,例如'你好'),第一次调用WSARecv将立即返回。仍然会调用完成例程,但我不会干净地退出完成例程,并发出另一个WSARecv。我假设第二个完成例程最终会被调用,我可以检查bytes是否为零,如果是{则不退出(不发出对WSARecv的另一个调用)。相反,永远不会执行第二个完成例程。

这是对完成例程如何工作的误解吗?忽略第二个完成例程永远不会触发的事实是否安全?

为了完整起见,我使用Python脚本与我的服务器进行交互:

import socket
import sys
import time

HOST, PORT = "localhost", 9999
data = " ".join(sys.argv[1:])

# Create a socket (SOCK_STREAM means a TCP socket)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
    # Connect to server and send data
    sock.connect((HOST, PORT))
    sock.sendall(data + "\n")

    # Receive data from the server and shut down
    received = sock.recv(1024)
finally:
    sock.close()

print "Sent:     {}".format(data)
print "Received: {}".format(received)

我没有得到"数据结束"因为我是如何与Python互动的?

1 个答案:

答案 0 :(得分:0)

如果WSARecv返回-1并且GetLastError不是WSA_IO_PENDING,则不会出现异步完成。如果您想通过额外的异步操作来处理这种情况,可以使用QueueUserAPC或PostQueuedCompletionStatus自行安排。