最近项目中有一个功能需要用到Android与PC端同步数据。查阅了相关资料后,采取了一种建立在adb基础之上的Usb通信方式:由于adb可以将Usb模拟为网卡,所以可以利用socket通信的方式实现Android与PC机的通信,以完成同步功能。
一、Android与PC通信的实现
在《PC客户端与Android服务端的Sockect同步通信》一文详细介绍了建立在adb基础之上的usb(socket)实现的具体方法。大体上的思路如下:
①Android作为server,侦探socket连接请求;添加一个服务类来实现侦听功能。
②PC端作为Client,请求建立socket连接。
③Android端添加一个广播接收类,接受PC端通过Adb发送的广播消息,以启动或者停止①中添加的服务。
④PC端通过Adb发送广播消息通知Android端启动或停止用来侦听socket连接的服务。
1、PC端通过Adb发送广播,使Android端开启侦听Socket的服务,然后再请求连接。代码如下:
//连接
public bool DoConnect()
{
string strCmd = "adb shell am broadcast -a NotifyServiceStop";
Execute(strCmd, wait_ms);
Thread.Sleep(wait_ms);
strCmd = "adb forward tcp:12580 tcp:10086";
Execute(strCmd, wait_ms);
Thread.Sleep(wait_ms);
strCmd = "adb shell am broadcast -a NotifyServiceStart";
Execute(strCmd, wait_ms);
Thread.Sleep(wait_ms);
IPAddress ipaddress = IPAddress.Parse("127.0.0.1");
tcpClient.Connect(ipaddress, 12580);
Thread.Sleep(wait_ms);
if (tcpClient != null)
{
NetworkStream networkkStream = tcpClient.GetStream();
networkkStream.ReadTimeout = timeOut;
networkkStream.WriteTimeout = timeOut;
reader = new BinaryReader(networkkStream);
writer = new BinaryWriter(networkkStream);
return true;
}
else
return false;
}
其中,Execute()函数用来执行cmd命令,private string Execute(string command, int seconds)
{
string output = ""; //输出字符串
if (command != null && !command.Equals(""))
{
Process process = new Process();//创建进程对象
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "cmd.exe";//设定需要执行的命令
startInfo.Arguments = "/C " + command;//“/C”表示执行完命令后马上退出
startInfo.UseShellExecute = false;//不使用系统外壳程序启动
startInfo.RedirectStandardInput = false;//不重定向输入
startInfo.RedirectStandardOutput = true; //重定向输出
startInfo.CreateNoWindow = true;//不创建窗口
process.StartInfo = startInfo;
try
{
if (process.Start())//开始进程
{
if (seconds == 0)
{
process.WaitForExit();//这里无限等待进程结束
}
else
{
process.WaitForExit(seconds); //等待进程结束,等待时间为指定的毫秒
}
output = process.StandardOutput.ReadToEnd();//读取进程的输出
}
}
finally
{
if (process != null)
process.Close();
}
}
return output;
}
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(ConnectService.TAG, Thread.currentThread().getName() + "---->"
+ "ServiceBroadcastReceiver onReceive");
String action = intent.getAction();
if (START_ACTION.equalsIgnoreCase(action)) {
context.startService(new Intent(context, ConnectService.class));
Log.d(ConnectService.TAG, Thread.currentThread().getName() + "---->"
+ "ServiceBroadcastReceiver onReceive start end");
} else if (STOP_ACTION.equalsIgnoreCase(action)) {
context.stopService(new Intent(context, ConnectService.class));
Log.d(ConnectService.TAG, Thread.currentThread().getName() + "---->"
+ "ServiceBroadcastReceiver onReceive stop end");
}
}
}
然后,添加一个服务类,用来侦听客户端的连接请求:
public class ConnectService extends Service{
public static final String TAG = "chl";
public static Boolean mainThreadFlag = true;
public static Boolean ioThreadFlag = true;
ServerSocket serverSocket = null;
final int SERVER_PORT = 10086;
@Override
public IBinder onBind(Intent intent)
{
return null;
}
@Override
public void onCreate()
{
super.onCreate();
Log.d(TAG, "androidService--->onCreate()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
mainThreadFlag = true;
new Thread()
{
public void run()
{
doListen();
};
}.start();
return START_NOT_STICKY;
}
private void doListen()
{
serverSocket = null;
try
{
serverSocket = new ServerSocket(SERVER_PORT);
while (mainThreadFlag)
{
Socket socket = serverSocket.accept();
new Thread(new ThreadReadWriterIOSocket(this, socket)).start();
}
} catch (IOException e)
{
e.printStackTrace();
}
}
@Override
public void onDestroy()
{
super.onDestroy();
mainThreadFlag = false;
ioThreadFlag = false;
try
{
if (serverSocket != null)
serverSocket.close();
} catch (IOException e)
{
e.printStackTrace();
}
}
}
还需要添加一个线程类,来处理客户端的连接请求。建立一个连接后,启动一个此线程来完成与客户端的通信。在这里可以定义与实际需求相应的通信协议。
public class ThreadReadWriterIOSocket implements Runnable{
private Socket client;
private Context context;
private PigProtocol pigProtocol;
public ThreadReadWriterIOSocket(Context context, Socket client)
{
this.client = client;
this.context = context;
pigProtocol = new PigProtocol();
}
@Override
public void run(){
BufferedOutputStream out;
BufferedInputStream in;
try {
Header header = null;
out = new BufferedOutputStream(client.getOutputStream());
in = new BufferedInputStream(client.getInputStream());
ConnectService.ioThreadFlag = true;
while (ConnectService.ioThreadFlag){
try {
if(!client.isConnected()){
break;
}
header = pigProtocol.readHeaderFromSocket(in);
switch (header.CmdId) {
case 0x0001:
//
break;
case 0x0002:
//
break;
default:
break;
}
}
catch (Exception e)
{
// TODO: handle exception
e.printStackTrace();
}
}
out.close();
in.close();
}
catch (Exception e)
{
// TODO: handle exception
e.printStackTrace();
}
finally
{
try
{
if (client != null)
{
client.close();
}
} catch (IOException e)
{
e.printStackTrace();
}
}
}
}
最后,还需要修改程序清单manifest.xml,加入代码,使程序能够接收到广播消息,并且指定处理消息的类。注意,在指定接收广播消息类、服务类时,一定要指出完整名称,如com.example.connect.ServiceBroadcastReceiver。
二、Android与PC端传通过socket传递对象
有了上面的工作,就可以实现Android与PC端通过socket(usb)传递简单的字符串了。但是在实际使用过程中,我们更多的往往是传递对象而不是简单的字符串。最简单的方法是定义相应的协议,在发送端将对象拼接为字符串,然后在接收将接收到的字符串端拆分组合为对象。但是这样实现起来不是很方便,《C#(服务器)与Java(客户端)通过Socket传递对象》一文介绍了利用Json来传递对象的方法。利用文中介绍的方法可以很方便的实现对象传递。《DataContractJsonSerializer类操作json类型数据》一文介绍了C#端json类型数据的具体使用方法。
我们知道,C#和Java都是完全面向对象的语言,它们都提供了List
1、在PC端,首先需要完成Json类型与List
public static string Obj2Json(T data)
{
try
{
DataContractJsonSerializer json = new DataContractJsonSerializer(data.GetType());
using (MemoryStream ms = new MemoryStream())
{
json.WriteObject(ms, data);
return Encoding.UTF8.GetString(ms.ToArray());
}
}
catch (System.Exception ex)
{
throw ex;
}
}
public static object Json2Obj(string strJson, Type t)
{
try
{
DataContractJsonSerializer json = new DataContractJsonSerializer(t);
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(strJson)))
{
return json.ReadObject(ms);
}
}
catch (System.Exception ex)
{
throw ex;
}
}
对象转换为Json字符串:
try {
String strTmp = gson.toJson(content);
out.write(strTmp.getBytes());
out.flush();
} catch (Exception e) {
// TODO: handle exception
Log.d("error","writeHeaderFromSocket" + e.getMessage());
}
byte[] tempbuffer = new byte[MAX_BUFFER_BYTES];
try
{
int numReadedBytes = in.read(tempbuffer, 0, MAX_BUFFER_BYTES);
String strJson = new String(tempbuffer, 0, numReadedBytes, "utf-8");
MyType myType = gson.fromJson(strJson, MyType.class);
tempbuffer = null;
} catch (Exception e)
{
e.printStackTrace();
}
byte[] tempbuffer = new byte[MAX_BUFFER_BYTES];
try
{
int numReadedBytes = in.read(tempbuffer, 0, MAX_BUFFER_BYTES);
String strJson = new String(tempbuffer, 0, numReadedBytes, "utf-8");
List listMyType = gson.fromJson(strJson,
new TypeToken>(){}.getType());
tempbuffer = null;
} catch (Exception e)
{
e.printStackTrace();
}