我正在使用套接字和输入,输出流(没有包装类,如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
调用中进行了阻止,则上述(尚未)与读调用有关的条件不适用。到目前为止的解释。现在到解决方案:
即使通信空闲,您也可以(多种可能性之一)定期发送消息。因此,您的发送者将检测到连接丢失,并且也可以关闭视频插播。
编辑:为了使它更清晰一点...
然而
我想要指出的是(我假设)您的服务器进入读取状态并停留了很长时间,而您的客户端在连接断开时正在接收数据。因此它将不断地从
read
调用并返回。在等待读取取消阻止时仍然有可能发生连接丢失,但是可能性要小得多。
关于java - Android客户端-服务器通信在input.read上引发异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25823826/