欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go網(wǎng)絡(luò)編程TCP抓包實(shí)操示例探究

 更新時(shí)間:2024年01月18日 10:27:24   作者:機(jī)器鈴砍菜刀  
作為一名軟件開(kāi)發(fā)者,網(wǎng)絡(luò)編程是必備知識(shí),本文通過(guò)?Go?語(yǔ)言實(shí)現(xiàn)?TCP?套接字編程,并結(jié)合?tcpdump?工具,展示它的三次握手、數(shù)據(jù)傳輸以及四次揮手的過(guò)程,幫助讀者更好地理解?TCP?協(xié)議與?Go?網(wǎng)絡(luò)編程

Go 網(wǎng)絡(luò)編程模型

在實(shí)現(xiàn) Go 的 TCP 代碼前,我們先了解一下 Go 的網(wǎng)絡(luò)編程模型。

網(wǎng)絡(luò)編程屬于 IO 的范疇,其發(fā)展可以簡(jiǎn)單概括為:多進(jìn)程 -> 多線程 -> non-block + I/O 多路復(fù)用。

想必讀者在初學(xué) IO 模型時(shí),一定對(duì)阻塞和非阻塞、同步和異步感到頭疼,而 I/O 多路復(fù)用的回調(diào)更是讓人抓狂。Go 在設(shè)計(jì)網(wǎng)絡(luò)模型時(shí),就考慮到需要幫助開(kāi)發(fā)者簡(jiǎn)化開(kāi)發(fā)復(fù)雜度,降低心智負(fù)擔(dān),同時(shí)滿足高性能要求。

Go 語(yǔ)言的網(wǎng)絡(luò)編程模型是同步網(wǎng)絡(luò)編程。它基于 協(xié)程 + I/O 多路復(fù)用 (linux 下 epoll,darwin 下 kqueue,windows 下 iocp,通過(guò)網(wǎng)絡(luò)輪詢器 netpoller 進(jìn)行封裝),結(jié)合網(wǎng)絡(luò)輪詢器與調(diào)度器實(shí)現(xiàn)。

用戶層 goroutine 中的 block socket,實(shí)際上是通過(guò) netpoller 模擬出來(lái)的。runtime 攔截了底層 socket 系統(tǒng)調(diào)用的錯(cuò)誤碼,并通過(guò) netpoller 和 goroutine 調(diào)度讓 goroutine 阻塞在用戶層得到的 socket fd 上。

Go 將網(wǎng)絡(luò)編程的復(fù)雜性隱藏于 runtime 中:開(kāi)發(fā)者不用關(guān)注 socket 是否是 non-block 的,也不用處理回調(diào),只需在每個(gè)連接對(duì)應(yīng)的 goroutine 中以 block I/O 的方式對(duì)待 socket 即可。

例如:當(dāng)用戶層針對(duì)某個(gè) socket fd 發(fā)起 read 操作時(shí),如果該 socket fd 中尚無(wú)數(shù)據(jù),那么 runtime 會(huì)將該 socket fd 加入到 netpoller 中監(jiān)聽(tīng),同時(shí)對(duì)應(yīng)的 goroutine 被掛起,直到 runtime 收到 socket fd 數(shù)據(jù) ready 的通知,runtime 才會(huì)重新喚醒等待在該 socket fd 上準(zhǔn)備 read 的那個(gè)goroutine。而這個(gè)過(guò)程從 goroutine 的視角來(lái)看,就像是 read 操作一直 block 在那個(gè) socket fd 上似的。

一句話總結(jié):Go 將復(fù)雜的網(wǎng)絡(luò)模型進(jìn)行封裝,放在用戶面前的只是阻塞式 I/O 的 goroutine,這讓我們可以非常輕松地實(shí)現(xiàn)高性能網(wǎng)絡(luò)編程。

TCP server

在 Go 中,網(wǎng)絡(luò)編程非常容易。我們通過(guò) Go 的 net 包,可以輕松實(shí)現(xiàn)一個(gè) TCP 服務(wù)器。

