Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码)

【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
https://blog.csdn.net/m0_69908381/article/details/131615974
出自【进步*于辰的博客

注:
1、IP 类:InetAddress类
2、套接字 IP 类:InetSocketAddress类。

文章目录

1、概述

继承关系:

  • java.lang.Object
    • java.net.Socket

实现的所有接口
CloseableAutoCloseable
直接已知子类:
SSLSocket

功能实现类继承关系:(此类负责客户端套接字的搭建,其相应功能则依托其他类完成)

  • java.lang.Object
    • java.net.SocketOptions
      • java.net.SocketImpl
        • java.net.AbstractPlainSocketImpl
          • java.net.PlainSocketImpl
            • java.net.SocksSocketImpl

(注:SocksSocketImpl是此类功能的具体实现类,PlainSocketImpl是功能封装类,而其他类都是抽象类。)


public class Socket extends Object

此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器之间的通信端点。

套接字的实际工作由 SocketImpl 类的实例执行。应用程序通过更改创建套接字实现的套接字工厂可以配置它自身,以创建适合本地防火墙的套接字。

从以下版本开始:
JDK1.0
另请参见:
setSocketImplFactory(java.net.SocketImplFactory)SocketImplSocketChannel

2、构造方法摘要

2.1 null

通过系统默认类型的 SocketImpl 创建未连接套接字。

2.2 InetAddress address, int port

创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
说明:
先看源码。
在这里插入图片描述
底层调用第4.1项。

示例:
注:构建Socket服务器的示例见ServerSocket类的第4个构造方法。

// 构造第1个Socket客户端
InetAddress ip = InetAddress.getByName("localhost");
Socket client = new Socket(ip, 8080);

// 构造第2个Socket客户端
InetAddress ip = InetAddress.getByName("localhost");
Socket client = new Socket(ip, 8080);

打印结果:
在这里插入图片描述
由于Socket服务器只能接收1个连接请求,故第1次请求连接时正常,第2次时报错。

2.3 InetAddress host, int port, boolean stream

已过时Use DatagramSocket instead for UDP transport

2.4 InetAddress address, int port, InetAddress localAddr, int localPort

创建一个套接字并将其连接到指定远程端口上的指定远程地址。
说明:
先看源码。
在这里插入图片描述
底层调用第4.1项。

2.5 Proxy proxy

根据不管其他设置如何都应使用的指定代理类型(如果有),创建一个未连接的套接字。
说明:
先看源码。
在这里插入图片描述
后续解析。

示例:

InetSocketAddresssip = new InetSocketAddress("localhost", 8080);
Proxy proxy = new Proxy(Proxy.Type.SOCKS, sip);
Socket client = new Socket(proxy);
client.connect(sip);

关于sip,见InetSocketAddress类的第3个构造方法;关于proxy,见Proxy类;关于connect(),见第3.3项。

采用代理方式构造 Socket 套接字/客户端,初始是未连接的,因此手动连接。

打印结果:
在这里插入图片描述
为何显示Connection reset,后续解析。

2.6 protected (SocketImpl impl)

创建带有用户指定的 SocketImpl 的未连接 Socket。
说明:
先看源码。
在这里插入图片描述
关于checkPermission(),见第5.4项。

底层调用第4.2项。

2.7 String host, int port

创建一个流套接字并将其连接到指定主机上的指定端口号。
说明:
先看源码。
在这里插入图片描述
底层调用第4.1项。

2.8 String host, int port, boolean stream

已过时。 使用 DatagramSocket 取代 UDP 传输

2.9 String host, int port, InetAddress localAddr, int localPort

创建一个套接字并将其连接到指定远程主机上的指定远程端口。
说明:
先看源码。
在这里插入图片描述
底层调用第4.1项。

3、方法摘要

3.1 void bind(SocketAddress bindpoint)

将套接字绑定到本地地址。
说明:
先看源码。
在这里插入图片描述
关于isClosed(),见第24项;关于oldImpl,见第5.1项;关于isBound(),见第23项。

!oldImpl && isBound()为 true 说明:

  1. bound = true,表示“已绑定”;
  2. oldImpl = false,表示当前套接字实现类是“新的”(即“可用”)。(可进一步证明在第5.1项中我对字段oldImpl作用的判断和总结)

