Linux网络编程03

news2024/11/24 17:57:28

select的缺陷
(1)fd,set的本质是一个位图,容量是固定的1024,因此最大只能监听1024个连接 (可以扩容)
(2)监听和就绪用的是同一个数据结构,使用困难
(3)存在多次大量的从用户态到内核态的拷贝,因为我们设置fd_set 都是在用户态,但是要实现监听必须要将fd_set从用户态拷贝到内核态
(4)采用轮询找到就绪的fd,在海量连接少量就绪的情况下,会浪费了大量的时间进行轮询

高并发服务器的基石epoll(最重要的)

epoll是一种高性能的IO多路复用

epoll由监听集合和就绪队列组成

(1)将所有的监听数据放在内核态,看成一个文件对象(避免拷贝问题),epoll是一个文件对象,文件对象中有监听集合监听所有连接,查找的时候使用二分查找,使用红黑树进行存储,因此epoll可以建立大量的连接,实现快速查找
(2)把监听和就绪分离,就绪事件用队列保存,有任何事件处于就绪的时候会把就绪事件队列拷贝到用户态
只有Linux操作系统才可以使用epoll
在这里插入图片描述

使用epoll的步骤
(1)创建一个文件对象
(2)设置监听
(3)陷入阻塞,直到任一监听就绪
(4)遍历就绪事件队列处理事件(和select有区别)

(1)创建文件对象

epoll_create 返回值为一个非负的文件描述符
在这里插入图片描述

(2)设置监听
在这里插入图片描述
epfd:epoll对象的文件描述符,上一个函数的返回值
op的操作
在这里插入图片描述

EPOLL_CTL_ADD增加 监听
EPOLL_CTL_MOD修改 监听
EPOLL_CTL_DEL删除 监听
fd:我们要监听的文件对象的文件描述符
event:描述监听的类型
在这里插入图片描述
epoll_data是一个联合体,里面的数据四选一,一般选择int fd
其中epoll_event中的events是用来指定是读就绪还是写就绪或者异常就绪,我们一般填写的是读就绪EPOLLIN
epoll_event中的data就是上一个结构体

(3)陷入阻塞

在这里插入图片描述
events:用户给就绪事件队列分配的地址,本质上是一个数组,数组中有一个重要成员data.fd,这个成员用来说明是谁就绪了
maxevents:数组的长度,因为数组在传递的过程中会丢失长度信息
timeout:表示等待的毫秒数,精度不高,-1表示无线等待

示例
在这里插入图片描述

在这里插入图片描述
不能使用break,只能使用goto

