自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Go 語言實現(xiàn)簡易版 netstat 命令

開發(fā) 后端
netstat 使用 go 語言實現(xiàn)是什么操作?本文從 netstat 原理出發(fā)詳細(xì)解讀了這一實踐。

  netstat 使用 go 語言實現(xiàn)是什么操作?本文從 netstat 原理出發(fā)詳細(xì)解讀了這一實踐。

netstat 工作原理

netstat 命令是 linux 系統(tǒng)中查看網(wǎng)絡(luò)情況的一個命令。比如我們可以通過netstat \-ntlp | grep 8080查看監(jiān)聽 8080 端口的進程。

netstat 工作原理如下:

  1.  通過讀取/proc/net/tcp 、/proc/net/tcp6 文件,獲取 socket 本地地址,本地端口,遠(yuǎn)程地址,遠(yuǎn)程端口,狀態(tài),inode 等信息
  2.  接著掃描所有/proc/[pid]/fd 目錄下的的 socket 文件描述符,建立 inode 到進程 pid 映射
  3.  根據(jù) pid 讀取/proc/[pid]/cmdline 文件,獲取進程命令和啟動參數(shù)
  4.  根據(jù) 2,3 步驟,即可以獲得 1 中對應(yīng) socket 的相關(guān)進程信息

我們可以做個測試驗證整個流程。先使用 nc 命令監(jiān)聽 8090 端口: 

  1. nc -l 8090 

找到上面 nc 進程的 pid,查看該進程所有打開的文件描述符: 

  1. vagrant@vagrant:/proc/25556/fd$ ls -alh  
  2. total 0  
  3. dr-x------ 2 vagrant vagrant  0 Nov 18 12:21 .  
  4. dr-xr-xr-x 9 vagrant vagrant  0 Nov 18 12:20 ..  
  5. lrwx------ 1 vagrant vagrant 64 Nov 18 12:21 0 -> /dev/pts/1  
  6. lrwx------ 1 vagrant vagrant 64 Nov 18 12:21 1 -> /dev/pts/1  
  7. lrwx------ 1 vagrant vagrant 64 Nov 18 12:21 2 -> /dev/pts/1  
  8. lrwx------ 1 vagrant vagrant 64 Nov 18 12:21 3 -> socket:[2226056] 

上面列出的所有文件描述中,socket:[2226056]為 nc 命令監(jiān)聽 8090 端口所創(chuàng)建的 socket。其中2226056為該 socket 的 inode。

根據(jù)該 inode 號,我們查看/proc/net/tcp對應(yīng)的記錄信息,其中1F9A為本地端口號,轉(zhuǎn)換成十進制恰好為 8090: 

  1. vagrant@vagrant:/proc/25556/fd$ cat /proc/net/tcp | grep 2226056  
  2.    1: 00000000:1F9A 00000000:0000 0A 00000000:00000000 00:00000000 00000000  1000        0 2226056 1 0000000000000000 100 0 0 10 0 

根據(jù)進程 id,我們查看進程名稱和啟動參數(shù): 

  1. vagrant@vagrant:/proc/25556/fd$ cat /proc/25556/cmdline  
  2. nc-l8090 

下面我們看下/proc/net/tcp文件格式。

/proc/net/tcp 文件格式

/proc/net/tcp文件首先會列出所有監(jiān)聽狀態(tài)的 TCP 套接字,然后列出所有已建立的 TCP 套接字。我們通過head \-n 5 /proc/net/tcp命令查看該文件頭五行: 

  1. sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode  
  2.    0: 0100007F:0019 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 22279 1 0000000000000000 100 0 0 10 0  
  3.    1: 00000000:1FBB 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 21205 1 0000000000000000 100 0 0 10 0  
  4.    2: 00000000:26FB 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 21203 1 0000000000000000 100 0 0 10 0  
  5.    3: 00000000:26FD 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 21201 1 0000000000000000 100 0 0 10 0 

每一行各個字段解釋說明如下,由于太長分為三部分說明:

第一部分: 

  1. 46: 010310AC:9C4C 030310AC:1770 01   
  2. |      |      |      |      |   |--> 連接狀態(tài),16 進制表示,具體值見下面說明  
  3. |      |      |      |      |------> 遠(yuǎn)程 TCP 端口號,主機字節(jié)序,16 進制表示  
  4. |      |      |      |-------------> 遠(yuǎn)程 IPv4 地址,網(wǎng)絡(luò)字節(jié)序,16 進制表示  
  5. |      |      |--------------------> 本地 TCP 端口號,主機字節(jié)序,16 進制表示  
  6. |      |---------------------------> 本地 IPv4 地址,網(wǎng)絡(luò)字節(jié)序,16 進制表示  
  7. |----------------------------------> 條目編號,從 0 開始 

