一個由端口號引起的奇怪事件

一個由端口號引起的奇怪事件

引子

最近在寫畢業論文,我的研究方向與計算機網絡相關,實驗部分還差點數據仿真測試。一般,網絡的仿真,一般用iperf這個工具進行帶寬測試。iperf的使用方法挺簡單的,以下面這個圖為例,兩個終端分別是兩個由Mininet模擬出來的用戶,h1的IP地址是10.0.0.1,h3的IP地址是10.0.0.3。在h3上運行iperf服務器端(參數-s),監聽UDP(參數-u),每秒輸出一次(參數-i),在h1上運行iperf客戶端(參數-c),指定服務器端IP地址為10.0.0.3(即h3),每秒輸出一次(參數-i),指定UDP帶寬為50M(參數-b)。

iperf示例.png

這里可以提供一個先驗知識,iperf服務器端開啟的默認端口是5001,客戶端沒有固定端口(比如上圖h1的端口是48921)

場景復現

跑仿真、跑數據,肯定不可能手動開啟一個個終端再一個個去執行命令,最好是寫一個自動化的腳本去模擬我要做的事。

比如現在我想同時運行h1 iperf h3與h2 iperf h3,如果要手動操作,得開四個終端,其中兩個終端是h3,運行iperf服務器端,另外兩個終端分別是h1與h2,運行iperf客戶端,這樣肯定效率非常低下,而且切換終端是有時間差的,數據可能不精準,于是我考慮在Mininet源碼中加入一些命令,方便我測試,下面的截圖就是我在Mininet CLI中執行自定義的iperfdouble命令的效果圖(在本例中,都是h3作為iperf服務器端)

mininet> iperfdouble h1 h2 h3 50m 60
*** Iperf: testing bandwidth between h1 and h3
***start server***
***start client***
***client ping server***
*** Iperf: testing bandwidth between h2 and h3
***start server***
***start client***
mininet> 【光標閃爍】

自定義的iperfdouble實現代碼如下,前三個主機參數必須有,后面兩個分別是帶寬與持續時間,因為下游函數iperf_single中對這兩個參數設置了默認值,故可忽略

    def do_iperfdouble( self, line ):
        """Multi iperf UDP test between two specified nodes
        $iperfdouble h1 h2 h3 50m 10"""
        
        args = line.split()
        hosts1 = []
        hosts2 = []
        now = strftime("%Y%m%dT%H%M%S", localtime())
        for i in range(3):
            if args[i] not in self.mn:
                err = True
                error( "node '%s' not in network\n" % args[i] )
            else:
                if i == 0:
                    hosts1.append( self.mn[ args[i] ] )
                elif i == 1:
                    hosts2.append( self.mn[ args[i] ] )
                else:
                    hosts1.append( self.mn[ args[i] ] )
                    hosts2.append( self.mn[ args[i] ] )
        err = False
        if len(args) == 3:
            if not err:
                self.mn.iperf_single(now, hosts1 )
                self.mn.iperf_single(now, hosts2 )
        elif len(args) == 5:
            udpBw = args[ 3 ]
            period = args[ 4 ]
            err = False
            if not err:
                self.mn.iperf_single(now, hosts1, udpBw, float(period))
                self.mn.iperf_single(now, hosts2, udpBw, float(period))
        else:
            error('invalid number of args: iperfdouble node1 node2 node3 udpBw period\n' +
                'examples: iperfdouble h1 h2 h3 [50M] [10]\n')

