跳到主要内容 Go Web 开发:网络通信、Socket 与数据库实战 | 极客日志
Go / Golang 大前端
Go Web 开发:网络通信、Socket 与数据库实战 Go Web 开发涉及网络通信基础、Socket 编程、MySQL 交互及 Go 标准库 Web 服务构建。内容涵盖 Handler 接口实现、路由控制、请求参数解析、HTML 模板渲染与嵌套、文件上传下载、Ajax 数据交互、正则表达式应用及 Cookie 管理机制。此外还补充了交换机、路由器、网关、子网划分及 TCP 三次握手四次挥手等网络协议原理。
黑客帝国 发布于 2026/3/15 更新于 2026/4/18 2 浏览学习目标:
本篇内容旨在为后续复习提供便利,涵盖 Go Web 开发的核心基础。
一、网络通信概述
1、什么是网络通信?
网络通信是指不同设备(如计算机、手机、服务器等)通过计算机网络进行数据交换和信息传递的过程。其核心目标是实现设备之间的互联互通,让数据能够准确、高效地从发送端传输到接收端。
2、网络通信的核心组成部分
硬件层面
终端设备 :发送或接收数据的设备(如手机、电脑、服务器、物联网传感器)。
传输介质 :数据传输的物理 / 无线通道,包括:
有线介质 :双绞线、同轴电缆、光纤(速度快、稳定性高)。
无线介质 :无线电波、微波、蓝牙、Wi-Fi、5G(灵活性高,适合移动场景)。
网络设备 :负责数据转发、路由、信号放大等,如路由器、交换机、调制解调器(Modem)、集线器。
软件层面
通信协议 :规定数据格式、传输规则和交互流程的'语言'(如 TCP/IP、HTTP、FTP)。
操作系统与应用程序 :提供网络接口(如 Socket 编程接口),支持上层应用(如浏览器、邮件客户端)实现通信。
3、网络通信的工作原理
TCP(传输控制协议)、UDP(用户数据报协议)都属于 TCP/IP 协议族中的传输层协议。IP 地址是网络世界的'身份证',用于跨区域'找到人';MAC 地址是网络世界的'门牌号',用于在具体'房间'(局域网)内'敲开门'。两者缺一不可,共同实现从'全球定位'到'最后一公里交付'的完整通信流程。
分层模型(以 TCP/IP 为例)
为简化复杂问题,网络通信采用分层架构,每层负责特定功能,层间通过接口交互。
应用层 :直接为用户程序提供服务(如 HTTP 用于网页浏览,SMTP 用于邮件传输)。
传输层 :确保端到端的数据传输,主要协议:
TCP (面向连接,可靠传输,如网页加载、文件传输)。
UDP (无连接,不可靠但高效,如视频流、实时通信)。
网络层 :负责网络间的路由和寻址,核心协议是 IP(为设备分配 IP 地址,确定数据传输路径)。
数据链路层 :在相邻设备间传输数据,处理物理地址(MAC 地址)和错误检测(如以太网协议)。
物理层 :定义物理设备的电气、机械特性(如电压、接口标准)。
数据传输流程
发送端:数据从应用层逐层封装(添加头部信息),最终通过物理层发送。
接收端:数据从物理层逐层解封装(去除头部信息),最终传递给应用层处理。
4、网络通信的主要类型
按通信对象分类
点对点(Point-to-Point) :两台设备直接通信(如蓝牙设备配对)。
点对多点(Point-to-Multipoint) :一台设备向多台设备发送数据(如广播、组播)。
端到端(End-to-End) :跨越多个网络设备,最终在两个终端间建立逻辑连接(如通过路由器连接的两台远程电脑)。
按通信方式分类
同步 vs 异步 :
同步:发送方等待接收方响应(如 TCP 请求 - 响应模式)。
微信扫一扫,关注极客日志 微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具 Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
HTML转Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
JSON 压缩 通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
JSON美化和格式化 将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online
异步:发送方无需等待,直接继续执行(如 UDP 发送数据后不等待确认)。 面向连接 vs 无连接 :
面向连接:先建立连接(如 TCP 的三次握手),再传输数据(可靠但开销大)。
无连接:直接发送数据(如 UDP,适合实时性要求高的场景)。
基础协议
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 通信基于客户端 - 服务器(Client-Server)模型。API(程序编程接口)是软件用来和外部程序进行数据交换的一个渠道。Socket 是一种网络编程 API,通过指定 IP 地址、端口号和协议类型(如 TCP 或 UDP),调用其提供的函数实现网络通信。
package main
import (
"fmt"
"net"
"strings"
)
type User struct {
Username string
Othername string
Msg string
ServerMsg 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]), "-" )
if len (array) >= 4 {
user.Username = array[0 ]
user.Othername = array[1 ]
user.Msg = array[2 ]
user.ServerMsg = array[3 ]
userMap[user.Username] = conn
if 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)))
}
}
}
}()
}
}
package main
import (
"fmt"
"net"
"os"
"strings"
"sync"
)
type User struct {
Username string
Othername string
Msg string
ServerMsg 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 main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
func main () {
db, err := sql.Open("mysql" , "root:1234@tcp(localhost:3306)/goweb" )
defer db.Close()
if err != nil {
fmt.Println("数据库连接错误" , err)
return
}
stmt, err := db.Prepare("insert into people values(default,?,?)" )
defer stmt.Close()
if err != nil {
fmt.Println("预处理失败:" , err)
return
}
r, _ := stmt.Exec("张三" , "上海" )
count, _ := r.RowsAffected()
fmt.Println("修改了:" , count)
fmt.Println(r.LastInsertId())
}
package main
import (
"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)
return
}
defer db.Close()
stmt, err := db.Prepare("delete from people where id = 2" )
defer stmt.Close()
if err != nil {
fmt.Println("预处理失败:" , err)
return
}
r, _ := stmt.Exec()
count, _ := r.RowsAffected()
if count > 0 {
fmt.Println("删除成功" )
} else {
fmt.Println("删除失败" )
}
}
package main
import (
"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)
return
}
defer db.Close()
stmt, err := db.Prepare("update people set name = ?,address = ? where id=3 ;" )
defer stmt.Close()
if err != nil {
fmt.Println("预处理失败:" , err)
return
}
r, _ := stmt.Exec("朝阳" , "新乡" )
count, _ := r.RowsAffected()
if count > 0 {
fmt.Println("修改成功" )
} else {
fmt.Println("修改失败" )
}
}
package main
import (
"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)
return
}
defer db.Close()
stmt, err := db.Prepare("select * from people" )
defer stmt.Close()
if err != nil {
fmt.Println("预处理失败:" , err)
return
}
rows, err := stmt.Query()
if err != nil {
fmt.Println("获取值出错" , err)
return
}
defer rows.Close()
for rows.Next() {
var id int
var name, address string
rows.Scan(&id, &name, &address)
fmt.Println(id, " " , name, " " , address)
}
}
四、goWeb
控制器 Handler 接口 定义 HTTP 处理逻辑的规范(要求实现 ServeHTTP 方法)。所有 HTTP 处理程序必须直接或间接实现此接口。
Handle 函数 将一个 Handler 实例绑定到 URL 路径模式(pattern)。
HandleFunc 函数 便捷方式:将一个函数包装为 Handler 接口的实现,并绑定到 URL 路径模式。
package main
import (
"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()
}
package main
import "net/http"
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, "第一个" )
var acc []string = r.Header["Accept" ]
for _, n := range acc {
fmt.Fprintln(w, "Accepth 内容" , n)
}
}
func main () {
server := http.Server{
Addr: "localhost:8081" ,
}
http.HandleFunc("/param" , param)
server.ListenAndServe()
}
package main
import (
"fmt"
"net/http"
)
func param (w http.ResponseWriter, r *http.Request) {
h := r.Header
fmt.Fprintln(w, h["Accept-Encoding" ][0 ])
r.ParseForm()
fmt.Fprintln(w, r.Form)
}
func main () {
server := http.Server{
Addr: "localhost:8081" ,
}
http.HandleFunc("/param" , param)
server.ListenAndServe()
}
html 模板与静态资源 package main
import (
"fmt"
"html/template"
"net/http"
)
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()
}
<!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 >
function myClick ( ){
alert ("您点击了按钮" )
}
函数/数据->模板
向模板传递参数
向模板传递结构体
向模板传递 map
package main
import (
"fmt"
"html/template"
"net/http"
"time"
)
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)
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()
}
<!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 package main
import (
"fmt"
"html/template"
"net/http"
)
func welcome (w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles("GoWebDevelopment/basis/action/view/index.html" )
if err != nil {
fmt.Println("解析出错:" , err)
}
varInt := []int {1 , 2 , 3 , 4 , 5 }
t.Execute(w, varInt)
}
func main () {
server := http.Server{
Addr: "localhost:8081" ,
}
http.HandleFunc("/1" , welcome)
server.ListenAndServe()
}
<!DOCTYPE html >
<html lang ="en" >
<head >
<meta charset ="UTF-8" >
<title > Title</title >
</head >
<body >
{{$n:=100}}
{{if gt $n 101}} 你好呀<br >
{{else}} 好遗憾,他没看到呢<br >
{{end}}
{{range .}} {{.}}<br >
{{end}}
</body >
</html >
模板嵌套 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()
}
{{define "head"}}
<!DOCTYPE html >
<html lang ="en" >
<head >
<meta charset ="UTF-8" >
<title > Title</title >
</head >
<body >
我是头部
</body >
</html >
{{end}}
{{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}}
{{define "end"}}
<!DOCTYPE html >
<html lang ="en" >
<head >
<meta charset ="UTF-8" >
<title > Title</title >
</head >
<body >
这里是结尾呦
</body >
</html >
{{end}}
文件上传 package main
import (
"fmt"
"html/template"
"io/ioutil"
"net/http"
"strings"
)
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)
if err != nil {
fmt.Fprintln(w, "错误:读取文件内容失败" , err)
return
}
suffix := fileHeader.Filename[strings.LastIndex(fileHeader.Filename, "." ):]
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 类似标签,多用途互联网邮件扩展类型,是一种用于标识文档、文件或字节流的性质和格式的标准。
package main
import (
"fmt"
"html/template"
"net/http"
"os"
)
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, "下载失败:%v" , err)
return
}
h := w.Header()
h.Set("Content-Type" , "application/octet-stream" )
h.Set("Content-Disposition" , "attachment;filename=" +filename)
w.Write(b)
}
func main () {
server := http.Server{
Addr: "localhost:8081" ,
}
http.HandleFunc("/1" , welcome)
http.HandleFunc("/download" , download)
server.ListenAndServe()
}
<!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 数据 package main
import (
"encoding/json"
"fmt"
"html/template"
"net/http"
)
type User struct {
Name string
Age int
}
func welcome (w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("GoWebDevelopment/basis/ajax/view/index.html" )
t.Execute(w, nil )
}
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))
}
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()
}
<!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 : {},
success : function (data ) {
var res = "" ;
for (let i = 0 ; i < data.length ; i++) {
res += "<tr><td>" + data[i].Name + "</td><td>" + data[i].Age + "</td></tr>" ;
}
$("#mybody" ).html (res);
},
error : function (xhr, status, error ) {
alert ("数据加载失败!错误:" + error);
}
});
});
});
</script >
</head >
<body >
<button > 加载数据</button >
<table >
<thead >
<tr >
<td > 姓名</td >
<td > 年龄</td >
</tr >
</thead >
<tbody id ="mybody" >
</tbody >
</table >
</body >
</html >
正则表达式 package main
import (
"fmt"
"regexp"
)
func main () {
flag, _ := regexp.MatchString(`^\D+$` , "abs" )
fmt.Println(flag)
r := regexp.MustCompile(`\d` )
flag = r.MatchString("fsaf" )
fmt.Println(flag)
str := r.FindAllString("234" , -1 )
fmt.Println(str)
str = r.Split("d12jkj231dd" , -1 )
fmt.Println(str[0 ], str)
st := r.ReplaceAllString("d1w2k3k3" , "美女" )
fmt.Println(st)
}
Cookie Cookie 是一种客户端存储技术,用于解决 HTTP 协议无状态的问题。HTTP 协议本身不会记录用户的任何操作状态,而 Cookie 可以让服务器在客户端存储少量数据(以键值对形式),当客户端再次访问服务器时,会将这些 Cookie 携带在请求中发送给服务器,从而实现会话跟踪、用户身份识别等功能。
package main
import (
"fmt"
"html/template"
"net/http"
"net/url"
)
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 main
import (
"net/http"
"time"
)
func serv (w http.ResponseWriter, r *http.Response) {
c1 := http.Cookie{Name: "myname" , Value: "myvalue" , HttpOnly: true }
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 风格 Restful 只是一种风格,而不是一种协议。它只是将 HTTP 协议应用的更规范。它基于 HTTP 协议对资源操作进行规范设计。
拓展:
一、交换机与路由器: 1、交换机是什么?
交换机就是把数据包发送到正确的位置。交换机相当于邮递员,根据数据包中的目标 mac 地址,找到它对应的物理端口。
2、交换机与路由器有什么区别
一台交换机有很多个端口。它们都有自己的编号。计算机的网卡通过网线连接到交换机的端口上。这个端口就是一个确定的物理位置。我们只要知道某个网卡的 mac 地址在哪个端口上,我们就能正确的把数据包发给它。所以在交换机中,有一张端口与 mac 地址的影射关系表,我们称之为 mac 地址表。交换机维护这张映射关系表。想要与某个 mac 地址通信时,只需要来查询一下,这个 mac 地址在哪个端口上,然后从对应的端口发送出去就可以了。
每一包数据都会有两个 mac 地址,一个是发送方的 mac 地址成为源 mac,另一个是接收方的 mac 地址称为目标 mac,交换机受到一包数据后,首先要把这包数据的源 mac 与接收端口进行绑定,然后交换机要根据目标 mac 查找,从哪个端口把数据包发送出去,这时候就会出现两种情况。第一种是 mac 地址表中查询到了关联的端口,则直接从关联端口发出。第二种情况是 mac 地址表中没有查询到关联端口。则向除了接收端口之外的所有端口群发,这种行为称之为泛洪,如果目标 mac 地址在这个网络中则它一定能受到群发的数据包,如此运行一段时间后,通过交换机的 mac 地址表,就可以找到网络中的所有网卡设备。由此可见,交换机只会关心数据包中的 mac 地址,而不会关心 ip 地址,mac 地址在 TCP/IP 协议中处于第二层数据链路层,所以交换机通常也被称为二层设备。
路由器有两种接口,一种是 LAN 口,一种是 WAN 口,LAN 口可以有多个用来接家庭网络设备。比如,台式机,手机,笔记本,其中手机和笔记本是通过 wifi 连接到路由器的设备,也相当于连接到了 LAN 口上。WAN 口只有一个,用来接入运用商网络,以连接到互联网中。如果把路由器的 WAN 口忽略,只用 LAN 口,其实路由器就是一台交换机。
3、什么是网关?
子网如何划分?IP 地址和子网掩码按位相与。'与'的意思就是 1 与几就是几,而 0 与几都是 0。我们常用的子网掩码 255.255.255.0 前 3 个字节也就是前 24 位全为 1,后 8 位全为 0。所以按位相与的结果一定是这个 IP 地址的前三个字节不变,而最后一个字节是 0。比如 192.168.1.10 与 255.255.255.0=192.168.1.0。我们把 IP 地址与子网掩码相与之后的结果是相同的两个 IP 认为是在同一个子网中,也就是说 IP 为 192.168.1.10 子网掩码位 255.255.255.0 的这张网卡与另一个 192.168.1.X 的网卡一定是在同一个子网之中。因为子网掩码都是连续的 1 和连续的 0,所以我们通常用 1 的数量来表示子网掩码。比如:255.255.255.0,就是 24。我们用 IP/子网掩码来表示一个网络。比如:192.168.1.0/24 表示的网络中拥有 255 个 IP 地址。所以如果想扩大子网中 IP 地址的数量,我们只需要把子网掩码调小,如果想减少网中 IP 地址的数量,我们只需要把子网掩码调大就可以了。
子网的意义:因为 TCP/IP 协议规定,不同子网之间是不可以直接通信的,如果要通信需要通过网关来进行转发。(实现了对应的功能就算网关)网关上有两张网卡。分别配置了属于两个子网的 IP 地址。可以在两个网络之间转发数据包。这样我们就拥有了一个连接了两个子网的网络。比如:子网 1 中的计算机 A 发送数据包时,首先计算机 A 会根据目标 IP 判断是否跟自己属于同一个子网。如果是同一个子网则直接从网卡发出。如果不是同一个子网,则需要把数据包的目标 mac 地址改为网关 mac,然后发送给网关。网关拿到这一包数据后再通过路由表查询到这一包数据属于子网 2,网关修改目标 mac 地址为计算机 B 的 mac 地址。修改原 mac 为自己的 mac。然后从子网 2 的网卡发出。(ip 判断'身份时用的',mac 转发地址时用的)
4、什么是路由
以上出现了多次根据目标 IP 判断数据包因该如何发送的行为,我们就称之为路由。路由器有一个 WAN 口接入互联网。多个 LAN 口接入本地网络。它们就分别属于两个不同的子网。所以从内网访问互联网就是跨网络的行为。这时候就需要路由器来担任网关的角色。它的行为就叫路由。
交换机和路由器通常配合使用,共同构建完整的网络架构:
家庭网络示例:路由器:连接宽带调制解调器(Modem),作为家庭网络的'网关',负责拨号上网、分配 IP 地址(DHCP)、提供 Wi-Fi 服务。交换机:若家庭设备较多(如多个电脑、游戏机),可通过交换机扩展 LAN 接口,设备通过交换机连接到路由器,间接访问互联网。
企业网络示例:路由器:作为企业网络与互联网的边界设备,负责路由选择、流量控制、安全防护(如防火墙)。交换机:在企业内部,多台交换机级联或堆叠,构建局域网,实现设备互联;核心交换机连接到路由器,使内部设备能够访问外部网络。
交换机像个桥梁。路由器像个交通枢纽。
二、三次握手
当客户端向服务端发起连接时,会先发一包 syn 包连接请求数据,进行询问,能否建立连接。
如果服务端同意连接,则回复一包 syn+ack 包。
客户端收到之后回复一包 ack 包,连接建立。
2、为什么要三次握手而不是两次握手呢?
服务端回复完 syn+ack 之后就建立连接,这是为了防止因为已失效的请求报文,突然又传到服务器引起错误。
假设采用两次握手建立连接,客户端向服务端发送了一个 syn 包,来请求建立连接,因为某些未知原因,并没有到达服务器,在中间某个网络节点产生了滞留,为了建立连接客户端会重发 syn 包。这次的数据包正常送达,服务端回复 syn+ack 之后建立了连接。
但是第一包数据阻塞的网点节点,突然恢复,第一包 syn 包又送达到服务端,这时服务端会误认为是客户端又发起了一个新的连接,从而在两次握手之后,进入等待数据状态,服务端认为是两个连接,而客户端认为是一个连接,造成了状态不一致。
如果在三次握手的情况下,服务端收不到最后的 ack 包,自然不会认为连接建立成功,所有三次握手本质上来说,就是为了保证在不可靠的网络链路中,建立起可靠的连接。如 syn 包阻塞重发会导致服务器创建多重连接,而客户端只接受唯一连接。从而造成状态不一致的情况。
3、什么是四次挥手?
处于连接状态的客户端和服务端,都可以发起关闭连接请求,此时需要四次挥手来进行连接关闭。
假设客户端主动发起连接关闭请求,他需要向服务端发起一包 fin 包,表示要关闭连接,自己进入终止等待 1 状态,这是第一次挥手。
服务端收到 fin 包,发送一包 ack 包,表示自己进入了关闭等待状态,客户端进入终止等待 2 状态,这是第二次挥手
服务端此时还可以发送未发送的数据,而客户端还可以接收数据,待服务端发送完数据之后,发送一包 fin 包,进入最后确认状态。这是第三次挥手。
客户端收到之后回复 ack 包,进入超时等待状态,经过超时时间后关闭连接,而服务端收到 ack 包后,立即关闭连接。这是第四次挥手。
为什么客户端需要等待超时时间?这是为了保证服务端已收到 ack 包。因为假设客户端发送完最后一包 ack 包后就释放了连接,一旦 ack 包在网络中丢失,服务端将一直停留在最后确认状态。如果客户端发送最后一包 ack 包后,等待一段时间,这时服务端因为没有收到 ack 包,会重发 fin 包,客户端会响应这个 fin 包,重发 ack 包并刷新超时时间。保证在不可靠的网络链路中,进行可靠的连接断开确认。
为什么要四次挥手?由于 TCP 的半关闭(half-close)特性,任何一方都可以在数据传送结束后,发出连接关闭的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,则发出连接关闭通知,对方确认后就完全关闭了 TCP 连接。通俗的来说,两次挥手就可以释放一端到另一端的 TCP 连接,完全释放连接一共需要四次挥手。
5、三次握手详解:
注意:我们上面写的 ack 和 ACK,不是同一个概念:小写的 ack 代表的是头部的确认号 Acknowledge number,缩写 ack,是对上一个包的序号进行确认的号,ack=seq+1。大写的 ACK,则是我们上面说的 TCP 首部的标志位,用于标志的 TCP 包是否对上一个包进行了确认操作,如果确认了,则把 ACK 标志位 设置成 1。