关于isUnresolved()/new InetSocketAddress()/getAddress()/getPort(),见套接字 IP 类的第3.7、2.2、3.3、3.5项;关于checkAddress(),见第5.5项;关于checkListen(),见SecurityManager类的第4.12项。

进入bind()。(出自实现类 PlainSocketImpl)
在这里插入图片描述
进入bind()。(出自实现类 AbstractPlainSocketImpl)
在这里插入图片描述
后续解析。

3.2 void close()

关闭此套接字。

3.3 void connect(SocketAddress endpoint)

将此套接字连接到服务器。
说明:
先看源码。
在这里插入图片描述

指定:
超时时间 timeout为 0。

底层调用下1项。

3.4 void connect(SocketAddress endpoint, int timeout)

将此套接字连接到具有指定超时值的服务器。
说明:
先看源码。
在这里插入图片描述
关于isClosed(),见第24项;关于oldImpl,见第5.1项;关于isConnected(),见第25项。

!oldImpl && isConnected()为 true 说明:

  1. connected = true,表示“已连接”;
  2. oldImpl = false,表示当前套接字实现类是“新的”(即“可用”)。(可进一步证明在第5.1项中我对字段oldImpl作用的判断和总结)

endpoint instanceof InetSocketAddress表示仅支持“套接字 IP”。

关于getAddress()/getPort()/isUnresolved()/getHostName(),见套接字 IP 类的第3.3、3.5、3.7、3.4项;关于checkAddress(),见第5.5项;关于checkConnect(),见SecurityManager类的第4.5项;关于getHostAddress(),见IP 类的第2.8项;关于createImpl(),见第5.3项。

进入connect()。(出自实现类 PlainSocketImpl)
在这里插入图片描述
进入connect()。(出自实现类 AbstractPlainSocketImpl)
在这里插入图片描述
后续解析。

3.5 SocketChannel getChannel()

返回与此数据报套接字关联的惟一 SocketChannel 对象(如果有)。

3.6 InetAddress getInetAddress()

返回套接字连接的地址。

3.7 InputStream getInputStream()

返回此套接字的输入流。

3.8 boolean getKeepAlive()

测试是否启用 SO_KEEPALIVE

3.9 InetAddress getLocalAddress()

获取套接字绑定的本地地址。

3.10 int getLocalPort()

返回此套接字绑定到的本地端口。

3.11 SocketAddress getLocalSocketAddress()

返回此套接字绑定的端点的地址,如果尚未绑定则返回 null。

3.12 boolean getOOBInline()

测试是否启用 OOBINLINE

3.13 OutputStream getOutputStream()

返回此套接字的输出流。

3.14 int getPort()

返回此套接字连接到的远程端口。

3.15 int getReceiveBufferSize()

获取此 Socket 的 SO_RCVBUF 选项的值,该值是平台在 Socket 上输入时使用的缓冲区大小。

3.16 SocketAddress getRemoteSocketAddress()

返回此套接字连接的端点的地址,如果未连接则返回 null。

3.17 boolean getReuseAddress()

测试是否启用 SO_REUSEADDR

3.18 int getSendBufferSize()

获取此 Socket 的 SO_SNDBUF 选项的值,该值是平台在 Socket 上输出时使用的缓冲区大小。

3.19 int getSoLinger()

返回 SO_LINGER 的设置。

3.20 int getSoTimeout()

返回 SO_TIMEOUT 的设置。

3.21 boolean getTcpNoDelay()

测试是否启用 TCP_NODELAY

3.22 int getTrafficClass()

为从此 Socket 上发送的包获取 IP 头中的流量类别或服务类型。

3.23 boolean isBound()

返回套接字的绑定状态。
说明:
先看源码。
在这里插入图片描述
关于oldImpl,见第5.1项。

3.24 boolean isClosed()

返回套接字的关闭状态。
说明:
先看源码。
在这里插入图片描述
关于closeLock,暂未知。

3.25 boolean isConnected()

返回套接字的连接状态。
说明:
先看源码。
在这里插入图片描述
关于oldImpl,见第5.1项。

3.26 boolean isInputShutdown()

