【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
https://blog.csdn.net/m0_69908381/article/details/131615974
出自【进步*于辰的博客】
注:
1、IP 类:InetAddress类;
2、套接字 IP 类:InetSocketAddress类。
1、概述
继承关系:
- java.lang.Object
- java.net.Socket
实现的所有接口
Closeable、AutoCloseable
直接已知子类:
SSLSocket
功能实现类继承关系:(此类负责客户端套接字的搭建,其相应功能则依托其他类完成)
- java.lang.Object
- java.net.SocketOptions
- java.net.SocketImpl
- java.net.AbstractPlainSocketImpl
- java.net.PlainSocketImpl
- java.net.SocksSocketImpl
- java.net.PlainSocketImpl
- java.net.AbstractPlainSocketImpl
- java.net.SocketImpl
- java.net.SocketOptions
(注:SocksSocketImpl
是此类功能的具体实现类,PlainSocketImpl
是功能封装类,而其他类都是抽象类。)
public class Socket extends Object
此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器之间的通信端点。
套接字的实际工作由 SocketImpl 类的实例执行。应用程序通过更改创建套接字实现的套接字工厂可以配置它自身,以创建适合本地防火墙的套接字。
从以下版本开始:
JDK1.0
另请参见:setSocketImplFactory(java.net.SocketImplFactory)
、SocketImpl、SocketChannel
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 说明:
-
bound = true
,表示“已绑定”; -
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 说明:
-
connected = true
,表示“已连接”; -
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三个版本》。文章来源:https://www.toymoban.com/news/detail-538830.html
本文暂缓更新。文章来源地址https://www.toymoban.com/news/detail-538830.html
到了这里,关于Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!