600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > Java使用Socket实现服务器与客户端通信

Java使用Socket实现服务器与客户端通信

时间:2022-10-23 05:44:09

相关推荐

Java使用Socket实现服务器与客户端通信

最近在写一个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就可以解决乱码问题

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。