1

私はこれを数時間見てきました。思いつく限りのことをやってみましたが、率直に言って意味がありません。ソケットを使って問題なく積極的に送受信していますが、データを同じスタイルの別のメッセージに変更すると、受信が停止します。TCPを使用しています。私はマネージャープロセスにテーブルデータを含む最大N個のルーターメッセージを送信させています。後で同じスタイルのパケットを送信し、それを受信して​​から受信を停止します。コードはループの先頭に戻りますが、それ以上データを取得しません。

ああ、私が使用しているネットワークコードは、beejsTCPサーバークライアントコードのコピーアンドペーストです。http://beej.us/guide/bgnet/output/html/multipage/clientserver.html

マネージャースレッド、この部分は機能します

for(vector< vector<int> >::iterator it = table.begin(); it!=table.end(); ++it ){
            vector< int > d = *it;

            for(vector<int>::iterator itA = d.begin(); itA!=d.end(); ++itA ){
                cout << "Sending... "<< *itA << endl;
                s <<*itA<<" ";
            }
            if (send(new_fd, s.str().c_str(), 13, 0) == -1)
                perror("Serv:send");
            sleep(2);
            logs << "Sent to router " << i <<":\n" << s.str();
            writeLog(logs.str().c_str());
            s.str("");
            logs.str("");


        }
        s<<"done";
        if (send(new_fd, s.str().c_str(), 13, 0) == -1)
            perror("Serv:send");
        writeLog(s.str().c_str());

管理2、最初のメッセージのみが通過する

 for(vector <vector <int > >::iterator it = toSendPackets.begin(); it != toSendPackets.end(); ++it){
    sleep(3);
    vector<int> tsp = *it;
    int a,b,c = 0;
    for(vector<int>::iterator itr = tsp.begin(); itr != tsp.end(); ++itr){
        if(c==0){
            a = *itr;
        }
        if(c==1){
            b = *itr;
        }

        c++;
    }
    ss.str("");
    ss << a << " " << b;
    for(int i = 0; i < numN; i++){
        int curSoc = socketList[i];
        stringstream sl;

        sl<<"sent:"<< ss.str().c_str();
        cout << "sending..  " << ss.str() << " to " << i << endl;
        if (send(curSoc, "HOP", strlen("HOP")+1, 0) == -1)
            perror("Serv:send");
        sleep(2);
        if (send(curSoc, ss.str().c_str(), strlen(ss.str().c_str())+1, 0) == -1)
            perror("Serv:send");
        writeLog(sl.str().c_str());
        sleep(1);

    }
}

ルーターコード。

上記のマネージャーコードとマネージャーコード2は、どちらもコードのこの部分に送信されます。最初の送信、この場合は「HOP」を取得し、その後何も送信しませんか?HOPパケットの解析を削除したので、文字通り、何かが読み取られたとだけ記載する必要があります。

if(tid == 0){// TCP
    stringstream s;
    bool proc = true;
    while(!doneFlag){
        proc = true;
        cout << "TCP RECEIVING... " << endl;
        int numbytes = 0;
        while(numbytes==0){
            if ((numbytes = recv(sockfd, buf, MAXDATASIZE, 0)) == -1) {
                perror("recvROUTERThread0");
                exit(1);
            }
        }
        buf[numbytes] = '\0';
        numbytes = 0;
        if(strcmp("Quit",buf)==0){
            writeLog("Quit read",outName);
            doneFlag = true;
            close(net.sockfd);
            floodUDP("Quit");
            pthread_exit(NULL);

        }
        else if(strcmp("HOP",buf)==0){
            cout << "HOP READ" << endl;
            numbytes = 0;
            while(numbytes==0){
                if ((numbytes = recv(sockfd, buf, MAXDATASIZE, 0)) == -1) {
                    perror("recvROUTERThread0");
                    exit(1);
                }
            }
            s << id << "R: Receiving a routing command! " << buf;
            cout << s.str().c_str() << endl;
            writeLog(s.str().c_str(),outName);
            HOPpacket hpo = genHopOrig(s.str().c_str());
            if(hpo.s == atoi(id)){
                printHOP(hpo);
                //              cout << "PACKET " << pr << endl;
                stringstream sl;
                char* hop = generateHopPacket(hpo);
                sl << "Generating HOP packet and sending.. " << hop;
                writeLog(sl.str().c_str(),outName);
                sendHOP(hop);
            }
        }
        else{
            cout << "Table row data from manager" << endl;
            s.str("");
            s << id << "R: MANAGER MESSAGE: " << buf << endl;
            cout << s.str() << endl;
            writeLog(s.str().c_str(),outName);
            int intID = atoi(id);
            vector <int> tr = processTR(buf,intID,basePN);
            table.push_back(tr);
        }

    }
   }

