0

クライアントへのオープン接続用のセッション スレッドを使用してサーバーを作成しています。かなり単純です。バッファ付きリーダーを使用して入力から行を読み取るだけです。行が、ユーザーが終了したいことを示す特別な値に等しい場合、または null の場合、ループは終了します。それ以外の場合、メッセージはチェーンを介して、それを処理するためのモジュールに渡されます。テストの目的で、サーバーに telnet で接続し、コマンドを手動で入力するだけです。

端末ウィンドウを閉じるなど、QUITと入力する以外の方法で接続を終了しない限り、これで問題ありません。次に、未知の文字シーケンスを含むメッセージが生成され、不正な形式のメッセージがチェーンを通過します。この単純なテスト ケースでは、それほど重要ではありませんが、対処する必要がある問題を示しています。

私のコードは次のとおりです。

    public void run () {
        BufferedReader  inReader;
        UpstreamMessage message;
        String          lastLine;

        Thread.currentThread ().setName ("UpstreamThread_" + outer.getId ());

        try {
            inReader    = new BufferedReader (new InputStreamReader (outer.clientSocket.getInputStream (), "UTF8"));
            while (!this.ending) {
                // Read whatever was in the input buffer
                lastLine    = inReader.readLine ();
                Logger.getLogger (this.getClass ().getName ()).log (Level.INFO, "Last data from input stream reader was \"{0}\"", lastLine);
                if (null != lastLine && !lastLine.equals ("QUIT")) {
                    message = new UpstreamMessage (outer.sessionId, lastLine);
                    outer.server.acceptMessage (message);
                } else {
                    // End the session
                    Logger.getLogger (this.getClass ().getName ()).log (Level.INFO, "Thread ending");
                    break;
                }
            }
        } catch (IOException | IllegalArgumentException ex) {
            Logger.getLogger (this.getClass ().getName ()).log (Level.SEVERE, ex.getMessage (), ex);
        } catch (InterruptedException ex) {
            // This is thrown when we're telling the thread to shut down so it's normal
            Logger.getLogger (this.getClass ().getName ()).log (Level.INFO, ex.getMessage (), ex);
        } finally {
            this.terminate ();
        }
    }

ターミナル ウィンドウを閉じると、コンソールに次のログが出力されます。

2013 年 7 月 27 日 7:37:00 PM bikeshop.server.Session$UpstreamChannel 実行情報: 入力ストリーム リーダーからの最後のデータは " " でした

あるいは、lastMessage 変数が null を参照しているにもかかわらず、null チェックが失敗することを示唆する次のメッセージがロガーによって報告される場合もあります。

2013 年 7 月 27 日 7:37:00 PM bikeshop.server.Session$UpstreamChannel 実行情報: 入力ストリーム リーダーからの最後のデータは「null」でした

私は明らかに何か間違ったことをしているのですが、何が悪いのかわかりません。私のテスト 'if (null != lastLine && !lastLine.equals ("QUIT"))' は、端末を終了したときにバッファにあるものをキャッチしていません。このケースをより適切に処理するにはどうすればよいですか?

編集: ログと単一ステップのデバッグをより注意深く分析すると、telnet クライアントが終了時に一連の制御文字を送信することが明らかになりました。これはバッファから読み取られ、チェーンを通過して、プログラムの他の部分で警告をトリガーします。次に、セッションは再びループを通過し、現在閉じられているソケットからの読み取りを試みます。これは null を返し、ループは終了します。これが、クライアントから読み取られた最後の行が null であったかどうかについての混乱の原因です。最後に読み取られたものは実際には null でしたが、最後から 2 番目の読み取りが重要であり、異常な動作を引き起こしたものでした。

したがって、質問は、a) 終了時に telnet クライアントが送信する制御シーケンスは何か、または b) Java はその制御シーケンスをチェックする方法を知っているかのいずれかに変わったと思います。

4

1 に答える 1

1

(これを回答に移動することにしました)

BufferedReader.readLine()nullドキュメントに従ってソケットが閉じられている場合に返します。示されているように、if ステートメントの最初の条件:

if (null != lastLine && !lastLine.equals ("QUIT"))

falseその場合 (または、コンピュータが壊れている ;))と評価されます。

ログに「null」と表示され、評価結果がfalse... でない場合、文字列には実際のテキスト「null」が含まれます。デバッガーに入れ、このループにブレークポイントを設定し、ステップスルーして の値を確認しlastLineます。何が起こっているかがわかります。

つまり、サーバーを作成する最初のルールは、決してクライアントを信頼しないことです。telnet を使用する場合のように、最終的に送信される内容を制御することはできません。

そのソケットから読み取るときは、有効でないものを処理しようとしないように、さらに入力の検証を行う必要があります。アプローチ方法はさまざまですが、通常の解決策は、Map有効なコマンド、入力構造を検証する正規表現などです。これを行わないと、非常に壊れやすいサーバーソフトウェアが発見されます。

于 2013-07-27T19:25:15.490 に答える