当前位置 : 首页 » 文章分类 :  开发  »  Java-Net

Java-Net

Java 网络相关笔记


异常

BindException: Address already in use

java.net.BindException: Address already in use: JVM_Bind
该异常发生在服务器端进行 new ServerSocket(port) (port是一个0,65536的整型值)操作时。异常的原因是与port一样的一个端口已经被启动,并进行监听。此时用 netstat –an 命令,可以看到一个Listening状态的端口。只需要找一个没有被占用的端口就能解决这个问题。

ConnectException: Connection refused

java.net.ConnectException: Connection refused: connect
该异常发生在客户端进行 new Socket(ip, port) 操作时,该异常发生的原因是或者具有ip地址的机器不能找到(也就是说从当前机器不存在到指定ip路由),或者是该ip存在,但找不到指定的端口进行监听。
出现该问题,首先检查客户端的ip和port是否写错了,如果正确则从客户端ping一下服务器看是否能ping通,如果能ping通(服务服务器端把ping禁掉则需要另外的办法),则看在服务器端的监听指定端口的程序是否启动,这个肯定能解决这个问题。

SocketException: Socket is closed

java.net.SocketException: Socket is closed
该异常在客户端和服务器均可能发生。异常的原因是己方主动关闭了连接后(调用了Socket的close方法)再对网络连接进行读写操作。

SocketException: Connection reset

SocketException: Connection reset by peer

java.net.SocketException: Connection reset
java.net.SocketException: Connection reset by peer: socket write error

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

导致“Connection reset”的原因是服务器端因为某种原因关闭了 Connection,而客户端依然在读写数据,此时服务器会返回复位标志“RST”,然后此时客户端就会提示 “java.net.SocketException: Connection reset”

服务器返回了“RST”时,如果此时客户端正在从 Socket 套接字的输出流中读数据则会提示 “Connection reset”
服务器返回了“RST”时,如果此时客户端正在往 Socket 套接字的输入流中写数据则会提示 “Connection reset by peer: socket write error”

java.net.SocketException: Connection reset
        at java.net.SocketInputStream.read(SocketInputStream.java:210)
        at java.net.SocketInputStream.read(SocketInputStream.java:141)

Oracle 官方文档中关于正常 TCP 连接释放和异常 TCP 连接释放的说明
Orderly Versus Abortive Connection Release in Java
https://docs.oracle.com/javase/8/docs/technotes/guides/net/articles/connection_release.html

SocketException: Broken pipe

java.net.SocketException: Broken pipe
该异常在客户端和服务器均有可能发生。在第4个异常的第一种情况中(也就是抛出 SocketExcepton:Connect reset by peer:Socket write error 后),如果再继续写数据则抛出该异常。前两个异常的解决方法是首先确保程序退出前关闭所有的网络连接,其次是要检测对方的关闭连接操作,发现对方关闭连接后自己也要关闭该连接。

一次SocketException:Connection reset 异常排查
https://www.cnblogs.com/shoren/p/httpclient-connectionreset.html


Socket

package java.net;
public class Socket implements java.io.Closeable {
}

close()

public synchronized void close() throws IOException {
    synchronized(closeLock) {
        if (isClosed())
            return;
        if (created)
            impl.close();
        closed = true;
    }
}

setSoLinger()

在Java Socket中,当我们调用Socket的 close() 方法时,默认的行为是当底层网卡所有数据都发送完毕后,关闭连接
通过setSoLinger方法,我们可以修改close方法的行为。

1、on = false
这是默认行为,当on为false时,linger对应的设置就没有意义,当 socket 主动 close,调用的线程会马上返回,不会阻塞,残留在缓冲区中的数据将继续发送给对端,并且与对端进行 FIN-ACK 协议交换,最后进入 TIME_WAIT 状态。

2、on = true, linger > 0
当网卡收到关闭连接请求后,等待 linger 时间
如果在 linger 过程中数据发送完毕,正常四次挥手关闭连接
如果在 delay_time 过程中数据没有发送完毕,发送 RST 包关闭连接

调用 close 的线程将阻塞,发生两种可能的情况:
1是剩余的数据继续接收,进行关闭协议交换;
2就是超时过期,剩余的数据将被删除,进行 FIN-ACK 交换。

