『 Linux 』HTTP(一)

news2024/9/20 16:51:20

文章目录

    • 域名
    • URL
    • URLEncode和URLDecode
    • HTTP的请求
    • HTTP的响应
    • 请求与响应的获取
    • 简单的Web服务器


域名

任何客户端在需要访问一个服务端时都需要一个IP和端口号,而当一个浏览器去访问一个网页时通常更多使用的是域名而不是IP:port的方式,

www.baidu.com

这是百度的域名;

实际上当浏览器访问到一个网站的域名时将会把域名解析成对应的IP:port即IP+端口号的形式进行访问,本质上还是采用IP和端口号的形式进行访问,只不过使用域名访问的方式将大幅度提升用户的使用体验;

域名实际上是一串字符串,这是运营商的一种映射的方式,将对应的字符串映射至对应的IP和端口号从而对服务端进行访问;

通常使用ping来查看当前网络连接的情况,在进行ping时对应的也会将对应域名的IP地址暴露出来;

ping www.baidu.com

在这个例子中使用ping来查看当前计算机与百度首页的连接,其中返回了一个180.101.50.188的IP地址,可以通过浏览器访问该IP地址来直接访问百度的首页;

在使用浏览器对一个IP进行访问时,若是在访问时只有IP没有端口号,浏览器默认将会以HTTP协议对该IP进行访问;

 http://180.101.50.188/ 等价于 180.101.50.188:80
https://180.101.50.188/ 等价于 180.101.50.188:443

HTTP协议是一种应用层协议,该协议默认的端口号为80,HTTPS协议默认端口号为443;


URL

URL(Uniform Resource Locator) 统一资源定位符;

所有网络上的资源都可以用唯一的一个字符串表示,可以通过该统一资源定位符在公网中获取对应的网络资源;

https://gitee.com/half-intermediate-mangfu/my_-linux/tree/master/Pro24/Network/HTTP

通过域名来区分不同主机的IP,资源由哪个进程访问由端口号来决定,不仅如此服务器上也有不同的路径,无论是域名还是端口号都是具有唯一性的,域名(IP)在公网中具有唯一性,端口号则是在同一台主机下具有唯一性,同样的不同的路径也是在主机中具有唯一性的;

在上面的这段urlhttps://为协议,也为端口号(https默认端口号为443),gitee.com为域名,/half-intermediate-mangfu/my_-linux/tree/master/Pro24/Network/HTTP则为路径;

这是一个较为简单的URL但并不是一个完整的URL,完整URL如下:

  • http

    该字段为协议,方案名称,表示需要使用哪种协议能够正确访问该IP;

  • user:pass

    表示用户的登录信息(认证),但随着登陆页面的独立化该字段也越来越少见;

  • www.example.com

    表示服务器的域名,即IP地址;

  • 80

    这里的80表示是端口号,但是由于在该URL的开头处已经指定了使用的协议,意味着该字段可以被省略;

    这也是上面的URL不存在端口号信息

  • /dir/index.htm

    表明是一个文件路径,标识着该网络资源在该服务器上的相对路径或者绝对路径;

    当一个网页资源的路径以/作为分隔符,那么这个网络资源则有肯能部署在类UINX系统中;

    其中第一个/表示的是Web根目录,Web根目录可能是服务器中的根目录,也可能是服务器中的一个特定目录作为根目录,而路径则为一个相对路径;

  • ?uid=1

    网络的行为实际上只有两种:

    • 获取别人的网络资源
    • 上传自己的资源至网络

    在访问一个网页时实际上就是将别人的htm文件资源获取至自己的浏览器中;

    而当需要上传一个信息时可能该主机的一些信息也会被上传至网络,其中uid=1是一种Key-Value的键值对形式,表示该请求的一些动态数据;

    url可以跟?符号,?符号后可以带参表示一些需要携带的信息;

  • #ch1

    表示片段标识符;

    通常用在网页中以实现页面内的跳转,它在URL中以 # 符号开始,后接标诈字;


URLEncode和URLDecode

URLEncode和URLDecode为URL编码与URL解码;

这是用于处理URL中特殊字符的函数或者方法,在上文中提到,URL实际上存在许多的特殊字符,包括/ : . @ ? #等特殊符号,如果在搜索过程中直接将特殊符号以符号的形式进行搜索那么将会使得URL错误,因此在URL中对于搜索的特殊符号需要进行特殊的处理,即URLEncode(URL编码);

