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

简单实现基于UDP与TCP的回显服务器

目录

  • 前言
  • UDP 版的回显服务器
    • 需要用到的 api
    • 服务端
    • 客户端
      • UDP 版本的字典客户端和字典服务器
  • TCP 版的回显服务器
    • 需要用到的 api
    • 服务器
    • 客户端
      • 对服务器进行改进(使用线程池)
      • TCP 版本的字典客户端和字典服务器

前言

我们写网络程序, 主要编写的是应用层代码.
真正要发送这个数据, 还需要上层协议调用下层协议, 也就是应用层调用传输层.
传输层给应用层提供了一组 api, 统称为 socket api, 系统给程序提供的 api 是C 风格的, JDK 针对这些 api 进行封装, 封装成 Java 风格的 api.

提供的 socket api 主要是这两组 :

  1. 基于 UDP 的 api
  2. 基于 TCP 的 api

因为 UDP 与 TCP 的协议差别很大, 所以这两组 api 差别也很大.

那这两个协议都有啥特点呢?

UDP :

  1. 无连接 (使用 UDP 的双方不需要刻意保存对端的相关信息)
  2. 不可靠传输 (消息发送完了就行, 不关注结果)
  3. 面向数据报 (以一个 UDP 数据报为基本单位)
  4. 全双工 (一条路径, 双向通信)

TCP :

  1. 有连接 (使用 TCP 的双方要刻意保存对端的相关信息)
  2. 可靠传输 (发送消息后, 知道对方是否接收到)
  3. 面向字节流 (以字节为传输的基本单位, 读写方式非常灵活)
  4. 全双工 (一条路径, 双向通信)

UDP 版的回显服务器

需要用到的 api

  1. DatagramSocket API

DatagramSocket 是 UDP Socket,用于发送和接收UDP数据报。
主要用到的构造方法 :

DatagramSocket()

创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口(一般用于客户端)

DatagramSocket(intport)

创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(一般用于服务端)

主要用到的方法 :

void receive(DatagramPacket p)

从此套接字接收数据报(如果没有接收到数据报,该方法会阻塞等待)

void send(DatagramPacketp)

从此套接字发送数据报包(不会阻塞等待,直接发送)

void close()

关闭此数据报套接字

  1. DatagramPacket API

DatagramPacket是UDP Socket发送和接收的数据报.
主要用到的构造方法 :

DatagramPacket(byte[] buf, int length)

构造一个DatagramPacket以用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度(第二个参数 length)

DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)

构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组(第一个参数buf)中,从offset到指定长度(第三个参数length)。address指定目的主机的IP和端口号

主要用到的方法 :

InetAddress getAddress()

从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址.

int getPort()

从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获
取接收端主机端口号

byte[] getData()

获取数据报中的数据

构造UDP发送数据报时,需要传入 SocketAddress ,该对象可以使用InetSocketAddress 来创建.

服务端

