JDK中socket源码解析

目录

1、Java.net包

1. Socket通信相关类

2. URL和URI处理类

3. 网络地址和主机名解析类

4. 代理和认证相关类

5. 网络缓存和Cookie管理类

6. 其他网络相关工具类

2、什么是socket?

3、JDK中socket核心Api

4、核心源码

1、核心方法

2、本地方法

3、linux是怎么处理链接的?

5、demo验证

建立连接

发送数据

断开连接


1、Java.net包

java.net包提供了一组用于网络编程的类,支持开发网络应用程序和处理网络通信。java.net包可以大致分为六个部分:

1. Socket通信相关类

这些类用于实现基于 TCP 和 UDP 协议的网络通信。

  • Socket:实现客户端的 TCP 套接字,允许程序与远程主机建立连接并传输数据。
  • ServerSocket:实现服务器的 TCP 套接字,用于监听客户端连接并接受连接请求。
  • DatagramSocket:用于 UDP 套接字通信,通过不可靠的无连接数据报协议传输数据。
  • MulticastSocket:扩展了 DatagramSocket,用于支持多播组通信,允许数据发送到多个接收者。
  • SocketAddress:表示一个套接字地址,通常用于绑定到特定 IP 地址和端口。
  • InetSocketAddress:表示 IP 地址和端口的组合,继承自SocketAddress

2. URL和URI处理类

这些类用于处理统一资源定位符(URL)和统一资源标识符(URI)。

  • URL:表示一个 URL(统一资源定位符),用于定位网络资源。支持从指定的 URL 中读取和解析数据。
  • URI:表示一个 URI(统一资源标识符),提供对 URI 的结构化处理。
  • URLStreamHandler:处理 URL 的协议细节,通常用于为不同协议(如 http、ftp)定义特定的处理逻辑。
  • URLConnection:表示到 URL 所引用的资源的通信链接,可以处理资源的获取和发送。
    • HttpURLConnection:继承自URLConnection ,用于处理 HTTP 协议的 URL 连接,支持 HTTP 请求和响应。
    • JarURLConnection:继承自URLConnection ,用于从 JAR 文件中获取资源。

3. 网络地址和主机名解析类

这些类用于表示 IP 地址和解析主机名。

  • InetAddress:表示 IP 地址,可以通过主机名或 IP 地址来查找和操作网络主机。
    • Inet4Address:表示 IPv4 地址。
    • Inet6Address:表示 IPv6 地址。
  • NetworkInterface:表示网络接口(如以太网接口),用于获取和操作本地计算机的网络接口。

4. 代理和认证相关类

这些类用于处理网络代理和认证功能。

  • Proxy:表示网络通信中的代理设置,支持 HTTP 和 SOCKS 等代理类型。
  • ProxySelector:用于选择代理服务器的策略,可以根据 URI 的类型来选择合适的代理。
  • Authenticator:用于实现网络请求中的认证机制,允许为 HTTP 和 FTP 请求提供用户名和密码等凭据。
  • PasswordAuthentication:表示用户名和密码对,用于 Authenticator  类中的认证操作。

5. 网络缓存和Cookie管理类

这些类用于处理缓存和 Cookie 的存储和管理。

  • CookieHandler:用于管理 HTTP 协议中的 Cookie。
    • CookieManager:具体的 Cookie 管理器实现,允许存储和检索 Cookie。
  • CacheRequest:表示缓存系统中的请求,允许将资源写入缓存。
  • CacheResponse:表示缓存系统中的响应,允许从缓存中读取资源。

6. 其他网络相关工具类

一些提供额外网络功能的工具类。

  • DatagramPacket:用于表示数据报,包含通过  DatagramSocket 发送或接收的数据。
  • IDN:提供对国际化域名的处理,允许将 Unicode 域名转换为符合 Punycode 编码的 ASCII 字符串。
  • NetworkInterface:表示计算机上的网络接口,允许列举和操作网络接口。
  • StandardProtocolFamily:定义标准协议族(如 INET 和INET6),用于套接字通信。

2、什么是socket?

