使用 Python 建立 Socket 物件並綁定 (bind) 到網卡後 , 該如何取得實際綁定的 IP 位址 ? 這個問題 多數人都會覺得簡單 , 但真的有這麼容易嗎 ?

問題描述

在 Python 網路服務器程式中 , 假設使用 UDP 協定的話 , 通常可以看到類似下面程式片段用來綁定 socket:

self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self._socket.bind( ('0.0.0.0', 1234) )

這樣的程式碼稀鬆平常 , 但您知道該如何取得實際綁定的本地 IP 位址嗎 ? 查了一下參考資料 , 您很快就會發現 Socket 物件有個 'getsockname' 方法可以回傳位址跟 port。

ip, port = self._socket.getsockname()

正當您覺得簡單完成任務的時候 , 卻發現 'ip' 變數得到的是 '0.0.0.0', 跟當初綁定用的值相同 ! 試了 '127.0.0.1' 也都是同樣的情況 , 怎麼會這樣 ? 顯然 'getsockname' 的實作跟我們的期待完全不同 。

解決方案

下面程式片段示範如何取的本地網卡所使用的 IP 位址跟埠號 (port):

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 80))  # 1
ip, _ = s.getsockname()
s.close()
return ip

其中標記 1 的地方是整個片段可以運作的關鍵 。 另外 , 上述程式片段有可能會丟出 IOError 的例外 , 請記得要處理 。

這裡假設綁定 socket 的網卡跟用來實際通訊的網卡是同一張 。

結語

雖然 StackOverflow 網站可以輕鬆找到這種問題的解答 , 或者該說眾多的解答 , 例如這個 :http://stackoverflow.com/questions/166506/finding-local-ip-addresses-using-pythons-stdlib 。 但您看有多少個答案 ! 排第一的答案還不適用山姆鍋遇到的情況 。 所以 , 簡單歸簡單 , 這個問題還是值得紀錄一下讓更多人容易找到答案 。