3

我的情况如下:

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

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

另一个是任何带有 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 评论的心跳协议。

为此,我创建了一个超时的新线程,如果服务器在发送数据包后没有回复,则停止连接并通知客户端

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

3

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

于 2013-05-23T12:35:59.827 回答
0

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

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

于 2013-05-23T20:47:51.667 回答