上一篇的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 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则断开,并移除连接列表