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

【Linux网络编程】应用层:HTTP协议 | URL | 简单实现一个HTTP服务器 | 永久重定向与临时重定向


前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站


在这里插入图片描述

🌈个人主页: 南桥几晴秋
🌈C++专栏: 南桥谈C++
🌈C语言专栏: C语言学习系列
🌈Linux学习专栏: 南桥谈Linux
🌈数据结构学习专栏: 数据结构杂谈
🌈数据库学习专栏: 南桥谈MySQL
🌈Qt学习专栏: 南桥谈Qt
🌈菜鸡代码练习: 练习随想记录
🌈git学习: 南桥谈Git

🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈
本科在读菜鸡一枚,指出问题及时改正


文章目录

  • 前言
  • HTTP协议
  • URL
    • urlencode 和 urldecode(了解)
  • HTTP协议请求与响应格式
  • 简单实现一个HTTP服务器
    • HTTP请求及反序列化
      • HTTP请求的基本框架
      • 封装反序列化
      • 获取每一行:Getline
      • 进一步解析请求行
      • 进一步解析请求报头
      • 测试
    • HTTP响应及序列化
      • HTTP响应基本框架
      • 封装序列化
      • 使用telnet进行抓包
      • 完整代码
      • 测试
      • 为什么只访问首页时但是依然有下面的图片
    • 理解网站页面跳转
    • HTTP 常见 Header
      • 关于connection报头
    • HTTP的状态码
    • 永久重定向与临时重定向
    • HTTP请求方法
      • HTTP常见方法
        • GET方法
        • POST方法
    • 完整HTTP代码



前言

实现一个简单的HTTP服务器点击链接获取源码

在这里插入图片描述

在这里插入图片描述

HTTP协议

应用层有很多协议,其中HTTP协议就是其中一个,HTTP协议(超文本传输协议)也是较为重要的一个协议。

在互联网世界中, HTTP(HyperText Transfer Protocol, 超文本传输协议) 是一个至关重要的协议。 它定义了客户端(如浏览器) 与服务器之间如何通信, 以交换或传输超文本(如 HTML 文档)。

HTTP 协议是客户端与服务器之间通信的基础。 客户端通过 HTTP 协议向服务器发送请求, 服务器收到请求后处理并返回响应。 HTTP 协议是一个无连接、 无状态的协议, 即每次请求都需要建立新的连接, 且服务器不会保存客户端的状态信息。

URL

说到URL大家可能不熟悉,但是说到“网址”大家就一目了然,实际上,“网址”就是URL。

在这里插入图片描述

在这里插入图片描述

  • 域名会自动转化成IP地址,称为DNS
  • 协议名称和端口号是强关联的,在浏览器这个网址中将端口号默认忽略了,当浏览器发起请求时会自动拼接端口号80(指明端口)

HTTP做的工作:用户在网络上获取资源(图片、文本、视频等)时是在服务器端获取,所有的资源都是在服务器端。通过某种协议(如http协议或者https协议)来标识用户所需要的资源,然后返回给用户。

我们知道Linux适合做后端开发,那么这些服务器端的资源是在Linux操作系统中。而Linux操作系统一切皆文件,必须得找到对应的资源,需要通过路径来标识,因此在URL后半部分就是路径。

因此,URL就是统一资源定位符,域名+文件路径,标识互联网中唯一的文件资源。

urlencode 和 urldecode(了解)

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

在这里插入图片描述

HTTP协议请求与响应格式

HTTP请求

在HTTP请求中,格式如下:

在这里插入图片描述

  • 首行:称之为请求行,格式为 方法(POST、GET、HEAD…)+URI(即URL后半部分—>用户访问资源的路径)+版本号

  • HTTP的方法:
    在这里插入图片描述

HTTP响应

在HTTP请求中,格式如下:

在这里插入图片描述

  • 这里的DATA是用户需要的资源内容:网页,图片的二进制、视频的二进制、音频的二进制等

HTTP中如何将报头和有效载荷进行分离(封装)? 通过空行即\r\n

如何HTTP的请求和响应读到一个完整的报头?通过换行符可以知道读到一个完整的报文。

如果请求和响应中包含了正文(DATA),如何保证读到一个完整的报头?怎么知道正文部分的长度?在之前的网络版本计算器中只有一个字段,拥有有效载荷长度的字段。在HTTP的请求和应答中报头属性中有一个公共属性叫做Content-Length: xxx,无论是请求还是响应,如果有正文部分,那么这个字段一定包含,这样就能读到一个完整的报文。

简单实现一个HTTP服务器

HTTP请求及反序列化

HTTP请求的基本框架

