2011-04-04 85 views
1

我有一個WinSock服務器設置,它正確接受客戶端並中繼相應的信息。服務器需要兩個客戶端,接收一個256字節的固定大小的緩衝區,將其存儲起來,然後將另一個緩衝區中繼給客戶端。 (即client1發送它的緩衝區,服務器保存它,然後向client1發送client2的緩衝區)。第一個客戶端在多路winsock服務器上是laggy

只要客戶端1更改其緩衝區,client2接收更改大約需要4秒鐘的時間。如果client2進行更改,client1幾乎立即收到更新(小於0.1s)。

Nagle的算法被禁用,我嘗試改變服務器處理請求的順序,但client1總是滯後。數據始終顯示完整,但時間過長。下面是服務器用於處理數據的循環:

for(;;) 
{ 
    // check if more clients can join 
    if (numClients < MAX_CLIENTS) 
    { 
     theClients[numClients] = accept(listeningSocket, NULL, NULL); 
     if (theClients[numClients] == INVALID_SOCKET) 
     { 
      nret = WSAGetLastError(); 
      JBS::reportSocketError(nret, "server accept()"); 
      closesocket(listeningSocket); 
      WSACleanup(); 
      exit(0); 
     } 
     // disable Nagle's algorithm 
     int flag = 1; 
     int result = setsockopt(theClients[numClients], IPPROTO_TCP, TCP_NODELAY, 
      (char *) &flag, sizeof(int)); 
     if (result < 0) 
     { 
      nret = WSAGetLastError(); 
      JBS::reportSocketError(nret, "client connect()"); 
      closesocket(theClients[numClients]); 
      WSACleanup(); 
     } 
     // make the socket non-blocking 
     u_long iMode = 1; 
     ioctlsocket(theClients[numClients],FIONBIO, &iMode); 

     cout << "Client # " << numClients << " connected." << endl; 
     numClients++; 
     started = true; 
    } 
    else 
    { 
     // we've received all the connections, so close the listening socket 
     closesocket(listeningSocket); 
    } 

    // process client2 
    if (theClients[1] != INVALID_SOCKET) 
    { 
     memset(keys2, 0, 255); 
     // receive the updated buffer 
     nBytes = recv(theClients[1], keys2, sizeof(keys2), 0); 
     receiveResult = WSAGetLastError(); 
     if ((receiveResult != WSAEWOULDBLOCK) && (receiveResult != 0)) 
     { 
      JBS::reportSocketError(receiveResult, "server receive keys2()"); 
      shutdown(theClients[1],2); 
      closesocket(theClients[1]); 
      WSACleanup(); 
      exit(0); 
      break; 
     } 
     // send client1's buffer to client2 
     send(theClients[1],keys1,sizeof(keys1),0); 
     sendResult = WSAGetLastError(); 
     if((sendResult != WSAEWOULDBLOCK) && (sendResult != 0)) 
     { 
      JBS::reportSocketError(sendResult, "server send keys1()"); 
      shutdown(theClients[1],2); 
      closesocket(theClients[1]); 
      WSACleanup(); 
      exit(0); 
      break; 
     } 
    } 

    // process client1 
    if (theClients[0] != INVALID_SOCKET) 
    { 
     memset(keys1, 0, 255); 
     // receive the updated buffer 
     nBytes = recv(theClients[0], keys1, sizeof(keys1), 0); 
     receiveResult = WSAGetLastError(); 
     if ((receiveResult != WSAEWOULDBLOCK) && (receiveResult != 0)) 
     { 
      JBS::reportSocketError(receiveResult, "server receive keys1()"); 
      shutdown(theClients[0],2); 
      closesocket(theClients[0]); 
      WSACleanup(); 
      exit(0); 
      break; 
     } 
     // send client2's buffer to client1 
     send(theClients[0],keys2,sizeof(keys2),0); 
     sendResult = WSAGetLastError(); 
     if((sendResult != WSAEWOULDBLOCK) && (sendResult != 0)) 
     { 
      JBS::reportSocketError(sendResult, "server send keys2()"); 
      shutdown(theClients[0],2); 
      closesocket(theClients[0]); 
      WSACleanup(); 
      exit(0); 
      break; 
     } 
    } 
    Sleep((float)(1000.0f/30.0f)); 
} 