返回是否关闭套接字连接的半读状态 (read-half)。

3.27 boolean isOutputShutdown()

返回是否关闭套接字连接的半写状态 (write-half)。

3.28 void sendUrgentData(int data)

在套接字上发送一个紧急数据字节。

3.29 void setKeepAlive(boolean on)

启用/禁用 SO_KEEPALIVE

3.30 void setOOBInline(boolean on)

启用/禁用 OOBINLINE(TCP 紧急数据的接收者) 默认情况下,此选项是禁用的,即在套接字上接收的 TCP 紧急数据被悄悄丢弃。

3.31 void setPerformancePreferences(int connectionTime, int latency, int bandwidth)

设置此套接字的性能偏好。

3.32 void setReceiveBufferSize(int size)

将此 Socket 的 SO_RCVBUF 选项设置为指定的值。

3.33 void setReuseAddress(boolean on)

启用/禁用 SO_REUSEADDR 套接字选项。

3.34 void setSendBufferSize(int size)

将此 Socket 的 SO_SNDBUF 选项设置为指定的值。

3.35 static void setSocketImplFactory(SocketImplFactory fac)

为应用程序设置客户端套接字实现工厂。

3.36 void setSoLinger(boolean on, int linger)

启用/禁用具有指定逗留时间(以秒为单位)的 SO_LINGER

3.37 void setSoTimeout(int timeout)

启用/禁用带有指定超时值的 SO_TIMEOUT,以毫秒为单位。

3.38 void setTcpNoDelay(boolean on)

启用/禁用 TCP_NODELAY(启用/禁用 Nagle 算法)。

3.39 void setTrafficClass(int tc)

为从此 Socket 上发送的数据包在 IP 头中设置流量类别 (traffic class) 或服务类型八位组 (type-of-service octet)。

3.40 void shutdownInput()

此套接字的输入流置于“流的末尾”。

3.41 void shutdownOutput()

禁用此套接字的输出流。

3.42 String toString()

将此套接字转换为 String。

3.43 static synchronized void setSocketImplFactory(SocketImplFactory fac)

为应用程序设置客户端套接字实现工厂。
说明:
先看源码。
在这里插入图片描述
注译:
为应用程序设置客户端套接字实现工厂。工厂只能指定一次。

当应用程序创建新的客户端套接字时,将调用套接字实现工厂的createSocketImpl()方法来创建实际的套接字实现。

除非已经设置了工厂,否则将null传递给方法是不操作的。

如果存在安全管理器,则此方法首先调用安全管理器的checkSetFactory()方法以确保允许操作。这可能导致 SecurityException。


关于checkSetFactory(),见SecurityManager类的第4.27项。

factory用于构造客户端套接字实现类。

4、构造方法摘要(不开放)

4.1 private (SocketAddress address, SocketAddress localAddr, boolean stream)

先看源码。
在这里插入图片描述
关于setImpl(),见第5.2项;关于createImpl(),见第5.3项;关于bind(),见第3.1项。

后续补充解析。

4.2 private Socket(Void ignore, SocketImpl impl)

根据指定的套接字实现类,构造套接字。
说明:
先看源码。
在这里插入图片描述
关于checkOldImpl(),见第5.1项。

5、方法摘要(不开放)

5.1 private void checkOldImpl()

记录当前套接字实现类的“新旧”状态。
说明:
先看源码。
在这里插入图片描述
impl的类型是SocketImpl,这是套接字实现类的超类,是一个抽象类。从概述中可知,SocketImpl 抽象类的实现类有两个:PlainSocketImpl 和 SocksSocketImpl,其子类是 AbstractPlainSocketImpl。