断线重连

  #include <43func.h>
   //服务端
   int main(int argc,char * argv[]) {
       ARGS_CHECK(argc,3);
       int sockfd = socket(AF_INET,SOCK_STREAM,0);//创建你IPv4的TCP连接
       ERROR_CHECK(sockfd,-1,"socket");
       int optval = 1;
       int ret = setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(int));
       ERROR_CHECK(ret,-1,"setsockopt");
      struct sockaddr_in addr;
      addr.sin_family = AF_INET;
      addr.sin_port = htons(atoi(argv[2]));
      addr.sin_addr.s_addr = inet_addr(argv[1]);
      ret = bind(sockfd,(struct sockaddr*)&addr,sizeof(addr));
      ERROR_CHECK(ret,-1,"bind");
      ret = listen(sockfd,10);//更改server端socket的结构,更改半连接队列长度为10
      ERROR_CHECK(ret,-1,"listen");
      int netfd = -1;
      int epofd = epoll_create(1);//创建epoll文件对象
      ERROR_CHECK(epofd,-1,"epoll_create");
      struct epoll_event event;//创建监听描述符
      event.data.fd = STDIN_FILENO;//将文件输入流加入监听
      event.events = EPOLLIN;//将监听设置为读就绪监听
      epoll_ctl(epofd,EPOLL_CTL_ADD,STDIN_FILENO,&event);//将读事件stdin加入监听集合
      event.data.fd = sockfd;//将网络输入流加入监听
      event.events = EPOLLIN;//将监听设置为读就绪监听
      epoll_ctl(epofd,EPOLL_CTL_ADD,sockfd,&event);//将读事件stdin加入监听集合
      char buf[4097] = {0};
      struct epoll_event readyArr[3];//就绪队列
      while(1) {
          int readNum = epoll_wait(epofd,readyArr,3,-1);//开启阻塞,等待就绪队列中的连接
          puts("epoll_wait returns");
          for(int i = 0;i < readNum;i++) {
              if(readyArr[i].data.fd == STDIN_FILENO){//输入流就绪
                  bzero(buf,sizeof(buf));//清空buf
                  ret = read(STDIN_FILENO,buf,sizeof(buf));
                  if(ret == 0) {//读取到关闭的消息
                      goto end;
                  }
                  send(netfd,buf,sizeof(buf),0);//往网络文件复制数据
              }
              else if(readyArr[i].data.fd == sockfd) {//数据来源于网络文件
                  netfd = accept(sockfd,NULL,NULL);//创建新的套接字
                  ERROR_CHECK(netfd,-1,"accept");
                  event.data.fd = netfd;
                  event.events = EPOLLIN;
                  epoll_ctl(epofd,EPOLL_CTL_ADD,netfd,&event);//将网络文件描述符添加监听
              }
              else if(readyArr[i].data.fd == netfd) {//读取网络文件数据
                  bzero(buf,sizeof(buf));
                  ret = recv(netfd,buf,sizeof(buf),0);
                  if(ret == 0) {//将netfd移除监听队列
                      close(netfd);
                      event.data.fd = netfd;
                      event.events = EPOLLIN;
                      epoll_ctl(epofd,EPOLL_CTL_DEL,netfd,&event);
                  }
                  puts(buf);
              }
          }
      }
      end:
          close(netfd);
   }

在这里插入图片描述

recv和read的非阻塞

当缓冲区中有数据,正常读取
当缓冲区中没有数据,直接返回-1.好处进行不会切换状态

如果我们能够预期这个数据很快就能到达,那么我们使用非阻塞效果会更好
非阻塞适用于持续但不连续(网络)的数据传输

异步非阻塞(io_wring–Linux;icop–windows):当线程读取缓冲区的时候没有数据就会立刻返回,去执行其他操作,当缓冲区就绪的某一个时刻在通知线程进行读取
同步阻塞:假如线程执行read操作读取缓冲区,如果缓冲区没有数据线程会陷入阻塞等到缓冲区中有数据读取,并且读取数据之后才会返回
同步非阻塞:线程读取缓冲区,如果缓冲区没有数据,那么线程会立刻返回,然后立刻再调用read区查看缓冲区,直到读取到数据,才执行之后的事件,这样不会导致线程阻塞,会让线程一直运行,但是相比阻塞这样的方式一直在消耗CPU资源
IO多路复用:将全部需要进行等待阻塞的操作统一到一起,既可配阻塞,也可配合非阻塞

给已经打开的文件描述符加上非阻塞属性
在这里插入图片描述
cmd的取值,表示他是获取属性还是修改属性
在这里插入图片描述

epoll的触发方式
在这里插入图片描述
水平触发:只要缓冲区有数据,就会触发读取操作
如果某个用户有大量数据,epoll会重复就绪,好处是这个用户的数据会在有限时间取完,代价就是要循环好多次epoll
边缘触发:只有当新数据进来,数据量增加的时候才会触发读取操作
哪个用户来使用读取操作,就读取哪个用户的数据,但是不会循环的去读取,保证用户使用epoll的公平性,代价是如果有一个数据量大的用户,可能数据永远取不完

采用epoll的边缘触发,配合while+非阻塞recv