假设需要搜索一个内容为aaaaa://?##bbbbb的内容,如果未经过URLEncode,最终的URL将为:

https://www.bing.com/search?q=aaaaa+://?##bbbbb

但有时这些特殊符号会导致URL解析错误;

而当对特殊符号进行URLEncode编码那么特殊符号将不会造成URL解析错误;

本质原因是当把未经编码的特殊符号传给服务端时服务端将无法直接区分众多特殊符号中哪些属于URL哪些属于用户需要搜索的内容;

少量的情况,提交或者获取数据本身可能包含和URL中特殊字符冲突的字符,要求Browser和Server双方进行编码和解码;

当Browser向Server发送数据时需要对URL中的特殊符号进行Encode编码,当Server接收到来自Browser发送来的数据时需要对URL中编码后的特殊符号进行解码,从而保证需要搜索(作为数据发送)的特殊符号不会与URL原本的特殊符号产生冲突;

  • URLEncode 编码的规则如下

    将需要转码的字符转为16进制,然后够从右到左取四位,不足四位直接处理,每两位作一位,前面加上%,编码成%XY的格式;

    • 获取字符的编码值

      对于ASCII字符,先获取其对应的ASCII码(范围在0-127);

      对于非ASCII字符(如中文字符),先将字符按照UTF-8编码,得到对应的字节序列;

    假设需要编码的字符为?,其ASCII码为63,将63转化为十六进制得到3F,最后在3F前加上%得到%3F;

    假设需要编码的字符为,其UTF-8编码为E4 BD A0(十六进制),将每个字符前加上%,得到%E4%BD%A0;

可以使用URLEncode工具进行验证;

通常情况下在浏览器中进行搜索,遇到特殊符号时浏览器将自动为特殊符号进行URLEncode编码;


HTTP的请求

HTTP的请求与响应都是以行位单位进行陈列的,都是以多行为构成,这里的行实际上是以一个分隔符作为区分为一行,也可以不将分隔符作为行分隔符从而看整体为一个字符串,行分隔符可以是\n,也可以是\r\n,每一行的结尾都是以\n或是\r\n作为一行的结束;

HTTP的请求为四个部分组成,分别为 请求行 , 请求头部 , 空行请求数据 组成;

请求行以三个部分组成,分别为请求方法,URL ,以及协议版本,字段与字段之间通常以空格作为分隔符;

  • 请求方法

    常见的请求方法为如下:

    但最常用的请求方法实际上为GETPOST,即获取服务器的资源与向服务器发送资源;

  • URL

    即统一资源标识符,包括请求的资源路径,通常包括主机名,端口号(如果非默认),路径以及查询字符串等(参照上文);

  • HTTP版本

    常见的HTTP版本为,HTTP/1.0,HTTP/1.1,HTTP/2等;

请求行的格式通常为:

GET /index.html HTTP/1.1

HTTP的请求行过后是请求头部,请求行与请求头部以行分隔符进行分割,同样的分割符可以是\n或者是\r\n;

请求头部存在多行,每一行都以Key-Value的方式进行存储,通常情况下请求头部的每一行存储的是一些关于请求的属性;

当服务端接收到来自浏览器的请求时需要对请求行和请求头部进行读取以及分析,而请求头部过后紧接着的是请求数据,也就是请求正文,而为了避免请求头部与请求数据中的混淆,请求头部和请求数据(请求正文)之间将存在一行为空行,这个空行只有行分隔符,当服务端读取至一行只存在行分隔符时则表示请求行和请求头部已经读取完毕,剩下的内容即为请求数据(请求正文),换种说法即为报头与有效载荷进行分离;

请求正文为客户端/浏览器在发送请求时需要传输的数据,这些数据可以是图片,音频,视频等二进制流,同时这个字段可以为空,因为在一些时候只是单纯向服务端发送请求使服务端将数据以响应的形式反馈给客户端;

在请求报头中存在一个属性为Content-Length,这个属性表示请求正文的长度以保证服务端能够判断接收到的报文是否为一个完整的报文;


HTTP的响应

