从未调用过WSASend完成例程

WSASend completion routine has never been called

本文关键字:例程 WSASend 调用      更新时间:2023-10-16

我在玩Overlapped IO,突然发现我似乎是唯一一个不能鼓励Completion回调工作的人(所有声明都是关于:它有效,我不喜欢它(。

我的应用程序的想法是:客户端(telnet localhost 27015(连接到服务器,服务器开始向客户端推送大量数据。我从来没有打过CompletionCallback。

这是代码:

#include <winsock2.h>
#include <ws2tcpip.h>
#include <atomic>
#pragma comment(lib, "ws2_32.lib")
#define DATA_BUFSIZE 16384
class CSync
{
private:
    CRITICAL_SECTION  m_cs;
public:
    CSync()     { ZeroMemory(&m_cs, sizeof(m_cs)); InitializeCriticalSection(&m_cs); }
    ~CSync()    { DeleteCriticalSection(&m_cs);   ZeroMemory(&m_cs, sizeof(m_cs)); }
    inline void Lock()   { EnterCriticalSection(&m_cs); }
    inline void Unlock() { LeaveCriticalSection(&m_cs); }
    inline BOOL WINAPI TryLock() { return TryEnterCriticalSection(&m_cs); }
};
class ScopedLock
{
public:
    ScopedLock(CSync& lock) : m_lock(lock) { m_lock.Lock(); }
    ~ScopedLock() { m_lock.Unlock(); }
private:
    CSync m_lock;
};

class SendServer
{
private:
    SOCKET                socket;
    //  std::atomic<bool>     busy;
    char                  buffer[2][DATA_BUFSIZE];
    CSync                 syncer;
    WSABUF                wsabuf;
    OVERLAPPED            overlapped;
    //  HANDLE                socketEvent;
    std::atomic_flag      busy;
    char                  toBuffer; // 0 or 1
    DWORD                 sent;
public:
    SendServer(SOCKET& sock);
    virtual ~SendServer();
    bool Write(char* buff);
};

static void __stdcall Produce(SendServer *server);
void CALLBACK CompletionCallback(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags);

static bool run = 1;
int main(int argc, char* argv[])
{
    WSADATA wsd;
    struct addrinfo *result = NULL;
    struct addrinfo hints;
    SOCKET ListenSocket = INVALID_SOCKET;
    SOCKET AcceptSocket = INVALID_SOCKET;
    int err = 0;
    int rc;
    // Load Winsock
    rc = WSAStartup((2, 2), &wsd);
    if (rc != 0) {
        printf("Unable to load Winsock: %dn", rc);
        return 1;
    }
    // Make sure the hints struct is zeroed out
    SecureZeroMemory((PVOID)& hints, sizeof(struct addrinfo));
    // Initialize the hints to obtain the 
    // wildcard bind address for IPv4
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;
    rc = getaddrinfo(NULL, "27015", &hints, &result);
    if (rc != 0) {
        printf("getaddrinfo failed with error: %dn", rc);
        return 1;
    }
    ListenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
        //socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (ListenSocket == INVALID_SOCKET) {
        printf("socket failed with error: %dn", WSAGetLastError());
        freeaddrinfo(result);
        return 1;
    }
    rc = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
    if (rc == SOCKET_ERROR) {
        printf("bind failed with error: %dn", WSAGetLastError());
        freeaddrinfo(result);
        closesocket(ListenSocket);
        return 1;
    }
    rc = listen(ListenSocket, 1);
    if (rc == SOCKET_ERROR) {
        printf("listen failed with error: %dn", WSAGetLastError());
        freeaddrinfo(result);
        closesocket(ListenSocket);
        return 1;
    }
    // Accept an incoming connection request
    AcceptSocket = accept(ListenSocket, NULL, NULL);
    if (AcceptSocket == INVALID_SOCKET) {
        printf("accept failed with error: %dn", WSAGetLastError());
        freeaddrinfo(result);
        closesocket(ListenSocket);
        return 1;
    }
    printf("Client Accepted...n");
    SendServer server(AcceptSocket);
    HANDLE h[1];
    h[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&Produce, &server, 0, NULL);
    getchar();
    run = 0;
    WaitForMultipleObjects(1, h, TRUE, INFINITE);
    return 0;
}
void __stdcall Produce(SendServer *server)
{
    char buf[] = "------------------------------------------------------------------------------------------";
    char s = 0;
    while (run) {
        buf[0] = '0' + s++;
        if (s > 9)
            s = 0;
        server->Write(buf);
        Sleep(10);
    }
}
void CALLBACK CompletionCallback(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags)
{
    ((SendServer*)(lpOverlapped->hEvent))->Write(NULL);
}
SendServer::SendServer(SOCKET& sock) : toBuffer(0)
{
    socket = sock;
    ZeroMemory(buffer, DATA_BUFSIZE << 1);
    busy.clear();
}
SendServer::~SendServer()
{
    shutdown(socket, 2);
    closesocket(socket);
}
bool SendServer::Write(char* buff)
{
    ScopedLock lock(syncer);
    int size = strlen(buffer[toBuffer]), toAdd = 0;
    if (buff == NULL) {
        busy.clear();
        SecureZeroMemory(buffer[!toBuffer], DATA_BUFSIZE);
    }
    else {
        toAdd = strlen(buff);
        if (size + toAdd < DATA_BUFSIZE) {
            memcpy_s(buffer[toBuffer] + size, toAdd, buff, toAdd);
            size += toAdd;
            buffer[toBuffer][size] = 0;
            return TRUE;
        }
        else {
            printf("nCan't add anymore!n");
        }
    }
    if (size > 0 && !busy.test_and_set()) {
        wsabuf.buf = (char*)buffer[toBuffer];
        wsabuf.len = size;
        SecureZeroMemory(&overlapped, sizeof OVERLAPPED);
        overlapped.hEvent = this;
        toBuffer = !toBuffer;
        size = WSASend(socket, &wsabuf, 1, &sent, 0, &overlapped, CompletionCallback);
        if (size == 0) {
            //return Write(NULL);
        }
        if (WSA_IO_PENDING != WSAGetLastError()) {
            return FALSE;
        }
    }
    return TRUE;
}

谢谢。

在可报警等待期间调用完成回调。您没有可提醒的等待,所以完成回调会排队,但永远没有机会运行。

WaitForMultipleObjects更改为具有WaitForMultipleObjectsEx的循环,将Sleep更改为SleepEx,并传递TRUE作为bAlertable参数。

WSASend文档对此进行了说明

完成例程遵循与Windows文件I/O完成例程相同的规则在线程处于可报警等待状态之前,不会调用完成例程,例如当调用fAlertable参数设置为TRUE的函数WSAWaitForMultipleEvents时可能会发生这种情况。