上面連接狀態(tài)所有值如下,具體參見 linux 源碼 tcp\_states.h[1]: 

  1. enum {  
  2.  TCP_ESTABLISHED = 1 
  3.  TCP_SYN_SENT,  
  4.  TCP_SYN_RECV,  
  5.  TCP_FIN_WAIT1,  
  6.  TCP_FIN_WAIT2,  
  7.  TCP_TIME_WAIT,  
  8.  TCP_CLOSE,  
  9.  TCP_CLOSE_WAIT,  
  10.  TCP_LAST_ACK,  
  11.  TCP_LISTEN,  
  12.  TCP_CLOSING, /* Now a valid state */  
  13.  TCP_NEW_SYN_RECV,  
  14.  TCP_MAX_STATES /* Leave at the end! */  
  15. }; 

第二部分: 

  1. 00000150:00000000 01:00000019 00000000    
  2.       |        |     |     |       |--> number of unrecovered RTO timeouts  
  3.       |        |     |     |----------> number of jiffies until timer expires  
  4.       |        |     |----------------> timer_active,具體值見下面說明  
  5.       |        |----------------------> receive-queue,當(dāng)狀態(tài)是 ESTABLISHED,表示接收隊列中數(shù)據(jù)長度;狀態(tài)是 LISTEN,表示已經(jīng)完成連接隊列的長度  
  6.       |-------------------------------> transmit-queue,發(fā)送隊列中數(shù)據(jù)長度 

timer_active 所有值與說明如下:

  •  0 no timer is pending
  •  1 retransmit-timer is pending
  •  2 another timer (e.g. delayed ack or keepalive) is pending
  •  3 this is a socket in TIME_WAIT state. Not all fields will contain data (or even exist)
  •  4 zero window probe timer is pending

第三部分: 

  1. 1000        0 54165785 4 cd1e6040 25 4 27 3 -1  
  2.    |          |    |     |    |     |  | |  | |--> slow start size threshold,   
  3.    |          |    |     |    |     |  | |  |      or -1 if the threshold  
  4.    |          |    |     |    |     |  | |  |      is >= 0xFFFF  
  5.    |          |    |     |    |     |  | |  |----> sending congestion window  
  6.    |          |    |     |    |     |  | |-------> (ack.quick<<1)|ack.pingpong  
  7.    |          |    |     |    |     |  |---------> Predicted tick of soft clock  
  8.    |          |    |     |    |     |              (delayed ACK control data)  
  9.    |          |    |     |    |     |------------> retransmit timeout  
  10.    |          |    |     |    |------------------> location of socket in memory  
  11.    |          |    |     |-----------------------> socket reference count  
  12.    |          |    |-----------------------------> socket 的 inode 號  
  13.    |          |----------------------------------> unanswered 0-window probes 
  14.    |---------------------------------------------> socket 所屬用戶的 uid 

Go 實現(xiàn)簡易版本 netstat 命令

netstat 工作原理和/proc/net/tcp文件結(jié)構(gòu),我們都已經(jīng)了解了,現(xiàn)在可以使用據(jù)此使用 Go 實現(xiàn)一個簡單版本的 netstat 命令。

