Java的Socket通信的断网重连的正确写法
- Socket通信的断网重连介绍
- 客户端与服务端源码
- 演示截图
- 本地演示
- 服务器演示
- 演示截图
- 总结
Socket通信的断网重连介绍
针对于已经建立通信的客户端与服务器,当客户端与服务器因为网络问题导致网络不通而断开连接了或者由于服务器端的服务被突然停掉,而客户端进行的一种尝试重新建立连接的操作;我采用的是Socket自带的往通道内写数据是否成功来判断当前连接是否断开,采用该方式的好处是简洁易懂,高效通用。
一般的采用ping命令,定时尝试重新建立新的连接的方式,都有一些不足之处,前者是没办法针对端口进行判断,效果不好;后者则是每次都建立新的连接,导致服务器端压力会较大,而且相对的代码逻辑也会更加复杂。而采用本文介绍的写数据的方式,即可简单判断连接是否断开;原理是当连接断开后,写数据时会报错,捕获该错误即可;底层原理则是Java的Socket底层代码,采用C语言或者C++编写,在那里肯定是去判断IP和端口是否可以访问到的逻辑;有需要的话,下篇文章可以查看源码,进一步熟悉Java的网络编程知识。
客户端与服务端源码
为了方便演示和理解,仅进行了服务端向客户端发送数据,客户端接收数据的单向通信方式;实际扩展也非常简单。可以由读者进行尝试实现,增加网络编程知识的熟练度。
服务端
package com;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
/**
* @author BBYH
*/
public class MyServerSocket {
public static final Integer PORT = 8080;
private static Socket myServerSocket;
private static OutputStream outputStream;
public static void listen() {
System.out.println("服务端监听端口" + PORT);
new Thread(() -> {
try (ServerSocket serverSocket = new ServerSocket(PORT)) {
while (true) {
try {
myServerSocket = serverSocket.accept();
System.out.println("服务端接收到来自客户端的连接");
outputStream = myServerSocket.getOutputStream();
new Thread(() -> {
Scanner scanner = new Scanner(System.in);
while (true) {
try {
System.out.println("向客户端发送消息:");
String sendContent = scanner.next();
outputStream.write(sendContent.getBytes(StandardCharsets.UTF_8));
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
public static void main(String[] args) {
listen();
}
}
客户端
package com;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Date;
/**
* @author BBYH
* @description 通过socket的写数据,来判断当前的socket是否连通
*/
public class ClientSocket {
public static final String IP = "127.0.0.1";
public static final Integer PORT = 8080;
private static Socket socket;
private static InputStreamReader reader;
private static OutputStream outputStream;
public static void main(String[] args) {
// 首次建立连接
connect();
}
public static void connect() {
System.out.println("向服务端端口" + PORT + "申请建立连接");
try {
socket = new Socket(IP, PORT);
System.out.println("客户端与服务器端连接建立成功");
reader = new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8);
outputStream = socket.getOutputStream();
// 开启读取线程
openReadThread();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void openReadThread() {
new Thread(() -> {
// 开启断网重连线程
new Thread(() -> {
while (true) {
if (isDisContentWithServer()) {
while (true) {
try {
socket = new Socket(IP, PORT);
reader = new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8);
outputStream = socket.getOutputStream();
System.out.println(new Date() + ":重新建立连接成功");
break;
} catch (IOException e) {
System.out.println(new Date() + ":尝试重新建立连接失败");
try {
Thread.sleep(3000);
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}
}
}
}
}).start();
char[] readBuf = new char[1024];
while (true) {
try {
if (reader.ready()) {
int read = reader.read(readBuf);
System.out.println("接收到来自服务端的消息:" + new String(readBuf, 0, read));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
public static boolean isDisContentWithServer() {
try {
outputStream.write(("发送数据").getBytes(StandardCharsets.UTF_8));
Thread.sleep(3000);
return false;
} catch (Exception e) {
return true;
}
}
}
尽管在客户端开启断网重连线程中进行了两次while循环,不是很优雅,但仍然可以较为清晰的理解到代码的执行逻辑;即,每隔固定时间(3秒)进行是否连通的判断,当发现不连通的,则进行尝试重连(重新建立连接),该处的逻辑没有办法省略,由于客户端一般不长时间运行,消耗的资源虽然较多,但在程序运行结束后也自然就释放了。
演示截图
演示流程为:开启服务器 – 客户端建立连接 – 服务器发送一些数据 – 服务器断开连接 – 客户端尝试重连(自动进行) – 重新开启服务器 – 服务器再次发送数据
在该过程中,服务器在首次开启后的关闭,即代表了客户端的断网,实际情况中可以采用服务器,然后采用断开Wifi的方式代替这个关闭服务器,只要保证了客户端与服务器无法连通,即代表断网了,后面的重连则可以采用再次打开Wifi,重新连接上网络,连接则自动建立好
本地演示
开启服务器 – 客户端建立连接 – 服务器发送一些数据
服务器断开连接 – 客户端尝试重连(自动进行)
重新开启服务器 – 服务器再次发送数据
服务器演示
采用的是我申请的阿里云服务器,流程与上述文字说明一致,采用断开Wifi的形式来代替服务器关闭的效果;服务器关闭同样被我测试过,与本地一样,可以进行重连。由于我采用Xshell进行连接,当断开连接后日志会失效,所以采用后台任务记录运行打印的日志;命令为 nohup java com.MyServerSocket > MyServerSocket.out &
关于不采用防火墙来演示断网效果,则是因为防火墙无法拦截已经建立好的Socket通道,经测试发现该现象,读者可手动进行验证,如有意外情况,可向我进行反馈。
由于nohup命令的输入重定向,我暂时还没研究明白,所以目前服务端也不进行发送数据,两边都只是简单的建立连接和断开,代码进行了简单的调整,如下:
服务器端
package com;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author BBYH
*/
public class MyServerSocket {
public static final Integer PORT = 8080;
private static Socket myServerSocket;
public static void listen() {
System.out.println("服务端监听端口" + PORT);
new Thread(() -> {
try (ServerSocket serverSocket = new ServerSocket(PORT)) {
while (true) {
try {
myServerSocket = serverSocket.accept();
System.out.println("服务端接收到来自客户端的连接");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
public static void main(String[] args) {
listen();
}
}
客户端
package com;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Date;
/**
* @author BBYH
* @description 通过socket的写数据,来判断当前的socket是否连通
*/
public class ClientSocket {
public static final String IP = "127.0.0.1";
public static final Integer PORT = 8080;
private static Socket socket;
private static OutputStream outputStream;
public static void main(String[] args) {
// 首次建立连接
connect();
}
public static void connect() {
System.out.println("向服务端端口" + PORT + "申请建立连接");
try {
socket = new Socket(IP, PORT);
System.out.println("客户端与服务器端连接建立成功");
outputStream = socket.getOutputStream();
// 开启断网重连线程
openReConnectThread();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void openReConnectThread() {
new Thread(() -> {
while (true) {
try {
if (isDisContentWithServer()) {
while (true) {
try {
socket = new Socket(IP, PORT);
outputStream = socket.getOutputStream();
System.out.println(new Date() + ":重新建立连接成功");
break;
} catch (IOException e) {
System.out.println(new Date() + ":尝试重新建立连接失败");
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
public static boolean isDisContentWithServer() {
try {
outputStream.write(("发送数据").getBytes(StandardCharsets.UTF_8));
Thread.sleep(2000);
return false;
} catch (Exception e) {
return true;
}
}
}
演示截图
开启服务器 – 客户端建立连接
断开Wifi – 客户端尝试重新建立连接 – 重新打开Wifi – 连接成功
总结
本次断网重连演示完美结束,如有疑问,可在评论区进行提问