在HTTP请求中,需要基本的属性(请求行、请求报头、空行、请求正文)以及进一步显示出具体属性(使用的方法、使用的URL、使用的HTTP版本、请求报头以KV形式显示)

class HttpRequest
{
public:HttpRequest():_blank_line(base_sep) {}void Deserialize(std::string &reqstr){}~HttpRequest() {}
private:// 请求的基本格式std::string _req_line;std::vector<std::string> _req_handers;std::string _blank_line;std::string _body_text;//具体属性std::string _method;std::string _url;std::string _version;std::unordered_map<std::string,std::string> _headers_kv;
};

封装反序列化

在反序列化中:

  1. 首先需要通过Getlinereqstr中获取请求行
  2. 获取请求报头:通过一个 do-while 循环来读取所有的请求头部信息。每次循环调用 Getline(reqstr) 获取一行数据,并将它存储在 header
  3. 获取请求正文
  4. 进一步解析请求行和请求头
void Deserialize(std::string &reqstr)
{// 基本的反序列化_req_line=Getline(reqstr);std::string header;do{header=Getline(reqstr);if(header.empty()) break;else if(header==base_sep) break;_req_handers.push_back(header);}while(true);if(!reqstr.empty())_body_text=reqstr;//进一步反序列化ParseReqLine();ParseReqHeader();
}

获取每一行:Getline

寻找base_sep的位置,如果找不到说明没有分隔符,是一个空串;如果找到了base_sep就分割从0base_sep之间的字符串。然后更新reqstr,即删除已经提取的部分。如果 line 是空字符串(即没有有效的行内容),则返回 base_sep,表示一个空行或分隔符。否则,返回提取的有效行内容 line

const static std::string base_sep="\r\n";std::string Getline(std::string &reqstr)
{auto pos=reqstr.find(base_sep);if(pos==std::string::npos) return std::string();std::string line=reqstr.substr(0,pos);reqstr.erase(0,line.size()+base_sep.size());return line.empty()?base_sep:line;
}

进一步解析请求行

解析一个请求行,并将解析的结果存储到类的成员变量 _method_url _version 中。

代码通过 std::stringstream 提供的流提取操作符 (>>) 来从 ss 中依次提取数据并赋值给 _method_url _version

  • 第一个 >> _method:将从流中提取的第一个单词(如 GET)赋值给 _method。通常情况下,_method 代表 HTTP 请求的方法(例如 GET、POST 等)。
  • 第二个 >> _url:提取流中的第二个单词(如 /index.html),并将其赋值给 _url。这个字符串通常代表请求的 URL 路径。
  • 第三个 >> _version:提取流中的第三个单词(如 HTTP/1.1),并将其赋值给 _version。这个字符串代表请求的 HTTP 版本。
void ParseReqLine()
{std::stringstream ss(_req_line);ss>>_method>>_url>>_version;
}

进一步解析请求报头

未解析的解析报头是以: 分割的,因此这里使用: 作为分隔符。

将每个请求头的键值对存储到 _headers_kv 容器中。它通过遍历 _req_handers,提取出每个请求头的键和值,并将它们插入到一个键值对容器中。

