Winsock非阻塞重叠IO仍然阻塞

时间:2014-09-28 04:46:44

标签: sockets network-programming winsock2

我正在尝试编写一个简单的winsock客户端,将数据发送到 使用非阻止 重叠 IO的winsock服务器。问题是WSASend调用阻塞了。客户端代码如下所示

// Initialize Winsock
WSAStartup(MAKEWORD(2, 2), &wsaData);

// Create a SOCKET for connecting to server
ConnectSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);

// Connect to server.
WSAConnect(ConnectSocket, ai_addr, ai_addrlen, 0, 0, 0, 0);

// Make the socket non-blocking
u_long iMode = 1;
ioctlsocket(ConnectSocket, FIONBIO, &iMode);

// Send the data
WSAOVERLAPPED SendOverlapped{};
SendOverlapped.hEvent = WSACreateEvent();
WSASend(ConnectSocket, &DataBuf, 1, &SendBytes, 0, &SendOverlapped, 0);

我通过ioctlsocket函数使套接字无阻塞,并且我向WSA_FLAG_OVERLAPPED函数提供了WSASocket标志。我还为lpOverlapped函数提供了WSASend参数。但是,对WSASend的调用仍在阻止。我在这里错过了什么吗?

请原谅上述代码中没有错误检查,只是为了解决这个问题的目的。

澄清:在我看来呼叫阻塞的原因是这样的 - 我在一个循环中调用WSASend函数2560次,每次在循环中发送4MB。所有数据传输到服务器后,循环在16秒内完成。如果它是非阻塞的,我会期望循环更快地完成。正如所料,WSASend函数确实返回ERROR_IO_PENDING

以下是完整的客户端代码

// Client.cpp
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <stdio.h>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <ctime>

#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")

#define DEFAULT_BUFLEN 4 * 1024 * 1024
#define DEFAULT_PORT "27015"

