2011-09-08 78 views
2

我對使用socket()時Linux上的協議定義之間的區別有點困惑。我試圖通過TCP使用socket(PF_INET, SOCK_STREAM, proto)來監聽連接,其中proto(在我的腦海中)有爭議,或者至少看起來很奇怪。奇怪的Linux套接字協議行爲

<netinet/in.h>

... 
IPPROTO_IP = 0, /* Dummy protocol for TCP. */ 
... 
IPPROTO_TCP = 6,  /* Transmission Control Protocol. */ 
... 

商定由/etc/protocols

ip  0  IP    # internet protocol, pseudo protocol number 
hopopt 0  HOPOPT   # hop-by-hop options for ipv6 
... 
tcp  6  TCP    # transmission control protocol 
... 

我從網上教程中學和從該名男子頁tcp(7)您初始化使用

TCP套接字
tcp_socket = socket(AF_INET, SOCK_STREAM, 0); 

其中工作絕對好,當然一個TCP套接字。有關使用上述論點初始化一個插座的一件事是代碼

struct timeval timeout = {1, 0}; 
setsockopt(tcp_socket, 0, SO_RCVTIMEO, &timeout, sizeof(timeout); // 1s timeout 
// Exactly the same for SO_SNDTIMEO here 

作品精美絕倫,但替換所有協議參數(包括socket())與IPPROTO_TCP後,相對於IPPROTO_IP他們有, 如上。

因此與差異實驗後,我需要問幾個搜索的問題:

  1. 爲什麼,當我IPPROTO_TCP取代所有協議參數,做我得到錯誤92(「協議不可用」 )當設置超時時,協議0顯然只是一個'虛擬'的TCP?
  2. 爲什麼socket()需要當該信息從協議中隱含地知道(總是?)時它應該是流,數據報還是原始套接字的信息,反之亦然? (即,TCP是流協議,UDP是數據報協議,...)
  3. 「虛擬TCP」是什麼意思?
  4. 什麼是hopopt,爲什麼它與'ip'具有相同的協議號?

非常感謝。

回答

3

將0作爲協議傳送到socket僅表示您希望使用系列/ socktype對的默認協議。在這種情況下,就是TCP,因此得到與IPPROTO_TCP相同的結果。

您的錯誤發生在setsockopt調用中。正確的將是

setsockopt(tcp_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); // 1s timeout 

0沒有協議,但爲選項級別。 IPPROTO_TCP是另一個選項級別,但不能將其與SO_RCVTIMEO結合使用。它只能與SOL_SOCKET一起使用。 與IPPROTO_TCP一起使用的是tcp(7)中列出的例如。 TCP_NODELAY

+0

啊,謝謝!我認爲我在'tcp(7)'中被這句話推翻了:「例如,爲了表明一個選項要被TCP協議解釋,_level_應該被設置爲TCP的協議號」,但是設置超時是在套接字API級別執行? – Doddy

+1

超時位於套接字API級別。 tcp(7)聯機幫助頁描述了TCP級別的可用選項。 – nos

+0

中間還有一個級別,即IPPROTO_IP。這些被列在ip(7)中。 –

2

socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);應該可以正常工作。

傳遞0作爲協議只是意味着,給我默認。在處理IP時,每個系統上的TCP是用於流套接字的,UDP是用於數據報套接字的。但是socket()可以用於很多其他的東西,它會給你一個TCP或UDP套接字。

socket()本質上是相當一般的。 socket(AF_INET, SOCK_STREAM, 0);只讀爲; 「給我一個IP協議族內的流媒體套接字」。傳遞0意味着你對哪個協議沒有偏好 - 儘管TCP對於任何系統來說都是明顯的選擇。但理論上,它可能會給你例如一個SCTP插座。

無論你想要的數據報還是流式套接字都是而不是對於協議是隱含的。有許多協議基於IP的協議,許多協議可以用於數據報或流模式,如SS7網絡中使用的SCCP。

對於基於IP的協議,SCTP可用於基於數據報或流媒體的方式。因此socket(AF_INET,IPPROTO_SCTP);會含糊不清。對於數據報套接字,還有其他選擇,UDP,DCCP,UDPlite。

socket(AF_INET,SOCK_SEQPACKET,0);是另一個有趣的選擇。它不能返回一個TCP套接字,TCP不是基於數據包的。它不能返回和UDP套接字,UDP不保證順序傳遞。但是如果系統支持它,SCTP套接字就可以做到。

我對爲什麼有人在linux的netinet/in.h中

hopopt提出的意見「虛擬TCP」沒有解釋是IPv6逐跳選項。在IPv6中,協議鑑別符字段也用作擴展機制。在IPv4數據包中有一個協議字段,它是協議鑑別器,如果IPv4數據報攜帶TCP,它將被設置爲IPPROTO_TCP。如果該IPv4數據包還帶有一些附加信息(選項),則它們由其他機制編碼。

IPv6的做法與此不同,如果存在擴展名(可選),則該擴展名將在協議字段中編碼。因此,如果IPv6數據包需要逐跳選項,IPPROTO_HOPOPTS將放置在協議字段中。實際的逐跳選項還有一個協議鑑別器,它表示下一個協議是什麼 - 可能是IPPROTO_TCP,或者是另一個選項。

+0

謝謝,非常豐富!插座很笨拙。 – Doddy

+0

'AF_INET'是POSIX中指定的常量; 'PF_INET'在那裏不存在。所以前者會一直工作,因爲Linux不會像'socket()'調用那樣基本上降低POSIX兼容性。 – caf

+0

是的。刪除了所有的東西。 – nos