【问题标题】:Android client-server communication throws exception on input.readAndroid 客户端-服务器通信在 input.read 上引发异常
【发布时间】:2014-11-07 13:12:00
【问题描述】:

我正在使用套接字和输入、输出流(没有包装类,如 datainputstream、objectinput 流等)开发 Android-Server 通信

通信基于每侧的三个线程(服务器有额外的线程来接受新的套接字)

第一个线程是导向器,它通过 LinkedBlockingQueue 接受来自接收者的消息,对它们做出反应并通过 LinkedBlockingQueue 将数据发送给发送者

第二个线程是接收器,它定期读取套接字(通过 InputStream.read),如果有消息,它将通过 LinkedBlockingQueue 将其传递给导向器

当连接丢失时,Client-Android 设备(已阻止 input.read)立即抛出连接超时异常

第三个线程是发送者,它定期从 LinkedBlockingQueue 获取消息并将该数据发送到连接的另一端

问题是:防止客户端接收器上的异常抛出(这看起来有点像 Android 的东西,因为单独的 input.read 不应该抛出任何与超时连接相关的异常

这里是接收者的代码:

public class Receiver implements Runnable {

private boolean run = true;

BlockingQueue<MessageQueue> queueReceiverOut;

InputStream in;

////////////////////////////// CONSTRUCTOR ////////////////////////////////

public Receiver(BlockingQueue<MessageQueue> queueReceiverOut, InputStream in) {

    this.queueReceiverOut = queueReceiverOut;
    this.in = in;

}
// ////////////////////////////// METHODS ////////////////////////////////

/**
 * Runs when thread starts.
 */

public void run() {

    int[] message = new int[2];

    byte[] data;

    MessageQueue msg;

    try {

        while(true) {

            msg = new MessageQueue();

            message = receiveMessage();

            System.out.println("receives message");

            if(message[0] != -1) {

                System.out.println("receives full message");

                if(message[1] != 0) {

                    data = receiveData(message[1]);

                    msg.setMessageType(message[0]);
                    msg.setDataLength(message[1]);
                    msg.setData(data);

                    queueReceiverOut.put(msg);

                } else {

                    msg.setMessageType(message[0]);
                    msg.setDataLength(message[1]);
                    queueReceiverOut.put(msg);

                }
            }

        }

    } catch (IOException e) {

        System.out.println("----disconnected-----");

        try {

            MessageQueue msgReceiverOut = new MessageQueue();

            msgReceiverOut.setMessageType(SocketMessages.STATUS_OFFLINE);
            queueReceiverOut.put(msgReceiverOut);

        } catch (InterruptedException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        e.printStackTrace();
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

}



public int[] receiveMessage() throws IOException {

    int[] messageHead = new int[2];

    messageHead[0] = in.read();

    if(messageHead[0] != -1) {

        System.out.println("received message with type : " + messageHead[0]);

        int length1 = in.read();
        int length2 = in.read();
        int length3 = in.read();
        int length4 = in.read();

        messageHead[1] = ((length1 << 24) + (length2 << 16) + (length3 << 8) + (length4 << 0));

        System.out.println(" with length : " + messageHead[1]);

    }

    return messageHead;

}

public byte[] receiveData(int length) throws IOException {

    byte[] buffer = new byte[length];

     // Read in the bytes
    int offset = 0;
    int numRead = 0;
    while (offset < length
    && (numRead = in.read(buffer,
    offset, length - offset)) >= 0) {
    offset += numRead;
    }

    // Ensure all the bytes have been read in
    if (offset < length) {
    throw new IOException("Could not completely read file ");
    }

    return buffer;

}

public boolean isRun() {
    return run;
}

public void setRun(boolean run) {
    this.run = run;
}

}

和发件人:

   public class Sender implements Runnable {

private boolean run = true;

BlockingQueue<MessageQueue> queueSenderIn;

BlockingQueue<MessageQueue> queueSenderOut;

OutputStream out;

////////////////////////////// CONSTRUCTOR ////////////////////////////////

public Sender(BlockingQueue<MessageQueue> queueSenderIn, BlockingQueue<MessageQueue> queueSenderOut, OutputStream out) {

    this.queueSenderOut = queueSenderOut;
    this.queueSenderIn = queueSenderIn;
    this.out = out;

}
// ////////////////////////////// METHODS ////////////////////////////////

/**
 * Runs when thread starts.
 */

@Override
public void run() {

    MessageQueue msg;

    try {

        while(run) {

            msg = queueSenderIn.poll(2, TimeUnit.SECONDS);

            if(msg != null) {

                sendMessage(msg.getMessageType(),msg.getDataLength());

                if(msg.getDataLength()!=0) {

                    sendData(msg.getData());

                }

            }

        }

        Log.v(getClass().getName(),"sender destroyed");

    } catch (IOException e) {
        Log.v(getClass().getName(),"connection closed");
        e.printStackTrace();
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

}



public void sendMessage(int messageType, int dataLength) throws IOException, InterruptedException {

    MessageQueue msg = new MessageQueue();

    Log.v(getClass().getName(), "sending message type : " + messageType);
    out.write(messageType);

    Log.v(getClass().getName(), "sending data with length : " +dataLength);

    out.write((dataLength >>> 24) & 0xFF);
    out.write((dataLength >>> 16) & 0xFF);
    out.write((dataLength >>>  8) & 0xFF);
    out.write((dataLength >>>  0) & 0xFF);

    msg.setMessageType(messageType);

    queueSenderOut.put(msg);

}

public void sendData(byte[] data) throws IOException {

    String string = new String(data,"UTF-8");

    Log.v(getClass().getName(), " with content : " + string);

    out.write(data);

}

public boolean isRun() {
    return run;
}

public void setRun(boolean run) {
    this.run = run;
}

}

更新:因为误解异常

【问题讨论】:

  • 双方定期发送吗? read 只有在调用它时连接已经丢失时才抛出 IOExcpetion。如果它在阻塞时丢失,它不会注意到。

标签: java android sockets client-server


【解决方案1】:

在异常情况下,底层连接可能会被 远程主机或网络软件(例如连接 在 TCP 连接的情况下重置)。当连接断开时 网络软件检测到以下适用于退回的 输入流:

  • 网络软件可能会丢弃由 插座。未被网络软件丢弃的字节可以 使用 read 读取。

  • 如果套接字上没有缓冲的字节,或者所有缓冲的字节 已被 read 消耗,则 所有后续调用 read 将 抛出 IOException。

  • 如果套接字上没有缓冲字节,并且套接字没有 已使用 close 关闭,则 available 将返回 0。

来自:http://docs.oracle.com/javase/7/docs/api/java/net/Socket.html#getInputStream()

特别注意“后续调用”部分。这意味着如果您已经在 read 调用中阻塞,则上述关于读取调用的条件(尚)不适用。

到目前为止的解释。现在到解决方案: 即使通信空闲,您也可以(多种可能性之一)定期发送消息。因此,您的发件人会检测到连接丢失,并且也可以关闭插播广告。


编辑:让它更清楚一点...

  1. 连接丢失
  2. 调用读取
  3. IOException

  1. 调用读取(阻塞!)
  2. 等待输入:连接丢失
  3. --没有例外!

我想重点是(我假设)您的服务器进入读取并在那里停留很长时间,而您的客户端在连接断开时接收数据。因此它将不断地从read 调用和返回。在等待读取解除阻塞时仍有可能发生连接丢失,但可能性要小得多。

【讨论】:

  • 好吧,据我了解,如果连接中断,所有后续的读取调用(例如,已读取输入的服务器)都会抛出 IOException。它适用于一直有 input.read 的客户端,当互联网关闭时,它会抛出 io 异常。但具有相同线程的服务器(例如,从 inputstream 读取)不会抛出任何东西,只是挂起读取。跨度>
  • 连接断开后,您的服务器是否曾解除对读取的阻止?我猜不会。所以没有“后续”调用。
  • 我现在很困惑..服务器接收器和客户端接收器是相同的编码类,具有相同的方法,但一个抛出异常,另一个不抛出,你能更具体一点吗?
  • 立即获取,对此感到抱歉。它不完全是IOexception,而是抛出了SSLException,但客户端接收器对其做出反应,因为它是IOException,但服务器不是......
  • 你有那个堆栈跟踪吗?那么,我建议更新您的问题。我会删除答案,因为那是没有意义的。
猜你喜欢
  • 2011-07-06
  • 1970-01-01
  • 1970-01-01
  • 2016-05-05
  • 2013-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多