【问题标题】:Android client - server never detect connection lostAndroid客户端 - 服务器永远不会检测到连接丢失
【发布时间】:2013-05-23 08:48:43
【问题描述】:

我的情况如下:

两台设备,均运行 Android 操作系统,

一个设备是服务器,它与服务一起运行并运行线程以创建连接和所有正常协议,

另一个是任何带有安卓设备的客户端,它也运行一个带有必要线程的服务来连接。

一切正常,即使客户端关闭应用程序,向服务器发送通知并关闭套接字,如果服务器也正常停止。

问题是服务器突然关机(Power off)或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 cmets 的 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);
}

希望这能有所帮助。

【问题讨论】:

    标签: android sockets tcp client-server


    【解决方案1】:

    解决此类问题的常规方法是实现应用程序级协议心跳。假设在N 不活动秒后没有要通过连接发送的其他应用程序数据发送心跳消息。这意味着对方可以在超过N秒后没有收到任何消息后认为连接已失效。

    【讨论】:

    • 谢谢,我知道这会起作用,但我想避免,无论如何,我每 20 秒发送一个字节,如果在 10 秒内没有收到回复,我关闭连接。
    【解决方案2】:

    我还添加了一个系统,该系统使用 ObjectOutputStream 每 20 秒发送一个数据包,但这是毫无例外地发送的。

    TCP 使用重传计时器来检测发送失败,但它可能需要一些时间(甚至 10 分钟)。所以有可能 send 没有返回错误,因为数据是从主机发送的,但没有到达。尝试等待 15 分钟,看看发送是否仍然成功。

    【讨论】:

    • 无论如何,如果这就像你说的那样,对我的应用程序来说是完全不可接受的。所以最后我决定像@Nikolai N Fetissov 所说的那样实现心跳协议。我想避免这种情况,但接缝独特的解决方案
    猜你喜欢
    • 2018-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多