水平触发 在这里插入图片描述

在这里插入图片描述

边缘触发 在这里插入图片描述

在这里插入图片描述

使用while+边缘触发读取数据 在这里插入图片描述

在这里插入图片描述

设置socket属性

修改缓冲区大小 在这里插入图片描述
SO_SNDBUF:设置发送缓冲区大小 SO_RCVBUF:设置接收缓冲区大小

在这里插入图片描述
在这里插入图片描述

缓冲区下限

读取缓冲区和发送缓冲区的下限,要达到下限才能发送或者读取数据
在这里插入图片描述
在这里插入图片描述

recv 的MSG_PEEK属性
复制缓冲区的数据,但是不取出

bzero(buf,sizeof(buf));
ret = recv(netfd,buf,sizeof(buf),MSG_PEEK);//打印管道中的数据,将管道中的数据复制一份出来,但是不读取
puts(buf);//打印读取到的管道数据

进程池和线程池

设计架构考量方面
(1)高新能,充分利用操作系统提供的资源,能不浪费就不浪费
(2)可维护性,抽象

设计方案:
(1)基于线程【很高的可维护性{Apache},但是性能差:浪费大量的资源创建和销毁线程】,每当有一个客户端连接时,创建一个线程,断开时销毁
(2)池化的思路:申请的资源用完之后不要马上回归,可以交给另外的事情复用

进程池/线程池设计思路
(1)提前创建好若干个进程
(2)每当有任务到来分配一个进程
(3)任务完成后归还进程
(4)整个进程池关闭的时候在销毁

使用进程池/线程池出现的问题
事件驱动模型(性能更高)---- event-driven
任务太多,使用任务队列解决
事件太多,原本是采用进程来统一某些事件,但是现在需要每个事件拆分开来,每个事件使用一个进程完成,因此会有多个事件,使用IO多路复用机制管理事件

进程池(实现下载服务器)

首先我们会创建多个进程(工作进程),其中有一个是主进程(Master进程)

工作进程就是执行while(1){取任务;完成任务;恢复空闲;}的循环操作,被称为事件循环 — event loop

主线程:
(1)创建子进程
(2)监听子进程
(3)实现TCP,bind/listen,监听sockfd等操作
给工作线程分配工作,并且知道哪些线程是空闲线程,每当有客户端发起连接请求总是和主线程连接在一起,因此主线程需要使用epoll来管理连接

创建子进程
在这里插入图片描述

实现tcp连接/初始化tcp连接
启动之后使用netstat -an|grep 1234命令可以看到网络端口1234处于监听状态

在这里插入图片描述

父进程移交连接

惊群问题”:在前面我们创建子线程的时候都是先使用fork创建子进程,然后再用socket创建网络文件对象,但是父进程没有子进程的网络文件对象。假如我们将socket和fork顺序调换,那么又会出现父子进程共享网络文件对象,当客户端来一个连接时,就会有多个进程同时连接。这种多个进程同时连接的问题就叫做惊群问题选项SO_REWSEPORT可以解决惊群问题,但是对操作系统内核版本有限制,Nginx就是这样实现的

我们实现先fork再socket
如果已经fork过,怎么样让父子进程共享文件对象

在两个进程之间传递文件对象

除了可以传递文本内容,还可以传递控制信息,可以在两个进程之间传递信息
在这里插入图片描述

sockfd:不能是一个管道,必须时socket,因此父子进程传递信息必须要使用socket才能传递
msg:消息头部

msg_iov:io vector(数组),数组里面存储着不连续数据的指针
不连续数据结构体
在这里插入图片描述

在这里插入图片描述

msg_iovlen:数组的长度,如果其数值为1,那么说明msg_iov就是连续数据,
msg_control:控制信息
msg_controllen

控制信息的处理
(1)在堆区为struct cmaghdr申请内存
在这里插入图片描述
使用sendmsg将数据发送给对方,收取使用recvmsg去收
在这里插入图片描述