HTTP的响应格式与请求的格式几乎相同;

  • 状态码

    HTTP的状态码为服务器处理客户端请求的完成状态,常用的状态码为如下:

    • 200表明请求成功
    • 302表明请求重定向
    • 304表明请求资源没有改变,访问本地缓存
    • 404表明请求资源不存在,通常是用户路径编写错误,也可能是服务器资源已删除
    • 500表明服务器内部错误,通常程序抛异常
  • 短语

    短语也是状态信息,状态信息是根据状态码变化而变化的,是对状态码的文本描述,虽然状态短语不是严格必要的,但是能够帮助用户理解响应;

  • 响应头部

    响应头部与请求头部相同,包含多个以Key-Value键值对字段,传递关于响应的元数据,如服务器信息,内容类型等,同样的响应头部中同样存在一个Content-Length字段,这个字段用来描述响应正文的长度,使得客户端在接收到一个响应报文时判断响应报文是否为一个完整的报文;

  • 空行

    响应头部与响应数据中存在一个空行作为间隔,这个空行中只存在行分隔符使得客户端能够判断响应报文中哪些字段属于状态行与报头,哪些字段属于响应数据;

  • 响应数据

    响应数据也被成为响应正文,是服务器返回给客户端的实际内容,这个响应数据可能是HTML页面,JSON数据,文件等;

    当状态码为200时表示成功,响应正文将包含客户端请求的资源内容;


请求与响应的获取

可以使用一些较为基本的工具对响应进行一个抓取;

  • telnet工具

    在之前的文章中,在实现一些TCP程序时当未完成客户端的编写时采用了telnet工具作为平替,即使用telnet工具对自定义编写的TCP服务器进行访问;

    而该工具基本上可以访问任何可以访问的服务器;

    在这个例子中构建了一个最简单的请求;

    GET / HTTP/1.1
    
    

    GET为请求的方法,/表示根目录,一般情况下根目录表示获取该服务器的首页资源,HTTP/1.1表示HTTP协议的版本,这是一个最简单的请求,这个请求不需要报头也不携带任何请求资源;

    当第一次回车时表示第一个行分隔符,表示请求行已经结束,此时并没有反应,因为当只有空行结束时才表示这个请求已经结束;

    在上图中分为红色区域与黄色区域,其中红色区域表示请求报文,黄色区域表示响应报文,白色区域表示请求报文和响应报文中的空行(仅有行分隔符);

    响应报文中空行上部表示状态行与响应报头(以Key-Value的方式表示),空行下部表示响应正文(返回给客户端的文件等内容);

从响应的结果可知,无论是客户端还是服务端都需要互相通一下各自的HTTP协议版本;

通常情况下,不同版本的客户端支持的HTTP协议版本不同,当一个客户端使用HTTP/1.1协议版本的协议向服务端发送请求这也表示客户端也期望接收到的响应也应是HTTP/1.1协议版本的响应;

这种机制的网络通信可以保持稳定有效,避免由于协议不匹配引发潜在的问题;

  • Fiddler

    该工具是一个用于抓包的工具,可以对客户端向服务端发送的请求进行抓包;

    当使用浏览器访问一个Web服务器时实际上是通过浏览器作为客户端直接向Web服务器发送一个请求,而Fiddler工具则是浏览器不直接访问Web服务器,而是将请求交由Fiddler工具,由Fiddler向服务器发起请求,相应的服务器的响应也将先发送回给Fiddler,由Fiddler工具交还给浏览器;

    其中上部分内容为请求,下部分内容为响应;

    红色方框框住的内容分别为请求报文的请求行以及响应报文的状态行;

    该报文的正文部分并不太直观,本质原因是这两个报文实际上采用的是HTTPS协议,所以进行了一定的简单加密;

    换句话说Fiddler工具就相当于一个代理,也是一个Web调试工具;

  • Postman

    该工具是用来构建网络请求的;

    与Fiddler工具不同,Fiddler工具通过代理的方式来获取浏览器的请求,以该工具为浏览器和Web

    服务器的媒介,来发送请求与接收响应;

    Postman工具不同,该工具直接用于与Web服务器进行直接交互而不需要浏览器;

    在这个例子中使用GET方法向www.baidu.com发起请求,其中红色部分为请求部分,蓝色部分为响应部分;


简单的Web服务器

