查看源代码 gen_udp (内核 v10.2)
UDP 套接字的接口。
此模块提供通过 UDP 协议套接字进行通信的函数。
注意
创建套接字的函数可以采用可选选项;
{inet_backend, 后端}
,如果指定,则必须是第一个选项。这将选择平台套接字 API 的实现后端。这是一个临时选项,将在未来的版本中被忽略。
默认值为
后端 = inet
,它选择传统的inet_drv.c
驱动程序。另一个选择是后端 = socket
,它选择新的socket
模块及其 NIF 实现。当节点启动时,可以使用应用程序
kernel
的配置变量inet_backend
更改系统默认值。对于
gen_udp
与inet_backend = socket
,我们尝试尽可能“兼容”,但这有时是不可能的。以下是 inet 后端inet
(默认)和socket
的行为不同的情况列表
选项 read_packets 当前被忽略。
Windows 要求套接字(域 =
inet | inet6
)被绑定。当前,在 Windows 上使用
inet_backend = socket
创建的所有套接字都将被绑定。如果用户未提供地址,则 gen_udp 将尝试自行“找出”一个地址。
摘要
函数
关闭 UDP 套接字。
连接 UDP 套接字。
连接 UDP 套接字。
更改套接字的控制进程(所有者)。
等效于 open(Port, [])
。
打开 UDP 套接字。
在被动模式下从套接字接收数据包。
在已连接的 UDP 套接字上发送数据包。
将 UDP 数据包发送到指定的目标。
将带有辅助数据的数据包发送到指定的目标。
类型
-type ip6_membership() :: {MultiAddress :: inet:ip6_address(), IfIndex :: integer()}.
-type ip6_multicast_if() :: integer().
IPv6 此多播接口索引(一个整数)。
-type ip_membership() :: {MultiAddress :: inet:ip4_address(), Interface :: inet:ip4_address()} | {MultiAddress :: inet:ip4_address(), Address :: inet:ip4_address(), IfIndex :: integer()}.
IP 多播成员资格。
并非所有平台都支持 3 元组形式。“ifindex”在支持 3 元组变体的平台上默认为零 (0)。
-type ip_multicast_if() :: inet:ip4_address().
-type membership() :: ip_membership() | ip6_membership().
-type multicast_if() :: ip_multicast_if() | ip6_multicast_if().
-type open_option() :: {ip, inet:socket_address()} | {fd, non_neg_integer()} | {ifaddr, socket:sockaddr_in() | socket:sockaddr_in6() | inet:socket_address()} | inet:address_family() | {port, inet:port_number()} | {netns, file:filename_all()} | {bind_to_device, binary()} | option().
-type option() :: {active, true | false | once | -32768..32767} | {add_membership, membership()} | {broadcast, boolean()} | {buffer, non_neg_integer()} | {debug, boolean()} | {deliver, port | term} | {dontroute, boolean()} | {drop_membership, membership()} | {exclusiveaddruse, boolean()} | {header, non_neg_integer()} | {high_msgq_watermark, pos_integer()} | {low_msgq_watermark, pos_integer()} | {mode, list | binary} | list | binary | {multicast_if, multicast_if()} | {multicast_loop, boolean()} | {multicast_ttl, non_neg_integer()} | {priority, non_neg_integer()} | {raw, Protocol :: non_neg_integer(), OptionNum :: non_neg_integer(), ValueBin :: binary()} | {read_packets, non_neg_integer()} | {recbuf, non_neg_integer()} | {reuseaddr, boolean()} | {reuseport, boolean()} | {reuseport_lb, boolean()} | {sndbuf, non_neg_integer()} | {tos, non_neg_integer()} | {tclass, non_neg_integer()} | {ttl, non_neg_integer()} | {recvtos, boolean()} | {recvtclass, boolean()} | {recvttl, boolean()} | {ipv6_v6only, boolean()}.
-type option_name() :: active | broadcast | buffer | debug | deliver | dontroute | exclusiveaddruse | header | high_msgq_watermark | low_msgq_watermark | mode | multicast_if | multicast_loop | multicast_ttl | priority | {raw, Protocol :: non_neg_integer(), OptionNum :: non_neg_integer(), ValueSpec :: (ValueSize :: non_neg_integer()) | (ValueBin :: binary())} | read_packets | recbuf | reuseaddr | reuseport | reuseport_lb | sndbuf | tos | tclass | ttl | recvtos | recvtclass | recvttl | pktoptions | ipv6_v6only.
-type socket() :: inet:socket().
由 open/1,2
返回的套接字。
函数
-spec close(Socket) -> ok when Socket :: socket().
关闭 UDP 套接字。
-spec connect(Socket, SockAddr) -> ok | {error, Reason} when Socket :: socket(), SockAddr :: socket:sockaddr_in() | socket:sockaddr_in6(), Reason :: inet:posix().
连接 UDP 套接字。
连接 UDP 套接字仅表示存储指定的(目标)套接字地址(如 SockAddr
所指定),以便系统知道将数据发送到哪里。
当套接字“连接”后,发送数据报时无需指定目标地址。即可以使用 send/2
。
这也意味着套接字只会接收来自已连接地址的数据。其他消息会在到达时被 OS 协议栈丢弃。
-spec connect(Socket, Address, Port) -> ok | {error, Reason} when Socket :: socket(), Address :: inet:socket_address() | inet:hostname(), Port :: inet:port_number(), Reason :: inet:posix().
连接 UDP 套接字。
请参阅 connect/2
。
使用此函数,目标使用单独的 Address
和 Port
参数指定,其中 Address
可以是 IP 地址或 主机名。
-spec controlling_process(Socket, Pid) -> ok | {error, Reason} when Socket :: socket(), Pid :: pid(), Reason :: closed | not_owner | badarg | inet:posix().
更改套接字的控制进程(所有者)。
将新的控制进程 Pid
分配给 Socket
。控制进程是套接字将消息发送到的进程。如果此函数由当前控制进程之外的任何其他进程调用,则返回 {error, not_owner}
。
如果由 Pid
标识的进程不是现有本地 pid/0
,则返回 {error, badarg}
。当 Socket
在此函数执行期间关闭时,在某些情况下也可能返回 {error, badarg}
。
如果套接字处于活动模式,此函数会将调用者邮箱中套接字的任何消息传输到新的控制进程。
如果在传输期间任何其他进程与套接字交互,它可能无法正常工作,消息可能会保留在调用者的邮箱中。例如,在传输期间更改套接字的活动模式可能会导致这种情况。
-spec open(Port) -> {ok, Socket} | {error, Reason} when Port :: inet:port_number(), Socket :: socket(), Reason :: system_limit | inet:posix().
等效于 open(Port, [])
。
-spec open(Port, Opts) -> {ok, Socket} | {error, Reason} when Port :: inet:port_number(), Opts :: [inet:inet_backend() | open_option()], Socket :: socket(), Reason :: system_limit | inet:posix().
打开 UDP 套接字。
创建的套接字绑定到 UDP 端口号 Port
。如果 Port == 0
,则底层操作系统会分配一个空闲(临时)UDP 端口;使用 inet:port/1
来检索它。
调用此函数的进程成为 Socket
的控制进程(套接字所有者)。
UDP 套接字选项
list
- 接收到的Packet
将作为列表传递。binary
- 接收到的Packet
将作为二进制传递。{ip, Address}
- 如果本地主机有多个 IP 地址,此选项指定要使用的地址。{ifaddr, Address}
- 与{ip, Address}
相同。但是,如果这反而是一个
socket:sockaddr_in/0
或socket:sockaddr_in6/0
,则它优先于先前使用ip
选项设置的任何值。如果ip
选项在ifaddr
选项之后出现,则可以使用它来更新ifaddr
选项的相应字段(addr
字段)。{fd, integer() >= 0}
- 如果套接字在未使用gen_udp
的情况下以某种方式打开,请使用此选项传递其文件描述符。如果Port
未设置为0
和/或{ip, ip_address()}
与此选项组合,则fd
将在打开后绑定到指定的接口和端口。如果未指定这些选项,则假定fd
已被适当绑定。inet6
- 为 IPv6 设置套接字。inet
- 为 IPv4 设置套接字。local
- 设置 Unix 域套接字。请参阅inet:local_address/0
{udp_module, module()}
- 覆盖使用的回调模块。对于 IPv4,默认为inet_udp
,对于 IPv6,默认为inet6_udp
。{multicast_if, Address}
- 为多播套接字设置本地设备。{multicast_loop, true | false}
- 当true
时,发送的多播数据包会循环回到本地套接字。{multicast_ttl, Integer}
- 选项multicast_ttl
更改传出多播数据报的生存时间 (TTL),以控制多播的范围。TTL 为 1 的数据报不会转发到本地网络之外。默认为
1
。{add_membership, {MultiAddress, InterfaceAddress}}
- 加入多播组。{drop_membership, {MultiAddress, InterfaceAddress}}
- 离开多播组。option/0
- 请参阅inet:setopts/2
。
使用此套接字发送 UDP 数据包使用 send(Socket, ...)
。当 UDP 数据包到达 Socket
的 UDP 端口时,并且套接字处于活动模式,数据包将作为消息传递给控制进程(套接字所有者)
{udp, Socket, PeerIP, PeerPort, Packet} % Without ancillary data
{udp, Socket, PeerIP, PeerPort, AncData, Packet} % With ancillary data
PeerIP
和 PeerPort
是发送 Packet
的地址。Packet
是一个字节列表(如果选项 list
处于活动状态,则为 [
byte/0
]
,如果选项 binary
处于活动状态,则为 binary/0
(它们是互斥的)。
只有当套接字的选项 recvtos
、recvtclass
或 recvttl
中有任何一个处于激活状态时,消息才包含 AncData
字段。
当处于 {active, N}
模式(详情请参阅 inet:setopts/2
)的套接字转换为被动 ({active, false}
) 模式(N
倒数至 0
)时,控制进程会收到如下形式的消息通知
{udp_passive, Socket}
如果操作系统协议栈报告套接字错误,则会将以下消息发送到控制进程
{udp_error, Socket, Reason}
Reason
主要是一个 POSIX 错误代码。
如果套接字处于被动模式(而不是主动模式),则可以使用recv/2,3
](recv/2
) 调用检索接收到的数据。请注意,传入的 UDP 数据包如果长度超过接收缓冲区选项指定的长度,可能会被截断,而不会发出警告。
接收缓冲区选项的默认值为 {recbuf, 8192}
。
-spec recv(Socket, Length) -> {ok, RecvData} | {error, Reason} when Socket :: socket(), Length :: non_neg_integer(), RecvData :: {Address, Port, Packet} | {Address, Port, AncData, Packet}, Address :: inet:ip_address() | inet:returned_non_ip_address(), Port :: inet:port_number(), AncData :: inet:ancillary_data(), Packet :: string() | binary(), Reason :: not_owner | inet:posix().
-spec recv(Socket, Length, Timeout) -> {ok, RecvData} | {error, Reason} when Socket :: socket(), Length :: non_neg_integer(), Timeout :: timeout(), RecvData :: {Address, Port, Packet} | {Address, Port, AncData, Packet}, Address :: inet:ip_address() | inet:returned_non_ip_address(), Port :: inet:port_number(), AncData :: inet:ancillary_data(), Packet :: string() | binary(), Reason :: not_owner | timeout | inet:posix().
在被动模式下从套接字接收数据包。
Timeout
指定以毫秒为单位的超时时间。
如果套接字的任何选项 recvtos
、recvtclass
或 recvttl
处于活动状态,则 RecvData
元组包含 AncData
字段,否则不包含。
-spec send(Socket, Packet) -> ok | {error, Reason} when Socket :: socket(), Packet :: iodata(), Reason :: not_owner | inet:posix().
在已连接的 UDP 套接字上发送数据包。
-spec send(Socket, Destination, Packet) -> ok | {error, Reason} when Socket :: socket(), Destination :: {inet:ip_address(), inet:port_number()} | inet:family_address() | socket:sockaddr_in() | socket:sockaddr_in6(), Packet :: iodata(), Reason :: not_owner | inet:posix().
-spec send(Socket, Host, Port, Packet) -> ok | {error, Reason} when Socket :: socket(), Host :: inet:hostname() | inet:ip_address(), Port :: inet:port_number() | atom(), Packet :: iodata(), Reason :: not_owner | inet:posix(); (Socket, Destination, AncData, Packet) -> ok | {error, Reason} when Socket :: socket(), Destination :: {inet:ip_address(), inet:port_number()} | inet:family_address() | socket:sockaddr_in() | socket:sockaddr_in6(), AncData :: inet:ancillary_data(), Packet :: iodata(), Reason :: not_owner | inet:posix(); (Socket, Destination, PortZero, Packet) -> ok | {error, Reason} when Socket :: socket(), Destination :: {inet:ip_address(), inet:port_number()} | inet:family_address(), PortZero :: inet:port_number(), Packet :: iodata(), Reason :: not_owner | inet:posix().
将 UDP 数据包发送到指定的目标。
使用参数 Host
和 Port
参数 Host
可以是主机名或套接字地址,Port
可以是端口号或服务名称原子。这些将解析为 Destination
,然后此函数等效于 send(Socket, Destination, [], Packet)
,如下所示。
使用参数 Destination
和 AncData
(自 OTP 22.1 起)
将数据包发送到指定的 Destination
,并带有辅助数据 AncData
。
注意
辅助数据
AncData
包含此单个消息的选项,这些选项会覆盖套接字的默认选项,此操作可能并非所有平台都支持,如果不支持,则返回{error, einval}
。使用多个辅助数据项类型也可能不受支持。AncData =:= []
始终受支持。
使用参数 Destination
和 PortZero
(自 OTP 22.1 起)
将数据包发送到指定的 Destination
。由于 Destination
是一个完整的地址,PortZero
是冗余的,必须为 0
。
这是一个遗留子句,主要用于 Destination = {local, Binary}
,其中 PortZero
是多余的。等效于 send(Socket, Destination, [], Packet)
,就在这里之上。
-spec send(Socket, Host, Port, AncData, Packet) -> ok | {error, Reason} when Socket :: socket(), Host :: inet:hostname() | inet:ip_address() | inet:local_address(), Port :: inet:port_number() | atom(), AncData :: inet:ancillary_data(), Packet :: iodata(), Reason :: not_owner | inet:posix().
将带有辅助数据的数据包发送到指定的目标。
关于 Host
和 Port
,等效于 send(Socket, Host, Port, Packet)
,关于辅助数据 AncData
,也等效于 send(Socket, Destination, AncData, Packet)
。