一个IP地址和一个端口号称为一个套接字(socket)。此术语出现在最早的TCP规范(RFC793, Page 5)中。我们知道进程通信的方法有管道、命名管道、信号、消息队列、共享内存、信号量,这些方法都要求通信的两个进程位于同一个主机。但是如果通信双方不在同一个主机又该如何进行通信呢?在计算机网络中有一个tcp/ip协议族,使用tcp/ip协议族就能达到我们想要的效果,如下图所示:

socket就是提供了tcp/ip协议的抽象,对外提供了一套接口,同过这个接口就可以统一、方便的使用tcp/ip协议的功能了,使得我们可以在在不同的计算机或网络设备之间建立连接,允许数据的发送和接收。

3、JDK中socket核心Api

方法签名作用
构造函数public ServerSocket()创建未绑定的服务器套接字。稍后需要手动调用 bind() 来绑定到特定的端口。
public ServerSocket(int port)创建绑定到指定端口的服务器套接字,默认使用所有可用的网络接口(IP 地址)。
public ServerSocket(int port, int backlog)创建绑定到指定端口的服务器套接字,并指定连接请求队列的最大长度 backlog
public ServerSocket(int port, int backlog, InetAddress bindAddr)创建绑定到指定 IP 地址和端口的服务器套接字,指定 backlog 来定义连接请求的队列长度。
 public Socket(String host, int port)(客户端)创建一个客户端 Socket 并连接到指定的远程主机 host 和端口 port。
绑定端口方法public void bind(SocketAddress endpoint)(客户端、服务端)将服务器套接字绑定到指定的 SocketAddress,通常是 InetSocketAddress,以监听特定端口。
public void bind(SocketAddress endpoint, int backlog)绑定到指定的 SocketAddress 并设置连接请求队列的最大长度 backlog,队列满时拒绝新连接请求。
监听客户端protected void listen(int backlog) 将服务器套接字置于监听状态,backlog 参数指定允许等待连接的最大队列长度,超过该长度的连接请求将被拒绝。
接受客户端请求public Socket accept() throws IOException等待客户端的连接请求,接受成功后返回一个与客户端通信的新 Socket 对象。
尝试连接服务端socketpublic void connect(SocketAddress endpoint, int timeout)(客户端)尝试连接到远程服务器指定的 SocketAddress 地址,timeout 参数指定连接超时时间(毫秒)。如果在超时时间内未能连接,则抛出异常。
数据输入输出InputStream getInputStream()获取 Socket 的字节输入流
OutputStream getOutputStream()获取 Socket 的字节输出流
InetAddress getInetAddress()获取对端的 IP 地址
InetAddress getPort()获取对端的端口号

4、核心源码

1、核心方法

客户端和服务端最重要的两个类是Socket和Server Socket,这俩一个代表tcp通信的客户端,一个代表服务端。从源码来看,ServerSocket和socket内部分别持有一个SocketImpl对象,用于将对应的方法代理给native方法。以服务端ServerSocket为例:
构造函数中通过调用setImpl()创建了一个SocketImpl实现类,所有的创建链接、读写数据系统交互的本地方法都是在实现类中调用,也就是将对应的方法代理给native方法。

    public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException {setImpl();if (port < 0 || port > 0xFFFF)throw new IllegalArgumentException("Port value out of range: " + port);if (backlog < 1)backlog = 50;try {bind(new InetSocketAddress(bindAddr, port), backlog);} catch(SecurityException e) {close();throw e;} catch(IOException e) {close();throw e;}}

setImpl();方法在JDK17与JDK8中的实现方式不一样。JDK13之前使用的是PlainSocketImpl这个实现类(阻塞式 I/O),JDK 13后,Socket 的连接过程通过 NioSocketImpl 实现,采用了 NIO(非阻塞 I/O)技术来实现高效的网络连接和数据传输。具体可以参考:JDK13新特性

  private void setImpl() {SocketImplFactory factory = ServerSocket.factory;if (factory != null) {impl = factory.createSocketImpl();} else {impl = SocketImpl.createPlatformSocketImpl(true);}}static <S extends SocketImpl & PlatformSocketImpl> S createPlatformSocketImpl(boolean server) {  (默认传参false)if (USE_PLAINSOCKETIMPL) {return (S) new PlainSocketImpl(server);} else {return (S) new NioSocketImpl(server);}}