HTTP协议是应用层协议,同样的底层协议可以选择使用TCP或UDP作为底层协议,因此本次在实现时使用文章『 Linux 』协议的定制中 “套接字接口的封装” 中的网络插件(TCP套接字的封装),并使用自定义的日志插件进行服务端日志的打印;

  • 服务器大致框架

    在该版本的HTTP服务器中同样以一个类的形式对服务器进行一个封装;

    将套接字的初始化部分与运行部分进行整合为一个Start()函数;

    在该服务器中使用多线程的方式避免在单执行流情况下出现的阻塞问题;

    /* httpserver.hpp */
    
    class HttpServer
    {
    public:
        HttpServer(uint16_t port = defaultport) : port_(port) {}
    
        ~HttpServer() {}
    
        bool Start()
        {
        }
    
        static void *ThreadRun(void *args)
        {
            // 线程执行
        }
    
    protected:
    private:
        uint16_t port_;
        static const uint16_t defaultport;
        NetSocket listensock_;
    };
    const uint16_t HttpServer::defaultport = 8049;
    

    成员如下:

    • port_

      该成员变量负责服务端需要绑定的端口号;

    • defaultport

      该成员变量为端口号的默认值,进行了初始化默认为8049(便于测试);

    • listensock_

      TCP协议中存在两个套接字,一个套接字用于监听,一个套接字负责与客户端进行通信,用于监听的套接字一般称为监听套接字,该套接字使用的是封装后的TCP套接字,用于监听来自客户端的连接;

    • 构造函数

      构造函数主要负责初始化端口号;

    • Start()函数

      该成员函数主要负责初始化套接字信息,如调用NetSocket::Socket()函数创建套接字,调用NetSocket::Bind()绑定端口号,调用NetSocket::Listen()函数设置监听等操作;

      同时该函数也负责对执行流进行分离,即在调用NetSocket::Accept()函数后创建新线程由新线程执行主要任务,由主线程继续返回监听状态等待下一个来自客户端的连接到来;

    • static void *ThreadRun(void *args)

      这是一个静态成员函数,是线程的执行函数,为了避免成员函数中参数存在一个隐含的this指针导致参数不匹配,在函数前加上static修饰为静态成员函数;

      该函数主要为分离执行流后的新线程处理主要的工作;

  • Start()启动函数

    /* httpserver.hpp */
    
    class HttpServer
    {
    public:
        bool Start()
        {
            listensock_.Socket();
            listensock_.Bind(port_);
            listensock_.Listen();
            for (;;)
            {
                std::string clientip;
                uint16_t clientport;
                int sockfd = listensock_.Accept(&clientip, &clientport);
                pthread_t tid;
                ThreadData *td = new ThreadData(sockfd);
                pthread_create(&tid, nullptr, ThreadRun, td);
            }
        }
    
        static void *ThreadRun(void *args)
        {
        }
    
    protected:
        struct ThreadData
        {
            ThreadData(int sockfd) : sockfd_(sockfd) {}
            int sockfd_;
        };
    };
    const uint16_t HttpServer::defaultport = 8049;
    #endif
    

    该函数初始化TCP套接字需要的创建套接字,绑定端口,设置监听等操作,同时定义了一个内部结构体ThreadData,用于向线程执行函数传入需要进行通信的套接字描述符,同时利用了结构体以便于进行需要传递数据的拓展;

  • ThreadRun()函数

    该函数为线程的入口函数,主要执行一些重要的任务;

    /* httpserver.hpp */
    
    class HttpServer
    {
    public:
        static void *ThreadRun(void *args)
        {
            ThreadData *td = static_cast<ThreadData *>(args);
            pthread_detach(pthread_self());
            char buffer[10240];
            ssize_t n = recv(td->sockfd_, buffer, sizeof(buffer) - 1, 0);
            if (n > 0)
            {
                buffer[n] = 0;
                std::cout << buffer;
            }
    
            close(td->sockfd_);
            delete td;
            return nullptr;
        }
    private:
        uint16_t port_;
        static const uint16_t defaultport;
        NetSocket listensock_;
    };
    const uint16_t HttpServer::defaultport = 8049;
    

    在这个简单的服务器中主要的任务为打印来自客户端的请求,当打印完客户端的请求后服务器将关闭该连接,表示一次服务结束;

在该服务器中,main函数只需要进行Start()函数的调用即可;

/* httpserver.cc */

