1

Tomcat でメッセージを送受信するために、リモート サーバーへの SocketChannel を作成しました。リモート コンピューターからメッセージを受信するために、タスク専用のスレッドを使用しました (このスレッドのみがソケットから読み取ります)。

SocketChannel でいくつかのバイトが受信されると (新しいデータのために非ブロッキング モードで SocketChannel をポーリングし続けます)、最初に 4 バイトを読み取って次のメッセージの長さを取得し、次に SocketChannel から x バイトを割り当てて読み取ります。次に、デコードしてメッセージに再構築します。

以下は、受信スレッドのコードです。

@Override
public void run() {

    while (true) { //Don't exit thread

        //Attempt to read the size of the incoming message
        ByteBuffer buf = ByteBuffer.allocate(4);

        int bytesread = 0;
        try {
            while (buf.remaining() > 0) {
                bytesread = schannel.read(buf);

                if (bytesread == -1) { //Socket was terminated

                } 

                if (quitthread) break;
            }

        } catch (IOException ex) {

        }

        if (buf.remaining() == 0) {
            //Read the header
            byte[] header = buf.array();
            int msgsize = (0xFF & (int)header[0]) + ((0xFF & (int)header[1]) << 8)
                    + ((0xFF & (int)header[2]) << 16) + ((0xFF & (int)header[3]) << 24);

            //Read the message coming from the pipeline
            buf = ByteBuffer.allocate(msgsize);
            try {
                while (buf.remaining() > 0) {
                    bytesread = schannel.read(buf);

                    if (bytesread == -1) { //Socket was terminated

                    }

                    if (quitthread) break;
                }
            } catch (IOException ex) {

            }

            parent.recvMessage(buf.array());
        }

        if (quitthread) {
            break;
        }
    }

}

SocketChannel から受信した最初のバイトは問題なく、メッセージのデコードに成功しました。しかし、次に SocketChannel から読み取ると、ソケットが約 100 バイト先にスキップしたため、間違ったバイトが読み取られて長さとして解釈され、すべてが破損しました。

コードの何が問題になっていますか? SocketChannel から読み取っているスレッドは他にありません。

4

2 に答える 2

1

括弧がオフになっています。コードは次のとおりです。

(0xFF & ((int)header[1] << 8))

これは常に0です(<< 16および<< 24と同じ)、私の推測では、あなたは次のことを意味しています:

((0xFF & ((int)header[1])) << 8)

これにより、十分な数のメッセージ バイトが読み取られず、(読み取りが多すぎるのとは対照的に) 同期の不一致が発生します。

編集:上記を修正したので、何も問題はありません。最初のメッセージの長さと、消費される正確なバイト数との関係を教えていただけますか?

示されているコードに基づいて、私の唯一の推測は、schannel に影響を与える可能性がある、示されているサンプルから一部の動作を編集したということです。schannel は他の場所で参照されていますか?

行の場合:

ByteBuffer buf = ByteBuffer.allocate(4);

whileあなたが説明する動作をもたらす範囲外になりますが、サンプルコードではそうではありません。

于 2009-11-16T16:15:46.460 に答える
0

非ブロック モードでソケットをポーリングしていると言うとき、「標準的な」Selector.select()アプローチを使用していると思いますか?

select が戻り、ソケットから読み取ることができるデータがあることを示す場合、select() への呼び出しを再入力する前に、使用可能なバイトのみを読み取る必要があります。read() が -1 を返した場合、バッファ内ですぐに読み取ることができるバイトがこれ以上ないことを示します。これは、ソケットが閉じられたことを意味するものではありません。したがって、戻る前にバッファを完全に埋めようとするのは間違っていると思います。たとえそれが機能したとしても、データが到着している間、I/O スレッドは常に回転しています。特に、戻り値 -1 を単に無視しているように見えます。

有限状態マシン アプローチを使用するようにコードを再構築することを検討してください。たとえば、私は過去に IDLE、READ_MESSAGE_LENGTH、READ_MESSAGE の 3 状態モデルを使用してこれを実装しました。

于 2009-11-16T22:39:07.930 に答える