私の出力。この場合、10台のルーターが実行されています。HOPを送信してから05を送信していることを示すようにプリントを変更しなかったことに注意してください。

sending..  0 5 to 0

HOP READ
WRITTING Manager log:12-11-23::4:6:26:
sent:0 5

sending..  0 5 to 1
HOP READ
WRITTING Manager log:12-11-23::4:6:29:
sent:0 5

sending..  0 5 to 2
HOP READ
WRITTING Manager log:12-11-23::4:6:32:
sent:0 5

sending..  0 5 to 3
HOP READ
WRITTING Manager log:12-11-23::4:6:35:
sent:0 5

sending..  0 5 to 4
HOP READ
WRITTING Manager log:12-11-23::4:6:38:
sent:0 5

sending..  0 5 to 5
HOP READ
WRITTING Manager log:12-11-23::4:6:41:
sent:0 5

sending..  0 5 to 6
HOP READ
WRITTING Manager log:12-11-23::4:6:44:
sent:0 5

sending..  0 5 to 7
HOP READ
WRITTING Manager log:12-11-23::4:6:47:
sent:0 5

sending..  0 5 to 8
HOP READ
WRITTING Manager log:12-11-23::4:6:50:
sent:0 5

sending..  0 5 to 9
HOP READ
WRITTING Manager log:12-11-23::4:6:53:
sent:0 5

sending..  3 9 to 0
WRITTING Manager log:12-11-23::4:6:59:
sent:3 9

sending..  3 9 to 1
WRITTING Manager log:12-11-23::4:7:2:
sent:3 9

sending..  3 9 to 2
WRITTING Manager log:12-11-23::4:7:5:
sent:3 9

sending..  3 9 to 3
WRITTING Manager log:12-11-23::4:7:8:
sent:3 9

sending..  3 9 to 4
WRITTING Manager log:12-11-23::4:7:11:
sent:3 9

sending..  3 9 to 5
WRITTING Manager log:12-11-23::4:7:14:
sent:3 9

sending..  3 9 to 6
WRITTING Manager log:12-11-23::4:7:17:
sent:3 9

sending..  3 9 to 7
WRITTING Manager log:12-11-23::4:7:20:
sent:3 9

sending..  3 9 to 8
WRITTING Manager log:12-11-23::4:7:23:
sent:3 9

sending..  3 9 to 9
WRITTING Manager log:12-11-23::4:7:26:
sent:3 9
4

1 に答える 1

4

recvTCP はメッセージ ベースのソケットではなくストリーム ベースのソケットであるため、データを送信するときに問題が発生します。

send( sock, buf1, len1, 0 );  // Send HOP, since it is small, you OS merge this
send( sock, buf2, len2, 0 );  // with next send!

を使用してデータを受信しようとすると、recvへの 2 回の個別の呼び出しでデータを受信することが保証されないrecvため、 への 1 回の呼び出しで両方の送信済みバッファを受信する可能性がありますrecv

recv( sock, buf, len, 0 );  // This may receive both buffers in one call

したがってrecv、最初の呼び出しで既に受信したデータについては、次の呼び出しがブロックされます! また、大きなバッファを送信すると、別の問題になる可能性がありrecvsend.

受信したストリームでメッセージの終わりを定義するプロトコルを定義し、そのプロトコルに従ってデータを受信する必要があります。たとえば、最初にメッセージの長さを送信したり、メッセージの終わりを示すもの (\0または など\r\n) を定義したりできます。

