通过ADB将USB模拟为网卡,创建Socket进行通讯
-
- 前言
-
-
- 应用场景
- 实现效果
- 实现思路
-
- Android服务端实现
-
-
- MainActivity.java
- TcpConnectRunnable.java
-
- PC客户端实现
-
-
- FrmClient.cs
- SocketClient.cs
- DriverDetector.cs
- ADB操作
-
- 运行效果
-
-
- Android服务端
- PC客户端
-
- 参考资料
前言
应用场景
适用于工作环境无网络,只能通过USB将多台Android端数据上传到一台PC端的情况。
实现效果
- 启动PC端工作站
- 自动检测通过USB连接的Android端设备
- 自动启动Android端数据上传app
- 通过Socket向Android端发送命令
- Android端通过Socket上传数据
- 上传完成后自动关闭app
PC端工作站可长时间开启,外业人员工作回来后,将设备插到电脑上即可,上传操作将会自动执行,可同时插入多台Android设备。
实现思路
- 通过ADB将USB模拟为网卡
- Android端作为服务端,创建ServerSocket,监听客户端命令
- PC端作为客户端,请求建立Socket连接,向Android端发送命令
Android服务端实现
MainActivity.java
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private TextView tvText;
private TcpConnectRunnable tcpConnectRunnable = new TcpConnectRunnable(new TcpConnectRunnable.Callback() {
@Override
public void call(final String msg) {
// 回调信息显示到消息输出窗口
runOnUiThread(new Runnable() {
@Override
public void run() {
tvText.append(msg + "\r\n");
if (msg.contains("quit")) {
finish();
}
}
});
}
});
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvText = findViewById(R.id.tv_message);
// 清空消息输出窗口
findViewById(R.id.btn_clear).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
tvText.setText("");
}
});
// 开启Socket服务器
new Thread(tcpConnectRunnable).start();
}
@Override
protected void onDestroy() {
tcpConnectRunnable.stop();
super.onDestroy();
}
}
TcpConnectRunnable.java
import android.text.TextUtils;
import android.util.Log;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Socket服务器,持续监听客户端连接
*/
public class TcpConnectRunnable implements Runnable {
private static final String TAG = TcpConnectRunnable.class.getSimpleName();
private final int SERVER_PORT = 10086;
private ServerSocket serverSocket;
private Socket client;
private Callback callback;
private boolean isRun = false;
public TcpConnectRunnable(Callback callback) {
this.callback = callback;
}
/**
* 停止
*/
public void stop() {
isRun = false;
}
@Override
public void run() {
isRun = true;
try {
String ip = InetAddress.getLocalHost().getHostAddress();
serverSocket = new ServerSocket(SERVER_PORT);
callback.call("建立服务器:[" + ip + ":" + SERVER_PORT + "]");
}catch (IOException e) {
callback.call("建立服务器异常:" + e.getMessage());
}
while (isRun) {
BufferedOutputStream out = null;
BufferedReader in = null;
try {
client = serverSocket.accept();
callback.call("建立连接:" + client.getInetAddress().toString() + ":" + client.getPort());
out = new BufferedOutputStream(client.getOutputStream());
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
if (isRun == false) {
break;
}
String request = receive(in);
if (TextUtils.isEmpty(request))
{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
continue;
}
callback.call("client: " + request);
if ("quit".equals(request)) {
callback.call("origin request: " + request);
break;
}
if (isRun == false) {
break;
}
send(out, request);
} catch (IOException e) {
Log.e(TAG, "run: ", e);
callback.call(e.getMessage());
} finally {
close(out);
close(in);
close(client);
}
}
}
/**
* 向客户端发送数据
* @param out
* @param msg
* @throws IOException
*/
private void send(OutputStream out, String msg) throws IOException {
msg += "\n";
out.write(msg.getBytes("utf-8"));
}
/**
* 接收客户端的请求,并返回应答
* @param in
* @return
* @throws IOException
*/
private String receive(BufferedReader in) throws IOException {
String r = in.readLine();
if (TextUtils.isEmpty(r)) {
return "";
}
callback.call("origin request: " + r);
if (r.contains("upload")) {
r = r + " finished.";
}
return r;
}
private void close(OutputStream out) {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void close(BufferedReader in) {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void close(Socket socket) {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
Log.e(TAG, "run: ", e);
}
}
}
/**
* 消息回调,用于更新UI
*/
public interface Callback {
/**
* 回调
* @param msg
*/
void call(String msg);
}
}
PC客户端实现
FrmClient.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
namespace PhoneAidClient
{
public partial class FrmClient : Form
{
/// <summary>
/// 设备插拔检测的最小间隔(毫秒)
/// 用于避免插入设备触发多次事件的情况
/// </summary>
private static long DEVICE_DETECT_MIN_INTERVAL = 1000;
/// <summary>
/// 用于管理ADB命令等
/// </summary>
private CmdHelp cmdHelp = new CmdHelp();
/// <summary>
/// adb接收到数据的事件,一般只用在adb的开启关闭上,其他时候数据都由其他地方处理
/// </summary>
private CmdHelp.DelegateReceiveThreadData m_receiveDelegate = null;
/// <summary>
/// 系统信息的处理类
/// </summary>
private DriveDetector detector = new DriveDetector();
/// <summary>
/// 插入的设备列表
/// </summary>
private List<string> devices = new List<string>();
/// <summary>
/// 上次检测到设备插拔的时间(毫秒)
/// </summary>
private long lastDeviceDetectTime = -1;
/// <summary>
/// 用于锁住ADB的变量,否则一边作业一遍连接usb等操作,可能导致ADB崩溃
/// </summary>
private object m_lock = string.Empty;
/// <summary>
/// 本地端口列表
/// </summary>
private List<int> localPorts = new List<int>();
/// <summary>
/// 随机端口
/// </summary>
private Random randomPort = new Random();
public FrmClient()
{
InitializeComponent();
}
private void FrmClient_Load(object sender, EventArgs e)
{
//防止adb未完全关闭
Process[] adbList = Process.GetProcessesByName("adb");
foreach (Process adb in adbList)
{
adb.Kill();
adb.Close();
adb.Dispose();
}
treeDevice.Nodes.Clear();
treeDevice.Nodes.Add("已连接设备");
treeDevice.ExpandAll();
//注册事件
CmdHelp.EventReceiveData += new CmdHelp.DelegateReceiveData(CmdHelp_EventReceiveData);
m_receiveDelegate = new CmdHelp.DelegateReceiveThreadData(CmdHelp_EventReceiveThreadData);
CmdHelp.EventReceiveThreadData += m_receiveDelegate;
CmdHelp.EventReceiveThreadErrorData += new CmdHelp.DelegateReceiveThreadErrorData(CmdHelp_EventReceiveThreadErrorData);
//启动ADB服务
Thread openADBThread = new Thread(new ThreadStart(() =>
{
//先关闭,再开启,防止上一次的意外退出
cmdHelp.SendAdbCmd(CmdAdbInfo.adb_start_server);
}));
openADBThread.Start();
//搜索软件启动前插入的设备
StartSearchDevice();
//对系统的设备拔入拔出进行监听
detector.DeviceArrived += new EventHandler<DriveDetector.DriveDectctorEventArgs>((obj, ee) =>
{
long now = (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000;
if (lastDeviceDetectTime > 0)
{
if (now - lastDeviceDetectTime < DEVICE_DETECT_MIN_INTERVAL)
{
return;
}
}
lastDeviceDetectTime = now;
AppendText("设备插入或拔出");
StartSearchDevice();
});
detector.DeviceRemoved += new EventHandler<DriveDetector.DriveDectctorEventArgs>((obj, ee) =>
{
long now = (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000;
if (lastDeviceDetectTime > 0)
{
if (now - lastDeviceDetectTime < DEVICE_DETECT_MIN_INTERVAL)
{
return;
}
}
lastDeviceDetectTime = now;
AppendText("设备插入或拔出");
StartSearchDevice();
});
}
private void FrmClient_FormClosed(object sender, FormClosedEventArgs e)
{
// 退出时关闭adb
Process[] adbList = Process.GetProcessesByName("adb");
foreach (Process adb in adbList)
{
adb.Kill();
adb.Close();
adb.Dispose();
}
}
/// <summary>
/// 接收到数据(阻塞线程)
/// </summary>
/// <param name="data"></param>
private void CmdHelp_EventReceiveData(string data)
{
AppendTextInvoke(data);
}
/// <summary>
/// 接收错误数据
/// </summary>
/// <param name="data"></param>
private void CmdHelp_EventReceiveThreadErrorData(string data)
{
AppendTextInvoke(data);
}
/// <summary>
/// 从线程接收到数据(不阻塞线程)
/// </summary>
/// <param name="data"></param>
private void CmdHelp_EventReceiveThreadData(string data)
{
AppendTextInvoke(data);
}
/// <summary>
/// 接收系统的消息(可以捕获设备连接断开的消息)
/// </summary>
/// <param name="m"></param>
protected override void WndProc(ref Message m)
{
bool habled = false;
detector.WndProc(m.HWnd, m.Msg, m.WParam, m.LParam, ref habled);
base.WndProc(ref m);
}
/// <summary>
/// 开线程后台进行检索
/// </summary>
private void StartSearchDevice()
{
Thread searchThread = new Thread(new ThreadStart(() =>
{
//如果马上搜索设备,可能搜索不到
Thread.Sleep(1000);
SearchDevice();
}));
searchThread.Start();
}
/// <summary>
/// 通过ADB查找设备
/// 一般只在开启ADB成功之后,以及电脑检测到设备接入的时候运行
/// </summary>
private void SearchDevice()
{
lock (m_lock)
{
string[] deviceInfoList = cmdHelp.GetDevices(CmdAdbInfo.adb_devices);
// 一次插拔可能会触发多次事件,只在第一次时查找设备,
// 但第一次时查找可能查不到,因此等待一下后再次查询
if (deviceInfoList == null || deviceInfoList.Length < 1)
{
Thread.Sleep(500);
deviceInfoList = cmdHelp.GetDevices(CmdAdbInfo.adb_devices);
}
this.Invoke(new Action<string[]>(RefreshTree), new object[1] { deviceInfoList });
}
}
/// <summary>
/// 刷新设备列表
/// </summary>
/// <param name="deviceInfoList"></param>
private void RefreshTree(string[] deviceInfoList)
{
// 是否有新插入的设备
for (int i = 0; i < deviceInfoList.Length; i++)
{
string deviceNo = deviceInfoList[i];
if (devices.Contains(deviceNo) == false)
{
treeDevice.Nodes[0].Nodes.Add(cmdHelp.GetDeviceIMEI(deviceNo), deviceNo);
devices.Add(deviceNo);
// 新插入设备,开始工作
int port = randomPort.Next(10000, 20000);
while (localPorts.Contains(port))
{
port = randomPort.Next(10000, 20000);
}
localPorts.Add(port);
StartWork(deviceNo, port.ToString());
}
}
// 是否有拔出的设备
for (int i = devices.Count-1; i >= 0; i--)
{
if (deviceInfoList.Contains(devices[i]) == false)
{
treeDevice.Nodes[0].Nodes.RemoveAt(i);
// 移除设备,停止工作
StopWork(devices[i]);
devices.RemoveAt(i);
}
}
treeDevice.ExpandAll();
}
/// <summary>
/// 开始工作
/// 1.检查软件是否有安装,若有安装,则获取版本
/// 2.检查是否为新版本,不是的话,安装新版本
/// 3.启动软件
/// 4.创建socket,请求数据
/// 5.关闭软件
/// </summary>
/// <param name="deviceNo"></param>
private void StartWork(string deviceNo, string localPort)
{
// 启动软件
cmdHelp.RunApp(deviceNo, "com.zhd.phoneaid/com.zhd.phoneaid.MainActivity");
// 端口映射
cmdHelp.Forward(deviceNo, localPort, "10086");
// 创建socket,请求数据
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = false;
worker.DoWork += Worker_DoWork;
worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
worker.RunWorkerAsync(localPort);
}
private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// 退出软件
//cmdHelp.ExitApp(deviceNo, "com.zhd.phoneaid");
}
private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
SocketClient socketClient = new SocketClient(e.Argument.ToString());
string result = socketClient.Request("upload file1");
AppendTextInvoke(result);
Thread.Sleep(10000);
result = socketClient.Request("upload file2");
AppendTextInvoke(result);
Thread.Sleep(100);
// 通知Android服务端退出
socketClient.Request("quit");
localPorts.Remove(int.Parse(socketClient.GetPort()));
}
private void StopWork(string deviceNo)
{
AppendText(deviceNo + " : 已拔出");
}
/// <summary>
/// 输出消息
/// </summary>
/// <param name="data"></param>
private void AppendText(string data)
{
if (string.IsNullOrEmpty(data))
{
return;
}
if (data.EndsWith("\r\n") == false)
{
data = data + "\r\n";
}
txtMessage.AppendText(data);
}
/// <summary>
/// 输出消息(子线程调用)
/// </summary>
/// <param name="data"></param>
private void AppendTextInvoke(string data)
{
this.Invoke(new Action<string>(AppendText), new object[1] { data });
}
}
}
SocketClient.cs
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace PhoneAidClient
{
public class SocketClient
{
/// <summary>
/// 客户端端口
/// </summary>
private string localPort;
public string GetPort()
{
return localPort;
}
public SocketClient(string local_port)
{
this.localPort = local_port;
}
/// <summary>
/// 发送请求
/// </summary>
/// <param name="msg"></param>
/// <returns></returns>
public string Request(string msg)
{
Socket client = null;
try
{
// 创建客户端
client = Create();
// 连接到服务端
Connect(client, localPort);
if (client.Connected)
{
// 发送请求
Send(client, msg);
// 接收应答
return Receive(client);
}
else
{
return "连接失败";
}
}
catch (Exception e)
{
return $"Error:{e.Message}";
}
finally
{
// 断开连接
if (client.Connected)
{
client.Shutdown(SocketShutdown.Both);
client?.Close();
}
client = null;
}
}
/// <summary>
/// 创建Socket客户端
/// </summary>
/// <returns></returns>
private static Socket Create()
{
return new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
}
/// <summary>
/// 连接到服务端
/// </summary>
/// <param name="socket"></param>
/// <param name="port"></param>
private static void Connect(Socket socket, string port)
{
IPAddress iPAddress = IPAddress.Parse("127.0.0.1");
IPEndPoint iPEndPoint = new IPEndPoint(iPAddress, int.Parse(port));
socket.Connect(iPEndPoint);
}
/// <summary>
/// 发送请求
/// </summary>
/// <param name="socket"></param>
/// <param name="msg"></param>
private static void Send(Socket socket, string msg)
{
msg += "\n";
byte[] data = Encoding.UTF8.GetBytes(msg);
socket.Send(data);
}
/// <summary>
/// 接收应答
/// </summary>
/// <param name="socket"></param>
/// <returns></returns>
private static string Receive(Socket socket)
{
string str = "";
byte[] data = new byte[1024];
int len = 0;
int i = 0;
while ((i = socket.Receive(data, 1024, SocketFlags.None)) != 0)
{
len += i;
string piece = Encoding.UTF8.GetString(data, 0, i);
str += piece;
}
return str;
}
}
}
DriverDetector.cs
using System;
using System.Runtime.InteropServices;
namespace PhoneAidClient
{
/// <summary>
/// 监听设备的插入和拔出
/// </summary>
public class DriveDetector
{
/// <summary>
/// 设备插入事件
/// </summary>
public event EventHandler<DriveDectctorEventArgs> DeviceArrived = null;
/// <summary>
/// 设备拔出事件
/// </summary>
public event EventHandler<DriveDectctorEventArgs> DeviceRemoved = null;
/// <summary>
/// 消息处理(HwndSourceHook委托的签名)
/// </summary>
/// <param name="hwnd"></param>
/// <param name="msg"></param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <param name="handled"></param>
/// <returns></returns>
public IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == NativeConstants.WM_DEVICECHANGE)
{
switch (wParam.ToInt32())
{
case NativeConstants.DBT_DEVICEARRIVAL:
{
var devType = Marshal.ReadInt32(lParam, 4);
if (devType == NativeConstants.DBT_DEVTYP_VOLUME)
{
if (DeviceArrived != null)
{
DeviceArrived(this, null); //触发设备插入事件
}
}
}
break;
case NativeConstants.DBT_DEVICEREMOVECOMPLETE:
{
var devType = Marshal.ReadInt32(lParam, 4);
if (devType == NativeConstants.DBT_DEVTYP_VOLUME)
{
if (DeviceRemoved != null)
{
DeviceRemoved(this, null);
}
}
}
break;
//64位win10系统下,设备连接断开都发送一样的命令
case NativeConstants.DBT_DEVICEARRIVAL64:
DeviceArrived(this, null); //触发设备插入事件
break;
}
}
return IntPtr.Zero;
}
/// <summary>
/// 设备插入或拔出事件
/// </summary>
public class DriveDectctorEventArgs : EventArgs
{
/// <summary>
/// 获得设备卷标
/// </summary>
public string Drive { get; private set; }
public DriveDectctorEventArgs(string drive)
{
Drive = drive ?? string.Empty;
}
}
public partial class NativeConstants
{
/// WM_DEVICECHANGE -> 0x0219
public const int WM_DEVICECHANGE = 537;
/// BROADCAST_QUERY_DENY -> 0x424D5144
//public const int BROADCAST_QUERY_DENY = 1112363332;
//public const int DBT_DEVTYP_DEVICEINTERFACE = 5;
//public const int DBT_DEVTYP_HANDLE = 6;
public const int DBT_DEVICEARRIVAL = 0x8000; // system detected a new device
//public const int DBT_DEVICEQUERYREMOVE = 0x8001; // Preparing to remove (any program can disable the removal)
public const int DBT_DEVICEREMOVECOMPLETE = 0x8004; // removed
public const int DBT_DEVTYP_VOLUME = 0x00000002; // drive type is logical volume
public const int DBT_DEVICEARRIVAL64 = 0x00000007;
}
}
}
ADB操作
FrmClient中使用的CmdHelp用于执行ADB命令,涉及很多没有使用到的方法,就不附上了,下面只列出使用到的ADB命令。
- 启动adb服务
start-server
- 获取设备列表
devices
- 获取指定设备的IMEI
-s {deviceName} shell getprop gsm.mtk.imei1
- 启动app
-s {deviceName} shell am start -n {package/package.activity}
- 端口映射
-s {deviceName} forward tcp:{client port} tcp:{server port}
运行效果
Android服务端

PC客户端
参考资料
- PC通过USB连接Android通信(Socket)
- Android通过USB与PC通信