2011-09-08 29 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