2012-06-07 46 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”,服务器是否应该通过字节计数或终止零来查找消息的结尾?等等)。

相关问题