在最開始接觸bind的時候,只是在寫基於tcp的server端的時候,知道在listen之前需要先bind一下,用來確保socket能在某個固定的連接埠監聽。而bind的時候,函數參數中的連接埠填自己將要綁定的連接埠就行;而IP地址,需要填原生IP,但是也可以用一個宏INADDR_ANY代替,用這個宏就可以不用尋找原生IP,它就可以代替原生IP。當時只覺得這個INADDR_ANY比較神奇,但是由於當時覺得用起來很方便,也沒出啥問題,也就沒有再深究。
但是最近在做RTSP伺服器的時候,有種特殊的應用,導致我不得不對bind這個函數仔細地看一下。
我們知道無論是UDP還是TCP,socket都會與一個本地的IP和連接埠想對應,我們往往把這個IP和連接埠稱之為socket的源地址和源連接埠。當我們作為用戶端利用socket去發送資料時,很少會去考慮這個源地址和源連接埠到底是什麼,我們更關心的是它的目的地址和連接埠。我們往往只有在監聽的時候,才去考慮這個源連接埠,所以我們在監聽的時候會去用bind。當我們bind之後,核心就會將這個socket的源連接埠鎖定到我們設定的連接埠上。但是這就有一個問題,這個bind綁定連接埠,是將本來沒有源連接埠的socket綁定到我們指定的連接埠上,還是將一個已經分配了連接埠的socket重新導向到我們指定的連接埠上呢?
在《UNIX網路編程》這本書中提到:“如果一個TCP客戶或者伺服器未曾調用bind捆綁一個連接埠,當調用connect或listen時,核心就要為相應的通訊端選擇一個臨時介面。”從這句話中可以判斷出,其實在調用socket函數建立socket時,核心還並未給socket分配源地址和源連接埠。而對於UDP,我猜測在調用sendto發送資料時,在未捆綁連接埠的情況下,核心也會隨機分配連接埠。
而我遇到的特殊應用要求我在用UDP發送資料之前要告訴對方我的傳送埠,這也就意味著我在sendto之前必須要捆綁連接埠,因此我在發送資料之前就得調用bind函數綁定一下連接埠了。但是我就在想核心既然有隨機分配連接埠的能力,而我需要的也只是讓它綁定一下而不用綁定在固定連接埠的業務,socket中應該能夠提供這種業務。然後果然我發現bind就具備這種能力,當bind的參數中連接埠地址為0的時候,這時候就是由核心分配連接埠。這樣我就不用考慮連接埠地址重複的問題,而放心的把這個問題交給核心處理了。
就在發現bind的這個機制的同時,我發現其實bind對於源地址也同樣具備這種處理方式,當系統具有多IP(多網卡)的情況,當我們把bind函數中的ip參數置0時,就是由核心自己選擇分配IP。而之前一直覺得很神奇的INADDR_ANY其實一點也不神奇,它的值其實就是0。所以當我們只有單一IP的時候,我們就可以用INADDR_ANY去代替那個單一的IP,因為核心分配的時候只能選擇這一個IP。從而造成了INADDR_ANY就是本機IP的現象。