客戶端發送代碼:

int nError, sendResult; 

sendResult = send(theSocket, keys, sizeof(keys),0); 
nError=WSAGetLastError(); 
if((nError != WSAEWOULDBLOCK) && (nError != 0)) 
{ 
    JBS::reportSocketError(sendResult, "client send()"); 
    shutdown(theSocket,2); 
    closesocket(theSocket); 
    WSACleanup(); 
} 
+0

出於興趣,你怎麼沒有使用從套接字讀取的bBytes來決定你是否有一個完整的緩衝區?如果設置了WSAWOULDBLOCK,那麼這可能不是密鑰的大小?這是一段時間,但這似乎是錯誤的... – forsvarir 2011-04-04 21:32:44

+0

我只是有nBytes變量設置做一些輸出,以確保我總是得到256字節,我是,所以我不認爲這是必要的檢查。 – 131nary 2011-04-04 21:39:13

+0

您是否曾嘗試在recv + send的調用周圍添加記錄行,以便您知道服務器何時啓動/完成這些調用(這樣您就知道在哪裏花費時間)......在它的表面上,似乎您希望兩個緩衝區都能同時讀取某些內容... – forsvarir 2011-04-04 21:53:54

回答

1

我已經粘貼下面的代碼,並在它的一些在線評論,主要是因爲我可以」這一切都可以在評論中得到充分的體現。您如何確定從客戶端1到客戶端2的更改需要4秒鐘的時間?視力檢查?這是否意味着Client1在同一臺計算機上運行(沒有不同的網絡延遲問題需要擔心)?

我突出顯示了一些看起來不對的塊。他們可能不是,這可能是因爲你試圖簡化你發佈的代碼,而你錯過了一些代碼。我也提出了一些建議,你可能想添加一些日誌記錄。如果套接字真的是非阻塞的,你應該很快從所有的調用回來,並且不能讀取數據,除非客戶端發送了它。如果你有4秒的延遲,那麼問題可能是:

  • 客戶端還沒有發送它...是在客戶端上禁用Nagle?如果是這種情況,我希望接連接的電話會發生,沒有數據。
  • recv調用時間太長......套接字是否處於非阻塞模式?
  • 發送調用時間太長......是非阻塞模式下的套接字,是否被緩存,客戶端是否嘗試接收數據?

讓代碼的每個部分所需的時間將有助於追蹤問題出在哪裏。

你可以得到的時候,使用這樣的事情(從網上借):

struct timeval tv; 
struct timezone tz; 
struct tm *tm; 
gettimeofday(&tv, &tz); 
tm=localtime(&tv.tv_sec); 
printf(" %d:%02d:%02d %d \n", tm->tm_hour, tm->tm_min, 
      m->tm_sec, tv.tv_usec); 

您的代碼:

