网络协议栈--应用层--HTTP协议

目录

  • 本节重点
    • 理解应用层的作用, 初识HTTP协议
  • 一、应用层
  • 二、HTTP协议
    • 2.1 认识URL
    • 2.2 urlencode和urldecode
    • 2.3 HTTP协议格式
    • 2.4 HTTP的方法
    • 2.4 HTTP的状态码
    • 2.5 HTTP常见的Header属性
  • 三、最简单的HTTP服务器
    • 3.1 HttpServer.hpp
    • 3.2 HttpServer.cc
    • 3.3 HttpClient.cc
    • 3.4 log.hpp
    • 3.5
    • 3.6 makefile
    • 3.7 wwwroot目录下的资源
  • 四、HTTP协议内容一览图

本节重点

理解应用层的作用, 初识HTTP协议

一、应用层

程序员们写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层。

二、HTTP协议

虽然说, 应用层协议是程序员自己定的.
但实际上, 已经有大佬们定义了一些现成的, 又非常好用的应用层协议, 供我们直接参考使用. HTTP(超文本传输协议)就是其中之一。

2.1 认识URL

平时我们俗称的 “网址” 其实就是说的 URL。
在这里插入图片描述

2.2 urlencode和urldecode

像 / ? : 等这样的字符, 已经被url当做特殊意义理解了。 因此这些字符不能随意出现.比如, 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义。
转义的规则如下:
将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式。
在这里插入图片描述
“+” 被转义成了 “%2B”
urldecode就是urlencode的逆过程;

2.3 HTTP协议格式

HTTP请求:
在这里插入图片描述
(1)请求行: [请求方法] + [url] + [HTTP版本]。

(2)请求报头(Header): 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;

(3)空行:遇到单单一个\n空行表示Header部分结束.

(4)请求正文: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度;

HTTP响应:
在这里插入图片描述
(1)状态行: [HTTP版本] + [状态码] + [状态码描述]
(2)响应报头(Header): 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;
(3)空行:遇到单单一个\n空行表示Header部分结束。
(4)响应正文:空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度; 如果服务器返回了一个html页面, 那么html页面内容就是在body中。

2.4 HTTP的方法

在这里插入图片描述
其中最常用的就是GET方法和POST方法。

2.4 HTTP的状态码

在这里插入图片描述
最常见的状态码, 比如 200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定向), 504(Bad Gateway)

2.5 HTTP常见的Header属性

(1)Content-Type: 数据类型(text/html等)
(2)Content-Length: Body的长度
(3)Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;
(4)User-Agent: 声明用户的操作系统和浏览器版本信息;
(5)referer: 当前页面是从哪个页面跳转过来的;
(6)location: 搭配3xx状态码使用, 告诉客户端接下来要去哪里访问;
(7)Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能;

三、最简单的HTTP服务器

实现一个最简单的HTTP服务器, 只在网页上输出 “hello world”; 只要我们按照HTTP协议的要求构造数据, 就很容易能做到;
我们以下的服务器是添加了一点前端的代码的:

3.1 HttpServer.hpp

