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通信相关推荐

  1. Android设备与USB设备的连接通讯

    关于Android与USB设备的连接,所能搜索到的资料特别的少,但恰恰接到了一个这样的需求,使用android平板通过USB与一个外接设备进行通讯,这个设备是一个压力感应器,在压力发生变化时可以测到相 ...

  2. android 网络re,Gnirehtet让Android设备通过USB连接电脑上网

    Gnirehtet让Android设备通过USB连接电脑上网 2020年08月02日 | 萬仟网移动技术 | 我要评论 目录Gnirehtet简介Gnirehtet教程Gnirehtet下载地址功能快 ...

  3. Android设备通过usb控制zedboard产生pwm波实验

    Android设备通过usb控制zedboard产生pwm波实验 一.实验目的 安卓设备如安卓平板,通过usb与zedboard通信,在安卓设备端控制zedboard端产生的pwm波的频率和占空比. ...

  4. Android设备的USB通讯

    摘 要: AOA协议是Google公司推出的用于实现Android设备与外围设备之间USB通信的协议.该协议拓展了Android设备USB接口的功能,为基于Android系统的智能设备应用于数据采集和 ...

  5. android 蓝牙链接电脑,如何使从台式电脑到Android设备的测试蓝牙连接

    我正在使用具有蓝牙适配器的Ubuntu 11.10台式电脑和使用带有蓝牙的Android 2.2的Android平板电脑. (两款设备上的蓝牙版本应为2.0或2.1版本)如何使从台式电脑到Androi ...

  6. 【管理篇 / 登录】❀ 03. USB线连接登录 ❀ FortiGate 防火墙

    [简介]飞塔防火墙上都会配有 USB MGMT 接口,包装里都会配置一根 USB 线,通过这个接口和这根线,再加专用的 FortiExplorer 软件,使我们可以快速.轻松地配置飞塔防火墙,并同时具 ...

  7. Netty服务器部署在Android设备上,接收来自PC客户端的Java Socket客户端发送的JSON数据

    Netty服务器部署在Android设备上,接收来自PC客户端的Java Socket客户端发送的数据 一个简单的模型,在Android手机上部署一个Netty写的服务器,绑定端口9000,等待客户端 ...

  8. 用“USB2.0 To SATA IDE CABLE”可以将很多电脑的硬件设备通过USB口连接到电脑上。

    用"USB2.0 To SATA  IDE CABLE"可以将很多电脑的硬件设备通过USB口连接到电脑上.

  9. Android设备支持USB转RJ45有线网卡( 沐阳网卡JP1081B/9700)

    Android设备支持USB转RJ45有线网卡( 沐阳网卡JP1081B9700) 网卡设备: 沐阳网卡JP1081B/9700 系统平台: HISI3798MV100  Android : 4.2 ...

最新文章

  1. 【驱动】GPIO 作为按键时的 设备树 配置
  2. 在Cisco路由器上配置WCCP
  3. 西门子s7-200解密软件下载_西门子S7200仿真软件如何使用?
  4. 管理表空间和数据文件——维护表空间——设置默认表空间和删除表空间和删除数据文件盒临时文件...
  5. 【Java】NIO中Selector的select方法源码分析
  6. C++学习之路 | PTA乙级—— 1087 有多少不同的值 (20 分)(精简)
  7. Rational Rose学习笔记02:创建用例图
  8. 基于微型计算机系统的实时时钟设计,基于51单片机的实时时钟设计报告.doc
  9. 什么网了解c语言,什么是c语言?
  10. 网页版微博HTML解析和提取,使用Beautiful Soup抽取网页数据,解析微博用户关注信息...
  11. 卡密激活php代码,全自动PHP授权检测系统,卡密功能自助授权功能源码
  12. matlab手眼标定
  13. python打开摄像头黑屏怎么办_虚拟机ubuntu16下cheese打开摄像头黑屏问题
  14. C语言基础之小写字母转大写
  15. 第三篇--编译CM系统
  16. Java-Tcp/Ip-CS控制台聊天应用Demo
  17. 分享回顾|我们是神经搜索少年团!
  18. git如何撤销某次提交记录
  19. 个税局端服务器处理结果查询不到三方协议,「实用」电子税务局中三方协议验证失败如何处理?来看攻略啦!...
  20. 是时候选择NewSQL数据库了

热门文章

  1. linux服务器重启命令是什么
  2. java文档在线预览实现
  3. 如何安装webpack4
  4. html背景图片div设置宽自动,CSS背景图片固定宽高比自适应调整的实现方法
  5. “思考,快与慢”读书笔记(1)20230625
  6. 计算机主板的主要作用,什么是主板,主要作用是什么
  7. 鸿蒙系统和安卓系统是兄弟,比听到鸿蒙手机更兴奋!它与华为“兄弟连心”,来挽救国产机了...
  8. SuperMap GIS基础软件地图瓦片问题QA
  9. Hisat2 Bowtie2比对结果解读
  10. python异常处理(十分钟彻底搞定!)