JDK中socket源码解析

news2024/10/21 10:11:34

目录

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.c

JNIEXPORT 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.coloradmin.cn/o/2219957.html

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

相关文章

基于stm32的esp8266的WIFI控制风扇实验

实验案例&#xff37;&#xff29;&#xff26;&#xff29;控制风扇 项目需求 电脑通过esp8266模块远程遥控风扇。 项目框图 ​ 风扇模块封装 #include "sys.h" #include "fan.h"void fan_init(void) {GPIO_InitTypeDef gpio_initstruct;//打开时钟…

4K Mini-LED显示器平民价,一千多的联合创新27M3U到底有多香

哈喽小伙伴们好&#xff0c;我是Stark-C~ 要说前几年买显示器还是普通IPS的天下&#xff0c;那个时候虽说也有MiniLED或者OLED显示器&#xff0c;但是价格那也是真贵啊&#xff0c;毕竟那个时候MiniLED和OLED还没普及&#xff0c;只有一些高档电视或者显示器才会用到此技术。不…

OpenCV高级图形用户界面(18)手动设置轨迹条(Trackbar)的位置函数setTrackbarPos()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 该函数设置指定窗口中指定轨迹条的位置。 注意 [仅 Qt 后端] 如果轨迹条附加到控制面板&#xff0c;则 winname 可以为空。 函数原型 void cv…

三周精通FastAPI:4 使用请求从客户端(例如浏览器)向 API 发送数据

FastAPI官网手册&#xff1a;https://fastapi.tiangolo.com/zh/tutorial/query-params/ 上节内容&#xff1a;三周精通FastAPI&#xff1a;3 查询参数 请求 FastAPI 使用请求从客户端&#xff08;例如浏览器&#xff09;向 API 发送数据。 请求是客户端发送给 API 的数据。响…

国家信息安全水平考试(NISP一级)最新题库-第十六章

目录 另外免费为大家准备了刷题小程序和docx文档&#xff0c;有需要的可以私信获取 1 防火墙是一种较早使用、实用性很强的网络安全防御技术&#xff0c;以下关于防火墙说法错误的是&#xff08;&#xff09; A.防火墙阻挡对网络的非法访问和不安全数据的传递&#xff1b;B.防…

Leecode刷题之路第27天之移除元素

题目出处 27-移除元素-题目描述 题目描述 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。假设 nums 中不等于 val 的元素数量为 k&#xff0c;要通过此题&#x…