以常理而言impl的指向不会是 SocketImpl 或其子类的实例,因为抽象类不能进行实例化(new)。不过,抽象类是可以有实例的(通过反射newInstance()创建(当然,我们手动无法完成),也可以定义构造方法。
下图是执行newInstance()的结果。
在这里插入图片描述
不过,目前套接字功能的实现源码是分布在这3个子类中的,基于基本原理(成员方法需要实例进行调用),故impl在需要时会指向这3个子类中方法所属类的实例。
因此,在一定条件下,JVM 能为抽象类(SocketImpl 和其子类)创建实例,故impl的指向可以是这4个类的实例

看下述代码。

Class z1 = Class.forName("java.net.SocketImpl");
Class z2 = Class.forName("java.net.PlainSocketImpl");
Class z3 = Class.forName("java.net.AbstractPlainSocketImpl");
Class z4 = Class.forName("java.net.SocksSocketImpl");

Method m1 = z1.getDeclaredMethod("connect", SocketAddress.class, int.class);
System.out.println(m1);
m1 = z2.getDeclaredMethod("connect", SocketAddress.class, int.class);
System.out.println(m1);
m1 = z3.getDeclaredMethod("connect", SocketAddress.class, int.class);
System.out.println(m1);
m1 = z4.getDeclaredMethod("connect", SocketAddress.class, int.class);
System.out.println(m1);

打印结果。
在这里插入图片描述
源码中通过反射获取connect(),于是我手动尝试获取。
这4个类都未对外界开放,因此只能通过调用Class.forName()进行类加载获取其 Class 对象。
从打印结果可见,都能获取到connect(),说明这4个类中都声明或定义了connect()
当然,源码中并未接收,只是尝试通过反射获取。在获取时,若未找到,会抛出 NoSuchMethodException,然后执行异常块catch() {}

关于getSuperclass(),见Class<T>的第2.42项。可见,此方法返回的是当前类的父类的 Class 对象。

分析: \color{blue}{分析:} 分析:(字段oldImpl的作用)
由于getSuperclass()返回的是当前类的父类的 Class 对象,故红框为 true 表示:impl指向抽象类 AbstractPlainSocketImpl 的实例。
若是这样,前后矛盾。为什么?
因为假若程序能执行到这一步,说明通过反射未能获取到此connect()。但经过上文中手动获取测试,得出:抽象类 AbstractPlainSocketImpl 中定义了connect()。既然已定义,又怎会获取不到,进而抛出 NoSuchMethodException,故前后矛盾。
源码如此,只有一种可能。

在低版本JDK(1.3)之前,抽象类 SocketImpl 的继承体系并非如概述中所示,至少其子类不是 AbstractPlainSocketImpl。且关键是,到目前(JDK11)为止,虽然那些“过时”的实现类已被从 SocketImpl 的继承体系中“剔除”,但并未废弃(可能有某种顾虑),在实现套接字功能时,JVM 在底层可能会为那些实现类创建实例。
简言之,impl可能会指向那些“过时”的实现类的实例

结论: \color{red}{结论:} 结论:
字段oldImpl的作用是记录套接字实现类的“新旧”状态,目的是判断impl实时指向的实例所属类是否已“过时”(因为“过时”的实现类缺乏或不完善某些套接字功能,至少未定义connect())。
简言之,判断impl是否可用

5.2 void setImpl()

将当前客户端套接字实现类设置为系统默认客户端套接字实现类。
说明:
先看源码。
在这里插入图片描述
关于factory,见第3.43项;关于checkOldImpl(),,见上1项。

5.3 void createImpl(boolean stream)

创建套接字实现类对象。
说明:
先看源码。
在这里插入图片描述
关于setImpl(),见上1项。

进入create()。(出自实现类 PlainSocketImpl)
在这里插入图片描述

5.4 private static Void checkPermission(SocketImpl impl)

检查是否有权设置套接字实现类。
说明:
先看源码。
在这里插入图片描述
关于SET_SOCKETIMPL_PERMISSION,见SecurityConstants类的第2.3.8项;关于checkPermission(),见SecurityManager类的第4.18项。

5.5 private void checkAddress (InetAddress addr, String op)

检查 IP 地址是否有效。
说明:
先看源码。
在这里插入图片描述
目前,IP 地址只有 IPV4 和 IPV6 这两个版本有效。

5.6 SocketImpl getImpl()

获取套接字实现类对象。
说明:
先看源码。
在这里插入图片描述
关于createImpl(),见第5.3项。

最后

如果大家需要Java-API文档,我上传了《Java-API文档-包含5/8/11三个版本》。


本文暂缓更新。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进步·于辰

谢谢打赏!!很高兴可以帮到你!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值