Java Socket BIO多線程 (beta2)

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則斷開,並移除連接列表

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章