您当前的位置: 首页 > 

恐龙弟旺仔

暂无认证

  • 0浏览

    0关注

    282博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Socket常见异常场景模拟实战

恐龙弟旺仔 发布时间:2022-09-17 15:31:43 ,浏览量:0

前言:

如标题所示,本文主要罗列下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的一些场景

关注
打赏
1655041699
查看更多评论
立即登录/注册

微信扫码登录

0.0658s