socketpairpipe使用方法一致

在这里插入图片描述
使用socketpair会在进程中产生一个文件对象sv[0]sv[1],我们使用fork的时候会产生新的进程,此时进程中也会有一个sv[0]sv[1],并且这两个进程中sv[0]是相互连通的,sv[1]是相互连通的
在这里插入图片描述

发送的文件描述符的数值和收到的文件描述符的数值可能是不同的,但是其返回的文件对象是同一个 发送/接收实现
在这里插入图片描述

示例:

在这里插入图片描述

在这里插入图片描述

使用进程池实现服务端服务

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

epoll和select的使用对比 在这里插入图片描述

使用epoll实现聊天室
(1)获取readyNum
(2)遍历readyArr
(3)更局readyArr[i].data.fd数值找到分类,在处理对应事件
在这里插入图片描述

在这里插入图片描述

进程池

池:预先申请好所有资源,有任务到来时可以使用资源,任务完成之后可以留存资源,以供后续任务使用

(1)创建很多个进程分为主进程和工作进程,
主进程负责创建工作进程,一开始时创建,任务完成时不销毁
工作进程:完成具体的工作
(2)主进程的职责
a.创建子进程(使用fork,返回0就是工作进程),管理子进程(有任务到来,就分配给子进程,子进程完成任务通知主进程)主进程用epoll监听子进程的管道,子进昵称完成任务后用管道通知主线程
b.初始化TCP连接,有连接来就要将连接交给子进程,
在这里插入图片描述

代码的组织
在这里插入图片描述

主进程的工作
(1)TCP的初始化
(2)用epoll去管理多个文件描述符(sockfd和每个子进程的管道),封装epoll的操作(增加监听,删除监听)

开始工作
在这里插入图片描述

sendmsg和recvmsg

在这里插入图片描述

发送文本数据
在这里插入图片描述

使用sendmsg传递控制信息
在这里插入图片描述

在这里插入图片描述

给子进程增加下载的任务

TCP是一种字节流协议,消息之间没有边界,因此会导致接收的文件内容也会当作文件名
发送文件的方法------进程池进程调用下载方法-------客户端接收下载文件
在这里插入图片描述

在这里插入图片描述

应用层私有协议实现小文件传输

解决了TCP流中数据没有消息边界的问题,消息边界问题也称为”粘包“问题

解决的具体操作方法就是,我们下载文件之前,服务端将文件的文件名以及文件内容封装在一个结构体里面,这个结构体里面有Length表示数据长度,还有char buf[]来存储数据,客户端每次收到数据会先读取结构体中的Length然后在根据Length的长度来确定后面需要读取多少数据,这样就可以实现消息边界

typedef struct train_s{
	int length;//记录数据长度
	char buf[1000];//记录发送的数据,但是客户端只会读取Length长度的数据,同时1000也指定发送的文件大小不能超过1000个字节
} train_t;

在这里插入图片描述

在这里插入图片描述

大文件的传输
如果只是按照下面简单的使用while(1)循环实现大文件的下载,当文件大小过大的时候就会出现下载报错,客户端收到的数据长度过大
出现错误的原因:由于recvlen的长度我们虽然填的是1000个字节,但是不一定能够读到1000个字节的数据,,这个len只是规定接收端最大能接收多少,并不代表他一定能拿到多少,由于网络是不稳定的,并且TCP是流式的可靠协议,假如我们在读到一个struct结构体的头获取长度为1000,但是其数据只是到达900,那么接收端就会读取900个字节的数据,但是剩下的数据不会被丢弃,而且由于我们使用while(1)进行循环,此时就会把剩下的100个字节的头4个字节作为下一个struct结构体的头,这样就会导致读取错误。

在这里插入图片描述

