Android端与PC端同步绘画板(一)-使用Socket建立连接

xiaoxiao2021-02-28  103

源码已经放出,点击这里

一.前言

参加工作到现在大概已经接近2年时间,不长不短,2年的时光,在工作中遇到很多问题,总是会来csdn,github等寻找相对的解决办法,往往可以收获颇丰,一直在索取,但从未付出,最近连续加班2周,终于将手头的事情告一段落。有了一些自己的时间,决定来csdn开博客记录自己的一些东西,有代码,可能也会有一些自己的感想和生活。这篇文章算是一个开篇,第一次写博客,不足住处还望见谅;

二.概述

本系列文章来做一个使用Andorid端的手机和PC端电脑同步的绘画板,可以分别在手机或者电脑上各自绘制,也可以手机绘制,PC端同步。PC绘制,手机端同步。手机端和PC端都实现了一个画板的基础功能(画笔,橡皮,图形,选择颜色,rode,undo等),绘画板文章将会是一个小系列,主要记录一些开发中遇到的一些问题和解决方法,例如手机端和PC端建立连接互通数据,PC端和Android端如何适配屏幕等;由于这个小项目还有一些需要完善的地方,所以会先记录,之后会上传代码供大家参考,这篇文章将介绍如何让手机端和PC端建立连接并互通消息,为以后的功能打下基础,开始之前,我们需要先了解以下的一些知识点:

1.Socket实现基于TCP

Socket的通讯步骤

①创建Socket输入IP和端口连接服务器端 ②打开链接到Socket的输入\输出流 ③按照协议对Socket进行读写操作 ④关闭输入输出流,关闭Socket 服务器端 ①创建ServerSocket对象,绑定监听端口 ②通过accept()方法监听客户端请求 ③建立连接后通过输入流读取客户端发送的信息 ④通过输出流向客户端发送响应信息 ⑤关闭输入输出流,关闭Socket

2.使用Socket发送Object

由于绘画板需要频繁的互通消息,并且每个消息可能不是简单的一个String,int可以满足的,所以我们需要发送和接受Object来实现同步绘画板,要注意以下几点:

①传送的Obj首先需要一个普通的对象类,由于需要序列化这个对象以便在网络上传输,所以实现java.io.Serializable接口就是必不可少;

②手机端和PC端存放所有实现了Serializable接口的Object类的包命必须相同,否则会通讯过程中会找不到相应的类;

③使用ObjectOutputStream,ObjectInputStream来读取和发送对象,发送完成调用flash()方法;

三.项目中如何建立通讯

有了以上对Sokcet发送建立连接和发送对象的基本了解,我们来看下在项目中如何进行adroid端和PC端的通讯; 1.首先定义继承自Serializable接口的类,并且构造方法接收一个枚举和一个对象,这样子在通讯时,即可根据枚举判断当前Android和PC端互通的消息类型,例如当前是画线POINT的操作还是关于命令COMMAND例如redo,undo的操作,然后根据不同的操作将对象转换成相应的类型,从中提取出所需信息即可,例如发送POINT枚举,收到此消息后将对象强制转换后提取X,Y坐标来完成绘制;

package com.rock.drawboard.module; import java.io.Serializable; public class DataPackage implements Serializable { public enum DataType {LOGIN,POINT,COMMAND,COLOR,STROKE} private DataType type; private Object data; public DataPackage(DataType type) { this(type, null); } public DataPackage(DataType type, Object data) { this.type = type; this.data = data; } public DataType getType() { return type; } public Object getData() { return data; } }

用来画线的Point类

package com.rock.drawboard.module; import java.io.Console; import java.io.Serializable; public class Point implements Serializable { private int x; private int y; private int state; public Point(int x, int y, int state) { this.x = x; this.y = y; this.state = state; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } public int getState() { return state; } public void setState(int state) { this.state = state; } }

Android端与PC端读取和写出对象的类Client

package com.rock.drawboard.module; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.Socket; public class Client { private Socket socket; private ObjectOutputStream output; private ObjectInputStream input; public Client(Socket socket) { setSocket(socket); } public Socket getSocket() { return socket; } private void setSocket(Socket socket) { this.socket = socket; try { output = new ObjectOutputStream(socket.getOutputStream()); input = new ObjectInputStream(socket.getInputStream()); } catch (IOException e) { e.printStackTrace(); } } public boolean isClosed() { return socket.isClosed(); } public void close() { if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } public Object receive() throws ClassNotFoundException, IOException { return input.readObject(); } public void send(Object obj) throws IOException { output.writeObject(obj); output.flush(); } }

客户端socket通讯Action 构造方法中传入Server的端口和ip地址,然后开启线程循环等待接受消息,收到消息后根据枚举判断消息类型并通知界面完成绘制

