java_net_SocketException

问题分析

当服务端创建SocketServer时、客户端向服务端创建socket时、连接过程中发生错误时会抛出此类异常。此类异常主要有四种子类BindException、 ConnectException、NoRouteToHostException、PortUnreachableException,均可以被SocketException捕获。这四种子类的问题分析如下:

  1. BindException试图将套接字绑定到本地地址和端口时发生错误的情况下,抛出此异常。

  2. ConnectException试图将套接字连接到远程地址和端口时发生错误的情况下,抛出此异常,典型的方式是因为远程主机拒绝,例如远程主机不存在或并未监听此端口。

  3. NoRouteToHostException试图将套接字连接到远程地址和端口时发生错误的情况下,抛出此异常,一般原因是因为防火墙拦截或必经路由器中断引起的。

  4. PortUnreachableException在连接的数据报上已接收到ICMP Port Unreachable消息时,抛出该异常。

解决方案

当服务端创建SocketServer时、客户端向服务端创建socket时、连接过程中发生错误时会抛出此类异常,建议使用try catch finally块捕获异常并做好业务处理。

示例一

java.net.SocketException:Connection reset
 at java.net.SocketInputStream.read(SocketInputStream.java:196)
 at java.net.SocketInputStream.read(SocketInputStream.java:122)
 at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:283)
 at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:325)
 at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:177)
 at java.io.InputStreamReader.read(InputStreamReader.java:184)
 at java.io.BufferedReader.fill(BufferedReader.java:154)
 at java.io.BufferedReader.readLine(BufferedReader.java:317)
 at java.io.BufferedReader.readLine(BufferedReader.java:382)

问题分析:该异常在客户端和服务器端均有可能发生,引起该异常的原因有两个,第一个就是如果一端的Socket被关闭(或主动关闭或者因为异常退出而引起的关闭),另一端仍发送数据,发送的第一个数据包引发该异常(Connect reset by peer)。另一个是一端退出,但退出时并未关闭该连接,另一端如果在从连接中读数据则抛出该异常(Connection reset)。主要原因是在连接断开后的读和写操作引起的异常。

解决方案:建议不要频繁建立过多socket请求导致服务器压力增大抛出SocketException,并使用try catch块捕获异常做后续处理。如果服务是幂等的,则客户端可以重试连接。如果不是幂等的,重试可能造成重复提单。

代码示例:

1:服务端代码

class SimpleServer implements Runnable {
    @Override
    public void run() {
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(8111);
            serverSocket.setSoTimeout(3000);
            while (true) {
                try {
                    Socket clientSocket = serverSocket.accept();
                    BufferedReader inputReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                    System.out.println("Client said :"+ inputReader.readLine());
                } catch (SocketTimeoutException e) {
                    e.printStackTrace();
                }
            }
        }catch (SocketException e) {
            e.printStackTrace();
        }catch (IOException e1) {
            e1.printStackTrace();
        } finally {
            try {
                if (serverSocket != null) {
                    serverSocket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

2:客户端代码

class SimpleClient implements Runnable {
    @Override
    public void run() {
        Socket socket = null;
        try {
            socket = new Socket("localhost", 8111);
            PrintWriter outWriter = new PrintWriter(socket.getOutputStream(), true);
            System.out.println("Wait");
            Thread.sleep(15000);
            //throw new Exception("Random exception");
            outWriter.println("Hello Mr. Server!");
        }catch (SocketException e) {
            e.printStackTrace();
        }catch (InterruptedException e) {
            e.printStackTrace();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (socket != null)
                    socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

示例二

 java.net.ConnectException:Connection refused: connect
 at java.net.DualStackPlainSocketImpl.connect0(NativeMethod)
 at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79)
 at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339)
 at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200)
 at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182)
 at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
 at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
 at java.net.Socket.connect(Socket.java:579)
 at java.net.Socket.connect(Socket.java:528)
 at java.net.Socket.(Socket.java:425)
 at java.net.Socket.(Socket.java:208)

问题分析:服务端不能响应客户端的连接请求抛出异常。可能原因:IP或者端口写错、服务器宕机、防火墙等原因。

解决方案:服务端不能响应客户端的连接请求抛出异常。建议检查IP或者端口正确、服务器可以访问、防火墙未限制。代码示例与示例一相同。

参考文献

阿里云首页 移动测试 相关技术圈