PC 客户端与 Android 服务端的 Socket 同步通信(USB)
需求:
1.一个 android 端的 service 后台运行的程序,作为 socket 的服务器端;用于接收 Pc client
端发来的命令,来处理数据后,把结果发给 PC client
2.PC 端程序,作为 socket 的客户端,用于给 android 手机端发操作命令
难点分析:
1.手机一定要有 adb 模式,即插上 USB 线时马上提示的对话框选 adb。好多对手机的
操作都可以用 adb 直接作
不过,我发现 LG GW880 就没有,要去下载个
2.android 默认手机端的 IP 为“127.0.0.1”
3.要想联通 PC 与 android 手机的 sokcet,一定要用 adb forward 来作下端口转发才能连
上 socket.
view plaincopy to clipboardprint?
Runtime.getRuntime().exec("adb forward tcp:12580 tcp:10086");
Thread.sleep(3000);
Runtime.getRuntime().exec("adb forward tcp:12580 tcp:10086");
Thread.sleep(3000);
4.android 端的 service 程序 Install 到手机上容易,但是还要有方法来从 PC 的 client
端来启动手机上的 service ,这个办法可以通过 PC 端 adb 命令来发一个 Broastcast ,手机
端再写个接收 BroastcastReceive 来接收这个 Broastcast,在这个 BroastcastReceive 来启动
service
pc 端命令:
view plaincopy to clipboardprint?
Runtime.getRuntime().exec(
"adb shell am broadcast -a NotifyServiceStart");
Runtime.getRuntime().exec(
"adb shell am broadcast -a NotifyServiceStart");
android 端的代码:ServiceBroadcastReceiver.java
view plaincopy to clipboardprint?
package com.otheri.service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class ServiceBroadcastReceiver extends BroadcastReceiver {
private static String START_ACTION = "NotifyServiceStart";
private static String STOP_ACTION = "NotifyServiceStop";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(androidService.TAG, Thread.currentThread().getName() + "---->"
+ "ServiceBroadcastReceiver onReceive");
String action = intent.getAction();
if (START_ACTION.equalsIgnoreCase(action)) {
context.startService(new Intent(context, androidService.class));
Log.d(androidService.TAG, Thread.currentThread().getName() + "---->"
+ "ServiceBroadcastReceiver onReceive start end");
} else if (STOP_ACTION.equalsIgnoreCase(action)) {
context.stopService(new Intent(context, androidService.class));
Log.d(androidService.TAG, Thread.currentThread().getName() + "---->"
+ "ServiceBroadcastReceiver onReceive stop end");
}
}
}
package com.otheri.service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class ServiceBroadcastReceiver extends BroadcastReceiver {
private static String START_ACTION = "NotifyServiceStart";
private static String STOP_ACTION = "NotifyServiceStop";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(androidService.TAG, Thread.currentThread().getName() + "---->"
+ "ServiceBroadcastReceiver onReceive");
String action = intent.getAction();
if (START_ACTION.equalsIgnoreCase(action)) {
context.startService(new Intent(context, androidService.class));
Log.d(androidService.TAG, Thread.currentThread().getName() + "---->"
+ "ServiceBroadcastReceiver onReceive start end");
} else if (STOP_ACTION.equalsIgnoreCase(action)) {
context.stopService(new Intent(context, androidService.class));
Log.d(androidService.TAG, Thread.currentThread().getName() + "---->"
+ "ServiceBroadcastReceiver onReceive stop end");
}
}
}
5.由于是 USB 连接,所以 socket 就可以设计为一但连接就一直联通,即在 new socket 和
开完 out,in 流后,就用个 while(true){}来循环 PC 端和 android 端的读和写
android 的代码:
view plaincopy to clipboardprint?
public void run() {
Log.d(androidService.TAG, Thread.currentThread().getName() + "---->"
+ "a client has connected to server!");
BufferedOutputStream out;
BufferedInputStream in;
try {
/* PC 端发来的数据 msg */
String currCMD = "";
out = new BufferedOutputStream(client.getOutputStream());
in = new BufferedInputStream(client.getInputStream());
// testSocket();// 测试 socket 方法
androidService.ioThreadFlag = true;
while (androidService.ioThreadFlag) {
try {
if (!client.isConnected()) {
break;
}
/* 接收 PC 发来的数据 */
Log.v(androidService.TAG, Thread.currentThread().getName()
+ "---->" + "will read......");
/* 读操作命令 */
currCMD = readCMDFromSocket(in);
Log.v(androidService.TAG, Thread.currentThread().getName()
+ "---->" + "**currCMD ==== " + currCMD);
/* 根据命令分别处理数据 */
if (currCMD.equals("1")) {
out.write("OK".getBytes());
out.flush();
} else if (currCMD.equals("2")) {
out.write("OK".getBytes());
out.flush();
} else if (currCMD.equals("3")) {
out.write("OK".getBytes());
out.flush();
} else if (currCMD.equals("4")) {
/* 准备接收文件数据 */
try {
out.write("service receive OK".getBytes());
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
/* 接收文件数据,4 字节文件长度,4 字节文件格式,其后是文
件数据 */
byte[] filelength = new byte[4];
byte[] fileformat = new byte[4];
byte[] filebytes = null;
/* 从 socket 流中读取完整文件数据 */
filebytes = receiveFileFromSocket(in, out, filelength,
fileformat);
// Log.v(Service139.TAG, "receive data =" + new
// String(filebytes));
try {
/* 生成文件 */
File file = FileHelper.newFile("R0013340.JPG");
FileHelper.writeFile(file, filebytes, 0,
filebytes.length);
} catch (IOException e) {
e.printStackTrace();
}
} else if (currCMD.equals("exit")) {
}
} catch (Exception e) {
// try {
// out.write("error".getBytes("utf-8"));
// out.flush();
// } catch (IOException e1) {
// e1.printStackTrace();
// }
Log.e(androidService.TAG, Thread.currentThread().getName()
+ "---->" + "read write error111111");
}
}
out.close();
in.close();
} catch (Exception e) {
Log.e(androidService.TAG, Thread.currentThread().getName()
+ "---->" + "read write error222222");
e.printStackTrace();
} finally {
try {
if (client != null) {
Log.v(androidService.TAG, Thread.currentThread().getName()
+ "---->" + "client.close()");
client.close();
}
} catch (IOException e) {
Log.e(androidService.TAG, Thread.currentThread().getName()
+ "---->" + "read write error333333");
e.printStackTrace();
}
}
public void run() {
Log.d(androidService.TAG, Thread.currentThread().getName() + "---->"
+ "a client has connected to server!");
BufferedOutputStream out;
BufferedInputStream in;
try {
/* PC 端发来的数据 msg */
String currCMD = "";
out = new BufferedOutputStream(client.getOutputStream());
in = new BufferedInputStream(client.getInputStream());
// testSocket();// 测试 socket 方法
androidService.ioThreadFlag = true;
while (androidService.ioThreadFlag) {
try {
if (!client.isConnected()) {
break;
}
/* 接收 PC 发来的数据 */
Log.v(androidService.TAG, Thread.currentThread().getName()
+ "---->" + "will read......");
/* 读操作命令 */
件数据 */
currCMD = readCMDFromSocket(in);
Log.v(androidService.TAG, Thread.currentThread().getName()
+ "---->" + "**currCMD ==== " + currCMD);
/* 根据命令分别处理数据 */
if (currCMD.equals("1")) {
out.write("OK".getBytes());
out.flush();
} else if (currCMD.equals("2")) {
out.write("OK".getBytes());
out.flush();
} else if (currCMD.equals("3")) {
out.write("OK".getBytes());
out.flush();
} else if (currCMD.equals("4")) {
/* 准备接收文件数据 */
try {
out.write("service receive OK".getBytes());
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
/* 接收文件数据,4 字节文件长度,4 字节文件格式,其后是文
byte[] filelength = new byte[4];
byte[] fileformat = new byte[4];
byte[] filebytes = null;
/* 从 socket 流中读取完整文件数据 */
filebytes = receiveFileFromSocket(in, out, filelength,
fileformat);
// Log.v(Service139.TAG, "receive data =" + new
// String(filebytes));
try {
/* 生成文件 */
File file = FileHelper.newFile("R0013340.JPG");
FileHelper.writeFile(file, filebytes, 0,
filebytes.length);
} catch (IOException e) {
e.printStackTrace();
}
} else if (currCMD.equals("exit")) {
}
} catch (Exception e) {
// try {
// out.write("error".getBytes("utf-8"));
// out.flush();
// } catch (IOException e1) {
// e1.printStackTrace();
// }
Log.e(androidService.TAG, Thread.currentThread().getName()
+ "---->" + "read write error111111");
}
}
out.close();
in.close();
} catch (Exception e) {
Log.e(androidService.TAG, Thread.currentThread().getName()
+ "---->" + "read write error222222");
e.printStackTrace();
} finally {
try {
if (client != null) {
Log.v(androidService.TAG, Thread.currentThread().getName()
+ "---->" + "client.close()");
client.close();
}
} catch (IOException e) {
Log.e(androidService.TAG, Thread.currentThread().getName()
+ "---->" + "read write error333333");
e.printStackTrace();
}
}
6.如果是在 PC 端和 android 端的读写操作来 while(true){}循环,这样 socket 流的结尾不好
判断,不能用“-1”来判断,因为“-1”是只有在 socket 关闭时才作为判断结尾。
7.socket 在 out.write(bytes);时,要是数据太大时,超过 socket 的缓存,socket 自动分包发
送,所以对方就一定要用循环来多次读。最好的办法就是服务器和客户端协议好,比如发文
件时,先写过来一个要发送的文件的大小,然后再发送文件;对方用这个大小,来循环读取
数据。
android 端接收数据的代码:
view plaincopy to clipboardprint?
/**
* 功能:从 socket 流中读取完整文件数据
*
* InputStream in:socket 输入流
*
* byte[] filelength: 流的前 4 个字节存储要转送的文件的字节数
*
* byte[] fileformat:流的前 5-8 字节存储要转送的文件的格式(如.apk)
*
* */
public static byte[] receiveFileFromSocket(InputStream in,
OutputStream out, byte[] filelength, byte[] fileformat) {
byte[] filebytes = null;// 文件数据
try {
int filelen = MyUtil.bytesToInt(filelength);// 文件长度从 4 字节 byte[]转成 Int
String strtmp = "read file length ok:" + filelen;
out.write(strtmp.getBytes("utf-8"));
out.flush();
filebytes = new byte[filelen];
int pos = 0;
int rcvLen = 0;
while ((rcvLen = in.read(filebytes, pos, filelen - pos)) > 0) {
pos += rcvLen;
}
Log.v(androidService.TAG, Thread.currentThread().getName()
+ "---->" + "read file OK:file size=" + filebytes.length);
out.write("read file ok".getBytes("utf-8"));
out.flush();
} catch (Exception e) {
Log.v(androidService.TAG, Thread.currentThread().getName()
+ "---->" + "receiveFileFromSocket error");
e.printStackTrace();
}
return filebytes;
}
/**
* 功能:从 socket 流中读取完整文件数据
*
* InputStream in:socket 输入流
*
* byte[] filelength: 流的前 4 个字节存储要转送的文件的字节数
*
* byte[] fileformat:流的前 5-8 字节存储要转送的文件的格式(如.apk)