エラーの説明が不十分で申し訳ありません。あなたのコメントでは、HOPメッセージのサイズを大きくしたと言っています! しかし、それは確かに良い習慣ではありません。また、サイズの増加が非常に小さいため、OSにすぐに送信させることはありません(実際、OSに強制的に送信させる特定のサイズはありません)。OS にデータをすぐに送信させたい場合は、TCP_NO_DELAYオプションを使用して Nagle アルゴリズムを無効にする必要がありますが、その前にHow do I use TCP_NODELAY? を参照してください。. これを行うことも良い習慣ではありません。また、これを行うと、呼び出し時にパケットがすぐに送信されますsendが、受信側の OS にメッセージを個別に受信させることはありません!! これを行う正しい方法は何ですか?

問題を詳しく説明します。

// I don't know exact value of MAXDATASIZE but I will assume it is 128
char buf[ MAXDATASIZE ];

int numbytes = recv( sock, buf, MAXDATASIZE, 0 );
if( numbyte == -1 ) {
    // Handle error
}

// I assume HOP_MSG is a defined constant that contain value of HOP message
if( strcmp(buf, HOP_MSG) == 0 ) { // <-- (1)
    while( (numbytes = recv(sock, buf, MAXDATASIZE, 0)) != -1 ) { // <-- (2)
        if( numbytes == 0 ) break;
    }
    if( numbytes == -1 ) {
        // Handle error
    }
}

ちょっと待って!とマークされた行で、完全に読み取りのみ(1) を想定しましたが、なぜですか?? recvHOP_MSGHOP_MSG前に言ったようにTCP、 はストリーム プロトコルであり、メッセージ境界がないため、2 バイトしか読み取れない場合があります!! またはそれは読んだ1KB(それは確かに以上ですHOP_MSG、それで私は何をすべきですか??

実用的な答えは次のようなものです:

int receive_till_zero( SOCKET sock, char* tmpbuf, int& numbytes ) {
    int i = 0;
    do {
        // Check if we have a complete message
        for( ; i < numbytes; i++ ) {
            if( buf[i] == '\0' ) {
                // \0 indicate end of message! so we are done
                return i + 1; // return length of message
            }
        }
        int n = recv( sock, buf + numbytes, MAXDATASIZE - numbytes, 0 );
        if( n == -1 ) {
            return -1; // operation failed!
        }
        numbytes += n;
    } while( true );
}
void remove_message_from_buffer( char* buf, int& numbytes, int msglen ) {
    // remove complete message from the buffer.
    memmove( buf, buf + msglen, numbytes - msglen );
    numbytes -= msglen;
}

void main() {
    SOCKET s;
    char buf[ MAXDATASIZE ];
    int numbytes = 0, msglen;
    // Initialize socket and connect to server, you already do that

    while( true ) {
        msglen = receive_till_zero( s, buf, numbytes );
        if( msglen == -1 ) {/* Handle error */}

        if( !strcmp(buf, HOP_MSG) ) {
            remove_message_from_buffer( buf, numbytes, msglen );
            msglen = receive_till_zero( s, buf, numbytes );
            if( msglen == -1 ) {/* Handle error */}

            std::cout << "Message received from server: " << buf << std::endl;
            remove_message_from_buffer( buf, numbytes, msglen );
        }
    }
}

このコードをデバッグすることで、その目的を確実に理解できます。receive_till_zero以前の への呼び出しからの保留中のデータがバッファに既にあると仮定します。そのため、バッファrecvに完全なメッセージがあるかどうかを最初に確認し、データの受信が完了したとは想定しません。を 1 回呼び出すだけで、バッファにa が表示されるまでループでrecv呼び出されます。バッファ内のデータを処理し終えた後、バッファ内のデータがすでにある可能性があるため、バッファの先頭から受信を開始するだけでなく、そのデータとそのデータのみを食べるために呼び出します。recv\0remove_message_from_buffer

ご覧のとおり、コードは少し複雑です。より優れたプログラミング モデルとより優れた C++ コードを作成するには、非常に優れた設計で C++ と完全に連携するboost::asioを使用できます。iostream

于 2012-11-23T11:44:11.733 に答える