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

这篇具有很好参考价值的文章主要介绍了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

实现的所有接口
Closeable、AutoCloseable
直接已知子类:
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)、SocketImpl、SocketChannel

2、构造方法摘要

2.1 null

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

2.2 InetAddress address, int port

创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
说明:
先看源码。
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
底层调用第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);

打印结果:
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
由于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

创建一个套接字并将其连接到指定远程端口上的指定远程地址。
说明:
先看源码。
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
底层调用第4.1项。

2.5 Proxy proxy

根据不管其他设置如何都应使用的指定代理类型(如果有),创建一个未连接的套接字。
说明:
先看源码。
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
后续解析。

示例:

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 套接字/客户端,初始是未连接的,因此手动连接。

打印结果:
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
为何显示Connection reset,后续解析。

2.6 protected (SocketImpl impl)

创建带有用户指定的 SocketImpl 的未连接 Socket。
说明:
先看源码。
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
关于checkPermission(),见第5.4项。

底层调用第4.2项。

2.7 String host, int port

创建一个流套接字并将其连接到指定主机上的指定端口号。
说明:
先看源码。
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
底层调用第4.1项。

2.8 String host, int port, boolean stream

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

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

创建一个套接字并将其连接到指定远程主机上的指定远程端口。
说明:
先看源码。
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
底层调用第4.1项。

3、方法摘要

3.1 void bind(SocketAddress bindpoint)

将套接字绑定到本地地址。
说明:
先看源码。
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
关于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)
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
进入bind()。(出自实现类 AbstractPlainSocketImpl)
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
后续解析。

3.2 void close()

关闭此套接字。

3.3 void connect(SocketAddress endpoint)

将此套接字连接到服务器。
说明:
先看源码。
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket

指定:
超时时间 timeout为 0。

底层调用下1项。

3.4 void connect(SocketAddress endpoint, int timeout)

将此套接字连接到具有指定超时值的服务器。
说明:
先看源码。
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
关于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)
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
进入connect()。(出自实现类 AbstractPlainSocketImpl)
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
后续解析。

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()

返回套接字的绑定状态。
说明:
先看源码。
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
关于oldImpl,见第5.1项。

3.24 boolean isClosed()

返回套接字的关闭状态。
说明:
先看源码。
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
关于closeLock,暂未知。

3.25 boolean isConnected()

返回套接字的连接状态。
说明:
先看源码。
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
关于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)

为应用程序设置客户端套接字实现工厂。
说明:
先看源码。
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
注译:
为应用程序设置客户端套接字实现工厂。工厂只能指定一次。

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

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

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


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

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

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

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

先看源码。
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
关于setImpl(),见第5.2项;关于createImpl(),见第5.3项;关于bind(),见第3.1项。

后续补充解析。

4.2 private Socket(Void ignore, SocketImpl impl)

根据指定的套接字实现类,构造套接字。
说明:
先看源码。
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
关于checkOldImpl(),见第5.1项。

5、方法摘要(不开放)

5.1 private void checkOldImpl()

记录当前套接字实现类的“新旧”状态。
说明:
先看源码。
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
impl的类型是SocketImpl,这是套接字实现类的超类,是一个抽象类。从概述中可知,SocketImpl 抽象类的实现类有两个:PlainSocketImpl 和 SocksSocketImpl,其子类是 AbstractPlainSocketImpl。

以常理而言impl的指向不会是 SocketImpl 或其子类的实例,因为抽象类不能进行实例化(new)。不过,抽象类是可以有实例的(通过反射newInstance()创建(当然,我们手动无法完成),也可以定义构造方法。
下图是执行newInstance()的结果。
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
不过,目前套接字功能的实现源码是分布在这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);

打印结果。
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
源码中通过反射获取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()

将当前客户端套接字实现类设置为系统默认客户端套接字实现类。
说明:
先看源码。
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
关于factory,见第3.43项;关于checkOldImpl(),,见上1项。

5.3 void createImpl(boolean stream)

创建套接字实现类对象。
说明:
先看源码。
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
关于setImpl(),见上1项。

进入create()。(出自实现类 PlainSocketImpl)
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket

5.4 private static Void checkPermission(SocketImpl impl)

检查是否有权设置套接字实现类。
说明:
先看源码。
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
关于SET_SOCKETIMPL_PERMISSION,见SecurityConstants类的第2.3.8项;关于checkPermission(),见SecurityManager类的第4.18项。

5.5 private void checkAddress (InetAddress addr, String op)

检查 IP 地址是否有效。
说明:
先看源码。
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
目前,IP 地址只有 IPV4 和 IPV6 这两个版本有效。

5.6 SocketImpl getImpl()

获取套接字实现类对象。
说明:
先看源码。
Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码),Java-API,Java-API简析,Socket
关于createImpl(),见第5.3项。

最后

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


