Server端bind本機IP地址使用INADDR_ANY

如果bind綁定的是INADDR_ANY,即表示所有發送到服務器的這個端口,不管是哪個網卡/哪個IP地址接收到的數據,都由這個服務端進程進行處理。

一般情況下,如果你要建立網絡服務器應用程序,則你要通知服務器操作系統:請在某地址 xxx.xxx.xxx.xxx上的某端口 yyyy上進行偵聽,並且把偵聽到的數據包發送給我。這個過程,你是通過bind()系統調用完成的。——也就是說,你的程序要綁定服務器的某地址,或者說:把服務器的某地址上的某端口占爲已用。服務器操作系統可以給你這個指定的地址,也可以不給你。

如果你的服務器有多個網卡(每個網卡上有不同的IP地址),而你的服務(不管是在udp端口上偵聽,還是在tcp端口上偵聽),出於某種原因:可能是你的服務器操作系統可能隨時增減IP地址,也有可能是爲了省去確定服務器上有什麼網絡端口(網卡)的麻煩 —— 可以要在調用bind()的時候,告訴操作系統:“我需要在 yyyy 端口上偵聽,所有發送到服務器的這個端口,不管是哪個網卡/哪個IP地址接收到的數據,都是我處理的。”這時候,服務器程序則在0.0.0.0這個地址上進行偵聽。例如:

Proto Recv-Q Send-Q Local Address Foreign Address (state)

……

udp4 0 0 *.7913 *.*

udp4 0 0 *.7911 *.*

tcp4 0 0 *.ftp *.* LISTEN

……

……

以上這些是網絡偵聽的情況,其中Local Address 爲 “*.ftp”、“*.7911”等,代表了服務程序綁定了服務器的所有網卡。

好了,你明白了偵聽INADDR_ANY是什麼意思了,那麼,我的服務器有N個IP地址,會不會收到重複的數據包?收到數據包後,是不是會重複回覆客戶端呢?

答案是:不會收到重複的數據包,也不會重複發送數據。

爲什麼呢?因爲路由的關係,從客戶端來的IP包只可能到達其中一個網卡。同時在服務器進程發送數據時,操作系統根據自身維護着的路由表,決定IP數據包應該從哪一個outbound的gateway向目標端發送。根據gateway選擇的不同,也就決定了從哪一個網卡/哪個IP地址發送。

爲什麼不會接收到重複的數據包呢?

答:因爲客戶端只向你的服務器上的唯一一個IP地址發送數據了。

爲什麼不會重複發送數據包呢?

答:因爲發送數據包的路由(路徑)是唯一的。如果服務器不知道在發送數據的時候應該向哪個地址發送數據,那麼數據就會被髮送到“默認網關”上。

如何選擇發送數據的路徑呢?

答:依照路由表的要求發送。

如果路由表的記錄有重複/有衝突呢,這時候如何選擇路徑呢?

答:路由表記錄有優先級別。一般來說,Windows操作系統的路由表記錄,如果是重複的話,以後來加入的記錄爲準,而某些操作系統,象linux/FreeBSD是不允許加入重複的路由表記錄的;

如果是專用的路由器,有路由選擇算法,一般來說,到達網絡上的某一點的路徑是可以有很多條的。路由選擇算法可以確定“最好的一條路徑”,這條路徑要麼是延時最小的,要麼是通訊費用最低的,要麼是帶寬最高的,要麼是跳點最小的——究竟是如何選擇,就看路由器的管理員如何配置了。

對於客戶端如果綁定INADDR_ANY,情況類似。對於TCP而言,在connect()系統調用時將其綁定到一具體的IP地址。選擇的依據是該地址所在子網到目標地址是可達的(reachable). 這時通過getsockname()系統調用就能得知具體使用哪一個地址。對於UDP而言, 情況比較特殊。即使使用connect()系統調用也不會綁定到一具體地址。這是因爲對UDP使用connect()並不會真正向目標地址發送任何建立連接的數據,也不會驗證到目標地址的可達性。它只是將目標地址的信息記錄在內部的socket數據結構之中,供以後使用。只有當調用sendto()/send()時,由系統內核根據路由表決定由哪一個地址(網卡)發送UDP packet.

P.S.

-----------------------------------------------------------------------------

在IP層中有一個路由表:

在MSDOS窗口可以運行命令:netstat -r

來顯示路由表。根據路由表的條目從指定的網卡發送數據。

ARP緩存用:arp -a

來顯示。

通常以太網幀的目的MAC地址,是下一跳的MAC地址。

 

 

在Server端bind本機IP地址和端口的時候,有些程序會使用INADDR_ANY這個地址來取代本機地址。這是爲什麼呢? 
加了printf把INADDR_ANY打印出來看了一下,居然是零。 
查了一些資料和代碼,MAC中INADDR_ANY是定義在in.h中的:

#define INADDR_ANY      (u_int32_t)0x00000000

所以打印出來是零。

那麼,這個宏定義到底是什麼含義呢? 
這個宏能夠讓程序員在不知道本機IP地址的情況下,使用它來代表本機所有接口的IP地址。也就是說,使用這個宏作爲監聽地址的話,不管本機有多少個接口,socket都會監聽。 
舉個例子,假設一個主機有inter1,inter2,,inter3三個接口,如果一個socket綁定了INADDR_ANY的地址和8000的端口,那麼,從客戶端過來的一個UDP包到達該主機,不管客戶端connect的是inter1,inter2,inter3中的哪個地址,都會被該socket接收到。如果此時主機還要再建立一個新的socket,使用inter1接口和端口8000,將會失敗,因爲這個端口和地址已經被第一個socket監聽了。

上面是接收的情況,那麼,發送數據報給客戶端的時候呢,到底是用哪個接口發送呢? 
這個就是根據本機路由表的配置情況,選擇最合適的路徑對應的接口來發送。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章