2015-05-22 2 views
0

Я играю с Overlapped IO и внезапно узнал, что похоже, что я единственный, кто не может поощрять обратный вызов завершения (чтобы работать) (все претензии были о: он работает и Мне это не нравится).Процедура завершения WSASend никогда не называлась

Идея моего приложения: клиент (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: %d\n", 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: %d\n", 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: %d\n", WSAGetLastError()); 
     freeaddrinfo(result); 
     return 1; 
    } 

    rc = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen); 
    if (rc == SOCKET_ERROR) { 
     printf("bind failed with error: %d\n", WSAGetLastError()); 
     freeaddrinfo(result); 
     closesocket(ListenSocket); 
     return 1; 
    } 

    rc = listen(ListenSocket, 1); 
    if (rc == SOCKET_ERROR) { 
     printf("listen failed with error: %d\n", 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: %d\n", 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; 
} 

Спасибо.

ответ

2

Завершение обратных вызовов завершено во время alertable wait. У вас нет предупреждающего ожидания, поэтому обратные вызовы завершения попадают в очередь, но никогда не получат шанс на запуск.

Изменение WaitForMultipleObjects к петле с WaitForMultipleObjectsEx и Sleep к SleepEx, и передать TRUE в качестве параметра bAlertable.

Это объясняется прямо в the WSASend documentation

Процедура завершения следует тем же правилам, как это предусмотрено для Windows, файл ввода/вывода процедуры завершения. Процедура завершения не будет вызываться до тех пор, пока поток не будет находиться в состоянии ожидающего ожидания, которое может возникнуть, когда вызывается функция WSAWaitForMultipleEvents с параметром fAlertable, установленным в TRUE.

+0

Согласно [MSDN] (https://msdn.microsoft.com/en-us/library/windows/desktop/ms742203%28v=vs.85%29.aspx) Если lpCompletionRoutine не NULL, то параметр hEvent игнорируется и может использоваться приложением для передачи контекстной информации в процедуру завершения. Я так и сделал. В любом случае, как исправить код, пожалуйста? –

+1

Если вы не хотите использовать IOCP, я бы предложил иметь поток, который тратит все свое нерабочее время в аварийном состоянии ожидания. Создайте этот поток [initiate] (https://msdn.microsoft.com/en-us/library/windows/desktop/ms684954%28v=vs.85%29.aspx) все ваши асинхронные операции ввода-вывода. –

+2

^^ что @DavidSchwartz говорит. Где-то у вас должен быть цикл вокруг ожидаемого ожидания. Результат ожидания должен, как правило, игнорировать WAIT_COMPLETION и снова округлить круг. Другие результаты могут быть полезны, например. вы можете сделать ожидание на семафоре ввода, чтобы вы могли отправлять команды серверной системе из других потоков. –