3、on = true, linger = 0
这种方式就是所谓hard-close,这个方式是讨论或者争论最多的用法,当网卡收到关闭连接请求后,无论数据是否发送完毕,立即发送RST包关闭连接,任何剩余的数据都被立即丢弃,并且 FIN-ACK 交换也不会发生,替代产生 RST ,让对端抛出 Connection reset by peer 的 SocketException 。

public void setSoLinger(boolean on, int linger) throws SocketException {
    if (isClosed())
        throw new SocketException("Socket is closed");
    if (!on) {
        getImpl().setOption(SocketOptions.SO_LINGER, new Boolean(on));
    } else {
        if (linger < 0) {
            throw new IllegalArgumentException("invalid value for SO_LINGER");
        }
        if (linger > 65535)
            linger = 65535;
        getImpl().setOption(SocketOptions.SO_LINGER, new Integer(linger));
    }
}

tcp 参数 so_linger 说明及测试
http://www.4e00.com/blog/linux/2019/03/27/tcp-option-so-linger.html


InetAddress

Java InetAddress获取本机ip和端口号

IP
String IP = InetAddress.getLocalHost().getHostAddress();

端口号一般指的是 spring 端口号,直接读取配置项即可。

public class MyService {
    @Value("${server.port}")
    private int port;
}

InetAddress.getLocalHost().getHostAddress()获取的ip为127.0.0.1

在 test 环境可以通过 InetAddress.getLocalHost().getHostAddress() 顺利获取本机 ip,但换了一台新的服务器后每次获取的都是 127.0.0.1 了。

原因:
InetAddress.getLocalHost().getHostAddress() 是通过本机名去获取本机ip的
默认情况下本机名是localhost,在host文件中对应的ip是127.0.0.1,所以通过这个函数获取到的ip就是127.0.0.1了

解决方法:
1、hostname newhostname 修改主机名(重启后失效)
2、在 /etc/hosts 里加一行
本机IP newhostname

获取本机IP列表

若本机有多个网卡,下面方法可以获取所有的 ip

public static Set<InetAddress> resolveLocalAddresses() {
    Set<InetAddress> addrs = new HashSet<InetAddress>();
    Enumeration<NetworkInterface> ns = null;
    try {
        ns = NetworkInterface.getNetworkInterfaces();
    } catch (SocketException e) {
        // ignored...
    }
    while (ns != null && ns.hasMoreElements()) {
        NetworkInterface n = ns.nextElement();
        Enumeration<InetAddress> is = n.getInetAddresses();
        while (is.hasMoreElements()) {
            InetAddress i = is.nextElement();
            if (!i.isLoopbackAddress() && !i.isLinkLocalAddress() && !i.isMulticastAddress()
                    && !isSpecialIp(i.getHostAddress())) addrs.add(i);
        }
    }
    return addrs;
}

URL

package java.net;
public final class URL implements java.io.Serializable {
}

url.openStream() 等价于 openConnection().getInputStream()
openConnection() 返回一个 java.net.URLConnection 如果连接的是 Http 的 url,返回的就是一个 HttpURLConnection 对象,通过其 getInputStream() 方法直接就可以读取 http 资源。

HttpURLConnection 内部会初始化一个 TCP 连接到指定 http 地址,并发送一个 http get 请求,如果返回 200 则可以通过 InputStream 读取 response 内容。

使用Java URL读取图片数据

比如可以直接读取图片:

public static byte[] getImageBytesFromUrl(String imageUrl) {
    URL url = null;
    DataInputStream dataInputStream = null;
    ByteArrayOutputStream output = null;
    try {
        url = new URL(imageUrl);
        dataInputStream = new DataInputStream(url.openStream());

        output = new ByteArrayOutputStream();

        byte[] buffer = new byte[1024];
        int length;

        while ((length = dataInputStream.read(buffer)) > 0) {
            output.write(buffer, 0, length);
        }

        return output.toByteArray();
    } catch (IOException e) {
        logger.error("getImageBytesFromUrl error. imageUrl : " + imageUrl, e);
    } finally {
        IOUtils.closeQuietly(output);
        IOUtils.closeQuietly(dataInputStream);
    }
    return null;
}

HttpURLConnection用法详解
https://www.cnblogs.com/guodongli/archive/2011/04/05/2005930.html


上一篇 Helm

下一篇 MyCat

阅读
评论
1,840
阅读预计8分钟
创建日期 2020-09-08
修改日期 2020-09-22
类别
标签

页面信息

location:
protocol:
host:
hostname:
origin:
pathname:
href:
document:
referrer:
navigator:
platform:
userAgent:

评论