我刚看到一个奇怪的东西,是关于应用程序的,它默认使用SOCK_STREAM
函数。为什么要这样做?这个SOCK_STREAM
是创建多个流吗?还是标准的SOCK_STREAM
函数可用于创建TCP流?
我原以为tsunami是基于UDP的,但仍具有与TCP类似的一些特性,如TCP公平性,友好性等。
请问有人能解答一下这个问题吗?我对此完全感到困惑。
TCP几乎总是使用SOCK_STREAM
, UDP使用SOCK_DGRAM
。
TCP(SOCK_STREAM
)是一种基于连接的协议。建立连接后,双方进行对话直到由其中一方或网络错误终止连接。
UDP(SOCK_DGRAM
)是一种基于数据报的协议。您发送一个数据包并获得一个回复,然后连接将终止。
如果您发送多个数据包,TCP承诺按顺序传递它们。 如果顺序很重要,则UDP不会,因此接收方需要检查它们。
如果TCP数据包丢失,发送方可以告诉。 UDP则不能。
UDP数据报的大小有限制,从内存中我认为是512字节。 TCP可以发送比那更大的数据块。
TCP更加稳健,进行更多的检查。 UDP较轻量级(计算机和网络压力较小)。
选择与您与另一台计算机交互的方式相适应的协议。
Berkley 套接字 API 的一个思想是它可以使用不同的协议族,而不仅仅是互联网协议(IP)。因此你只需要一个API就可以处理各种不同的"地址族",例如:
AF_INET
AF_IPX
AF_APPLETALK
AF_NETBIOS
AF_INET6
AF_IRDA
AF_BTH
每个协议族通常都有一些类似的概念来处理套接字上的数据:
SOCK_STREAM
(IP人士称之为TCP)SOCK_DGRAM
(IP人士称之为UDP)不同的地址族对这些基本概念有不同的术语:
╔═══════════╦══════════════════════════╗
║ ║ Socket Type ║
║ Address ╟────────────┬─────────────╢
║ Family ║ SOCK_DGRAM │ SOCK_STREAM ║
╠═══════════╬════════════╪═════════════╣
║ IPX/SPX ║ SPX │ IPX ║
║ NetBIOS ║ NetBIOS │ n/a ║
║ IPv4 ║ UDP │ TCP ║
║ AppleTalk ║ DDP │ ADSP ║
║ IPv6 ║ UDP │ TCP ║
║ IrDA ║ IrLMP │ IrTTP ║
║ Bluetooth ║ ? │ RFCOMM ║
╚═══════════╩════════════╧═════════════╝
重点是:
同样地,如果我正在创建一个红外(IrDA,AF_IRDA
)套接字:
因此,您会说:
socket(AF_IRDA, SOCK_STREAM, 0);
并且Sockets会替我解决这个问题。
最初只有两个协议选项:
SOCK_DGRAM
)SOCK_STREAM
)后来添加了其他协议选择:
SOCK_RDM
-“可靠数据报多播”-已过时;不要在新程序中使用)SOCK_SEQPACKET
)╔═══════════╦══════════════════════════════════════════════════════╗
║ ║ Socket Type ║
║ Address ╟────────────┬─────────────┬──────────┬────────────────╢
║ Family ║ SOCK_DGRAM │ SOCK_STREAM │ SOCK_RDM │ SOCK_SEQPACKET ║
╠═══════════╬════════════╪═════════════╪══════════╪════════════════╣
║ IPX/SPX ║ SPX │ IPX │ ? │ ? ║
║ NetBIOS ║ NetBIOS │ n/a │ ? │ ? ║
║ IPv4 ║ UDP │ TCP │ ? │ SCTP ║
║ AppleTalk ║ DDP │ ADSP │ ? │ ? ║
║ IPv6 ║ UDP │ TCP │ ? │ SCTP ║
║ IrDA ║ IrLMP │ IrTTP │ ? │ ? ║
║ Bluetooth ║ ? │ RFCOMM │ ? │ ? ║
╚═══════════╩════════════╧═════════════╧══════════╧════════════════╝
并不保证每个地址族都支持这些协议选择,但有一些地址族支持。
希望现在您能够理解为什么在创建套接字时传递IPPROTO_TCP
协议是多余的:
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // passing IPPROTO_TCP is redundant
socket(AF_INET, SOCK_STREAM, 0); // better
你已经说过你想要一个SOCK_STREAM
。你不需要在它之上强制使用TCP
。同样地,下面的调用是冗余的:
socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); //passing IPPROTO_UDP is redundant
socket(AF_INET, SOCK_DGRAM, 0); // better
< p >< em >< strong >总结: 它是一种独立于协议的请求TCP或UDP的方式。但由于地球上没有人再使用AppleTalk、IPX/SPX、IrDA、蓝牙、NetBIOS,因此它基本上是遗留物。
更新:我的回答似乎不再相关,但原问题是关于UDT的,它是建立在UDP之上的面向连接的协议。更多信息请参见: http://en.wikipedia.org/wiki/UDP-based_Data_Transfer_Protocol
UDT似乎提供了与经典的BSD sockets API 相似的API,因此它可以用作流和数据报应用程序的替代品。例如,请检查sendmsg
和recvmsg
- 如果在使用使用SOCK_STREAM
创建的套接字上使用这两个函数,它们都会抛出异常;所有面向流的API也会对使用SOCK_DGRAM
创建的套接字抛出异常。
在SOCK_DGRAM
情况下,它会执行一些额外的处理,但在这种情况下并不会简单地透明地将UDP套接字包装起来 - 就我快速评估代码所了解的而言(我不熟悉UDT内部或协议规范),阅读技术论文可能会有所帮助。
该库始终将其底层的“真实”套接字创建为数据报(请检查 channel.cpp,CChannel::open
)。
以下是从我为自己编写的常用套接字演示文件中获取UDP与TCP数据包的一些有用宏。
来自这些文件:
在这里你可以找到这些宏:
// See: https://linux.die.net/man/7/ip
// AF = "Address Family"
// INET = "Internet"
// AF_INET = IPv4 internet protocols
// AF_INET6 = IPv6 internet protocols; see: https://linux.die.net/man/2/socket
// DGRAM = "Datagram" (UDP)
//
// IPv4
#define SOCKET_TYPE_TCP_IPV4 AF_INET, SOCK_STREAM, 0
#define SOCKET_TYPE_UDP_IPV4 AF_INET, SOCK_DGRAM, 0
#define SOCKET_TYPE_RAW_IPV4(protocol) AF_INET, SOCK_RAW, (protocol)
// IPv6
#define SOCKET_TYPE_TCP_IPV6 AF_INET6, SOCK_STREAM, 0
#define SOCKET_TYPE_UDP_IPV6 AF_INET6, SOCK_DGRAM, 0
#define SOCKET_TYPE_RAW_IPV6(protocol) AF_INET6, SOCK_RAW, (protocol)
使用示例:
int socket_tcp = socket(SOCKET_TYPE_TCP_IPV4);
int socket_udp = socket(SOCKET_TYPE_UDP_IPV4);
// See also: https://www.binarytides.com/raw-sockets-c-code-linux/
int socket_raw = socket(SOCKET_TYPE_RAW_IPV4(IPPROTO_RAW));
请参考 int socket(AddressFamily, Type, Protocol)
套接字创建函数的文档 这里 和 这里 (也可以通过运行 man 2 socket
查看)。它允许您指定这三个参数:
然而,对于许多(如果不是大多数)用例,这些参数的最有用选项通常是:
地址族: AF_INET
(用于IPv4地址) 或 AF_INET6
(用于IPv6地址)。
套接字类型: SOCK_DGRAM
或 SOCK_STREAM
。
协议: 只需使用0
,以允许其使用默认协议,如上面的文档链接中所指定的(强调添加):
协议: 指定要与套接字一起使用的特定协议。 指定
0
的协议参数会导致套接字子例程默认为请求返回套接字类型的典型协议。
SOCK_DGRAM
: 如果您使用AF_INET
创建套接字,则为
int s = socket(AF_INET, SOCK_DGRAM, 0)
或者使用AF_INET6
作为
int s = socket(AF_INET6, SOCK_DGRAM, 0)
...当选择(AF_INET
或AF_INET6
)地址族和SOCK_DGRAM
套接字类型时,默认情况下套接字使用UDP协议。
AF_UNIX
)中: 当通过AF_UNIX
地址族在同一操作系统上运行的进程之间进行通信时,这类似于进程间消息队列。AF_INET
和AF_INET6
)中: 当通过AF_INET
地址族在本地进程和远程主机上运行的进程之间进行通信时,这是“在用户数据报协议/Internet协议(UDP/IP)协议上实现的”。SOCK_STREAM
: 如果您使用AF_INET
创建套接字,则为
int s = socket(AF_INET, SOCK_STREAM, 0)
或者使用AF_INET6
作为
int s = socket(AF_INET6, SOCK_STREAM, 0)
...当选择(AF_INET
或AF_INET6
)地址族和SOCK_STREAM
套接字类型时,默认情况下套接字使用TCP协议。
AF_UNIX
)中: 当通过AF_UNIX
地址族在同一操作系统上运行的进程之间进行通信时,这种类型的套接字“类似于管道”IPC(进程间通信)机制。AF_INET
和AF_INET6
)中: 当通过AF_INET
地址族在本地进程和远程主机上运行的进程之间进行通信时,这是“在传输控制协议/Internet协议(TCP/IP)协议上实现的”。在下面的解释中,无论我(或他们,在引用部分中)使用AF_INET
(用于IPv4地址),请记住如果您喜欢,也可以使用AF_INET6
(用于IPv6地址)。
在基于套接字的通信中,包括在同一台计算机上的两个运行进程之间或在两台不同计算机之间来回发送UDP/IP和TCP/IP以太网数据包,必须同时指定地址族(这些常量以AF_
开头)和套接字类型(这些常量以SOCK_
开头)。
我发现关于套接字的最好文档来自IBM.com,例如这里:
int socket(AddressFamily, Type, Protocol)
: https://www.ibm.com/docs/en/aix/7.1?topic=s-socket-subroutine想要了解更多关于“套接字”的信息,请点击上述链接之一后在左侧导航面板中查看。
在linux.die.net网站上也可以找到其他优秀的文档,例如这里的ip(7)
页面。
AF_
)域从上面的“地址族”链接中,我们首先了解各种套接字地址族(AF)域,这是理解套接字类型的先决条件。以下是该信息(强调部分已添加,并在方括号中添加了我的注释):
一个套接字子程序,它以地址族(AF)作为参数,可以使用AF_UNIX
(UNIX)、AF_INET
(Internet)、AF_NS
(Xerox Network Systems)或 AF_NDD
(操作系统的网络设备驱动程序)协议。这些地址族是以下通信域的一部分:
UNIX:当指定 AF_UNIX
的地址族时,在运行在同一操作系统上的进程之间提供套接字通信。UNIX 域中的套接字名称是由 ASCII 字符串组成的,其最大长度取决于所使用的机器。
Internet:当指定 AF_INET
的地址族时,在本地进程和远程主机上运行的进程之间提供套接字通信。Internet 域要求在您的系统上安装传输控制协议/Internet 协议 (TCP/IP)。Internet 域中的套接字名称是由一个 32 位 IP 地址 [例如:192.168.0.1
] 和一个 16 位端口地址 [从 0
到 65535
中的任何数字;这里是常见 TCP 和 UDP 端口号列表] 组成的 Internet 地址。
NDD:当指定 AF_NDD
的地址族时,在本地进程和远程主机上运行的进程之间提供套接字通信。NDD 域使应用程序能够直接在物理网络顶部运行。这与 Internet 域不同,Internet 域中的应用程序运行在诸如TCP或用户数据报协议 (UDP)等传输协议之上。NDD 域中的套接字名称由操作系统 NDD 名称和第二部分(协议相关)组成。
通信域 [例如:AF_UNIX
或 AF_INET
] 由可加载的域数据结构描述。域内的通信协议 [例如:SOCK_DGRAM
(UDP) 或 SOCK_STREAM
(TCP)] 由为每个配置的协议实现定义的结构描述。当请求创建套接字时,系统使用通信域的名称线性搜索已配置的域列表。如果找到该域,则会查询该域支持的协议表,以查找适合正在创建的套接字类型或特定协议请求的协议。(原始域可能存在通配符条目。)如果多个协议条目满足请求,则选择第一个。
SOCK_
)从上面的“套接字类型”链接中,我们了解到各种“底层通信协议”(强调添加,我的注释用方括号 [] 表示):
Sockets are classified according to communication properties. Processes usually communicate between sockets of the same type. However, if the underlying communication protocols support the communication, sockets of different types can communicate.
Each socket has an associated type, which describes the semantics of communications using that socket. The socket type determines the socket communication properties such as reliability, ordering, and prevention of duplication of messages. The basic set of socket types is defined in the
sys/socket.h
file:/*Standard socket types */ #define SOCK_STREAM 1 /*virtual circuit*/ #define SOCK_DGRAM 2 /*datagram*/ #define SOCK_RAW 3 /*raw socket*/ #define SOCK_RDM 4 /*reliably-delivered message*/ #define SOCK_CONN_DGRAM 5 /*connection datagram*/
Other socket types can be defined.
The operating system supports the following basic set of sockets:
SOCK_DGRAM
: Provides datagrams, which are connectionless messages of a fixed maximum length. This type of socket is generally used for short messages, such as a name server or time server, because the order and reliability of message delivery is not guaranteed.In the UNIX domain [
AF_UNIX
], theSOCK_DGRAM
socket type is similar to a message queue. In the Internet domain [AF_INET
], theSOCK_DGRAM
socket type is implemented on the User Datagram Protocol/Internet Protocol (UDP/IP) protocol.A datagram socket supports the bidirectional flow of data, which is not sequenced, reliable, or unduplicated. A process receiving messages on a datagram socket may find messages duplicated or in an order different than the order sent. Record boundaries in data, however, are preserved. Datagram sockets closely model the facilities found in many contemporary packet-switched networks.
SOCK_STREAM
: Provides sequenced, two-way byte streams with a transmission mechanism for stream data. This socket type transmits data on a reliable basis, in order, and with out-of-band capabilities.In the UNIX domain [
AF_UNIX
], theSOCK_STREAM
socket type works like a pipe. In the Internet domain [AF_INET
], theSOCK_STREAM
socket type is implemented on the Transmission Control Protocol/Internet Protocol (TCP/IP) protocol.A stream socket provides for the bidirectional, reliable, sequenced, and unduplicated flow of data without record boundaries. Aside from the bidirectionality of data flow, a pair of connected stream sockets provides an interface nearly identical to pipes.
SOCK_RAW
: Provides access to internal network protocols and interfaces. This type of socket is available only to users with root-user authority, or to non-root users who have theCAP_NUMA_ATTACH
capability. (For non-root raw socket access, thechuser
command assigns theCAP_NUMA_ATTACH
capability, along withCAP_PROPAGATE
. For further information, refer to thechuser
command.)Raw sockets allow an application to have direct access to lower-level communication protocols. Raw sockets are intended for advanced users who want to take advantage of some protocol feature that is not directly accessible through a normal interface, or who want to build new protocols on top of existing low-level protocols.
Raw sockets are normally datagram-oriented, though their exact characteristics are dependent on the interface provided by the protocol.
SOCK_SEQPACKET
: Provides sequenced, reliable, and unduplicated flow of information.
SOCK_CONN_DGRAM
: Provides connection-oriented datagram service. This type of socket supports the bidirectional flow of data, which is sequenced and unduplicated, but is not reliable. Because this is a connection-oriented service, the socket must be connected prior to data transfer. Currently, only the Asynchronous Transfer Mode (ATM) protocol in the Network Device Driver (NDD) domain supports this socket type.
它们是如何工作的?
SOCK_DGRAM
和SOCK_RAW
套接字类型允许应用程序通过send
子例程向命名的通信者发送数据报。应用程序可以使用recv
子例程通过套接字接收数据报。当使用SOCK_RAW
套接字类型与低级协议或硬件接口进行通信时,协议参数非常重要。应用程序必须指定通信所在的地址族。
这是使用SOCK_STREAM
(TCP协议)套接字类型进行通信所需的一般函数调用序列:
在使用
SOCK_STREAM
套接字类型是全双工字节流。在可以发送或接收任何数据之前,必须连接流套接字。当使用流套接字进行数据传输时,应用程序需要执行以下序列:应用程序可以使用
send
和recv
子例程来管理带外数据。
SOCK_STREAM
时,errno
可能返回或设置的错误:SOCK_STREAM
旨在防止数据的丢失或重复。如果一个数据块无法在合理的时间内成功传输,而对等协议有缓冲空间,则连接会断开。当发生这种情况时,socket 子程序返回值为 -1
并设置全局变量 errno
为 ETIMEDOUT
,表示出现错误。如果进程在已断开的流上发送数据,则会引发 SIGPIPE
信号。不能处理该信号的进程将终止。当套接字上收到带外数据时,将向进程组发送 SIGURG
信号。
与套接字关联的进程组可以通过 SIOCGPGRP
或 SIOCSPGRP
ioctl
操作进行读取或设置。要在任何数据上接收信号,请同时使用 SIOCSPGRP
和 FIOASYNC
ioctl
操作。这些操作在 sys/ioctl.h
文件中定义。
大概就是这样了。我希望很快在我的eRCaGuy_hello_world存储库的c
dir中编写一些基本演示。
int socket(AddressFamily, Type, Protocol)
函数:https://www.ibm.com/docs/en/aix/7.1?topic=s-socket-subroutineSOCK_DGRAM
是 UDP... SOCK_STREAM
是 TCP" 这种说法在两方面都是不正确的。SOCK_DGRAM
是一个基于数据报的套接字,而使用的传输协议并不影响它的性质。UDP 是其中一种使用数据报的传输协议,但并不是唯一的一种。SOCK_STREAM
是一个基于流的套接字,同样也不受使用的传输协议影响。TCP 是其中一种使用流的传输协议,但同样也不是唯一的一种。socket()
函数的 protocol
参数决定了使用 UDP/TCP,而不是 type
参数。但是,只能在 SOCK_DGRAM
类型的套接字上使用 UDP,只能在 SOCK_STREAM
类型的套接字上使用 TCP。 - Remy LebeauAF_INET6
。地址族决定了对等方如何寻址(IPv4 vs IPv6 vs ...),与套接字类型(数据报 vs 流 vs ...)或使用的协议(UDP vs TCP vs ...)无关。只有当套接字协议明确为IPPROTO_UDP
时才使用UDP。只有当套接字协议明确为IPPROTO_TCP
时才使用TCP。其他传输也是可能的(ICMP、IPX、RAW等)。 - Remy Lebeausocket()
可以做出这种区分。您的解释并没有很好地解决这个问题。而且,您仍然完全忽略了AF_INET6
用于IPv6寻址。AF_INET
用于IPv4寻址。 - Remy LebeauSOCK_STREAM
使用的默认协议”是无意义的。 SOCK_STREAM
只是API的选择器常量。该API没有“默认为SOCK_STREAM
或SOCK_DGRAM
”的概念。也许在AF_X
+ SOCK_Y
的情况下,您可以用默认值来表达,但这忽略了问题的要点。 - Błażej MichalikAF
/SOCK
矩阵进行选择。说“它是SOCK_XYZ
的默认值”会使一个错误的想法持续存在,即在缺少AF_*
时存在SOCK_*
的默认值。 - Błażej MichalikTCP 使用 SOCK_STREAM,而 UDP 使用 SOCK_DGRAM。
socket()
系统调用。 - user207421IPPROTO_UDP
?以及IPPROTO_IP
与IPPROTO_TCP
/IPPROTO_UDP
的区别。 - Gabriel Staples