1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Go 語(yǔ)言實(shí)現(xiàn)簡(jiǎn)易版 netstat 命令

        共 15092字,需瀏覽 31分鐘

         ·

        2021-07-13 00:41

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

        netstat 工作原理

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

        netstat 工作原理如下:

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

        我們可以做個(gè)測(cè)試驗(yàn)證整個(gè)流程。先使用 nc 命令監(jiān)聽(tīng) 8090 端口:

        nc -l 8090

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

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

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

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

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

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

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

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

        /proc/net/tcp 文件格式

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

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

        每一行各個(gè)字段解釋說(shuō)明如下,由于太長(zhǎng)分為三部分說(shuō)明:

        第一部分:

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

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

        enum {
         TCP_ESTABLISHED = 1,
         TCP_SYN_SENT,
         TCP_SYN_RECV,
         TCP_FIN_WAIT1,
         TCP_FIN_WAIT2,
         TCP_TIME_WAIT,
         TCP_CLOSE,
         TCP_CLOSE_WAIT,
         TCP_LAST_ACK,
         TCP_LISTEN,
         TCP_CLOSING, /* Now a valid state */
         TCP_NEW_SYN_RECV,

         TCP_MAX_STATES /* Leave at the end! */
        };

        第二部分:

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

        timer_active 所有值與說(shuō)明如下:

        • 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

        第三部分:

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

        Go 實(shí)現(xiàn)簡(jiǎn)易版本 netstat 命令

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

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

        // 狀態(tài)碼值
        const (
         TCP_ESTABLISHED = iota + 1
         TCP_SYN_SENT
         TCP_SYN_RECV
         TCP_FIN_WAIT1
         TCP_FIN_WAIT2
         TCP_TIME_WAIT
         TCP_CLOSE
         TCP_CLOSE_WAIT
         TCP_LAST_ACK
         TCP_LISTEN
         TCP_CLOSING
         //TCP_NEW_SYN_RECV
         //TCP_MAX_STATES
        )

        // 狀態(tài)碼
        var states = map[int]string{
         TCP_ESTABLISHED: "ESTABLISHED",
         TCP_SYN_SENT:    "SYN_SENT",
         TCP_SYN_RECV:    "SYN_RECV",
         TCP_FIN_WAIT1:   "FIN_WAIT1",
         TCP_FIN_WAIT2:   "FIN_WAIT2",
         TCP_TIME_WAIT:   "TIME_WAIT",
         TCP_CLOSE:       "CLOSE",
         TCP_CLOSE_WAIT:  "CLOSE_WAIT",
         TCP_LAST_ACK:    "LAST_ACK",
         TCP_LISTEN:      "LISTEN",
         TCP_CLOSING:     "CLOSING",
         //TCP_NEW_SYN_RECV: "NEW_SYN_RECV",
         //TCP_MAX_STATES:   "MAX_STATES",
        }

        // socketEntry 結(jié)構(gòu)體,用來(lái)存儲(chǔ)/proc/net/tcp 每一行解析后數(shù)據(jù)信息
        type socketEntry struct {
         id      int
         srcIP   net.IP
         srcPort int
         dstIP   net.IP
         dstPort int
         state   string

         txQueue       int
         rxQueue       int
         timer         int8
         timerDuration time.Duration
         rto           time.Duration // retransmission timeout
         uid           int
         uname         string
         timeout       time.Duration
         inode         string
        }

        // 解析/proc/net/tcp 行記錄
        func parseRawSocketEntry(entry string) (*socketEntry, error) {
         se := &socketEntry{}
         entrys := strings.Split(strings.TrimSpace(entry), " ")
         entryItems := make([]string017)
         for _, ent := range entrys {
          if ent == "" {
           continue
          }
          entryItems = append(entryItems, ent)
         }

         id, err := strconv.Atoi(string(entryItems[0][:len(entryItems[0])-1]))
         if err != nil {
          return nil, err
         }
         se.id = id                                     // sockect entry id
         localAddr := strings.Split(entryItems[1], ":"// 本地 ip
         se.srcIP = parseHexBigEndianIPStr(localAddr[0])
         port, err := strconv.ParseInt(localAddr[1], 1632// 本地 port
         if err != nil {
          return nil, err
         }
         se.srcPort = int(port)

         remoteAddr := strings.Split(entryItems[2], ":"// 遠(yuǎn)程 ip
         se.dstIP = parseHexBigEndianIPStr(remoteAddr[0])
         port, err = strconv.ParseInt(remoteAddr[1], 1632// 遠(yuǎn)程 port
         if err != nil {
          return nil, err
         }
         se.dstPort = int(port)

         state, _ := strconv.ParseInt(entryItems[3], 1632// socket 狀態(tài)
         se.state = states[int(state)]

         tcpQueue := strings.Split(entryItems[4], ":")
         tQueue, err := strconv.ParseInt(tcpQueue[0], 1632// 發(fā)送隊(duì)列數(shù)據(jù)長(zhǎng)度
         if err != nil {
          return nil, err
         }
         se.txQueue = int(tQueue)
         sQueue, err := strconv.ParseInt(tcpQueue[1], 1632// 接收隊(duì)列數(shù)據(jù)長(zhǎng)度
         if err != nil {
          return nil, err
         }
         se.rxQueue = int(sQueue)

         se.uid, err = strconv.Atoi(entryItems[7]) // socket uid
         if err != nil {
          return nil, err
         }
         se.uname = systemUsers[entryItems[7]] // socket user name
         se.inode = entryItems[9]              // socket inode
         return se, nil
        }

        // hexIP 是網(wǎng)絡(luò)字節(jié)序/大端法轉(zhuǎn)換成的 16 進(jìn)制的字符串
        func parseHexBigEndianIPStr(hexIP string) net.IP {
         b := []byte(hexIP)
         for i, j := 1len(b)-2; i < j; i, j = i+2, j-2 { // 反轉(zhuǎn)字節(jié),轉(zhuǎn)換成小端法
          b[i], b[i-1], b[j], b[j+1] = b[j+1], b[j], b[i-1], b[i]
         }
         l, _ := strconv.ParseInt(string(b), 1664)
         return net.IPv4(byte(l>>24), byte(l>>16), byte(l>>8), byte(l))
        }

        文章轉(zhuǎn)載:IT大咖說(shuō) 
        (版權(quán)歸原作者所有,侵刪)

        點(diǎn)擊下方“閱讀原文”查看更多

        瀏覽 126
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            老女人性爱视频 | 青青久久 | 久久久久免费精品视频 | 国产激情电影 | 未满十八18禁止免费无码网站 | 成人无码影片在线观看 | 日韩一级精品视频 | 91蝌蚪91 九色白浆 | 丁香花高清在线完整版 | 中文字幕免费视频 |