[英]“UDP datagrams only” socket in C
In Linux, Ubuntu 14.04: I'm writing a code that implements socket to send pure UDP datagrams which includes UDP header+payload, without any part of IP header. 在Linux中,Ubuntu 14.04:我正在编写一个代码,该代码实现套接字以发送纯UDP数据报,其中包括UDP标头+有效载荷,而没有IP标头的任何部分。
I have created the socket 我已经创建了套接字
sokt_fd=socket(AF_INET, SOCK_RAW, IPPROTO_UDP)
Also, I have prepared the UDP header. 另外,我还准备了UDP标头。
I want to leave the IP encapsulation process to the kernel. 我想将IP封装过程留给内核。
I want to send the datagram over any available IP interface. 我想通过任何可用的IP接口发送数据报。 (I do not want to specify the source IP, and also leave this task to the kernel).
(我不想指定源IP,也不要将此任务留给内核)。
Do I need to specify the destination IP address before sending the datagram. 发送数据报之前是否需要指定目标IP地址。
I must use "sendto()" command to send the datagram; 我必须使用“ sendto()”命令发送数据报; how I must fill the "sockaddr" data structure?
我必须如何填充“ sockaddr”数据结构?
#include <netinet/in.h> struct sockaddr { unsigned short sa_family;// address family, AF_xxx char sa_data[14];// 14 bytes of protocol address };
Don't use the sockaddr
structure. 不要使用
sockaddr
结构。 Use sockaddr_in
instead and cast it when you have to pass a sockaddr*
to a function. 改用
sockaddr_in
并在必须将sockaddr*
传递给函数时进行sockaddr*
转换。
struct sockaddr_in myaddr;
int s;
myaddr.sin_family = AF_INET;
myaddr.sin_port = htons(3490);
inet_aton("63.161.169.137", &myaddr.sin_addr.s_addr);
s = socket(PF_INET, SOCK_STREAM, 0);
bind(s, (struct sockaddr*)myaddr, sizeof(myaddr));
The socket API is designed for different addressing families, others are Infrared and Bluetooth. 套接字API专为不同的寻址系列而设计,其他是红外和蓝牙。 Since AF_INET is only one of the families the API functions use the general
sockaddr
type in the parameters. 由于AF_INET只是其中一个系列,因此API函数在参数中使用常规的
sockaddr
类型。
There is a nice explanation of this in Chapter 3, "Sockets Introduction" in the well-known book Unix Network Programming, The Sockets Networking API (Volume 1) by Richard Stevens et. 理查德·史蒂文斯(Richard Stevens)等人着名的《 Unix网络编程》,《套接字网络API》 (第1卷),在第3章“套接字介绍”中对此进行了很好的解释。 al.
人。 Let me quote:
让我引用:
Most socket functions require a pointer to a socket address structure as an argument.
大多数套接字函数都需要一个指向套接字地址结构的指针作为参数。 Each supported protocol suite defines its own socket address structure.
每个受支持的协议套件都定义了自己的套接字地址结构。 The names of these structures begin with sockaddr_ and end with a unique suffix for each protocol suite.
这些结构的名称以sockaddr_开头,并以每个协议套件的唯一后缀结尾。
For the IP (Internet protocol) suite, the structure is sockaddr_in
so it follows that since your example is specifying the AF_INET address family when you created the socket that you would use the more specific sockaddr_in structure instead of the more generic sockaddr. 对于IP(Internet协议)套件,结构为
sockaddr_in
因此可以理解,由于您的示例在创建套接字时指定了AF_INET地址族,因此您将使用更具体的sockaddr_in结构而不是更通用的sockaddr。 The socket API, as a matter of efficiency uses the more generic sockaddr pointer in the signature prototype. 为了提高效率,套接字API在签名原型中使用了更通用的sockaddr指针。
With regard to using send()
versus sendto()
, I have found that sendto()
is used more commonly with UDP and send()
with TCP sockets. 关于使用
send()
与sendto()
,我发现sendto()
与UDP一起使用,而send()
与TCP套接字一起使用。 Therefore, to answer your question in #3 above, with UDP you don't have to specify the destination address up front, but instead it is supplied as an argument to sendto()
. 因此,要回答上面#3中的问题,使用UDP不必预先指定目标地址,而是将其作为
sendto()
的参数提供。
For a given udp_datagram
and datagram_length
, your code might look something like this: 对于给定的
udp_datagram
和datagram_length
,您的代码可能看起来像这样:
uint32_t address = inet_addr("1.2.3.4"); // can also provide hostname here
uint16_t port = 27890;
sockaddr_in_t dest_addr;
memset(*dest_addr, 0, sizeof(dest_addr));
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(port);
dest_addr.sin_addr.s_addr = htonl(address);
sendto(socket_fd,
(const char*)upd_datagram,
datagram_length,
0,
reinterpret_cast<sockaddr_t*>(&dest_addr),
sizeof(dest_addr));
The address API really wanted to be object-oriented, but had to deal with the fact that C isn't an OO language. 地址API确实希望是面向对象的,但必须处理C不是OO语言这一事实。
sockaddr
can be seen as the "base class" and the parameter type that bind, connect, sendto, recvfrom, etc. use when they need an address. sockaddr
可以看作是“基类”和绑定,连接,sendto,recvfrom等在需要地址时使用的参数类型。 However, you must provide a "subclassed" address matching the socket domain that you're using. 但是,您必须提供一个与您使用的套接字域匹配的“子类”地址。 This is because Berkeley sockets can be used for a wide and extensible range of protocols.
这是因为Berkeley套接字可用于广泛且可扩展的协议。 IPv4 and IPv6 are the most typical, but UNIX-based installs also support sockets as filesystem objects ("addressed" by path), and, for instance, a hypervisor driver can install support for special inter-VM or guest-to-host sockets.
IPv4和IPv6是最典型的,但是基于UNIX的安装还支持将套接字作为文件系统对象(按路径“寻址”),例如,虚拟机监控程序驱动程序可以安装对特殊的VM间或来宾到主机套接字的支持。 See
man 7 socket
for an overview. 有关概述,请参见
man 7 socket
。
If you use IPv4, you need to use sockaddr_in
. 如果使用IPv4,则需要使用
sockaddr_in
。 If you use IPv6, you need to use sockaddr_in6
. 如果使用IPv6,则需要使用
sockaddr_in6
。 In both cases, you need to cast your pointer to a sockaddr*
. 在这两种情况下,都需要将指针转换为
sockaddr*
。
To fill in a sockaddr_in
, you need to do something like this: 要填写
sockaddr_in
,您需要执行以下操作:
struct sockaddr_in inet_addr;
inet_addr.sin_family = AF_INET;
inet_addr.sin_port = htons(port);
inet_addr.sin_addr.s_addr = htonl(ip_address_as_number);
struct sockaddr* addr = (struct sockaddr*)&inet_addr;
htons
and htonl
stand for "host to network (short)" and "host to network (long)", respectively. htons
和htonl
分别表示“主机到网络(短)”和“主机到网络(长)”。 You need this because there was a time at which network drivers were too dumb to abstract away the machine's endianness and we can't go back in time to fix them. 您之所以需要它,是因为有时网络驱动程序过于笨拙,无法抽象出机器的字节序,因此我们无法及时返回来修复它们。 (The network byte order is big endian.)
(网络字节顺序为大字节序。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.