package com.rock.drawboard.socket; import android.util.Log; import android.widget.Switch; import com.rock.drawboard.constant.Config; import com.rock.drawboard.constant.EventConfig; import com.rock.drawboard.event.BoardEvent; import com.rock.drawboard.event.LoginEvent; import com.rock.drawboard.module.Client; import com.rock.drawboard.module.DataPackage; import com.rock.drawboard.module.Login; import com.rock.drawboard.module.Point; import com.rock.drawboard.utils.LogUtils; import com.rock.drawboard.utils.ToastUtil; import org.greenrobot.eventbus.EventBus; import java.io.IOException; import java.net.Socket; public class ClientAction extends Thread{ private static final String TAG = ClientAction.class.getSimpleName(); private static Client client; private String ip; private int port = 0; public ClientAction(String ip,int port) { this.port = port; this.ip = ip; } public ClientAction(String ip) { this.ip = ip; } private void initSocket() { try { Socket socket; if(port==0) { socket = new Socket(ip, Config.PORT); }else { socket = new Socket(ip,port); } client = new Client(socket); } catch (IOException e) { e.printStackTrace(); close(); } } @Override public void run() { initSocket(); if(client!=null) { while (!client.isClosed()) { try { DataPackage dtpg = (DataPackage) client.receive(); Object data = dtpg.getData(); switch (dtpg.getType()) { case LOGIN: EventBus.getDefault().post(new LoginEvent(EventConfig.LGOIN)); break; case POINT: EventBus.getDefault().post(new BoardEvent(EventConfig.POINT,data)); break; case COMMAND: EventBus.getDefault().post(new BoardEvent(EventConfig.COMMAND,data)); break; case COLOR: EventBus.getDefault().post(new BoardEvent(EventConfig.COLOR,data)); break; case STROKE: EventBus.getDefault().post(new BoardEvent(EventConfig.STROKE,data)); break; } } catch (ClassNotFoundException e) { e.printStackTrace(); close(); } catch (IOException e) { e.printStackTrace(); close(); } } } } public static void sendData(DataPackage.DataType type, Object data) { DataPackage dp = new DataPackage(type, data); sendDataPackage(dp); } public static void sendDataPackage(DataPackage dp) { if (client != null && !client.isClosed()) { try { client.send(dp); } catch (IOException e) { close(); } } } public static void close() { if (client != null) { client.close(); Log.e(TAG,"client close"); } } }

服务器端Socket通讯Action 开启后等待客户端连接,链接成功后循环等待消息接受并判断消息类型,提供接口完成消息的回调通知相应界面绘制;

package com.rock.drawboard.socket; import com.rock.drawboard.constant.Config; import com.rock.drawboard.module.*; import sun.rmi.runtime.Log; import java.io.IOException; import java.lang.invoke.SwitchPoint; import java.net.ServerSocket; import java.net.Socket; import java.util.Timer; import java.util.TimerTask; public class ServerAction extends Thread{ private static final String TAG = ServerAction.class.getSimpleName(); private static Client client; public ServerAction() { initSocket(); } private void initSocket() { ServerSocket serverSocket = null; try { serverSocket = new ServerSocket(Config.PORT); Socket socket=serverSocket.accept(); socket.setKeepAlive(true); System.out.println(TAG+": client success join,client ip:"+socket.getInetAddress().getHostAddress()); client = new Client(socket); sendData(DataPackage.DataType.LOGIN,new Login(true)); } catch (IOException e) { e.printStackTrace(); closeSocket(); } } @Override public void run() { while (!client.isClosed()) { try { DataPackage dtpg = ((DataPackage) client.receive()); Object object = dtpg.getData(); switch (dtpg.getType()) { case POINT: onEventListener.onPencil((Point) object); break; case COMMAND: onEventListener.onCommand((Command) object); break; case COLOR: onEventListener.onColor((SelectColor)object); break; case STROKE: onEventListener.onStroke((StrokeWidth) object); break; } } catch (ClassNotFoundException e) { e.printStackTrace(); closeSocket(); } catch (IOException e) { e.printStackTrace(); closeSocket(); } } } private OnEventListener onEventListener; public interface OnEventListener{ void onPencil(Point point); void onCommand(Command command); void onColor(SelectColor color); void onStroke(StrokeWidth strokeWidth); } public void setOnEventListener(OnEventListener onEventListener) { this.onEventListener = onEventListener; } public static void sendData(DataPackage.DataType type, Object data) { DataPackage dp = new DataPackage(type, data); sendData(dp); } public static void sendData(DataPackage dp) { if (client != null && !client.isClosed()) { try { client.send(dp); } catch (IOException e) { closeSocket(); } } } public static void closeSocket() { if(client!=null) { client.close(); System.out.println(TAG+": server close"); } } }
转载请注明原文地址: https://www.6miu.com/read-44198.html

最新回复(0)