2012-06-07 42 views
0

當我將BUF_SIZE設置爲10000時正常,但50000不正常。 我只是試圖從客戶端發送數據到服務器並測試緩衝區大小,但我發現在某些大小的情況下,應用程序無法正常工作。爲什麼這個緩衝區大小沒有被socket正確處理?

爲什麼?我怎麼能解決它?

例如,我運行服務器和客戶端,第一次嘗試正常工作,但是當我重新運行客戶端時,開始交付問題。

這裏是服務器:

#include <sys/types.h> 
#include <sys/wait.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <netdb.h> 
#include <cstdio> 
#include <cstdlib> 
#include <cstring> 
#include <iostream> 
#include <signal.h> 

#define BUF_SIZE 50000 

using namespace std; 

void manejador(int signo); 

int main() 
{ 
    int  sservice, sclient,l,nbytes_read, err, nbytes_sent; 
    bool end; 
    char buf[BUF_SIZE]; 
    struct sockaddr_in sin, clientfsin; 
    pid_t pid; 
    int status; 

    sservice=socket(PF_INET,SOCK_STREAM, 0); /*Open the socket*/ 
    if(sservice == -1) 
    { 
     perror("Server. Socket: "); 
     exit(-1); 
    } 

    sin.sin_family  = AF_INET; /*ARPANET address family*/ 
    sin.sin_addr.s_addr = INADDR_ANY; /*Accept connections on any Interface*/ 
    sin.sin_port  = htons(4000);   /*Service TSAP > 1023. CHANGE IT!*/ 

    /*Register the server in the system*/ 
    err=bind(sservice, (struct sockaddr*)&sin, sizeof(sin)); 
    if(err == -1) 
    { 
     perror("Server. bind: "); 
     exit(-1); 
    } 

    /*Up to 5 waiting connections*/ 
    err = listen(sservice,5); 
    if(err == -1) 
    { 
     perror("Server. Listen: "); 
     exit(-1); 
    } 

    /* Receiving requests loop */ 
    for(;;) 
    { 
     /*Accept a connection from a client*/ 
     l = sizeof(clientfsin); 
     sclient = accept(sservice,(struct sockaddr *)&clientfsin, (socklen_t*) &l); 
     if(sclient == -1) 
     { 
      perror("Server. Accept: "); 
      continue; 
     } 

     signal(SIGCHLD,manejador);   //Quitar si ponemos waitpid 
     pid = fork(); 

     if(pid == -1){ 
      printf("Error al crear el proceso hijo\n"); 
      exit(0); 
     } 

     if(pid){ 
      close(sclient); 
      //waitpid(pid,&status,0); //Descomentar si usamos waitpid 
     }else{ 

     close(sservice);  
     /*Give the service*/ 
     end = false; 
     int i=1; 
     while(!end && (i<=10)) 
     { 
      nbytes_read=recv(sclient,(char *)buf,sizeof(buf),0); 


      if (nbytes_read > 0) 
      { 
       buf[nbytes_read]='\0'; 
       //cout << "SERVER>Server received: " << buf << endl; 
       printf("Recepcion <%i>: Se han recibido <%i> bytes del cliente\n",i,nbytes_read); 
       cout.flush(); 

       err = 0; 
       //sprintf(buf,"%s_server",buf); 
       nbytes_sent = send(sclient,(char *)buf,sizeof(buf),0); 
       printf("Envio <%i>: Se han enviado <%i> bytes al cliente\n",i,nbytes_sent); 
       i++; 
      } 
      else  
      { 
       perror("Sever. Receive/read: "); 
       end=true; 
       err = -1; 
      } 
     } 

     if(err >= 0) 
      cout << "SERVER>Cliente Atendido" << endl; 
     else 
      cout << "SERVER>Finalizacion incorrecta del cliente" << endl; 

     /*Never forget to close a socket!*/ 
     close(sclient); 
     exit(0); 
     } 
    } 

    close(sservice); 
    printf("Fin server"); 

} /*main()*/ 

void manejador(int signo) //comentar si usamos waitpid 
{ 
     int estado; 
     wait(&estado); 
} 

這裏是客戶端:

#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <netdb.h> 
#include <cstdio> 
#include <cstdlib> 
#include <cstring> 
#include <iostream> 
#include <sys/time.h> 

#define BUF_SIZE 50000 

using namespace std; 

