Java Socket BIO多线程 (beta2)
上一篇的BIO多线程做了基本的框架搭建,这次做了beta2,做了心跳包管理和线程释放资源的处理
封装的服务器代码
-
封装类(SocketServer),并加了线程池
package com.server; import javax.swing.plaf.nimbus.AbstractRegionPainter; import java.io.*; import java.net.*; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.concurrent.*; public class SocketServer extends Thread{ /** * 用户扫已有的socket处理线程 * 1. 没有的线程不引用 * 2. 关注是否有心跳 * 3. 关注是否超过登陆时间 */ private ScheduledExecutorService scheduleSocketMonitorExecutor = Executors .newSingleThreadScheduledExecutor(r -> new Thread(r, "socket_monitor_" + r.hashCode())); /** * 存储只要有socket处理的线程 */ private List<SocketThread> existConnectionThreadList = Collections.synchronizedList(new ArrayList<>()); /** * 中间list,用于遍历的时候删除 */ private List<SocketThread> noConnectionThreadList = Collections.synchronizedList(new ArrayList<>()); /** * 存储当前由用户信息活跃的的socket线程 */ private ConcurrentMap<String, SocketThread> existSocketMap = new ConcurrentHashMap<>(); private static final int CORE_POOL_SIZE = 5; private static final int MAX_POOL_SIZE = 10; private static final int QUEUE_CAPACITY = 100; private static final Long KEEP_ALIVE_TIME = 1L; // ThreadPoolExecutor executorService = new ThreadPoolExecutor( // CORE_POOL_SIZE, // MAX_POOL_SIZE, // KEEP_ALIVE_TIME, // TimeUnit.SECONDS, // new ArrayBlockingQueue<>(QUEUE_CAPACITY), // new ThreadPoolExecutor.CallerRunsPolicy() // ); //private String host; private int port; //private Socket socket; ServerSocket server; //Map<Integer,SocketThread> socketThreadMap; List<SocketThread> threads = new ArrayList<>(); int count = 0; private boolean isRunning; public SocketServer(int port) { //this.host = host; this.port = port; isRunning = true; } public void Connect() throws IOException { server = new ServerSocket(port); //每隔1s扫一次ThreadList scheduleSocketMonitorExecutor.scheduleWithFixedDelay(() -> { Date now = new Date(); //删除list中没有用的thread引用 existConnectionThreadList.forEach(connectionThread -> { if (!connectionThread.isRunning()) { noConnectionThreadList.add(connectionThread); } else { //还在运行的线程需要判断心跳是否ok以及是否身份验证了 Date lastOnTime = connectionThread.getConnection().getLastOnTime(); long heartDuration = now.getTime() - lastOnTime.getTime(); if (heartDuration > SocketConstant.HEART_RATE) { //心跳超时,关闭当前线程 System.out.println(connectionThread.getSocketDescribe() + "心跳超时,close it"); connectionThread.Close(); } } }); noConnectionThreadList.forEach(connectionThread -> { existConnectionThreadList.remove(connectionThread); }); noConnectionThreadList.clear(); }, 0, 1, TimeUnit.SECONDS); } @Override public void run() { if(isInterrupted()) return; while (isRunning) { if(server.isClosed()){ isRunning = false; System.out.println("socket server IsClosed......"); break; } try{ System.out.println("Start socket wait......"); Socket socket = server.accept(); //启动线程 SocketThread socketThread = new SocketThread(socket); count++; socketThread.setName("Thread" + count); existConnectionThreadList.add(socketThread); socketThread.start(); // executorService.submit(socketThread); }catch (IOException e) { e.printStackTrace(); Close(); } } } public void Listener() throws IOException { } public void SendMsg(String data) { } public void Close() { try { //先关闭monitor线程,防止遍历list的时候 scheduleSocketMonitorExecutor.shutdownNow(); if (server != null && !server.isClosed()) { for (SocketThread currentThread : existConnectionThreadList) { currentThread.Close(); } } //executorService.shutdownNow(); server.close(); //终止线程次 isRunning = false; }catch (Exception e){ e.printStackTrace(); } } }
-
线程类(SocketThread)
package com.server; import java.io.*; import java.net.Inet4Address; import java.net.InetAddress; import java.net.Socket; import java.net.SocketException; import java.util.Date; public class SocketThread extends Thread { /** * 封装的客户端连接socket */ private Connection connection; private boolean isRunning; private Socket socket = null; private int byteLength = 1024; public String getSocketDescribe() { return socketDescribe; } private String socketDescribe = null; public Connection getConnection() { return connection; } //private InputStream inputStream; public SocketThread(Socket socket) { this.socket = socket; socketDescribe = "Client:" + this.socket.getInetAddress() +"_Port:" +this.socket.getPort(); System.out.println(socketDescribe + " Connected!"); connection = new Connection(socket, this); Date now = new Date(); connection.setCreateTime(now); connection.setLastOnTime(now); isRunning = true; } public boolean isRunning() { return isRunning; } @Override public void run() { if(isInterrupted()) return; while (isRunning) { if(socket.isClosed()) { isRunning = false; System.out.println(socketDescribe + "Client close."); break; } BufferedReader reader; try { reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); String message; while ((message = reader.readLine()) != null) { System.out.println(socketDescribe + "服务端收到消息:" + message); } } catch (IOException e) { System.out.println(socketDescribe + "ConnectionThread.run failed. IOException:{}"+ e.getMessage()); this.Close(); } } } public void Close() { isRunning = false; try { socket.shutdownInput(); socket.shutdownOutput(); }catch (IOException e) { System.out.println(e.toString()); }finally { try { socket.close(); }catch (IOException e) { e.printStackTrace(); } } } }
-
Connect信息记录函数
package com.server; import java.net.Socket; import java.util.Date; public class Connection { /** * 当前的socket连接实例 */ private Socket socket; /** * 当前连接线程 */ private SocketThread connectionThread; /** * 当前连接是否登陆 */ private boolean isLogin; /** * 存储当前的user信息 */ private String userId; /** * 创建时间 */ private Date createTime; /** * 最后一次更新时间,用于判断心跳 */ private Date lastOnTime; public SocketThread getConnectionThread() { return connectionThread; } public String getUserId() { return userId; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public Date getLastOnTime() { return lastOnTime; } public void setLastOnTime(Date lastOnTime) { this.lastOnTime = lastOnTime; } public Connection(Socket socket, SocketThread connectionThread) { this.socket = socket; this.connectionThread = connectionThread; } public void println(String message) { int count = 0; } }
-
常量函数
package com.server; public class SocketConstant { /** * 心跳频率为10s */ public static final int HEART_RATE = 10*1000; /** * 最多开2000个socket线程,超过的直接拒绝 */ public static final int MAX_SOCKET_THREAD_NUM = 2000; /** * 重试次数:3 */ public static final int RETRY_COUNT = 3; }
-
主函数调用测试
import com.server.SocketServer; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; //参考文档 //https://www.jianshu.com/p/cde27461c226 public class Main { public static void main(String[] args) { try{ SocketServer server =new SocketServer(888); BufferedReader bufferedReader =new BufferedReader(new InputStreamReader(System.in,"UTF-8")); while (true) { String str = bufferedReader.readLine(); if(str.equals("quit")) { server.Close(); break; } if(str.equals("start")) { server.Connect(); server.start(); } } }catch (IOException e){ e.printStackTrace(); } System.out.println("Exit"); } }
测试
开了一个SocketTool2(需要的自己网上下载),创建了多个客户端去连接服务器,可以同时发送信息(回测作为客户端发送完毕的结束符),这些信息都会显示,客户端断开后,服务器每10秒轮询客户端是否还连接,如果没有的话服务器对应的socket则断开,并移除连接列表