public class UdpEchoServer {//首先定义一个 socket 对象, 通过 socket 对象来发送读取信息private DatagramSocket socket = null;//绑定一个端口, 如果绑定的端口被别的进程占用了, 这里就会报错.//同一个主机上, 一个端口只能被一个进程绑定.public UdpEchoServer(int port) throws SocketException {//构造时, 指定要绑定的端口号.socket = new DatagramSocket(port);}//启动服务器的主逻辑public void start() throws IOException {System.out.println("服务器启动!!!");//因为服务器是要时刻读取客户端信息的, 所以使用while循环来重复读取并处理信息.while(true) {//每次循环都只做三件事// 1.读取请求并解析//   下面这是构造一个数据包, 就可以理解为一个餐盘, 这个餐盘里指定要放入的数据类型及大小//   空的餐盘构建好了就得装东西了DatagramPacket requestPacket = new DatagramPacket(new byte[666], 666); //   1kb=1024byte, UDP最多发送64kb(包含UDP首部8byte)//   通过 socket 对象来接收信息(也就是填充餐盘)socket.receive(requestPacket); //如果没接收到信息就会阻塞等待//   为了方便我们处理这个请求, 将数据包转为 StringString request = new String(requestPacket.getData(),0, requestPacket.getLength());// 2.根据请求来计算响应(这里直接返回了)String response = process(request);// 3.把响应结果写回客户端//   根据 response 来构造一个 DatagramPacket//   和之前构造不同, 本次构造相当于将餐盘填满, 注意还要指定发送给谁//   requestPacket.getSocketAddress()是获取客户端的IP和端口号(填充的 requestPacket 就包含了客户端的IP和地址)DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),0,response.getBytes().length,requestPacket.getSocketAddress());//   把结果发送给客户端socket.send(responsePacket);//打印一下客户端的IP和端口号, 还有请求和响应System.out.printf("[%s : %d] req: %s, resp: %s\n", requestPacket.getAddress().toString(),requestPacket.getPort(), request, response);}}//根据请求计算响应public String process(String request) {return request;}public static void main(String[] args) throws IOException {UdpEchoServer echoServer = new UdpEchoServer(8888);echoServer.start();}
}

客户端

public class UdpEchoClient {//客户端也需要 socket 对象来进行数据交互private DatagramSocket socket = null;private String serverIP;private int serverPort;//构造客户端时, 还需要知道服务器在哪, 才能去获取服务public UdpEchoClient(String serverIP, int serverPort) throws SocketException {//不同与服务器, 这里没有关联端口, 不代表不需要关联端口//而是系统会自动为客户端分配个空闲的端口socket = new DatagramSocket();this.serverIP = serverIP;this.serverPort = serverPort;}public void start() throws IOException {System.out.println("启动客户端!!!");//通过 Scanner 来读取用户输入Scanner scanner = new Scanner(System.in);while(true) {// 1.先从控制台读取一个字符串//   打印一个提示符, 提示用户输入System.out.print("-> ");String request = scanner.next();// 2.把字符串构造成 UDP packet, 并进行发送//   InetAddress.getByName() 确定主机的IP地址DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,InetAddress.getByName(serverIP), serverPort);socket.send(requestPacket);// 3.客户端尝试读取服务器返回的响应DatagramPacket responsePacket = new DatagramPacket(new byte[666],666);socket.receive(responsePacket);String response = new String(responsePacket.getData(),0,responsePacket.getLength());System.out.printf("req: %s, resp: %s\n", request,response);}}public static void main(String[] args) throws IOException {UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1",8888);udpEchoClient.start();}
}

启动看看效果 :
客户端 :
在这里插入图片描述
服务端 :
在这里插入图片描述

UDP 版本的字典客户端和字典服务器

要想实现单词查找功能, 首先要有一个数据结构来对单词进行存储, 一般用哈希表来存储, key 来存储单词, value 存储单词意思.

其实客户端都是一样的, 只是服务器根据请求, 返回的响应不一样了.
所以我们主要就是重写 process 方法.

服务端 :

public class UdpDictServer extends UdpEchoServer{private HashMap<String,String> dict = new HashMap<>();public UdpDictServer(int port) throws SocketException {super(port);dict.put("pen","笔");dict.put("dog","狗");dict.put("cat","猫");//...可以无限添加, 其实网上的翻译词典什么的,就是样本比这个大}@Overridepublic String process(String request) {return dict.getOrDefault(request,"未找到该单词.");}public static void main(String[] args) throws IOException {UdpDictServer udpDictServer = new UdpDictServer(8888);udpDictServer.start();}
}

客户端 :
在这里插入图片描述
服务端 :
在这里插入图片描述

TCP 版的回显服务器

需要用到的 api