package main

import (
 "log"
 "net"
)

func main() {
 // Part 1: create a listener
 l, err := net.Listen("tcp", ":8000")
 if err != nil {
  log.Fatalf("Error listener returned: %s", err)
 }
 defer l.Close()

 for {
  // Part 2: accept new connection
  c, err := l.Accept()
  if err != nil {
   log.Fatalf("Error to accept new connection: %s", err)
  }

  // Part 3: create a goroutine that reads and write back data
  go func() {
   log.Printf("TCP session open")
   defer c.Close()

   for {
    d := make([]byte, 100)

    // Read from TCP buffer
    _, err := c.Read(d)
    if err != nil {
     log.Printf("Error reading TCP session: %s", err)
     break
    }
    log.Printf("reading data from client: %s\n", string(d))

    // write back data to TCP client
    _, err = c.Write(d)
    if err != nil {
     log.Printf("Error writing TCP session: %s", err)
     break
    }
   }
  }()
 }
}

根據(jù)邏輯,我們將以上代碼分成三個(gè)部分。

第一部分:端口監(jiān)聽(tīng)。我們通過(guò) net.Listen("tcp", ":8000")開(kāi)啟在端口 8000 的 TCP 連接監(jiān)聽(tīng)。

第二部分:建立連接。在開(kāi)啟監(jiān)聽(tīng)成功之后,調(diào)用 net.Listener.Accept()方法等待 TCP 連接。Accept 方法將以阻塞式地等待新的連接到達(dá),并將該連接作為 net.Conn 接口類型返回。

第三部分:數(shù)據(jù)傳輸。當(dāng)連接建立成功后,我們將啟動(dòng)一個(gè)新的 goroutine 來(lái)處理 c 連接上的讀取和寫(xiě)入。本文服務(wù)器的數(shù)據(jù)處理邏輯是,客戶端寫(xiě)入該 TCP 連接的所有內(nèi)容,服務(wù)器將原封不動(dòng)地寫(xiě)回相同的內(nèi)容。

TCP client

同樣,通過(guò) net 包也能快速實(shí)現(xiàn)一個(gè) TCP 客戶端。

package main

import (
 "log"
 "net"
 "time"
)

func main() {
 // Part 1: open a TCP session to server
 c, err := net.Dial("tcp", "localhost:8000")
 if err != nil {
  log.Fatalf("Error to open TCP connection: %s", err)
 }
 defer c.Close()

 // Part2: write some data to server
 log.Printf("TCP session open")
 b := []byte("Hi, gopher?")
 _, err = c.Write(b)
 if err != nil {
  log.Fatalf("Error writing TCP session: %s", err)
 }

 // Part3: create a goroutine that closes TCP session after 10 seconds
 go func() {
  <-time.After(time.Duration(10) * time.Second)
  defer c.Close()
 }()

 // Part4: read any responses until get an error
 for {
  d := make([]byte, 100)
  _, err := c.Read(d)
  if err != nil {
   log.Fatalf("Error reading TCP session: %s", err)
  }
  log.Printf("reading data from server: %s\n", string(d))
 }
}

將以上代碼分為四個(gè)部分。

第一部分:建立連接。我們通過(guò) net.Dial("tcp", "localhost:8000")連接一個(gè) TCP 連接到服務(wù)器正在監(jiān)聽(tīng)的同一個(gè) localhost:8000 地址。

第二部分:寫(xiě)入數(shù)據(jù)。當(dāng)連接建立成功后,通過(guò) c.Write() 方法寫(xiě)入數(shù)據(jù) Hi, gopher? 給服務(wù)器。

第三部分:關(guān)閉連接。啟動(dòng)一個(gè)新的 goroutine,在 10s 后調(diào)用 c.Close() 方法關(guān)閉 TCP 連接。

第四部分:讀取數(shù)據(jù)。除非發(fā)生 error,否則客戶端通過(guò) c.Read()  方法(記住,是阻塞式的)循環(huán)讀取 TCP 連接上的內(nèi)容。

