Android socket 使用过程中跟 JAVA socket 基本相同,不过还是略有区别,在使用过程中有两点需要注意:
- 接收数据的逻辑
简单的接收逻辑都是读取字符流,while循环按行读取字符串:
private static void startRecive(Socket client) {
try {
log("startRecive");
InputStream ins = client.getInputStream();
InputStreamReader insr = new InputStreamReader(client.getInputStream());
BufferedReader reader = new BufferedReader(insr);
String str;
while((str = reader.readLine()) != null) {
log(str);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
log("Client Exit");
}
如果需要直接接收二进制内容(比如传输二进制文件),使用如下方式:
private static void startRecive(final Socket client) {
reciveThread = new Thread() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
log("startRecive");
InputStream ins = client.getInputStream();
byte[] bytes = new byte[1024];
while(ins.read(bytes) != -1) {
log(new String(bytes));
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
reciveThread.start();
}
需要注意的地方是,一般情况下(socket为阻塞模式)连接成功不断开,while条件中的 read 或 readLine 在收不到数据的时候是阻塞状态,也就是如果需要保持连接多次发送信息,需要自己对每次发送的信息设置间隔符号(按行发送的末尾添加\r\n),并在while(read)读到之后下一次循环之前判断并处理,例如下面:
reciveThread = new Thread() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
log("startRecive");
InputStream ins = client.getInputStream();
byte[] bytes = new byte[1024];
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int size = -1;
while((size=ins.read(bytes)) != -1) { // 收不到数据的时候回block在read
// 接收到消息之后判断间隔符,并在下一次while之前处理数据
if(size > 0 && bytes[size-1] == 0x24 /* $的ascii码值 */) {
baso.write(bytes, 0, size-1);
handleData(baos.toByteArray());
continue;
}
baos.write(bytes);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
- 对端关闭退出的监测逻辑:
一般情况下,当对端的socket关闭退出或进程意外终止,本端接收逻辑会捕获到SocketException(继承自IOException),根据测试这里的情况Android与JAVA略有区别:
- java
直接关闭对端程序,本端有日志:
java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at MainServer.startRecive(MainServer.java:50)
at MainServer.access$0(MainServer.java:45)
at MainServer$1.run(MainServer.java:34)
对应代码:
private static void startRecive(Socket client) {
try {
log("startRecive");
InputStream ins = client.getInputStream();
byte[] bytes = new byte[1024];
while(ins.read(bytes) != -1) { //此处抛出异常
log(new String(bytes));
}
log("Recive exit!");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
当对端执行 socket.shutdownOutput() 或 socket.close() 时,本端有输出:
Recive exit!
- Android
直接关闭对端应用(在【应用管理】中直接杀死应用,或在【最近任务】中清除应用),或对端应用执行 socket.shutdownOutput() 或 socket.close() ,本端应用有日志:
01-01 04:18:36.287 14529 14551 D MainServer: recive exit!
综上,在 JAVA 中对端进程强制终止时,本端会捕获到SocketException, 在Android 中对端应用程序被强制终止时,本端不会捕获到SocketException,而是会read()到 -1,并退出阻塞读状态;
在 JAVA 和 Android 中对端 socket 执行 shutdownOutput 或 close 时,本端都会read()到 -1,并退出阻塞读状态;
针对如上情况,可以在 Receive线程的while和catch流程之后,增加对端 socket 关闭或退出后的处理逻辑,如下例:
private static void startRecive(Socket conn) {
try {
log("startRecive");
InputStream ins = conn.getInputStream();
InputStreamReader insr = new InputStreamReader(client.getInputStream());
BufferedReader reader = new BufferedReader(insr);
String str;
while(ins.read(bytes) != -1) { //此处抛出异常
log(new String(bytes));
}
log("Recive exit!"); // 对端关闭退出
} catch (IOException e) { // 其他连接异常
// TODO Auto-generated catch block
e.printStackTrace();
} finally { //
try {
// 当前是服务端,关闭对应客户端的socket实例
conn.shutDownInputStream();
conn.shutDownOutStream();
conn.close();
// 当前是客户端,关闭当前socket实例
// socket.shutDownInputStream();
// socket.shutDownOutStream();
// socket.close();
} catch (IOException e) {
e.printStackTrace();
}
log("Client Exit");
}
}