0

BUF_SIZE を 10000 に設定すると正常に動作しますが、50000 では動作しません。クライアントからサーバーにデータを送信してバッファ サイズをテストしようとしていますが、特定のサイズではアプリケーションが正しく動作しないことがわかりました。

どうして??そして、どうすれば解決できますか??

たとえば、サーバーとクライアントを実行すると、最初の試行は正常に機能しますが、クライアントを再実行すると、配信に問題が発生します。

サーバーは次のとおりです。

#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()*/

トピック外: スペイン語でのコメントで申し訳ありません;)

4

2 に答える 2

3

バグ:

1) サーバーは、完全なメッセージを受信したことを確認しようとしません。

2) サーバーは、実際に送信する必要があるバイト数に関係なく、50,000 バイトを送信します。

3) サーバーが実際に 50,000 バイトを読み取る場合、終端のゼロを追加しようとするとバッファがオーバーフローします。

3) クライアントが 50,000 の不確定バイトを送信します。

4) クライアントは受信したバイト数を無視します。

5) クライアントは、サーバーから送信された終端の 0 バイトを実際に受信したことを保証しません。

しかし、あなたの最大の誤りはこれです: 賢明なプロトコルがありません。メッセージが 0 バイトで終了する場合、なぜ 50,000 バイトを送信するのでしょうか? また、メッセージが常に 50,000 バイトである場合、受信側が 50,000 バイトを受信しようとしないのはなぜでしょうか?

私がすべての TCP プログラマーに与える私の標準的なアドバイスをお伝えしましょう。プロトコルは、誰がいつ送信するかを指定する必要があります。プロトコルは、メッセージのフレーム化方法を指定する必要があります。プロトコルは、有効なメッセージと無効なメッセージのルールを指定する必要があります。プロトコルは、切断された接続を検出して処理する方法を指定する必要があります。等々。

プロトコルを適切に文書化するには 1 時間ほどかかりますが、それだけの価値があります。これがないと、上記の私のバグのどれが本当にバグなのかを判断するのは困難です。(たとえば、メッセージが単に "hi" であっても、常に 50,000 バイトを送信する必要があるかもしれません。サーバーは、バイト カウントまたは末尾のゼロによってメッセージの終わりを検出することになっていますか? など。)

于 2012-06-07T22:37:05.440 に答える
1

私はあなたのプログラムを実行しました。問題は、どのように機能するかを期待することrecvです。BUF_SIZE受信者が常に1 回の読み取りですべてのバイトを読み取れることを期待しています。これが期待される場合はMSG_WAITALL、呼び出しの最後のパラメーターにフラグを設定する必要がありますrecv。I/O のsendブロックでは、すべてのバイトが送信されるまで呼び出しはブロックされたままになりますが、デフォルトではrecv. ソケット入力キューにあるデータの量に関係なく受信するため、フラグrecvなしで期待するよりも短い場合があります。MSG_WAITALL

小さい値がBUF_SIZE機能し、大きい値が機能しない理由については、ソケット入力キューのサイズによって説明されます。オプションを使用setsockoptして、SO_RCVBUF自分と一致するか超えるものに設定して、BUF_SIZEそれが機能するかどうかを確認できます。しかし、実際には、ネットワークの状態によって、入力バッファーがいっぱいになるかどうかが決まります。そのため、短い読み取りはプログラムで対処する必要があります。

回答の残りの部分では、いくつかのスタイルの問題と、コード内の一種の「オフバイワン」エラーに対処しています。

スタックからかなり大きな配列を作成していることに気付きました。動的に割り当てることを検討してください。これを行う 1 つの方法は、ベクトルを使用することです。

std::vector<char> buf;

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

もう 1 つの注意点 (@Linux_iOS.rb.cpp.c.lisp.m.sh が指摘) はnbytes_read、値が の場合、入力を終了BUF_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);
于 2012-06-07T19:19:52.530 に答える