如标题所示,本文主要罗列下Socket编程中的那些常见异常,通过实战的方式来模拟这些异常。
1.java.net.BindException: Address already in use: JVM_Bind原因:就是当前端口号已经被其他进程占用了。
模拟:创建两个ServerSocket,以相同的端口号启动。
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress("localhost", 9999));
ServerSocket serverSocket2 = new ServerSocket();
serverSocket2.bind(new InetSocketAddress("localhost", 9999));
此时就会出现该异常
2.java.net.ConnectException: Connection refused: connect原因:当客户端通过Socket(ip,port)创建对远端ip:port的连接时,当对应的ip或port并不存在时(或者ip存在但是在执行port上没有进程在监听),则会报当前错
模拟:服务端以9999端口启动,客户端以8888端口连接
// 服务端启动
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress("localhost", 9999));
// 客户端启动
Socket socket = new Socket("localhost", 8888);
3.java.net.SocketTimeoutException: connect timed out
原因:客户端在规定时间内没有连接上服务端。这个报错的具体原因有很多种
模拟:笔者在Mac上启动ServerSocket,并打开防火墙,阻止所有传入连接;然后在Windows电脑上开启Socket连接
服务端代码
SocketAddress address = new InetSocketAddress("localhost", 9999);
ServerSocket ss = new ServerSocket();
ss.bind(address);
// 接收请求
Socket s = ss.accept();
// 直接sleep
Thread.sleep(1000000);
客户端代码
public class Client {
static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) {
connectTime();
}
public static void connectTime() {
Socket socket = new Socket();
try {
System.out.println("begin time:" + format.format(new Date()));
// 连接服务端ip:port
socket.connect(new InetSocketAddress("192.168.3.18", 9999), 3000);
System.out.println(socket.isConnected());
} catch (IOException e) {
e.printStackTrace();
} finally {
System.out.println("end time:" + format.format(new Date()));
}
}
}
测试:
先启动服务端,再启动客户端,可以看到客户端的控制台信息如下
begin time:2022-06-18 11:54:19
java.net.SocketTimeoutException: connect timed out
at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method)
at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:85)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:589)
at sockettimeout.Client.connectTime(Client.java:27)
at sockettimeout.Client.main(Client.java:19)
end time:2022-06-18 11:54:22
连接花费时间为3秒,就是我们设置的连接超时时间
4.java.net.SocketTimeoutException: Read timed out原因:客户端或服务端Socket设置了读超时时间,在指定时间内没有获取到结果,则会抛出该异常
模拟:服务端监听到连接后直接sleep,客户端在连接成功后从InputStream中获取数据,一直等待到超时
服务端代码
SocketAddress address = new InetSocketAddress("localhost", 9999);
ServerSocket ss = new ServerSocket();
ss.bind(address);
// 接收请求
Socket s = ss.accept();
// 直接sleep
Thread.sleep(1000000);
客户端代码
static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) {
try {
Socket socket = new Socket("localhost", 9999);
// 设置超时时间为5秒
socket.setSoTimeout(5000);
socket.setKeepAlive(true);
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
System.out.println("begin time:" + format.format(new Date()));
// 在这里阻塞read方法,一直到超时为止
while (inputStream.read() != -1) {
inputStream.read(bytes);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("end time:" + format.format(new Date()));
}
}
测试:
先启动服务端,再启动客户端,可以看到客户端的控制台信息如下
begin time:2022-06-18 11:16:04
java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at java.net.SocketInputStream.read(SocketInputStream.java:224)
at sockettimeout.Client.main(Client.java:27)
end time:2022-06-18 11:16:09
end-start正好等于我们设置的Socket.setSoTimeout(5000) 5秒钟。
5.java.net.SocketException: Socket closed原因:这种情况在客户端和服务端都有可能发生。主要原因是:在socket关闭了连接后,又发起了读写操作
模拟:客户端Socket创建完成,在调用close()方法后,再次发起写操作
5.1 服务端代码
下面展示的是一个完整的服务端代码,后续会采用该示例
public class BIOServerSocket {
private String address;
private int port;
public BIOServerSocket(String address, int port) {
this.address = address;
this.port = port;
}
public void startServer() {
try {
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(address, port));
System.out.println("bio server start...");
while (true) {
Socket clientSocket = serverSocket.accept();
System.out.println("client connect...");
// 写入 thread
ServerWriteThread serverWriteThread = new ServerWriteThread(clientSocket);
serverWriteThread.start();
// 读取数据
read(clientSocket);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void read(Socket clientSocket) {
try {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String msg = "";
while ((msg = bufferedReader.readLine()) != null) {
System.out.println("receive msg: " + msg);
if (msg.equals("bye")) {
bufferedReader.close();
clientSocket.close();
}
}
} catch (IOException e) {
try {
clientSocket.close();
} catch (IOException ex) {
ex.printStackTrace();
}
e.printStackTrace();
}
}
public static void main(String[] args) {
String address = "localhost";
int port = 9999;
BIOServerSocket bioServerSocket = new BIOServerSocket(address, port);
bioServerSocket.startServer();
}
}
/**
* 从Scanner获取输入信息,并写回到client
*/
class ServerWriteThread extends Thread {
private Socket socket;
private PrintWriter writer;
private Scanner scanner;
public ServerWriteThread(Socket socket) throws IOException{
this.socket = socket;
scanner = new Scanner(System.in);
this.writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
}
@Override
public void run() {
String msg = "";
try {
while ((msg = scanner.nextLine()) != null) {
if (msg.equals("bye")) {
socket.close();
break;
}
writer.println(msg);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
客户端代码
try {
Socket socket = new Socket("localhost", 9999);
OutputStream outputStream = socket.getOutputStream();
// 第一次写数据
outputStream.write(11);
// 关闭socket
socket.close();
// 第二次写数据
outputStream.write(12);
} catch (IOException e) {
e.printStackTrace();
}
测试:
先启动服务端,再启动客户端,可以看到客户端的控制台输出如下信息
java.net.SocketException: Socket closed
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:118)
at java.net.SocketOutputStream.write(SocketOutputStream.java:134)
at sockettimeout.Client.socketClose(Client.java:32)
at sockettimeout.Client.main(Client.java:20)
6.java.net.SocketException: Software caused connection abort: socket write error
原因:该问题有可能发生在客户端或服务端。主要因为:当一端的socket被关闭后,另一端仍进行数据读写
注意:与5的不同在于,5中的示例是当前socket关闭后又继续发送数据,而6该问题是对端关闭了socket,当前端仍进行数据读写
模拟:服务端接收到连接后,等待一段时间则关闭该socket,而此时客户端继续读写
服务端代码
SocketAddress address = new InetSocketAddress("localhost", 9999);
ServerSocket ss = new ServerSocket();
ss.bind(address);
// 接收连接
Socket s = ss.accept();
Thread.sleep(3000);
s.close();
客户端代码:
try {
System.out.println("begin time:" + format.format(new Date()));
Socket socket = new Socket("localhost", 9999);
OutputStream outputStream = socket.getOutputStream();
outputStream.write(11);
System.out.println("send 11");
Thread.sleep(1000);
outputStream.write(12);
System.out.println("send 12");
Thread.sleep(1000);
outputStream.write(13);
System.out.println("send 13");
Thread.sleep(1000);
outputStream.write(14);
System.out.println("send 14");
Thread.sleep(1000);
outputStream.write(15);
System.out.println("send 15");
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("end time:" + format.format(new Date()));
}
测试:先启动服务端,再启动客户端,控制台结果如下:
begin time:2022-06-18 12:16:14
send 11
send 12
send 13
java.net.SocketException: Software caused connection abort: socket write error
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:111)
at java.net.SocketOutputStream.write(SocketOutputStream.java:134)
at sockettimeout.Client.connectReset(Client.java:38)
at sockettimeout.Client.main(Client.java:20)
end time:2022-06-18 12:16:17
注意:还有可能遇到些别的异常信息,如connect reset by peer,实际是与当前场景类似的。
参考:java.net.SocketException四大异常解决方案_hzp666的博客-CSDN博客_socketexception
Java SDK报错:SocketTimeoutException - 表格存储 - 阿里云 SocketTimeoutException的一些场景