本文暂缓更新。文章来源地址https://www.toymoban.com/news/detail-538830.html

到了这里,关于Java-API简析_java.net.Socket类(基于 Latest JDK)(浅析源码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java-API简析_java.io.FilterOutputStream类(基于 Latest JDK)(浅析源码)

    【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) https://blog.csdn.net/m0_69908381/article/details/134106510 出自【进步*于辰的博客】 继承关系: java.lang.Object java.lang.Enum TimeUnit java.util.concurrent.TimeUnit 所有已实现的接口: Serializable、Comparable TimeUnit public enum Tim

    2024年02月08日
    浏览(27)
  • ES8 向量搜索(knn-search)java-api 实践

    官方文档-knn-search kNN搜索 k-nearest neighbor (kNN)搜索找到与查询向量最近的k个向量,如通过相似度计算。 kNN的常见用例包括: 基于自然语言处理(NLP)算法的相关性排序 产品推荐和推荐引擎 图像或视频的相似性搜索 要运行kNN搜索,您必须能够将数据转换为有意义的向量值

    2024年02月12日
    浏览(34)
  • Hadoop系统应用之HDFS相关操作 - - Java-API对HDFS的操作(IDEA版)

    通过Java API来操作HDFS文件系统HDFS,相关的操作有:文件上传、文件下载、新建文件夹、查看文件、删除文件。 1.Linux下安装好hadoop,这里使用hadoop2.7.3 2.window解压hadoop2.7.3的包 2.Windows下安装好jdk1.8  3.Windows下安装好IDEA,这里使用IDEA2022 4.Windows下安装好maven,这里使用Maven3.6.3

    2024年02月05日
    浏览(40)
  • Linux 下 Java Socket 编程报 java.net.Exception:Permission denied (权限不足)

    本人用Linux部署springboot项目时遇见这个错误,原因很简单,就是端口号没有选对。 在linux系统中,端口号再1024以下的需要root权限,只要把端口改成大于1024的就可以了,但避开一些软件的默认端口,如Tomcat的8080端口,mysql的3306端口。 解决方法: 设置临时属性重新启动即可。

    2024年02月11日
    浏览(34)
  • rabbitmq模块启动报java.net.SocketException: socket closed的解决方法

    问题 最近在接手一个项目时,使用的是spring-cloud微服务构架,mq消息消费模块是单独一个模块,但启动这个模块一直报如下错误: 这个错误是这个模块注册不到nacos报的错,刚开始就是检查模块的nacos配置和rabbitmq的配置,都没发现问题!后面详情看错误时发现还有这么一个警

    2024年02月16日
    浏览(38)
  • JAVA JDK API 1.6.0中文版文档

    API:应用程序接口。Java API是程序员的字典,是JDK中提供给我们使用的类的说明文档。 下面分享的是JDK API 1.6.0中文版文档,供大家学习使用。 链接:https://pan.baidu.com/s/1jrUm1lRJQNr3Dpq6U58dNg  提取码:1007

    2024年01月20日
    浏览(36)
  • Java常用类之 JDK 8之前的日期时间API 和 8中新日期时间API

    Java常用类 JDK 8 之前日期和时间的API测试: 详细代码如下: SimpleDateFormate的使用: SimpleDateFormate对日期Date类的格式化和解析 重点关注: 具体代码如下: 1.获取Calendar实例的方法 2.重点掌握几个常用方法: 注意: 获取月份时:一月是0,二月是1,以此类推,12月是11 获取星期

    2024年02月07日
    浏览(39)
  • 在使用socket进行通信连接时出现java.net.SocketException: Connection reset的解决方案

    在创建客户端和服务端后,出现java.net.SocketException: Connection reset的错误。 导致的原因 客户端或者服务端其中一端还在传输数据的途中,另外一端的socket被关闭(或者主动0关闭,或者运行异常退出导致的关闭),这时,客户端的第一个数据包传输过来后就会引发该异常。 一端

    2024年02月09日
    浏览(38)
  • 解决JDK7调用https报:java.net.SocketException: Connection reset错误

            大多数现代的 HTTPS 连接将使用 TLS 1.2 协议 或 TLS 1.3 协议,具体取决于服务器和客户端支持的版本以及其之间的协商,而 JDK7及以下 版本默认使用是 TLS v1 协议,所以在调用HTTPS接口时,会出现java.net.SocketException: Connection reset报错;         下面是不同JDK版本的默

    2024年04月26日
    浏览(27)
  • 掌握Java JDK 1.8 API帮助文档中文版,事半功倍编程

    引言: Java是一门强大且广泛应用的编程语言,在开发过程中,准确地了解和使用Java标准库的API是非常重要的。JDK(Java Development Kit)是Java开发工具包,提供了丰富的API文档,其中包含了Java标准库中各种类和方法的详细说明。本文将为您介绍如何使用JDK 1.8 API帮助文档的中文

    2024年02月07日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包