java - Android客户端-服务器通信在input.read上引发异常

标签 java android sockets client-server

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

通讯基于每边的三个线程(服务器具有额外的线程,可以接受新的套接字)

第一个线程是导向器,它通过LinkedBlockingQueue接收来自接收者的消息,对其进行 react 并通过LinkedBlockingQueue将数据发送给发送者

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

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

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

问题是:防止在ClientSide接收器上引发异常(这看起来像一些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;
}

}

更新:因为误解异常

最佳答案

Under abnormal conditions the underlying connection may be broken by the remote host or the network software (for example a connection reset in the case of TCP connections). When a broken connection is detected by the network software the following applies to the returned input stream :

  • The network software may discard bytes that are buffered by the socket. Bytes that aren't discarded by the network software can be read using read.

  • If there are no bytes buffered on the socket, or all buffered bytes have been consumed by read, then all subsequent calls to read will throw an IOException.

  • If there are no bytes buffered on the socket, and the socket has not been closed using close, then available will return 0.



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

尤其要注意“后续调用”部分。这意味着,如果您已经在read调用中进行了阻止,则上述(尚未)与读调用有关的条件不适用。

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

编辑:为了使它更清晰一点...
  • 连接丢失
  • 调用已读
  • IOException

  • 然而
  • 调用已读取(块!)
  • 等待输入:连接丢失
  • -不异常(exception)!

  • 我想要指出的是(我假设)您的服务器进入读取状态并停留了很长时间,而您的客户端在连接断开时正在接收数据。因此它将不断地从read调用并返回。在等待读取取消阻止时仍然有可能发生连接丢失,但是可能性要小得多。

    关于java - Android客户端-服务器通信在input.read上引发异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25823826/

    相关文章:

    sockets - gethostbyname()(实际上,Boost Asio resolve())完全在2小时(120分钟,7200秒)后发生段故障

    c++ - ZMQ C++ 从特定工作人员发送和接收

    java - 我如何让多个组件响应 JSF 中的单个事件?

    java - XPath Count 函数始终返回 0.0

    Android 屏蔽

    安卓线程/回调

    java - 通过 FilterInputStream 计算的文件大小比实际大小高 12.5%

    java - 如何从 Firebase 获取值(value)

    java - Android:BufferedReader 第二次后返回错误数据

    Java 服务器 : start a new thread for each received packet?