抓包分析

tcpdump 是一個(gè)非常好用的數(shù)據(jù)抓包工具,它可以幫助我們捕獲和查看網(wǎng)絡(luò)數(shù)據(jù)包。

現(xiàn)在,我們通過(guò) tcpdump 來(lái)抓取上文 TCP 客戶端與服務(wù)器通信全過(guò)程數(shù)據(jù)。

tcpdump -S -nn -vvv -i lo0 port 8000

在本例中,通過(guò)使用 -i lo0 指定捕獲環(huán)回接口 localhost,使用 port 8000 將網(wǎng)絡(luò)捕獲過(guò)濾為僅與端口 8000 通信或來(lái)自端口 8000 的流量,-vvv是為了打印更多的詳細(xì)描述信息,-S 顯示序列號(hào)絕對(duì)值。

當(dāng)運(yùn)行 tcpdump 后,我們分別啟動(dòng)服務(wù)端和客戶端代碼。

運(yùn)行服務(wù)端代碼

$ go run main.go
2021/09/20 19:41:17 TCP session open
2021/09/20 19:41:17 reading data from client: Hi, gopher?
2021/09/20 19:41:27 Error reading TCP session: EOF

服務(wù)器和客戶端建立連接之后,從客戶端讀取到數(shù)據(jù) Hi, gopher? 。在 10s 后,由于客戶端關(guān)閉了連接,服務(wù)端讀取到了 EOF 錯(cuò)誤。

運(yùn)行客戶端代碼

$ go run main.go
2021/09/20 19:41:17 TCP session open
2021/09/20 19:41:17 reading data from server: Hi, gopher?
2021/09/20 19:41:27 Error reading TCP session: read tcp 127.0.0.1:57596->127.0.0.1:8000: use of closed network connection

客戶端和服務(wù)器建立連接之后,發(fā)送數(shù)據(jù)給服務(wù)端,服務(wù)端返回相同的數(shù)據(jù) Hi, gopher? 回來(lái)。在 10s 后,客戶端通過(guò)一個(gè)新的 goroutine 主動(dòng)關(guān)閉了連接,因此阻塞在 c.Read 的客戶端代碼捕獲到了錯(cuò)誤:use of closed network connection

那我們通過(guò) tcpdump 抓取的本次通信過(guò)程如何呢?首先,我們先通過(guò)一張圖片回顧一下經(jīng)典的 TCP 通信全過(guò)程。

以下是 tcpdump 抓取的結(jié)果

$ tcpdump -S -nn -vvv -i lo0 port 8000
tcpdump: listening on lo0, link-type NULL (BSD loopback), capture size 262144 bytes
19:41:17.109462 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 64, bad cksum 0 (->3cb6)!)
    127.0.0.1.57596 > 127.0.0.1.8000: Flags [S], cksum 0xfe34 (incorrect -> 0x18e6), seq 2046827845, win 65535, options [mss 16344,nop,wscale 6,nop,nop,TS val 678438397 ecr 0,sackOK,eol], length 0
19:41:17.109547 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 64, bad cksum 0 (->3cb6)!)
    127.0.0.1.8000 > 127.0.0.1.57596: Flags [S.], cksum 0xfe34 (incorrect -> 0x8b10), seq 1697569320, ack 2046827846, win 65535, options [mss 16344,nop,wscale 6,nop,nop,TS val 678438397 ecr 678438397,sackOK,eol], length 0
19:41:17.109558 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 52, bad cksum 0 (->3cc2)!)
    127.0.0.1.57596 > 127.0.0.1.8000: Flags [.], cksum 0xfe28 (incorrect -> 0xec19), seq 2046827846, ack 1697569321, win 6379, options [nop,nop,TS val 678438397 ecr 678438397], length 0
19:41:17.109567 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 52, bad cksum 0 (->3cc2)!)
    127.0.0.1.8000 > 127.0.0.1.57596: Flags [.], cksum 0xfe28 (incorrect -> 0xec19), seq 1697569321, ack 2046827846, win 6379, options [nop,nop,TS val 678438397 ecr 678438397], length 0
