GoWeb开发
学习目标:
本篇要达到的目的,能为后续复习提供极大便利。
(当我写下本篇博客时,已复习3遍)
一、网络通信概述
(为本篇基础核心内容)
1、什么是网络通信?
网络通信是指不同设备(如计算机、手机、服务器等)通过计算机网络进行数据交换和信息传递的过程。其核心目标是实现设备之间的互联互通,让数据能够准确、高效地从发送端传输到接收端。
2、网络通信的核心组成部分
-
硬件层面
- 终端设备:发送或接收数据的设备(如手机、电脑、服务器、物联网传感器)。
- 传输介质:数据传输的物理 / 无线通道,包括:
- 有线介质:双绞线、同轴电缆、光纤(速度快、稳定性高)。
- 无线介质:无线电波、微波、蓝牙、Wi-Fi、5G(灵活性高,适合移动场景)。
- 网络设备:负责数据转发、路由、信号放大等,如路由器、交换机、调制解调器(Modem)、集线器。
-
软件层面
- 通信协议:规定数据格式、传输规则和交互流程的 “语言”(如 TCP/IP、HTTP、FTP)。
- 操作系统与应用程序:提供网络接口(如 Socket 编程接口),支持上层应用(如浏览器、邮件客户端)实现通信。
3、网络通信的工作原理
-
分层模型(以 TCP/IP 为例)
为简化复杂问题,网络通信采用分层架构,每层负责特定功能,层间通过接口交互。- 应用层:直接为用户程序提供服务(如 HTTP 用于网页浏览,SMTP 用于邮件传输)。
- 传输层:确保端到端的数据传输,主要协议:
- TCP(面向连接,可靠传输,如网页加载、文件传输)。
- UDP(无连接,不可靠但高效,如视频流、实时通信)。
- 网络层:负责网络间的路由和寻址,核心协议是IP(为设备分配 IP 地址,确定数据传输路径)。
- 数据链路层:在相邻设备间传输数据,处理物理地址(MAC 地址)和错误检测(如以太网协议)。
- 物理层:定义物理设备的电气、机械特性(如电压、接口标准)。
-
数据传输流程
- 发送端:数据从应用层逐层封装(添加头部信息),最终通过物理层发送。
- 接收端:数据从物理层逐层解封装(去除头部信息),最终传递给应用层处理。
4、网络通信的主要类型
-
按通信对象分类
- 点对点(Point-to-Point):两台设备直接通信(如蓝牙设备配对)。
- 点对多点(Point-to-Multipoint):一台设备向多台设备发送数据(如广播、组播)。
- 端到端(End-to-End):跨越多个网络设备,最终在两个终端间建立逻辑连接(如通过路由器连接的两台远程电脑)。
-
按通信方式分类
- 同步 vs 异步:
- 同步:发送方等待接收方响应(如 TCP 请求 - 响应模式)。
- 异步:发送方无需等待,直接继续执行(如 UDP 发送数据后不等待确认)。
- 面向连接 vs 无连接:
- 面向连接:先建立连接(如 TCP 的三次握手),再传输数据(可靠但开销大)。
- 无连接:直接发送数据(如 UDP,适合实时性要求高的场景)。
- 同步 vs 异步:
5、关键网络协议
-
基础协议
- TCP/IP:互联网的核心协议簇,定义了网络通信的完整架构(包含 IP、TCP、UDP 等)。
- IP(Internet Protocol):负责设备寻址和路由(IPv4/IPv6)。
- TCP(Transmission Control Protocol):提供可靠的字节流传输,确保数据无丢失、无乱序。
- UDP(User Datagram Protocol):提供轻量、快速的数据包传输(不保证可靠性)。
-
应用层协议
- HTTP/HTTPS:用于网页浏览(HTTPS 加密传输)。
- FTP/SFTP:文件传输协议(SFTP 加密)。
- SMTP/POP3/IMAP:邮件传输与接收协议。
- WebSocket:支持浏览器与服务器间的双向实时通信(如在线聊天、实时数据更新)。
二、Socket
基础概念
第一次复习:
如果要我解释socket,他就像一门面,封装着各种函数。
是一个接口API,正着说可能会让人误解。
反着说,socket既不是某种协议,也不是id+端口号的集合,
而是操控协议与这个地址结合的工具。
他封装着一组函数,通过接口的性质,进行通信。超级方便的哦。
(想用即拿)
第二次复习:
Socket 通信基于客户端 - 服务器(Client - Server)模型
小demo
根据本图,写相应的代码:
server
package mainimport ("fmt""net""strings"
)type User struct {Username stringOthername stringMsg stringServerMsg string
}var (user = new(User)userMap = make(map[string]net.Conn)
)func main() {// 地址addr, _ := net.ResolveTCPAddr("tcp4", "localhost:8889")lis, _ := net.ListenTCP("tcp4", addr)// 循环接收连接for {conn, _ := lis.Accept()go func() {for {b := make([]byte, 1024)count, _ := conn.Read(b)array := strings.Split(string(b[:count]), "-")user.Username = array[0]user.Othername = array[1]user.Msg = array[2]user.ServerMsg = array[3]// 加入对方userMap[user.Username] = connif v, ok := userMap[user.Othername]; ok && v != nil { // 存在 且 不为空n, err := v.Write([]byte(fmt.Sprintf("%s-%s-%s-%s", user.Username, user.Othername, user.Msg, user.ServerMsg)))// 关闭连接if n <= 0 || err != nil {fmt.Println("无效格式")delete(userMap, user.Othername)conn.Close()return} else {fmt.Println("发送成功")}} else {user.ServerMsg = "对方不在线"conn.Write([]byte(fmt.Sprintf("%s-%s-%s-%s", user.Username, user.Othername, user.Msg, user.ServerMsg)))}void }}()}
}
client
package mainimport ("fmt""net""os""strings""sync"
)type User struct {Username stringOthername stringMsg stringServerMsg string
}var (user = new(User)wg sync.WaitGroup
)func main() {wg.Add(1)fmt.Println("请输入你的账号")fmt.Scanln(&user.Username)fmt.Println("请输入你要给谁发送消息")fmt.Scanln(&user.Othername)addr, _ := net.ResolveTCPAddr("tcp4", "localhost:8889")conn, _ := net.DialTCP("tcp4", nil, addr)// 发送go func() {for {fmt.Println("请输入,你要发给谁?仅仅只提示一次")fmt.Scanln(&user.Msg)if user.Msg == "exit" {conn.Close()wg.Done()os.Exit(0)}n, _ := conn.Write([]byte(fmt.Sprintf("%s-%s-%s-%s", user.Username, user.Othername, user.Msg, user.ServerMsg)))fmt.Println(n, "发送成功")}}()// 接收go func() {for {rb := make([]byte, 1024)c, _ := conn.Read(rb)user2 := new(User)array := strings.Split(string(rb[:c]), "-")if len(array) < 3 {fmt.Println("无效格式:", array)break}user2.Username = array[0]user2.Othername = array[1]user2.Msg = array[2]user2.ServerMsg = array[3]if user2.ServerMsg != "" {fmt.Println("\t\t\t服务器消息:", user2.ServerMsg)} else {fmt.Println(user2.Username, ":", user2.Msg)}}}()wg.Wait()
}
三、Mysql
对数据库的操作:
开始之前的基操
create database goWeb;
use goWeb;
create table people(id int primary key auto_increment,name varchar(20),address varchar(100)
);
desc people;
select * from people;
增:
package mainimport ("database/sql""fmt"_ "github.com/go-sql-driver/mysql"
)/*数据库的连接是一个非常有趣的玩意[user[:password]@][net[(addr)]]/dbname[?param1=value1¶m2=value2...]还有一个奇怪的点,就是必须要导入_ "github.com/go-sql-driver/mysql"因为,他中的init的函数,是sql与go之间的桥梁,起到注册作用register但是,我没有理解透,感觉好难受
*/func main() {// 1、打开链接db, err := sql.Open("mysql", "root:1234@tcp(localhost:3306)/goweb")db.Ping()defer func() {if db != nil {db.Close()}}()if err != nil {fmt.Println("数据库连接错误", err)}// 2、预处理SQL// ?表示占位符stmt, err := db.Prepare("insert into people values(default,?,?)")if err != nil {fmt.Println("预处理失败:", err)}defer func() {if stmt != nil {stmt.Close()}}()r, _ := stmt.Exec("张三", "上海")// 3、获取结果count, _ := r.RowsAffected()fmt.Println("修改了:", count)// 获取最后修改的主键fmt.Println(r.LastInsertId())
}
删:
package mainimport ("database/sql""fmt"_ "github.com/go-sql-driver/mysql"
)func main() {// 连接db, err := sql.Open("mysql", "root:1234@tcp(localhost:3306)/goWeb")if err != nil {fmt.Println("连接失败", err)}defer db.Close()// 预处理stmt, err := db.Prepare("delete from people where id = 2")if err != nil {fmt.Println("预处理失败:", err)}defer stmt.Close()r, _ := stmt.Exec()// 预处理count, _ := r.RowsAffected()if count > 0 {fmt.Println("删除成功")} else {fmt.Println("负责删除失败")}}
改:
package mainimport ("database/sql""fmt"_ "github.com/go-sql-driver/mysql"
)/*
这里有一个很有趣的事情,如果修改没变化,则修改失败
*/
func main() {// 连接db, err := sql.Open("mysql", "root:1234@tcp(localhost:3306)/goWeb")db.Ping()if err != nil {fmt.Println("失败啦", err)}defer db.Close()// 预处理stmt, err := db.Prepare("update people set name = ?,address = ? where id=3 ;")if err != nil {fmt.Println("预处理失败:", err)}defer stmt.Close()r, _ := stmt.Exec("朝阳", "新乡")// 查看修改情况count, _ := r.RowsAffected()if count > 0 {fmt.Println("修改成功")} else {fmt.Println("修改失败")}
}
查:
package mainimport ("database/sql""fmt"_ "github.com/go-sql-driver/mysql"
)/*
写完之后,没啥感受,只是在想,这玩意咋都长一个样,背背方法就过去了,
可是好像了解了解底层呐
*/
func main() {// 连接db, err := sql.Open("mysql", "root:1234@tcp(localhost:3306)/goWeb")if err != nil {fmt.Println("连接失败", err)}defer db.Close()// 预处理stmt, err := db.Prepare("select * from people")if err != nil {fmt.Println("预处理失败:", err)}defer func() {if stmt != nil {stmt.Close()}}()// 获取rows, err := stmt.Query()if err != nil {fmt.Println("获取值出错", err)}for rows.Next() {var id intvar name, address stringrows.Scan(&id, &name, &address)fmt.Println(id, " ", name, " ", address)}defer rows.Close() // 关闭结果集
}
四、goWeb
控制器
当你再次回来看时,希望这个能加深你的理解:以下三个函数,的区别
Handler 接口 定义 HTTP 处理逻辑的规范(要求实现 ServeHTTP 方法) 所有 HTTP 处理程序必须直接或间接实现此接口
Handle 函数 将一个 Handler 实例绑定到 URL 路径模式(pattern)
参数 handler 必须是 Handler 接口的实现
HandleFunc 函数 便捷方式:将一个函数包装为 Handler 接口的实现,并绑定到 URL 路径模式
本质是 Handle(pattern, HandlerFunc(handler)),简化了手动实现接口的步骤
拓展,实现了handler接口的对象实例,都能被Handle调用。
单控制器
package mainimport ("net/http"
)/*何其抽象,这只是一个但控制器其实就是用结构体,实现一个端口
*/type MyHander struct {
}func (m *MyHander) ServeHTTP(w http.ResponseWriter, r *http.Request) {w.Write([]byte("返回的数据"))
}// 单控制器
func main() {m := MyHander{}server := http.Server{Addr: "localhost:8081",Handler: &m, // 一旦绑定在这里,无论访问什么路径,结果都是这个}server.ListenAndServe()//if err := server.ListenAndServe(); err != nil {// fmt.Printf("服务器启动失败: %v\n", err) // 打印具体错误(如端口冲突)//}}
多控制器
package mainimport "net/http"/*好抽象的呢,既然是重写函数,却要重写的一模一样,抽象啦简直气死我了捋一捋思路,发现就是1、先建立服务器2、注册路由3、监听函数
*/
/*
func first(w http.ResponseWriter, r *http.Request) {fmt.Fprintln(w, "first")
}
func second(w http.ResponseWriter, r *http.Request) {fmt.Fprintln(w, "second")
}func main() {// 多控制函数server := http.Server{Addr: "localhost:8081",}// 注册路由http.HandleFunc("/first", first)http.HandleFunc("/second", second)server.ListenAndServe()}
*/// 第二套就是绑定结构体
/*其实多控制器,用结构体,我觉得有点累赘和臃肿首先重写多个结构体,实现接口,然后将每个结构体,依次绑定到服务器上。与其用Handle绑定,不如直接用HandleFunc直接绑定但控制器,就是绑定一个url,多控制器就是绑定多个url。
*/
type Handle struct {
}func (m *Handle) ServeHTTP(w http.ResponseWriter, r *http.Request) {w.Write([]byte("一号"))
}type Handler struct{}func (m *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {w.Write([]byte("二号"))
}func main() {h1 := Handle{}h2 := Handler{}// 重写结构体server := http.Server{Addr: "localhost:8081",}http.Handle("/first", &h1)http.Handle("/second", &h2)// 监听server.ListenAndServe()}
请求头与请求参数
请求头
package main
import ("fmt""net/http"
)func param(w http.ResponseWriter, r *http.Request) {fmt.Fprintln(w, "第一个")// 请求头//fmt.Fprintln(w, r.Header)// 为了便于读写代码var acc []string = r.Header["Accept"]for _, n := range acc {fmt.Fprintln(w, "Accepth内容", n)}
}func main() {// 建立服务器server := http.Server{Addr: "localhost:8081",}// 绑定urlhttp.HandleFunc("/param", param)// 开启监听模式server.ListenAndServe()
}
请求参数
(可在url 或 请求体中)
package mainimport ("fmt""net/http"
)
/*
这里有个有意思的事情,是要用ParseForm去解析表单,才能用r.Form查到
因为,需要ParseForm更新并放置于了Form中
*/
func param(w http.ResponseWriter, r *http.Request) { // 我的名字叫做解析// 对请求头解析h := r.Header // header是一个map类型,选中key值后,返回的是一个string类型的切片fmt.Fprintln(w, h["Accept-Encoding"][0])// 必须先解析成formr.ParseForm()fmt.Fprintln(w, r.Form)}func main() {// 建立服务器server := http.Server{Addr: "localhost:8081",}http.HandleFunc("/param", param)// 开始作为服务端监听server.ListenAndServe()
}
html模板与静态资源
main
package mainimport ("fmt""html/template""net/http"
)/*第一遍学习时:这个解析模板,我有点不理解不是啊,哥们,这有点抽象第一遍复习:其实,我很无奈,因为我的目录与课程目录不同倒逼我去理解,某些函数的作用开始时,我最苦恼的是,StripPerfex与FileServer的作用。原来他的作用,就是解析。FileServer起一个拼接的作用,一旦出现 /static/ 拼接的作用,就开始显现url中如Handle的作用
*/// 与其绑定的url操作
func welcome(w http.ResponseWriter, r *http.Request) {t, err := template.ParseFiles("GoWebDevelopment/basis/htmlTest/view/index.html")if err != nil {fmt.Println("出错了: ", err)}t.Execute(w, nil)
}func main() {// 服务器server := http.Server{Addr: "localhost:8081",}http.HandleFunc("/1", welcome)http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("GoWebDevelopment/basis/htmlTest/static"))))// 开启监听server.ListenAndServe()
}
html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script type="text/javascript" src="/static/js/index.js"></script>
</head>
<body>你好啊,哥们<button onClick="myClick()"></button>
</body>
</html>
js
function myClick(){alert("您点击了按钮")
}
函数/数据->模板
难道看到这里你不好奇吗?
模板--数据,难道你不好奇吗,中间是怎么传输数据的?中间的具体过程,以及底层实现
第一次复习:我当然知道,不就是reflect嘛,但是我不希望就此止步于此
第二次复习:采用的都是链式调用(使用方法的精髓)
数据->模板
1、向模板传递参数
2、向模板传递结构体
3、向模板传递map
函数->模板
main
package mainimport ("fmt""html/template""net/http""time"
)/*说实话走到这里有一个非常抽象的事情,就是模板时间,你所设置的模板时间必须与这个一模一样这个是模板时间的整体性:"2006-01-02 15:04:05"其实最抽象的是FuncMap这个绑定的函数,我在这里错了好久。起因却是因为key-value出了问题。html文件中,用的函数名,就是key值,我之前写的却是fm := template.FuncMap{"mt": GetTime}--“mt” 但是,html中绑定的确是fm,这完全是混洗了概念的如果不明白上述说的啥,请让ai回溯
*/func GetTime(t time.Time) string {return t.Format("2006-01-02")
}func welcome(w http.ResponseWriter, r *http.Request) {curtime := time.Date(2018, 1, 2, 3, 4, 5, 0, time.Local)// time.Format:大致意思,就是你给他一个格式,他按照你给的格式编辑。// 解析成合适的函数fm := template.FuncMap{"fm": GetTime}// 绑定t := template.New("index.html").Funcs(fm)t, err := t.ParseFiles("GoWebDevelopment/basis/htmlTest1/PassFunction/view/index.html")if err != nil {fmt.Println("调用失败:", err)}t.Execute(w, curtime) // 这个是暂时的
}// 打着这个旗号
func main() {// 创建服务器server := http.Server{Addr: "localhost:8082",}http.HandleFunc("/2", welcome)server.ListenAndServe()
}
html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>这是是北京时间:{{.}}<br>今天是{{.Year}}年<br>格式化输出就是{{.Format "2006-01-02"}}<br>{{fm .}}
</body>
</html>
Action
if使用
二元比较(隶属于if)
if..else..if...else
range
main
package mainimport ("fmt""html/template""net/http"
)/*
1、测试变量-$
2、测试 if and if else
3、测试 range
*/func welcome(w http.ResponseWriter, r *http.Request) {t, err := template.ParseFiles("GoWebDevelopment/basis/action/view/index.html")if err != nil {fmt.Println("解析出错: ", err)}// []stringvarInt := []int{1, 2, 3, 4, 5}t.Execute(w, varInt)
}func main() {server := http.Server{Addr: "localhost:8081",}http.HandleFunc("/1", welcome)server.ListenAndServe()
}
html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><!--第一目,变量-->
{{$n:=100}}<!-- 第二目,if-else -->{{if gt $n 101}}你好呀<br>{{else}}好遗憾,他没看到呢<br>{{end}}<!-- 第三目,遍历 -->
{{range .}}
{{.}}<br> <!-- 遍历内部参数 -->
{{end}}</body>
</html>
模板嵌套
若我没猜错,你一定会回来看的。
用我复习3遍的经验告诉你,你可以直接看代码
或许你看着他们特别的复杂,其实除了主main函数
三个html函数,都依靠着各自的后背
head index(被定义为了layout) end 以index为主体,通过define定义,以template为连接。将他们链接在一起。
main
package main
import ("fmt""html/template""net/http"
)
func welcome(w http.ResponseWriter, r *http.Request) {t, err := template.ParseFiles("GoWebDevelopment/basis/Nesting/view/index.html","GoWebDevelopment/basis/Nesting/view/head.html", "GoWebDevelopment/basis/Nesting/view/end.html")if err != nil {fmt.Println("这里出错啦:", err)}t.ExecuteTemplate(w, "layout", nil)
}
func main() {// 设置服务器server := http.Server{Addr: "localhost:8081"}// 开启http.HandleFunc("/", welcome)// 开启监听server.ListenAndServe()
}
head
如果爆红,请不要紧张,编辑器的bug,不怪咱
{{define "head"}}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>我是头部
</body>
</html>
{{end}}
index
{{define "layout"}}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>{{template "head"}}<br>你好<br>{{template "end"}}<br>
</body>
</html>
{{end}}
end
{{define "end"}}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>这里是结尾呦
</body>
</html>
{{end}}
文件上传
在这里,html表单中的enctype是主角,
我个人感觉,后端只需要接收传递过来的信息,附带加工即可。
html-form表单
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>文件上传</title>
</head>
<body>
<!--这是一非常有趣的表单,form的作用,就是提取表单action:将表单提取到指定urlmethod代表应用method方法input--type:类型,name:input提交内容的名字
-->
<!--文件上传upload,表达提交-->
<form action="upload" enctype="multipart/form-data" method="post">用户名:<input type="text" name="username"><br>上传照片:<input type="file" name="photo"><br><input type="submit" value="注册">
</form>
</body>
</html>
main
package main
import ("fmt""html/template""io/ioutil""net/http""strings"
)
/*第一遍感悟:从->r.Request接收数据FormValue接收值-到-FormFile接收文件,文件又有File(粗略)与FileHeader(详细)两个方向解析其实到最后,还有一个奇葩的问题,就是form没数据,硬传,定报错,所以要用err第二遍感悟:先从http中获取名字(FormValue),在获取文件接口(FormFile),转为2进制(WriteFile),存入本地
*/
func welcome(w http.ResponseWriter, r *http.Request) {t, err := template.ParseFiles("GoWebDevelopment/basis/upload/view/index.html")if err != nil {fmt.Println("解析模板出错", err)}t.Execute(w, nil)
}func upload(w http.ResponseWriter, r *http.Request) {fileName := r.FormValue("name")fmt.Fprintln(w, fileName)// 检查文件上传错误(关键修正)file, fileHeader, err := r.FormFile("photo")if err != nil {fmt.Fprintln(w, "错误:未上传文件或请求不合法")return // 终止函数,避免后续空指针操作}defer file.Close() // 及时关闭文件流,释放资源b, err := ioutil.ReadAll(file) // 建议改为 io.ReadAll(file)(ioutil 已弃用)if err != nil {fmt.Fprintln(w, "错误:读取文件内容失败", err)return}// 获取后缀suffix := fileHeader.Filename[strings.LastIndex(fileHeader.Filename, "."):]// 建议改为 os.WriteFile(ioutil 已弃用)err = ioutil.WriteFile("D:\\workspace_go\\practice\\GoWebDevelopment\\basis\\upload\\file"+fileName+suffix, b, 0777)if err != nil {fmt.Fprintln(w, "错误:保存文件失败", err)return} t, err := template.ParseFiles("GoWebDevelopment/basis/upload/view/success.html")if err != nil {fmt.Fprintln(w, "错误:解析模板失败", err)return}t.Execute(w, nil)
}func main() {server := http.Server{Addr: "localhost:8081",}http.HandleFunc("/1", welcome)http.HandleFunc("/upload", upload)server.ListenAndServe()
}
文件下载
- 如果照片看不懂,就看我写的简介。
MIME类似标签,多用途互联网邮件扩展类型,是一种用于标识文档、文件或字节流的性质和格式的标准
基础概念
MIME
MIME类似标签,多用途互联网邮件扩展类型,是一种用于标识文档、文件或字节流的性质和格式的标准
- 在 HTTP 通信中,服务器通过响应头中的 MIME 类型,让浏览器判断是直接显示内容(如 text/html 类型的网页、image/jpeg 类型的图片),还是提示用户下载(如 application/pdf 类型的文件)。
- 邮件程序通过 MIME 类型检测附件格式,选择对应程序打开;文件管理器依据 MIME 类型,用合适的应用打开文件、显示文件类型描述及图标等。
main函数
package main
import ("fmt""html/template""net/http""os"
)
/*
关于请求下载,是一件非常有趣的事情
老样子,启动服务器,开启监听,绑定路由,用一个页面将基础内容展示出来
其次才是新东西,在html中,设置a标签。href设置成请求下载url,启动download路由
获取filename参数。开始申请本地文件,通过os.ReadAll转化为2进制,传递到客户端
并通过设置客户端标头,Head()...用set改变各种参数content-type与Disposition
是客户端下载下来
*/
func welcome(w http.ResponseWriter, r *http.Request) {t, _ := template.ParseFiles("GoWebDevelopment/basis/download/view/index.html")t.Execute(w, nil)
}func download(w http.ResponseWriter, r *http.Request) { // 人家这个函数,只是接收的一个请求而已filename := r.FormValue("filename")// 获取值b, err := os.ReadFile("GoWebDevelopment/basis/upload/" + filename)if err != nil {fmt.Fprintf(w, "下载失败:", err)return}h := w.Header()h.Set("Content-Type", "application/octet-stream")h.Set("Content-Disposition", "attachment;filename="+filename)w.Write(b)/*我知道,以后看到这里的时候,你一定会有疑惑,(w.Write()与Fprintln(w,)的区别)没事,我替你解决:若需要精确控制输出内容(如二进制数据、JSON、无额外字符的文本),选 w.Write([]byte);若需要快速拼接并输出文本(如调试信息、多变量组合输出),选 fmt.Fprintln(w, ...)。*/
}func main() {server := http.Server{Addr: "localhost:8081",}http.HandleFunc("/1", welcome)http.HandleFunc("/download", download)server.ListenAndServe()
}
index函数
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>下载链接</title>
</head>
<body><!--前提是,我可没有这个文件--><a href="download?filename=file.png">点击我下载</a>
</body>
</html>
ajax请求返回json数据
main
package mainimport ("encoding/json""fmt""html/template""net/http"
)/*
我幸运的孩子呐,如果你下载jquery不幸运落坑,这时我建议你,Ctrl+S试试
*/type User struct {Name stringAge int
}// 显示主页面
func welcome(w http.ResponseWriter, r *http.Request) {t, _ := template.ParseFiles("GoWebDevelopment/basis/ajax/view/index.html")t.Execute(w, nil)
}// 响应ajax请求
func showUser(w http.ResponseWriter, r *http.Request) {us := make([]User, 0)us = append(us, User{"张三", 12})us = append(us, User{"李四", 13})us = append(us, User{"王五", 14})b, _ := json.Marshal(us) // 转化成了二进制w.Header().Set("Content-Type", "application/json;charset=utf-8")fmt.Fprintln(w, string(b))/*我知道,很多天后,你肯定会出现疑惑!为什么要用string(b),而不直接用b呢??哈哈,我来教你:这是符合 Content-Type: application/json 的正确输出方式,客户端(如前端 Ajax)可以直接解析这个 JSON 字符串。其他底层的,是与前端接轨的,我一个后端的,暂时不研究*/
}func main() {// 启动服务器server := http.Server{Addr: ":8888",}// 处理静态资源http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("GoWebDevelopment/basis/ajax/static/"))))http.HandleFunc("/1", welcome)http.HandleFunc("/showUser", showUser)// 同步监听server.ListenAndServe()
}
html
html<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>用户数据展示</title><script type="text/javascript" src="/static/jquery-3.7.1.min.js"></script><script type="text/javascript">$(function () {$("button").click(function () {$.ajax({url: "/showUser",type: "GET",data: {}, // 若后端需要参数(如分页),可在此添加 {page: 1}success: function (data) {var res = "";// 遍历服务器返回的 data 数组(假设 data 是 [{Name: "张三", Age: 20}, ...])for (let i = 0; i < data.length; i++) {// 修正:使用 data[i] 而非 resKey[i]res += "<tr>";res += "<td>" + data[i].Name + "</td>"; // 拼接姓名res += "<td>" + data[i].Age + "</td>"; // 拼接年龄res += "</tr>";}// 将拼接好的行插入到 tbody#mybody 中$("#mybody").html(res);},error: function (xhr, status, error) {// 错误提示alert("数据加载失败!错误:" + error);}});});});</script>
</head>
<body>
<button>加载数据</button>
<!-- 修正:<tbody> 移到 <table> 内部 -->
<table border="1"><!-- 表头用 <thead> 包裹(可选但推荐) --><thead><tr><td>姓名</td><td>年龄</td></tr></thead><!-- 表体用 <tbody id="mybody"> 包裹 --><tbody id="mybody"><!-- 数据行将通过 AJAX 动态插入到这里 --></tbody>
</table>
</body>
</html>
非常有趣的小玩意
解释:
- 如果是浏览器可直接渲染的内容(如 text/html、image/png、text/plain 等):
- 浏览器会直接处理并显示。例如:
- 当响应体是 HTML 内容(Content-Type: text/html),浏览器会解析并渲染成网页。
- 当响应体是图片(Content-Type: image/png),浏览器会直接显示图片。
- 当响应体是纯文本(Content-Type: text/plain),浏览器会显示原始文本。
- 如果是数据格式(如 application/json、application/xml、text/csv 等):
- 浏览器不会直接渲染,而是将数据 “交给” 前端 JavaScript(如通过 AJAX/Fetch 请求获取),由脚本解析后再决定如何显示(例如更新页面 DOM、弹出提示等)。
这个是关键:
- 直接显示的情况:当响应体是浏览器可直接渲染的内容(如 HTML、图片、文本),且通过普通导航请求获取时,会直接显示在界面上。
- 需前端处理的情况:当响应体是数据格式(如 JSON、XML),或通过 AJAX/Fetch 异步获取时,需前端脚本解析并手动更新页面显示。
正则表达式
这里,我建议,最好的学习方式,是看看菜鸟文档 :: 我的笔记 ::
自己的小小测试,掌握到这里就足够了,当然要结合菜鸟看看基础知识点
::可以自己尝试写一个邮箱,用来测试自己::
package mainimport ("fmt""regexp"
)
/*
第一次复习的时候,巩固知识点,收获
1、动态编译(Compile)
2、静态编译(MustCompile)
1、匹配(MatchString)、查找(FindAllString)、分割(Split)、替换(ReplaceAllString)
*/
func main() {// ^与$ 两者结合起来的用法。\D的用法,反斜杠``转义的用法flag, _ := regexp.MatchString(`^\D+$`, "abs")fmt.Println(flag)/*创建一个regexp对象,然后调用方法*/r := regexp.MustCompile(`\d`)flag = r.MatchString("fsaf")fmt.Println(flag)// 返回所有切片,-1返回所有,1返回第一个,2返回前两个,3返回前三个str := r.FindAllString("234", -1)fmt.Println(str)// 按照规则切割,没有的话,返回空。n==0返回空,n<0返回所有,n>0返回对应个数+剩余个数str = r.Split("d12jkj231dd", -1)fmt.Println(str[0], str)// 就是起到一个替换的作用st := r.ReplaceAllString("d1w2k3k3", "美女")fmt.Println(st)
}
Cookie
设置(set)、获取(get) Cookie
package mainimport ("fmt""html/template""net/http""net/url"
)/*
为了写这个,真是命运多舛呐
cookie本来即使一个很好写的东西
无非就是创建cookie,然后通过响应传回去
并入到,请求标头中
通过request接受这个信息
!!! 但前提有一个重要的原因是,必须编译与解码!你可以把url.QueryFiles的作用是将信息转换成%+16进制
*/
func welcome(w http.ResponseWriter, r *http.Request) {t, _ := template.ParseFiles("GoWebDevelopment/basis/Cookie/setAndGetCookie/view/index.html")t.Execute(w, nil)
}func setCookie(w http.ResponseWriter, r *http.Request) {encodeValue := url.QueryEscape("成功了")c := http.Cookie{Name: "name", Value: encodeValue, Path: "/"}http.SetCookie(w, &c)t, _ := template.ParseFiles("GoWebDevelopment/basis/Cookie/setAndGetCookie/view/index.html")t.Execute(w, nil)
}func getCookie(w http.ResponseWriter, r *http.Request) {res := r.Cookies()for n, v := range res {str, _ := url.QueryUnescape(v.Value)fmt.Println(n, ":", str)}t, _ := template.ParseFiles("GoWebDevelopment/basis/Cookie/setAndGetCookie/view/index.html")t.Execute(w, res)
}
func main() {server := http.Server{Addr: ":8888",}http.HandleFunc("/1", welcome)http.HandleFunc("/setCookie", setCookie)http.HandleFunc("/getCookie", getCookie)server.ListenAndServe()
}
拓展:(HttpOnly、Path、MaxAge、Expires、Domain)
package mainimport ("net/http""time"
)func serv(w http.ResponseWriter, r *http.Response) {// 验证httpOnly// true不允许获得,false允许js脚本获得c1 := http.Cookie{Name: "myname", Value: "myvalue", HttpOnly: true}// /abc/ 代表能访问的路径,必须以/abc/以跟路径c2 := http.Cookie{Name: "myname", Value: "myvalue", Path: "/abc/"}// 设置存货时间c3 := http.Cookie{Name: "myname", Value: "myvalue", MaxAge: 10}t := time.Now() // 获取现在时间c4 := http.Cookie{Name: "myname", Value: "myvalue", Expires: t}// 必须以这个指定域名结尾,才可使用c5 := http.Cookie{Name: "myname", Value: "myvalue", Domain: ".bjsxt.com"}
}func main() {server := http.Server{Addr: ":8888",}http.HandleFunc("/1", serv)server.ListenAndServe()
}
第三方实现Restful风格
简而言之,第三个库实现了一个路由
借鉴博客:
1、我自己的笔记
2、菜鸟文档
::有道云笔记点击入口::
如果有帮助、记得点赞+收藏呐(〃 ̄︶ ̄)人( ̄︶ ̄〃)
相关文章:
GoWeb开发
学习目标: 本篇要达到的目的,能为后续复习提供极大便利。 (当我写下本篇博客时,已复习3遍) 一、网络通信概述 (为本篇基础核心内容) 1、什么是网络通信? 网络通信是指不同设备&…...
(7)Nokov 室内光学跟踪系统
文章目录 前言 7.1 所需硬件 7.2 Nokov 系统设置 7.3 配置旋翼机 7.4 启动 Nokov 模块 7.5 MAVProxy 准备 7.6 测试飞行 7.7 参数说明 前言 本文将介绍如何通过 Nokov 运动捕捉系统向旋翼机传输姿势信息。联系方式:NOKOV | Optical Motion Capture System。…...
Linux Shell编程和循环语句
一.for循环语句 1.for语句的结构2.for循环语句实例①根据姓名列表来批量创建多个用户②根据IP地址列表检查主机状态 二.使用while循环语句1.while语句结构2.while循环语句应用①批量添加规律编号用户②猜价格游戏 三 until循环语句1.until语句结构① 计算1-50的和 1.for语句的结…...
Java后端程序员学习前端之JavaScript
1.什么是JavaScript 1.1.概述 JavaScript是一门世界上最流行的脚本语言javaScript 一个合格的后端人员,必须要精通JavaScript 1.2.历史 JavaScript的起源故事-CSDN博客 2.快速入门 2.1.引入JavaScript 1.内部标签 <script>//.......</script> --…...
redis多路复用IO模型 以及 6.0引入的多线程模型
redis为什么选择单线程 采用多线程的话,会出现上下文切换的开销采用多线程,会带来共享资源的竞争控制,比如多个线程同时访问同一个资源(键值)时,需要额外的手段来保障共享资源的正确性,会带来额…...
101alpha_第6个
第6个alpha (-1 * correlation(open, volume, 10)) 这个就是看这两个相似性。10天之内的 如果结果为正且数值较大,投资者可能会认为在开盘价上涨时成交量萎缩,市场上涨动力不足,可能是卖出信号;反之,开盘价下跌时成交…...
crawl4ai能替代scrapy等传统爬虫框架吗?
传统爬虫框架就像拿着渔网在数字海洋中捕鱼——虽然能捞到东西,但面对现代网站的复杂性时常常"漏网之鱼"满天飞。以Scrapy为代表的工具存在三大致命短板:首先是JavaScript盲区,对动态渲染内容束手无策,就像试图用收音机…...
Sui Basecamp 2025 全栈出击
“我们不仅仅是在构建一个 L1,我们是在重建互联网。” — — Mysten Labs 首席产品官 Adeniyi Abiodun 本届 Sui Basecamp 汇聚了 Web3 领域的建设者、合作伙伴和思想领袖,为期两天,不仅展示了 Sui 的未来,也展现了去中心化互联网…...
计算机体系架构-----设计模式:状态模式(从程序员加班问题切入)
文章目录 1.梦开始的地方2.代码1.0版本3.代码2.0版本4.代码3.0版本5.梦结束的地方 最近在学习这个专业课里面的体系结构这门课程,作为专业里面的一门基础课,这个课程里面主要讲解的就是软件的设计思想,一些历程之类的,包括了面向对…...
【C/C++】RPC与线程间通信:高效设计的关键选择
文章目录 RPC与线程间通信:高效设计的关键选择1 RPC 的核心用途2 线程间通信的常规方法3 RPC 用于线程间通信的潜在意义4 主要缺点与限制4.1 缺点列表4.2 展开 5 替代方案6 结论 RPC与线程间通信:高效设计的关键选择 在C或分布式系统设计中,…...
数据结构之串
一、串的定义与基本概念 1. 串的定义 定义:串是由零个或多个字符组成的有限序列,记作 s"a1a2…an",例如 "data structure"、"123" 等。 空串:无任何字符,长度为 0,…...
基于腾讯云MCP广场的AI自动化实践:爬取小红书热门话题
基于腾讯云MCP广场的AI自动化实践:爬取小红书热门话题 我正在参加Trae「超级体验官」创意实践征文,本文所使用的 Trae 免费下载链接:www.trae.com.cn/?utm_source… 🔎 背景 在人工智能快速发展的时代,AI技术不仅重…...
AI领域的MCP(Model-Centric Paradigm)
1. 什么是MCP(Model-Centric Paradigm)? MCP(Model-Centric Paradigm)是人工智能开发中的一种核心理念,强调以模型的优化与改进作为主要驱动因素来提升AI系统的表现。在MCP模式下,开发者专注于…...
裸辞8年前端的面试笔记——JavaScript篇(一)
裸辞后的第二个月开始准备找工作,今天是第三天目前还没有面试,现在的行情是一言难尽,都在疯狂的压价。 下边是今天复习的个人笔记 一、事件循环 JavaScript 的事件循环(Event Loop)是其实现异步编程的关键机制。 从…...
力扣刷题Day 41:除自身以外数组的乘积(238)
1.题目描述 2.思路 方法1:搞一个数组存放各元素之前所有数的乘积(头为1),再搞一个数组存放各元素之后所有数的乘积(尾为1)。 方法2:上面的方法是很好理解的,在此基础上应该如何优化…...
金仓数据库征文-金仓KES数据同步优化实践:逻辑解码与增量同步
目录 一.同步场景与方案选型 二.同步环境配置 1.前置条件验证 2.逻辑解码配置 三.同步实施与问题排查 1.结构映射规则 2.增量数据捕获 3.数据一致性校验 四.性能调优实践 1.同步线程优化 2.批量提交优化 3.资源监控指标 五.典型场景解决方案 1.双向同步冲突处理 …...
【前端基础】9、CSS的动态伪类(hover、visited、hover、active、focus)【注:本文只有几个粗略说明】
一、什么是伪类 选择器的一种,用于选择处于特定状态的元素。 最常见的现象:鼠标放在某些文字上面,文字就会加上颜色。 鼠标没放上去之前: 鼠标放上去之后: 二、动态伪类 图片来源(链接文章也有其他伪…...
企业开发平台大变革:AI 代理 + 平台工程重构数字化转型路径
在企业数字化转型的浪潮中,开发平台正经历着前所未有的技术革命。从 AST(抽象语法树)到 AI 驱动的智能开发,从微服务架构到信创适配,这场变革不仅重塑了软件开发的底层逻辑,更催生了全新的生产力范式。本文…...
ZooKeeper工作机制与应用场景
目录 1.1、概述1.2、选举机制1.2.1、选举触发条件1.2.2、选举规则1.2.3、选举过程详解 1.3、数据同步机制1.3.1、正常同步1.3.2、宕机同步 1.4、客户端常用命令1.5、应用场景1.5.1、配置管理1.5.2、命令服务1.5.3、分布式锁服务1.5.4、集群管理1.5.5、分布式ID1.5.6、分布式协调…...
VR制作软件用途(VR制作软件概述)
虚拟现实(VR)制作软件作为现代科技的瑰宝,正以独特的魅力重塑各行各业。 通过构建三维虚拟环境,这些软件提供了前所未有的沉浸式体验,还推动了技术革新与产业升级。本文将探讨VR制作软件的主要用途,并重点…...
【PostgreSQL数据分析实战:从数据清洗到可视化全流程】电商数据分析案例-9.1 业务场景与数据准备
👉 点击关注不迷路 👉 点击关注不迷路 👉 点击关注不迷路 文章大纲 9.1 业务场景与数据准备9.1.1 业务场景描述核心业务目标业务挑战 9.1.2 数据来源与获取数据源构成数据获取方案 9.1.3 数据结构与字段说明核心数据表设计1. 订单事实表&…...
PyTorch 入门与核心概念详解:从基础到实战问题解决
PyTorch 入门与核心概念详解:从基础到实战问题解决 前言 用PyTorch 编写 Transformer 模型时遇到了多个错误,包括维度不匹配、NaN 损失、注意力权重未记录以及 OpenMP 库初始化等问题。 本文基于以上,对 PyTorch 的基本解释,并对…...
【办公类-99-05】20250508 D刊物JPG合并PDF便于打印
背景需求 委员让我打印2024年2025年4月的D刊杂志,A4彩打,单面。 有很多JPG,一个个JPG图片打开,实在太麻烦了。 我需要把多个jpg图片合并成成为一个PDF,按顺序排列打印。 deepseek写Python代码 代码展示 D刊jpg图片合…...
【C++】手搓一个STL风格的string容器
C string类的解析式高效实现 GitHub地址 有梦想的电信狗 1. 引言:字符串处理的复杂性 在C标准库中,string类作为最常用的容器之一,其内部实现复杂度远超表面认知。本文将通过一个简易仿照STL的string类的完整实现,揭示其设…...
无实体对话式社交机器人 拟人化印象形成机制:基于多模态交互与文化适配的拓展研究
《如何感知AI对话者:无实体对话式社交机器人拟人化对其印象形成效果影响机制的实验研究》解析 一、研究背景与核心问题 (一)技术背景与研究动机 随着生成式AI技术发展,以ChatGPT、文心一言为代表的无实体对话式社交机器人兴起,用户对其高度拟人化特征有显著需求,如扮演…...
存储器:DDR和独立显卡的GDDR有什么区别?
本文来简要对比DDR(Double Data Rate SDRAM)和GDDR(Graphics Double Data Rate SDRAM)的区别,重点说明它们在设计、性能和应用上的差异: 1. 设计目标与架构 DDR:通用型DRAM,设计为…...
viewDesign里的table内嵌套select动态添加表格行绑定内容丢失
问题 描述 viewDesign里的table内嵌套select,表格的行数是手动点击按钮添加的,添加第一行选择select的内容能正常展示,添加第二行第一行的select的内容消失 代码 <FormItem label"内饰颜色"><Tableclass"mt_10&q…...
vue v-html无法解析<
vue v-html无法解析字符串的小于号 方法一:可以替换成转义符 (实际还是会报错) let str 12345<445667 str.replaceAll(<, <)方法二:可以替换成中文小于号 let str 12345<445667 str.replaceAll(<, <)...
COLT_CMDB_linux_userInfo_20250508.sh修复历史脚本输出指标信息中userName与输出信息不一致问题
#!/bin/bash #IT_BEGIN #IT_TYPE3 #IT SYSTEM_LINUX_AGENTUSERDISCOVER|discovery.user[disc] #原型指标 #IT_RULE SYSTEM_LINUX_AGENTUSERGROUPID|groupId[{#USERNAME}] #IT_RULE SYSTEM_LINUX_AGENTUSERHOME|userHome[{#USERNAME}] #IT_RULE SYSTEM_LINUX_AGENTUSERNAME|user…...
A. Row GCD(gcd的基本性质)
Problem - 1458A - Codeforces 思路: 首先得知道gcd的两个基本性质: (1) gcd(a,b)gcd(a,|b-a|) (2) gcd(a,b,c)gcd(a,gcd(b,c)) 结合题目所给的a1bj,a2bj...... anbj 根据第一条性质得到: gcd(a1bj,a2bj)gcd(…...
k8s术语之Horizontal Pod Autoscaling
应用的资源使用率通常都有高峰和低谷的时候,如何削峰填谷,提高整体的整体资源利用率,让service中的Pod个数自动调整呢?Horizontal Pod Autoscaling:使pod水平自动缩放。这个Object也是最能体现kubernetes之于传统运维价值的地方&a…...
函数级重构:如何写出高可读性的方法?
1. 引言:为什么方法级别的重构如此重要? 在软件开发中,方法(函数)是程序逻辑的基本单元。一个高质量的方法不仅决定了程序是否能正常运行,更直接影响到: 代码的可读性:能否让其他开发者快速理解可维护性:未来修改是否容易出错可测试性:是否便于编写单元测试协作效率…...
手撕基于AMQP协议的简易消息队列-8(单元测试的编写)
在MQTest中编写模块的单元测试 在MQTest中编写makefile文件来编译客户端模块 all:Test_FileHelper Test_Exchange Test_Queue Test_Binding Test_Message Test_VirtualHost Test_Route Test_Consumer Test_Channel Test_Connection Test_VirtualHost:Test_VirtualHost.cpp ..…...
硬件选型:工控机的选择要素
在机器视觉应用中,工控机作为核心计算设备,承担着图像处理、数据分析和设备控制等多重任务。由于机器视觉常常在工业自动化、质量检测和精密控制中发挥重要作用,工控机的选型直接影响系统的性能和可靠性。 1. 应用场景与需求 机器视觉系统广…...
【芯片设计- RTL 数字逻辑设计入门 4.1 -- verilog 组合逻辑和时序逻辑延时比较】
文章目录 Overview时间线简单示意Overview 我们来详细分析下面这段 RTL Code , sbcs_sbbusy 为什么会比 sbcs_sbbusy_nx 慢一拍(晚一个时钟周期变化)。 assign sbcs_sbbusy_nx = set_sbcs_sbbusy;always @(posedge clk or negedge dmi_resetn) beginif (!dmi_resetn) begi…...
关于ubuntu下交叉编译arrch64下的gtsam报错问题,boost中boost_regex.so中连接libicui18n.so.55报错的问题
交叉编译gtsam时遇到的报错信息如下:gtsam需要连接boost, 解决办法: 1.重新编译boost可解决。 2.自己搞定生成一个libicui18n.so.55。 由于我们的boost是公用的,因此1不太可能(我试过重新编译完boost,在编译gtsam完…...
IoT平台和AIoT平台的区别
1. 什么是AIoT平台? AIoT(人工智能物联网,Artificial Intelligence of Things)平台 是 人工智能(AI) 与 物联网(IoT) 深度融合的技术框架,通过将AI算法嵌入物联网终端或…...
iOS 模块化开发流程
iOS模块化开发是一种将大型项目拆分为独立、可复用模块的开发模式,能够提升代码可维护性、团队协作效率和动态交付能力。以下是iOS模块化开发的核心流程与关键要点: 一、模块化设计阶段 业务解耦与模块划分 横向分层:基础层(网络、…...
云原生安全治理体系建设全解:挑战、框架与落地路径
📝个人主页🌹:慌ZHANG-CSDN博客 🌹🌹期待您的关注 🌹🌹 一、引言:云原生环境下,安全治理正在被重构 在传统IT架构中,安全防护多依赖边界设备(如防火墙、WAF、堡垒机)进行集中式防护。然而,在云原生环境下,这种“边界式”安全模型正面临颠覆。 应用微服务化…...
如何在Vue-Cli中使用Element-UI和Echarts和swiper插件(低版本)
1st.Element-UI 1.1 安装 在终端输入 npm install element-ui 1.2 导入 在全局main.js中全局导入Element-UI: // 导入element-ui组件库 import ElementUI from element-ui; // 导入element-ui组件库的样式 import element-ui/lib/theme-chalk/index.css; // 注…...
[特殊字符]【实战教程】用大模型LLM查询Neo4j图数据库(附完整代码)
🌟 核心要点速览 ✅ 基于LangChain框架实现LLM查询Neo4j ✅ 使用Qwen2.5模型(实测Llama3.1查不出内容) ✅ 包含完整数据准备代码实现效果演示 ✅ GitHub/Gitee源码已同步(文末获取) 🛠️ 环境准备 1️⃣ 安装Neo4j图数据库 # Windows安装指南参考&…...
Qt获取CPU使用率及内存占用大小
Qt 获取 CPU 使用率及内存占用大小 文章目录 Qt 获取 CPU 使用率及内存占用大小一、简介二、关键函数2.1 获取当前运行程序pid2.2 通过pid获取运行时间2.3 通过pid获取内存大小 三、具体实现五、写在最后 一、简介 近期在使用软件的过程中发现一个有意思的东西。如下所示&a…...
算法解密:除自身以外数组的乘积问题详解
算法解密:除自身以外数组的乘积问题详解 一、引言 在算法的奇妙旅程中,我们时常会遇到一些看似简单却蕴含深刻智慧的问题,“除自身以外数组的乘积”就是其中之一。这个问题不仅考验我们对数组操作的熟练程度,还要求我们在特定的限制条件下(不能使用除法且时间复杂度为O(n…...
基于Kubernetes的Apache Pulsar云原生架构解析与集群部署指南(上)
#作者:闫乾苓 文章目录 概念和架构概述主要特点消息传递核心概念Pulsar 的消息模型Pulsar 的消息存储与分发Pulsar 的高级特性架构BrokerBookKeeperZooKeeper 概念和架构 概述 Pulsar 是一个多租户、高性能的服务器到服务器消息传递解决方案。Pulsar 最初由雅虎开…...
信创生态核心技术栈:数据库与中间件
🧑 博主简介:CSDN博客专家、CSDN平台优质创作者,高级开发工程师,数学专业,10年以上C/C, C#, Java等多种编程语言开发经验,拥有高级工程师证书;擅长C/C、C#等开发语言,熟悉Java常用开…...
CMU-15445(3)——PROJECT#1-BufferPoolManager-Task#1
PROJECT#1-BufferPoolManager 在完成了前面基础的PROJECT#0后,从本节开始才正式进入了CMU-15445的学习,最终目的是构建一个面向磁盘的数据库管理系统。 PROJECT#1 的主要任务是实现数据库管理系统的缓冲池管理器,缓冲池负责在主存缓冲区与持…...
《数据结构初阶》【链式二叉树】
《数据结构初阶》【链式二叉树】 前言:---------------树---------------什么是树?📌爱心❤小贴士:树与非树?树的基本术语有哪些?关于节点的一些定义:关于树的一些定义:关于森林的定…...
Oracle免费认证来袭
1、Oracle Cloud Infrastructure 2025 Foundations Associate” 🔗 考证地址:https://mylearn.oracle.com/ou/exam-unproctored/oracle-cloud-infrastructure-2025-foundations-associate-1z0-1085-25/148056/241954 2、Oracle Cloud Infrastructure 2…...
Vim 编辑器常用快捷键速查表
Vim 编辑器常用快捷键速查表 Vim 快捷键大全 **1. 基础操作****2. 光标移动****3. 编辑文本****4. 查找替换****5. 分屏操作****6. 可视化模式** **附:Vim 模式切换流程图** 1. 基础操作 快捷键功能说明i进入插入模式(光标前)a进入插入模式&…...
从父类到子类:C++ 继承的奇妙旅程(1)
前言: 在前文,小编讲述了C模板的进阶内容,下面我们就要结束C初阶的旅行,开始进入C进阶容的旅c程,今天旅程的第一站就是C三大特性之一——继承的旅程,各位扶好扶手,开始我们今天的C继承的奇妙旅程…...