  1. ServerSocket API

ServerSocket 是创建TCP服务端 Socket 的 API (给服务器用的)

ServerSocket 一定要绑定具体端口号. (服务器得绑定的端口号才能提供服务)

主要用到的构造方法 :

ServerSocket(int port)

创建一个服务端流套接字Socket,并绑定到指定端口.

主要用到的方法 :

Socket accept()

开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端 Socket 对象,并基于该Socket建立与客户端的连接,否则阻塞等待.

void close()

关闭此套接字 (大多情况下, ServerSocket 的生命周期都会跟随整个程序, 所以不调 close 也问题不大)

accept 就是接受的意思.
客户端主动发起连接, 服务端被动接受连接.
其实 tcp 的连接是在内核就完成了的, 这里的 accept 是应用层层面的接受, 并返回一个 Socket 对象, 通过这个对象就可以与客户端进行交互了.

  1. Socket API

Socket 即会给服务端用, 也会给客户端用.
不管是客户端还是服务端Socket,都是双方建立连接以后,保存对端信息,用来与对方收发数据的。

Socket 和 DatagramSocket 类似, 都是构造的时候指定一个具体的端口, 让服务器绑定该端口.

主要用到的构造方法 :

Socket(String host, int port)

两个参数表示 ip 和 端口号, TCP是有连接的, 在客户端 new Socket 时, 就会尝试和指定 ip 端口的目标建立连接.

主要用到的方法 :

InetAddress getInetAddress()

返回套接字所连接的地址

InputStream getInputStream()

返回此套接字的输入流

OutputStream getOutputStream()

返回此套接字的输出流

void close()

关闭此套接字

TCP 是面向字节流的, 所以我们可以通过上述字节流对象进行数据传输.
从 InputStream 读数据, 就相当于从网卡接收.
往 OutputStream 写数据, 就相当于从网卡发送.

服务器

public class TcpEchoServer {// serverSocket 就是外场拉客的小哥哥// serverSocket 只有一个.private ServerSocket serverSocket = null;public TcpEchoServer(int port) throws IOException {serverSocket = new ServerSocket(port);}public void start() throws IOException {// 服务器不止是对一个客户端进行服务// 加上 while 循环, 是让服务器随时可以获取到新的客户端的请求连接while(true) {// clientSocket 就是内场服务的小姐姐.// clientSocket 会给每个客户端都分配一个.(可以通过它来获取客户端的信息)Socket clientSocket = serverSocket.accept();// 我们对多客户端的交互应该是并行的// 所以创建线程来对每个客户端进行响应Thread t = new Thread(() -> {try {// 该方法就是对客户端进行交互了processConnection(clientSocket);} catch (IOException e) {e.printStackTrace();}});t.start();}}private void processConnection(Socket clientSocket) throws IOException {// 提醒客户端已上线.System.out.printf("[%s : %d] 客户端已上线!\n",clientSocket.getInetAddress(),clientSocket.getPort());// 将输入,输出流放入 try() 中, 可以不用考虑资源释放.// 通过 clientSocket 获得输入, 输出流.try(InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {// 因为 tcp 是面向字节流的, 所以为了方便读取, 给输入流套一个外壳// Scanner 是面向字符流.Scanner scanner = new Scanner(inputStream);// 同样的, 为了方便写入, 给输出流套一个壳// PrintWriter 是面向字符流PrintWriter printWriter = new PrintWriter(outputStream);// 因为服务器与客户端交互不是一次就完了, 所以得加个循环while(true) {// 判断是否还有数据在输入流中,如果没有了就认为客户端下线了,退出循环if(!scanner.hasNext()) {System.out.printf("[%s : %d] 客户端已下线!\n",clientSocket.getInetAddress(),clientSocket.getPort());break;}// 通过 scanner 读取网卡(客户端的请求)String request = scanner.next();// 通过请求计算响应String response = process(request);// 将响应写入缓存区, 当缓存区满了 就会写入网卡// 因为 Scanner 读取是不会读空格和回车的// 所以写入的时候要加换行, 以便客户端区分请求和响应printWriter.println(response);// 因为写入缓存区时, 缓存区并不一定会刷新// 所以我们要手动刷新, 将缓存中的数据刷入网卡printWriter.flush();System.out.printf("[%s %d] req: %s resp: %s\n", clientSocket.getInetAddress(),clientSocket.getPort(),request,response);}} catch (IOException e) {e.printStackTrace();}finally {// 因为 clientSocket 在客户端下线后就没用了,所以要释放// serverSocket 之所以不要释放, 是因为它的生命周期伴随整个程序// 程序结束, 它自然被释放了clientSocket.close();}}//计算响应public String process(String request) {return request;}public static void main(String[] args) throws IOException {TcpEchoServer tcpEchoServer = new TcpEchoServer(8080);tcpEchoServer.start();}
}

客户端

public class TcpEchoClient {private Socket socket = null;public TcpEchoClient(String serverIP, int port) throws IOException {// 这个操作相当于让客户端和服务器建立 tcp 连接.// 这里的连接连上了, 服务器的 accept 就会返回.socket = new Socket(serverIP,port);}public void start() {// 这个 scanner 是用来读取用户输入内容的Scanner scanner = new Scanner(System.in);try(InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()) {PrintWriter printWriter = new PrintWriter(outputStream);// 这里的 scannerFromSocket 是用来读取网卡(服务器响应)Scanner scannerFromSocket = new Scanner(inputStream);// 通过循环来对服务器进行多次请求while(true) {// 提示用户输入System.out.printf("-> ");// 读取键盘输入String request = scanner.next();// 将键盘输入写入缓存, 当缓存满了就自动刷新 并写入网卡// 注意带回车, 不然服务器读不到空白符,会一直读printWriter.println(request);// 手动刷新缓存, 将数据写入网卡printWriter.flush();// 读取服务器写入网卡的响应String response =  scannerFromSocket.next();System.out.printf("req: %s resp: %s\n", request,response);}} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) throws IOException {TcpEchoClient tcpEchoClient = new TcpEchoClient("127.0.0.1", 8080);tcpEchoClient.start();}
}

服务端 :

在这里插入图片描述

客户端 :

在这里插入图片描述
当然了, 我们还可以多启动几个客户端, 与服务器进行交互.

服务端 :
在这里插入图片描述

对服务器进行改进(使用线程池)

这里主要是修改 start 方法里的线程创建, 就单独拿出来修改 :

