class UDPSocket
UDPSocket 表示一个 UDP/IP 套接字。
公共类方法
源代码
static VALUE
udp_init(int argc, VALUE *argv, VALUE sock)
{
VALUE arg;
int family = AF_INET;
int fd;
if (rb_scan_args(argc, argv, "01", &arg) == 1) {
family = rsock_family_arg(arg);
}
fd = rsock_socket(family, SOCK_DGRAM, 0);
if (fd < 0) {
rb_sys_fail("socket(2) - udp");
}
return rsock_init_sock(sock, fd);
}
创建一个新的 UDPSocket 对象。
address_family 应该是一个整数、字符串或符号:Socket::AF_INET, “AF_INET”, :INET 等。
require 'socket' UDPSocket.new #=> #<UDPSocket:fd 3> UDPSocket.new(Socket::AF_INET6) #=> #<UDPSocket:fd 4>
公共实例方法
源代码
static VALUE
udp_bind(VALUE self, VALUE host, VALUE port)
{
struct udp_arg arg = {.io = self};
arg.res = rsock_addrinfo(host, port, rsock_fd_family(rb_io_descriptor(self)), SOCK_DGRAM, 0);
VALUE result = rb_ensure(udp_bind_internal, (VALUE)&arg, rsock_freeaddrinfo, (VALUE)arg.res);
if (!result) {
rsock_sys_fail_host_port("bind(2)", host, port);
}
return INT2FIX(0);
}
将 udpsocket 绑定到 host:port。
u1 = UDPSocket.new u1.bind("127.0.0.1", 4913) u1.send "message-to-self", 0, "127.0.0.1", 4913 p u1.recvfrom(10) #=> ["message-to", ["AF_INET", 4913, "localhost", "127.0.0.1"]]
源代码
static VALUE
udp_connect(VALUE self, VALUE host, VALUE port)
{
struct udp_arg arg = {.io = self};
arg.res = rsock_addrinfo(host, port, rsock_fd_family(rb_io_descriptor(self)), SOCK_DGRAM, 0);
int result = (int)rb_ensure(udp_connect_internal, (VALUE)&arg, rsock_freeaddrinfo, (VALUE)arg.res);
if (!result) {
rsock_sys_fail_host_port("connect(2)", host, port);
}
return INT2FIX(0);
}
将 udpsocket 连接到 host:port。
这使得无需目标地址即可发送数据。
u1 = UDPSocket.new u1.bind("127.0.0.1", 4913) u2 = UDPSocket.new u2.connect("127.0.0.1", 4913) u2.send "uuuu", 0 p u1.recvfrom(10) #=> ["uuuu", ["AF_INET", 33230, "localhost", "127.0.0.1"]]
源代码
# File ext/socket/lib/socket.rb, line 1688 def recvfrom_nonblock(len, flag = 0, outbuf = nil, exception: true) __recvfrom_nonblock(len, flag, outbuf, exception) end
使用 recvfrom(2) 从 udpsocket 接收最多 maxlen 字节的数据,前提是底层文件描述符已设置 O_NONBLOCK。flags 是零个或多个 MSG_ 选项。结果的第一个元素 mesg 是接收到的数据。第二个元素 sender_inet_addr 是一个表示发送者地址的数组。
当 recvfrom(2) 返回 0 时,Socket#recv_nonblock 返回 nil。在大多数情况下,这意味着连接已关闭,但也可能意味着收到了一个空数据包,因为底层 API 无法区分这两种情况。
参数¶ ↑
-
maxlen- 从套接字接收的字节数 -
flags- 零个或多个MSG_选项 -
outbuf- 目标String缓冲区 -
options- 关键字哈希,支持 ‘exception: false`
示例¶ ↑
require 'socket' s1 = UDPSocket.new s1.bind("127.0.0.1", 0) s2 = UDPSocket.new s2.bind("127.0.0.1", 0) s2.connect(*s1.addr.values_at(3,1)) s1.connect(*s2.addr.values_at(3,1)) s1.send "aaa", 0 begin # emulate blocking recvfrom p s2.recvfrom_nonblock(10) #=> ["aaa", ["AF_INET", 33302, "localhost.localdomain", "127.0.0.1"]] rescue IO::WaitReadable IO.select([s2]) retry end
有关调用 recvfrom_nonblock 失败时可能抛出的异常,请参阅 Socket#recvfrom。
UDPSocket#recvfrom_nonblock 可能会引发与 recvfrom(2) 失败相对应的任何错误,包括 Errno::EWOULDBLOCK。
如果异常是 Errno::EWOULDBLOCK 或 Errno::EAGAIN,它会被 IO::WaitReadable 扩展。因此,可以使用 IO::WaitReadable 来捕获异常,以便重试 recvfrom_nonblock。
通过指定关键字参数 exception 为 false,您可以指示 recvfrom_nonblock 不应引发 IO::WaitReadable 异常,而是返回符号 :wait_readable。
另请参阅¶ ↑
源代码
static VALUE
udp_send(int argc, VALUE *argv, VALUE sock)
{
VALUE flags, host, port;
struct udp_send_arg arg;
VALUE ret;
if (argc == 2 || argc == 3) {
return rsock_bsock_send(argc, argv, sock);
}
rb_scan_args(argc, argv, "4", &arg.sarg.mesg, &flags, &host, &port);
StringValue(arg.sarg.mesg);
GetOpenFile(sock, arg.fptr);
arg.sarg.fd = arg.fptr->fd;
arg.sarg.flags = NUM2INT(flags);
arg.res = rsock_addrinfo(host, port, rsock_fd_family(arg.fptr->fd), SOCK_DGRAM, 0);
ret = rb_ensure(udp_send_internal, (VALUE)&arg,
rsock_freeaddrinfo, (VALUE)arg.res);
if (!ret) rsock_sys_fail_host_port("sendto(2)", host, port);
return ret;
}
通过 udpsocket 发送 mesg。
flags 应该是 Socket::MSG_* 常量的按位 OR。
u1 = UDPSocket.new u1.bind("127.0.0.1", 4913) u2 = UDPSocket.new u2.send "hi", 0, "127.0.0.1", 4913 mesg, addr = u1.recvfrom(10) u1.send mesg, 0, addr[3], addr[1] p u2.recv(100) #=> "hi"