创建对应的socket实现类以后,调用bind方法绑定端口与开启监听,真正的本地方法的调用都在:getImpl().bind(epoint.getAddress(), epoint.getPort())、getImpl().listen(backlog)中实现:

 public void bind(SocketAddress endpoint, int backlog) throws IOException {if (backlog < 1)backlog = 50;try {@SuppressWarnings("removal")SecurityManager security = System.getSecurityManager();if (security != null)security.checkListen(epoint.getPort());getImpl().bind(epoint.getAddress(), epoint.getPort());getImpl().listen(backlog);bound = true;} catch(SecurityException e) {bound = false;throw e;} catch(IOException e) {bound = false;throw e;}}

  static void bind(ProtocolFamily family, FileDescriptor fd,InetAddress addr, int port) throws IOException{boolean preferIPv6 = isIPv6Available() &&(family != StandardProtocolFamily.INET);if (addr.isLinkLocalAddress()) {addr = IPAddressUtil.toScopedAddress(addr);}bind0(fd, preferIPv6, exclusiveBind, addr, port);}

2、本地方法

JDK8源码中的路径:C:\jdk-8\src\solaris\native\java\net\PlainSocketImpl.cJNIEXPORT void JNICALL
Java_java_net_PlainSocketImpl_socketBind(JNIEnv *env, jobject this,jobject iaObj, jint localport) {}

3、linux是怎么处理链接的?

前面提到的核心Api中,有关建立socket连接的Api中都有一个参数叫做backlog,源码中这个参数如果不设置,系统默认值为50,那么这个参数到底是什么?

 public ServerSocket(int port) throws IOException {this(port, 50, null);}public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException {setImpl();}

关于这个参数,只需要看懂下面这张图就明白了:

Linux内核协议栈为TCP连接管理使用两个队列,一个是SYN队列(半链接队列,用来保存处于SYN_SENT和SYN_RECV状态的请求),一个是accpetd队列(用来保存处于established状态,但是应用层没有调用accept取走的请求),这两个队列是内核实现的,当服务器绑定、监听了某个端口后,这个端口的SYN队列和ACCEPT队列就建立好了。如图,整个过程就是:

1. 当SYN包到达了服务器后,内核会把这一信息放到SYN队列(即未完成握手队列)中,同时回一个SYN+ACK包给客户端。
2. 一段时间后,客户端再次发来了针对服务器SYN包的ACK网络分组时,内核会把连接从SYN队列中取出,再把这个连接放到ACCEPT队列(即已完成握手队列)中。
3. 服务器在第3步调用accept时,其实就是直接从ACCEPT队列中取出已经建立成功的连接套接字而已。

那么问题来了,当这两个队列满了后,新的请求到达了又将发生什么?

  • 对于SYN队列,若队列满,则会直接丢弃请求,即新的SYN网络分组会被丢弃,这个问题好解决,客户端接收不到回复,会再一次发送,然后服务端继续丢弃,知道队列有空闲的位置。而客户端如果一直接收不到回复,发几次之后就会停止。
  • 对于ACCEPT队列的处理就有点复杂了,分两种情况:
    1. 如果server端设置了sysctl_tcp_abort_on_overflow,那么server会发送rst给client,并删除掉这个链接。默认情况下是不会设置的。
    2. 如果没有设置sysctl_tcp_abort_on_overflow ,server端只是标记连接请求块的acked标志,并且连接建立定时器,会遍历半连接表,重新发送synack,重复上面的过程,如果重传次数超过synack重传的阀值,会把该连接从半连接链表中直接删除。

5、demo验证

建立连接

  • 1、服务端创建服务端套接字,并开启客户端监听与等待连接请求
  • 2、客户端客户端套接字,尝试连接服务端
  • 3、客户端调用private static native int connect0(boolean preferIPv6,
                                           FileDescriptor fd,
                                           InetAddress remote,
                                           int remotePort)
  • 3次握手建立连接

