20

Android 2.3 では正常に動作する TCP ソケット接続がありますが、Android 4.1 ではいくつかの問題に直面しています。問題は、接続が閉じられているように、InputStream.read() メソッドが常に (ブロックせずに) -1 を返すことです。

ソケットの作成:

SocketFactory socketFactory = SocketFactory.getDefault();
Socket socket = socketFactory.createSocket("c.whatsapp.net", 5222);
socket.setSoTimeout(3*60*1000);
socket.setTcpNoDelay(true);

入力ストリームと出力ストリームを取得し、いくつかの初期データを書き込みます。

InputStream inputStream = new BufferedInputStream(socket.getInputStream());
OutputStream outputStream = new BufferedOutputStream(socket.getOutputStream());

outputStream.write(87);
outputStream.write(65);
outputStream.write(1);
outputStream.write(2);
outputStream.flush();

次に、この条件は常にブロックせずに通過します。

int c = inputStream.read();
if (c < 0) {
    Log.d(TAG, "End of stream");
}

このコードはバックグラウンド スレッドで実行されています。そして、ジンジャーブレッドに取り組んでいました。

直接ストリームの代わりに InputStreamReader と OutputStreamWriter を使用しようとしましたが、効果はありません。

4

7 に答える 7

0

同様の問題があり、このような回避策で修正しました

private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);

private static class WatchDog implements Runnable{
    private Thread thread = Thread.currentThread();

    public void run() {
        Log.d(LOG_TAG, "Interrupting read due to timeout");
        thread.interrupt();
    }
}

private void read(InputStream in, ByteBuffer bb, long waitTime) throws IOException {
    int startingPos = bb.position();
    long timeout = System.currentTimeMillis() + RESPONSE_TIMEOUT;


    ScheduledFuture<?> watchdogFuture = executor.schedule(new WatchDog(), RESPONSE_TIMEOUT, TimeUnit.MILLISECONDS);
    try {
        while(System.currentTimeMillis() < timeout && bb.hasRemaining()){ //workaround fixing timeout after 1ms
            try{
                int read = in.read(bb.array(), bb.position(), bb.remaining());
                if(read > 0){
                    bb.position(bb.position()+read);
                }
            } catch(SocketTimeoutException e){}
            if(bb.hasRemaining()){
                Thread.sleep(5);
            }
        }
        watchdogFuture.cancel(true);
    } catch (InterruptedException e) {}


    if(bb.hasRemaining()){
        throw new SocketTimeoutException("Unable to read requested bytes: " 
                + (bb.position()-startingPos) + "/" +  (bb.limit()-startingPos)
                + " after " + (System.currentTimeMillis() - timeout + RESPONSE_TIMEOUT) + "ms");
    }
}
于 2013-10-25T07:44:30.570 に答える
0

inputStream.read()-1が返され、例外が発生しないという同様の問題がありました。実際、サーバーがダウンし、接続が切断されました。4.0のみで、さまざまなバージョンでテストしませんでした。

この動作に関するGoogle バグ レポートは次のとおりです。

残念ながら、バグのステータスは再現できないため「クローズ」されているようです。

私の回避策は、-1 をソケットのクローズと到達不能なサーバーとして解釈することでした。再接続しようとすると、適切なエラーが発生します。

于 2013-09-13T16:55:10.040 に答える
0

BufferedReader と PrintWriter の使用は、私にとってすべてのバージョンで機能し、任意の通信プロトコルを介して必要なもの (JSON 文字列を含む) を送受信するのに非常に便利です。次のようにバックグラウンド スレッドを開始するときに、メンバー変数として保存してみてください。

mInput = new BufferedReader(new InputStreamReader(
            socket.getInputStream()));
mOutput = new PrintWriter(new BufferedWriter(
            new OutputStreamWriter(socket.getOutputStream())), true);

非同期通信の場合、バックグラウンド スレッドは次のようになります。

@Override
public final void run() {
    while (!Thread.currentThread().isInterrupted()) {
        if (mInput == null) {
            break;
        }
        String message = null;
        try {
            message = mInput.readLine();
        } catch (IOException e) {
            // handle the exception as you like
            break;
        }
        if (Thread.currentThread().isInterrupted()) {
            // thread was interrupted while reading
            break;
        } else if (message != null) {
            // handle the message as you like
        }
    }
}

別のバックグラウンド スレッドを使用してメッセージを送信します。

@Override
public void run() {
    if (mOutput != null) {
        mOutput.println(<message to be );
        if (mOutput == null) {
            // the above thread was interrupted while writing
        } else if (!mOutput.checkError()) {
            // everything went fine
        } else {
            // handle the exception
        }
    }
}

また、readLine が永久にブロックされないように、外部からストリームを閉じる必要があります。

try {
    mOutput.close();
    mInput.close();
    mOutput = null;
    mInput = null;
} catch (IOException e) {
    // log the exception
}

現在、TCP ソケットを使用しているため、ソケットが実際には死んでおり、readLine がまだブロックしている場合があります。それを検出して、上記のようにストリームを閉じる必要があります。そのためには、キープアライブ メッセージを定期的に送信する別のスレッドを (まあ) 追加する必要があります。X 秒間リモート デバイスからメッセージを受信しなかった場合、ストリームを閉じる必要があります。

この全体的なアプローチにより、ソケットが閉じられ、すべてのスレッドがすべての状況で終了することが保証されます。もちろん、必要な場合は、送信側スレッドを削除し、代わりにリーダー スレッド内に println() を含めることで、通信を同期させることができます。お役に立てば幸いです (回答が 8 か月遅れても)。

于 2013-11-08T13:14:39.703 に答える
0

友人、

try inputStream.readLine();(ie) DataInputStream.readLine();(非推奨の方法)

これは私のために働いた...

于 2013-10-25T04:39:36.937 に答える
0

このコードを試してください -

Runnable runnable = new Runnable() {

    @Override
    public void run() {

        synchronized (this) {
            Socket s = null;
            String inMsg = null, msg2 = null;
            try {
                try {
                    s = new Socket(server, port);
                } catch (Exception e) {
                    return;
                }
                BufferedReader in = new BufferedReader(
                        new InputStreamReader(s.getInputStream()));
                BufferedWriter out = new BufferedWriter(
                        new OutputStreamWriter(s.getOutputStream()));
                try {
                    inMsg = in.readLine()
                            + System.getProperty("line.separator");
                } catch (Exception e) {
                    return;
                }

                out.write(message + "\n\r");
                out.flush();
                try {
                    msg2 = in.readLine();
                    if (msg2 == null) {
                        return;
                    }
                } catch (Exception e) {
                    return;
                }
                out.close();
                s.close();
            } catch (Exception e) {
                return;
            }
        }

    }

};

わたしにはできる。

于 2013-12-13T08:18:48.170 に答える
-1

Apache Commons IO を使用する必要があります: http://commons.apache.org/proper/commons-io/

http://commons.apache.org/proper/commons-io/javadocs/api-release/index.html?org/apache/commons/io/package-summary.htmlを参照してください。IOUtils.copy()

于 2013-09-12T08:15:44.287 に答える