如果我们把服务端的ERROR_CHECK错误检测,主进程就会陷入死循环,并且我们发现会有一个僵尸进程。这是由于客户端先出错,就关闭了读操作,然而服务端不知道客户端已经断开读操作,服务器就会继续写入数据,此时就会触发SIGPIPE信号,导致子进程终止,然而父进程在执行epoll_wait,无法为子进程清空数据,因此就编程僵尸进程
死循环的原因:父子进程之间的管道关闭了,父进程的epoll_wait就一直就绪

生成指定大小的空文件,文件中的内容都为0
truncate -s 100M file1

使用gdb调试多进程
set follow -fork -mode child/parent追踪子进程或父进程

临时屏蔽SIGPIPE信号----保证客户端可以死,但是服务端不能跟着客户端一起死
sendflag参数
在这里插入图片描述

在使用send时操作参数使用MSG_NOSIGNAL确保客户端死,不会导致服务端死
在这里插入图片描述

让每一次接收一定能收完
recv函数操作添加MSG_WAITALL,如果管道没有关闭,recv确保能收到len字节的数据
在这里插入图片描述
在这里插入图片描述
如果文件比较大的时候MSG_WAITALL可能会出错,他可能不会等到对应len长度数据就已经存储了

自己实现MSG_WAITALL

int recvn(int sockFd,void *pstart,int len){
	int total = 0;//记录获取到的数据总数
	int ret;
	char *p = (char *)pstart;//强转为char*
	while(total < len) {//循环获取数据
		ret = recv(sockFd,p+total,len-total,0);
		total += ret;
	}
	return 0;
}

在这里插入图片描述

在这里插入图片描述

文件校验

摘要哈希:把任何文本内容经过一i写算法,将其生成一段摘要,如果文件内容一致,则摘要一致,文件内容不一致,则摘要极大可能不一致

MD5(常用):效率比SHA高
SHA:有1—64—128数字越大,效率越低,碰撞越小
CRC:效率高,碰撞几率大,

计算MD5码
使用命令sudo apt install openssl下载相应的包
可以使用sha1sum file1计算出文件file1SHA1的摘要
使用md5sum file1计算出文件file1MD5

显示进度条

函数fstat可以根据文件描述符获取文件大小
在这里插入图片描述
服务端先向客户端发送文件大小,客户端在根据文件大小显示下载百分比
服务端

//服务端
struct stat statbuf;
ret = fstat(fd,&statbuf);//保存文件信息
ERROR_CHECK(ret,-1,"fstat");
train.length = 4;
int fileSize = statbuf.st_size;//长度转换成int
memcpy(train.buf,&fileSize,sizeof(int));//int存入train.buf中
send(netFd,&traiin,train.length + sizeof(train),MSG_NOSIGNAL);//将文件长度发送给客户端

在这里插入图片描述
在这里插入图片描述

零拷贝
我们之前实现文件的下载,是首先在内核态创建对应物理内存的映射,然后在用read将数据拷贝到用户态的train.buf,再通过send将数据从用户态的train.buf拷贝到内核态的socket,这样的来回拷贝就会消耗大量的资源和时间,因此我们下面要使用零拷贝技术来减少这样的内核于用户态之间频繁拷贝的开销。

mmap(文件映射)
(1)使用mmap实现片段发送(效率没有提升)
让用户态空间的一部分和内核态空间的一部分对应同一份物理内存,用户对物理内存更改数据,其实际就是对内核态更改数据,但是这没有对数据进行拷贝,只是使用映射机制将其对应同一个区域。
在这里插入图片描述

在这里插入图片描述
(2)使用mmap实现整个文件发送
首先使用struct发送文件名称和名称大小
在通过结构体发送文件内容,因为TCP是可靠连接,因此只需要确定文件内容大小就可以实现一次性传输
在这里插入图片描述

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

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

相关文章

Nacos2.2.3版本运行startup.cmd出现闪退,无错误信息解决方法

