Socket INADDR_ANY詳解


轉載:http://hi.baidu.com/zorro_knight/item/37af9e8c9dc71253e73d1924

 

linux下的socket INADDR_ANY表示的是一個服務器上所有的網卡(服務器可能不止一個網卡)
多個本地ip地址都進行綁定端口號,進行偵聽。

不光是多個網卡的問題.

見如下server listen:

80           0.0.0.0               //INADDR_ANY,外部的client ask 從哪個server的地址近來都可以連接到80端口.
8088       192.168.1.11     //外部的client ask 從server地址192.168.1.11進來才可以連接到8088端口.
8089       192.168.1.12     //外部的client ask 從server地址192.168.1.12進來才可以連接到8089端口.

也就是說0.0.0.0 是指本地的地址(也就是代表了所有本地的地址,同一個網卡上也可能有多個地址).
這點上linux,windows系統都是相同的.

而對於在connect中指定了INADDR_ANY,那么:
  1. 在語義上一定是連接到本地地址,不可能是外部地址.
  2. INADDR_ANY在語義上有可能是對應了幾個本地地址,因此有的系統會根據缺省規則連接本地指定的服務,而有的系統則因為不能確定用戶的任意本地地址是哪個而不能有效連接(如linux和windows不同).

 

 

INADDR_ANY就是指定地址為0.0.0.0的地址,這個地址事實上表示不確定地址,或“所有地址”、“任意地址”。 一般來說,在各個系統中均定義成為0值。

例如在ubuntu的/usr/include/netinet/in.h定義為:

/* Address to accept any incoming messages.  */
#define    INADDR_ANY        ((in_addr_t) 0x00000000)


一 般情況下,如果你要建立網絡服務器應用程序,則你要通知服務器操作系統:請在某地址 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數據包應該 c從哪一個outbound的gateway向目標端發送。根據gateway選擇的不同,也就決定了從哪一個網卡/哪個IP地址發送。
為什么不會接收到重復的數據包呢?
答:因為客戶端只向你的服務器上的唯一一個IP地址發送數據了。
為什么不會重復發送數據包呢?
答:因為發送數據包的路由(路徑)是唯一的。如果服務器不知道在發送數據的時候應該向哪個地址發送數據,那么數據就會被發送到“默認網關”上。
如何選擇發送數據的路徑呢?
答:依照路由表的要求發送。
如果路由表的記錄有重復/有沖突呢,這時候如何選擇路徑呢?
答:路由表記錄有優先級別。一般來說,Windows操作系統的路由表記錄,如果是重復的話,以后來加入的記錄為准,而某些操作系統,象linux/FreeBSD是不允許加入重復的路由表記錄的;
如果是專用的路由器,有路由選擇算法,一般來說,到達網絡上的某一點的路徑是可以有很多條的。路由選擇算法可以確定“最好的一條路徑”,這條路徑要么是延時最小的,要么是通訊費用最低的,要么是帶寬最高的,要么是跳點最小的——究竟是如何選擇,就看路由器的管理員如何配置了。

 

 

INADDR_ANY 的具體含義是,綁定到0.0.0.0。此時,對所有的地址都將是有效的,如果系統考慮冗余,采用多個網卡的話,那么使用此種bind,將在所有網卡上進行綁定。在這種情況下,你可以收到發送到所有有效地址上數據包。

  例如:

  SOCKADDR_IN Local;

  Local.sin_addr.s_addr = htonl(INADDR_ANY);

  
  另外一種方式如下:

  SOCKADDR_IN Local;

  hostent* thisHost = gethostbyname("");

  char* ip = inet_ntoa(*(struct in_addr *)*thisHost->h_addr_list);

  Local.sin_addr.s_addr = inet_addr(ip);

  在這種方式下,將在系統中當前第一個可用地址上進行綁定。在多網卡的環境下,可能會出問題。

  
  最常見的方式:

  const char LocalIP[] = "192.168.0.100";

  SOCKADDR_IN Local;

  Local.sin_addr.s_addr = inet_addr(LocalIP);

  bind(socket, (LPSOCKADDR)&Local, sizeof(SOCKADDR_IN)


  bind的安全問題:

  如果你在bind時,使用了INADDR_ANY那么,你將可以在所有有效的地址上進行監聽,但是Socket有一個特性:可在同一端口上綁定多個Socket。

  讓我們看看下面的情況:假設你的系統只有一個IP:192.168.0.100,你希望bind到4096端口。對於下面的兩種bind,當數據包到達時,誰會接收到呢?

  Local.sin_addr.s_addr = htonl(INADDR_ANY);

  Local.sin_addr.s_addr = inet_addr(“192.168.0.100”);


   WinSocke庫是這樣處理的:誰綁定的最明確,誰獲取數據包。顯然,第二種bind將獲取到達的數據包。如果避免這種情況呢?使用 SO_EXECLUSINEADDRUSE選項。需要注意的是,此選項在Windows NT 4 Service Pack 4以后(包括SP4)才可以使用。

  示例代碼:

  #ifndef SO_EXECLUSINEADDRUSE

  #define SO_EXECLUSINEADDRUSE ((int)(~SO_REUSEADDR))

  #endif
  

  SOCKADDR_IN Local;

  BOOL val = TRUE;

  
  Local. sin_family = AF_INET;

  Local. sin_port = htons(4096);

  Local.sin_addr.s_addr = htonl(INADDR_ANY);

  
  setsocketopt(socket,

  SOL_SOCKET,

  SO_EXECLUSINEADDRUSE,

  (char*)&val,

  sizeof(val));

  
  bind(socket, (LPSOCKADDR)&Local, sizeof(SOCKADDR_IN)


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

 

SOCKET bind INADDR_LOOPBACK和INADDR_ANY的區別

今天寫程序時候,服務器端啟動了,然后客戶端總是連接不上,connect返回錯誤號是10061,服務器積極拒絕請求。

用telnet連接一下端口,發現服務端服務沒有開啟,但是我程序是啟動的,用netstat -a 命令看服務器是監聽狀態。

把流程走一遍,發現bind用的參數是INADDR_LOOPBACK,改成INADDR_ANY就OK了。

只是有些困惑,因為之前一個程序用的是INADDR_LOOPBACK運行沒有問題,為何這里運行不了?

sa.sin_addr.s_addr        = htonl(INADDR_LOOPBACK); // 1
sa.sin_addr.s_addr        = htonl(INADDR_ANY  ); //2

兩者的區別


INADDR_ANY是ANY,是綁定地址0.0.0.0上的監聽, 能收到任意一塊網卡的連接;
INADDR_LOOPBACK, 也就是綁定地址LOOPBAC, 往往是127.0.0.1, 只能收到127.0.0.1上面的連接請求


 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM