Bug #13407
closedWe have recv_nonblock but not send_nonblock... can we add it?
Added by ioquatix (Samuel Williams) almost 7 years ago. Updated over 4 years ago.
Description
We have recv_nonblock, read_nonblock, write_nonblock but not BasicSocket#send_nonblock. Is this a mistake?
Updated by normalperson (Eric Wong) almost 7 years ago
samuel@oriontransfer.org wrote:
Bug #13407: We have recv_nonblock but not send_nonblock... can we add it?
https://bugs.ruby-lang.org/issues/13407
We have recv_nonblock, read_nonblock, write_nonblock but not BasicSocket#send_nonblock. Is this a mistake?
I've sometimes wondered that too...
However we have sendmsg_nonblock, is that insufficient?
So I also wonder why recv_nonblock exists, since
recvmsg_nonblock is a superset of its functionality;
and redundant methods waste memory...
It's too late to remove recv_nonblock, of course.
Updated by ioquatix (Samuel Williams) almost 7 years ago
Thanks for the quick reply and interesting information.
I think I'd like to see send_nonblock, but I'm not sure if it's implementation is different than sendmsg_nonblock. If it's not different, an alias may be sufficient.
Updated by ioquatix (Samuel Williams) almost 7 years ago
I've been playing around with sendmsg_nonblock, but I can't see how to make a wrapper around it with the equivalent API to send. It seems like it should be possible, but it always requires ancillary data, can't provide nil?
Updated by normalperson (Eric Wong) almost 7 years ago
samuel@oriontransfer.org wrote:
I've been playing around with sendmsg_nonblock, but I can't
see how to make a wrapper around it with the equivalent API to
send. It seems like it should be possible, but it always
requires ancillary data, can't provide nil?
Huh? The following works for me:
require 'socket'
a, b = UNIXSocket.pair
b.sendmsg_nonblock("HI", exception: false)
p a.recv(2)
# prints "HI"
Updated by ioquatix (Samuel Williams) almost 7 years ago
Okay, so I found the issue.
Firstly, sendmsg
doesn't work for UDP sockets, I get EINVAL
on Darwin OS.
Secondly, UDPSocket
overrides #send
but only in a specific case:
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;
}
If you call udp_socket.send(data, flags, host, port)
it uses sendto
which works, otherwise it uses rsock_bsock_send
which fails with EINVAL
.
If you already constructed a sockaddr
, for example, trying udp_socket.send(data, flags, sockaddr)
will fail. It's all a bit confusing. Additionally, for UDPSocket, there is no send_nonblock
which is my real issue here, and using sendmsg_nonblock
fails.
Updated by normalperson (Eric Wong) almost 7 years ago
samuel@oriontransfer.org wrote:
Firstly,
sendmsg
doesn't work for UDP sockets, I getEINVAL
on Darwin OS.
Can you show us a sample code of what you're trying?
I wonder if it's something that works on Linux but not Darwin
or if there's something else... I don't have too much
experience with UDP, but maybe it's something I can still help
with.
Updated by ioquatix (Samuel Williams) almost 7 years ago
Okay.
Here is the working example.
#!/usr/bin/env ruby
require 'socket'
port = 6778
server = UDPSocket.new.tap{|socket| socket.bind("localhost", port)}
client = UDPSocket.new
data = "Matz is nice so we are nice."
t1 = Thread.new do
packet, (_, remote_port, remote_host) = server.recvfrom(512)
server.send(packet, 0, remote_host, remote_port)
end
t2 = Thread.new do
client.send(data, 0, "localhost", port)
response, _ = client.recvfrom(512)
puts "Got response: #{response.inspect}"
end
[t1, t2].each(&:join)
puts "Finished."
Here is one that fails with EINVAL:
#!/usr/bin/env ruby
Thread.abort_on_exception = true
require 'socket'
port = 6778
server = UDPSocket.new.tap{|socket| socket.bind("localhost", port)}
client = UDPSocket.new
data = "Matz is nice so we are nice."
t1 = Thread.new do
puts "Server waiting for packet..."
packet, (_, remote_port, remote_host) = server.recvfrom(512)
server.send(packet, 0, remote_host, remote_port)
end
t2 = Thread.new do
address = Addrinfo.udp("localhost", port)
puts "Sending data to #{address.inspect}"
# Should call ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen), but calls send which fails with EINVAL.
result = client.send(data, 0, address.to_sockaddr)
response, _ = client.recvfrom(512)
puts "Got response: #{response.inspect}"
end
[t1, t2].each(&:join)
puts "Finished."
Updated by nobu (Nobuyoshi Nakada) almost 7 years ago
Please file a new issue.
Updated by ioquatix (Samuel Williams) almost 7 years ago
@nobu (Nobuyoshi Nakada) here is the issue https://bugs.ruby-lang.org/issues/13409 for the specific problem mentioned above.
But, if possible, I'd like send_nonblock too :)
Updated by akr (Akira Tanaka) almost 7 years ago
I cannot remember why I didn't add send_nonblock.
However I feel adding send_nonblock is considerable because
sendmsg is much more complex than send/sendto.
Updated by hsbt (Hiroshi SHIBATA) over 6 years ago
- Status changed from Open to Assigned
- Assignee set to akr (Akira Tanaka)
Updated by ioquatix (Samuel Williams) almost 6 years ago
I have since worked around this issue, by using sendmsg
as an alias. https://github.com/socketry/async-io/blob/dbcfb05d209a41d2283e49c75c407a93179a64b6/lib/async/io/socket.rb#L47
Updated by ioquatix (Samuel Williams) over 5 years ago
We can close this issue, as there exists a satisfactory alternative.
Updated by akr (Akira Tanaka) over 4 years ago
- Status changed from Assigned to Rejected
ioquatix (Samuel Williams) wrote:
We can close this issue, as there exists a satisfactory alternative.
I see.
(I don't change my feeling, https://bugs.ruby-lang.org/issues/13407#note-10 , though)