2

私の元の質問に答えたので、これはこの質問の続きですが、バグは解決しませんでした。

質問:

  • この行にぶら下がっているコードを修正するにはどうすればよいですか inStream.readline()

私の意図:

  1. これは、outMessageがあるかどうかのチェックをループするスレッド内にあり、ある場合はメッセージを送信します。
  2. 次に、インストリームに何かがあるかどうかをチェックし、ある場合は、メインアクティビティのハンドラーに送信します。
  3. 最後に、1秒間スリープしてから、もう一度確認します。
  4. これにより、ソケットを開閉しなくても、何度も読み取り/書き込みができるようになります。

問題:

  • 読み取りと書き込みは改善されていますが、それでも正しく機能していません

今何が起こっているのか:

  • outMessageが値で初期化されている場合、サーバーとの接続時に、ソケットは次のようになります。
    1. 値を書き込んでフラッシュします(サーバーは受信して応答します)
    2. outMessageの値を更新します(ハードコーディングの方法に応じて、nullまたは "x"に)
    3. サーバーからの応答メッセージを読み取って表示します
    4. 次のループに再び入る
    5. outMessageをnullに設定すると、それをスキップして、ifステートメントが正しくハングします。それ以外の場合、outMessageを文字列(たとえば「x」)に設定すると、ifステートメント全体を通過してからハングします。
      • ハングするコードは、 inStream.readline()呼び出しのいずれかです(現在、コメントアウトされています)。

追加情報:-接続したら、「送信」ボックスに入力し、送信(outMessage値を更新)してから切断します。再接続すると、値が読み取られ、同じ行でスタックするまでシーケンスが再度実行されます。

参照された質問以降の変更点:-outMessageとconnectionStatusの両方を「volatile」にしました-必要な場所に行末区切り文字を追加しました。

コード:

        public void run() { 
            while (connectionStatus != TCP_SOCKET_STATUS_CONNECTED) {
                try {
                    Thread.sleep(500);  
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            while (connectionStatus == TCP_SOCKET_STATUS_CONNECTED) {
                try {   
                    if (outMessage != null){                                            
                        OutStream.writeBytes(outMessage + "\n");                    
                        OutStream.flush();                                          
                        sendMessageToAllUI(0, MAINACTIVITY_SET_TEXT_STATE, "appendText" , "OUT TO SERVER: " + outMessage);
                        outMessage = "x";                                           
                    }                                                           
                    Thread.sleep(100);
 //             if (InStream.readLine().length() > 0) {                             
                        String modifiedSentence = InStream.readLine();              
                        sendMessageToAllUI(0, MAINACTIVITY_SET_TEXT_STATE, "appendText" , "IN FROM SERVER: " + modifiedSentence);
//                  }                                                   
                    Thread.sleep(1000);
                } catch (IOException e) {                               
                    connectionLost();
                    break;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }                               
        }

ソケットを作成するスレッド:

public void run() {
        setName("AttemptConnectionThread");
        connectionStatus = TCP_SOCKET_STATUS_CONNECTING;
        try {
            SocketAddress sockaddr = new InetSocketAddress(serverIP, port);
            tempSocketClient = new Socket(); // Create an unbound socket

            // This method will block no more than timeoutMs. If the timeout occurs, SocketTimeoutException is thrown.
            tempSocketClient.connect(sockaddr, timeoutMs);
            OutStream = new DataOutputStream(tempSocketClient.getOutputStream());
            InStream = new BufferedReader(new InputStreamReader(tempSocketClient.getInputStream()));
            socketClient = tempSocketClient;
            socketClient.setTcpNoDelay(true);
            connected(); 
        } catch (UnknownHostException e) {
            connectionFailed();
        } catch (SocketTimeoutException e) {
            connectionFailed();
        } catch (IOException e) {
            // Close the socket
            try {
                tempSocketClient.close();
            } catch (IOException e2) {
            }
            connectionFailed();
            return;
        }
    } 

サーバ:

public static void main(String[] args) throws IOException {
    String clientSentence;
    String capitalizedSentence;
    try {
        ServerSocket welcomeSocket = new ServerSocket(8888);
        SERVERIP = getLocalIpAddress();
        System.out.println("Connected and waiting for client input!\n Listening on IP: " + SERVERIP +"\n\n");
        Socket connectionSocket = welcomeSocket.accept();
        BufferedReader inFromClient = new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));
        DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream());
        while(true)
        {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            clientSentence = inFromClient.readLine();
            System.out.println("clientSentance == " + clientSentence);
            String ip = connectionSocket.getInetAddress().toString().substring(1);
            if(clientSentence != null)
            {
                System.out.println("In from client ("+ip+")("+ System.currentTimeMillis() +"): "+clientSentence);
                capitalizedSentence = clientSentence.toUpperCase() + '\n';
                outToClient.writeBytes(capitalizedSentence + '\n');
                System.out.println("Out to client ("+ip+"): "+capitalizedSentence);
            }
        }
    } catch (IOException e) {
        //if server is already running, it will not open new port but instead re-print the open ports information
         SERVERIP = getLocalIpAddress();
         System.out.println("Connected and waiting for client input!\n");
         System.out.println("Listening on IP: " + SERVERIP +"\n\n");

    }
}

