当前位置: 首页 > article >正文

Java网络编程API 1

Java中的网络编程API一共有两套:一套是UDP协议使用的API;另一套是TCP协议使用的API。这篇文章我们先来介绍UDP版本的API,并尝试来写一个回显服务器(接收到的请求是什么,返回的响应就是什么)。

UDP数据报套接字编程

API介绍

DatagramSocket

DatagramSocket是UDPSocket,用于发送和接收UDP数据报,就相当于网卡的作用。

DatagramSocket的构造方法:

DaragramSocket方法:DatagramPacket

DatagramPacket是UDPSocket发送和接收的数据报。

DatagramPacket构造方法:

 DatagramPacket方法:

 构造UDP发送数据报的时候,需要传入SocketAddress(要发送信息的地址)。

补充:

UDPSocket中API的使用,是Java把操作系统中的原生API进行了一层封装。

其中最核心的类就是上面两个,DatagramSocket和DatagramPacket。

DatagramSocket:操作系统中有一类文件,就叫做socket(文件普通文件和目录文件都是存放在硬盘上的,socket文件,就抽象地表示了“网卡”这样的硬件设备,进行网络通信最核心的硬件设备就是网卡(通过网卡发送数据,就相当于写socket文件;通过网卡接收数据,就相当于读socket文件)。

DatagramPacket:UDP面向的是数据报,每次发送、接收数据的基本单位,就是一个UDP数据报,而DatagramPacket就表示了一份UDP数据报。

下面我们通过写一个回显服务器,来认识这些API的使用,并且了解在实际开发中,服务器需要做哪些事情,客户端需要做哪些事情。

回显服务器

回显服务器:客户端发什么请求,服务器就返回什么响应。(并没有什么业务逻辑,实际开发中,我们要返回什么响应是要根据业务逻辑来的)。

服务器端

第一步,先创建DatagramSocket对象,并且通过构造方法为服务器指定一个端口号:

我们的服务器程序一启动,就需要绑定/关联上操作系统的一个端口号,端口号也是一个整数,用来区分一个主机上进行网络通信的程序(一个端口号只能绑定一个程序,反过来,一个程序能够绑定多个端口号,上面抛出的SocketException就是为了处理多个程序绑定了同一个端口号导致Socket创建不成功的情况),在创建服务器时需要我们手动指定一个端口号

下面我们来写start方法:

对于服务器来说,需要不停地接收请求,返回响应,接收请求,返回响应。(一个服务器单位时间能处理地请求,能返回地响应越多,服务器水平越高)。

服务器往往都是7*24小时运行,因此这里地while(true)并没有退出的必要,如果我们确实向重启服务器,直接“杀”进程即可。

当然,我们这里的while(true)是非常简单粗暴的写法。实际开发中的服务器,很可能要实现“优雅退出”的效果,即确保当前正在进行的请求做完了之后再进行退出。

在start方法中,我们要完成四步工作;

1、读取请求并解析。

2、根据请求计算响应(但我们这里只是简单构造回显服务器,这一步啥也不用干)。

3、把响应返回给客户端。 

4、打印日志。


1、读取请求并解析 

读取请求并解析的过程中,需要使用socket的receive方法:

使用receive需要传入一个输出型参数DatagramPacket对象,这个对象里有一个内置的字节数组(我们可以设置数组的最大长度),会保存我们收到的消息正文(应用层数据包,UDP数据报的载荷部分)。此外UDP报头、数据的源IP和源端口号等都会被这个DatagramPacket对象所保存。 注意:这里的receive方法是自带阻塞功能的,如果客户端没有发来请求就会阻塞等待。

我们可以将得到的字节数组,转换成String,方便后续根据请求计算响应:

基于字节数组构造出String,字节数组里面保存的内容不一定是二进制数据,也可能是文本数据,如果是文本数据,将其交给String ,也是没有问题的;如果是二进制数据,String也是可以保存的。

第一个参数中,通过requestPacket对象调用getData方法,将上面字节数组中的信息获取到,然后第二个参数表示从字节数组的0号位置,开始构造String,第三个参数,是通过requestPacket对象获取到字节数组的有效长度(如果通过最大长度构造,那么字符串后面的很大部分都会是空白)。通过这三个参数来构造我们的String对象。

补充:

receive是传输层的UDP协议提供的一个API,传输层会给每个socket对象在内核中分配一个缓冲区,每次网卡读到一个数据读到一个数据,都会层层分用,解析好之后,最后放到这个缓冲区中,应用程序调用receive本质上就是从缓冲区中拿走一个数据。

2、根据请求计算响应

因为是回显服务器,这一步我们就只需要简单return以下即可 

 3、把响应返回到客户端

要想把响应返回给客户端,这里需要使用send方法,这里面同样需要传一个DatagramPacket对象:

此时我们构造DatagramPacket时,参数又不一样了: 

第一个参数:response.getByte()是将我们刚才得到的响应(String类型)以字节数组的形式得到。

第二个参数:response.getBytes().length这里是获取字节数组的长度,以字节为单位进行计算,如果传入的参数是response.length(),此时的单位就是字符了。

第三个参数:requestPacket.getSocketAddress(),这里调用方法的的对象是requestPacket,这个对象是我们用来读取请求的,表示的是客户端来的数据报。调用getSocketAddress方法,调用这个方法,会获取到一个InetAddress对象,这个INetAddress对象,就包含了和服务器通信对应的客户端的ip和端口号(即响应的目的ip和目的端口号)。

通过上面的代码,我们也可以看出UDP是无连接的通信。UDP的DatagramSocket对象自身不保存对端的IP和端口。而是在每一份数据报中,都有一个对端的IP和端口。另外代码中也没有建立连接和接收连接的操作。

而UDP的不可靠传输,无法在代码中体现。但是UDP面向数据报的特点,可以看到,在send返回响应和receive读取请求时,都是以DatagramPacket为单位的。

4、打印日志

注意:这里的requestPacket.getAddress().toString会以点分十进制的方式输出客户端的IP地址 

再写一个main方法: 

注意:这里传入的端口号一般在1024~65535之间(但不可以和其他进程冲突),1~1024之前的端口号是系统保留自用的端口号——知名端口号(不可占用)。

完整代码: 

package UDPEcho;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;public class UDPEchoSever {//先创建一个DatagramSocket对象private DatagramSocket socket = null;//服务启动需要绑定端口号//一个主机的一个端口只能绑定一个进程,反过来一个进程可以绑定多个端口public  UDPEchoSever(int port) throws SocketException {socket = new DatagramSocket(port);}public void  start() throws IOException {System.out.println("服务器启动!");//需要不停接收请求并返回响应while(true){//1、读取请求并解析//通过这个字节数组保存收到的消息正文(应用层数据包),也就是UDP数据报的载荷部分DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);//此处的receive就从网卡读取到了一个UDP数据报,放到requestPacket中,载荷部分就被放到requestPacket内置的字节数组中了//此处的UDP报头、源ip和端口号也会被保存socket.receive(requestPacket);//把读到的字节数组转为字符串String request = new String(requestPacket.getData(),0, requestPacket.getLength());//2、根据请求构造响应String response = process(request);//3、把响应返回给客户端//构造体格DatagramPacket作为响应对象,里面不是空白的字节数组了,而是把String里面包含的字节数组拿进来了//注意:获取长度是字节数组,单位是字节。如果直接获取字符串长度,单位是字符不一样的//requestPacket.getSocketAddress()这个对象就包含了你和服务器对应的ip和端口号DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());socket.send(responsePacket);//打印日志System.out.printf("[%s:%d],res:%s,response:%s\n",requestPacket.getAddress().toString(),requestPacket.getPort(),request,response);}}public String process(String request) {return request;}public static void main(String[] args) throws IOException {UDPEchoSever udpEchoSever = new UDPEchoSever(9090);udpEchoSever.start();}
}

客户端

编写客户端的代码,也需要创建一个DatagramSocket对象。但是此处就不必指定端口号了。 

服务器编写代码的时候需要手动指定端口号,但是在客户端这边,一般不需要手动指定,系统会自动分配一个空闲的端口号。

代码中手动指定端口号,可以保证端口号始终都是固定的,如果不手动指定,依赖系统自动分配,导致服务器每次重启之后,端口号可能就变了,一旦变了,客户端就有可能找不到这个服务器在哪里了,所以我们的服务器代码,一般要手动指定端口号。

但是客户端,端口号让系统随机分配一个空闲的即可。(主要是我们无法确保手动指定的端口号是可用的(可能被其他进程占用了))。

服务器的端口就不会被占用吗?为什么只担心客户端呢?

服务器的机器是在程序员手里的,是可控的。程序员事先编写代码之前,是能知道服务器上有哪些端口是空闲的。但是,客户端时在普通用户的机器上的,用户千千万万,上面的环境也是千差万别,天知道某个用户会装什么奇奇怪怪的程序把端口号占用,此时,我们的程序无法正常启动,用户只会怪到程序员的头上。

上面的客户端构造方法还需要,我们需要指定服务器短的ip地址和端口号。因为时客户端主动给服务器发起请求,发起请求的前提就是需要知道服务器在哪里~~~(比如说我要去餐厅吃饭,我就需要知道餐厅的位置在哪里,才能去吃饭)

此处,客户端发起请求的目的ip(severIp)和目的端口(severPort)就是客户端的ip和端口,而请求的源ip就是客户端本机的ip,源端口就是客户端本机分配的端口。

接下来,和服务器一样我们的客户端也需要实现一个start方法,在start方法中,我们也要做四件事:

1、从控制台读取要发送的请求数据

2、构造请求并发送

3、读取服务器的响应

4、把响应显示在控制台上

因为我们是要从控制台读取要发送的数据,所以需要先创建一个Scanner对象,还需要一个循环不断读取数据并发送给服务器。 

1、从控制台读取要发送的请求数据

这里的if语句是可以让用户在结束输入的时候,程序可以正确地跳出循环,避免程序一直等待用户输入。

从控制台读取数据的时候,最好使用scanner读取字符串,最好使用next而不是nextLine(如果是文件的话就无所谓了) 

如果使用nextLine读取,就需要手动输入换行符——Enter来控制。而由于Enter键不仅会产生\n这样的换行,还会产生其他字符,就可能会导致读到的内容出现问题。

而next是以"空白符"作为分隔符,包括但不限于,换行,空格,制表符……

 2、构造请求并发送

同样的socket调用send方法,同样需要传入一个DatagramPacket类型的参数。​​​​​​​​​​​​​​​​​​​​​

此处的Datagram参数又有所不同,第三个参数是我们当前要通信服务器的ip(注意:这里并不能传入String类型的severIP,而是需要调用InetAddress.getByName(severIP)),第四个参数则是我们当前要通信服务器上的端口号。

3、读取服务器的响应

这里仍然是使用socket的receive方法同样参数需要传入一个DatagramPacket类型的对象,此时构造方法仍然搭配receive使用,所以构造方法只需要指空白的字节数组即可 

4、把响应显示在控制台上 

这里也需要基于DatagramPacket构造字符串,进行打印。 

主函数:

其中127.0.0.1表示的是本机的ip,9090就是我们刚才在服务器中指定的端口号

完整代码: 

package UDPEcho;import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;public class UDPEchoCline {private DatagramSocket socket = null;//客户端的IPprivate String severIp;//客户端端口号private int severPort;public UDPEchoCline(String severIp,int severPort) throws SocketException {this.severIp = severIp;this.severPort = severPort;socket = new DatagramSocket();}public void start() throws IOException {System.out.println("客户端启动");Scanner scanner = new Scanner(System.in);while(true) {System.out.print("->");//1、从控制台读取要发送请求的数据if(!scanner.hasNext()){break;}String request = scanner.next();//2、构造请求并发送DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length, InetAddress.getByName(severIp),severPort);socket.send(requestPacket);//3、读取服务器响应DatagramPacket responesePacket = new DatagramPacket(new byte[4096],4096);socket.receive(responesePacket);//4、把响应打印到控制台上String response = new String(responesePacket.getData(),0,responesePacket.getLength());System.out.println(response);}}public static void main(String[] args) throws IOException {UDPEchoCline udpEchoCline = new UDPEchoCline("127.0.0.1",9090);udpEchoCline.start();}
}

基于DatagramPacket的补充

第一种:构造时指定空白的字节数组和字节数组的长度即可(搭配receive进行使用):

第二种:构造的时候指定有内容的字节数组,并且通过requestPacket.getSocketAddress方法获取请求方的IP和端口号。(用于服务器向客户端返回响应)。

第三种:构造的时候指定有内容的字节数组,并且分别将数据的目的IP和端口号作为两个参数。(用于客户端向服务器发起请求

代码演示:

注意:这里一定要先启动服务器,再启动客户端

此时,我们再客户端控制台输入,就会返回同样的值

同时,我们的服务器也会打印日志 

梳理代码 

下面是一个大致的流程图:

图文并茂讲解:

1、服务器启动之后,进入receive阻塞,等待客户端的请求。 

2、客户端在启动之后,在hasnext进入阻塞,等待用户输入。 3、用户在控制台输入之后,客户端会拿到用户输入的字符串,构造出请求,发送请求,并且在receive处等待响应返回。

4、服务器收到响应,就从receive解除阻塞,继续往下执行。 

 这里服务器执行完上述逻辑后,就会进入到下次循环中的receive的阻塞等待中,等待下一个客户端的请求过来。

5、客户端receive中返回,得到了服务器返回的响应数据,并且将数据打印在控制台上。

客户端在打印完毕之后也会进行下一次循环,等待用户再次从控制台输入信息。

补充 :

我们可以把写好的服务器代码,放到一个云服务器上,从而实现跨主机通信。

相关文章:

Java网络编程API 1

Java中的网络编程API一共有两套:一套是UDP协议使用的API;另一套是TCP协议使用的API。这篇文章我们先来介绍UDP版本的API,并尝试来写一个回显服务器(接收到的请求是什么,返回的响应就是什么)。 UDP数据报套…...

Android协程学习

目录 Android上的Kotlin协程介绍基本概念与简单使用示例协程的高级用法 结构化并发线程调度器(Dispatchers)自定义调度器并发:同步 vs 异步 异步并发(async 并行执行)同步顺序执行协程取消与超时 取消机制超时控制异步数据流 Flow协程间通信 使用 Channel使用 StateFlow /…...

Angular报错:cann‘t bind to ngClass since it is‘t a known property of div

遇到的错误: Cant bind to ngClass since it isnt a known property of div这个错误是 Angular 中 最常见的模板编译错误之一,通常出现在你试图使用 ngClass 指令,但 Angular 没有识别它的情况下。 ✅ 错误的根本原因 Angular 不知道 ngCla…...

uniapp+vue3实现CK通信协议(基于jjc-tcpTools)

1. TCP 服务封装 (tcpService.js) export class TcpService {constructor() {this.connections uni.requireNativePlugin(jjc-tcpTools)this.clients new Map() // 存储客户端连接this.servers new Map() // 存储服务端实例}// 创建 TCP 服务端 (字符串模式)createStringSe…...

Python爬虫实战:研究urlparse库相关技术

1 引言 1.1 研究背景与意义 网络爬虫作为互联网数据采集的核心技术,在信息检索、舆情分析、数据挖掘等领域具有广泛应用。随着 Web 技术的发展,现代网站 URL 结构日益复杂,包含路径参数、查询参数、锚点等多种组件,且存在相对路径、URL 编码等问题,给爬虫开发带来了挑战…...

解锁FastAPI与MongoDB聚合管道的性能奥秘

title: 解锁FastAPI与MongoDB聚合管道的性能奥秘 date: 2025/05/20 20:24:47 updated: 2025/05/20 20:24:47 author: cmdragon excerpt: MongoDB聚合管道是一种分阶段处理数据的流水线,通过$match、$group等阶段对文档进行特定操作,具有内存优化和原生操…...

软件工程方法论:在确定性与不确定性的永恒之舞中寻找平衡

更多精彩请访问:通义灵码2.5——基于编程智能体开发Wiki多功能搜索引擎-CSDN博客 当我们谈论“软件工程”时,“工程”二字总暗示着某种如桥梁建造般的精确与可控。然而,软件的本质却根植于人类思维的复杂性与需求的流变之中。软件工程方法论的…...

Unity中的MonoSingleton<T>与Singleton<T>

1.MonoSingleton 代码部分 using UnityEngine;/// <summary> /// MonoBehaviour单例基类 /// 需要挂载到GameObject上使用 /// </summary> public class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T> {private static T _instance;…...

怎么通过 jvmti 去 hook java 层函数

使用 JVMTI 手动实现 Android Java 函数 Hook 要通过 JVMTI 手动实现 Android Java 函数 Hook&#xff0c;需要编写 Native 层代码并注入到目标进程中。以下是详细步骤和示例&#xff1a; 一、核心实现原理 JVMTI 提供两种主要 Hook 方式&#xff1a; Method Entry/Exit 事…...

兰亭妙微 | 医疗软件的界面设计能有多专业?

从医疗影像系统到手术机器人控制界面&#xff0c;从便携式病原体检测设备到多平台协同操作系统&#xff0c;兰亭妙微为众多医疗设备研发企业&#xff0c;打造了兼具专业性与可用性的交互界面方案。 我们不仅做设计&#xff0c;更深入理解医疗场景的实际需求&#xff1a; 对精…...

前端原生构建交互式进度步骤组件(Progress Steps)

在现代网页设计中&#xff0c;进度步骤&#xff08;Progress Steps&#xff09; 是一种常见的 UI 模式&#xff0c;常用于引导用户完成注册流程、多步表单、教程或任何需要分步骤操作的场景。本文将带你从零开始构建一个美观且功能完整的 “进度步骤”组件&#xff0c;并详细讲…...

如何给windos11 扩大C盘容量

动不动C盘就慢了&#xff0c;苹果逼着用户换手机&#xff0c;三天两头更新系统&#xff0c;微软也是毫不手软。c盘 从10个G就够用&#xff0c;到100G 也不够&#xff0c;看来通货膨胀是部分行业的。 在 Windows 11 中扩大 C 盘容量&#xff0c;主要取决于磁盘分区布局和可用空…...

【基于阿里云搭建数据仓库(离线)】Data Studio创建资源与函数

Data Studio支持在您的数据分析代码中引用自定义的资源和函数&#xff08;支持MaxCompute、EMR、CDH、Flink&#xff09;&#xff0c;您需要先创建或上传资源、函数至目标工作空间&#xff0c;上传后才可在该工作空间的任务中使用。您可参考本文了解如何使用DataWorks可视化方式…...

Linux_T(Sticky Bit)粘滞位详解

Linux 粘滞位&#xff08;Sticky Bit&#xff09;详解 一、什么是粘滞位&#xff08;Sticky Bit&#xff09; 粘滞位&#xff08;Sticky Bit&#xff09;是 Linux 和 Unix 系统中一种特殊的权限设置&#xff0c;主要应用于目录&#xff0c;其作用是在多人共享访问的目录中&am…...

web3-以太坊智能合约基础(理解智能合约Solidity)

以太坊智能合约基础&#xff08;理解智能合约/Solidity&#xff09; 无需编程经验&#xff0c;也可以帮助你了解Solidity独特的部分&#xff1b;如果本身就有相应的编程经验如java&#xff0c;python等那么学起来也会非常的轻松 一、Solidity和EVM字节码 实际上以太坊链上储存…...

高敏感应用如何保护自身不被逆向?iOS 安全加固策略与工具组合实战(含 Ipa Guard 等)

如果你正在开发一款涉及支付、隐私数据或企业内部使用的 App&#xff0c;那么你可能比多数开发者更早意识到一件事——App 一旦被破解&#xff0c;损失的不只是代码&#xff0c;还有信任与业务逻辑。 在我们为金融类工具、HR 系统 App、数据同步组件等高敏感项目提供支持的过程…...

【C++项目】负载均衡在线OJ系统-2

文章目录 oj_server模块编写oj_server框架的搭建-oj_server/oj_server.cpp 路由框架 oj_model模块编写题目信息设置v1.文件版本-common/util.hpp boost库spilt函数的使用-oj_server/oj_model_file.hpp 文件版本model编写v2.mysql数据库版本1.mysql创建授权用户、建库建表录入操…...

GC1809:高性能24bit/192kHz音频接收芯片解析

1. 芯片概述 GC1809 是数字音频接收芯片&#xff0c;支持IEC60958、S/PDIF、AES3等协议&#xff0c;集成8选1输入切换、低抖动时钟恢复和24bit DAC&#xff0c;适用于家庭影院、汽车音响等高保真场景。 核心特性 高精度&#xff1a;24bit分辨率&#xff0c;动态范围105dB&…...

2025年06月05日Github流行趋势

项目名称&#xff1a;onlook 项目地址url&#xff1a;https://github.com/onlook-dev/onlook项目语言&#xff1a;TypeScript历史star数&#xff1a;16165今日star数&#xff1a;1757项目维护者&#xff1a;Kitenite, drfarrell, spartan-vutrannguyen, apps/devin-ai-integrat…...

flask功能使用总结和完整示例

Flask 功能使用总结与完整示例 一、Flask 核心功能总结 Flask 是轻量级 Web 框架&#xff0c;核心功能包括&#xff1a; 路由系统&#xff1a;通过 app.route 装饰器定义 URL 与函数的映射。模板引擎&#xff1a;默认使用 Jinja2&#xff0c;支持动态渲染 HTML。请求处理&…...

AWS 亚马逊 S3存储桶直传 前端demo 复制即可使用

自己踩过坑不想别人也踩坑了 亚马逊S3存储桶直传前端demo复制即可使用 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0…...

DAY 15 复习日

浙大疏锦行 数据使用爬虫爬取weibo数据&#xff0c;下面是代码 import datetime import os import csv import timeimport numpy as np import random import re import urllib.parse import requests from fake_useragent import UserAgentdef init():if not os.path.exists…...

Vue Router 导航方法完全指南

&#x1f4d6; 前言 在 Vue 项目中&#xff0c;我们经常需要在不同页面之间跳转&#xff0c;或者更新当前页面的 URL 参数。Vue Router 提供了几种不同的导航方法&#xff0c;每种方法都有其特定的使用场景。本文将详细讲解这些方法的区别和最佳实践。 &#x1f3af; 核心概念…...

MidJourney入门学习

1. 引言 MidJourney 是一款由美国科技公司开发的先进文本到图像生成 AI 工具,自 2022 年推出以来迅速在创意产业和社交媒体领域引发轰动。与 Stable Diffusion 不同,MidJourney 以其独特的美学风格、高度细节化的图像生成能力和强大的创意引导功能著称,成为设计师、艺术家和…...

2025最新Java日志框架深度解析:Log4j 2 vs Logback性能实测+企业级实战案例

一、为什么printStackTrace是"代码坟场"&#xff1f; 你写的日志可能正在拖垮系统&#xff01; 在Java开发中&#xff0c;直接调用printStackTrace()打印异常堆栈是最常见的"自杀式操作"。这种方式会导致三大致命问题&#xff1a; 无法分级控制&#xff…...

如何安全高效的文件管理?文件管理方法

文件的管理早已不只是办公场景中的需求。日常生活、在线学习以及个人收藏中&#xff0c;文件管理正逐渐成为我们数字生活中的基础。但与此同时&#xff0c;文件管理的混乱、低效以及安全性问题也频繁困扰着许多人。 文件管理的挑战与解决思路 挑战一&#xff1a;文件存储无序…...

基于BI PaaS架构的衡石HENGSHI SENSE平台技术解析:重塑企业级数据分析基座

在数据驱动决策的时代&#xff0c;传统BI工具日益显露出扩展性弱、灵活性差、资源利用率低等痛点。衡石科技推出的HENGSHI SENSE平台&#xff0c;创新性地采用BI PaaS&#xff08;平台即服务&#xff09;架构&#xff0c;为企业构建了一个强大、开放、可扩展的数据分析基础设施…...

Hive中ORC存储格式的优化方法

优化Hive中的ORC(Optimized Row Columnar)存储格式可显著提升查询性能、降低存储成本。以下是详细的优化方法,涵盖参数配置、数据组织、写入优化及监控调优等维度: 一、ORC核心参数优化 1. 存储与压缩参数 SET orc.block.size=268435456; -- 块大小(默认256MB)…...

代码训练LeetCode(23)随机访问元素

代码训练(23)LeetCode之随机访问元素 Author: Once Day Date: 2025年6月5日 漫漫长路&#xff0c;才刚刚开始… 全系列文章可参考专栏: 十年代码训练_Once-Day的博客-CSDN博客 参考文章: 380. O(1) 时间插入、删除和获取随机元素 - 力扣&#xff08;LeetCode&#xff09;力…...

【R语言编程绘图-plotly】

安装与加载 在R中使用plotly库前需要安装并加载。安装可以通过CRAN进行&#xff0c;使用install.packages()函数。加载库使用library()函数。 install.packages("plotly") library(plotly)测试库文件安装情况 # 安装并加载必要的包 if (!requireNamespace("p…...