http://blog.csdn.net/wufenglong/article/details/5778862
PC 客户端与 Android 服务端的 Socket 同步通信(USB)
分类: android 2010-07-31 17:23 8027 人阅读 评论(52) 收藏 举报
需求:
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?
1. Runtime.getRuntime().exec("adb forward tcp:12580 tcp:10086");
2.
Thread.sleep(3000);
Runtime.getRuntime().exec("adb forward tcp:12580 tc
Thread.sleep(3000);
4.android 端的 service 程序 Install 到手机上容易,但是还要有方法来从 PC 的 client 端来启动手机上的 service ,这个办法可以通过
PC 端 adb 命令来发一个 Broastcast ,手机端再写个接收 BroastcastReceive 来接收这个 Broastcast,在这个 BroastcastReceive 来启动
service
pc 端命令:
view plaincopy to clipboardprint?
1. Runtime.getRuntime().exec(
2. "adb shell am broadcast -a NotifyServiceStart");
Runtime.getRuntime().exec(
"adb shell am broadcast -a NotifyServiceStart");
android 端的代码:ServiceBroadcastReceiver.java
view plaincopy to clipboardprint?
1. package com.otheri.service;
2.
3. import android.content.BroadcastReceiver;
4. import android.content.Context;
5. import android.content.Intent;
6. import android.util.Log;
7.
8. public class ServiceBroadcastReceiver extends BroadcastReceiver {
9.
10.
11.
12.
13.
14.
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");
}
}
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30. }
package com.otheri.service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class ServiceBroadcastReceiver extends Broad
private static String START_ACTION = "Notif
private static String STOP_ACTION = "Notify
@Override
public void onReceive(Context context, Inte
Log.d(androidService.TAG, Thread.cu
+ "ServiceBroadcast
String action = intent.getAction();
if (START_ACTION.equalsIgnoreCase(a
5.由于是 USB 连接,所以 socket 就可以设计为一但连接就一直联通,即在 new socket 和开完 out,in 流后,就用个
while(true){}来循环 PC 端和 android 端的读和写
android 的代码:
view plaincopy to clipboardprint?
1. public void run() {
2.
3.
4.
5.
6.
Log.d(androidService.TAG, Thread.currentThread().getName() + "---->"
+ "a client has connected to server!");
BufferedOutputStream out;
BufferedInputStream in;
try {
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
/* 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 字节文件格式,其后是文件数据 */
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
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 {
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
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.cu
+ "a client has con
BufferedOutputStream out;
BufferedInputStream in;
try {
/* PC端发来的数据msg */
String currCMD = "";
out = new BufferedOutputStr
in = new BufferedInputStrea
// testSocket();// 测试sock
androidService.ioThreadFlag
while (androidService.ioThr
try {
if (!client
bre
}
6.如果是在 PC 端和 android 端的读写操作来 while(true){}循环,这样 socket 流的结尾不好判断,不能用“-1”来判断,
因为“-1”是只有在 socket 关闭时才作为判断结尾。
7.socket 在 out.write(bytes);时,要是数据太大时,超过 socket 的缓存,socket 自动分包发送,所以对方就一定要用
循环来多次读。最好的办法就是服务器和客户端协议好,比如发文件时,先写过来一个要发送的文件的大小,然后再
发送文件;对方用这个大小,来循环读取数据。
android 端接收数据的代码:
view plaincopy to clipboardprint?
1. /**
2.
3.
4.
5.
6.
7.
8.
9.
* 功能:从 socket 流中读取完整文件数据
*
* InputStream in:socket 输入流
*
* byte[] filelength: 流的前 4 个字节存储要转送的文件的字节数
*
* byte[] fileformat:流的前 5-8 字节存储要转送的文件的格式(如.apk)
*
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
* */
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字节存储要转送
*
* */
public static byte[] receiveFileFromSocket(
OutputStream out, byte[] fi
byte[] filebytes = null;// 文件数据
try {
int filelen = MyUtil.bytesT
String strtmp = "read file
out.write(strtmp.getBytes("
out.flush();
8.socket 的最重要的机制就是读写采用的是阻塞的方式,如果客户端作为命令发起者,服务器端作为接收者的话,只
有当客户端 client 用 out.writer()写到输出流里后,即流中有数据 service 的 read 才会执行,不然就会一直停在 read()
那里等数据。
9.还要让服务器端可以同时连接多个 client,即服务器端用 new thread()来作数据读取操作。
源码:
客户端(pc 端):
testPcClient.java
view plaincopy to clipboardprint?
1. import java.io.BufferedInputStream;
2. import java.io.BufferedOutputStream;
3. import java.io.BufferedReader;
4. import java.io.ByteArrayOutputStream;
5. import java.io.IOException;
6. import java.io.InputStream;
7. import java.io.InputStreamReader;
8. import java.net.InetAddress;
9. import java.net.Socket;
10. import java.net.UnknownHostException;
11.
12. public class testPcClient {
13.
14.
15.
16.
17.
18.
19.
20.
21.
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
try {
Runtime.getRuntime().exec(
"adb shell am broadcast -a NotifyServiceStop");