核心代碼如下,完整代碼參加 go-netstat[2]: 

  1. // 狀態(tài)碼值  
  2. const (  
  3.  TCP_ESTABLISHED = iota + 1  
  4.  TCP_SYN_SENT  
  5.  TCP_SYN_RECV  
  6.  TCP_FIN_WAIT1  
  7.  TCP_FIN_WAIT2 
  8.  TCP_TIME_WAIT  
  9.  TCP_CLOSE  
  10.  TCP_CLOSE_WAIT  
  11.  TCP_LAST_ACK  
  12.  TCP_LISTEN  
  13.  TCP_CLOSING  
  14.  //TCP_NEW_SYN_RECV  
  15.  //TCP_MAX_STATES  
  16.  
  17. // 狀態(tài)碼  
  18. var states = map[int]string{  
  19.  TCP_ESTABLISHED: "ESTABLISHED",  
  20.  TCP_SYN_SENT:    "SYN_SENT",  
  21.  TCP_SYN_RECV:    "SYN_RECV",  
  22.  TCP_FIN_WAIT1:   "FIN_WAIT1",  
  23.  TCP_FIN_WAIT2:   "FIN_WAIT2",  
  24.  TCP_TIME_WAIT:   "TIME_WAIT",  
  25.  TCP_CLOSE:       "CLOSE",  
  26.  TCP_CLOSE_WAIT:  "CLOSE_WAIT",  
  27.  TCP_LAST_ACK:    "LAST_ACK",  
  28.  TCP_LISTEN:      "LISTEN",  
  29.  TCP_CLOSING:     "CLOSING",  
  30.  //TCP_NEW_SYN_RECV: "NEW_SYN_RECV",  
  31.  //TCP_MAX_STATES:   "MAX_STATES",  
  32.  
  33. // socketEntry 結(jié)構(gòu)體,用來存儲/proc/net/tcp 每一行解析后數(shù)據(jù)信息  
  34. type socketEntry struct {  
  35.  id      int  
  36.  srcIP   net.IP  
  37.  srcPort int  
  38.  dstIP   net.IP  
  39.  dstPort int  
  40.  state   string  
  41.  txQueue       int  
  42.  rxQueue       int  
  43.  timer         int8  
  44.  timerDuration time.Duration  
  45.  rto           time.Duration // retransmission timeout  
  46.  uid           int  
  47.  uname         string  
  48.  timeout       time.Duration  
  49.  inode         string  
  50.  
  51. // 解析/proc/net/tcp 行記錄  
  52. func parseRawSocketEntry(entry string) (*socketEntry, error) {  
  53.  se := &socketEntry{} 
  54.  entrys :strings.Split(strings.TrimSpace(entry), " ")  
  55.  entryItems :make([]string, 0, 17)  
  56.  for _, ent :range entrys {  
  57.   if ent == "" {  
  58.    continue  
  59.   }  
  60.   entryItems = append(entryItems, ent)  
  61.  }  
  62.  id, err :strconv.Atoi(string(entryItems[0][:len(entryItems[0])-1]))  
  63.  if err != nil {  
  64.   return nil, err  
  65.  }  
  66.  se.id = id                                     // sockect entry id  
  67.  localAddr :strings.Split(entryItems[1], ":") // 本地 ip  
  68.  se.srcIP = parseHexBigEndianIPStr(localAddr[0])  
  69.  port, err :strconv.ParseInt(localAddr[1], 16, 32) // 本地 port  
  70.  if err != nil {  
  71.   return nil, err  
  72.  }  
  73.  se.srcPort = int(port)  
  74.  remoteAddr :strings.Split(entryItems[2], ":") // 遠(yuǎn)程 ip  
  75.  se.dstIP = parseHexBigEndianIPStr(remoteAddr[0])  
  76.  port, err = strconv.ParseInt(remoteAddr[1], 16, 32) // 遠(yuǎn)程 port  
  77.  if err != nil { 
  78.   return nil, err  
  79.  }  
  80.  se.dstPort = int(port)  
  81.  state, _ :strconv.ParseInt(entryItems[3], 16, 32) // socket 狀態(tài)  
  82.  se.state = states[int(state)] 
  83.  tcpQueue :strings.Split(entryItems[4], ":")  
  84.  tQueue, err :strconv.ParseInt(tcpQueue[0], 16, 32) // 發(fā)送隊列數(shù)據(jù)長度  
  85.  if err != nil {  
  86.   return nil, err  
  87.  }  
  88.  se.txQueue = int(tQueue)  
  89.  sQueue, err :strconv.ParseInt(tcpQueue[1], 16, 32) // 接收隊列數(shù)據(jù)長度  
  90.  if err != nil {  
  91.   return nil, err  
  92.  }  
  93.  se.rxQueue = int(sQueue)   
  94.  se.uid, err = strconv.Atoi(entryItems[7]) // socket uid  
  95.  if err != nil {  
  96.   return nil, err  
  97.  }  
  98.  se.uname = systemUsers[entryItems[7]] // socket user name 
  99.  se.inode = entryItems[9]              // socket inode  
  100.  return se, nil  
  101. // hexIP 是網(wǎng)絡(luò)字節(jié)序/大端法轉(zhuǎn)換成的 16 進制的字符串  
  102. func parseHexBigEndianIPStr(hexIP string) net.IP {  
  103.  b := []byte(hexIP)  
  104.  for i, j :1, len(b)-2; i < j; i, j = i+2, j-2 { // 反轉(zhuǎn)字節(jié),轉(zhuǎn)換成小端法  
  105.   b[i], b[i-1], b[j], b[j+1] = b[j+1], b[j], b[i-1], b[i]  
  106.  }  
  107.  l, _ :strconv.ParseInt(string(b), 16, 64)  
  108.  return net.IPv4(byte(l>>24), byte(l>>16), byte(l>>8), byte(l))  
  109.  

 

責(zé)任編輯:龐桂玉 來源: 馬哥Linux運維
相關(guān)推薦

2023-12-29 08:31:49

Spring框架模塊

2020-08-12 08:56:30

代碼凱撒密碼函數(shù)

2022-11-01 18:29:25

Go語言排序算法

2023-05-08 07:55:05

快速排序Go 語言

2024-08-29 13:23:04

WindowsGo語言

2022-05-19 14:14:26

go語言限流算法

2012-03-13 10:40:58

Google Go

2012-08-06 08:50:05

Go語言

2022-02-11 13:44:56

fiber架構(gòu)React

2024-06-06 09:47:56

2017-01-13 08:37:57

PythonAlphaGoMuGo

2023-07-31 08:01:13

二叉搜索測試

2021-07-26 09:47:38

Go語言C++

2022-04-18 10:01:07

Go 語言漢諾塔游戲

2023-03-27 00:20:48

2022-10-20 11:49:49

JS動畫幀,CSS

2021-03-01 18:35:18

Go語言虛擬機

2021-03-01 21:59:25

編程語言GoCX

2011-12-05 10:37:53

Linux服務(wù)器Shell腳本

2014-12-26 09:52:08

Go
點贊
收藏

51CTO技術(shù)棧公眾號