3

私の状況は次のとおりです。

どちらも Android OS を搭載した 2 台のデバイス

One Devices はサーバーです。これは Service で実行され、スレッドを実行して接続とすべての通常のプロトコルを作成します。

もう 1 つは Android デバイスを使用する任意のクライアントです。これも、接続に必要なスレッドを使用してサービスを実行します。

クライアントがアプリを閉じ、サーバーに通知を送信してソケットを閉じても、サーバーが適切に停止しても、すべてが正常に機能します。

問題は、サーバーが突然シャットダウン (電源オフ) したり、wifi を失ったりした場合です。

その後、クライアントは、サーバーがソケットが壊れていることを暗示するのを停止したことを決して検出しません。数分待っても、接続はまだ有効です。また、ObjectOutputStream を使用して 20 秒ごとにパケットを送信するシステムを追加しますが、これは例外なく送信されます。

なぜこれが起こっているのですか?どうすればこの問題を解決できますか?

これに関する問題のある投稿はほとんど読んだことがありませんが、どの解決策でもうまくいきます。

これはサーバー スレッド接続です。

@Override
        public void run() {
            // TODO Auto-generated method stub
            super.run();
            try {
                mSocket.setKeepAlive(true);
                mSocket.setSoTimeout(20 * 1000);
                mOutput = new ObjectOutputStream(mSocket.getOutputStream());
                mInput = new ObjectInputStream(mSocket.getInputStream());
                while (mSocket.isConnected() && isAlive) {
                    try {
                        MotePacket packet = (MotePacket) mInput.readObject();
                        if (packet==null) continue;
                        MotePacket result = renderPacket(packet);
                        if (result==null) continue;
                        mOutput.writeObject(result);
                        mOutput.flush();
                        if (!isAccepted) break;
                    } catch (java.net.SocketTimeoutException ste) {
                        continue;
                    } catch (ClassNotFoundException ex) {
                        continue;
                    } catch (StreamCorruptedException ex) {
                        continue;
                    }

                }
                closeSocket();
            } catch (Exception e) {
                e.printStackTrace();
                try {
                    closeSocket();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }

そして、これはクライアント スレッドです。

@Override
    public void run() {
        // TODO Auto-generated method stub
        super.run();
        try {
            mSocket = new Socket(mIp, mPort + 1);
            mSocket.setSoTimeout(10 * 1000);
            ;// Log.v("MPB", "Sck connected " + mSocket.isConnected());
            mOutput = new ObjectOutputStream(mSocket.getOutputStream());
            mInput = new ObjectInputStream(mSocket.getInputStream());
            ;// Log.v("MPB", "Socket");
            isAlive = true;
            mOutput.writeObject(new MotePacket(Type.DISCOVERY,new ConnectPacket(android_id, DeviceInfo.getDeviceName())));
            mOutput.flush();
            while (mSocket.isConnected() && isAlive) {
                try {
                    MotePacket packet = (MotePacket) mInput.readObject();
                    if (packet==null) continue; 
                    switch (packet.getHeader()) {
                    case DISCOVERY:
                        DiscoveryPacket data = (DiscoveryPacket) packet
                                .getPayload();
                        if (data.getAck() == EngelAck.ENGEL_REJECT) {
                            isAlive = false;
                            mCallback.onConnectionReject();
                            break;
                        }
                        mCallback.onConnectionStablished(data.getNative_service_port());
                        break;
                    case CONNECTION:
                        ;// Log.v("MPB", "Packet connection recevied");
                        mCallback.onPacketReceived((NetPacket) packet.getPayload());
                        break;
                    }
                } catch (java.net.SocketTimeoutException ste) {
                    mSocket.sendUrgentData(1);
                    continue;
                } catch (ClassNotFoundException ex) {
                    ex.printStackTrace();
                    continue;
                } catch (StreamCorruptedException ex) {
                    ex.printStackTrace();
                    continue;
                }

            }
            ;// Log.v("MPB", "Socket dead");
            mCallback.onConnectionLost();
            mSocket.shutdownOutput();
            mSocket.shutdownInput();
            mSocket.close();

        } catch (Exception e) {
            // TODO Auto-generated catch block
            ;// Log.v("MPB", "Socket Exception, closing it");
            mCallback.onConnectionLost();
            e.printStackTrace();
            try {
                mSocket.shutdownOutput();
                mSocket.shutdownInput();
                mSocket.close();
            } catch (Exception e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
        }
    }

私はそれががらくたでいっぱいに見えることを知っていますが、すべてをチェックするだけです. 誰かが私を助けることができれば、私は感謝します

ありがとう。

アップデート

誰かがこれを解決する方法に興味を持っている場合は、@ Nikolai N Fetissov がコメントしている Heartbeat プロトコルを最終的に実行しました。

それを行うために、タイムアウトを使用して新しいスレッドを作成しました。パケットを送信した後にサーバーが応答しなかった場合、これは接続を停止し、クライアントに通知します

Handler hReply;
Runnable checkReply = new Runnable() {

    @Override
    public void run() {
        // TODO Auto-generated method stub
        Log.i("MPB", "Check if closed");
            isAlive=false;
            mCallback.onConnectionLost();
            try {
                mSocket.shutdownOutput();
                mSocket.shutdownInput();
                mSocket.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                Log.i("MPB", "Already closed");
            }
    }

};

public void sendPacketToJavaServer(EngelMotePacket packet) throws Exception {
    if (mOutput!=null) {
        if (hReply!=null)
            hReply.removeCallbacks(checkReply);
        hReply = new Handler();
        Log.v("MPB", "send packet to server");
        mOutput.writeObject(packet);
        mOutput.flush();
        hReply.postDelayed(checkReply, DELAY_REPLY);
    }
    else 
        throw new SocketTimeoutException();
}

そして、クライアントが回答を受け取る部分で、回答が受信された場合はコールバックを削除します

if (hReply !=null) {
Log.i("MPB", "remove check if closed");
hReply.removeCallbacks(checkReply);
}

これが役立つことを願っています。

4

2 に答える 2

0

また、ObjectOutputStream を使用して 20 秒ごとにパケットを送信するシステムを追加しますが、これは例外なく送信されます。

TCP は再送信タイマーを使用して送信の失敗を検出しますが、時間がかかる場合があります (10 分程度)。そのため、データがホストから送信されたが到着していないため、send がエラーを返さない可能性があります。15 分間待って、送信がまだ成功するかどうかを確認してください。

于 2013-05-23T20:47:51.667 に答える