for(;;) 
{ 

/* This block of code is checking the server socket and accepting 
* connections, until two? (MAX_CLIENTS isn't defined in visible code) 
* connections have been made. After this, it is attempting to close 
* the server socket everytime around the loop. This may have side 
* effects (although probably not), so I'd clean it up, just in case 
*/ 
/* LOG TIME 1 */ 
    // check if more clients can join 
    if (numClients < MAX_CLIENTS) 
    { 
     theClients[numClients] = accept(listeningSocket, NULL, NULL); 
     if (theClients[numClients] == INVALID_SOCKET) 
     { 
      nret = WSAGetLastError(); 
      JBS::reportSocketError(nret, "server accept()"); 
      closesocket(listeningSocket); 
      WSACleanup(); 
      exit(0); 
     } 
     // disable Nagle's algorithm 
     int flag = 1; 
     int result = setsockopt(theClients[numClients], IPPROTO_TCP, TCP_NODELAY, 
      (char *) &flag, sizeof(int)); 
     if (result < 0) 
     { 
      nret = WSAGetLastError(); 
      JBS::reportSocketError(nret, "client connect()"); 
      closesocket(theClients[numClients]); 
      WSACleanup(); 
     } 
     // make the socket non-blocking 
     u_long iMode = 1; 
     ioctlsocket(theClients[numClients],FIONBIO, &iMode); 

     cout << "Client # " << numClients << " connected." << endl; 
     numClients++; 
/* This started variable isn't used, is it supposed to be wrapping 
* this server code in an if statement? 
*/ 
     started = true; 
    } 
    else 
    { 
     // we've received all the connections, so close the listening socket 
     closesocket(listeningSocket); 
    } 
/* LOG TIME 2 */ 

// process client2 
    if (theClients[1] != INVALID_SOCKET) 
    { 
     memset(keys2, 0, 255); 
     // receive the updated buffer 
/* LOG TIME 3 */ 
     nBytes = recv(theClients[1], keys2, sizeof(keys2), 0); 
/* LOG TIME 4 */ 
     receiveResult = WSAGetLastError(); 
     if ((receiveResult != WSAEWOULDBLOCK) && (receiveResult != 0)) 
     { 
      JBS::reportSocketError(receiveResult, "server receive keys2()"); 
      shutdown(theClients[1],2); 
      closesocket(theClients[1]); 
      WSACleanup(); 
      exit(0); 
      break; 
     } 
     // send client1's buffer to client2 
/* LOG TIME 5 */ 
     send(theClients[1],keys1,sizeof(keys1),0); 
/* LOG TIME 6 */ 
     sendResult = WSAGetLastError(); 
     if((sendResult != WSAEWOULDBLOCK) && (sendResult != 0)) 
     { 
      JBS::reportSocketError(sendResult, "server send keys1()"); 
      shutdown(theClients[1],2); 
      closesocket(theClients[1]); 
      WSACleanup(); 
      exit(0); 
      break; 
     } 
    } 

    // process client1 
/* If the client has been accepted (note that because this 
* is part of the same block of code, and there's no protection 
* around it, the first connection will process it's first 
* receive/send combination before the second socket has been accepted) 
*/ 
    if (theClients[0] != INVALID_SOCKET) 
    { 
     memset(keys1, 0, 255); 
     // receive the updated buffer 
/* You're trying a receive against a non-blocking socket. I would expect this 
* to fail with WSAEWOULDBLOCK, if nothing has been sent by the client, but 
* this block of data will still be sent to the client 
*/ 
/* LOG TIME 7 */ 
     nBytes = recv(theClients[0], keys1, sizeof(keys1), 0); 
/* LOG TIME 8 */ 
     receiveResult = WSAGetLastError(); 
     if ((receiveResult != WSAEWOULDBLOCK) && (receiveResult != 0)) 
     { 
      JBS::reportSocketError(receiveResult, "server receive keys1()"); 
      shutdown(theClients[0],2); 
      closesocket(theClients[0]); 
      WSACleanup(); 
      exit(0); 
      break; 
     } 
     // send client2's buffer to client1 
/* The first time around the loop, you're sending the buffer to the 
* first connected client, even though the second client hasn't connected yet. 
* This will continue 30 times a second, until the second client connects. Does 
* the client handle this correctly? 
*/ 
/* LOG TIME 9 */ 
     send(theClients[0],keys2,sizeof(keys2),0); 
/* LOG TIME 10 */ 
     sendResult = WSAGetLastError(); 
     if((sendResult != WSAEWOULDBLOCK) && (sendResult != 0)) 
     { 
      JBS::reportSocketError(sendResult, "server send keys2()"); 
      shutdown(theClients[0],2); 
      closesocket(theClients[0]); 
      WSACleanup(); 
      exit(0); 
      break; 
     } 
    } 
    Sleep((float)(1000.0f/30.0f)); 
} 

客戶端發送代碼:

int nError, sendResult; 
/* There's no recv/loop in this section 
*/ 
sendResult = send(theSocket, keys, sizeof(keys),0); 
nError=WSAGetLastError(); 
if((nError != WSAEWOULDBLOCK) && (nError != 0)) 
{ 
    JBS::reportSocketError(sendResult, "client send()"); 
    shutdown(theSocket,2); 
    closesocket(theSocket); 
    WSACleanup(); 
} 
相關問題