Nacos2.2.3版本运行startup.cmd出现闪退&#xff0c;无错误信息解决方法 一、问题描述二、解决方法 一、问题描述 当我下载好nacos2.2.3版解压之后&#xff0c;直接双击startup.cmd出现闪退&#xff0c;而且 没有错误提示信息。后来经过一番搜索尝试&#xff0c;终于解决了自己…

InnoDB中Buffer Pool详解

1. 概念及特点 Buffer Pool 是 MySQL 中 InnoDB 存储引擎用来缓存表数据和索引数据的内存区域。这个内存区域被用来存储磁盘上的数据页的副本&#xff0c;这样常用的数据可以在内存中快速被访问&#xff0c;而不必每次都从磁盘中读取。 以下是 Buffer Pool 的一些重要特点&a…

kali Linux中更换为阿里镜像源

准备&#xff1a; kali Linux 阿里源链接 deb kali安装包下载_开源镜像站-阿里云 kali-rolling main non-free contrib deb-src kali安装包下载_开源镜像站-阿里云 kali-rolling main non-free contrib 配置&#xff1a; 打开kali 终端输入&#xff1a;sudo nano /etc/apt…

2023.11.5 关于 Spring 创建 和 使用

目录 创建 Spring 项目 1.创建 Maven 项目 2.添加 Spring 依赖 将 Bean 对象存储到 Spring 容器中 创建 Bean 存储 Bean ApplicationContext 获取 Bean BeanFactory 获取 Bean ApplicationContext 和 BeanFactory 的区别 获取 Bean 的三种方式 根据 Bean id 获取…

基于8086电压表系统仿真系统设计

**单片机设计介绍&#xff0c;1665基于8051单片机与1601LCD的计算器设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 一个基于8086的电压表系统仿真系统可以分为硬件和软件两部分。 硬件部分包括输入设备&#xff08;例如模拟…

什么是缓冲区溢出?

缓冲区溢出 1. 什么是缓冲区溢出2. 缓冲区溢出攻击的类型3. 攻击者如何利用缓冲区溢出4. 如何防止缓冲区溢出攻击 1. 什么是缓冲区溢出 &#xff08;1&#xff09;缓冲区 缓冲区是一块连续的计算机内存区域&#xff0c;用于在将数据从一个位置移到另一位置时临时存储数据。这…

[动态规划] (七) 路径问题:LCR 166.剑指offer 47. 珠宝的最高价值

[动态规划] (七) 路径问题&#xff1a;LCR 166./剑指offer 47. 珠宝的最高价值 文章目录 [动态规划] (七) 路径问题&#xff1a;LCR 166./剑指offer 47. 珠宝的最高价值题目解析解题思路状态表示状态转移方程初始化和填表顺序 返回值代码实现总结 LCR 166. 珠宝的最高价值 题目…

一致性哈希在分库分表的应用

文章目录 前言分库分表方法一致性哈希介绍分库分表的应用 总结 前言 大家应该都知道一些哈希算法&#xff0c;比如MD5、SHA-1、SHA-256等&#xff0c;通常被用于唯一标识、安全加密、数据校验等场景。除此之外&#xff0c;还有一种应用是对某个数据进行哈希取模映射到一个有限…

【数据开发】大数据平台架构,Hive / THive介绍

1、大数据引擎 大数据引擎是用于处理大规模数据的软件系统&#xff0c; 常用的大数据引擎包括Hadoop、Spark、Hive、Pig、Flink、Storm等。 其中&#xff0c;Hive是一种基于Hadoop的数据仓库工具&#xff0c;可以将结构化的数据映射到Hadoop的分布式文件系统上&#xff0c;并提…

什么是防火墙?详解三种常见的防火墙及各自的优缺点