iperfdouble會提取兩對主機,分別運行iperf_single函數,該函數定義如下,iperf服務器端的輸出會附帶當前時間戳存儲到log文件夾中:

    def iperf_single( self, now, hosts=None, udpBw='10M', period=10, port=5001):
        """Run iperf between two hosts using UDP.
        hosts: list of hosts; if None, uses opposite hosts
        returns: results two-element array of server and client speeds"""
        if not hosts:
            return
        else:
            assert len( hosts ) == 2
        client, server = hosts
        iperf_output = 'h' + client.name[1:] + 'iperf' + 'h' + server.name[1:] + '.' + now
        # ping_output = 'h' + client.name[1:] + 'ping' + 'h' + server.name[1:] + '.' + now
        output( '*** Iperf: testing bandwidth between ' )
        output( "%s and %s\n" % ( client.name, server.name ) )
        iperfArgs = 'iperf -u '
        bwArgs = '-b ' + udpBw + ' '
        print "***start server***"
        server.cmd(iperfArgs + '-s -P 1 -i 1' +
                   ' > /home/liaoss/network/log/' + iperf_output + '&')
        print "***start client***"
        client.cmd(iperfArgs + '-t '+ str(period) + ' -c ' + server.IP() + ' ' + bwArgs + '&')
        # print "***client ping server***"
        # client.cmd('ping -c' + str(period) + ' ' + server.IP() +
        #            ' ' + ' > /home/liaoss/network/log/' + ping_output + '&')

于是,我就得到了這樣的log文件,有什么發現奇怪的地方嗎?

logstrange.png

看看左邊文件紅線框出來的,文件名是h1iperfh3(從iperf_single的代碼可以看出來,log文件命名格式是:[client]iperf[server].timestamp),但是根據iperf輸出,這些數據來自于h2!右邊log文件正好相反,文件名中client是h2,但是數據來自于h1!

是不是很奇怪?讀者讀到這里,可以再仔細看看iperfdoube與iperf_single的代碼,看看能不能發現到底哪里出了問題。

問題原因

其實,本文的標題已經給出了答案,問題的根源在于端口號,注意到,iperf服務器端的默認端口號是5001,iperf客戶端如果不指定端口號,默認發送的服務器端端口號也會5001,觀察上圖兩個log文件,h3作為iperf服務器端開了兩個進程,效果等同于下圖兩個iperf服務器端進程,pid分別為1676與1916

iperfserversame.png

假設這時h1作為iperf客戶端向h3發起UDP流量,到底h3哪個iperf服務器端進程會建立連接呢?下圖給出了答案,pid為1916的被選擇了(我也不太清楚為啥選擇它,可能是隨機的?經測試,兩個h3iperf服務器端開啟先后順序不影響結論,讀者如果有想法可以告訴筆者)

h1twoh3.png

寫到這里,問題的根源應該有些眉目了,在iperf_single函數中,iperf并未指定端口號,所以h3iperf服務器端的輸出并不與函數的client參數一致,我們再以一個無序的例子佐以證明,下圖的左上、右上運行h3iperf服務器端,左下與右下分別是h1與h2的iperf客戶端,四個命令均不指定端口號,在h1與h2上運行iperf客戶端,發現都是由右上的h3iperf服務器端接收,看起來這真的挺隨機的。

iperfunordered.png

解決辦法

在iperf_single中,指定端口號即可,因為在本場景中,都是h1與h2作為iperf客戶端,所以這里把'hx'中的數字'x'提取出來,拼在'500'后面即可,這樣h1連接的就是端口5001,h2連接的就是端口5002,所以有點hard code之嫌,但這里僅提供一個解決方法而已

iperf-p.png

最終,log日志正確輸出了

logyes.png

總結

有時候,問題的根源不像表面那般清晰,得掌握一定的知識才能深入分析,從而解決問題。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 實施基本的Stateful Firewall 說明:翻譯自P4官方教程P4 Tutorial的Implementi...
    SmartSloth閱讀 1,378評論 0 0
  • 端口號:具有網絡功能的應用軟件的標識號。注意,端口號是不固定的,即可以由用戶手工可以分配(當然,一般在軟件編寫時就...
    隨心吧閱讀 1,501評論 0 2
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML標準。 注意:講述HT...
    kismetajun閱讀 27,646評論 1 45
  • Doctype作用? 嚴格模式與混雜模式如何區分?它們有何意義? 聲明位于文檔中的最前面,處于 標簽之前。告知瀏覽...
    空城皆是舊夢閱讀 660評論 0 3
  • 今天感恩節哎,感謝一直在我身邊的親朋好友。感恩相遇!感恩不離不棄。 中午開了第一次的黨會,身份的轉變要...
    迷月閃星情閱讀 10,609評論 0 11