#pragma once#include <iostream>
#include "Socket.hpp"
#include <functional>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#include <unordered_map>// HTTP协议可以以"\r\n"作为行分隔符,也可以是"\n"
const std::string sep = "\r\n";// 传说中的web根目录
const std::string wwwroot = "./wwwroot";// 服务器主页
const std::string homepage = "index.html";const uint16_t DEFAULT_PORT = 8080;class HttpServer;class ThreadData
{
public:ThreadData(int sockfd, HttpServer *svr): _sockfd(sockfd), _svr(svr){}public:int _sockfd;HttpServer *_svr;
};class Request
{
public:// 反序列化void Deserialize(std::string req){while (true){// 用行分隔符"\r\n",分割请求报头中的属性信息auto pos = req.find(sep);if (pos == string::npos){break;}std::string tmp = req.substr(0, pos);if (tmp.empty()){// 说明读取到了空行,即报头已经读完了req.erase(pos, sep.size());break;}_req_head.push_back(tmp);// 没获取一条属性信息记得在原报头中删除req.erase(0, pos + sep.size());}// 取出报头之后得到的剩余的就是请求正文_body = req;}// 解析http协议void Parse(){std::string req_line = _req_head[0];//stringstream默认是以空格作为分隔符的std::stringstream ss(req_line);//必须按顺序ss >> _method >> _url >> _http_version;// 拼接资源的路径_filepath = wwwroot;if (_url == "/" || _url == "/index.html"){_filepath += "/";_filepath += homepage;}else{_filepath += _url;}//从后往前找到'.',从而找出后缀auto pos = _filepath.rfind('.');if (pos == string::npos){_suffix = ".html";}else{_suffix = _filepath.substr(pos);}}//用来调试的void DebugPrint(){for (auto &line : _req_head){std::cout << "--------------------------------" << std::endl;std::cout << line << "\n\n";}std::cout << "method: " << _method << std::endl;std::cout << "url: " << _url << std::endl;std::cout << "http_version: " << _http_version << std::endl;std::cout << "file_path: " << _filepath << std::endl;std::cout << _body << std::endl;}std::string GetFilePath(){return _filepath;}std::string GetFileSuffix(){return _suffix;}private:std::string _method;                     //请求方法std::string _url;                        //请求的资源的urlstd::string _http_version;               //http协议版本号std::vector<std::string> _req_head;      //请求报头:包括请求行、报头属性信息std::string _body;                       //请求正文std::string _filepath;                   //文件的路径,从web根目录开始,即wwwroot/urlstd::string _suffix;                     //url的后缀
};class HttpServer
{
public:HttpServer(const uint16_t &port = DEFAULT_PORT): _port(port){//.html对应的网页是文本类型_content_type[".html"] = "text/html";//  _content_type[".html"] = "application/json";//.png对应的网页是图片_content_type[".png"] = "image/png";}~HttpServer(){}void InitHttpServer(){// 1、创建套接字_listen_sock.Socket();// 2、绑定_listen_sock.Bind(_port);// 3、监听_listen_sock.Listen();}static string ReadIndexHtml(const std::string &path){// 有坑?std::string str;//注意,这里一定要用二进制的方式去读取,否则类似于图片的文件就有可能会读取出//错从而在访问的时候看不到图片ifstream in(path, std::ios::binary);if (!in.is_open()){return "";}//对于seekg函数,0表示偏移量,std::ios_base::end是基准,//意思是:设置当前位置相对于最后一个位置的偏移量是0,//说明当前指针的位置就指向文件内容的最后一个位置in.seekg(0, std::ios_base::end);//对于tellg函数,是获取当前读取位置的,因为上面已经设置了//当前指针指向的位置是文件内容的最后一个位置,所以当前位置//的数值就等于文件内容的大小,即一共有len个自己的内容int len = in.tellg();//同上,即设置指针指向文件的开始位置in.seekg(0, std::ios_base::beg);std::string content;content.resize(len);//把内容读取到content中in.read((char *)content.c_str(), len);in.close();return content;}//url的后缀转换成对应的后缀描述,用于构建响应std::string SuffixToDesc(const std::string &suffix){auto ret = _content_type.find(suffix);if (ret == _content_type.end()){//如果从类型中没有找到,那就统一当成是.html后缀//返回对应的文本类型的描述"text/html"return _content_type[".html"];}else{return _content_type[suffix];}}static void *Handler(void *args){//线程分离pthread_detach(pthread_self());ThreadData *ptd = static_cast<ThreadData *>(args);int sockfd = ptd->_sockfd;while (true){char buffer[10240] = {0};ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);if (n > 0){buffer[n] = '\0';std::cout << buffer << std::endl;//解析http协议请求Request req;req.Deserialize(buffer);req.Parse();req.DebugPrint();// 构建http响应std::string response;bool ok = true;//根据请求的url,读取对应的文件内容作为响应的正文std::string response_body = ReadIndexHtml(req.GetFilePath());if (response_body.empty()){ok = false;std::string err_html = wwwroot;err_html += '/';err_html += "err.html";response_body = ReadIndexHtml(err_html);}// 状态行std::string state_line;if (ok){state_line = "HTTP/1.0 200 OK\r\n";}else{state_line = "HTTP/1.0 404 Not Found\r\n";}response += state_line;//做重定向的状态行//response = "HTTP/1.0 302 Found\r\n";// 响应报头std::string response_head;//属性1:content-lenthstd::string content_lenth = "Content-Lenth: ";content_lenth + to_string(response_body.size());//属性2:content-typestd::string content_type = "Content-Type: ";content_type += ptd->_svr->SuffixToDesc(req.GetFileSuffix());response_head += content_lenth;response_head += "\r\n";response_head += content_type;response_head += "\r\n";//属性3:set cookieresponse_head += "Set-Cookie: name=kobe&&passwd=123456";response_head += "\r\n";//属性4:Location,设置重定向时需要访问的网址// response_head += "Location: http://www.qq.com/";// response_head += "\r\n";response += response_head;// 空行response += "\r\n";// 正文response += response_body;ssize_t n = send(sockfd, response.c_str(), response.size(), 0);}else if (n == 0){log(Info, "client quit...,关闭连接:%d", sockfd);close(sockfd);return nullptr;}else if (n < 0){std::cout << "n=" << n << std::endl;log(Error, "recv error,关闭连接:%d", sockfd);close(sockfd);return nullptr;}}log(Info, "服务器退出,关闭连接:%d", sockfd);close(sockfd);return nullptr;}void Start(){while (true){std::string client_ip;uint16_t client_port;int sockfd = _listen_sock.Accept(client_ip, client_port);if (sockfd < 0){continue;}pthread_t tid;ThreadData td(sockfd, this);// 创建线程pthread_create(&tid, nullptr, Handler, (void *)(&td));}}private:Sock _listen_sock;uint16_t _port;std::unordered_map<std::string, std::string> _content_type;
};

3.2 HttpServer.cc

#include "HttpServer.hpp"
#include <memory>void Usage(const std::string& proc)
{std::cout<<"\t\n"<<std::endl;std::cout<<"Usage: "<<proc<<" server_port[>=1024]"<<std::endl<<std::endl;
}int main(int argc, char* argv[])
{if(argc!=2){Usage(argv[0]);exit(1);}uint16_t server_port=(uint16_t)stoi(argv[1]);std::unique_ptr<HttpServer> svr(new HttpServer(server_port));svr->InitHttpServer();svr->Start();return 0;
}

3.3 HttpClient.cc

提示一下:如果直接用HttpClient.cc访问服务器,那么需要按照HTTP协议的标准格式构建报文才能正确地返回,否则服务器会发生段错误的。

#include "HttpServer.hpp"
#include "Socket.hpp"int main()
{Sock sock;sock.Socket();sock.Connect("43.138.156.240",8081);string buffer;while(true){std::cout<<"Please Enter:";std::getline(std::cin,buffer);send(sock.Sockfd(),buffer.c_str(),buffer.size(),0);sleep(1);char tmp[10240]={0};recv(sock.Sockfd(),tmp,sizeof(tmp),0);std::cout<<tmp<<std::endl;}return 0;
}

3.4 log.hpp

#pragma once#include <iostream>
using namespace std;
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string>
#include <time.h>
#include <stdarg.h>// 日志等级
#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4#define Screen 1
#define OneFile 2
//向多个文件打印
#define Classfile 3
#define SIZE 1024#define LogFile "log.txt"class Log
{
public:Log(){printMethod = Screen;path = "./log/";}void Enable(int mothod){printMethod = mothod;}string LevelToString(int level){switch (level){case Info:{return "Info";}case Debug:{return "Debug";}case Warning:{return "Warning";}case Error:{return "Error";}case Fatal:{return "Fatal";}default:{return "None";}}}void printlog(int level,const string& logtxt){switch(printMethod){case Screen:{cout<<logtxt<<endl;break;}case OneFile:{PrintOneFile(LogFile,logtxt);break;}case Classfile:{PrintClassfile(level,logtxt);break;}default:{break;}}}void PrintOneFile(const string& logname,const string& logtxt){string _logname=path+logname;int fd=open(_logname.c_str(),O_WRONLY|O_CREAT|O_APPEND,0666);if(fd<0){perror("open fail");return;}write(fd,logtxt.c_str(),logtxt.size());close(fd);}void PrintClassfile(int level,const string& logtxt){string filename=LogFile;filename+='.';filename+=LevelToString(level);PrintOneFile(filename,logtxt);}void operator()(int level,const char* format,...){time_t t=time(nullptr);struct tm* ctime=localtime(&t);char leftbuffer[SIZE];snprintf(leftbuffer,SIZE,"[%s][%d-%d-%d %d:%d:%d]",LevelToString(level).c_str(),ctime->tm_year+1900,ctime->tm_mon+1,ctime->tm_mday,ctime->tm_hour,ctime->tm_min,ctime->tm_sec);va_list s;va_start(s,format);char rightbuffer[SIZE]={0};vsnprintf(rightbuffer,SIZE,format,s);va_end(s);char logtxt[SIZE*2];snprintf(logtxt,sizeof(logtxt),"%s %s",leftbuffer,rightbuffer);printlog(level,logtxt);}~Log(){}private:// 打印方法int printMethod;string path;
};//定义一个全局的log
Log log;

3.5

#pragma once#include <iostream>
using namespace std;
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "log.hpp"
#include <unistd.h>
#include <strings.h>
#include <cstring>
#include <string>int backlog = 10;enum
{SockErr = 2,BindErr,ListenErr,ConnectErr,
};class Sock
{
public:Sock(): _sockfd(-1){}~Sock(){if(_sockfd>0){close(_sockfd);}}// 创建套接字void Socket(){_sockfd = socket(AF_INET, SOCK_STREAM, 0);if (_sockfd < 0){log(Fatal, "socket failed,errno:%d,errstring:%s", errno, strerror(errno));exit(SockErr);}int opt=1;if(setsockopt(_sockfd,SOL_SOCKET,SO_REUSEADDR|SO_REUSEPORT,&opt,sizeof(opt))<0){log(Warning, "setsockopt failed, sockfd:%d", _sockfd);}log(Info, "setsockopt successed, sockfd:%d", _sockfd);log(Info, "socket successed, sockfd:%d", _sockfd);}// 绑定void Bind(const uint16_t &serverPort){struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(serverPort);local.sin_addr.s_addr = INADDR_ANY;if (bind(_sockfd, (struct sockaddr *)(&local), sizeof(local)) < 0){log(Fatal, "bind failed,errno:%d,errstring:%s", errno, strerror(errno));exit(BindErr);}log(Info, "bind successed...");}// 监听void Listen(){if (listen(_sockfd, backlog) < 0){log(Fatal, "set listen state failed,errno:%d,errstring:%s", errno, strerror(errno));exit(ListenErr);}log(Info, "set listen state successed");}//获取连接int Accept(string& clientip,uint16_t& clientport){struct sockaddr_in client;socklen_t len=sizeof(client);bzero(&client,sizeof(client));int sockfd=accept(_sockfd,(struct sockaddr*)(&client),&len);if(sockfd<0){log(Warning, "accept new link failed,errno:%d,errstring:%s", errno, strerror(errno));return -1;}log(Info,"accept a new link...,sockfd:%d",sockfd);clientip=inet_ntoa(client.sin_addr);clientport=(uint16_t)(ntohs(client.sin_port));return sockfd;}// 连接void Connect(const string &serverIp, const uint16_t &serverPort){struct sockaddr_in server;bzero(&server, sizeof(server));server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(serverIp.c_str());server.sin_port = htons(serverPort);if (connect(_sockfd, (struct sockaddr *)(&server), sizeof(server)) < 0){log(Fatal, "connect server failed,errno:%d,errstring:%s", errno, strerror(errno));exit(ConnectErr);}log(Info, "connect server succeeded...");}void Close(){if(_sockfd>0){close(_sockfd);}}int Sockfd(){return _sockfd;}private:int _sockfd;
};

3.6 makefile

.PHONY:all
all:http_server http_clienthttp_server:HttpServer.ccg++ -o $@ $^ -std=c++11 -lpthread http_client:HttpClient.ccg++ -o $@ $^ -std=c++11 .PHONY:clean
clean:rm -f http_server http_client

3.7 wwwroot目录下的资源

自行创建wwwroot目录并把资源按要求放到目录下:
web根目录下的资源

在这里插入图片描述
备注:
此处我们使用 8081 端口号启动了HTTP服务器. 虽然HTTP服务器一般使用80端口,但这只是一个通用的习惯. 并不是说HTTP服务器就不能使用其他的端口号.使用Edge测试我们的服务器时, 可以看到服务器打出的请求中还有一个
GET /favicon.ico HTTP/1.1 这样的请求。favicon.ico是用来设置网页上的小图标的:
在这里插入图片描述

可以试试把返回的状态码改成404, 403, 504等, 看浏览器上分别会出现什么样的效果。

四、HTTP协议内容一览图

在这里插入图片描述

以上就是今天想要跟大家分享的关于HTTP协议的所有内容了,你学会了吗?如果感觉到有所收获的话,那就点点小心心,再点点关注呗,后期还会持续更新有关Linux网络编程的相关知识哦,我们下期见!!!!!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

5G智能制造热力工厂数字孪生可视化平台,推进热力行业数字化转型

5G智能制造热力工厂数字孪生可视化平台&#xff0c;推进热力行业数字化转型。在当今这个信息化、数字化的时代&#xff0c;热力生产行业也迎来了转型的关键时刻。为了提升生产效率、降低成本、提高产品质量&#xff0c;越来越多的热力生产企业开始探索数字化转型之路。而5G智能…

备份 ChatGPT 的聊天纪录

备份 ChatGPT 的聊天纪录 ChatGPT 在前阵子发生了不少次对话纪录消失的情况&#xff0c;让许多用户觉得困扰不已&#xff0c;也担心自己想留存的聊天记录消失不见。 好消息是&#xff0c;OpenAI 在 2023 年 4 月 11 日推出了 ChatGPT 聊天记录备份功能&#xff0c;无论是免费…

Flink并行度

1、Task flink中每个算子就是一个Task&#xff0c;比如flatMap、map、sum是一个Task。 2、SubTask 算子有几个并行度SubTask的数量就是几&#xff0c;比如 3、算子并行度 算子并行度指的是每个算子的并行度&#xff0c;可用env.setParallelism(1);设置所有算子的并行度&am…

微服务架构 | 多级缓存

INDEX 通用设计概述2 优势3 最佳实践 通用设计概述 通用设计思路如下图 内容分发网络&#xff08;CDN&#xff09; 可以理解为一些服务器的副本&#xff0c;这些副本服务器可以广泛的部署在服务器提供服务的区域内&#xff0c;并存有服务器中的一些数据。 用户访问原始服务器…

内联函数|auto关键字|范围for的语法|指针空值

文章目录 一、内联函数1.1概念1.2特性 二、auto关键字2.2类型别名思考2.3auto简介2.4auto使用细则2.4 auto不能推导的场景 三、基于范围的for循环(C11)3.1 范围for的语法 四、指针空值nullptr(C11)4.1 C98中的指针空值 所属专栏:C初阶 一、内联函数 1.1概念 以inline修饰的函…

【Spring云原生系列】Spring RabbitMQ:异步处理机制的基础--消息队列 原理讲解+使用教程

&#x1f389;&#x1f389;欢迎光临&#xff0c;终于等到你啦&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;持续更新的专栏《Spring 狂野之旅&#xff1a;从入门到入魔》 &a…

JAVA虚拟机实战篇之内存调优[4](内存溢出问题案例)

文章目录 版权声明修复问题内存溢出问题分类 分页查询文章接口的内存溢出问题背景解决思路问题根源解决思路 Mybatis导致的内存溢出问题背景问题根源解决思路 导出大文件内存溢出问题背景问题根源解决思路 ThreadLocal占用大量内存问题背景问题根源解决思路 文章内容审核接口的…

2024 GoLand激活,分享几个GoLand激活的方案

文章目录 GoLand公司简介我这边使用GoLand的理由GoLand 最新变化GoLand 2023.3 最新变化AI Assistant 正式版GoLand 中的 AI Assistant&#xff1a;_Rename_&#xff08;重命名&#xff09;GoLand 中的 AI Assistant&#xff1a;_Write documentation_&#xff08;编写文档&…

【工具】Raycast – Mac提效工具

引入 以前看到同事们锁屏的时候&#xff0c;不知按了什么键&#xff0c;直接调出这个框&#xff0c;然后输入lock屏幕就锁了。 跟我习惯的按Mac开机键不大一样。个人觉得还是蛮炫酷的&#xff5e; 调研 但是由于之前比较繁忙&#xff0c;这件事其实都忘的差不多了&#xff0…

C# Winform画图绘制圆形

一、因为绘制的圆形灯需要根据不同的状态切换颜色,所以就将圆形灯创建为用户控件 二、圆形灯用户控件 1、创建用户控件UCLight 2、设值用户控件大小(30,30)。放一个label标签,AutoSize为false(不自动调整大小),Dock为Fill(填充),textaglign为居中显示。 private Color R…

ReentrantLock

文章目录 ReentrantLockReentrantLock 是什么&#xff1f;公平锁和非公平锁有什么区别&#xff1f;synchronized 和 ReentrantLock 有什么区别&#xff1f;两者都是可重入锁synchronized 依赖于 JVM 而 ReentrantLock 依赖于 APIReentrantLock 比 synchronized 增加了一些高级功…

RabbitMQ的web控制端介绍

2.1 web管理界面介绍 connections&#xff1a;无论生产者还是消费者&#xff0c;都需要与RabbitMQ建立连接后才可以完成消息的生产和消费&#xff0c;在这里可以查看连接情况channels&#xff1a;通道&#xff0c;建立连接后&#xff0c;会形成通道&#xff0c;消息的投递、获取…

Chrome安装Axure插件

打开原型目录/resources/chrome&#xff0c;重命名axure-chrome-extension.crx&#xff0c;修改后缀为rar&#xff0c;axure-chrome-extension.rar 解压到axure-chrome-extension目录打开Chrome&#xff0c;更多工具->扩展程序&#xff0c;打开开发者模式&#xff0c;选择加…

支持向量机 SVM | 线性可分:软间隔模型

目录 一. 软间隔模型1. 松弛因子的解释小节 2. SVM软间隔模型总结 线性可分SVM中&#xff0c;若想找到分类的超平面&#xff0c;数据必须是线性可分的&#xff1b;但在实际情况中&#xff0c;线性数据集存在少量的异常点&#xff0c;导致SVM无法对数据集线性划分 也就是说&…

uniapp踩坑之项目:uni.previewImage简易版预览单图片

主要使用uni.previewImage //html <view class"box-card" v-for"(item,index) in DataList" :key"index"><view>图片&#xff1a;</view><image :src"item.Path" tap.stop"clickImg(item.Path)">&l…

BUUCTF---[MRCTF2020]你传你呢1

1.题目描述 2.打开题目链接 3.上传shell.jpg文件&#xff0c;显示连接成功&#xff0c;但是用蚁剑连接却连接不上。shell文件内容为 <script languagephp>eval($_REQUEST[cmd]);</script>4.用bp抓包&#xff0c;修改属性 5.需要上传一个.htaccess的文件来把jpg后缀…

#QT(串口助手-界面)

1.IDE&#xff1a;QTCreator 2.实验&#xff1a;编写串口助手 3.记录 接收框:Plain Text Edit 属性选择&#xff1a;Combo Box 发送框:Line Edit 广告&#xff1a;Group Box &#xff08;1&#xff09;仿照现有串口助手设计UI界面 &#xff08;2&#xff09;此时串口助手大…

O2OA(翱途)开发平台如何在流程表单中使用基于Vue的ElementUI组件?

本文主要介绍如何在O2OA中进行审批流程表单或者工作流表单设计&#xff0c;O2OA主要采用拖拽可视化开发的方式完成流程表单的设计和配置&#xff0c;不需要过多的代码编写&#xff0c;业务人员可以直接进行修改操作。 在流程表单设计界面&#xff0c;可以在左边的工具栏找到Ele…

第三百九十回

文章目录 1. 概念介绍2. 思路与方法2.1 实现思路2.2 实现方法 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何把异步的CallBack转换成事件流"相关的内容&#xff0c;本章回中将介绍如何延时处理数据.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介…

[递归、搜索、回溯]----递归

前言 作者&#xff1a;小蜗牛向前冲 专栏&#xff1a;小蜗牛算法之路 专栏介绍&#xff1a;"蜗牛之道&#xff0c;攀登大厂高峰&#xff0c;让我们携手学习算法。在这个专栏中&#xff0c;将涵盖动态规划、贪心算法、回溯等高阶技巧&#xff0c;不定期为你奉上基础数据结构…