int main(int argc, char *argv[])
{
    std::unique_ptr<HttpServer> svr(new HttpServer());
    svr->Start();

    return 0;
}

这里使用了智能指针来管理实例的生命周期;

  • 测试

    启动服务器,并使用telnet或浏览器等工具以IP/port的方式向服务器发起请求,服务器将打印对应的请求信息;

    • 使用telnet工具

      使用telnet工具发送的请求被服务器接收并打印;

    • 使用浏览器

      使用浏览器发送的请求被服务器接收并打印,黄色部分为请求的空行部分,该请求不懈怠任何正文;

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

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

相关文章

数据结构和算法|排序算法系列(五)|排序总结(时间复杂度和是否稳定)

文章目录 选择排序冒泡排序插入排序快排归并排序堆排序 选择排序 一句话总结&#xff0c;开启一个循环&#xff0c;每轮从未排序区间选择****最小的元素&#xff0c;将其放到已排序区间的末尾。「未排序区间一般也放在后面&#xff0c;已排序区间放在前面」 选择排序 时间复…

2024蓝桥杯省B好题分析

题解来自洛谷&#xff0c;作为学习 目录 宝石组合 数字接龙 爬山 拔河 宝石组合 # [蓝桥杯 2024 省 B] 宝石组合## 题目描述在一个神秘的森林里&#xff0c;住着一个小精灵名叫小蓝。有一天&#xff0c;他偶然发现了一个隐藏在树洞里的宝藏&#xff0c;里面装满了闪烁着美…

Flutter Android Package调用python

操作步骤 一、创建一个Flutter Package 使用以下指令创建一个Flutter Package flutter create --templateplugin --platformsandroid,ios -a java flutter_package_python 二、修改android/build.gradle文件 在buildscript——>dependencies中添加以下内容 //导入Chaqu…

接口幂等性和并发安全的区别?

目录标题 幂等性并发安全总结 接口幂等性和并发安全是两个不同的概念&#xff0c;虽然它们在设计API时都很重要&#xff0c;但侧重点不同。 幂等性 定义&#xff1a;幂等性指的是无论对接口进行多少次相同的操作&#xff0c;结果都是一致的。例如&#xff0c;HTTP的PUT和DELE…

TON基金会与Curve Finance合作:推出基于TON的新型稳定币互换项目

2024年&#xff0c;TON基金会宣布与去中心化金融&#xff08;DeFi&#xff09;领域的知名协议Curve Finance建立战略合作&#xff0c;携手推出一个全新的基于TON区块链的稳定币交换项目。这一合作标志着TON生态系统在DeFi领域的进一步扩展&#xff0c;并将通过Curve Finance的核…

玖逸云黑系统源码 v1.3.0全解无后门 +搭建教程

功能带有卡密生成和添加黑名单等&#xff0c;反正功能也不是很多具体的自己看程序截图即可。 搭建教程 完成 1.我们先添加一个站点 2.PHP选择7.3 3.上传源码解压 4.导入数据库 5.配置数据库信息config.php 源码下载&#xff1a;https://download.csdn.net/download/m0_6…

Python包管理工具pip 入门

主要参考资料&#xff1a; 全面解析 python 包管理工具 pip: https://blog.csdn.net/maiya_yayaya/article/details/135341026 目录 pypi社区pip工具安装 piprequirements.txt 记录python包管理工具8.1 什么是 requirements.txt8.2 requirements.txt 格式8.3 示例8.4 pip 安装 …

鸿蒙手势交互(二:单一手势)

二、单一手势 有六种&#xff1a;点击手势(TapGesture)、长按手势(LongPressGesture)、拖动手势(PanGesture) 捏合手势(PinchGesture)、旋转手势(RotationGesture)、滑动手势(SwipeGesture) 点击手势(TapGesture) TapGesture(value?:{count?:number, fingers?:number}) /…

7.7opencv中(基于C++) 翻转图像

基本概念 在OpenCV中&#xff0c;翻转图像指的是沿着一个或多个轴翻转图像。OpenCV提供了一个函数 flip 来完成这个任务。这个函数可以沿着水平轴、垂直轴或者同时沿着水平和垂直轴翻转图像。 函数原型 void flip(InputArray src,OutputArray dst,int flipCode );参数说明 •…

vulnhub-prime1

