环境:

  • MacBook

  • WireShark

先看一段 Goang 实现的 HTTP 服务端程序。

server.go
package main

import (
	"io"
	"log"
	"net/http"
)

func headers(w http.ResponseWriter, req *http.Request) {
	log.Println(req.Method, req.Proto, req.Header, req.Body)
	_, _ = io.WriteString(w, "Hello, world!\n")
}

func main() {
	http.HandleFunc("/", headers)
	log.Fatal(http.ListenAndServe(":30080", nil))
}

运行 HTTP 服务端:

go run server.go

然后启动 WireShark 命令行 tshark 程序,监听 30080 端口,并将抓包结果写入文件:

tshark -i lo0 -f 'tcp port 30080' -w Downloads/http.pcapng -P

打开终端,使用 curl 命令模拟 HTTP 客户端访问 http://127.0.0.1:30080

curl http://127.0.0.1:30080

可以看到 WireShark 命令行程序 tshark 输出,这便是 HTTP 从建立连接到关闭连接整个过程:

Capturing on 'Loopback: lo0'
    1   0.000000    127.0.0.1 → 127.0.0.1    TCP 68 51930 → 30080 [SYN] Seq=0 Win=65535 Len=0 MSS=16344 WS=64 TSval=274718867 TSecr=0 SACK_PERM=1
    2   0.000085    127.0.0.1 → 127.0.0.1    TCP 68 30080 → 51930 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=16344 WS=64 TSval=274718867 TSecr=274718867 SACK_PERM=1
    3   0.000102    127.0.0.1 → 127.0.0.1    TCP 56 51930 → 30080 [ACK] Seq=1 Ack=1 Win=408256 Len=0 TSval=274718867 TSecr=274718867
    4   0.000116    127.0.0.1 → 127.0.0.1    TCP 56 [TCP Window Update] 30080 → 51930 [ACK] Seq=1 Ack=1 Win=408256 Len=0 TSval=274718867 TSecr=274718867
    5   0.000150    127.0.0.1 → 127.0.0.1    HTTP 135 GET / HTTP/1.1
    6   0.000166    127.0.0.1 → 127.0.0.1    TCP 56 30080 → 51930 [ACK] Seq=1 Ack=80 Win=408192 Len=0 TSval=274718867 TSecr=274718867
    7   0.000370    127.0.0.1 → 127.0.0.1    HTTP 187 HTTP/1.1 200 OK  (text/plain)
    8   0.000385    127.0.0.1 → 127.0.0.1    TCP 56 51930 → 30080 [ACK] Seq=80 Ack=132 Win=408128 Len=0 TSval=274718867 TSecr=274718867
    9   0.000572    127.0.0.1 → 127.0.0.1    TCP 56 51930 → 30080 [FIN, ACK] Seq=80 Ack=132 Win=408128 Len=0 TSval=274718867 TSecr=274718867
   10   0.000592    127.0.0.1 → 127.0.0.1    TCP 56 30080 → 51930 [ACK] Seq=132 Ack=81 Win=408192 Len=0 TSval=274718867 TSecr=274718867
   11   0.000627    127.0.0.1 → 127.0.0.1    TCP 56 30080 → 51930 [FIN, ACK] Seq=132 Ack=81 Win=408192 Len=0 TSval=274718867 TSecr=274718867
   12   0.000652    127.0.0.1 → 127.0.0.1    TCP 56 51930 → 30080 [ACK] Seq=81 Ack=133 Win=408128 Len=0 TSval=274718867 TSecr=274718867

~/Download/ 目录下 http.pcapng 文件用 WireShark 打开如下图:

202001 wireshark http
Figure 1. 源文件:https://1drv.ms/u/s!Anua9d_TpfhPoRs6kCfGKZwpUgZQ

整个的连接过程我们首先从 TCP 协议说起。

TCP 协议

TCP 协议是 OSI 模型中传输层协议,主要解决数据如何在网络中传输。

Golang 的 TCP 实现

Golang 实现 TCP 的服务端和客户端,代码如下:

server.go
package main

import (
	"bufio"
	"fmt"
	"net"
	"strings"
)

