【计算机网络】HTTP协议以及简单的HTTP服务器实现
文章目录
- 一、HTTP协议
- 1.认识URL
- 2.urlencode和urldecode
- 3.HTTP协议格式
- 4.HTTP的方法
- 5.HTTP的状态码
- 6.HTTP常见Header
- 7.重定向
- 8.长连接
- 9.会话保持
- 10.基本工具
- 二、简单的HTTP服务器实现
- 1.err.hpp
- 2.log.hpp
- 3.procotol.hpp
- 4.Sock.hpp
- 5.Util.hpp
- 6.httpServer.hpp
- 7.httpServer.cc
- 8.总结分析
一、HTTP协议
虽然我们说, 应用层协议是我们程序猿自己定的.
但实际上, 已经有大佬们定义了一些现成的, 又非常好用的应用层协议, 供我们直接参考使用. HTTP(超文本传输议)就是其中之一。
1.认识URL
平时我们俗称的 “网址” 其实就是说的 URL
2.urlencode和urldecode
像 / ? : 等这样的字符, 已经被url当做特殊意义理解了. 因此这些字符不能随意出现.
比如, 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义.
转义的规则如下:
将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编成%XY格式
“+” 被转义成了 “%2B”
urldecode就是urlencode的逆过程;
encode:对特殊符号和汉字编码为%XX
decode:服务器(软件)收到url请求–自己对特殊%XX进行解码
urldecode工具
3.HTTP协议格式
HTTP请求
首行: [方法] + [url] + [版本]
Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束
Body: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度;
HTTP响应
首行: [版本号] + [状态码] + [状态码解释]
Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束
Body: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度; 如果服务器返回了一个html页面, 那么html页面内容就是在body中
状态码有200,400,302,307,500,404等等
状态码描述:404 -> Not Found 200 -> OK
1.请求和响应怎么保证应用层完整读取完毕了?
a.首先读取完整的一行
b.while(读取完整的一行)-所有的请求行+请求报头全部读完-直到空行
c.我们能保证把报头读完,报头有一个属性:Content-Length:XXX正文长度
d.解析出来内容长度,再根据内容长度,读取正文即可。
2.请求和响应是怎么做到序列化和反序列化的?
http自己实现的,第一行+请求/响应报头,只要按照\r\n将字符串1->n即可,正文则不需要做
响应的正文可以是html/css/js/图片/视频/音频等
4.HTTP的方法
其中最常用的就是GET方法和POST方法.
5.HTTP的状态码
最常见的状态码, 比如 200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定向), 504(Bad Gateway)
6.HTTP常见Header
Content-Type: 数据类型(text/html等)
Content-Length: Body的长度
Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;
User-Agent: 声明用户的操作系统和浏览器版本信息;
referer: 当前页面是从哪个页面跳转过来的;
location: 搭配3xx状态码使用, 告诉客户端接下来要去哪里访问;
Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能;
7.重定向
我们使用手机或者浏览器的时候,屏幕什么也没有点,就跳转到了其他的页面,比如自动跳转到广告商等,这是通过重定向完成的。重定向分为临时重定向和永久重定向。
重定向过程:客户端向服务器发送http请求,服务器给客户端发送的http响应的状态码为3XX,附带一个新的url,然后客户端重新向新的服务器发起请求—临时重定向。假如一个公司的一个网站,因为各种原因(比如同时访问数量受限等),所以重新写了一个网站,但是客户不知道新的网站链接,只知道老的链接,所以公司对老的链接进行永久重定向,客户在访问以前的网站的时候自动跳转到新的网站—永久重定向
8.长连接
其实我们看到的网页,实际上可能由多种元素构成,即一张完整的网页需要多次http请求
http网页中可能包含多个元素,如果频繁的发起http请求,http是基于TCP的,TCP是面向连接的,就会有频繁创建连接的问题(客户端和服务器都需要对连接进行管理,先描述再组织,会对连接创建对应的内核数据结构,对连接的管理就变成了对数据结构的管理,就会有时间和空间的成本)
所以就提出了长连接,长连接需要client和server都要支持,建立好一条连接,获取一份资源的时候,通过同一条连接来完成
Connection:Keep-alive --支持长连接
Connection:close --不支持长连接
9.会话保持
我们使用网页的bilibili的时候,我们登录一次之后,后面的一段时间都不需要我们重新进行登录了,关闭页面,然后重新点进去也不需要重新登录,这就是http的会话保持做到的
会话保持严格意义不是http天然具备的,而是后面使用发现需要的
http协议是无状态的,即http不关心上一次和下一次的请求,只负责当前请求的传输,但是用户需要,因为用户查看新的网页是常规操作,如果发送页面跳转,那么新的页面也就无法识别是哪一个用户了,为了让用户一经登录,可以在整个网站按照自己的身份进行随意访问,就需要会话保持。
我们看腾讯视频的时候,对于需要会员的视频,我们可以通过链接直接获取而不进行登录吗,答案是不行的,凡是对网页访问有权限要求的网页,在被获取之前,全部都要做判断,进行身份认证。
会话保持有两种方法:
第一种方案:我们在浏览器进行登录输入信息之后,浏览器会把我们用户输入的信息:用户名&&密码保持起来,这个被称为cookie数据。cookie分为cookie文件级数据和cookie内存级数据。往后只要访问同一个网站,浏览器就会自动推送历史保留信息给服务器
我们登录的时候,浏览器保存了cookie文件,每次请求账号密码都要进行推送,然后服务器再返回资源,但是这里有个问题,如果黑客在我们的电脑上种植了木马病毒,获取了我们的cookie文件,这样我们的账号密码就泄漏了,那么黑客使用他的浏览器登录我们的账号,此时服务器会误认为这个非法用户是你,如果是QQ,微信对我们就会造成很大的影响
第二种方案:我们进行登录的时候输入用户名和密码,此时服务器端会根据用户的信息形成一个session文件,它有唯一的名称:session id来进行唯一标识,然后将当前用户的session id返回给用户,此时浏览器的cookie文件中保存的是session id。此后用户发送http request 和session id给服务器,服务器根据session id进行鉴权,此时client保存了cookie,server保存了session。
如果黑客获取了用户的session,此时服务器还是会误认为这个非法用户是你,但是此时我们已经将矛盾转移了,此时黑客盗取的是公司的私密数据,有法律进行维护,此外还配合了其他的策略来缓解该类问题,比如我们在很短的时间内从一个地区到另一个地区,QQ就会提醒我们异地登录,需要重新进行登录等等
10.基本工具
这里介绍两个基本工具:postman 和 fiddler
postman不是抓包工具,而是一个模拟客户端—浏览器的行为的工具
fiddler是一个抓包工具,http工具
postman和fiddler的原理:
浏览器将请求发送给fiddler,此时fiddler可以作为代理服务器看待,然后就fiddler将请求转发给服务器,服务器响应的信息通过fiddler转发到用户的手中,而postman则就是相当于客户端向服务器发起请求
我们对下文的程序进行抓包:
我们可以看到,我们的账号和密码都是明文的,所以POST和GET都是不安全的,只有是否私密的区别,安全需要HTTPS来完成。
二、简单的HTTP服务器实现
1.err.hpp
#pragma onceenum
{USAGE_ERR = 1,SOCKET_ERR,BIND_ERR,LISTEN_ERR
};
2.log.hpp
#pragma once#include <iostream>
#include <string>
#include <cstdarg>
#include <ctime>
#include <unistd.h>#define DEBUG 0
#define NORMAL 1
#define WARNING 2
#define ERROR 3
#define FATAL 4#define LOG_NORMAL "log.txt"
#define LOG_ERR "log.error"#define NUM 1024const char *to_levelstr(int level)
{switch (level){case DEBUG:return "DEBUG";case NORMAL:return "NORMAL";case WARNING:return "WARNING";case ERROR:return "ERROR";case FATAL:return "FATAL";default:return nullptr;}
}void logMessage(int level, const char *format, ...)
{char logprefix[NUM];snprintf(logprefix, sizeof(logprefix), "[%s][%ld][pid: %d]",to_levelstr(level), (long int)time(nullptr), getpid());char logcontent[NUM];va_list arg;va_start(arg, format);vsnprintf(logcontent, sizeof(logcontent), format, arg);std::cout << logprefix << logcontent << std::endl;FILE *log = fopen(LOG_NORMAL, "a");FILE *error = fopen(LOG_ERR, "a");if (log && error){FILE *cur = nullptr;if (level == DEBUG || level == NORMAL || level == WARNING)cur = log;if (level == ERROR || level == FATAL)cur = error;if (cur)fprintf(cur, "%s%s\n", logprefix, logcontent);fclose(log);fclose(error);}
}
3.procotol.hpp
#pragma once#include <iostream>
#include <string>
#include <sstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "Util.hpp"const std::string sep = "\r\n";
const std::string default_root = "./wwwroot";
const std::string home_page = "index.html";
const std::string html_404 = "wwwroot/404.html";class HttpRequest
{
public:HttpRequest() {}~HttpRequest() {}public:void parse(){// 1. 从inbuffer中拿到第一行,分隔符\r\nstd::string line = Util::getOneLine(inbuffer, sep);if (line.empty())return;// 2. 从请求行中提取三个字段// std::cout << "line: " << line << std::endl;std::stringstream ss(line);ss >> method >> url >> httpversion;// 3. 添加web默认路径path = default_root; // ./wwwroot,path += url; //./wwwroot/a/b/c.html, ./wwwroot/if (path[path.size() - 1] == '/')path += home_page;// 4.获取path对应资源的后缀// ./wwwroot.index.html// ./wwwroot.1.jpgauto pos = path.rfind(".");if (pos == std::string::npos)suffix = ".html";elsesuffix = path.substr(pos);// 5.得到资源的大小struct stat st;int n = stat(path.c_str(), &st);if (n == 0)size = st.st_size;elsesize = -1;}public:std::string inbuffer;std::string method;std::string url;std::string httpversion;std::string path;std::string suffix;int size;
};class HttpResponse
{
public:std::string outbuffer;
};
4.Sock.hpp
#pragma once#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include "log.hpp"
#include "err.hpp"static const int backlog = 5;class Sock
{
public:void Socket(){_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock < 0){logMessage(FATAL, "create socket error");exit(SOCKET_ERR);}int opt = 1;setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof opt);logMessage(NORMAL, "create socket success");}void Bind(const uint16_t &port){struct sockaddr_in local;memset(&local, 0, sizeof local);local.sin_family = AF_INET;local.sin_addr.s_addr = INADDR_ANY;local.sin_port = htons(port);int n = bind(_listensock, (struct sockaddr *)&local, sizeof local);if (n < 0){logMessage(FATAL, "bind socket error");exit(BIND_ERR);}logMessage(NORMAL, "bind socket success");}void Listen(){int n = listen(_listensock, backlog);if (n < 0){logMessage(FATAL, "listen socket error");exit(LISTEN_ERR);}logMessage(NORMAL, "listen socket success");}int Accept(){struct sockaddr_in peer;socklen_t len = sizeof(peer);int sock = accept(_listensock, (struct sockaddr *)&peer, &len);return sock;}int Fd(){return _listensock;}void Close(){if (_listensock > 0)close(_listensock);}private:int _listensock;
};
5.Util.hpp
#pragma once#include <string>
#include <fstream>class Util
{
public:static std::string getOneLine(std::string &buffer, const std::string &sep){auto pos = buffer.find(sep);if (pos == std::string::npos)return "";std::string sub = buffer.substr(0, pos);buffer.erase(0, sub.size() + sep.size());return sub;}static bool readFile(const std::string resource, char *buffer, int size){std::ifstream in(resource, std::ios::binary);if (!in.is_open())return false;in.read(buffer, size);in.close();return true;}
};
6.httpServer.hpp
#pragma once #include <functional>
#include <sys/wait.h>
#include "Sock.hpp"
#include "protocol.hpp"
#include "log.hpp"
#include "err.hpp"
#include "Util.hpp"namespace server
{static const int defaultport = 8080;using func_t = std::function<bool(const HttpRequest &, HttpResponse &)>;class httpServer{public:httpServer(const func_t func, const uint16_t &port = defaultport): _func(func), _port(port){}void initServer(){_sock.Socket();_sock.Bind(_port);_sock.Listen();}void handlerEvent(int sock){// 1. 读到完整的http请求// 2. 反序列化// 3. httprequst, httpresponse, _func(req, resp)// 4. resp序列化// 5. sendchar buffer[4096];HttpRequest req;HttpResponse resp;size_t n = recv(sock, buffer, sizeof(buffer) - 1, 0);if (n > 0){buffer[n] = 0;req.inbuffer = buffer;req.parse();_func(req, resp); // req -> respsend(sock, resp.outbuffer.c_str(), resp.outbuffer.size(), 0);}}void start(){for (;;){int sock = _sock.Accept();if (sock < 0)continue;pid_t id = fork();if (id == 0){_sock.Close();if (fork() > 0)exit(0);handlerEvent(sock);close(sock);exit(0);}close(sock);waitpid(id, nullptr, 0);}}~httpServer(){}private:Sock _sock;uint16_t _port;func_t _func;};
}
7.httpServer.cc
#include "httpServer.hpp"
#include <memory>using namespace std;
using namespace server;void Usage(std::string proc)
{cerr << "Usage:\n\t" << proc << " port\r\n\r\n";
}std::string suffixToDesc(const std::string suffix)
{std::string ct = "Content-Type: ";if (suffix == ".html")ct += "text/html";else if (suffix == ".jpg")ct += "application/x-jpg";ct += "\r\n";return ct;
}// 1. 服务器和网页分离,html
// 2. url -> / : web根目录
bool Get(const HttpRequest &req, HttpResponse &resp)
{// for testcout << "----------------------http start---------------------------" << endl;cout << req.inbuffer;std::cout << "method: " << req.method << std::endl;std::cout << "url: " << req.url << std::endl;std::cout << "httpversion: " << req.httpversion << std::endl;std::cout << "path: " << req.path << std::endl;std::cout << "suffix: " << req.suffix << std::endl;std::cout << "size: " << req.size << "字节" << std::endl;cout << "----------------------http end---------------------------" << endl;// std::string respline = "HTTP/1.1 200 OK\r\n";std::string respline = "HTTP/1.1 307 Permanent Redirect\r\n";std::string respheader = suffixToDesc(req.suffix);respheader += "Location: https://www.qq.com/\r\n";std::string respblank = "\r\n";// std::string body = "<html lang=\"en\"><head><meta charset=\"UTF-8\"><title>for test</title><h1>hello world</h1></head><body><p>多情只有春庭月,犹为离人照落花</p></body></html>";std::string body;body.resize(req.size + 1);if (!Util::readFile(req.path, (char *)body.c_str(), req.size)){Util::readFile(html_404, (char *)body.c_str(), req.size);}respheader += "Content-Length: ";respheader += to_string(body.size());respheader += "\r\n";resp.outbuffer += respline;resp.outbuffer += respheader;resp.outbuffer += respblank;cout << "----------------------http response start---------------------------" << endl;std::cout << "resp.outbuffer" << resp.outbuffer << std::endl;cout << "----------------------http response end---------------------------" << endl;resp.outbuffer += body;return true;
}// ./httpServer port
int main(int argc, char *argv[])
{// if(argc != 2)// {// Usage(argv[0]);// exit(0);// }// uint16_t port = atoi(argv[1]);// unique_ptr<httpServer> httpsvr(new httpServer(Get, port));unique_ptr<httpServer> httpsvr(new httpServer(Get));httpsvr->initServer();httpsvr->start();return 0;
}
8.总结分析
一个用户看到的网页结果,可能是多个资源结合而成的,所以要获取一个网页效果,我们的浏览器一定会 发起多次http请求。
我们进行数据提交的时候,本质前端要通过form表单提交的,浏览器会自动将form表单中的内容转换成GET/POST方法请求
GET和POST的区别
如下是GET的方式
如下是POST的方式
二者的区别如下:
GET通过URL传递参数,具体:http://ip:port/XXX/YYY?name=value&name1=value1
POST提交参数通过http请求的正文提交参数
POST方法通过正文的提交参数,所以一般用户看不到,私密性更好,私密性 != 安全性,GET方法不私密
无论是GET还是POST方法,都不安全,要谈安全,必须加密 --https
通过URL传递参数,注定了不能太大
但是POST方法,通过正文,正文可以很大,甚至可以是其他的东西
相关文章:

【计算机网络】HTTP协议以及简单的HTTP服务器实现
文章目录 一、HTTP协议1.认识URL2.urlencode和urldecode3.HTTP协议格式4.HTTP的方法5.HTTP的状态码6.HTTP常见Header7.重定向8.长连接9.会话保持10.基本工具 二、简单的HTTP服务器实现1.err.hpp2.log.hpp3.procotol.hpp4.Sock.hpp5.Util.hpp6.httpServer.hpp7.httpServer.cc8.总…...

04 SpringBoot整合Druid/MyBatis/事务/AOP+打包项目
整合Druid 项目结构: 引入依赖: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaL…...
C++程序编译时的_GLIBCXX_USE_CXX11_ABI参数的值选择,适配昇腾Transformer推理加速库与LLM推理模型库
目录 2024/1/19日更新确定已安装G编译测试程序获取宏值安装对应的Transformer LLM推理模型库和Transformer推理加速库小结 2024/1/19日更新 具体使用cxx11abi0 还是cxx11abi1 可通过python命令查询 import torch torch.compiled_with_cxx11_abi()若返回True 则使用 cxx11abi1…...
什么是站群服务器?
网站群服务器是管理多个网站的强大工具,可以帮助站长轻松管理和维护多个网站,提高网站运营效率。在本文中,我们将讨论站点组服务器的优势,以及为什么它是网站管理员不可或缺的工具。 介绍站群服务器 网站群服务器是一个集中管理…...

《WebKit 技术内幕》之四(3): 资源加载和网络栈
3. 网络栈 3.1 WebKit的网络设施 WebKit的资源加载其实是交由各个移植来实现的,所以WebCore其实并没有什么特别的基础设施,每个移植的网络实现是非常不一样的。 从WebKit的代码结构中可以看出,网络部分代码的确比较少的,它们都在…...

vue3-模板引用
//1.调用ref函数 -> ref对象 const h1Ref ref(null) const comRef ref(null) //组件挂载完毕之后才能获取 onMounted(()>{console.log(h1Ref.value);console.log(comRef.value); })<div class"father"><!-- 通过ref标识绑定ref对象 --><h2 re…...

聚类模型评估指标
聚类模型评估指标-轮廓系数 计算样本i到同簇其它样本到平均距离ai,ai越小,说明样本i越应该被聚类到该簇(将ai称为样本i到簇内不相似度);计算样本i到其它某簇Cj的所有样本的平均距离bij,称为样本i与簇Cj的…...

测试 ASP.NET Core 中间件
正常情况下,中间件会在主程序入口统一进行实例化,这样如果想单独测试某一个中间件就很不方便,为了能测试单个中间件,可以使用 TestServer 单独测试。 这样便可以: 实例化只包含需要测试的组件的应用管道。发送自定义请…...
智能小程序小部件(Widget)媒体组件属性说明和示例代码汇总
camera 基础库 2.2.0 开始支持, 低版本需做兼容处理。 系统相机。相关 API:ty.createCameraContext。这是基于异层渲染的原生组件, 请注意原生组件使用限制。 属性说明 属性名类型默认值必填说明modestringnormal否应用模式,只在初始化时有效ÿ…...
enum的比较
enum的比较 使用enum定义的枚举类是一种引用类型。 前面我们讲到,引用类型比较,要使用equals()方法,如果使用比较,它比较的是两个引用类型的变量是否是同一个对象。 因此,引用类型比较,要始终使用equals…...

网工每日一练(1月15日)
1.某计算机系统由下图所示的部件构成,假定每个部件的千小时可靠度为R,则该系统的千小时的可靠度为 ( D ) 。 2.以下IP地址中,属于网络 201.110.12.224/28 的主机IP是( B )。 A.201.110.12.224 B.201.110.12.238 C.20…...
henauOJ 1113: 计算x的n次方
题目描述 给定x和n,计算出x^n。 0<x<10,0<n<9; 要求定义一个函数:int pow1(int x,int n);//返回x^n。 本题只允许提交函数及其内容,提交多余内容会编译错误 C/C函数原型 int pow1(int x,int n); Java函数原型 public static int pow1(int…...

64.Spring事件监听的核心机制是什么?
Spring事件监听的核心机制是什么? spring的事件监听有三个部分组成 事件(ApplicationEvent) 负责对应相应监听器 事件源发生某事件是特定事件监听器被触发的原因监听器(ApplicationListener) 对应于观察者模式中的观察者。监听器监听特定事件,并在内部定义了事件发生后的响应…...

《C++大学教程》3.12Account类
题目: (Account类)创建一个名为Account 的类,银行可以使用它表示客户的银行账户。这个类应该包括一个类型为int 的数据成员,表示账户余额。【注意:在后续章节中,将使用称为浮点值的包含小数点的数(例如2.75)表示美元数。】 这个…...
【工作记录】基于springboot3+springsecurity实现多种方式登录及鉴权(二)
前言 上篇文章介绍了基于springboot3+springsecurity实现的基于模拟数据的用户多种方式登录及鉴权的流程和代码实现,本文我们继续完善。 主要完善的点 主要通过如下几个点来完成优化和完善: 用户信息获取通过查询mysql数据库实现token生成方式使用jwt用户信息存储及读取使用…...
CSS笔记III
选择器 结构伪类选择器 作用:根据元素的结构关系查找元素 选择器说明E:first-child查找第一个E元素E:last-child查找最后一个E元素E:nth-child(N)查找第N个E元素(第一个元素N值为1) nth-child(公式) 作用是可以根据元素的结构关系查找多个元素 偶数标签&#x…...
openssl3.2 - 官方demo学习 - mac - siphash.c
文章目录 openssl3.2 - 官方demo学习 - mac - siphash.c概述笔记END openssl3.2 - 官方demo学习 - mac - siphash.c 概述 MAC算法为 SIPHASH, 设置参数(C-rounds, D-rounds, 也可以不设置, 有默认值) 用key初始化MAC算法, 算明文的MAC值 笔记 /*! \file siphash.c \note op…...

(六)深入理解Bluez协议栈之“GATT Client Profile”
前言: 本章节我们继续介绍GATT Client Profile的实现,参考的程序是tools\btgatt-client.c,需要注意的一点,在./configure时,需要添加 --enable-test --enable-testing才会编译该c文件,编译完成后,生成的可执行程序为btgatt-client。本文主要以btgatt-client运行时可能会…...

SVO编译
文章目录 软件版本错误编译运行轨迹路径保存运行TUM数据集 附录针对svo slam的/svo/pose_imu转为tum格式代码 软件版本 ubuntu 20 rosnoeticSVO SLAM虚拟机 windows 11 错误 常见的git clone问题可以使用DevSidecar解决,在 加速服务-基本设置-绑定IP 设置为0.0.0…...

地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...

大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...

cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...

Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...

Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...

视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

基于Java+MySQL实现(GUI)客户管理系统
客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息,对客户进行统一管理,可以把所有客户信息录入系统,进行维护和统计功能。可通过文件的方式保存相关录入数据,对…...