目录 靶场环境解题过程 靶场环境 项目ip靶机&#xff08;prime&#xff09;未知攻击机&#xff08;kali&#xff09;10.128.129.128 解题过程 打开靶机&#xff0c;我们只能看见一个登录界面&#xff0c;上面只有半截提示 我们首先要做的是主机发现&#xff0c;因为是网络适…

使用 HFD 加快 Hugging Face 模型和数据集的下载

Hugging Face 提供了丰富的预训练模型和数据集&#xff0c;而且使用 Hugging Face 提供的 from_pretrained() 方法可以轻松加载它们&#xff0c;但是&#xff0c;模型和数据集文件通常体积庞大&#xff0c;用默认方法下载起来非常花时间。 本文将指导你如何使用 HFD&#xff08…

makefile 的语法(9):函数 file foreach

&#xff08;57&#xff09; 之前学了处理文本的函数&#xff0c;处理文件名的函数&#xff0c;现在学习读取文件的函数 file &#xff1a; &#xff08;58&#xff09;可以对文本中每一项进行函数处理的 foreach &#xff1a; &#xff08;59&#xff09; &#xff08;60&…

路由原理介绍

定义与过程 定义&#xff1a;是指导IP报文发送的路径信息 过程&#xff1a; 检查数据包的目的地确定信息源发现可能的路径选择最佳路径验证和维护路由信息 路由来源 直连路由&#xff1a;不需配置&#xff0c;路由器配置IP后自动生效 静态路由&#xff1a;手动配置 ip r…

小商品市场配电系统安全用电解决方案

1.概述 随着市场经济的快速发展和人民生活水平的不断提高,全国各地相继建起了大批大型小商品批发市场,此类市场以其商品种类繁多、价格实惠、停车方便等特点吸引了大量的顾客,成为人们日常光顾的重要场所,地方便了广大人民群众的日常生活。 小商品市场集商品销售和短时货物储…

分享一个 在线拍卖系统 商品竞拍平台Java、python、php三个技术版本(源码、调试、LW、开题、PPT)

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人 八年开发经验&#xff0c;擅长Java、Python、PHP、.NET、Node.js、Android、微信小程序、爬虫、大数据、机器学习等&#xff0c;大家有这一块的问题可以一起交流&…

【zookeeper安装】zookeeper安装详细教程(单机/集群部署)(linux版)

文章目录 前言一、zookeeper简介二、获取Zookeeper安装包2.1. 离线获取2.2. 在线获取2.3. 解压包 三、单机部署3.1. 配置conf文件3.2. 启动服务 四、集群部署4.1. 概念4.2. 配置conf文件4.3. 创建myid文件4.3. 启动每个节点的zookeeper服务 五、配置systemctl管理&#xff08;选…

neo4j:ubuntu环境下的安装与使用

一、neo4j安装 1. 下载安装包 进入网站&#xff1a;https://neo4j.com/deployment-center/#community 在上图中选择下载即可&#xff08;社区版免费&#xff09; 注意&#xff1a;neo4j的版本要和电脑安装的jdk版本对应&#xff0c;jdk版本使用java --version查看&#xff1a;…

不得不说 Sam‘s Club 的数字化做得挺好

因正好有东西要退货就顺便看了下订单如何退货。 但发现 Sam’s Club 的所有交易都能够从后台查到&#xff0c;同时还提供了个 CSV 文件的下载。 打开下载文件就能看到全部的数字化的交易记录。 就拿加油这个事情来说&#xff0c;能够非常清楚这一年在 Sam’s Club 加油多少加…

【docker】命令之容器操作

一、前言 在上篇博客介绍了关于如何从应用市场&#xff0c;下载镜像后&#xff0c;对镜像的相关操作了。这篇博客呢我们就要讲解我们把镜像下载下来了&#xff0c;启动这个镜像后&#xff0c;就是我们说的容器了&#xff0c;那么容器的具体操作又有那些呢&#xff1f; 二、容器…

基于深度学习的眼部疾病检测识别系统

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长 QQ 名片 :) 1. 项目简介 眼部疾病的早期诊断对于防止视力下降乃至失明至关重要。然而&#xff0c;专业的医疗资源分布不均&#xff0c;尤其是在偏远地区&#xff0c;人们很难获得专业的眼科医生提供的及时诊断服务。本系统…