int __cdecl main(int argc, char **argv)
{
    // Initialize Winsock
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed with error: %d\n", iResult);
        return 1;
    }

    // Resolve the server address and port
    addrinfo *result = NULL;
    addrinfo hints{};
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    iResult = getaddrinfo("127.0.0.1", DEFAULT_PORT, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed with error: %d\n", iResult);
        WSACleanup();
        return 1;
    }

    // Create a SOCKET for connecting to server
    SOCKET ConnectSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
    if (ConnectSocket == INVALID_SOCKET) {
        printf("socket failed with error: %ld\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }

    // Connect to server.
    iResult = WSAConnect(ConnectSocket, result->ai_addr, result->ai_addrlen, 0, 0, 0, 0);
    freeaddrinfo(result);
    if (ConnectSocket == INVALID_SOCKET) {
        printf("Unable to connect to server!\n");
        WSACleanup();
        return 1;
    }

    // Make the socket non-blocking
    u_long iMode = 1;
    iResult = ioctlsocket(ConnectSocket, FIONBIO, &iMode);
    if (iResult != NO_ERROR) {
        printf("ioctlsocket failed with error: %ld\n", iResult);
        WSACleanup();
        return 1;
    }

    // Prepare the buffer
    char *sendbuf = new char[DEFAULT_BUFLEN];
    for (int i = 0; i < DEFAULT_BUFLEN; ++i)
        sendbuf[i] = 'a';
    WSABUF DataBuf;
    DWORD SendBytes = 0;
    DataBuf.buf = sendbuf;
    DataBuf.len = DEFAULT_BUFLEN;

    // Send the buffer in a loop
    int loopCount = 2560;
    WSAOVERLAPPED* SendOverlapped = (WSAOVERLAPPED*)calloc(loopCount, sizeof(WSAOVERLAPPED));
    clock_t start = clock();
    for (int i = 0; i < loopCount; ++i)
    {
        SendOverlapped[i].hEvent = WSACreateEvent();
        iResult = WSASend(ConnectSocket, &DataBuf, 1, &SendBytes, 0, SendOverlapped + i, 0);
        if (iResult == SOCKET_ERROR)
        {
            if (ERROR_IO_PENDING == WSAGetLastError())
            {
                continue;
            }           

            printf("send failed with error: %d\n", WSAGetLastError());
            closesocket(ConnectSocket);
            WSACleanup();
            return 1;
        }
    }

    std::cout << "initiating send data took " << clock() - start << " ms" << std::endl;

    // Wait for all the events to be signalled
    for (int i = 0; i < loopCount; ++i)
    {
        iResult = WSAWaitForMultipleEvents(1, &SendOverlapped[i].hEvent, TRUE, INFINITE, TRUE);
        if (iResult == WSA_WAIT_FAILED) {
            printf("WSAWaitForMultipleEvents failed with error: %d\n", WSAGetLastError());
            closesocket(ConnectSocket);
            WSACleanup();
            return 1;
        }

        DWORD Flags = 0;
        BOOL result = WSAGetOverlappedResult(ConnectSocket, SendOverlapped + i, &SendBytes, FALSE, &Flags);
        if (result == FALSE) {
            printf("WSASend failed with error: %d\n", WSAGetLastError());
            break;
        }
    }

    std::cout << "actual send data took " << clock() - start << " ms";

    // shutdown the connection since no more data will be sent
    iResult = shutdown(ConnectSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        printf("shutdown failed with error: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }

    // cleanup
    closesocket(ConnectSocket);
    WSACleanup();
    ////free(SendOverlapped);
    return 0;
}

这是服务器端代码

// Server.cpp
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <stdio.h>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>

#pragma comment (lib, "Ws2_32.lib")

#define DEFAULT_BUFLEN 4 * 1024 * 1024
#define DEFAULT_PORT "27015"

int __cdecl main(void)
{
    // Initialize Winsock
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed with error: %d\n", iResult);
        return 1;
    }

    // Resolve the server address and port
    addrinfo hints{};
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;
    addrinfo *result = NULL;
    iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed with error: %d\n", iResult);
        WSACleanup();
        return 1;
    }

    // Create a SOCKET for connecting to server
    SOCKET ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (ListenSocket == INVALID_SOCKET) {
        printf("socket failed with error: %ld\n", WSAGetLastError());
        freeaddrinfo(result);
        WSACleanup();
        return 1;
    }

    // Setup the TCP listening socket
    iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
    if (iResult == SOCKET_ERROR) {
        printf("bind failed with error: %d\n", WSAGetLastError());
        freeaddrinfo(result);
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    freeaddrinfo(result);

    iResult = listen(ListenSocket, SOMAXCONN);
    if (iResult == SOCKET_ERROR) {
        printf("listen failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    // Accept a client socket
    SOCKET ClientSocket = accept(ListenSocket, NULL, NULL);
    if (ClientSocket == INVALID_SOCKET) {
        printf("accept failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    // No longer need server socket
    closesocket(ListenSocket);

    // Receive until the peer shuts down the connection
    char* recvbuf = new char[DEFAULT_BUFLEN];
    do {
        iResult = recv(ClientSocket, recvbuf, DEFAULT_BUFLEN, 0);
        if (iResult > 0) {
            printf("Bytes received: %d\n", iResult);
        }
        else if (iResult == 0)
            printf("Connection closing...\n");
        else  {
            printf("recv failed with error: %d\n", WSAGetLastError());
            closesocket(ClientSocket);
            WSACleanup();
            return 1;
        }

    } while (iResult > 0);

    // shutdown the connection since we're done
    iResult = shutdown(ClientSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        printf("shutdown failed with error: %d\n", WSAGetLastError());
        closesocket(ClientSocket);
        WSACleanup();
        return 1;
    }

    // cleanup
    closesocket(ClientSocket);
    WSACleanup();

    return 0;
}

1 个答案:

答案 0 :(得分:-1)

WSASend来电被阻止,因为它完成得太快了。有人会认为4MB传输足够大,需要异步处理但事实并非如此。当我在服务器端插入1 ms的睡眠时,我可以看到客户端异步处理,并且循环在数据传输之前就结束了。

相关问题