Android客户端 - 服务器从不检测连接丢失

时间:2013-05-23 08:48:43

标签: android sockets tcp client-server

我的情况如下:

两个设备,均使用Android OS运行,

One Devices是服务器,它运行Service并运行线程来创建连接和所有正常协议,

另一个是带有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协议。

要做到这一点,我创建了一个超时的新线程,如果服务器在发送数据包后没有回复,则停止conexion并通知客户端

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);
}

希望这可以提供帮助。

2 个答案:

答案 0 :(得分:3)

解决此类问题的传统方法是实施应用程序级协议心跳。假设没有其他应用程序数据通过连接发送,请在N秒不活动后发送心跳消息。这意味着另一方可以假设在没有收到任何消息超过N秒后连接已经死亡。

答案 1 :(得分:0)

  

此外,我添加了一个使用ObjectOutputStream每20秒发送一个数据包的系统,但这是在没有任何异常的情况下发送的。

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