1

私はJava 1.6を使用してインスタントメッセンジャーに取り組んでいます。IM は、マルチスレッド (メイン スレッド、受信、および ping) を使用します。TCP/IP 通信には SocketChannel を使用しました。また、サーバーから大きなパッケージを受信する際に問題があるようです。サーバーは1つではなく2つのパッケージを送信し、そこから問題が始まります。最初の 8 バイトごとに、パッケージの種類とサイズが示されます。これが私が読んだ方法です:

public void run(){
    while(true){
        try{
        Headbuffer.clear();
        bytes = readChannel.read(Headbuffer); //ReadableByteChannel
        Headbuffer.flip();
        if(bytes != -1){
            int head = Headbuffer.getInt();
            int size = Headbuffer.getInt();
            System.out.println("received pkg: 0x" + Integer.toHexString(head)+" with size "+ size+" bytes);
            switch(head){
            case incoming.Pkg1: ReadWelcome(); break;
            case incoming.Pkg2: ReadLoginFail();break;
            case incoming.Pkg3: ReadLoginOk();break;
            case incoming.Pkg4: ReadUserList();break;
            case incoming.Pkg5: ReadUserData();break;
            case incoming.Pkg6: ReadMessage();break;
            case incoming.Pkg7: ReadTypingNotify();break;
            case incoming.Pkg8: ReadListStatus();break;
            case incoming.Pkg9: ChangeStatus();break;
            }
        }
        }catch(Exception e){
            e.printStackTrace();
        }
    }   
}

そして、テスト中、アカウントにログインしてバディリストをインポートするまでは、すべて問題ありませんでした。ステータスのリクエストをサーバーに送信すると、80 件の連絡先のうち約 10 件が返信されました。だから私はこのようなものを思いついた:

public synchronized void readInStatus(ByteBuffer headBuffer){

    byteArray.add(headBuffer); //Store every buffer in ArrayList
    int buddies = MainController.controler.getContacts().getSize();
    while(buddies>0){

          readStuff();
          readDescription();
        --buddies; 
    }       
}

各 readStuff() と readDescription() は、バッファ内の残りのバイトで各パラメータのサイズをチェックしています。

 if(byteArray.get(current).remaining() >= 4){

      uin = byteArray.get(current).getInt();
      }else{
          byteArray.add(Receiver.receiver.read());
          current = current +1;
          uin = byteArray.get(current).getInt(); 
      }

および Receiver.receiver.read() は次のとおりです。

public ByteBuffer read(){
    try {
        ByteBuffer bb = ByteBuffer.allocate(40000);
        bb.order(ByteOrder.LITTLE_ENDIAN);
        bytes = readChannel.read(bb);
        bb.flip();
        return bb;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

したがって、アプリケーションは起動され、ログに記録され、連絡先が送信されます。サーバーは私のリストの一部だけを私に送り返してきます。しかし、メソッドreadInStatus(ByteBuffer headBuffer)では、リストの残りを強制しようとします。そして今、楽しい部分 - しばらくすると、Receiver.receiver.read()に到達し、バイト = readChannel.read(bb)で停止し、理由がわかりません。しばらくしてもエラーはなく、何もありません。アイデアの。私はこの一週間ずっと戦っていますが、解決策に近づくことはできません。提案をいただければ幸いです。ありがとう。


返信ありがとうございます。はい、ブロッキング SocketChannel を使用しています。ノンブロッキングを試しましたが、暴走して制御不能になったため、アイデアをスキップしました。私が期待するバイトについて-これはちょっと奇妙です。なぜなら、サイズはヘッドで1回だけですが、パッケージ全体ではなく最初の部分のサイズであり、他の部分にはヘッダーバイトがまったく含まれていないからです。それが何バイトになるかは予測できません。その理由は、容量が 255 バイトの記述です。これがまさに私が変数バディを作成した理由です:これpublic synchronized void readInStatus(ByteBuffer headBuffer) は基本的にバディリストの長さであり、各フィールドを読み取る前に、十分なバイトが残っているかどうかを確認します。そうでない場合はread()を実行します. ただし、説明の前の最後のフィールドは、着信説明の長さを持つ整数です。ただし、何らかの処理が完了するまで、パッケージの長さを判断することは不可能です。@robertその状況でノンブロッキングSocketChannelにもう一度切り替えてみるべきだと思いますか?

4

2 に答える 2

1

問題は、読み取ろうとしているよりも少ないバイトを送信している可能性が最も高いです。何かを書き忘れたり、間違った順序で書き込んだり、サイズ フィールドを読み違えたりした可能性があります。

この問題に対処するには、トレース コードを追加して、読み取りおよび書き込みのバイト数、概念上のパケット サイズなどをカウントおよびログに記録することをお勧めします。次に実行し、トレースを比較して、どこから同期が外れ始めているかを確認します。

于 2011-09-05T12:43:31.190 に答える
0

ブロッキングSocketChannelを使用している場合、読み取りは、バッファーがいっぱいになるか、サーバーがストリームの終わりを配信するまでブロックされます。接続が維持されているサーバーの場合、サーバーはストリームの終わりを送信しません。データの送信を停止するだけで、読み取りは無期限に、またはタイムアウトになるまでハングします。

次のいずれかを実行できます:(i)非ブロッキングSocketChannelを使用して、読み取りが0バイトになるまで繰り返し読み取ります(ただし、0バイトは必ずしもストリームの終了を意味するわけではありません-中断を意味する可能性があります)、または(ii)必要に応じてブロッキングバージョンを使用すると、読み取りに残っているバイト数がbuffer.capacity()未満の場合に、サーバーから、たとえばヘッダーから何バイトを期待していたかがわかります。読み取り前に必要なスペースのみをバッファーに残します。私は現在このソリューションに取り組んでいます。それがあなたのために働くならば、私に知らせてください!

私が解決できる限り、ブロッキングSocketChannelを使用する必要があり、予想されるバイト数がわからず、サーバーがストリームの終わりを送信しない場合、解決策はありません。

于 2011-09-07T09:27:44.897 に答える