最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時(shí)間:2024年01月18日 10:27:24   作者:機(jī)器鈴砍菜刀  
作為一名軟件開發(fā)者,網(wǎng)絡(luò)編程是必備知識(shí),本文通過?Go?語言實(shí)現(xiàn)?TCP?套接字編程,并結(jié)合?tcpdump?工具,展示它的三次握手、數(shù)據(jù)傳輸以及四次揮手的過程,幫助讀者更好地理解?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í),就考慮到需要幫助開發(fā)者簡(jiǎn)化開發(fā)復(fù)雜度,降低心智負(fù)擔(dān),同時(shí)滿足高性能要求。

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

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

Go 將網(wǎng)絡(luò)編程的復(fù)雜性隱藏于 runtime 中:開發(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 中尚無數(shù)據(jù),那么 runtime 會(huì)將該 socket fd 加入到 netpoller 中監(jiān)聽,同時(shí)對(duì)應(yīng)的 goroutine 被掛起,直到 runtime 收到 socket fd 數(shù)據(jù) ready 的通知,runtime 才會(huì)重新喚醒等待在該 socket fd 上準(zhǔn)備 read 的那個(gè)goroutine。而這個(gè)過程從 goroutine 的視角來看,就像是 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ò)編程非常容易。我們通過 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)聽。我們通過 net.Listen("tcp", ":8000")開啟在端口 8000 的 TCP 連接監(jiān)聽。

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

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

TCP client

同樣,通過 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è)部分。

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

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

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

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

抓包分析

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

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

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

在本例中,通過使用 -i lo0 指定捕獲環(huán)回接口 localhost,使用 port 8000 將網(wǎng)絡(luò)捕獲過濾為僅與端口 8000 通信或來自端口 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? 回來。在 10s 后,客戶端通過一個(gè)新的 goroutine 主動(dòng)關(guān)閉了連接,因此阻塞在 c.Read 的客戶端代碼捕獲到了錯(cuò)誤:use of closed network connection。

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

以下是 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 通信的全過程如下圖所示。

總結(jié)

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

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

參考

http://m.fzitv.net/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ù)的實(shí)現(xiàn)

    Golang中匿名函數(shù)的實(shí)現(xiàn)

    本文主要介紹了Golang中匿名函數(shù)的實(shí)現(xiàn),包括直接調(diào)用、賦值給變量及定義全局匿名函數(shù)三種方式,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-06-06
  • Go中Goroutines輕量級(jí)并發(fā)的特性及效率探究

    Go中Goroutines輕量級(jí)并發(fā)的特性及效率探究

    這篇文章主要為大家介紹了Go中Goroutines輕量級(jí)并發(fā)的特性及效率探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • 淺談Go語言多態(tài)的實(shí)現(xiàn)與interface使用

    淺談Go語言多態(tài)的實(shí)現(xiàn)與interface使用

    如果大家系統(tǒng)的學(xué)過C++、Java等語言以及面向?qū)ο蟮脑?,相信?yīng)該對(duì)多態(tài)不會(huì)陌生。多態(tài)是面向?qū)ο蠓懂牣?dāng)中經(jīng)常使用并且非常好用的一個(gè)功能,它主要是用在強(qiáng)類型語言當(dāng)中,像是Python這樣的弱類型語言,變量的類型可以隨意變化,也沒有任何限制,其實(shí)區(qū)別不是很大
    2021-06-06
  • 一文帶你熟悉Go語言中的分支結(jié)構(gòu)

    一文帶你熟悉Go語言中的分支結(jié)構(gòu)

    這篇文章主要和大家分享一下Go語言中的分支結(jié)構(gòu)(if?-?else-if?-?else、switch),文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Go語言有一定的幫助,需要的可以參考一下
    2022-11-11
  • go實(shí)現(xiàn)冒泡排序的示例代碼

    go實(shí)現(xiàn)冒泡排序的示例代碼

    這篇文章主要介紹了go實(shí)現(xiàn)冒泡排序的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • gin自定義中間件解決requestBody不可重讀(請(qǐng)求體取值)

    gin自定義中間件解決requestBody不可重讀(請(qǐng)求體取值)

    這篇文章主要介紹了gin自定義中間件解決requestBody不可重讀,確??刂破髂軌颢@取請(qǐng)求體值,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • Golang負(fù)載均衡和保活設(shè)計(jì)原理示例探究

    Golang負(fù)載均衡和保活設(shè)計(jì)原理示例探究

    這篇文章主要為大家介紹了Golang負(fù)載均衡和?;钤O(shè)計(jì)原理示例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • Golang標(biāo)準(zhǔn)庫之errors包應(yīng)用方式

    Golang標(biāo)準(zhǔn)庫之errors包應(yīng)用方式

    Go語言的errors包提供了基礎(chǔ)的錯(cuò)誤處理能力,允許通過errors.New創(chuàng)建自定義error對(duì)象,error在Go中是一個(gè)接口,通過實(shí)現(xiàn)Error方法來定義錯(cuò)誤文本,對(duì)錯(cuò)誤的比較通?;趯?duì)象地址,而非文本內(nèi)容,因此即使兩個(gè)錯(cuò)誤文本相同
    2024-10-10
  • Go語言中字符串與字節(jié)切片轉(zhuǎn)換的詳細(xì)過程

    Go語言中字符串與字節(jié)切片轉(zhuǎn)換的詳細(xì)過程

    在Go語中,字符串(string)和字節(jié)切片([]byte)是處理文本和二進(jìn)制數(shù)據(jù)的重要類型,理解它們的轉(zhuǎn)換機(jī)制及底層表示形式,是編寫高效代碼的關(guān)鍵,本文將深入探討字符串與字節(jié)切片的轉(zhuǎn)換過程,并通過示例和流程圖幫助讀者掌握其核心原理,需要的朋友可以參考下
    2025-05-05
  • Go語言學(xué)習(xí)之接口類型(interface)詳解

    Go語言學(xué)習(xí)之接口類型(interface)詳解

    接口是用來定義行為的類型,定義的行為不由接口直接實(shí)現(xiàn),而由通過方法由定義的類型實(shí)現(xiàn),本文就來和大家詳細(xì)講講Go語言中接口的使用吧
    2023-03-03

最新評(píng)論

阳西县| 渑池县| 舟曲县| 万山特区| 叙永县| 正镶白旗| 班戈县| 苗栗县| 河池市| 喀喇沁旗| 元谋县| 渭源县| 林口县| 三原县| 冕宁县| 阿瓦提县| 浦江县| 汶川县| 梁平县| 枝江市| 石台县| 德昌县| 南江县| 枞阳县| 梅河口市| 潞城市| 潞西市| 潜山县| 汝城县| 太湖县| 井冈山市| 阿合奇县| 金坛市| 吉木乃县| 姜堰市| 武定县| 漾濞| 兰考县| 丹寨县| 沁阳市| 铜鼓县|