int main() 
{ 
    int  sock, err; 
    bool end; 
    char buf[BUF_SIZE]; 
    struct sockaddr_in sout; 

    sock=socket(PF_INET,SOCK_STREAM, 0); /*Open the socket*/ 
    if(sock == -1) 
    { 
     perror("Client. Socket: "); 
     exit(-1); 
    } 


    sout.sin_family  = AF_INET;      /*ARPANET address family*/ 
    sout.sin_addr.s_addr = inet_addr("127.0.0.1");  /*Which server?*/ 
    sout.sin_port  = htons(4000);      /*Output port*/ 

    /*Connect to the server*/ 
    err = connect(sock,(struct sockaddr *)&sout, sizeof(sout)); 
    if(err == -1) 
    { 
     perror("Client. Connect: "); 
     exit(-1); 
    } 

    end = false; 
    double t1,t2; 
    while(!end) 
    { 
     /*Ask for the service*/ 
     //cout << endl << "CLIENT> Send a message...: " ; cout.flush(); 
     //cin.getline(buf, 128); 
     int i=0; 
     for(i=0;i<10;i++){ 
      timeval tim; 
        gettimeofday(&tim, NULL); 
        t1=tim.tv_sec+(tim.tv_usec/1000000.0); 

      err = send(sock,(char *)buf,sizeof(buf),0); 

      if(err == -1) 
      { 
      perror("Client. Send/write: "); 
      exit(-1); 
      } 
      printf("Envio <%i>: Se han enviado <%i> bytes\n",i+1,err); 

      gettimeofday(&tim, NULL); 
        t2=tim.tv_sec+(tim.tv_usec/1000000.0); 
        printf("%.6lf para el envio de <%i>\n", t2-t1,i+1); 

      err = recv(sock,(char *)buf,sizeof(buf),0); 
      printf("Recepcion <%i>: Se han recibido <%i> bytes\n",i+1,err); 
      //cout << "CLIENT> Server response: " << buf; 
      cout.flush(); 
     } 
     end=true; 
    } 

    close(sock); 

} /*main()*/ 

Offtopic:對不起,在西班牙的意見;)

+1

我不確定我是否正確,但TCP連接通常會發送大小爲1500字節(由於以太網連接限制/標準)的數據包。爲什麼你需要發送50000字節的數據包?你爲什麼不把小包分成小塊呢? –

+5

你的意思是「應用程序無法正常工作」。請進一步解釋錯誤。 – vz0

+0

在該行'nbytes_sent = send(sclient,(char *)buf,sizeof(buf),0);'你試圖將*整個*緩衝區發送回客戶端。我認爲它應該是'nbytes_sent = send(sclient,(char *)buf,nbytes_read,0);'而不是。 – vstm

回答

1

我跑你的程序。問題在於你對recv如何工作的期望。您希望接收器能夠在一次讀取中始終讀取所有BUF_SIZE字節。如果這是您的期望,那麼您應該在recv調用中的最後一個參數中設置MSG_WAITALL標誌。儘管在阻止I/O時,send呼叫將保持阻塞狀態,直到發送完所有字節爲止,默認情況下recv不是這樣。它會收到無​​數的數據在套接字輸入隊列中,所以recv可能會比沒有MSG_WAITALL標誌時期望的短。

至於爲什麼BUF_SIZE這個較小的值有效,而較大值沒有,這可以通過套接字輸入隊列的大小來解釋。您可以嘗試使用setsockoptSO_RCVBUF選項將其設置爲匹配或超過您的BUF_SIZE並查看它是否適合您。但是,在現實生活中,網絡條件會決定輸入緩衝區是否保持滿,所以短讀取只是您的程序應該處理的內容。

答案的其餘部分解決了代碼中的一些樣式問題和一種「錯誤」錯誤。

我注意到你正在創建一個相當大的陣列。你可能會考慮動態分配它。做這件事的一種方法是使用矢量。

std::vector<char> buf; 

buf.resize(BUF_SIZE); 
nbytes_read = recv(sclient, &buf[0], buf.size(), 0); 

注意其他的事情(通過@ Linux_iOS.rb.cpp.c.lisp.m.sh注意到)是,如果有nbytes_readBUF_SIZE,然後試圖NUL終止輸入錯誤的事情去做。它將訪問緩衝區外的數據。如果你真的必須NUL終止,那麼用矢量,你可以使用push_back

if (nbytes_read == buf.size()) buf.push_back('\0'); 
else buf[nbytes_read] = '\0'; 

但是,您應該更容易設置緩衝區的大小。

buf.resize(nbytes_read); 

然後,當您回顯數據時,您只會回顯所讀的內容。

send(sclient, &buf[0], buf.size(), 0); 
3

錯誤:

1)您的服務器沒有試圖確保其已接收到完整的消息。

2)無論實際需要發送多少字節,您的服務器都會發送50,000個字節。 3)如果您的服務器實際讀取50,000個字節,則嘗試添加終止零將溢出緩衝區。

3)您的客戶端發送50,000個不確定字節。

4)您的客戶端忽略它收到的字節數。

5)客戶端不確保它實際收到服務器發送的終止零字節。

但是你最大的錯誤是這樣的:你沒有明智的協議。如果消息以零字節結束,爲什麼發送50,000字節?如果你的信息總是50000字節,爲什麼接收器不接收50000字節?

我給你我的標準建議我給每一個TCP程序員:從開發一個協議開始並記錄它。該協議應指定誰發送和何時。該協議應指定消息的框架。該協議應指定有效和無效消息的規則。該協議應指定如何檢測並處理死連接。等等。

需要一個小時左右才能正確記錄一個協議,但它非常值得。沒有人,很難說我的哪個bug是真正的bug。 (例如,也許你真的應該總是發送50,000個字節,即使消息只是「hi」,服務器是否應該通過字節計數或終止零來查找消息的結尾?等等)。

相關問題