当前位置: 首页 > 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…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应&#xff0c;这是一种非线性光学现象&#xff0c;主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场&#xff0c;对材料产生非线性响应&#xff0c;可能…...

MongoDB学习和应用(高效的非关系型数据库)

一丶 MongoDB简介 对于社交类软件的功能&#xff0c;我们需要对它的功能特点进行分析&#xff1a; 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具&#xff1a; mysql&#xff1a;关系型数据库&am…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

现代密码学 | 椭圆曲线密码学—附py代码

Elliptic Curve Cryptography 椭圆曲线密码学&#xff08;ECC&#xff09;是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础&#xff0c;例如椭圆曲线数字签…...

如何理解 IP 数据报中的 TTL?

目录 前言理解 前言 面试灵魂一问&#xff1a;说说对 IP 数据报中 TTL 的理解&#xff1f;我们都知道&#xff0c;IP 数据报由首部和数据两部分组成&#xff0c;首部又分为两部分&#xff1a;固定部分和可变部分&#xff0c;共占 20 字节&#xff0c;而即将讨论的 TTL 就位于首…...

服务器--宝塔命令

一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行&#xff01; sudo su - 1. CentOS 系统&#xff1a; yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...