19:41:17.109767 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 63, bad cksum 0 (->3cb7)!)
    127.0.0.1.57596 > 127.0.0.1.8000: Flags [P.], cksum 0xfe33 (incorrect -> 0xfb32), seq 2046827846:2046827857, ack 1697569321, win 6379, options [nop,nop,TS val 678438397 ecr 678438397], length 11
19:41:17.109781 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 52, bad cksum 0 (->3cc2)!)
    127.0.0.1.8000 > 127.0.0.1.57596: Flags [.], cksum 0xfe28 (incorrect -> 0xec0e), seq 1697569321, ack 2046827857, win 6379, options [nop,nop,TS val 678438397 ecr 678438397], length 0
19:41:17.109862 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 152, bad cksum 0 (->3c5e)!)
    127.0.0.1.8000 > 127.0.0.1.57596: Flags [P.], cksum 0xfe8c (incorrect -> 0xface), seq 1697569321:1697569421, ack 2046827857, win 6379, options [nop,nop,TS val 678438397 ecr 678438397], length 100
19:41:17.109872 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 52, bad cksum 0 (->3cc2)!)
    127.0.0.1.57596 > 127.0.0.1.8000: Flags [.], cksum 0xfe28 (incorrect -> 0xebab), seq 2046827857, ack 1697569421, win 6378, options [nop,nop,TS val 678438397 ecr 678438397], length 0
19:41:27.113831 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 52, bad cksum 0 (->3cc2)!)
    127.0.0.1.57596 > 127.0.0.1.8000: Flags [F.], cksum 0xfe28 (incorrect -> 0xc49f), seq 2046827857, ack 1697569421, win 6378, options [nop,nop,TS val 678448392 ecr 678438397], length 0
19:41:27.113910 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 52, bad cksum 0 (->3cc2)!)
    127.0.0.1.8000 > 127.0.0.1.57596: Flags [.], cksum 0xfe28 (incorrect -> 0x9d93), seq 1697569421, ack 2046827858, win 6379, options [nop,nop,TS val 678448392 ecr 678448392], length 0
19:41:27.114089 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 52, bad cksum 0 (->3cc2)!)
    127.0.0.1.8000 > 127.0.0.1.57596: Flags [F.], cksum 0xfe28 (incorrect -> 0x9d92), seq 1697569421, ack 2046827858, win 6379, options [nop,nop,TS val 678448392 ecr 678448392], length 0
19:41:27.114187 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 52, bad cksum 0 (->3cc2)!)
    127.0.0.1.57596 > 127.0.0.1.8000: Flags [.], cksum 0xfe28 (incorrect -> 0x9d93), seq 2046827858, ack 1697569422, win 6378, options [nop,nop,TS val 678448392 ecr 678448392], length 0
 

我們重點(diǎn)關(guān)注內(nèi)容 Flags [],其中 [S] 代表 SYN 包,[F] 代表 FIN,[.] 代表對(duì)應(yīng)的 ACK 包。例如 [S.] 代表 SYN-ACK,[F.] 代表 FIN-ACK??梢院苊黠@看出 TCP 通信的全過(guò)程如下圖所示。

總結(jié)

本文簡(jiǎn)單介紹了 Go 同步編程模式的網(wǎng)絡(luò)模型。有了 runtime 中網(wǎng)絡(luò)輪訓(xùn)器與調(diào)度器的參與,使用 Go 進(jìn)行高性能網(wǎng)絡(luò)編程,高手與菜鳥(niǎo)開(kāi)發(fā)者的差距被極大地縮小。

Go 原生的 net 庫(kù)對(duì) socket 編程進(jìn)行了很好地封裝,它提供的函數(shù)方法語(yǔ)義明朗,邏輯清晰?;谕骄幊棠J?,每個(gè)人都可以很容易地進(jìn)行 TCP 網(wǎng)絡(luò)編程。利用 tcpdump 工具,我們能夠進(jìn)行網(wǎng)絡(luò)分析和問(wèn)題排查,建議實(shí)操掌握。