const static std::string line_sep=": ";void ParseReqHeader(){for(auto &header:_req_handers){auto pos=header.find(line_sep);if(pos==std::string::npos) continue;std::string k=header.substr(0,pos); // [)std::string v=header.substr(pos+line_sep.size());if(k.empty()||v.empty()) continue;_headers_kv.insert(std::make_pair(k,v));}}

测试

在这里插入图片描述


HTTP响应及序列化

HTTP响应基本框架

class HttpResponse
{
public:HttpResponse():_version(httpversion),_blank_line(base_sep){}void AddCode(int code){_status_code=code;_desc="OK";}void AddHeader(const std::string &k,const std::string &v){_headers_kv[k]=v;}void AddBodyText(const std::string body_text){_resp_body_text=body_text;}std::string Serialize(){}~HttpResponse(){}private:// 基本属性std::string _version;int _status_code;std::string _desc;std::unordered_map<std::string,std::string> _headers_kv;// 响应的基本格式std::string _status_line;std::vector<std::string> _resp_handers;std::string _blank_line;std::string _resp_body_text;
};

封装序列化

在HTTP响应序列化中:

  1. 构建状态行,并将其存储在 _status_line 字符串中
  2. 构建响应头,遍历 _headers_kv 容器,它是一个存储 HTTP 响应头的键值对容器。每一行头部字符串会被 push_back _resp_handers 容器中,最终该容器保存了所有响应头的字符串
  3. 正式序列化,将之前构建的各部分拼接成最终的响应报文字符串 responsestr
std::string Serialize()
{// 构建状态行_status_line=_version+spacesep+std::to_string(_status_code)+spacesep+_desc+base_sep;// 构建应答报头for(auto &hander:_headers_kv){std::string header_line=hander.first+line_sep+hander.second+base_sep;_resp_handers.push_back(header_line);}// 空行和正文// 空行已经初始化,正文已经保存在_resp_body_text中// 正式序列化std::string responsestr=_status_line;for(auto &line:_resp_handers){responsestr+=line;}responsestr+=_blank_line;responsestr+=_resp_body_text;return responsestr;
}

使用telnet进行抓包

telnet 127.0.0.1 8888
在这里插入图片描述

通过这个测试,得到响应

完整代码

class HttpResponse
{
public:HttpResponse():_version(httpversion),_blank_line(base_sep){}void AddCode(int code){_status_code=code;_desc="OK";}void AddHeader(const std::string &k,const std::string &v){_headers_kv[k]=v;}void AddBodyText(const std::string body_text){_resp_body_text=body_text;}std::string Serialize(){// 构建状态行_status_line=_version+spacesep+std::to_string(_status_code)+spacesep+_desc+base_sep;// 构建应答报头for(auto &hander:_headers_kv){std::string header_line=hander.first+line_sep+hander.second+base_sep;_resp_handers.push_back(header_line);}// 空行和正文// 空行已经初始化,正文已经保存在_resp_body_text中// 正式序列化std::string responsestr=_status_line;for(auto &line:_resp_handers){responsestr+=line;}responsestr+=_blank_line;responsestr+=_resp_body_text;return responsestr;}~HttpResponse(){}private:// 基本属性std::string _version;int _status_code;std::string _desc;std::unordered_map<std::string,std::string> _headers_kv;// 响应的基本格式std::string _status_line;std::vector<std::string> _resp_handers;std::string _blank_line;std::string _resp_body_text;
};

html

<!DOCTYPE html>
<html>
<head><title>南桥几晴秋(gwj.cn)</title>        <meta charset="UTF-8">
</head>
<body><div id="container" style="width:800px"><div id="header" style="background-color:#FFA500;"><h1 style="margin-bottom:0;">南桥几晴秋</h1></div><div id="menu" style="background-color:#FFD700;height:200px;width:100px;float:left;"><b>Menu</b><br>HTML<br>CSS<br>JavaScript</div><div id="content" style="background-color:#EEEEEE;height:200px;width:700px;float:left;">这是一个测试</div><div id="footer" style="background-color:#FFA500;clear:both;text-align:center;">Copyright © gwj.com</div></div><div><img src="/image/1.jpg" alt="一张图片"></div>
</body>
</html>

测试

在浏览器中输入:http://119.3.220.34:8888/,得到如下界面:

在这里插入图片描述

为什么只访问首页时但是依然有下面的图片

在这里插入图片描述

获得一个完整的网页,浏览器要先得到html,根据html的标签检测出我们还要获取的其他资源,浏览器会继续发起HTTP请求。

理解网站页面跳转

我们在使用网站时,点击某个按钮进行跳转,实际上是在访问wwwroot中的文件

在这里插入图片描述

在我自己的HTTP服务器中显示如下:

在这里插入图片描述

在现代Web开发中,HTML文件的内容通常属于前端部分,而通过后端调用和渲染这些内容是常见的架构设计。

在这里插入图片描述

HTTP 常见 Header

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

关于connection报头

HTTP 中的 Connection 字段是 HTTP 报文头的一部分, 它主要用于控制和管理客户端与服务器之间的连接状态。

  • 管理持久连接: Connection 字段还用于管理持久连接(也称为长连接) 。 持久连接允许客户端和服务器在请求/响应完成后不立即关闭 TCP 连接, 以便在同一个连接上发送多个请求和接收多个响应。
  • HTTP/1.1: 在 HTTP/1.1 协议中, 默认使用持久连接。 当客户端和服务器都不明确指定关闭连接时, 连接将保持打开状态, 以便后续的请求和响应可以复用同一个连接。
  • HTTP/1.0: 在 HTTP/1.0 协议中, 默认连接是非持久的。 如果希望在 HTTP/1.0上实现持久连接, 需要在请求头中显式设置 Connection: keep-alive

语法格式
Connection: keep-alive: 表示希望保持连接以复用 TCP 连接。
Connection: close: 表示请求/响应完成后, 应该关闭 TCP 连接。

HTTP的状态码

最常见的状态码, 比如 200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定
向), 504(Bad Gateway)

在这里插入图片描述

永久重定向与临时重定向

什么叫做重定向?

用户浏览器想目标服务器发起请求,但是目标服务器想用户浏览器推送了一个服务器,此时用户浏览器向推送的服务器进行请求,重定向的地址在location字段中。

在这里插入图片描述

所谓的永久与临时无非就是字面意思,永久即下次再请求时直接去新的服务器,会修改客户的地址,临时不对客户做任何影响。

永久重定向:当你决定将一个页面永久迁移到新地址时,使用永久重定向是正确的做法。例如,网站改版、页面URL结构优化或域名更换时。

临时重定向:当你需要在短期内将页面临时指向另一个URL时使用,比如进行系统维护、A/B测试或者临时更新页面内容时。

if(req.Path()=="wwwroot/redir")
{std::string reire_path="https://www.qq.com";resp.AddCode(302,_code_to_desc[302]);resp.AddHeader("Location",reire_path);
}

在这里插入图片描述

HTTP请求方法

在这里插入图片描述

HTTP常见方法

GET方法
  • 用途:用于请求URL指定的资源
  • 特性:指定资源经服务器端解析后返回响应内容

GET一般用来获取静态资源,也可以通过URL向服务器传递参数

std::string Method()
{LOG(DEBUG,"Client request method is %s\n",_method.c_str());return _method;
}

在这里插入图片描述

在这里插入图片描述

POST方法
  • 用途:用于传输实体的主体, 通常用于提交表单数据
  • 特性:可以发送大量的数据给服务器, 并且数据包含在请求体中

POST方法可以通过httprequest的正文来进行参数传递

在这里插入图片描述

POST方法传递参数比GET方法更私密,登录信息不回显示在浏览器中。但是都不安全。要想保证安全必须对http的参数部分进行加密。

完整HTTP代码

#pragma once#include<iostream>
#include<string>
#include<vector>
#include<functional>
#include<sstream>
#include<fstream>
#include<unordered_map>const static std::string base_sep="\r\n";
const static std::string line_sep=": ";
const static std::string prefixpath="wwwroot"; // web根目录
const static std::string homepage="index.html"; // web根目录
const static std::string httpversion="HTTP/gwj_1.0";
const static std::string spacesep=" ";
const static std::string suffixsep=".";
const static std::string html_404="404.html";
const static std::string arg_sep="?";class HttpRequest
{
private:std::string Getline(std::string &reqstr){auto pos=reqstr.find(base_sep);if(pos==std::string::npos) return std::string();std::string line=reqstr.substr(0,pos);reqstr.erase(0,line.size()+base_sep.size());return line.empty()?base_sep:line;}void ParseReqLine(){std::stringstream ss(_req_line);ss>>_method>>_url>>_version;if(strcasecmp(_method.c_str(),"GET")==0){auto pos=_url.find(arg_sep);if(pos!=std::string::npos){_body_text=_url.substr(pos+arg_sep.size());_url.resize(pos);  // 只保留了?前面的字符}}_path+=_url;if(_path[_path.size()-1]=='/'){_path+=homepage;}auto pos=_path.rfind(suffixsep);if(pos!=std::string::npos){_suffix=_path.substr(pos);}else{_suffix=".default";}}void ParseReqHeader(){for(auto &header:_req_handers){auto pos=header.find(line_sep);if(pos==std::string::npos) continue;std::string k=header.substr(0,pos); // [)std::string v=header.substr(pos+line_sep.size());if(k.empty()||v.empty()) continue;_headers_kv.insert(std::make_pair(k,v));}}public:HttpRequest():_blank_line(base_sep),_path(prefixpath){}void Deserialize(std::string &reqstr){// 基本的反序列化_req_line=Getline(reqstr);std::string header;do{header=Getline(reqstr);if(header.empty()) break;else if(header==base_sep) break;_req_handers.push_back(header);}while(true);if(!reqstr.empty())_body_text=reqstr;//进一步反序列化ParseReqLine();ParseReqHeader();}std::string Url(){LOG(DEBUG,"Client Want path %s\n",_path.c_str());return _url;}std::string Path(){LOG(DEBUG,"Client Want %s\n",_url.c_str());return _path;}std::string Suffix(){return _suffix;}std::string Method(){LOG(DEBUG,"Client request method is %s\n",_method.c_str());return _method;}std::string GetRequestBody(){LOG(DEBUG,"Client request method is %s, args: %s, request path: %s\n",_method.c_str(),_body_text.c_str(),_path.c_str());return _body_text;}void Print(){std::cout<<"-----------------------------------------------"<<std::endl;std::cout<<"###"<<_req_line<<std::endl;for(auto &header:_req_handers){std::cout<<"@@@"<<header<<std::endl;}std::cout<<"***"<<_blank_line;std::cout<<">>>"<<_body_text<<std::endl;std::cout<<"Method: "<<_method<<std::endl;std::cout<<"URL: "<<_url<<std::endl;std::cout<<"Version: "<<_version<<std::endl;for(auto header_kv:_headers_kv){std::cout<<"< "<<header_kv.first<<" > : < "<<header_kv.second<<" >"<<std::endl;}}~HttpRequest() {}
private:// 请求的基本格式std::string _req_line;std::vector<std::string> _req_handers;std::string _blank_line;std::string _body_text;//具体属性std::string _method;std::string _url;std::string _path;std::string _suffix; // 资源后缀std::string _version;std::unordered_map<std::string,std::string> _headers_kv;
};class HttpResponse
{
public:HttpResponse():_version(httpversion),_blank_line(base_sep){}void AddCode(int code,const std::string &desc){_status_code=code;_desc=desc;}void AddHeader(const std::string &k,const std::string &v){_headers_kv[k]=v;}void AddBodyText(const std::string body_text){_resp_body_text=body_text;}std::string Serialize(){// 构建状态行_status_line=_version+spacesep+std::to_string(_status_code)+spacesep+_desc+base_sep;// 构建应答报头for(auto &hander:_headers_kv){std::string header_line=hander.first+line_sep+hander.second+base_sep;_resp_handers.push_back(header_line);}// 空行和正文// 空行已经初始化,正文已经保存在_resp_body_text中// 正式序列化std::string responsestr=_status_line;for(auto &line:_resp_handers){responsestr+=line;}responsestr+=_blank_line;responsestr+=_resp_body_text;return responsestr;}~HttpResponse(){}private:// 基本属性std::string _version;int _status_code;std::string _desc;std::unordered_map<std::string,std::string> _headers_kv;// 响应的基本格式std::string _status_line;std::vector<std::string> _resp_handers;std::string _blank_line;std::string _resp_body_text;
};using func_t=std::function<HttpResponse(HttpRequest&)>;class HttpServer
{
private:std::string GetFileContent(const std::string path){std::ifstream in(path,std::ios::binary);if(!in.is_open()) return std::string();in.seekg(0,in.end);int filesize=in.tellg(); //读写偏移量,计算文件大小in.seekg(0,in.beg);std::string content;content.resize(filesize);in.read((char*)content.c_str(),filesize);in.close();return content;}public:HttpServer(){_mime_type.insert(std::make_pair(".html","text/html"));_mime_type.insert(std::make_pair(".jpg","image/jpeg"));_mime_type.insert(std::make_pair(".png","image/png"));_mime_type.insert(std::make_pair(".default","text/html"));_code_to_desc.insert(std::make_pair(100,"Continue"));_code_to_desc.insert(std::make_pair(200,"OK"));_code_to_desc.insert(std::make_pair(201,"Created"));_code_to_desc.insert(std::make_pair(301,"Moved Permanently"));_code_to_desc.insert(std::make_pair(302,"Found"));}
// #define TESTstd::string HandlerHttpRequest(std::string &reqstr) //reqstr被客户序列化过{
#ifdef TESTstd::cout<<"------------------------------------------------------"<<std::endl;std::cout<<reqstr;std::string responsestr = "HTTP/1.1 200 OK\r\n";responsestr += "Content-Type: text/html\r\n";responsestr += "\r\n";responsestr += "<html><h1>hello Linux, hello gwj!</h1></html>";return responsestr;#elsestd::cout << "---------------------------------------" << std::endl;std::cout << reqstr;std::cout << "---------------------------------------" << std::endl;HttpRequest req;HttpResponse resp;  req.Deserialize(reqstr);// req.Method();if(req.Path()=="wwwroot/redir"){std::string reire_path="https://www.qq.com";resp.AddCode(302,_code_to_desc[302]);resp.AddHeader("Location",reire_path);}else if (!req.GetRequestBody().empty()){if(IsServiceExists(req.Path())){resp=_service_lists[req.Path()](req);}}else{std::string content = GetFileContent(req.Path());if (content.empty()){content = GetFileContent("wwwroot/404.html");resp.AddCode(404, _code_to_desc[404]);resp.AddHeader("Content-Length", std::to_string(content.size()));resp.AddHeader("Content-Type", _mime_type[".html"]);resp.AddBodyText(content);}else{resp.AddCode(200, _code_to_desc[200]);resp.AddHeader("Content-Length", std::to_string(content.size()));resp.AddHeader("Content-Type", _mime_type[req.Suffix()]);resp.AddBodyText(content);}}return resp.Serialize();#endif}void InsertService(const std::string servicename,func_t f){std::string s=prefixpath+servicename;_service_lists[s]=f;}bool IsServiceExists(const std::string &servicename){auto iter=_service_lists.find(servicename);if(iter==_service_lists.end()) return false;else return true;}~HttpServer() {}private:std::unordered_map<std::string,std::string> _mime_type;std::unordered_map<int,std::string> _code_to_desc;std::unordered_map<std::string,func_t> _service_lists;
};


在这里插入图片描述

相关文章:

【Linux网络编程】应用层:HTTP协议 | URL | 简单实现一个HTTP服务器 | 永久重定向与临时重定向

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 &#x1f308;个人主页&#xff1a; 南桥几晴秋 &#x1f308;C专栏&#xff1a; 南桥谈C &#x1f308;C语言专栏&#xff1a; C语言学习系…...

电压调整电路汇总

目录&#xff1a; 一、LDO 1、LM1117 2、NCV33275 3、TLE42764 4、TPS7B67xx-Q1 5、总结 二、DCDC转换器 1、LM2576与LM2596 2、MC34063 一、LDO 1、LM1117 LM1117 是一款在 800mA 负载电流下具有 1.2V 压降的低压降稳压器。 LM1117 提供可调节电压版本&#xff0c…...

day28 文件IO及进程线程基础

讨论光标共享情况 1.dup和dup2定义变量赋值都共享光标 2.使用两个描述符调用两次open函数打开同一个文件&#xff0c;不共享光标 #include <myhead.h>int main(int argc, const char *argv[]) {//1、描述符赋值给新的变量char buff[1024] "abcdefg";int ne…...

【Azure 架构师学习笔记】- Azure Function (1) --环境搭建和背景介绍

本文属于【Azure 架构师学习笔记】系列。 本文属于【Azure Function 】系列。 前言 随着无服务计算的兴起和大数据环境中的数据集成需求&#xff0c; 需要使用某些轻量级的服务&#xff0c;来实现一些简单操作。因此Azure Function就成了微软云上的一个必不可少的组成部分。 …...

前端文件下载

这里写自定义目录标题 前端文件下载方法使用a标签使用iframe标签二进制流 前端文件下载方法 使用a标签 /*** 文件下载方法 使用a标签* 存在浏览器下载时&#xff0c;太快的话&#xff0c;会取消上次的下载请求* param {*} href* param {*} filename*/ export function downlo…...

前端成长之路:HTML(3)

在HTML中&#xff0c;有列表标签。列表最大的特点是整齐、简洁、有序&#xff0c;用列表进行布局会更加自由方便。根据使用的情景不同&#xff0c;可以将列表分为三大类&#xff1a;无序列表、有序列表和自定义列表。 无序列表 在HTML中使用<ul>标签定义一个无序列表&a…...

无人机自动机库的功能与作用!

一、无人机自动机库的功能 智能停放与管理 无人机自动机库为无人机提供了一个安全、可靠的停放环境。通过先进的感知技术和安全防护措施&#xff0c;它能够实时监测周围环境&#xff0c;确保无人机免受恶劣天气或潜在风险的侵害。 无人机在机库内可以实现智能停放&#xff0…...

ubuntu 新建脚本shell并增加图标 双击应用实现python运行

1.使用nano创建shell脚本文件 需要在终端窗口中输入“nano”以打开文本编辑器。 nano 在创建脚本文件前&#xff0c;我们要了解脚本文件是如何运行的&#xff1a; 直接运行&#xff1a;直接在终端直接输入需要运行的脚本文件名称&#xff0c;系统或用缺省版本的shell运行脚…...

ANR 分析SOP

遇到ANR问题不要慌&#xff0c;大部分情况下可能是系统or测试手段问题&#xff0c;我们按照如下关键字排查定位 文章目录 1 是否是 heapdump 导致&#xff1f;1.1 dump开始1.2 dump结束 1 是否是 heapdump 导致&#xff1f; 使用 hprof: heap dump 关键词过滤&#xff0c;在d…...

COLA学习之环境搭建(三)

小伙伴们&#xff0c;你们好&#xff0c;我是老寇&#xff0c;上一节&#xff0c;我们学习了COLA代码规范&#xff0c;继续跟老寇学习COLA环境搭建 首先&#xff0c;打开GitHub&#xff0c;搜索 COLA 请给这个COLA项目点个Star&#xff0c;养成好习惯&#xff0c;然后Fork到自…...

CSS输入框动态伸缩动效

前言 下面我们将会做出如下图输入框样式&#xff0c;并且附上组件代码&#xff0c;有特殊需求的可以自行优化同理&#xff0c;下拉框的话只要把el-input标签修改掉即可 MyInput组件 <template><div class"my-input" click.stop"showInput !showInput…...

hbuilder 安卓app手机调试中基座如何设置

app端使用基座 手机在线预览功能 1.点击运行 2.点击运行到手机或者模拟器 3.制作自定义调试基座 4.先生成证书【可以看我上一篇文档写的有】&#xff0c;点击打包 5.打包出android自定义调试基座【android_debug.apk】,【就跟app打包一样需要等个几分钟】 6.点击运行到手…...

探索视觉与语言模型的可扩展性

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…...

sock_recvmsg函数

sock_recvmsg 是一个在 Linux 内核中用于处理接收网络数据的函数。它通常与套接字 (socket) 操作相关,特别是在网络协议栈中用于处理从网络中接收到的数据。这个函数是内核的一部分,提供了一种机制把接收到的数据从网络协议栈转移到用户空间,或者在内核内进一步处理。 以下是…...

HCIA笔记8--DHCP、Telnet协议

1. DHCP介绍 对于主机的网络进行手动配置&#xff0c;在小规模的网络中还是可以运作的&#xff0c;但大规模网络是无力应对的。因此就有了DHCP协议来自动管理主机网络的配置。 DHCP(Dynamic Host Configuration Protocol): 动态主机配置协议&#xff0c;主要需要配置的参数有…...

Scala的单例对象

在Scala中&#xff0c;单例对象是一种特殊的类&#xff0c;它只能有一个实例&#xff0c;并且这个实例在需要时会自动创建。单例对象在Scala中通过object关键字来定义&#xff0c;它类似于Java中的静态成员和方法&#xff0c;但更加灵活和强大。 定义单例对象 以下是定义一个…...

【笔记】分布式任务调度平台XXL-JOB

这篇笔记主要记录以下内容&#xff1a; &#xff08;1&#xff09;第一次启动xxl-job的过程 &#xff08;2&#xff09;模块、文件、数据库&#xff08;表和字段&#xff09;的作用 &#xff08;3&#xff09;极少的源码解读&#xff08;XxlJobConfig&#xff09; 有点像实…...

PDFMathTranslate,PDF多语言翻译,批量处理,学术论文,双语对照(WIN/MAC)

分享一个非常实用的PDF文档翻译项目——PDFMathTranslate。作为一个经常逛GitHub的开发者&#xff0c;我总喜欢翻看各种项目附带的论文&#xff0c;虽然大多时候是瞎研究&#xff0c;但却乐在其中。该项目能够完美保留公式、图表、目录和注释&#xff0c;对于需要阅读外文文献的…...

zerotier实现内网穿透(访问内网服务器)

moo 内网穿透工具 实用工具&#xff1a;zerotier 目录 内网穿透工具 Windows下zerotier安装 ubuntu系统下的zerotier安装 使用moon加速 Windows下zerotier安装 有了网络之后&#xff0c;会给你一个网络id&#xff0c;这个网络id是非常重要的&#xff0c;其它设备要加入…...

Formality:set_svf命令

相关阅读 Formalityhttps://blog.csdn.net/weixin_45791458/category_12841971.html?spm1001.2014.3001.5482 svf文件的全称是Setup Verification for Formality&#xff0c;即Design Compiler提供给Formality的设置验证文件&#xff0c;它的作用是为Formality的指导模式(Gui…...

IDEA报错:无效的源发行版、无效的目标发行版

1. 无效的源发行版 创建项目的时候&#xff0c;会遇见这个报错&#xff0c;原因就是编译的JDK版本与发布版本不一致。 解决方法&#xff1a; 1.1. 找到问题所在地 英文&#xff1a;File -> Project Structure ->Project Settings 中文&#xff1a;文件->项目结构 …...

#渗透测试#红蓝对抗#SRC漏洞挖掘# Yakit插件使用及编写01

免责声明 本教程仅为合法的教学目的而准备&#xff0c;严禁用于任何形式的违法犯罪活动及其他商业行为&#xff0c;在使用本教程前&#xff0c;您应确保该行为符合当地的法律法规&#xff0c;继续阅读即表示您需自行承担所有操作的后果&#xff0c;如有异议&#xff0c;请立即停…...

Scala中求斐波那契数列的第n项

求斐波那契数列的第n项 问题&#xff1a;求 斐波那契数列的第n项 记&#xff1a; 0 1 1 2 3 5 8 13 21 34 55 ... 从第3项开始 f(n) f(n-1) f(n-2) 1.基本情况&#xff08;直接能求的&#xff09;&#xff1a;f(0) 0,f(1) 1 2.递归情况&#xff08;大事化小&#xff0c;自己…...

ORACLE修改序列值为表内某字段(主键)最大值

ORACLE修改序列值为表内某字段&#xff08;主键&#xff09;最大值 想修改序列值&#xff0c;网上基本的都是自己看要加到多少&#xff0c;然后要么调步长&#xff0c;要么删了重建。 想直接用declare使用标量然后调&#xff0c;但是逻辑里面不能有DDL语句。 不过最后解决了 直…...

前端html,vue使用第三方地图详细教程,以百度地图为例,实现地图标注,导航,定位,路线规划,坐标转换

目录 示例&#xff1a; 准备&#xff1a; ?编辑 开始&#xff1a; 1、新建页面&#xff0c;在script标签中引入百度地图的api数据&#xff0c;把自己在控制台创建的应用的ak替换上去 2、创建一个dom对象&#xff0c;设置宽高 3、在js中初始化地图 进阶&#xff1a; 1…...

【入门】加密四位数

描述 某军事单位用 4 位整数来传递信息&#xff0c;传递之前要求先对这个 4 位数进行加密。加密的方式是每一位都先加上 5 然后对 10 取余数&#xff0c;再将得到的新数颠倒过来。 例如&#xff1a;原数是 1379 &#xff0c;那么每位加 5 对 10 取余数的结果为 6824 &#xf…...

[游戏开发] Unity中使用FlatBuffer

什么是FlatBuffer 为什么用FloatBuffer&#xff0c;优势在哪&#xff1f; 下图是常规使用的各种数据存储类型的性能对比。 对序列化数据的访问不需要打包和拆包——它将序列化数据存储在缓存中&#xff0c;这些数据既可以存储在文件中&#xff0c;又可以通过网络原样传输&…...

云计算IaaS-PaaS-SaaS三种服务模式转至元数据结尾

在当今数字化时代&#xff0c;云计算已经成为推动企业创新与发展的核心力量。而云计算的模型主要有三种&#xff1a;IAAS、PAAS 和 SAAS&#xff0c;它们各自在云计算的庞大体系中扮演着独特且关键的角色&#xff0c;恰似一座大厦的不同楼层&#xff0c;共同构建起强大而灵活的…...

【数据结构——查找】二叉排序树(头歌实践教学平台习题)【合集】

目录&#x1f60b; 任务描述 相关知识 测试说明 我的通关代码: 测试结果&#xff1a; 任务描述 本关任务&#xff1a;实现二叉排序树的基本算法。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;二叉树的创建、查找和删除算法。具体如下&#xff1a; (1)由…...

代码随想录第43天

300.最长递增子序列 # Dynamic programming. class Solution:def lengthOfLIS(self, nums: List[int]) -> int:if not nums: return 0dp [1] * len(nums)for i in range(len(nums)):for j in range(i):if nums[j] < nums[i]: # 如果要求非严格递增&#xff0c;将此行 …...

创新驱动发展战略意义/seo优化需要做什么

实验环境 摘要拓扑图&#xff1a; 实验拓扑图 设备登录凭据 软路由设备&#xff1a;( GatewayIP: 1-9 ) Hostname IP Address Username/Password ispct-vyos eth0IP: 192.168.31.41/24 eth1IP: 192.168.10.1/24 dns: 192.168.30.191 ospfv2: Route-ID(1.1.1.1/32) vyos/12…...

可以做网站的电脑软件/360指数查询

网站被挂马一直是被广大站长所痛恶&#xff0c;又感到困扰的一件事&#xff0c;下面我把一些快速查找木马的方法分享给大家。虚拟主机站长上传源码后一段时间后发现网站被挂马&#xff0c;怎样快速查找锁定木马呢&#xff1f;首先我们要知道一般cms工作的时候其文件时不会改变的…...

沈阳公司建站/武汉大学人民医院怎么样

一直困惑于vlan的link问题&#xff0c;access link还稍微可以理解&#xff0c;而trunk link和hybrid link 怎么让untagged frame通过&#xff0c;我却一直很迷惑&#xff0c;终于在老师的指点下&#xff0c;知道了部分原理&#xff08;首先得谢谢老师&#xff09;废话少说&…...

网站建设好推荐/推广类软文

最全的cmap的用法 此文是复制来的0.0 1. map最基本的构造函数&#xff1b;map<string ,int>mapstring; map<int,string >mapint;map<sring,char>mapstring; map< char ,string>mapchar;map<char,int>mapchar; map<int ,char>mapint&#x…...

海勃湾网站建设/市场调研报告1000字

配置是如何进行的每个GNU发布版本都应该还有一个名为configure的shell脚本。你需要把你希望在那种机器和系统上编译程序作为参数告诉这个脚本。脚本configure必须记录配置信息以便它们可以影响编译工作。这样做的一种方式是把一个诸如config.h的标准名字和为选定的系统匹配的正…...

如何做网络网站推广/推广文案范例

最近一个项目要用到ActiveMq&#xff0c;并且需要最大程度的保证消息不丢失。以前对activeMq不是很熟悉&#xff0c;完全是摸着石头过河&#xff0c;目前基 本配置都搞定了。只是对于它的自动重连一直找不到好的解决办法&#xff0c;我希望的效果是当一个broker&#xff08;假设…...