Class: Socket
- Inherits:
-
BasicSocket
- Object
- IO
- BasicSocket
- Socket
- Defined in:
- lib/socket/mri.rb,
lib/socket/ifaddr.rb,
lib/socket/option.rb,
lib/socket/socket.rb,
lib/socket/constants.rb,
lib/socket/ancillary_data.rb
Defined Under Namespace
Modules: Constants Classes: AncillaryData, Ifaddr, Option, UDPSource
Class Method Summary collapse
-
.accept_loop(*sockets) ⇒ Object
yield socket and client address for each a connection accepted via given sockets.
- .getaddrinfo(host, service, family = 0, socktype = 0, protocol = 0, flags = 0, reverse_lookup = nil) ⇒ Object
- .gethostbyaddr(addr, family = nil) ⇒ Object
- .gethostbyname(hostname) ⇒ Object
- .gethostname ⇒ Object
- .getifaddrs ⇒ Object
- .getnameinfo(sockaddr, flags = 0) ⇒ Object
- .getservbyname(service, proto = 'tcp') ⇒ Object
- .getservbyport(port, proto = nil) ⇒ Object
- .ip_address_list ⇒ Object
-
.ip_sockets_port0(ai_list, reuseaddr) ⇒ Object
:stopdoc:.
- .pack_sockaddr_in(port, host) ⇒ Object (also: sockaddr_in)
- .pack_sockaddr_un(file) ⇒ Object (also: sockaddr_un)
- .socketpair(family, type, protocol = 0) ⇒ Object (also: pair)
-
.tcp(host, port, *rest) ⇒ Object
:call-seq: Socket.tcp(host, port, local_host=nil, local_port=nil, [opts]) {|socket| … } Socket.tcp(host, port, local_host=nil, local_port=nil, [opts]).
-
.tcp_server_loop(host = nil, port, &b) ⇒ Object
creates a TCP/IP server on port and calls the block for each connection accepted.
-
.tcp_server_sockets(host = nil, port) ⇒ Object
creates TCP/IP server sockets for host and port.
- .tcp_server_sockets_port0(host) ⇒ Object
-
.udp_server_loop(host = nil, port, &b) ⇒ Object
:call-seq: Socket.udp_server_loop(port) {|msg, msg_src| … } Socket.udp_server_loop(host, port) {|msg, msg_src| … }.
-
.udp_server_loop_on(sockets, &b) ⇒ Object
:call-seq: Socket.udp_server_loop_on(sockets) {|msg, msg_src| … }.
-
.udp_server_recv(sockets) ⇒ Object
:call-seq: Socket.udp_server_recv(sockets) {|msg, msg_src| … }.
-
.udp_server_sockets(host = nil, port) ⇒ Object
:call-seq: Socket.udp_server_sockets([host, ] port).
-
.unix(path) ⇒ Object
creates a new socket connected to path using UNIX socket socket.
-
.unix_server_loop(path, &b) ⇒ Object
creates a UNIX socket server on path.
-
.unix_server_socket(path) ⇒ Object
creates a UNIX server socket on path.
- .unpack_sockaddr_in(sockaddr) ⇒ Object
- .unpack_sockaddr_un(addr) ⇒ Object
Instance Method Summary collapse
- #accept ⇒ Object
- #accept_nonblock ⇒ Object
- #bind(addr) ⇒ Object
- #connect(sockaddr) ⇒ Object
- #connect_nonblock(sockaddr) ⇒ Object
-
#initialize(family, socket_type, protocol = 0) ⇒ Socket
constructor
A new instance of Socket.
-
#ipv6only! ⇒ Object
enable the socket option IPV6_V6ONLY if IPV6_V6ONLY is available.
- #listen(backlog) ⇒ Object
- #local_address ⇒ Object
- #recvfrom(bytes, flags = 0) ⇒ Object
- #recvfrom_nonblock(bytes, flags = 0) ⇒ Object
- #remote_address ⇒ Object
- #sysaccept ⇒ Object
Methods inherited from BasicSocket
#close_read, #close_write, #connect_address, do_not_reverse_lookup, #do_not_reverse_lookup, do_not_reverse_lookup=, #do_not_reverse_lookup=, for_fd, #getpeereid, #getpeername, #getsockname, #getsockopt, #recv, #recv_nonblock, #recvmsg, #recvmsg_nonblock, #send, #sendmsg, #sendmsg_nonblock, #setsockopt, #shutdown
Constructor Details
#initialize(family, socket_type, protocol = 0) ⇒ Socket
Returns a new instance of Socket.
262 263 264 265 266 267 268 269 270 271 272 273 274 |
# File 'lib/socket/socket.rb', line 262 def initialize(family, socket_type, protocol = 0) @no_reverse_lookup = self.class.do_not_reverse_lookup @family = RubySL::Socket.protocol_family(family) @socket_type = RubySL::Socket.socket_type(socket_type) descriptor = RubySL::Socket::Foreign.socket(@family, @socket_type, protocol) Errno.handle('socket(2)') if descriptor < 0 IO.setup(self, descriptor, nil, true) binmode end |
Class Method Details
.accept_loop(*sockets) ⇒ Object
yield socket and client address for each a connection accepted via given sockets.
The arguments are a list of sockets. The individual argument should be a socket or an array of sockets.
This method yields the block sequentially. It means that the next connection is not accepted until the block returns. So concurrent mechanism, thread for example, should be used to service multiple clients at a time.
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 |
# File 'lib/socket/mri.rb', line 554 def self.accept_loop(*sockets) # :yield: socket, client_addrinfo sockets.flatten!(1) if sockets.empty? raise ArgumentError, "no sockets" end loop { readable, _, _ = IO.select(sockets) readable.each {|r| begin sock, addr = r.accept_nonblock rescue IO::WaitReadable next end yield sock, addr } } end |
.getaddrinfo(host, service, family = 0, socktype = 0, protocol = 0, flags = 0, reverse_lookup = nil) ⇒ Object
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/socket/socket.rb', line 12 def self.getaddrinfo(host, service, family = 0, socktype = 0, protocol = 0, flags = 0, reverse_lookup = nil) if host host = RubySL::Socket.coerce_to_string(host) end if host && (host.empty? || host == '<any>') host = "0.0.0.0" elsif host == '<broadcast>' host = '255.255.255.255' end if service.kind_of?(Fixnum) service = service.to_s elsif service service = RubySL::Socket.coerce_to_string(service) end family = RubySL::Socket.address_family(family) socktype = RubySL::Socket.socket_type(socktype) addrinfos = RubySL::Socket::Foreign .getaddrinfo(host, service, family, socktype, protocol, flags) reverse_lookup = RubySL::Socket .convert_reverse_lookup(nil, reverse_lookup) addrinfos.map do |ai| addrinfo = [] unpacked = RubySL::Socket::Foreign .unpack_sockaddr_in(ai[4], reverse_lookup) addrinfo << Socket::Constants::AF_TO_FAMILY[ai[1]] addrinfo << unpacked.pop # port # Canonical host is present (e.g. when AI_CANONNAME was used) if ai[5] and !reverse_lookup unpacked[0] = ai[5] end addrinfo.concat(unpacked) # hosts addrinfo << ai[1] # family addrinfo << ai[2] # socktype addrinfo << ai[3] # protocol addrinfo end end |
.gethostbyaddr(addr, family = nil) ⇒ Object
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/socket/socket.rb', line 128 def self.gethostbyaddr(addr, family = nil) if !family and addr.bytesize == 16 family = Socket::AF_INET6 elsif !family family = Socket::AF_INET end family = RubySL::Socket.address_family(family) RubySL::Socket::Foreign.char_pointer(addr.bytesize) do |in_pointer| in_pointer.write_string(addr) out_pointer = RubySL::Socket::Foreign .gethostbyaddr(in_pointer, in_pointer.total, family) unless out_pointer raise SocketError, "No host found for address #{addr.inspect}" end struct = RubySL::Socket::Foreign::Hostent.new(out_pointer) [struct.hostname, struct.aliases, struct.type, *struct.addresses] end end |
.gethostbyname(hostname) ⇒ Object
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/socket/socket.rb', line 102 def self.gethostbyname(hostname) addrinfos = Socket .getaddrinfo(hostname, nil, nil, :STREAM, nil, Socket::AI_CANONNAME) hostname = addrinfos[0][2] family = addrinfos[0][4] addresses = [] alternatives = RubySL::Socket.aliases_for_hostname(hostname) addrinfos.each do |a| sockaddr = Socket.sockaddr_in(0, a[3]) if a[4] == AF_INET offset, size = RubySL::Socket::Foreign::SockaddrIn.layout[:sin_addr] addresses << sockaddr.byteslice(offset, size) elsif a[4] == AF_INET6 offset, size = RubySL::Socket::Foreign::SockaddrIn6.layout[:sin6_addr] addresses << sockaddr.byteslice(offset, size) end end [hostname, alternatives, family, *addresses] end |
.gethostname ⇒ Object
94 95 96 97 98 99 100 |
# File 'lib/socket/socket.rb', line 94 def self.gethostname RubySL::Socket::Foreign.char_pointer(::Socket::NI_MAXHOST) do |pointer| RubySL::Socket::Foreign.gethostname(pointer, pointer.total) pointer.read_string end end |
.getifaddrs ⇒ Object
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/socket/socket.rb', line 174 def self.getifaddrs initial = RubySL::Socket::Foreign::Ifaddrs.new status = RubySL::Socket::Foreign.getifaddrs(initial) ifaddrs = [] index = 1 Errno.handle('getifaddrs()') if status < 0 begin initial.each_address do |ifaddrs_struct| ifaddrs << Ifaddr.new( addr: ifaddrs_struct.address_to_addrinfo, broadaddr: ifaddrs_struct.broadcast_to_addrinfo, dstaddr: ifaddrs_struct.destination_to_addrinfo, netmask: ifaddrs_struct.netmask_to_addrinfo, name: ifaddrs_struct.name, flags: ifaddrs_struct.flags, ifindex: index ) index += 1 end ifaddrs ensure RubySL::Socket::Foreign.freeifaddrs(initial) end end |
.getnameinfo(sockaddr, flags = 0) ⇒ Object
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/socket/socket.rb', line 62 def self.getnameinfo(sockaddr, flags = 0) port = nil host = nil family = Socket::AF_UNSPEC if sockaddr.is_a?(Array) if sockaddr.size == 3 af, port, host = sockaddr elsif sockaddr.size == 4 af = sockaddr[0] port = sockaddr[1] host = sockaddr[3] || sockaddr[2] else raise ArgumentError, "array size should be 3 or 4, #{sockaddr.size} given" end if af == 'AF_INET' family = Socket::AF_INET elsif af == 'AF_INET6' family = Socket::AF_INET6 end sockaddr = RubySL::Socket::Foreign .pack_sockaddr_in(host, port, family, Socket::SOCK_STREAM, 0) end _, port, host, _ = RubySL::Socket::Foreign.getnameinfo(sockaddr, flags) [host, port] end |
.getservbyname(service, proto = 'tcp') ⇒ Object
153 154 155 156 157 158 159 160 161 |
# File 'lib/socket/socket.rb', line 153 def self.getservbyname(service, proto = 'tcp') pointer = RubySL::Socket::Foreign.getservbyname(service, proto) raise SocketError, "no such service #{service}/#{proto}" unless pointer struct = RubySL::Socket::Foreign::Servent.new(pointer) RubySL::Socket::Foreign.ntohs(struct.port) end |
.getservbyport(port, proto = nil) ⇒ Object
163 164 165 166 167 168 169 170 171 172 |
# File 'lib/socket/socket.rb', line 163 def self.getservbyport(port, proto = nil) proto ||= 'tcp' pointer = RubySL::Socket::Foreign.getservbyport(port, proto) raise SocketError, "no such service for port #{port}/#{proto}" unless pointer struct = RubySL::Socket::Foreign::Servent.new(pointer) struct.name end |
.ip_address_list ⇒ Object
2 3 4 5 6 7 8 9 10 |
# File 'lib/socket/socket.rb', line 2 def self.ip_address_list ips = [] getifaddrs.each do |ifaddr| ips << ifaddr.addr if ifaddr.addr end ips end |
.ip_sockets_port0(ai_list, reuseaddr) ⇒ Object
:stopdoc:
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 |
# File 'lib/socket/mri.rb', line 423 def self.ip_sockets_port0(ai_list, reuseaddr) sockets = [] begin sockets.clear port = nil ai_list.each {|ai| begin s = Socket.new(ai.pfamily, ai.socktype, ai.protocol) rescue SystemCallError next end sockets << s s.ipv6only! if ai.ipv6? if reuseaddr s.setsockopt(:SOCKET, :REUSEADDR, 1) end if !port s.bind(ai) port = s.local_address.ip_port else s.bind(ai.family_addrinfo(ai.ip_address, port)) end } rescue Errno::EADDRINUSE sockets.each {|s| s.close } retry rescue Exception sockets.each {|s| s.close } raise end sockets end |
.pack_sockaddr_in(port, host) ⇒ Object Also known as: sockaddr_in
203 204 205 |
# File 'lib/socket/socket.rb', line 203 def self.pack_sockaddr_in(port, host) RubySL::Socket::Foreign.pack_sockaddr_in(host, port) end |
.pack_sockaddr_un(file) ⇒ Object Also known as: sockaddr_un
235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/socket/socket.rb', line 235 def self.pack_sockaddr_un(file) struct = RubySL::Socket::Foreign::SockaddrUn.new struct[:sun_family] = Socket::AF_UNIX struct[:sun_path] = file begin struct.to_s ensure struct.free end end |
.socketpair(family, type, protocol = 0) ⇒ Object Also known as: pair
220 221 222 223 224 225 226 227 |
# File 'lib/socket/socket.rb', line 220 def self.socketpair(family, type, protocol = 0) family = RubySL::Socket.address_family(family) type = RubySL::Socket.socket_type(type) fd0, fd1 = RubySL::Socket::Foreign.socketpair(family, type, protocol) [for_fd(fd0), for_fd(fd1)] end |
.tcp(host, port, *rest) ⇒ Object
:call-seq:
Socket.tcp(host, port, local_host=nil, local_port=nil, [opts]) {|socket| ... }
Socket.tcp(host, port, local_host=nil, local_port=nil, [opts])
creates a new socket object connected to host:port using TCP/IP.
If local_host:local_port is given, the socket is bound to it.
The optional last argument opts is options represented by a hash. opts may have following options:
- :connect_timeout
-
specify the timeout in seconds.
If a block is given, the block is called with the socket. The value of the block is returned. The socket is closed when this method returns.
If no block is given, the socket is returned.
Socket.tcp("www.ruby-lang.org", 80) {|sock|
sock.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n"
sock.close_write
puts sock.read
}
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 |
# File 'lib/socket/mri.rb', line 372 def self.tcp(host, port, *rest) # :yield: socket opts = Hash === rest.last ? rest.pop : {} raise ArgumentError, "wrong number of arguments (#{rest.length} for 2)" if 2 < rest.length local_host, local_port = rest last_error = nil ret = nil connect_timeout = opts[:connect_timeout] local_addr_list = nil if local_host != nil || local_port != nil local_addr_list = Addrinfo.getaddrinfo(local_host, local_port, nil, :STREAM, nil) end Addrinfo.foreach(host, port, nil, :STREAM) {|ai| if local_addr_list local_addr = local_addr_list.find {|local_ai| local_ai.afamily == ai.afamily } next if !local_addr else local_addr = nil end begin sock = local_addr ? ai.connect_from(local_addr, :timeout => connect_timeout) : ai.connect(:timeout => connect_timeout) rescue SystemCallError last_error = $! next end ret = sock break } if !ret if last_error raise last_error else raise SocketError, "no appropriate local address" end end if block_given? begin yield ret ensure ret.close if !ret.closed? end else ret end end |
.tcp_server_loop(host = nil, port, &b) ⇒ Object
creates a TCP/IP server on port and calls the block for each connection accepted. The block is called with a socket and a client_address as an Addrinfo object.
If host is specified, it is used with port to determine the server addresses.
The socket is not closed when the block returns. So application should close it explicitly.
This method calls the block sequentially. It means that the next connection is not accepted until the block returns. So concurrent mechanism, thread for example, should be used to service multiple clients at a time.
Note that Addrinfo.getaddrinfo is used to determine the server socket addresses. When Addrinfo.getaddrinfo returns two or more addresses, IPv4 and IPv6 address for example, all of them are used. Socket.tcp_server_loop succeeds if one socket can be used at least.
# Sequential echo server.
# It services only one client at a time.
Socket.tcp_server_loop(16807) {|sock, client_addrinfo|
begin
IO.copy_stream(sock, sock)
ensure
sock.close
end
}
# Threaded echo server
# It services multiple clients at a time.
# Note that it may accept connections too much.
Socket.tcp_server_loop(16807) {|sock, client_addrinfo|
Thread.new {
begin
IO.copy_stream(sock, sock)
ensure
sock.close
end
}
}
613 614 615 616 617 |
# File 'lib/socket/mri.rb', line 613 def self.tcp_server_loop(host=nil, port, &b) # :yield: socket, client_addrinfo tcp_server_sockets(host, port) {|sockets| accept_loop(sockets, &b) } end |
.tcp_server_sockets(host = nil, port) ⇒ Object
creates TCP/IP server sockets for host and port. host is optional.
If no block given, it returns an array of listening sockets.
If a block is given, the block is called with the sockets. The value of the block is returned. The socket is closed when this method returns.
If port is 0, actual port number is chosen dynamically. However all sockets in the result has same port number.
# tcp_server_sockets returns two sockets.
sockets = Socket.tcp_server_sockets(1296)
p sockets #=> [#<Socket:fd 3>, #<Socket:fd 4>]
# The sockets contains IPv6 and IPv4 sockets.
sockets.each {|s| p s.local_address }
#=> #<Addrinfo: [::]:1296 TCP>
# #<Addrinfo: 0.0.0.0:1296 TCP>
# IPv6 and IPv4 socket has same port number, 53114, even if it is chosen dynamically.
sockets = Socket.tcp_server_sockets(0)
sockets.each {|s| p s.local_address }
#=> #<Addrinfo: [::]:53114 TCP>
# #<Addrinfo: 0.0.0.0:53114 TCP>
# The block is called with the sockets.
Socket.tcp_server_sockets(0) {|sockets|
p sockets #=> [#<Socket:fd 3>, #<Socket:fd 4>]
}
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 |
# File 'lib/socket/mri.rb', line 510 def self.tcp_server_sockets(host=nil, port) if port == 0 sockets = tcp_server_sockets_port0(host) else last_error = nil sockets = [] begin Addrinfo.foreach(host, port, nil, :STREAM, nil, Socket::AI_PASSIVE) {|ai| begin s = ai.listen rescue SystemCallError last_error = $! next end sockets << s } if sockets.empty? raise last_error end rescue Exception sockets.each {|s| s.close } raise end end if block_given? begin yield sockets ensure sockets.each {|s| s.close if !s.closed? } end else sockets end end |
.tcp_server_sockets_port0(host) ⇒ Object
459 460 461 462 463 464 465 466 467 468 469 470 471 |
# File 'lib/socket/mri.rb', line 459 def self.tcp_server_sockets_port0(host) ai_list = Addrinfo.getaddrinfo(host, 0, nil, :STREAM, nil, Socket::AI_PASSIVE) sockets = ip_sockets_port0(ai_list, true) begin sockets.each {|s| s.listen(Socket::SOMAXCONN) } rescue Exception sockets.each {|s| s.close } raise end sockets end |
.udp_server_loop(host = nil, port, &b) ⇒ Object
:call-seq:
Socket.udp_server_loop(port) {|msg, msg_src| ... }
Socket.udp_server_loop(host, port) {|msg, msg_src| ... }
creates a UDP/IP server on port and calls the block for each message arrived. The block is called with the message and its source information.
This method allocates sockets internally using port. If host is specified, it is used conjunction with port to determine the server addresses.
The msg is a string.
The msg_src is a Socket::UDPSource object. It is used for reply.
# UDP/IP echo server.
Socket.udp_server_loop(9261) {|msg, msg_src|
msg_src.reply msg
}
785 786 787 788 789 |
# File 'lib/socket/mri.rb', line 785 def self.udp_server_loop(host=nil, port, &b) # :yield: message, message_source udp_server_sockets(host, port) {|sockets| udp_server_loop_on(sockets, &b) } end |
.udp_server_loop_on(sockets, &b) ⇒ Object
:call-seq:
Socket.udp_server_loop_on(sockets) {|msg, msg_src| ... }
Run UDP/IP server loop on the given sockets.
The return value of Socket.udp_server_sockets is appropriate for the argument.
It calls the block for each message received.
758 759 760 761 762 763 |
# File 'lib/socket/mri.rb', line 758 def self.udp_server_loop_on(sockets, &b) # :yield: msg, msg_src loop { readable, _, _ = IO.select(sockets) udp_server_recv(readable, &b) } end |
.udp_server_recv(sockets) ⇒ Object
:call-seq:
Socket.udp_server_recv(sockets) {|msg, msg_src| ... }
Receive UDP/IP packets from the given sockets. For each packet received, the block is called.
The block receives msg and msg_src. msg is a string which is the payload of the received packet. msg_src is a Socket::UDPSource object which is used for reply.
Socket.udp_server_loop can be implemented using this method as follows.
udp_server_sockets(host, port) {|sockets|
loop {
readable, _, _ = IO.select(sockets)
udp_server_recv(readable) {|msg, msg_src| ... }
}
}
728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 |
# File 'lib/socket/mri.rb', line 728 def self.udp_server_recv(sockets) sockets.each {|r| begin msg, sender_addrinfo, _, *controls = r.recvmsg_nonblock rescue IO::WaitReadable next end ai = r.local_address if ai.ipv6? and pktinfo = controls.find {|c| c.cmsg_is?(:IPV6, :PKTINFO) } ai = Addrinfo.udp(pktinfo.ipv6_pktinfo_addr.ip_address, ai.ip_port) yield msg, UDPSource.new(sender_addrinfo, ai) {|reply_msg| r.sendmsg reply_msg, 0, sender_addrinfo, pktinfo } else yield msg, UDPSource.new(sender_addrinfo, ai) {|reply_msg| r.send reply_msg, 0, sender_addrinfo } end } end |
.udp_server_sockets(host = nil, port) ⇒ Object
:call-seq:
Socket.udp_server_sockets([host, ] port)
Creates UDP/IP sockets for a UDP server.
If no block given, it returns an array of sockets.
If a block is given, the block is called with the sockets. The value of the block is returned. The sockets are closed when this method returns.
If port is zero, some port is chosen. But the chosen port is used for the all sockets.
# UDP/IP echo server
Socket.udp_server_sockets(0) {|sockets|
p sockets.first.local_address.ip_port #=> 32963
Socket.udp_server_loop_on(sockets) {|msg, msg_src|
msg_src.reply msg
}
}
641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 |
# File 'lib/socket/mri.rb', line 641 def self.udp_server_sockets(host=nil, port) last_error = nil sockets = [] ipv6_recvpktinfo = nil if defined? Socket::AncillaryData if defined? Socket::IPV6_RECVPKTINFO # RFC 3542 ipv6_recvpktinfo = Socket::IPV6_RECVPKTINFO elsif defined? Socket::IPV6_PKTINFO # RFC 2292 ipv6_recvpktinfo = Socket::IPV6_PKTINFO end end local_addrs = Socket.ip_address_list ip_list = [] Addrinfo.foreach(host, port, nil, :DGRAM, nil, Socket::AI_PASSIVE) {|ai| if ai.ipv4? && ai.ip_address == "0.0.0.0" local_addrs.each {|a| next if !a.ipv4? ip_list << Addrinfo.new(a.to_sockaddr, :INET, :DGRAM, 0); } elsif ai.ipv6? && ai.ip_address == "::" && !ipv6_recvpktinfo local_addrs.each {|a| next if !a.ipv6? ip_list << Addrinfo.new(a.to_sockaddr, :INET6, :DGRAM, 0); } else ip_list << ai end } if port == 0 sockets = ip_sockets_port0(ip_list, false) else ip_list.each {|ip| ai = Addrinfo.udp(ip.ip_address, port) begin s = ai.bind rescue SystemCallError last_error = $! next end sockets << s } if sockets.empty? raise last_error end end sockets.each {|s| ai = s.local_address if ipv6_recvpktinfo && ai.ipv6? && ai.ip_address == "::" s.setsockopt(:IPV6, ipv6_recvpktinfo, 1) end } if block_given? begin yield sockets ensure sockets.each {|s| s.close if !s.closed? } if sockets end else sockets end end |
.unix(path) ⇒ Object
creates a new socket connected to path using UNIX socket socket.
If a block is given, the block is called with the socket. The value of the block is returned. The socket is closed when this method returns.
If no block is given, the socket is returned.
# talk to /tmp/sock socket.
Socket.unix("/tmp/sock") {|sock|
t = Thread.new { IO.copy_stream(sock, STDOUT) }
IO.copy_stream(STDIN, sock)
t.join
}
835 836 837 838 839 840 841 842 843 844 845 846 847 |
# File 'lib/socket/mri.rb', line 835 def self.unix(path) # :yield: socket addr = Addrinfo.unix(path) sock = addr.connect if block_given? begin yield sock ensure sock.close if !sock.closed? end else sock end end |
.unix_server_loop(path, &b) ⇒ Object
creates a UNIX socket server on path. It calls the block for each socket accepted.
If host is specified, it is used with port to determine the server ports.
The socket is not closed when the block returns. So application should close it.
This method deletes the socket file pointed by path at first if the file is a socket file and it is owned by the user of the application. This is safe only if the directory of path is not changed by a malicious user. So don’t use /tmp/malicious-users-directory/socket. Note that /tmp/socket and /tmp/your-private-directory/socket is safe assuming that /tmp has sticky bit.
# Sequential echo server.
# It services only one client at a time.
Socket.unix_server_loop("/tmp/sock") {|sock, client_addrinfo|
begin
IO.copy_stream(sock, sock)
ensure
sock.close
end
}
922 923 924 925 926 |
# File 'lib/socket/mri.rb', line 922 def self.unix_server_loop(path, &b) # :yield: socket, client_addrinfo unix_server_socket(path) {|serv| accept_loop(serv, &b) } end |
.unix_server_socket(path) ⇒ Object
creates a UNIX server socket on path
If no block given, it returns a listening socket.
If a block is given, it is called with the socket and the block value is returned. When the block exits, the socket is closed and the socket file is removed.
socket = Socket.unix_server_socket("/tmp/s")
p socket #=> #<Socket:fd 3>
p socket.local_address #=> #<Addrinfo: /tmp/s SOCK_STREAM>
Socket.unix_server_socket("/tmp/sock") {|s|
p s #=> #<Socket:fd 3>
p s.local_address #=> # #<Addrinfo: /tmp/sock SOCK_STREAM>
}
865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 |
# File 'lib/socket/mri.rb', line 865 def self.unix_server_socket(path) if !unix_socket_abstract_name?(path) begin st = File.lstat(path) rescue Errno::ENOENT end if st && st.socket? && st.owned? File.unlink path end end s = Addrinfo.unix(path).listen if block_given? begin yield s ensure s.close if !s.closed? if !unix_socket_abstract_name?(path) File.unlink path end end else s end end |
.unpack_sockaddr_in(sockaddr) ⇒ Object
207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/socket/socket.rb', line 207 def self.unpack_sockaddr_in(sockaddr) _, address, port = RubySL::Socket::Foreign .unpack_sockaddr_in(sockaddr, false) return port, address rescue SocketError => e if e. =~ /ai_family not supported/ raise ArgumentError, 'not an AF_INET/AF_INET6 sockaddr' else raise e end end |
.unpack_sockaddr_un(addr) ⇒ Object
247 248 249 250 251 252 253 254 255 |
# File 'lib/socket/socket.rb', line 247 def self.unpack_sockaddr_un(addr) struct = RubySL::Socket::Foreign::SockaddrUn.with_sockaddr(addr) begin struct[:sun_path].to_s ensure struct.free end end |
Instance Method Details
#accept ⇒ Object
342 343 344 |
# File 'lib/socket/socket.rb', line 342 def accept RubySL::Socket.accept(self, Socket) end |
#accept_nonblock ⇒ Object
346 347 348 |
# File 'lib/socket/socket.rb', line 346 def accept_nonblock RubySL::Socket.accept_nonblock(self, Socket) end |
#bind(addr) ⇒ Object
276 277 278 279 280 281 282 283 284 285 286 |
# File 'lib/socket/socket.rb', line 276 def bind(addr) if addr.is_a?(Addrinfo) addr = addr.to_sockaddr end err = RubySL::Socket::Foreign.bind(descriptor, addr) Errno.handle('bind(2)') unless err == 0 0 end |
#connect(sockaddr) ⇒ Object
288 289 290 291 292 293 294 295 296 297 298 |
# File 'lib/socket/socket.rb', line 288 def connect(sockaddr) if sockaddr.is_a?(Addrinfo) sockaddr = sockaddr.to_sockaddr end status = RubySL::Socket::Foreign.connect(descriptor, sockaddr) RubySL::Socket::Error.write_error('connect(2)', self) if status < 0 0 end |
#connect_nonblock(sockaddr) ⇒ Object
300 301 302 303 304 305 306 307 308 309 310 311 312 |
# File 'lib/socket/socket.rb', line 300 def connect_nonblock(sockaddr) fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK) if sockaddr.is_a?(Addrinfo) sockaddr = sockaddr.to_sockaddr end status = RubySL::Socket::Foreign.connect(descriptor, sockaddr) RubySL::Socket::Error.write_nonblock('connect(2)') if status < 0 0 end |
#ipv6only! ⇒ Object
enable the socket option IPV6_V6ONLY if IPV6_V6ONLY is available.
340 341 342 343 344 |
# File 'lib/socket/mri.rb', line 340 def ipv6only! if defined? Socket::IPV6_V6ONLY self.setsockopt(:IPV6, :V6ONLY, 1) end end |
#listen(backlog) ⇒ Object
338 339 340 |
# File 'lib/socket/socket.rb', line 338 def listen(backlog) RubySL::Socket.listen(self, backlog) end |
#local_address ⇒ Object
314 315 316 317 318 |
# File 'lib/socket/socket.rb', line 314 def local_address sockaddr = RubySL::Socket::Foreign.getsockname(descriptor) Addrinfo.new(sockaddr, @family, @socket_type, 0) end |
#recvfrom(bytes, flags = 0) ⇒ Object
326 327 328 329 330 |
# File 'lib/socket/socket.rb', line 326 def recvfrom(bytes, flags = 0) , addr = recvmsg(bytes, flags) return , addr end |
#recvfrom_nonblock(bytes, flags = 0) ⇒ Object
332 333 334 335 336 |
# File 'lib/socket/socket.rb', line 332 def recvfrom_nonblock(bytes, flags = 0) , addr = recvmsg_nonblock(bytes, flags) return , addr end |
#remote_address ⇒ Object
320 321 322 323 324 |
# File 'lib/socket/socket.rb', line 320 def remote_address sockaddr = RubySL::Socket::Foreign.getpeername(descriptor) Addrinfo.new(sockaddr, @family, @socket_type, 0) end |
#sysaccept ⇒ Object
350 351 352 353 354 |
# File 'lib/socket/socket.rb', line 350 def sysaccept socket, addrinfo = accept return socket.fileno, addrinfo end |