    public void start() throws IOException {while(true) {ExecutorService executorService = Executors.newCachedThreadPool();Socket clientSocket = serverSocket.accept();executorService.submit(new Runnable() {@Overridepublic void run() {try {processConnection(clientSocket);} catch (IOException e) {e.printStackTrace();}}});}}

效果还是一样的.

TCP 版本的字典客户端和字典服务器

其实和 UDP 版的基本一样, 只需要对服务端进行修改 :

public class TcpDictServer extends TcpEchoServer{private HashMap<String,String> dict = new HashMap<>();public TcpDictServer(int port) throws IOException {super(port);dict.put("pen","笔");dict.put("dog","狗");dict.put("cat","猫");}public String process(String request) {return dict.getOrDefault(request,"未找到该单词");}public static void main(String[] args) throws IOException {TcpDictServer tcpDictServer = new TcpDictServer(8080);tcpDictServer.start();}
}

在这里插入图片描述

在这里插入图片描述

相关文章:

简单实现基于UDP与TCP的回显服务器

目录 前言UDP 版的回显服务器需要用到的 api服务端客户端UDP 版本的字典客户端和字典服务器 TCP 版的回显服务器需要用到的 api服务器客户端对服务器进行改进(使用线程池)TCP 版本的字典客户端和字典服务器 前言 我们写网络程序, 主要编写的是应用层代码. 真正要发送这个数据,…...

家用洗地机有什么推荐的吗?家用洗地机哪款好

洗地机是创新、高效的清洁工具&#xff0c;其具有高性能的清洁能力和卓越的操作体验。与传统的清洁工具相比&#xff0c;洗地机可以迅速而彻底地打扫地面&#xff0c;降低清洁时间和人力成本&#xff0c;让我们在工作之余不用花费大量的时间和精力去打扫卫生&#xff0c;下面就…...

深度学习与文本聚类:一篇全面的介绍与实践指南

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…...

AP5153 线性降压恒流驱动芯片 2.5A

AP5153 是一种 PWM 调光的、低压 差的 LED 线性降压恒流驱动器。 AP5153 仅需要外接一个电阻和一个 NMOS 管就可以构成一个完整的 LED 恒 流驱动电路&#xff0c; 调节该外接电阻就可以调节 输出电流&#xff0c;输出电流可调范围为 20mA 到 3.0A。 AP5153 还可以通过在 DIM…...

Unity物理系统脚本编程(下)

一、修改物理材质 Unity对物体表面材料的性质做了件化处理&#xff0c;仅有5种常用属性&#xff1a; Dynamic Friction&#xff08;动态摩擦系数&#xff09;Static Friction&#xff08;静态摩擦系数&#xff09;Bounciness&#xff08;弹性系数&#xff09;Friction Combine…...

容器技术的发展

容器技术的发展 近年来&#xff0c;随着计算机硬件、网络以及云计算等技术的迅速发展&#xff0c;云原生的概念也越来越受到业界人士的广泛关注&#xff0c;越来越多的应用场景开始拥抱云原生&#xff0c;其中容器技术的发展起着至关重要的作用。本章将介绍容器技术的基础知识…...

Python Flask request中常见存储参数的介绍

Python Flask request中常见存储参数的介绍 首先从flask模块中导入请求对象&#xff1a; from flask import requestrequest.form 通过method属性可以操作当前请求方法&#xff0c;通过使用form属性处理表单数据&#xff08;本质也是得到一个字典&#xff0c;如果传输的是字…...

php+vue网盘系统的设计与实现

该网盘系统的开发和设计根据用户的实际情况出发&#xff0c;对系统的需求进行了详细的分析&#xff0c;然后进行系统的整体设计&#xff0c;最后通过测试使得系统设计的更加完整&#xff0c;可以实现系统中所有的功能&#xff0c;在开始编写论文之前亲自到图书馆借阅php书籍&am…...

[前端]深浅拷贝

一、回顾变量类型 基础类型 boolean&#xff08;bool&#xff09; number string null undefined 引用类型 object ​ function ​ array 基本类型与引用类型的存储 基本类型一般存储在 栈 (栈小) 栈一旦确认 大小就固定 可能会造成溢出栈一般是先进后出用于存储…...

文章纠错免费软件-文字校对软件免费下载

自动校对稿件的软件 自动校对稿件的软件是一种基于自然语言处理&#xff08;Natural Language Processing, NLP&#xff09;和机器学习&#xff08;Machine Learning&#xff09;技术的工具&#xff0c;可以较为准确地检测和纠正文本中出现的语法、拼写、标点符号以及其他笔误…...

【Redis】Redis缓存雪崩、缓存穿透、缓存击穿(热key问题)

目录 一、缓存穿透 1、概念 2、解决办法 1.缓存空对象 2.布隆过滤 二、缓存雪崩 1、概念 2、解决办法 1.给key设置随机的过期时间TTL 2.业务添加多级缓存 3.利用集群提供服务可用性 4.缓存业务添加降级限流 三、缓存击穿 1、概念 2、解决办法 1.互斥锁 2.逻辑…...

为什么很多程序员喜欢linux系统?

a> Linux哪些行业在运用&#xff1f; Linux系统运用极其广泛&#xff0c;不少用户只知道windows&#xff0c;是因为&#xff0c;Linux的运用主要是在企业端。现在科技极其发达&#xff0c;我们手机在手&#xff0c;就能干很多事情&#xff0c;只需点一点屏幕&#xff0c;轻松…...

Bean 作用域和生命周期

✏️作者&#xff1a;银河罐头 &#x1f4cb;系列专栏&#xff1a;JavaEE &#x1f332;“种一棵树最好的时间是十年前&#xff0c;其次是现在” 目录 lombok的使用案例引入作用域定义singleton单例作用域prototype原型作用域(多例作用域)request请求作用域session会话作用域ap…...

PMP考试常见13个固定套路

一、变更批准之后 变更批准后要做三件事: 1、在变更日志中记录 2、通知相关干系人 3、更新项目管理计划 二、风险的情景题 1、先判断风险识别了&#xff0c;还是风险发生了。 2、若是风险识别&#xff0c;按风险管理程序走; 3、若是风险发生&#xff0c;则应采取应急措施…...

Leecode101 ——对称二叉树

对称二叉树:Leecode 101 leecode 101 对称二叉树 根据题目描述&#xff0c;首先想清楚&#xff0c;对称二叉树要比较的是哪两个节点。对于二叉树是否对称&#xff0c;要比较的是根节点的左子树与根节点的右子树是不是相互翻转的&#xff0c;其实也就是比较两个树&#xff0c;…...

JVM学习随笔03——Java堆中new一个对象的步骤

目录 一、进行类加载 二、堆中分配内存 1、怎么输出GC日志&#xff1a; 2、内存分配的两种方式&#xff1a; 3、内存分配过程中并发控制的两种方式&#xff1a; 三、内存空间初始化 四、对象头初始化&#xff08;对象头包含哪些信息&#xff1f;&#xff09; 五、执行构…...

虹科方案 | CEMEX 使用HK-Edgility 智能边缘计算平台简化其企业 WAN 管理和运营

一、应对价值 130 亿美元的跨国企业的网络挑战 “我们选择 Edgility 是因为其卓越的管理和协调功能&#xff0c;它为我们提供了一个端到端的工具集&#xff0c;可以经济高效地部署和管理我们边缘设备的生命周期。” —— Fernando Garcia -Villaraco Casero, CEMEX 全球IT 战略…...

rk3568 系统移植和编译

1。 硬件问题 尽量根据原版 evb 开发版 pcb 进行布线和移植&#xff0c;切记不可自行走线。 emmc 和 ddr4 选型都有要求的&#xff0c;按照硬件手册进行设计 2。软件问题 2.1 目前固件系统选用1.3.2 版本进行设计 解压后运行 .repo/repo/repo sync -c 更新代码 2.2 ubo…...

深度解析C++异常处理机制:分类、处理方式、常见错误及11新增功能

C 基础知识 八 异常处理 上篇 一、基础1. 异常的概念2. 异常的分类2.1 内置异常2.2 自定义异常 3. 异常的处理方式3.1 try-catch 语句3.2 throw 语句3.3 noexcept 修饰符3.4 finally 语句块 二、 异常处理机制1 try-catch 语句块2 异常处理流程3 标准异常类 三、 抛出异常1 thr…...

FPGA时序约束(四)主时钟、虚拟时钟和时钟特性的约束

系列文章目录 FPGA时序约束&#xff08;一&#xff09;基本概念入门及简单语法 FPGA时序约束&#xff08;二&#xff09;利用Quartus18对Altera进行时序约束 FPGA时序约束&#xff08;三&#xff09;时序约束基本路径的深入分析 文章目录 系列文章目录前言主时钟约束跨时钟域…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

HBuilderX安装(uni-app和小程序开发)

下载HBuilderX 访问官方网站&#xff1a;https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本&#xff1a; Windows版&#xff08;推荐下载标准版&#xff09; Windows系统安装步骤 运行安装程序&#xff1a; 双击下载的.exe安装文件 如果出现安全提示&…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战

在现代战争中&#xff0c;电磁频谱已成为继陆、海、空、天之后的 “第五维战场”&#xff0c;雷达作为电磁频谱领域的关键装备&#xff0c;其干扰与抗干扰能力的较量&#xff0c;直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器&#xff0c;凭借数字射…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

Springboot社区养老保险系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;社区养老保险系统小程序被用户普遍使用&#xff0c;为方…...

Fabric V2.5 通用溯源系统——增加图片上传与下载功能

fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...