Android设备通过USB线连接PC进行Socket通信
Android设备分别作为客户端和服务端与PC通讯
- 背景简介
- 核心原理
- 整体思路
- 代码讲解
- 1.创建广播监听类ConnectStateReceiver
- 2.建立Socket连接
- 3.Socket连接成功,数据交互
- 4.断开连接,释放资源
- 彩蛋部分
- 1.FileTransferUtil.java
- 1.SendRequestUtil.java
- 完整Demo下载:
背景简介
这是一项比较老旧的技术了,不过在一些特定的场合(例如:无网络),进行传输数据,使用这项技术还是很方便的。网上有好很多类似的文章,个人觉得都不是特别完整,正好公司最近有涉及这方面的功能需求,借此机会整理Android分别作为客户端和服务端的实现。
核心原理
其实Socket通信,最根本的就是,Socket客户端和Socket服务端通过输入流和输出流进行数据传输。
整体思路
1.通过广播,来监听USB状态及Socket状态。
2.建立Socket连接。
3.获取Socket对象中的输入流或输出流,实现数据的接收或发送。
4.释放资源,关闭连接。
代码讲解
使用Rxjava和线程池来进行异步处理,发挥Service的优势增加稳定性。虽然是个小的功能,不过我也进行了多层封装,是代码更加健壮,灵活。
1.创建广播监听类ConnectStateReceiver
根据不同场景,分别进行说明
//服务端在监听
private static final String ACTION_SERVER_LISTENING = "SocketServerListening";
//Socket连接成功
private static final String ACTION_CONNECT_SUCCESS = "SocketConnectSuccess";
//Socket连接失败
private static final String ACTION_CONNECT_FAIL = "SocketConnectFail";
//USB线连接状态
private static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE";
场景一,Android作为客户端,PC作为服务端:(要求Android设备与PC所处同一局域网内)
1.ACTION_USB_STATE :USB连接状态,系统自行发送。
2.ACTION_SERVER_LISTENING :PC的Socket服务端正在监听,PC发送。
3.ACTION_CONNECT_SUCCESS :Socket连接成功,PC发送。
4.ACTION_CONNECT_FAIL :Socket连接失败,PC发送。
场景二,Android作为服务端,PC作为客户端:
1.ACTION_USB_STATE :USB连接状态,系统自行发送。
2.ACTION_CONNECT_SUCCESS :Socket连接成功,Android发送。
3.ACTION_CONNECT_FAIL :Socket连接失败,Android发送。
如何处理接收到的通知:
String action = intent.getAction();if (TextUtils.equals(action, ACTION_SERVER_LISTENING)) {ToastUtil.showToast(context, "服务端开始监听");LogUtil.e("服务端开始监听");//连接服务端SocketManager.getInstance().connectServer(context);} else if (TextUtils.equals(action, ACTION_CONNECT_SUCCESS)) {ToastUtil.showToast(context, "Socket连接成功");LogUtil.e("Socket连接成功");//1.初始化发送请求工具类SendRequestUtil.getInstance().init(SocketManager.getInstance().getDataOutputStream());//2.启动Service,接收消息context.startService(new Intent(context, DataService.class));} else if (TextUtils.equals(action, ACTION_CONNECT_FAIL)) {ToastUtil.showToast(context, "Socket连接失败");LogUtil.e("Socket连接失败");} else if (TextUtils.equals(action, ACTION_USB_STATE)) {boolean connectedState = intent.getBooleanExtra("connected", false);if (connectedState) {ToastUtil.showToast(context, "USB已连接,开始监听客户端");LogUtil.e("USB已连接,开始监听客户端");//开始监听SocketManager.getInstance().acceptClient(context);} else {ToastUtil.showToast(context, "USB断开连接");LogUtil.e("USB断开连接");//1.关闭Service,停止接收消息context.stopService(new Intent(context, DataService.class));//2.释放socketSocketManager.getInstance().close();}}
以上代码结合了两种场景,可以根据自己的需求,对代码进行适当删减。
2.建立Socket连接
根据不同场景,分别进行说明
场景一,Android作为客户端,PC作为服务端:(要求Android设备与PC所处同一局域网内)
收到PC端发来的“Socket服务端正在监听”通知(即ACTION_SERVER_LISTENING)后,进行连接操作:
public void connectServer(final Context context) {mConnectDisposable = Observable.create(new ObservableOnSubscribe<Boolean>() {@Overridepublic void subscribe(ObservableEmitter<Boolean> emitter) throws Exception {Socket socket = connect();if (socket != null) {emitter.onNext(true);} else {emitter.onNext(false);}emitter.onComplete();}}).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<Boolean>() {@Overridepublic void accept(Boolean aBoolean) throws Exception {if (aBoolean) {ToastUtil.showToast(context, "连接服务端成功");mState = STATE_CONNECT_SUCCESSED;} else {ToastUtil.showToast(context, "连接服务端失败");mState = STATE_CONNECT_FAILED;//弹出Dialog,提示重新连接}}});}private Socket connect() {Socket socket = null;try {socket = new Socket(AppConfig.SERVER_ADDRESS, AppConfig.SERVER_PORT);} catch (IOException e) {e.printStackTrace();}if (socket != null) {mSocket = socket;try {mDataInputStream = new DataInputStream(mSocket.getInputStream());mDataOutputStream = new DataOutputStream(mSocket.getOutputStream());} catch (IOException e) {e.printStackTrace();}}return socket;}
场景二,Android作为服务端,PC作为客户端:
当收到系统发送的“USB已连接状态”通知(即ACTION_USB_STATE),后进行Socket监听,等待PC接入:
public void acceptClient(final Context context) {if(mState != STATE_LISTEN){mState = STATE_LISTEN;mAcceptDisposable = Observable.create(new ObservableOnSubscribe<Boolean>() {@Overridepublic void subscribe(ObservableEmitter<Boolean> emitter) throws Exception {Socket socket = accept();if (socket != null) {emitter.onNext(true);} else {emitter.onNext(false);}emitter.onComplete();}}).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<Boolean>() {@Overridepublic void accept(Boolean aBoolean) throws Exception {if (aBoolean) {ToastUtil.showToast(context, "接入客户端成功");mState = STATE_CONNECT_SUCCESSED;context.sendBroadcast(new Intent(ConnectStateReceiver.ACTION_CONNECT_SUCCESS));} else {ToastUtil.showToast(context, "接入客户端失败");mState = STATE_CONNECT_FAILED;context.sendBroadcast(new Intent(ConnectStateReceiver.ACTION_CONNECT_FAIL));}}});}}private Socket accept() {Socket socket = null;try {if(mServerSocket == null){mServerSocket = new ServerSocket(AppConfig.LOCAL_PORT);}socket = mServerSocket.accept();} catch (IOException e) {e.printStackTrace();}if (socket != null) {mSocket = socket;try {mDataInputStream = new DataInputStream(mSocket.getInputStream());mDataOutputStream = new DataOutputStream(mSocket.getOutputStream());mServerSocket.close();} catch (IOException e) {e.printStackTrace();}}return socket;}
3.Socket连接成功,数据交互
Socket成功建立连接,收到“Socket连接成功”通知(即ACTION_CONNECT_SUCCESS),我们就可以通过操作Socket提供的输入流和输出流进行数据交互。
Socket为双向通信,既可以接收数据,也可以发送数据。
接收数据
原理:读取Socket中的输入流,如果需要下载文件,则配合本地的输出流,就可以进行消息或文件的接收。
在后台启动一个Service,用于接收数据,直到USB断开连接。
context.startService(new Intent(context, DataService.class));
以下为Service代码以及接收数据线程的代码 (FileTransferUtil是封装的工具类,用于文件的操作):
public class DataService extends Service {private ExecutorService mExecutorService;@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {super.onCreate();mExecutorService = Executors.newCachedThreadPool();}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {ReceiveRunnable receiveRunnable = new ReceiveRunnable(SocketManager.getInstance().getDataInputStream());mExecutorService.execute(receiveRunnable);return START_NOT_STICKY;}@Overridepublic void onDestroy() {super.onDestroy();mExecutorService.shutdown();}
}public class ReceiveRunnable implements Runnable {private DataInputStream mDataInputStream;public ReceiveRunnable(DataInputStream dis) {mDataInputStream = dis;}@Overridepublic void run() {try {while (true) {String jsonRaw = mDataInputStream.readUTF();DataEvent dataEvent = new DataEvent("testJSON");dataEvent.setTestJSON(jsonRaw);EventManager.post(dataEvent);FileTransferUtil.getInstance().downloadFile(mDataInputStream, fileLength);}} catch (IOException e) {e.printStackTrace();}}
}
发送数据
原理:操作Socket中的输出流,如果需要上传文件,则配合本地的输入流,就可以进行消息或文件的发送。
初始化SendRequestUtil工具类
SendRequestUtil.getInstance().init(SocketManager.getInstance().getDataOutputStream());
(SendRequestUtil是封装的工具类,用于发送消息及文件操作,可随时在点击事件里面进行调用)
以下为SendRequestUtil中的核心代码:
public Disposable post(final String jsonString, final File localFile) {return Observable.create(new ObservableOnSubscribe<Boolean>() {@Overridepublic void subscribe(ObservableEmitter<Boolean> emitter) throws Exception {if (localFile == null) {emitter.onNext(sendInfor(jsonString));} else {emitter.onNext(sendInforAndFile(jsonString, localFile));}emitter.onComplete();}}).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<Boolean>() {@Overridepublic void accept(Boolean aBoolean) throws Exception {if(mRequestListener == null){return;}if(aBoolean){mRequestListener.onRequestSuccessed();}else{mRequestListener.onRequestFailed();}}});}private boolean sendInfor(String json) {try {mDataOutputStream.writeUTF(json);mDataOutputStream.flush();} catch (IOException e) {e.printStackTrace();try {mDataOutputStream.flush();} catch (IOException e1) {e1.printStackTrace();}return false;}return true;}private boolean sendInforAndFile(String json, File file) {boolean result = false;try {mDataOutputStream.writeUTF(json);result = FileTransferUtil.getInstance().uploadFile(mDataOutputStream, file);} catch (IOException e) {e.printStackTrace();}return result;}
4.断开连接,释放资源
在我们数据交互结束后,一定要记得释放资源。
通过监听USB的连接状态,可以在USB线断开连接的时候,收到“USB连接状态”通知(即ACTION_USB_STATE),进行资源的释放。
1.停止Service,不再接收数据。
context.stopService(new Intent(context, DataService.class));
2.释放socket,关闭文件流
SocketManager.getInstance().close();
看一下close()方法中的代码:
public void close() {if (mSocket != null) {mState = STATE_NONE;try {mDataInputStream.close();mDataOutputStream.close();mSocket.close();mSocket = null;} catch (IOException e) {e.printStackTrace();}}}
彩蛋部分
1.FileTransferUtil.java
public class FileTransferUtil {private static volatile FileTransferUtil mInstance = null;private Timer mTimer;private TimerTask mTimerTask = null;private String mProgress = "";private FileTransferUtil() {mTimer = new Timer();}public static FileTransferUtil getInstance() {if (mInstance == null) {synchronized (FileTransferUtil.class) {if (mInstance == null) {mInstance = new FileTransferUtil();}}}return mInstance;}public void downloadFile(DataInputStream dataInputStream, long fileLength) {FileOutputStream fos = null;try {fos = new FileOutputStream(AppConfig.SAVE_PATH + AppConfig.DB_FILE_NAME);byte[] b = new byte[4 * 1024];int length;long writeLength = 0L;while ((length = dataInputStream.read(b)) != -1) {fos.write(b, 0, length);writeLength += length;int progress = (int) (((float) writeLength / (float) fileLength) * 100);if (writeLength >= fileLength) {transferCompleted();break;}updateProgress(progress + "%");}} catch (IOException ioe) {ioe.printStackTrace();transferFailed();} finally {try {if (fos != null) {fos.close();}} catch (IOException e) {e.printStackTrace();}}}public boolean uploadFile(DataOutputStream dataOutputStream, File file) {FileInputStream fis = null;try {long fileLength = file.length();fis = new FileInputStream(file);int length;long readLength = 0L;byte[] b = new byte[4 * 1024];while ((length = fis.read(b)) != -1) {dataOutputStream.write(b, 0, length);readLength += (long) length;int progress = (int) (((float) readLength / (float) fileLength) * 100);if (readLength >= fileLength) {transferCompleted();break;}updateProgress(progress + "%");}return true;} catch (IOException ioe) {ioe.printStackTrace();transferFailed();return false;} finally {try {if (fis != null) {fis.close();}dataOutputStream.flush();} catch (IOException e) {e.printStackTrace();}}}private void updateProgress(String progress) {mProgress = progress;if (mTimerTask == null) {mTimerTask = new TimerTask() {@Overridepublic void run() {DataEvent dataEvent = new DataEvent(FileConstant.FILE_PROGRESS);dataEvent.setProgress(mProgress);EventManager.post(dataEvent);}};mTimer.schedule(mTimerTask, 1000);}}private void transferCompleted() {mTimer.cancel();mTimerTask = null;DataEvent dataEvent = new DataEvent(FileConstant.FILE_TRANSFER_COMPLETE);dataEvent.setProgress("100%");EventManager.post(dataEvent);}private void transferFailed() {mTimer.cancel();mTimerTask = null;DataEvent dataEvent = new DataEvent(FileConstant.FILE_TRANSFER_FAILED);EventManager.post(dataEvent);}
}
1.SendRequestUtil.java
public class SendRequestUtil {private DataOutputStream mDataOutputStream;private SendRequestUtil() {}private static class Holder {private static final SendRequestUtil INSTANCE = new SendRequestUtil();}public static SendRequestUtil getInstance() {return Holder.INSTANCE;}public void init(DataOutputStream dos) {mDataOutputStream = dos;}public Disposable post(final String jsonString, final File localFile) {return Observable.create(new ObservableOnSubscribe<Boolean>() {@Overridepublic void subscribe(ObservableEmitter<Boolean> emitter) throws Exception {if (localFile == null) {emitter.onNext(sendInfor(jsonString));} else {emitter.onNext(sendInforAndFile(jsonString, localFile));}emitter.onComplete();}}).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<Boolean>() {@Overridepublic void accept(Boolean aBoolean) throws Exception {if(mRequestListener == null){return;}if(aBoolean){mRequestListener.onRequestSuccessed();}else{mRequestListener.onRequestFailed();}}});}private boolean sendInfor(String json) {try {mDataOutputStream.writeUTF(json);mDataOutputStream.flush();} catch (IOException e) {e.printStackTrace();try {mDataOutputStream.flush();} catch (IOException e1) {e1.printStackTrace();}return false;}return true;}private boolean sendInforAndFile(String json, File file) {boolean result = false;try {mDataOutputStream.writeUTF(json);result = FileTransferUtil.getInstance().uploadFile(mDataOutputStream, file);} catch (IOException e) {e.printStackTrace();}return result;}private OnRequestListener mRequestListener;public void registerRequestListener(OnRequestListener listener){this.mRequestListener = listener;}public void unregisterRequestListener(){this.mRequestListener = null;}public interface OnRequestListener{void onRequestSuccessed();void onRequestFailed();}
}
完整Demo下载:
USBSocketClient.
说明:
我的开发环境有点旧,是Android Studio3.2,下载源码如果出现build失败,请更换build.gradle中依赖包的版本,或直接屏蔽掉,不影响功能。
最后谢谢您能花时间来看这篇文章,希望对您有所帮助!
Android设备通过USB线连接PC进行Socket通信相关推荐
- Android设备与USB设备的连接通讯
关于Android与USB设备的连接,所能搜索到的资料特别的少,但恰恰接到了一个这样的需求,使用android平板通过USB与一个外接设备进行通讯,这个设备是一个压力感应器,在压力发生变化时可以测到相 ...
- android 网络re,Gnirehtet让Android设备通过USB连接电脑上网
Gnirehtet让Android设备通过USB连接电脑上网 2020年08月02日 | 萬仟网移动技术 | 我要评论 目录Gnirehtet简介Gnirehtet教程Gnirehtet下载地址功能快 ...
- Android设备通过usb控制zedboard产生pwm波实验
Android设备通过usb控制zedboard产生pwm波实验 一.实验目的 安卓设备如安卓平板,通过usb与zedboard通信,在安卓设备端控制zedboard端产生的pwm波的频率和占空比. ...
- Android设备的USB通讯
摘 要: AOA协议是Google公司推出的用于实现Android设备与外围设备之间USB通信的协议.该协议拓展了Android设备USB接口的功能,为基于Android系统的智能设备应用于数据采集和 ...
- android 蓝牙链接电脑,如何使从台式电脑到Android设备的测试蓝牙连接
我正在使用具有蓝牙适配器的Ubuntu 11.10台式电脑和使用带有蓝牙的Android 2.2的Android平板电脑. (两款设备上的蓝牙版本应为2.0或2.1版本)如何使从台式电脑到Androi ...
- 【管理篇 / 登录】❀ 03. USB线连接登录 ❀ FortiGate 防火墙
[简介]飞塔防火墙上都会配有 USB MGMT 接口,包装里都会配置一根 USB 线,通过这个接口和这根线,再加专用的 FortiExplorer 软件,使我们可以快速.轻松地配置飞塔防火墙,并同时具 ...
- Netty服务器部署在Android设备上,接收来自PC客户端的Java Socket客户端发送的JSON数据
Netty服务器部署在Android设备上,接收来自PC客户端的Java Socket客户端发送的数据 一个简单的模型,在Android手机上部署一个Netty写的服务器,绑定端口9000,等待客户端 ...
- 用“USB2.0 To SATA IDE CABLE”可以将很多电脑的硬件设备通过USB口连接到电脑上。
用"USB2.0 To SATA IDE CABLE"可以将很多电脑的硬件设备通过USB口连接到电脑上.
- Android设备支持USB转RJ45有线网卡( 沐阳网卡JP1081B/9700)
Android设备支持USB转RJ45有线网卡( 沐阳网卡JP1081B9700) 网卡设备: 沐阳网卡JP1081B/9700 系统平台: HISI3798MV100 Android : 4.2 ...
最新文章
- 【驱动】GPIO 作为按键时的 设备树 配置
- 在Cisco路由器上配置WCCP
- 西门子s7-200解密软件下载_西门子S7200仿真软件如何使用?
- 管理表空间和数据文件——维护表空间——设置默认表空间和删除表空间和删除数据文件盒临时文件...
- 【Java】NIO中Selector的select方法源码分析
- C++学习之路 | PTA乙级—— 1087 有多少不同的值 (20 分)(精简)
- Rational Rose学习笔记02:创建用例图
- 基于微型计算机系统的实时时钟设计,基于51单片机的实时时钟设计报告.doc
- 什么网了解c语言,什么是c语言?
- 网页版微博HTML解析和提取,使用Beautiful Soup抽取网页数据,解析微博用户关注信息...
- 卡密激活php代码,全自动PHP授权检测系统,卡密功能自助授权功能源码
- matlab手眼标定
- python打开摄像头黑屏怎么办_虚拟机ubuntu16下cheese打开摄像头黑屏问题
- C语言基础之小写字母转大写
- 第三篇--编译CM系统
- Java-Tcp/Ip-CS控制台聊天应用Demo
- 分享回顾|我们是神经搜索少年团!
- git如何撤销某次提交记录
- 个税局端服务器处理结果查询不到三方协议,「实用」电子税务局中三方协议验证失败如何处理?来看攻略啦!...
- 是时候选择NewSQL数据库了
热门文章
- linux服务器重启命令是什么
- java文档在线预览实现
- 如何安装webpack4
- html背景图片div设置宽自动,CSS背景图片固定宽高比自适应调整的实现方法
- “思考,快与慢”读书笔记(1)20230625
- 计算机主板的主要作用,什么是主板,主要作用是什么
- 鸿蒙系统和安卓系统是兄弟,比听到鸿蒙手机更兴奋!它与华为“兄弟连心”,来挽救国产机了...
- SuperMap GIS基础软件地图瓦片问题QA
- Hisat2 Bowtie2比对结果解读
- python异常处理(十分钟彻底搞定!)