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

問題描述

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

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

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

1
ip, port = self._socket.getsockname()

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

解決方案

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

1
2
3
4
5
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 。 但您看有多少個答案!排第一的答案還不適用山姆鍋遇到的情況。 所以,簡單歸簡單,這個問題還是值得紀錄一下讓更多人容易找到答案。