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

Go網絡編程TCP抓包實操示例探究

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

Go 網絡編程模型

在實現 Go 的 TCP 代碼前,我們先了解一下 Go 的網絡編程模型。

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

想必讀者在初學 IO 模型時,一定對阻塞和非阻塞、同步和異步感到頭疼,而 I/O 多路復用的回調更是讓人抓狂。Go 在設計網絡模型時,就考慮到需要幫助開發(fā)者簡化開發(fā)復雜度,降低心智負擔,同時滿足高性能要求。

Go 語言的網絡編程模型是同步網絡編程。它基于 協(xié)程 + I/O 多路復用 (linux 下 epoll,darwin 下 kqueue,windows 下 iocp,通過網絡輪詢器 netpoller 進行封裝),結合網絡輪詢器與調度器實現。

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

Go 將網絡編程的復雜性隱藏于 runtime 中:開發(fā)者不用關注 socket 是否是 non-block 的,也不用處理回調,只需在每個連接對應的 goroutine 中以 block I/O 的方式對待 socket 即可。

例如:當用戶層針對某個 socket fd 發(fā)起 read 操作時,如果該 socket fd 中尚無數據,那么 runtime 會將該 socket fd 加入到 netpoller 中監(jiān)聽,同時對應的 goroutine 被掛起,直到 runtime 收到 socket fd 數據 ready 的通知,runtime 才會重新喚醒等待在該 socket fd 上準備 read 的那個goroutine。而這個過程從 goroutine 的視角來看,就像是 read 操作一直 block 在那個 socket fd 上似的。

一句話總結:Go 將復雜的網絡模型進行封裝,放在用戶面前的只是阻塞式 I/O 的 goroutine,這讓我們可以非常輕松地實現高性能網絡編程。

TCP server

在 Go 中,網絡編程非常容易。我們通過 Go 的 net 包,可以輕松實現一個 TCP 服務器。

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
    }
   }
  }()
 }
}

根據邏輯,我們將以上代碼分成三個部分。

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

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

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

TCP client

同樣,通過 net 包也能快速實現一個 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))
 }
}

將以上代碼分為四個部分。

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

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

第三部分:關閉連接。啟動一個新的 goroutine,在 10s 后調用 c.Close() 方法關閉 TCP 連接。

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

抓包分析

tcpdump 是一個非常好用的數據抓包工具,它可以幫助我們捕獲和查看網絡數據包。

現在,我們通過 tcpdump 來抓取上文 TCP 客戶端與服務器通信全過程數據。

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

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

當運行 tcpdump 后,我們分別啟動服務端和客戶端代碼。

運行服務端代碼

$ 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

服務器和客戶端建立連接之后,從客戶端讀取到數據 Hi, gopher? 。在 10s 后,由于客戶端關閉了連接,服務端讀取到了 EOF 錯誤。

運行客戶端代碼

$ 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

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

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

以下是 tcpdump 抓取的結果

$ 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
 

我們重點關注內容 Flags [],其中 [S] 代表 SYN 包,[F] 代表 FIN,[.] 代表對應的 ACK 包。例如 [S.] 代表 SYN-ACK,[F.] 代表 FIN-ACK??梢院苊黠@看出 TCP 通信的全過程如下圖所示。

總結

本文簡單介紹了 Go 同步編程模式的網絡模型。有了 runtime 中網絡輪訓器與調度器的參與,使用 Go 進行高性能網絡編程,高手與菜鳥開發(fā)者的差距被極大地縮小。

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

參考

http://www.fzitv.net/article/202349.htm

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

以上就是Go網絡編程TCP抓包實操示例探究的詳細內容,更多關于Go網絡編程TCP抓包的資料請關注腳本之家其它相關文章!

相關文章

  • Golang中匿名函數的實現

    Golang中匿名函數的實現

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

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

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

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

    如果大家系統(tǒng)的學過C++、Java等語言以及面向對象的話,相信應該對多態(tài)不會陌生。多態(tài)是面向對象范疇當中經常使用并且非常好用的一個功能,它主要是用在強類型語言當中,像是Python這樣的弱類型語言,變量的類型可以隨意變化,也沒有任何限制,其實區(qū)別不是很大
    2021-06-06
  • 一文帶你熟悉Go語言中的分支結構

    一文帶你熟悉Go語言中的分支結構

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

    go實現冒泡排序的示例代碼

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

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

    這篇文章主要介紹了gin自定義中間件解決requestBody不可重讀,確保控制器能夠獲取請求體值,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-10-10
  • Golang負載均衡和保活設計原理示例探究

    Golang負載均衡和保活設計原理示例探究

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

    Golang標準庫之errors包應用方式

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

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

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

    Go語言學習之接口類型(interface)詳解

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

最新評論

车致| 会同县| 华安县| 依兰县| 仁寿县| 山西省| 田林县| 子长县| 那曲县| 诸城市| 营口市| 临颍县| 呼玛县| 资源县| 寻乌县| 三河市| 勐海县| 广宗县| 栾川县| 蒙山县| 嘉义市| 军事| 曲沃县| 修武县| 永宁县| 石狮市| 隆回县| 来宾市| 卢龙县| 香港 | 天气| 阿尔山市| 鹤山市| 会同县| 青岛市| 安泽县| 武清区| 陆丰市| 宁陵县| 张家川| 瑞金市|