參考

http://www.dbjr.com.cn/article/202349.htm

https://draveness.me/golang/docs/part3-runtime/ch06-concurrency/golang-netpoller/ 

以上就是Go網(wǎng)絡(luò)編程TCP抓包實(shí)操示例探究的詳細(xì)內(nèi)容,更多關(guān)于Go網(wǎng)絡(luò)編程TCP抓包的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 基于Golang實(shí)現(xiàn)Excel表格的導(dǎo)入導(dǎo)出功能

    基于Golang實(shí)現(xiàn)Excel表格的導(dǎo)入導(dǎo)出功能

    最近項(xiàng)目開(kāi)發(fā)中有涉及到Excel的導(dǎo)入與導(dǎo)出功能,特別是導(dǎo)出表格時(shí)需要特定的格式,所以本文給大家介紹了基于Golang實(shí)現(xiàn)Excel表格的導(dǎo)入導(dǎo)出功能,文中通過(guò)代碼示例和圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-12-12
  • 通過(guò)示例深度理解Go channel range

    通過(guò)示例深度理解Go channel range

    這篇文章主要為大家介紹了Go channel range使用示例深度理解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • Go中的go.mod使用詳解

    Go中的go.mod使用詳解

    這篇文章主要介紹了Go中的go.mod使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • Go語(yǔ)言定時(shí)任務(wù)cron的設(shè)計(jì)與使用

    Go語(yǔ)言定時(shí)任務(wù)cron的設(shè)計(jì)與使用

    這篇文章主要為大家詳細(xì)介紹了Go語(yǔ)言中定時(shí)任務(wù)cron的設(shè)計(jì)與使用,文中的示例代碼講解詳細(xì),對(duì)我們深入掌握Go語(yǔ)言有一定的幫助,需要的可以參考下
    2023-11-11
  • golang如何去除字符串的換行符

    golang如何去除字符串的換行符

    這篇文章主要介紹了golang如何去除字符串的換行符問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • Go語(yǔ)言標(biāo)準(zhǔn)輸入輸出庫(kù)的基本使用教程

    Go語(yǔ)言標(biāo)準(zhǔn)輸入輸出庫(kù)的基本使用教程

    輸入輸出在任何一門(mén)語(yǔ)言中都必須提供的一個(gè)功能,下面這篇文章主要給大家介紹了關(guān)于Go語(yǔ)言標(biāo)準(zhǔn)輸入輸出庫(kù)的基本使用,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-02-02
  • Golang?channel為什么不會(huì)阻塞的原因詳解

    Golang?channel為什么不會(huì)阻塞的原因詳解

    這篇文章主要為大家介紹了Golang?channel為什么不會(huì)阻塞的原因詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • windows安裝部署go超詳細(xì)實(shí)戰(zhàn)記錄(實(shí)測(cè)有用!)

    windows安裝部署go超詳細(xì)實(shí)戰(zhàn)記錄(實(shí)測(cè)有用!)

    Golang語(yǔ)言在近年來(lái)因?yàn)槠涓咝阅?、編譯速度快、開(kāi)發(fā)成本低等特點(diǎn)逐漸得到大家的青睞,這篇文章主要給大家介紹了關(guān)于windows安裝部署go超詳細(xì)實(shí)戰(zhàn)的相關(guān)資料,需要的朋友可以參考下
    2023-02-02
  • golang實(shí)現(xiàn)各種情況的get請(qǐng)求操作

    golang實(shí)現(xiàn)各種情況的get請(qǐng)求操作

    這篇文章主要介紹了golang實(shí)現(xiàn)各種情況的get請(qǐng)求操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • Go?time包AddDate使用解惑實(shí)例詳解

    Go?time包AddDate使用解惑實(shí)例詳解

    這篇文章主要為大家介紹了Go?time包AddDate使用解惑實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09

最新評(píng)論