发送数据

  • 1、客户端调用OutputStreamWriter发送数据
  • 2、服务端调用InputStreamReader接收数据

断开连接

  • 手动使客户端关闭连接
  • 4次挥手断开连接

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/57018.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【python_修改PPT中字体,run.font.name只对英文生效怎么办?】

python_修改PPT中字体&#xff0c;run.font.name只对英文生效怎么办&#xff1f; 参考&#xff1a;使用pptx_ea_font库设置中文字体 from pptx import Presentation from pptx.util import Pt from pptx_ea_font import set_font# 打开现有的 PPT 文件 prs Presentation(D:\…

JDK 1.6主要特性

JDK 1.6&#xff0c;也被称为Java 6或Java Platform, Standard Edition 6&#xff0c;是Java编程语言的第六个主要版本&#xff0c;由Sun Microsystems公司在2006年发布。JDK 1.6在JDK 1.5的基础上继续进行了改进和增强&#xff0c;进一步提高了Java的性能和易用性。 主要特性…

SQL Server 2019数据库“正常,已自动关闭”

现象&#xff1a; SQL Server 2019中&#xff0c;某个数据库在SQL Server Management Studio&#xff08;SSMS&#xff09;中的状态显示为“正常&#xff0c;已自动关闭”。 解释&#xff1a; 如此显示&#xff0c;是由于该数据库的AUTO_ CLOSE选项被设为True。 在微软的官…

基于 Konva 实现Web PPT 编辑器(三)

完善公式 上一节我们简单讲述了公式的使用&#xff0c;并没有给出完整的样例&#xff0c;下面还是完善下相关步骤&#xff0c;我们是默认支持公式的编辑功能的哈&#xff0c;因此&#xff0c;我们只需要提供必要的符号即可&#xff1a; 符号所表达的含义是 mathlive 的command命…

电力系统IEC-101报文主要常用详解

文章目录 1️⃣ IEC-1011.1 前言1.2 101规约简述1.3 固定帧格式1.4 可变帧格式1.5 ASDU1.5.1 常见类型标识1.5.2 常见结构限定词1.5.3 常见传送原因1.5.4 信息体地址 1.6 常用功能报文1.6.1 初始化链路报文1.6.2 总召报文1.6.3 复位进程1.8.4 对时1.8.4.1时钟读取1.8.4.2时钟写…

适用于 vue react Es6 jQuery 等等的组织架构图(组织结构图)

我这里找的是 OrgChart 插件; 地址: GitHub - dabeng/OrgChart: Its a simple and direct organization chart plugin. Anytime you want a tree-like chart, you can turn to OrgChart. 这里面能满足你对组织架构图的一切需求! ! ! 例: 按需加载 / 拖拽 / 编辑 / 自定义 / …

基于STM32F407VGT6芯片----跑马灯实验

一、在STM32F407VGT6芯片中配置GPIO环境 对于一个跑马灯实验&#xff0c;首先&#xff0c;要了解的就是&#xff0c;芯片是如何构造出来的&#xff0c;设计GPIO引脚&#xff1a;根据原理图&#xff0c; PC4&#xff0c;PC5,PC6,PC7 为 LED 输出控制管脚&#xff0c;PE0 为蜂鸣…

机器学习面试笔试知识点-线性回归、逻辑回归(Logistics Regression)和支持向量机(SVM)

机器学习面试笔试知识点-线性回归、逻辑回归Logistics Regression和支持向量机SVM 一、线性回归1.线性回归的假设函数2.线性回归的损失函数(Loss Function)两者区别3.简述岭回归与Lasso回归以及使用场景4.什么场景下用L1、L2正则化5.什么是ElasticNet回归6.ElasticNet回归的使…

navicat 3730错误

Navicat 3730 错误通常是由于在执行 SQL 语句时出现了语法错误或者其他问题导致的。具体来说&#xff0c;这个错误通常出现在 Navicat 尝试解析 SQL 语句时发现无法识别的语法或结构错误。 解决步骤 检查 SQL 语句的语法&#xff1a; 确保 SQL 语句语法正确无误。逐条执行 SQL…

嵌套div导致子区域margin失效问题解决

