2012-08-30 152 views
1

我需要你的幫助,因爲我必須在C++中創建兩個控制檯應用程序:一個能夠儘可能多地向服務器發送字符串的客戶端(爲了發送座標) 。我成功地創建了一個阻塞套接字,但由於我必須在每幀調用腳本的開發平臺(3D VIA Virtools)中集成它,因此除了使用異步套接字之外,沒有其他解決方案。服務器/客戶端TCP異步(winsock)// FD_WRITE問題

*我的問題是,我只能發送字符串一次,之後我沒有收到FD_WRITE了... *

這開始讓我瘋狂所以任何幫助將不勝感激(我在編程初學者),在此先感謝大家誰都會覺得我的問題

這裏是我的代碼有關一點點,

服務器

#include <winsock2.h> 
#include <Windows.h> 
#include <conio.h> 

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

#define SOCKET_ERRNO WSAGetLastError() 
#define ADDRESS "127.0.0.1" 
#define PORT 1234 

static SOCKET ListenFirstFreePort() 
{ 
    struct sockaddr_in addr; 
    int len = sizeof(addr);  
    SOCKET hSocket; 

    // Create socket 
    hSocket = socket(PF_INET, SOCK_STREAM, 0); 
    if(hSocket == INVALID_SOCKET) 
    { 
     printf("socket() error %d\n", SOCKET_ERRNO); 
     exit(1); 
    } 

    // Connexion setting for local connexion 
    addr.sin_family = AF_INET ; 
    addr.sin_addr.s_addr = inet_addr(ADDRESS); 
    addr.sin_port = htons (PORT); 

    // bind socket 
    if (bind(hSocket, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR) 
    { 
     printf("bind() error %d\n", SOCKET_ERRNO); 
     exit(1); 
    } 

    // listen 
    if (listen(hSocket, 100) == SOCKET_ERROR) 
    { 
     printf("listen() error %d\n", SOCKET_ERRNO); 
     exit(1); 
    } 

    return hSocket; 
} 



void main() 
{ 
    WSADATA stack_info; 
    SOCKET ahSocket[2]; 
    WSAEVENT ahEvents[2]; 
    DWORD dwEvent; 
    WSANETWORKEVENTS NetworkEvents; 
    int rc; 

    // Initialize Winsock 
    WSAStartup(MAKEWORD(2,0), &stack_info) ; 

    // Create events 
    ahEvents[0] = WSACreateEvent(); 
    ahEvents[1] = WSACreateEvent(); 


    // Create listening socket 
    ahSocket[0] = ListenFirstFreePort(); 
    rc = WSAEventSelect(ahSocket[0], ahEvents[0], FD_ACCEPT); 
    if(rc == SOCKET_ERROR) 
    { 
     printf("WSAEventSelect() error %d\n", SOCKET_ERRNO); 
     exit(1); 
    } 

    while (TRUE) 
    { 
     // Waiting for so;ething to happen 
     // Basically we'll firstly receive the connexion of the client socket 
     // and then we'll be notificated when there will be some data to read 

     // look for events 
     dwEvent = WSAWaitForMultipleEvents(2, ahEvents, FALSE, WSA_INFINITE, FALSE); 

     switch (dwEvent) 
     { 
     case WSA_WAIT_FAILED: 
     printf("WSAEventSelect: %d\n", WSAGetLastError()); 
     break; 
     case WAIT_IO_COMPLETION: 
     case WSA_WAIT_TIMEOUT: 
     break; 

     default: 

     //if there is one dwEvent-WSA_WAIT_EVENT_0 has to be substracted so as to dwEvent correspond to the index of the concerned socket 
     dwEvent -= WSA_WAIT_EVENT_0; 

     // enumeration of the events on the socket[dwEvent] 
     if (SOCKET_ERROR == WSAEnumNetworkEvents(ahSocket[dwEvent], ahEvents[dwEvent], &NetworkEvents)) 
     { 
      printf("WSAEnumNetworkEvent: %d lNetworkEvent %X\n", 
       WSAGetLastError(), NetworkEvents.lNetworkEvents); 
      NetworkEvents.lNetworkEvents = 0; 
     } 
     else 
     { 

      if (FD_CLOSE & NetworkEvents.lNetworkEvents) 
      { 

       printf("FD_CLOSE ok (dwEvent=%d)\n", dwEvent); 
       printf("press a key to exit\n"); 
       getch(); // require conio.h 

       WSACloseEvent(ahEvents[0]); 
       WSACloseEvent(ahEvents[1]); 
       exit(0); 
      } 
      if (FD_READ & NetworkEvents.lNetworkEvents) 
      { 
       char szBuffer[256]; int cbRecv; 

       // Only the second socket expect to receive data 
       printf("FD_READ ok (dwEvent=%d)\n", dwEvent); 

       // read data 
       cbRecv = recv(ahSocket[dwEvent], szBuffer, sizeof(szBuffer) - 1, 0); 
       if(cbRecv <= 0) 
       { 
        printf("recv() error %d\n", SOCKET_ERRNO); 
        exit(1); 
       } 

       // On ecrit ce paquet (On remet le 0 au cas ou le paquet 
       // ait ete coupe en 2 - je sais, ca n'arrivera jamais en local) 
       // we put the 0 in case it has been cut - unlikey to happen on local network 
       szBuffer[cbRecv] = 0; 

       // write data in console window 
       printf("socket %d : '%s'\n", dwEvent, szBuffer); 
      } 
     } 
     if (FD_ACCEPT & NetworkEvents.lNetworkEvents) 
     { 
      struct sockaddr_in addrAccept; 
      int lenAccept; 
      lenAccept = sizeof(addrAccept); 

      // we should have dwEvent=0 
      printf("accept ok (dwEvent=%d)\n", dwEvent); 

      // we create another socket to accept the connexion with the client socket 
      ahSocket[1] = accept(ahSocket[dwEvent], (struct sockaddr *)&addrAccept, &lenAccept); 

      // we want to be informed on when we'll be able read data from it 
      rc = WSAEventSelect(ahSocket[1], ahEvents[1], FD_READ|FD_CLOSE ); 
      if(rc == SOCKET_ERROR) 
      { 
       printf("WSAEventSelect() error %d\n", SOCKET_ERRNO); 
       exit(1); 
      } 
     } 
     } 
    } 
} 

客戶

#include <winsock2.h> 
#include <conio.h> 
#include <time.h> 

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

#define SOCKET_ERRNO WSAGetLastError() 
#define ADDRESS "127.0.0.1" 
#define PORT 1234 

SOCKET ConnectToPort() 
{ 
    struct sockaddr_in addr; 
    SOCKET hSocket; 
    u_long arg; int err; 

    // Create socket 
    hSocket = socket(PF_INET, SOCK_STREAM, 0); 
    if(hSocket == INVALID_SOCKET) 
    { 
     printf("socket() error %d\n", SOCKET_ERRNO); 
     exit(1); 
    } 

    // Connexion setting for local connexion 
    addr.sin_family = AF_INET ; 
    addr.sin_addr.s_addr = inet_addr(ADDRESS); 
    addr.sin_port = htons (PORT); 

    // Connect 
    if(connect(hSocket, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR) 
    { 
     // As we are in non-blocking mode we'll always have the error 
     // WSAEWOULDBLOCK whichis actually not one 
     if(SOCKET_ERRNO != WSAEWOULDBLOCK) 
     { 
     printf("connect() error (%d)\n", SOCKET_ERRNO); 
     exit(1); 
     } 
    } 

    return hSocket; 
} 



void main() 
{ 
    int initClockTime; 
    WSADATA stack_info; 
    SOCKET ahSocket[1]; 
    WSAEVENT ahEvents[1]; 
    DWORD dwEvent; 
    WSANETWORKEVENTS NetworkEvents; 
    int rc; 

    // Initialize Winsock 
    WSAStartup(MAKEWORD(2,0), &stack_info) ; 

    // Create event 
    ahEvents[0] = WSACreateEvent(); 

    // Create and connect a socket on the server socket 
    ahSocket[0]= ConnectToPort(); 

    // not sure if I have to use or not 
    /*u_long arg = 1; 
    ioctlsocket(ahSocket[0] , FIONBIO, &arg);*/ 

    // the application wants to receive notification of a completed connection 
    rc = WSAEventSelect(ahSocket[0], ahEvents[0], FD_CONNECT ); 
    if(rc == SOCKET_ERROR) 
    { 
     printf("WSAEventSelect() error %d\n", SOCKET_ERRNO); 
     exit(1); 
    } 

    while (TRUE) 
    { 
     // look for events 
     dwEvent = WSAWaitForMultipleEvents(1, ahEvents, FALSE, 1000, FALSE); 

     switch (dwEvent) 
     { 
     case WSA_WAIT_FAILED: 
     printf("WSAEventSelect: %d\n", WSAGetLastError()); 
     break; 
     case WAIT_IO_COMPLETION: 
     case WSA_WAIT_TIMEOUT: 
     break; 

     default: 

     printf("while\n"); 

     //if there is one dwEvent-WSA_WAIT_EVENT_0 has to be substracted so as to dwEvent correspond to the index of the concerned socket 
     dwEvent -= WSA_WAIT_EVENT_0; 

     // enumeration of the events on the socket[dwEvent] 
     if (SOCKET_ERROR == WSAEnumNetworkEvents(ahSocket[dwEvent], ahEvents[dwEvent], &NetworkEvents)) 
     { 
      printf("WSAEnumNetworkEvent: %d lNetworkEvent %X\n", WSAGetLastError(), NetworkEvents.lNetworkEvents); 
      NetworkEvents.lNetworkEvents = 0; 
     } 
     else 
     { 


      if (FD_CONNECT & NetworkEvents.lNetworkEvents) 
      { 
       //connexion is OK 
       printf("FD_CONNECT ok (dwEvent=%d)\n", dwEvent); 

       // now that we are connected we want to send data or be aware when the other socket is disconnected 
       rc = WSAEventSelect(ahSocket[dwEvent], ahEvents[dwEvent], FD_CLOSE | FD_WRITE); 
       if(rc == SOCKET_ERROR) 
       { 
        printf("WSAEventSelect() error %d\n", SOCKET_ERRNO); 
        exit(1); 
       } 
      } 
      if (FD_CLOSE & NetworkEvents.lNetworkEvents) 
      { 
       printf("FD_CLOSE ok (dwEvent=%d)\n", dwEvent); 
       printf("press a key to exit\n"); 
       getch(); 

       WSACloseEvent(ahEvents[0]); 
       exit(0); 
      } 

      if (FD_WRITE & NetworkEvents.lNetworkEvents) 
      { 
       char szBuffer[256]; int cbBuffer; 

       printf("FD_WRITE ok (dwEvent=%d)\n", dwEvent); 

       // create string and return the size 
       cbBuffer = sprintf(szBuffer, "Coucou", dwEvent); 


       // send the string with 0 at the end 
       rc = send(ahSocket[dwEvent], szBuffer, cbBuffer + 1, 0); 
       if (SOCKET_ERROR == rc) 
       { 
        printf("WSAEnumNetworkEvent: %d lNetworkEvent %X\n", WSAGetLastError(), NetworkEvents.lNetworkEvents); 
       } 

       // not sure if I have to use it 
       //WSAResetEvent(ahEvents[0]); 

      } 

     } 
     } 
    } 
} 

下載.cpp文件:https://www.dropbox.com/s/pjuipz7v4iwr5ea/Clientserver%20TCP.zip

回答

1

我建議看看普通select()對於非阻塞I/O第一。這裏有幾個鏈接,你就可以開始:

+0

謝謝,我也來看看那個吧 – user1637091

+0

Windows中的Regular select()有着衆所周知的性能問題。它的性能會隨着套接字的數量而線性地降低O(N)。 – RajV

+0

是的,'select()'在任何地方都有問題,不僅僅是Windows,它是理解概念的起點。 –

2

你不是第一個之後得到FD_WRITE通知,因爲你沒有考慮到從the documentation以下段落:

的FD_WRITE網絡事件略有不同的處理。一個 FD_WRITE網絡事件被記錄在套接字首先連接 與到連接,ConnectEx,WSAConnect,WSAConnectByList, 或WSAConnectByName函數的調用,或當一個插座與接受, 的AcceptEx,或WSAAccept函數,然後接受之後發送失敗, WSAEWOULDBLOCK和緩衝區空間變爲可用。因此,一個 應用程序可以假定可以從第一個網絡事件設置爲 開始發送並持續到一個發送返回 WSAEWOULDBLOCK。在發生這種故障後,應用程序將找出 ,當FD_WRITE網絡事件記錄爲 且設置了關聯的事件對象時,該應用程序將再次發送。

在第一次調用send()後,套接字仍然是可寫的,因爲其出站緩衝區未滿。只要您仍有數據要發送,請繼續呼叫send(),直到它報告指示緩衝區已滿的WSAWOULDBLOCK錯誤。此時,您必須跟蹤您的剩餘數據,直到您收到FD_WRITE通知,指示套接字可再次寫入,以便您可以繼續從停止的地方發送剩餘數據。

+0

謝謝你的迅速!我在文檔中看到過,但這是我難以理解的一部分。你建議我這樣的事情: 'rc = 0; int dada = 0; (數據庫= WSAEWOULDBLOCK) rc = send(ahSocket [dwEvent],szBuffer,cbBuffer + 1,0); (SOCKET_ERROR == rc) { \t dada = WSAGetLastError();如果(SOCKET_ERROR == rc) (「WSAEnumNetworkEvent:%d lNetworkEvent%X \ n」,WSAGetLastError(),NetworkEvents.lNetworkEvents); } }' (對不起,我沒有成功格式化它) – user1637091