Linux网络——HTTP
一.应用层
我们程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层.
我们上一次写的网络版本计算器就是一个应用层的网络程序。
我们约定了数据的读取,一端发送时构造的数据, 在另一端能够正确的进行解析, 就是ok的. 这种约定, 就是应用层协议。
在应用层我们只负责将数据怎么读取,和怎么构造数据,然后将构造好的数据交给应用层的协议层传输层,再由传输层何其一下的网络协议栈来帮我们完成数据在网络中发送,至于数据在这之间是怎么发送的,什么时候发送的,我们不知道,不清楚,也不关心。
二.认识URL
平时我们俗称的 "网址" 其实就是说的 URL.
1.域名
服务器地址可以是一个IP地址,但是IP地址是点分十进制的字符串,为了方便记忆就有了和IP地址一 一对应的域名。
例如:
- 112.29.213.131 —— www.JD.com
- 36.155.132.55 —— www.baidu.com
2.urlencode和urldecode
像 / ? : 等这样的字符, 已经被url当做特殊意义理解了. 因此这些字符不能随意出现.
比如, 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义.
转义的规则如下:
将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式,这个过程就是urlencode。
"+" 被转义成了 "%2B"。
urldecode就是urlencode的逆过程。
三.HTTP协议格式
1.请求格式http
见一见http请求:
部分测试代码:
编写一个服务器,服务器多线程接受网络数据,使用浏览器在url栏框中输入服务端车程序的IP和端口,然后回车。此时浏览器就会构建一个http请求发送给我们的服务端程序,服务端程序直接将收到的数据直接以字符串的形式输出到终端。
void serverIO(int fd){string message;char buff[102400];// 读取一个完整的http请求报文int n = recv(fd, buff, sizeof(buff), 0);message = buff;// 直接将该请求以字符串的形式输出cout << "得到一个HTTP请求" << endl;cout << message << endl;close(fd);}
得到的http请求:
2.响应格式
见一见http响应:
使用telnet 向百度模拟发送一个http请求,接受返回的http响应。
HTTP协议的格式大致有四部分组成:
- 首行:HTTP请求——[ 方法 ]+[ url ]+[ 版本 ];HTTP响应——[ 版本 ]+[ 状态码 ]+[ 描述 ];其中请求中url实际上就是服务器上的请求的资源路径。
- Header:请求/响应 的属性,冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束。例如:Content-Length:8899,标识 请求/响应 的有效载荷长度,有效载荷的长度是8899。
- 空行:用来分隔Header和Body。
- Body:就是有效载荷部分,空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度;
四.HTTP响应状态码
状态码 | 类别 | 描述 |
1XX | lnformational(信息性状态码) | 接收的请求正在处理 |
2XX | Success(成功状态码) | 请求正常处理完毕 |
3XX | Redirection(重定向状态码) | 需要进行附加操作以完成请求 |
4XX | Client Error(客户端错误状态码) | 服务器无法处理请求 |
5XX | Server Error(服务器错误状态码) | 服务器处理请求出错 |
说明:
Client Error:
- 客户端连接的端口错了。
- 客户端连接的域名或者IP地址错误。
- 如果客户端使用了域名连接,域名可能指向了错误的服务器。
- 当客户端发送了一个无效请求时,服务器可能会因为无法处理而返回 Client Error。
lnformational:
- 主要作用是告知客户端请求已经被接受 。
Redirection:
- 是服务器在处理客户端请求时,为了保证流程的顺利进行而发出的一种信号。
- 当客户端发送一个请求到服务器,服务器会根据需要将请求重定向到另一个URL。
Success:
- 表示客户端与服务器之间的通信已经成功完成,并且服务器已经处理了客户端的请求。
Server Error:
- 表示服务器在处理客户端请求时发生了错误。
- 服务器遇到了一个未知的错误,无法完成请求的处理。
- 服务器当前无法处理请求,因为过载或维护。
五.HTTP常见Header
Header是以K/V形式存储的字符串,主要是一些协议的属性说明等。
- Content-Type: 数据类型(text/html等)
- Content-Length: Body的长度
- Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;
- User-Agent: 声明用户的操作系统和浏览器版本信息;
- referer: 当前页面是从哪个页面跳转过来的;
- location: 搭配3xx(重定向)状态码使用, 告诉客户端接下来要去哪里访问;
- Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能;
介绍Content-Type:表明此次http报文的body传输的是什么内容。
对于服务器上的每一种资源,都是以文件的形式存在的,文件都会有自己的类型,例如(网页文件).html,(音频文件).mp3,(图片).jpg/.png,(视频).mp4。
每一种文件类型,都会有自己对应的Content-Type类型:
- text/html:HTML 文档
- text/css:CSS 样式表
- text/javascript:JavaScript 脚本
- image/jpeg:JPEG 图像
- image/png:PNG 图像
- image/gif:GIF 图像
Content-Type 对照表
六.简单的HTTP服务器
编写一个服务器,多线程处理请求,对于请求的处理,解析一个http请求反序列化,根据http的请求的资源,构建响应并返回。
Sock.hpp
#pragma once
#include <iostream>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "Log.hpp"
#define TCP SOCK_STREAM
#define UDP SOCK_DGRAM
const static int backlog = 32;enum
{SOCK_ERR = 10,BING_ERR,LISTEN_ERR,CONNECT_ERR
};class Udp
{
public:Udp(int SOCK){_listensock = socket(AF_INET, SOCK, 0);if (_listensock == -1){Logmessage(Fatal, "socket err ,error code %d,%s", errno, strerror(errno));exit(SOCK_ERR);}}Udp(uint16_t port, int SOCK): _port(port){_listensock = socket(AF_INET, SOCK, 0);if (_listensock == -1){Logmessage(Fatal, "socket err ,error code %d,%s", errno, strerror(errno));exit(10);}}void Bind(){struct sockaddr_in host;host.sin_family = AF_INET;host.sin_port = htons(_port);host.sin_addr.s_addr = INADDR_ANY; // #define INADDR_ANY 0x00000000socklen_t hostlen = sizeof(host);int n = bind(_listensock, (struct sockaddr *)&host, hostlen);if (n == -1){Logmessage(Fatal, "bind err ,error code %d,%s", errno, strerror(errno));exit(BING_ERR);}}int FD(){return _listensock;}~Udp(){close(_listensock);}protected:int _listensock;uint16_t _port;
};class Tcp : public Udp
{
public:Tcp(uint16_t port): Udp(port, TCP){}Tcp(): Udp(TCP){}void Listen(){int n = listen(_listensock, backlog);if (n == -1){Logmessage(Fatal, "listen err ,error code %d,%s", errno, strerror(errno));exit(LISTEN_ERR);}}int Accept(string *clientip, uint16_t *clientport){struct sockaddr_in client;socklen_t clientlen;int sock = accept(_listensock, (struct sockaddr *)&client, &clientlen);if (sock < 0){Logmessage(Warning, "bind err ,error code %d,%s", errno, strerror(errno));}else{*clientip = inet_ntoa(client.sin_addr);*clientport = ntohs(client.sin_port);}return sock;}void Connect(string ip, uint16_t port){struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(port);server.sin_addr.s_addr = inet_addr(ip.c_str());socklen_t hostlen = sizeof(server);int n = connect(_listensock, (struct sockaddr *)&server, hostlen);if (n == -1){Logmessage(Fatal, "Connect err ,error code %d,%s", errno, strerror(errno));exit(CONNECT_ERR);}}~Tcp(){}
};
Server.hpp
#pragma once
#include <iostream>
#include <string>
#include <pthread.h>
#include <functional>
#include "Sock.hpp"using fun_t = std::function<string(string &)>;
class Httpserver;
struct Args
{Args(Httpserver *ser, string ip, uint16_t port, int fd): _ip(ip), _port(port), _pserver(ser), _fd(fd){}int _fd;uint16_t _port;string _ip;Httpserver *_pserver;
};class Httpserver
{
public:Httpserver(fun_t func, uint16_t port): _func(func){tcp = new Tcp(port);tcp->Bind();tcp->Listen();cout << "服务器创建成功" << endl;}void start(){while (1){string clientip;uint16_t clientport;cout << "start accept" << endl;int sock = tcp->Accept(&clientip, &clientport);cout << "get a new connect" << endl;// 多线程处理请求pthread_t t;Args *args = new Args(this, clientip, clientport, sock);pthread_create(&t, nullptr, ThreadRun, args);}}~Httpserver(){delete tcp;}private:static void *ThreadRun(void *args){pthread_detach(pthread_self());Args *ts = static_cast<Args *>(args);ts->_pserver->serverIO(ts->_fd);delete ts;return nullptr;}void serverIO(int fd){string message;char buff[102400];// 1.确信,读取一个完整的http请求报文int n = recv(fd, buff, sizeof(buff), 0);message = buff;string re = _func(message);send(fd, re.c_str(), re.length(), 0);close(fd);}private:Tcp *tcp;fun_t _func;
};
Server.cc
#include <sstream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/socket.h>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <vector>
#include "Server.hpp"
#include "util.hpp"const string wwwroot = "./html/3";
const string defaupath = "/index.html";class HttpRequest
{
public:HttpRequest(){}~HttpRequest() {}void Print(){cout << method_ << ":" << url_ << ":" << httpVersion_ << endl;for (auto &e : body_)cout << e << endl;cout << "suffix:" << suffix_ << endl;cout << "path:" << path_ << endl;}public:std::string method_; // 请求方法std::string url_; // 资源地址std::string httpVersion_; // 协议版本std::vector<std::string> body_; // 报头std::string path_; // 请求资源路径std::string suffix_; // 资源类型
};HttpRequest Deserialize(string &message)
{HttpRequest req;// 1.拿到请求行string oneline = HeadOneLine(message, SEP);// 2.解析请求行DisposeOneLine(oneline, &req.method_, &req.url_, &req.httpVersion_);// 3.解析反序列化报头while (!message.empty()){string mes = HeadOneLine(message, SEP);req.body_.push_back(mes);}// 4.设置请求资源路径 url:/a/b/cif (req.url_[req.url_.length() - 1] == '/'){req.path_ = wwwroot + defaupath; //./html/index.html}else{req.path_ = wwwroot + req.url_; //./html/a/b/c}// 5.设置请求资源类型auto pos = req.url_.find('.');if (pos == string::npos)req.suffix_ = ".html";elsereq.suffix_ = req.url_.substr(pos);return req;
}string Dispose(string &message)
{// 这里我们一定读取的是一个完整的报文// 一个网页会有很多的资源,每一个资源(网页,图片,视频),都需要一次http请求来得到// 所以我们需要知道,每次请求的资源是什么,即需要知道请求的url是什么,url的类型是什么// 反序列化请求HttpRequest req = Deserialize(message);req.Print();// 响应————最简单的一个响应// 4.有效载荷string body = Readfile(req.path_) + SEP;// 1.响应的状态行————"HTTP版本 状态码 状态描述\r\n"string request_head = string("HTTP/1.0 200 OK") + SEP;// 2.响应报头————Content-Length,Content-Typerequest_head += string("Content-Length: ") + to_string(body.length()) + SEP;request_head += GetContentType(req.suffix_) + SEP;// 3.空行request_head += SEP;// 整体的响应报文string responce = request_head + body;return responce;
}void daemonize()
{// 1.忽略SIGPIPE信号signal(SIGPIPE, SIG_IGN);// 2.更改进程的工作目录// chdir();// 3.让自己不要成为进程组组长if (fork() > 0)exit(0);// 4.设置自己是一个独立的会话setsid();// 5.重定向0,1,2int fd = 0;if (fd = open("dev/null", O_RDWR) != -1){dup2(fd, STDIN_FILENO);dup2(fd, STDOUT_FILENO);dup2(fd, STDERR_FILENO);// 6.关闭掉不需要的fdif (fd > STDERR_FILENO){close(fd);}}
}int main(int argc, char *argv[])
{daemonize();uint16_t port = atoi(argv[1]);Httpserver httpser(Dispose, port);httpser.start();return 0;
}
Util.hpp
#pragma once
#include <iostream>
#include <unistd.h>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>#define SEP "\r\n"
using namespace std;string Readfile(const string path)
{// 1. 获取文件本身的大小string fileContent;struct stat st;int n = stat(path.c_str(), &st);if (n < 0)return "";int size = st.st_size;// 2. 调整string的空间fileContent.resize(size);// 3. 读取int fd = open(path.c_str(), O_RDONLY);if (fd < 0)return "";read(fd, (char *)fileContent.c_str(), size);close(fd);return fileContent;
}string HeadOneLine(string &message, const string &sep)
{auto pos = message.find(sep, 0);if (pos == string::npos)return "";string oneline = message.substr(0, pos);message.erase(0, pos + sep.size());return oneline;
}void DisposeOneLine(const string &oneline, string *method, string *url, string *httpVersion)
{stringstream line(oneline);line >> *method >> *url >> *httpVersion;
}string GetContentType(const string &suffix)
{std::string content_type = "Content-Type: ";if (suffix == ".html" || suffix == ".htm")content_type + "text/html";else if (suffix == ".css")content_type += "text/css";else if (suffix == ".js")content_type += "application/x-javascript";else if (suffix == ".png")content_type += "image/png";else if (suffix == ".jpg")content_type += "image/jpeg";else{}return content_type;
}
效果展示:
七.HTTP的方法
HTTP的方法有很多但是最常用的只有两个:GET,POST。
1.GET方法
我们要促使浏览器使用不同的方法进行资源请求,提交,要使用html的表单。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>CSDN</title>
</head><body><h1>test</h1><form action="/a/b/c.exe" method="get">姓名: <input type="text" name="myname" value=""><br />密码: <input type="password" name="mypasswd"><br /><input type="submit" value="提交"><br /></form></body></html>
尝试提交数据:
2.POST方法
再次尝试提交数据:
区别:
- GET能获取一个静态网页,GET也能提交参数,通过URL的方式提交参数。
- POST请求,提交的参数的时候,是通过正文的部分提交的参数。
- GET方法提交参数,不私密(没有不安全的说法),安全对HTTP来说本身就没有保障。
- POST提交参数比较私密一些。
- 由于GET使用url提交参数,所以大小一般会受限。
- POST使用正文提交参数,正文理论上可以非常大。
相关文章:

Linux网络——HTTP
一.应用层 我们程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层. 我们上一次写的网络版本计算器就是一个应用层的网络程序。 我们约定了数据的读取,一端发送时构造的数据, 在另一端能够正确的进行解析, 就是ok的. 这种约定, 就是应…...
ElasticSearch综合练习题,ES为8版本,使用Kibana运行语句
文章目录 前言一、ES查询集群情况二、ES索引习题查询所有索引查询单个索引 三、ES增删改查数据单条处理批量处理 四、雇员查询练习题五、学生查询练习题六、商品信息联系题其他:一问一答参考文档 前言 ES8版本没有type概念,所以语法可能会与其他版本有差…...

Java方法中不使用的对象应该手动赋值为NULL吗?
在java方法中,不使用的对象是否应该手动赋值为null?我们先来通过一个示例看一下。 垃圾回收示例一 public class GuoGuoTest {public static void main(String[] args) {byte[] placeholder new byte[64 * 1024 * 1024];System.gc();} } 上面代码向内…...

Mysql主从搭建
Mysql主从搭建 1.Mysql下载1.1 查看操作系统2.2 下载mysql安装包 2.Mysql安装2.1 解压2.2 目录重命名2.3 创建data,存储文件2.4 创建用户组2.5 授权用户2.6 配置环境变量2.7 编辑my.cnf2.8 创建相关目录和文件2.9 初始化数据库2.10 复制mysql.server到/etc/init.d/下…...

WPF程序给按钮增加不同状态的图片
首先我们在资源里添加几个图片,Up,Over和Down状态。 然后我们创建一个Style。默认我们的背景设置成Up 然后在Triggers里添加代码,当Property:IsMouseOver为True的时候更换成Over;当Property:IsPressed为Tr…...
Java编程陷阱(三)
陷阱11:不要使用StringBuffer类来拼接字符串 StringBuffer是Java中的一个类,它可以表示一个可变的字符串,也就是可以对字符串进行修改和追加的操作,比如使用append或insert方法来拼接字符串。有时候,我们需要使用StringBuffer类来拼接字符串,比如在循环中动态地构建一个字…...
数据仓库相关
在阿里巴巴的数据体系中,我们建议将数据仓库分为三层,自下而上为:数据引入层(ODS,Operation Data Store)、数据公共层(CDM,Common Data Model)和数据应用层(…...
SpringBoot学习笔记-创建个人中心页面(下)
笔记内容转载自 AcWing 的 SpringBoot 框架课讲义,课程链接:AcWing SpringBoot 框架课。 CONTENTS 1. 实现个人中心页面2. POJO时区修改3. 集成代码编辑器 本节实现个人中心的前端页面,用户能够查看自己的 Bot 信息,并能创建、修改…...
电子秤方案:做一个宠物勺方案设计
养宠物是一件费心劳力的事情,但同时也是能够给你带来快乐和幸福感的事情。就是有时候会怕宠物毫无征兆地生病令人措手不及,所以电子秤方案设计鼎盛合科技分享一个小方案,能够及时了解到宠物的身体状况问题。 蓝牙宠物勺是一种具有记录和称重…...

Debezium-Embedded 实时监控MySQL数据变更
1.Debezium-Embedded 简介 Debezium连接器的操作通常是将它们部署到Kafka Connect服务,并配置一个或多个连接器来监控上游数据库,并为它们在上游数据库中看到的所有更改生成数据更改事件。这些数据更改事件被写入Kafka,在那里它们可以被许多不…...

计算机是如何工作的(简单介绍)
目录 一、冯诺依曼体系 二、CPU基本流程工作 逻辑⻔ 电⼦开关——机械继电器(Mechanical Relay) ⻔电路(Gate Circuit) 算术逻辑单元 ALU(Arithmetic & Logic Unit) 算术单元(ArithmeticUnit) 逻辑单元(Logic Unit) ALU 符号 寄存器(Regis…...

JSP基本表单和Request对象使用例子
表单的jsp; <%page contentType"text/html;charsetgbk" pageEncoding"UTF-8"%> <!DOCTYPE html> <html><head><meta http-equiv"Content-Type" content"text/html; charsetUTF-8"><titl…...

【Redux】Redux 基本使用
1. Redux 快速上手 Redux 是 React 最常用的集中状态管理工具,类似于Vue中的Pinia(Vuex),可以独立于框架运行。 <button id"decrement">-</button> <span id"count">0</span> <…...

多线程Thread(初阶一:认识线程)
目录 一、引用线程的原因 二、线程的概念 三、进程和线程的区别 四、多线程编程 一、引用线程的原因 多任务操作系统,希望系统能同时运行多个任务。所以会涉及到进程,需要对进程进行管理、调度等。 而单任务操作系统,就完全不涉及到进程…...

系列五、GC垃圾回收【四大垃圾算法-复制算法】
一、堆的内存组成 二、复制算法 2.1、发生位置 复制算法主要发生在新生代,发生在新生代的垃圾回收也被叫做Minor GC。 2.2、 Minor GC的过程 复制>清空》交换 1、eden、from区中的对象复制到to区,年龄1 首先,当eden区满的时候会触发第一…...

LeetCode(24)文本左右对齐【数组/字符串】【困难】
目录 1.题目2.答案3.提交结果截图 链接: 文本左右对齐 1.题目 给定一个单词数组 words 和一个长度 maxWidth ,重新排版单词,使其成为每行恰好有 maxWidth 个字符,且左右两端对齐的文本。 你应该使用 “贪心算法” 来放置给定的单…...
Spring-Spring之事务底层源码解析
EnableTransactionManagement工作原理 开启Spring事务本质上就是增加了一个Advisor,但我们使用EnableTransactionManagement注解来开启Spring事务是,该注解代理的功能就是向Spring容器中添加了两个Bean: AutoProxyRegistrarProxyTransactio…...

后端面经学习自测(三)
文章目录 1、ArrayList和Linkedlist区别?2、ArrayList扩容机制?3、ArrayList和Linkedlist分别能做什么场景?4、事务特性?MySQL事务Redis事务Spring事务5、在Spring中事务失效的场景?6、Java泛型?7、泛型擦除…...
力扣labuladong——一刷day40
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、力扣341. 扁平化嵌套列表迭代器 前言 N叉树的结构,构造迭代器 一、力扣341. 扁平化嵌套列表迭代器 /*** // This is the interface that allo…...

在VS Code中使用VIM
文章目录 安装和基本使用设置 安装和基本使用 VIM是VS Code的强大对手,其简化版本VI是Linux内置的文本编辑器,堪称VS Code问世之前最流行的编辑器,也是VS Code问世之后,我仍在使用的编辑器。 对VIM无法割舍的原因有二࿰…...

网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...

VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...

使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...