服务端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
public class Server {
private static List<Socket> clientList = new ArrayList<>();
private static Map<Socket, Date> heartbeatMap = new HashMap<>(16);
private static final long TIMEOUT = 10 * 1000;
private static final int PORT = 12345;
private static final String HEARTBEAT = "heartbeat";
private static final String HEARTBEAT_RECEIPT = "heartbeat_receipt";
private static final String EXIT = "exit";
private static final String EXIT_RECEIPT = "exit_receipt";
public static void main(String[] args) {
new Server().start();
}
private void start() {
try {
ServerSocket server = new ServerSocket(PORT);
System.out.println("服务端开启,等待客户端连接中...");
while (true) {
Socket client = server.accept();
clientList.add(client);
System.out.println("有建立连接了,客户端地址:" + client.getRemoteSocketAddress().toString().replace("/", "") + ",当前连接数量:" + clientList.size());
heartbeatMap.put(client, new Date());
new MessageListener(client).start();
new HeartbeatListener(client).start();
}
} catch (IOException e) {
e.getStackTrace();
}
}
class MessageListener extends Thread {
private Socket client;
private MessageListener(Socket socket) {
this.client = socket;
}
@Override
public void run() {
try {
sendMsg(client, "连接已经建立,请开始消息传输");
String msg;
while (heartbeatMap.get(client) != null) {
msg = receiveMsg(client);
if (msg == null) {
continue;
}
if (HEARTBEAT.equals(msg)) {
heartbeatMap.put(client, new Date());
sendMsg(client, HEARTBEAT_RECEIPT);
} else if (EXIT.equals(msg)) {
clientList.remove(client);
heartbeatMap.remove(client);
sendMsg(client, EXIT_RECEIPT);
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("有断开连接了,客户端地址:" + client.getRemoteSocketAddress().toString().replace("/", "") + ",当前连接数量:" + clientList.size());
} else {
System.out.println("[" + client.getPort() + "]:" + msg);
sendMsg(client, "我已接收信息\"" + msg + "\"");
}
}
} catch (IOException e) {
e.getStackTrace();
}
}
}
class HeartbeatListener extends Thread {
private Socket client;
private HeartbeatListener(Socket socket) {
this.client = socket;
}
@Override
public void run() {
Date time, now;
while ((time = heartbeatMap.get(client)) != null) {
now = new Date();
if (now.getTime() - time.getTime() > TIMEOUT) {
heartbeatMap.remove(client);
clientList.remove(client);
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("有心跳超时了,客户端地址:" + client.getRemoteSocketAddress().toString().replace("/", "") + ",当前连接数量:" + clientList.size());
}
}
}
}
private void sendMsg(Socket socket, String msg) throws IOException {
OutputStream out = socket.getOutputStream();
PrintWriter writer = new PrintWriter(out);
writer.println(msg);
writer.flush();
}
private String receiveMsg(Socket socket) throws IOException {
InputStream in = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
return reader.readLine();
}
}
客户端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.*;
public class Client {
private static final String HOST = "127.0.0.1";
private static final int PORT = 12345;
private static final long HEARTBEAT_INTERVAL = 2 * 1000;
private static final long HEARTBEAT_TIMEOUT = 10 * 1000;
private static final String HEARTBEAT = "heartbeat";
private static final String HEARTBEAT_RECEIPT = "heartbeat_receipt";
private static final String EXIT_RECEIPT = "exit_receipt";
private static Map<String, Socket> socketMap = new HashMap<>(16);
private static Map<String, Boolean> connectionStatusMap = new HashMap<>(16);
private static Map<String, Date> heartbeatReceiptTimeMap = new HashMap<>(16);
private static Map<String, Boolean> connectionRetryStatusMap = new HashMap<>(16);
private static Map<String, Integer> connectionRetryCountMap = new HashMap<>();
private static final long CONNECTION_RETRY_INTERVAL = 5 * 1000;
public static void main(String[] args) {
String threadKey = UUID.randomUUID().toString();
new Client().start(threadKey);
}
private void start(String threadKey) {
try {
Socket socket = new Socket(HOST, PORT);
socketMap.put(threadKey, socket);
heartbeatReceiptTimeMap.put(threadKey, new Date());
System.out.println("连接服务器成功,本机地址:" + socket.getLocalSocketAddress().toString().replace("/", ""));
connectionStatusMap.put(threadKey, true);
connectionRetryStatusMap.put(threadKey, null);
connectionRetryCountMap.put(threadKey, 0);
new MessageListener(threadKey).start();
} catch (IOException e) {
e.getStackTrace();
}
new HeartbeatListener(threadKey).start();
new ConnectionRetryListener(threadKey).start();
}
class MessageListener extends Thread {
private String threadKey;
private MessageListener(String threadKey) {
this.threadKey = threadKey;
}
@Override
public void run() {
try {
String msg;
while (connectionStatusMap.get(threadKey)) {
msg = receiveMsg(threadKey);
if (msg == null) {
continue;
}
if (HEARTBEAT_RECEIPT.equals(msg)) {
heartbeatReceiptTimeMap.put(threadKey, new Date());
} else if (EXIT_RECEIPT.equals(msg)) {
connectionRetryStatusMap.put(threadKey, false);
System.out.println("[服务端]:我已收到关闭指令,连接已关闭");
try {
socketMap.get(threadKey).close();
} catch (IOException e) {
e.printStackTrace();
}
System.exit(0);
} else {
System.out.println("[服务端]:" + msg);
new TaskHandler (threadKey).start();
}
}
} catch (IOException e) {
e.getStackTrace();
}
}
}
class TaskHandler extends Thread {
private String threadKey;
private TaskHandler (String threadKey) {
this.threadKey = threadKey;
}
@Override
public void run() {
try {
System.out.print("[客户端]:");
sendMsg(threadKey, new Scanner(System.in).nextLine());
} catch (IOException e) {
e.getStackTrace();
}
}
}
class HeartbeatListener extends Thread {
private String threadKey;
private HeartbeatListener(String threadKey) { this.threadKey = threadKey; }
@Override
public void run() {
try {
Date time, now;
while ((time = heartbeatReceiptTimeMap.get(threadKey)) != null) {
now = new Date();
if (now.getTime() - time.getTime() > HEARTBEAT_TIMEOUT) {
connectionStatusMap.put(threadKey, false);
connectionRetryStatusMap.put(threadKey, true);
try {
socketMap.get(threadKey).close();
} catch (IOException e) {
e.printStackTrace();
}
return;
}
sendMsg(threadKey, HEARTBEAT);
try {
Thread.sleep(HEARTBEAT_INTERVAL);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.getStackTrace();
}
}
}
class ConnectionRetryListener extends Thread {
private String threadKey;
private ConnectionRetryListener(String threadKey) { this.threadKey = threadKey; }
@Override
public void run() {
while (connectionRetryStatusMap.get(threadKey) == null || connectionRetryStatusMap.get(threadKey)) {
if (connectionRetryStatusMap.get(threadKey) != null) {
connectionRetryCountMap.put(threadKey, connectionRetryCountMap.getOrDefault(threadKey, 0) + 1);
if (connectionRetryCountMap.get(threadKey) == 1) {
System.out.println();
}
System.out.println("连接已断开,正在尝试第" + connectionRetryCountMap.get(threadKey) + "次连接...");
try {
Thread.sleep(CONNECTION_RETRY_INTERVAL);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Client().start(threadKey);
return;
}
}
}
}
private void sendMsg(String threadKey, String msg) throws IOException {
OutputStream out = socketMap.get(threadKey).getOutputStream();
PrintWriter writer = new PrintWriter(out);
writer.println(msg);
writer.flush();
}
private String receiveMsg(String threadKey) throws IOException {
InputStream in = socketMap.get(threadKey).getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
return reader.readLine();
}
}