最近在写一个Android 网络局域网通信功能,客户端将数据发送到服务器后,服务器再将这个数据转发给其他客户端为了方便起见,写了一个工具类,废话不多说,上代码,这段代码是基于Gson 的,需要在Android文件build.gradle 中引用。

 //Gsonimplementation 'com.google.code.gson:gson:2.8.0'

思路解析:

先使用Socket连接,获取异常,判断是否存在这个服务器是否可以连接

 try {Socket socket = new Socket(host, port);SocketNode node = SocketNode.getNode(socket);if(node != null){return new SocketManager(node);}else{return null;}} catch (IOException e) {}

如果没有被连接,就创建一个新的ServerSocket 再通过线程去不断读取数据

虽然这个连接不是很安全,最好还是用加密方式将数据进行加密。

全部代码:


/*** Socket 通信控制* @author Sobadfish** */
public class SocketManager {// 线程池管理public static ExecutorService executor = Executors.newCachedThreadPool() ;private SocketNode socket;/*是否为启动状态* */public boolean enable;/*** 主机分配的端口* */private static int port = -1;private final SocketType type;private SocketDataListener dataListener;private SocketConnectListener connectListener;private ServerSocket serverSocket;private List<SocketNode> sockets = new CopyOnWriteArrayList<>();private SocketManager(SocketNode socket){this.socket = socket;type = SocketType.SOCKET;enable = true;executor.execute(new SocketThread(this) {@Overridepublic void run() {if (socket != null && socket.isConnected()) {if(socket.isEnable()){if(!socket.read(getSocketManager())){socket.disable();}}}}});}private SocketManager(ServerSocket serverSocket){this.serverSocket = serverSocket;this.sockets = new CopyOnWriteArrayList<>();port = serverSocket.getLocalPort();type = SocketType.SERVER;enable = true;enable();}public SocketType getType() {return type;}/*** 绑定数据监听 网络通信中获取到的数据将通过监听事件发送* @param listener 事件监听* */public void setDataListener(SocketDataListener listener) {this.dataListener = listener;}/*** 建立服务端与客户端的通信 如果服务端不存在,则创建服务端** @param socket Socket连接对象* */public static SocketManager connectManager(Socket socket){//TODO 重新建立线程池 因为上个线程池被关闭了executor = Executors.newCachedThreadPool() ;try {SocketNode node = SocketNode.getNode(socket);if(node != null){return new SocketManager(node);}else{return null;}} catch (Exception e) {e.printStackTrace();}return null;}/*** 建立服务端与客户端的通信 如果服务端不存在,则创建服务端** @param host 服务器IP* @param port 服务器端口* */public static SocketManager connectManager(String host, int port){try {Socket socket = new Socket(host, port);SocketNode node = SocketNode.getNode(socket);if(node != null){return new SocketManager(node);}else{return null;}} catch (Exception e) {// 不存在服务器System.out.println("不存在服务主机 或 主机无法连接 " + host + " 正在创建端口 " + port + " 的主机");try {ServerSocket serverSocket = new ServerSocket(port);return new SocketManager(serverSocket);} catch (IOException ioException) {System.out.println("创建失败 " + ioException.getMessage());}}return null;}/*** 绑定连接事件监听* 当有 Socket 连接的时候会通过这个事件回调** @param listener 监听器* */public void setConnectListener(SocketConnectListener listener) {this.connectListener = listener;}/*** 向网络中发送数据* @param messageData 监听器* */public synchronized boolean sendMessage(MessageData messageData,SocketManager manager){switch (type){case SOCKET:if(socket != null && port > 0){// 通信建立后才行发送数据if(!socket.sendMessage(messageData)){if(manager.connectListener != null){manager.connectListener.quit(socket);}}return true;}break;case SERVER:if(sockets.size() > 0) {for (SocketNode node : sockets) {if(!node.sendMessage(messageData)){if(manager.connectListener != null){manager.connectListener.quit(node);}}}return true;}break;default:break;}return false;}private void enable() {executor.execute(() -> {Socket socket = null;while (serverSocket != null && !serverSocket.isClosed()){try {socket = serverSocket.accept();SocketNode node = SocketNode.getNode(socket);if(node != null){sockets.add(node);// 回传分配的端口MessageData messageData = MessageData.createMessage(new PortBack(node.getPort()));messageData.type = "port";node.sendMessage(messageData);if(connectListener != null){connectListener.join(node);}new Thread(new SocketThread(this) {@Overridepublic void run() {if(!node.read(getSocketManager())){node.disable();if(connectListener != null){connectListener.quit(node);}}}}).start();}} catch (IOException e) {if(socket != null){System.out.println(socket.getInetAddress()+":"+socket.getPort()+"断开连接");}}try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}});}/**** 数据通信类* 用于向网络发送数据* */public static class MessageData{public String host;public int port;public String msg;public Long time;public String type;public static MessageData createMessage(Object o){MessageData data = new MessageData();try {InetAddress addr = InetAddress.getLocalHost();data.host = new String(Base64.getEncoder().encode(addr.getHostAddress().getBytes(StandardCharsets.UTF_8)));} catch (UnknownHostException e) {data.host =  new String(Base64.getEncoder().encode("Unknown Host".getBytes(StandardCharsets.UTF_8)));;}data.time = System.currentTimeMillis();Gson gson = new Gson();data.msg = gson.toJson(o);return data;}public <T> T getData(Class<T> data){Gson gson = new Gson();return gson.fromJson(msg.trim(),data);}@Overridepublic String toString() {Gson gson = new Gson();return gson.toJson(this);}}public static class PortBack{/*** host : MTkyLjE2OC4xMzIuMQ==* port : 55179* msg : eyJwb3J0Ijo1NTE3OX0=* time : 1674997819196*/public String host;public int port;public long time;public PortBack(){}public PortBack(int port){this.port = port;}@Overridepublic String toString() {return "PortBack{" +"host='" + host + '\'' +", port=" + port +", time=" + time +'}';}}public enum SocketType{/*** 类型为主机*/SERVER,/*** 类型为客户端*/SOCKET}public interface SocketDataListener{void handleMessage(SocketManager socketManager,MessageData messageData);}public interface SocketConnectListener{void join(SocketNode socket);void quit(SocketNode socket);}public void sendMessage(Object o){MessageData messageData = MessageData.createMessage(o);if(!executor.isShutdown()) {executor.execute(new SocketThread(this) {@Overridepublic void run() {if (!sendMessage(messageData, getSocketManager())) {System.out.println("数据发送失败 端口:" + port);}}});}}public static class SocketNode{private Socket socket;private final String ip;private boolean enable;private final int port;public String getIPAddress(){return ip;}public int getPort() {return port;}private SocketNode(Socket socket){this.socket = socket;enable = true;if(socket != null && socket.isConnected()){ip = socket.getInetAddress().getHostAddress();port =  socket.getPort();try {inputStream = socket.getInputStream();outputStream = socket.getOutputStream();} catch (IOException e) {e.printStackTrace();inputStream = null;outputStream = null;}}else{ip = "Unknown";port = -1;}}public boolean isEnable() {return enable;}public void disable(){enable = false;if(socket != null){try {socket.close();} catch (IOException e) {e.printStackTrace();}}}public boolean read(SocketManager manager){if(inputStream == null){return false;}Gson gson = new Gson();while (!socket.isClosed()){try {//TODO 心跳包判断连接是否保持try {socket.sendUrgentData(0);}catch (IOException e){return false;}byte[] bytes = new byte[1024];inputStream.read(bytes,0,bytes.length);String out = new String(bytes, StandardCharsets.UTF_8).trim();if(!"".equalsIgnoreCase(out.trim()) ){MessageData data = gson.fromJson(out, MessageData.class);PortBack portBack = null;if("port".equalsIgnoreCase(data.type)){portBack = data.getData(PortBack.class);if (portBack != null && portBack.port > 0) {SocketManager.port = portBack.port;if (manager.connectListener != null) {manager.connectListener.join(this);}}else{portBack = null;}}// 判断是否为主机 如果是主机就把收到的数据分发给其他客户端if (manager.type == SocketType.SERVER) {for (SocketNode node : manager.sockets) {// 别再把数据又发回去了,这不就重复了吗//当然你也可以选择重复,只在接收区显示//增加这个判断是为了 忽略发送数据到服务器的客户端if (node.equals(this)) {continue;}node.sendMessage(data);}}if (manager.dataListener != null) {if (portBack != null) {continue;}manager.dataListener.handleMessage(manager, data);}}}catch (Exception e){return false;}}// 进入这个判断后说明连接被关闭了switch (manager.type){case SERVER:if(manager.connectListener != null){manager.connectListener.quit(this);}return false;case SOCKET:// 尝试重连主机int error = 0;while (error < 3){try {
//socket = new Socket(socket.getInetAddress(), socket.getPort());// 重连完成break;}catch (Exception e) {error++;}}if(error >= 3){return false;}break;default:break;}return true;}public static SocketNode getNode(Socket socket){if(socket.isConnected()){return new SocketNode(socket);}return null;}public boolean isConnected() {return socket != null && socket.isConnected() && !socket.isClosed();}private boolean sendMessage(MessageData messageData){if(isConnected()) {messageData.port = port;Gson gson = new Gson();byte[] msg = gson.toJson(messageData).getBytes(StandardCharsets.UTF_8);try {if(outputStream != null){try {socket.sendUrgentData(0);}catch (IOException e){return false;}outputStream.write(msg);return true;}} catch (IOException e) {return false;}}return false;}private InputStream inputStream;private OutputStream outputStream;@Overridepublic boolean equals(Object o) {if (this == o) {return true;}if (o == null || getClass() != o.getClass()) {return false;}SocketNode node = (SocketNode) o;return Objects.equals(socket, node.socket) && port == node.port;}@Overridepublic int hashCode() {return Objects.hash(socket);}}public void disable(){enable = false;executor.shutdown();if(serverSocket != null){try {serverSocket.close();serverSocket = null;sockets.clear();} catch (IOException e) {e.printStackTrace();}}if(socket != null){try {socket.socket.close();socket = null;} catch (IOException e) {e.printStackTrace();}}}public abstract static class SocketThread implements Runnable{private SocketManager socketManager;public SocketThread(SocketManager socketManager){this.socketManager = socketManager;}public SocketManager getSocketManager() {return socketManager;}}}

使用方法:

SocketManager manager = SocketManager.connectManager("你要连接的服务器IP","你要连接的服务器端口");

获取传输的数据

socketManager.setDataListener(new SocketManager.SocketDataListener() {@Overridepublic void handleMessage(SocketManager socketManager, SocketManager.MessageData messageData) {
//接收到的数据}});

客户端连接/断开服务器监听

 socketManager.setConnectListener(new SocketManager.SocketConnectListener() {@Overridepublic void join(SocketManager.SocketNode socket) {//客户端连接}@Overridepublic void quit(SocketManager.SocketNode socket) {//客户端断开}});

在编写时遇到的问题

建立连接后,当节点断开,主机这边会有一个关于List的报错,是因为在删除的时候又遍历节点去获取内容,将保存的节点数组改为

private List<SocketNode> sockets = new CopyOnWriteArrayList<>();

就可以解决那个问题了。

问题2:

在传送字符串的时候会出现乱码情况,所以在这里我使用了Base64进行编码解码,统一编码为UTF-8 就可以解决乱码问题

Java使用Socket实现服务器与客户端通信相关推荐

  1. JAVA之socket编程服务器与客户端通信--实现简易聊天室

    本文将介绍TCP和UDP两种服务器与客户端之间的通信协议 1.首先介绍TCP和UDP分别是什么:TCP(Transfer Control Protocol) 是传输控制协议的缩写,被称 TCP / I ...

  2. java socket 编程 客户机服务器_Java Socket编程服务器响应客户端实例代码

    通过输入流来读取客户端信息,相应的时候通过输出流来实现. 服务端类的代码: import java.io.BufferedReader; import java.io.IOException; imp ...

  3. java服务端代码_Java Socket编程服务器响应客户端实例代码

    通过输入流来读取客户端信息,相应的时候通过输出流来实现. 服务端类的代码: import java.io.BufferedReader; import java.io.IOException; imp ...

  4. python 实现TCP socket通信和 HTTP服务器、服务器和客户端通信python实例

    python 实现TCP socket通信和 HTTP服务器.服务器和客户端通信实例 socket是什么? 服务器和客户端通信的流程 python 实现TCP socket通信例子 关于Host和PO ...

  5. 使用python中的socket实现服务器和客户端,并完成图片的传输

    使用python中的socket实现服务器和客户端,并完成图片的传输. 2018年03月09日 16:05:23 阅读数:301 socket服务器代码: [python] view plaincop ...

  6. java getbasicremote_Vue+Java 通过websocket实现服务器与客户端双向通信操作

    1. vue代码 methods: { //在方法里调用 this.websocketsend()发送数据给服务器 onConfirm () { //需要传输的数据 let data = { code ...

  7. java 网页通讯_Vue+Java 通过websocket实现服务器与客户端双向通信操作

    1. vue代码 methods: { //在方法里调用 this.websocketsend()发送数据给服务器 onConfirm () { //需要传输的数据 let data = { code ...

  8. Socket编程服务器和客户端【重要】

    socket编程服务器和客户端[重要] 看过很多网上大神们写的socket,无论复杂或是简洁,感觉对于新手都不是很友好,写的步骤和过程描述得不是很清晰,最近又想起来要写一写,就当做个笔记吧~ 不多说! ...

  9. 一个 Java 的 Socket 服务器和客户端通信的例子

    http://blog.csdn.net/defonds/article/details/7971259 多个客户端对应一个服务端的通信的一个小例子.  服务端和客户端代码: public class ...

最新文章

  1. 霍夫曼树(最优二叉树)的实现
  2. 如何避免云整合的问题
  3. 【MySQL】UPDATE LEFT JOIN
  4. 网络基础:网络IP 、子网掩码 、路由器 、DNS知识分享
  5. nginx php怎么配置伪静态,nginx rewrite 伪静态配置参数和使用例子
  6. Unity客户端开发优化要点
  7. 计算机f8键的功能,详解:XP系统修改启动时F8键有哪些功能?
  8. SpringBoot核心原理:自动配置、事件驱动、Condition
  9. IT运维管理为重,从业务需求看网管系统的选择
  10. 浅谈linux性能调优之六:IO调度算法的选择
  11. 190523每日一句
  12. 神奇的泡泡java游戏,抖音挤泡泡游戏叫什么名字 抖音上很火的减压游戏介绍
  13. 本地SecureCRT连接VMware中XUbuntu
  14. vue控制台报错Extraneous non-props attributes (class) were passed to component but could not be automatica
  15. 深入了解C/C++开发就业前景如何?
  16. 基于PMOS的过压保护(OVP)电路仿真
  17. 前端笔试—编译和体系结构
  18. 「自控元件及线路」6 无刷直流电动机
  19. docker查看容器IP的方法
  20. 服务器安全基线检查(Python)代码执行

热门文章

  1. 最短通路——迪克斯特拉算法
  2. sis、sisx文件解压 SISWare
  3. 三星I560 UCWEB7.0正式版下载 SISX安装包
  4. 重装win10时,出现“在EFI系统上,windows只能安装到GPT磁盘”解决方法
  5. php商城系统文和答题_ThinkPHP5实现答题管理系统(一)
  6. Allegro专题【1】——orcad创建元器件方法
  7. c++虚函数、虚析构函数
  8. c语言如何实现多线程VC,vc多线程(c++多线程)
  9. Java毕设项目-外卖订餐系统
  10. 怎么才能真正理解服务器是什么?