目录 防火墙的定义 防火墙的功能 防火墙的特性 防火墙的必要性 防火墙的优点 防火墙的局限性 防火墙的分类 分组过滤防火墙 优点&#xff1a; 缺点&#xff1a; 应用代理防火墙 优点 缺点 状态检测防火墙 优点 缺点 防火墙的定义 防火墙的本义原是指古代人们…

【蓝桥杯选拔赛真题11】C++求平方 青少年组蓝桥杯C++选拔赛真题 STEMA比赛真题解析

目录 C/C++求平方 一、题目要求 1、编程实现 2、输入输出 二、算法分析

基于.NET、Uni-App开发支持多平台的小程序商城系统 - CoreShop

前言 小程序商城系统是当前备受追捧的开发领域&#xff0c;它可以为用户提供一个更加便捷、流畅、直观的购物体验&#xff0c;无需下载和安装&#xff0c;随时随地轻松使用。今天给大家推荐一个基于.NET、Uni-App开发支持多平台的小程序商城系统&#xff08;该商城系统完整开源…

前端框架Vue学习 ——(二)Vue常用指令

文章目录 常用指令 常用指令 指令: HTML 标签上带有 “v-” 前缀的特殊属性&#xff0c;不同指令具有不同含义。例如: v-if, v-for… 常用指令&#xff1a; v-bind&#xff1a;为 HTML 标签绑定属性值&#xff0c;如设置 href&#xff0c;css 样式等 <a v-bind:href"…

NLP入门——基础知识

生成式AI和AIGC&#xff1a; 生成式AI所生成的内容就是AIGC AI的Venn图&#xff1a; 注意&#xff1a; 监督学习&#xff08;训练数据带标签&#xff09;&#xff1a; 经典的监督学习任务包括分类&#xff08;划分类别&#xff09;和回归&#xff08;预测&#xff09;&#x…

基于葡萄串的采摘点定位方法

文章目录 概要所需设备方法基于RGB图像的YOLOV8目标检测基于深度图的区域种子生长利用峰值定位法来确定竖向位置核心代码演示效果概要 这里将介绍如何用图像识别方法来定位葡萄串采摘点,用于机器人自动采摘操作。 所需设备 深度相机,这里我用的是realsense-L515 方法 主…

python爬虫(数据获取——双R)

静态资源加载 静态资源给了请求头和url即可 动态资源加载 headers {User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 } url "https://www.xinpianchang.com/api/xpc/comments/article…

【论文精读】PlanT: Explainable Planning Transformers via Object-Level Representations

1 基本信息 院校&#xff1a;德国的图宾根大学 网站&#xff1a;https://www.katrinrenz.de/plant 2 论文背景 2.1 现有问题 现在的基于学习的方法使用高精地图和BEV&#xff0c;认为准确的&#xff08;达到像素级的pixel-level&#xff09;场景理解是鲁棒的输出的关键。re…

SpringBoot案例(数据层、业务层、表现层)

1.创建项目 2.选择坐标 3.添加坐标 说明&#xff1a;为了便于开发&#xff0c;引入了lombak坐标。 <!--添加mybatis-plus坐标--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><ver…

JavassmMySQL宠物网站系统07183-计算机毕业设计项目选题推荐(附源码)

摘 要 在信息飞速发展的今天&#xff0c;网络已成为人们重要的信息交流平台。宠物网站每天都有大量的信息需要通过网络发布&#xff0c;为此&#xff0c;本人开发了一个基于B/S&#xff08;浏览器/服务器&#xff09;模式的宠物网站系统。 该系统以JJava编程语言、MySQL和SSM框…

【Mac环境搭建】JDK安装、多JDK安装与切换

文章目录 JDK下载与安装下载安装 配置环境变量安装多个JDK共存 JDK下载与安装 下载 Oracle官网提供了非常多个版本的JDK供下载&#xff0c;可以点击如下链接重定向到JDK下载页面 ORACLE官网JDK下载 安装 下面的官方文档可以点开收藏到浏览器的收藏夹&#xff0c;这样后续在开…