前もって感謝します!

編集:

  • 更新後にサーバーコードを追加しました
  • ソケットのSoTimoutを設定してみましたが、それを元に戻しました
4

2 に答える 2

2

サーバーは、クライアントから正確に1行を受信し、正確に1行を送り返すように特別に設計されています。コードを見てください:

    while (true) {
        Socket connectionSocket = welcomeSocket.accept();
        BufferedReader inFromClient = new BufferedReader(
                new InputStreamReader(connectionSocket.getInputStream()));
        DataOutputStream outToClient = new DataOutputStream(
                connectionSocket.getOutputStream());

        clientSentence = inFromClient.readLine();
        String ip = connectionSocket.getInetAddress().toString()
                .substring(1);
        System.out.println("In from client (" + ip + "): "
                + clientSentence);
        if (clientSentence != null) {
            capitalizedSentence = clientSentence.toUpperCase() + '\n';
            System.out.println("Out to client (" + ip + "): "
                    + capitalizedSentence);
            outToClient.writeBytes(capitalizedSentence + "\n");
        }

ループ内では、新しい接続を受け入れ、正確に1行を読み取り、次に正確に1行を書き込むことに注意してください。接続は閉じません。それはまともな会話を終わらせません。読むのをやめるだけです。

このサーバーで動作するクライアントは、接続し、正確に1行を送信し、正確に1行を読み戻してから、接続を閉じる必要があります。あなたのクライアントはそれをしません。なんで?あなたはそれがあなたがしなければならなかったことを知らなかったので。なんで?あなたにはデザインがなかったので...計画もありません。

それがあなたの特定の問題です。しかし、どうか、私はあなたに大きな一歩を踏み出し、あなたのアプローチを完全に変えるようにあなたに促します。1行のコードを記述する前に、実際にバイトレベルでプロトコルを設計および指定してください。プロトコルは、どのデータが送信されるか、メッセージがどのように区切られるか、誰がいつ送信するか、誰が接続を閉じるかなどを指定する必要があります。

そうしないと、コードをデバッグできません。上記のサーバーコードを見て、それは正しいですか?まあ、誰が知っています。それが何をすべきかがはっきりしないからです。クライアントを作成したとき、サーバーは一方向に動作すると想定していました。その仮定は有効でしたか?サーバーが壊れていませんか?サーバーが何をすべきかについての仕様がないので、誰が知っていますか。

于 2012-08-08T21:47:53.523 に答える
1

利用可能なデータがあるかどうかを確認する必要があります。

if (InStream.available > 0) {                                                      
   String modifiedSentence = InStream.readLine();
   sendMessageToAllUI(0, MAINACTIVITY_SET_TEXT_STATE, "appendText" , "IN FROM SERVER: " + modifiedSentence); 
}

しかし、正直なところ、それでも理想的ではありません。なぜなら、あなたは、ラインの上のものが受け取られるという保証がないからです。サーバーが数バイトを送信しても行末を送信しない場合でも、永久にブロックされます。本番ソケットコードは決して依存するべきではなくreadLine、代わりにバッファに読み込まれ、そのバッファの行末(またはプロトコルが必要とする基準)をチェックします。


よく読んでいなかったので、一例だと思いInStreamましたInputStreamInputStream持っていavailableます。 InputStreamReaderhas ready(これは順番に呼び出しますInputStream.available。これらのいずれかへの参照を保持している限り、データを読み取ることができるかどうかを確認できます。

于 2012-08-08T21:48:25.400 に答える