C++ | Leetcode C++题解之第491题非递减子序列

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<int> temp; vector<vector<int>> ans;void dfs(int cur, int last, vector<int>& nums) {if (cur nums.size()) {if (temp.size() > 2) {ans.push_back(temp);}return;}if…

【题解】—— LeetCode一周小结42

&#x1f31f;欢迎来到 我的博客 —— 探索技术的无限可能&#xff01; &#x1f31f;博客的简介&#xff08;文章目录&#xff09; 【题解】—— 每日一道题目栏 上接&#xff1a;【题解】—— LeetCode一周小结41 14.鸡蛋掉落 题目链接&#xff1a;887. 鸡蛋掉落 给你 k 枚…

c++迷宫游戏

1、问题描述 程序开始运行时显示一个迷宫地图&#xff0c;迷宫中央有一只老鼠&#xff0c;迷宫的右下方有一个粮仓。游戏的任务是使用键盘上的方向健操纵老鼠在规定的时间内走到粮仓处。 基本要求: 老鼠形象可以辨认,可用键盘操纵老鼠上下左右移动&#xff1b;迷宫的墙足够结…

博弈论学习笔记【施工中】

SG函数 首先定义就不用我讲了吧&#xff0c;还不会的自己看看 传送门 再进一步理解一下吧&#xff1a; 黑色数字是节点编号&#xff0c;红色是 S G SG SG 函数值 看下它的过程&#xff1a; 首先 5 5 5 和 6 6 6 没有后继节点&#xff0c;为必败态&#xff0c;先赋值为 …

OpenCV和HALCON

OpenCV和HALCON是两种广泛用于图像处理和计算机视觉的开发库&#xff0c;它们各有优缺点&#xff0c;适合不同的应用场景。以下是两者的比较&#xff1a; 1. 开发背景与定位 OpenCV (Open Source Computer Vision Library)&#xff1a; 开源库&#xff0c;最初由Intel开发&…

【图解版】力扣第146题:LRU缓存

力扣第146题&#xff1a;LRU缓存 一、LRU算法1. 基本概念2. LRU 和 LFU 的区别&#xff1a;3. 为什么 LRU 不需要记录使用频率&#xff1f; 二、Golang代码实现三、代码图解1. LRUCache、DLinkedNode两个结构体2. 初始化结构体对象3. addToHead函数4. removeNode函数5. moveToH…

基于单片机的多功能鱼缸控制系统设计

本设计以STC12C5A60S2单片机为核心的多功能鱼缸控制系统&#xff0c;该系统可分别利用温度传感器、水位传感器和浑浊度传感器来检测鱼缸内部的水温、液体高度和浑浊程度&#xff0c;并在显示屏上进行显示。若检测结果超出阈值范围&#xff0c;则继电器工作从而控制内部环境。通…

LeetCode102. 二叉树的层序遍历(2024秋季每日一题 43)

给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[3],[9,20],[15,7]] 示例 2&#xff1a; 输入…

白嫖正版xshell和XFTP

在哪里可以下载正版免费的xshell和XFTP&#xff0c;并且还能够获得官网免费持久更新 白嫖步骤 首先直接在浏览器搜索xshell官网 点进官网之后直接点击下载 接着点击免费授权页面 进入之后就可以免费下载了 下载安装完成后填写用户名和邮箱并提交&#xff0c;这里就以xshell为…

Veritas NetBackup 10.5 发布,新增功能概览

Veritas NetBackup 10.5 发布&#xff0c;新增功能概览 Veritas NetBackup 10.5 (Unix, Linux, Windows) - 领先的企业备份解决方案 The #1 enterprise backup and recovery solution. 请访问原文链接&#xff1a;https://sysin.org/blog/veritas-netbackup-10/ 查看最新版。…

EditPlus的安装软件包

解压并粘贴到C:\Program Files (x86)中 点击激活密匙,并一直同意 确认并选择默认的位置: 关闭并重新激活密匙 就好了 无需添加快捷方式: 只需要选择任意文件 并选择该应用打开一次即可 通过百度网盘分享的文件&#xff1a;EditPlus_5.0.611.zip 链接&#xff1a;https://pa…

在Debian 11/Debian 10上安装MySQL 5.7

本文借鉴 如何在 Debian 11/Debian 10 上安装 MySQL 5.7 |https://cn.linux-console.net/?p20728 下载安装存储库 安装 根据提示选择mysql5.7即可(会车键选择) wget https://dev.mysql.com/get/mysql-apt-config_0.8.16-1_all.debsudo dpkg -i mysql-apt-config_0.8.16-1_a…

MFC工控项目实例二十四模拟量校正值输入

承接专栏《MFC工控项目实例二十三模拟量输入设置界面》 对模拟量输入的零点校正值及满量程对应的电压值进行输入。 1、在SenSet.h文件中添加代码 #include "BtnST.h" #include "ShadeButtonST.h"/ // SenSet dialogclass SenSet : public CDialog { // Co…

AWD初步学习

一般的AWD不提供外网环境&#xff0c; AWD比赛中一般准备语言环境&#xff0c;工具、exploit及相关脚本框架。 1.脚本环境 一般在/var/www/html目录的下面&#xff0c;需要提前PHP和常用的Web开发语言环境在本地进行配置&#xff0c;且统一语言尽量多配置环境&#xff0c;比如P…