func main() {
	ln, _ := net.Listen("tcp", ":30081")
	conn, _ := ln.Accept()

	message, _ := bufio.NewReader(conn).ReadString('\n')
	fmt.Print("Message Received:", message)
	newMessage := strings.ToUpper(message)
	_, _ = conn.Write([]byte(newMessage + "\n"))
	_ = conn.Close()
}
client.go
package main

import (
	"bufio"
	"fmt"
	"net"
)

func main() {
	conn, _ := net.Dial("tcp", "127.0.0.1:30081")

	fmt.Print("Text to send: ")
	_, _ = fmt.Fprintf(conn, "hello tcp!\n")
	message, _ := bufio.NewReader(conn).ReadString('\n')

	fmt.Print("Message from server: " + message)
}

首先启动抓包程序:

tshark -i lo0 -f 'tcp port 30081' -w Downloads/tcp.pcapng -P

然后先执行 TCP 服务端 server.go,再执行客户端 client.go,输出如下:

Capturing on 'Loopback: lo0'
    1 0.000000       127.0.0.1             127.0.0.1             TCP      68     63299 → 30081 [SYN] Seq=0 Win=65535 Len=0 MSS=16344 WS=64 TSval=517670095 TSecr=0 SACK_PERM=1
    2 0.000124       127.0.0.1             127.0.0.1             TCP      68     30081 → 63299 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=16344 WS=64 TSval=517670095 TSecr=517670095 SACK_PERM=1
    3 0.000149       127.0.0.1             127.0.0.1             TCP      56     63299 → 30081 [ACK] Seq=1 Ack=1 Win=408256 Len=0 TSval=517670095 TSecr=517670095
    4 0.000166       127.0.0.1             127.0.0.1             TCP      56     [TCP Window Update] 30081 → 63299 [ACK] Seq=1 Ack=1 Win=408256 Len=0 TSval=517670095 TSecr=517670095
    5 0.000292       127.0.0.1             127.0.0.1             TCP      67     63299 → 30081 [PSH, ACK] Seq=1 Ack=1 Win=408256 Len=11 TSval=517670095 TSecr=517670095
    6 0.000319       127.0.0.1             127.0.0.1             TCP      56     30081 → 63299 [ACK] Seq=1 Ack=12 Win=408256 Len=0 TSval=517670095 TSecr=517670095
    7 0.000429       127.0.0.1             127.0.0.1             TCP      68     30081 → 63299 [PSH, ACK] Seq=1 Ack=12 Win=408256 Len=12 TSval=517670095 TSecr=517670095
    8 0.000457       127.0.0.1             127.0.0.1             TCP      56     63299 → 30081 [ACK] Seq=12 Ack=13 Win=408256 Len=0 TSval=517670095 TSecr=517670095
    9 0.001006       127.0.0.1             127.0.0.1             TCP      56     30081 → 63299 [FIN, ACK] Seq=13 Ack=12 Win=408256 Len=0 TSval=517670096 TSecr=517670095
   10 0.001040       127.0.0.1             127.0.0.1             TCP      56     63299 → 30081 [ACK] Seq=12 Ack=14 Win=408256 Len=0 TSval=517670096 TSecr=517670096
   11 0.001058       127.0.0.1             127.0.0.1             TCP      56     63299 → 30081 [FIN, ACK] Seq=12 Ack=14 Win=408256 Len=0 TSval=517670096 TSecr=517670096
   12 0.001118       127.0.0.1             127.0.0.1             TCP      56     30081 → 63299 [ACK] Seq=14 Ack=13 Win=408256 Len=0 TSval=517670096 TSecr=517670096

打开 Wireshark 抓包数据,如下图:

202001 wireshark tcp
Figure 2. 源文件:https://1drv.ms/u/s!Anua9d_TpfhPoUEcI6DxI13ND6sl

TCP 三次握手

TCP 四次挥手

HTTP 协议

HTTP 协议是 OSI 模型中应用层协议,主要解决数据如何包装。HTTP 协议建立在 TCP 协议之上, TCP 协议在传输层负责数据的传输,数据到达应用层后,由 HTTP 协议负责解析数据,再进行之后的处理。

浏览器是如何工作的:

疑问

为什么是 3 次握手,为什么是 4 次挥手?

未完,点我催更