嵌套div导致子区域margin失效问题解决 现象原因解决方法 现象 <div class"prev"></div> <div class"parent"><div class"child"></div><div class"child"></div> </div> <div cl…

浅谈C++的future

std::future 是 C 标准库中的一个模板类&#xff0c;提供了一种机制来管理和获取异步任务的结果。它常与异步操作相关&#xff0c;允许你在不同线程中执行任务&#xff0c;并在将来&#xff08;即“未来”&#xff09;某个时刻获取这些任务的结果。std::future 通常和 std::asy…

如何处理mysql主从延迟

处理 MySQL 主从延迟的问题&#xff0c;可以考虑以下几个方面&#xff1a; 监控延迟&#xff1a; 使用 SHOW SLAVE STATUS 命令查看从库的状态&#xff0c;重点关注 Seconds_Behind_Master 字段&#xff0c;这个值表示从库落后主库的秒数。 优化 SQL 查询&#xff1a; 检查并优…

cisco网络安全技术第3章测试及考试

测试 使用本地数据库保护设备访问&#xff08;通过使用 AAA 中央服务器来解决&#xff09;有什么缺点&#xff1f; 试题 1选择一项&#xff1a; 必须在每个设备上本地配置用户帐户&#xff0c;是一种不可扩展的身份验证解决方案。 请参见图示。AAA 状态消息的哪一部分可帮助…

c++应用网络编程之十二Linux下的epoll模式分析

一、epoll的原理 在上一篇文章基本明白了epoll的入门知识&#xff0c;本篇开始分析一下其内在的原理&#xff0c;让大家对epoll的运行机制有一个真正的了解。其实分析epoll的原理就必须先说明一下epoll在整个网络通信过程中的位置或者说环节&#xff0c;这样才能从整体上对其有…

低代码可视化-uniapp海报可视化设计-代码生成

在uni-app中&#xff0c;海报生成器通常是通过集成特定的插件或组件来实现的&#xff0c;这些插件或组件提供了生成海报所需的功能和灵活性。我们采用了lime-painter海报组件。lime-painter是一款canvas海报组件&#xff0c;可以更轻松地生成海报。它支持通过JSON及Template的方…

企业网站设计之网站版式设计

一个成功的企业网站不仅仅需要强大的技术支持&#xff0c;更需要合理而吸引人的版式设计。版式设计在网站建设中扮演着关键角色&#xff0c;直接影响用户体验和品牌形象。本文将探讨主题企业网站版式设计的关键要素。 一、清晰的信息结构&#xff1a; 一个主题企业网站应该具有…

【rCore OS 开源操作系统】Rust 类型转换

读了一些参考资料&#xff0c;可以知道有这么些个转化方式&#xff1a; 知识点 在 Rust 中&#xff0c;提供了多种方式进行类型转换。以下是总结的主要类型转换方式&#xff1a; 1. 类型强转 (as) 这是最简单的类型转换方式&#xff0c;使用 as 关键字来进行显式的类型转换…

Linux-网络命令

Ping 命令 $ ping www.qq.com$ ping -c 5 www.qq.com netstat netstat 是一个用来查看网络状态的重要工具。 语法&#xff1a;netstat【选项】 功能&#xff1a;查看网络状态 常用选项&#xff1a; n 拒绝显示别名&#xff0c;能显示数字的全部转化成数字l 仅列出有在 Li…

uniapp做的app实现首页左滑退出应用

正常来说使用onBackPress方法就可以了 onBackPress() {uni.showModal({title: 提示,content: 您确定要退出应用吗&#xff1f;,success: function (res) {if (res.confirm) {plus.runtime.quit();} }}); },但是因为uniapp里面引用了uni-simple-router插件&#xff0c;导致首页…

STM32学习笔记---独立看门狗

目录 一、什么是独立看门狗 1、什么是看门狗 2、看门狗的原理 3、看门狗的作用 4、看门狗的分类 二、如何配置独立看门狗 1、独立看门狗框图 2、独立看门狗的相关寄存器 2.1 关键字寄存器 2.2 分频寄存器 2.3 重载值寄存器 2.4 状态寄存器 3、程序设计 4、独立看门…