Class: Socket

Inherits:
BasicSocket show all
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

Instance Method Summary collapse

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.



261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/socket/socket.rb', line 261

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

.gethostnameObject



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

.getifaddrsObject



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

Raises:



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

Raises:



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_listObject



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
# File 'lib/socket/socket.rb', line 235

def self.pack_sockaddr_un(file)
  sockaddr = [Socket::AF_UNIX].pack('s') + file
  struct   = RubySL::Socket::Foreign::SockaddrUn.with_sockaddr(sockaddr)

  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
}

Raises:

  • (ArgumentError)


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.message =~ /ai_family not supported/
    raise ArgumentError, 'not an AF_INET/AF_INET6 sockaddr'
  else
    raise e
  end
end

.unpack_sockaddr_un(addr) ⇒ Object



246
247
248
249
250
251
252
253
254
# File 'lib/socket/socket.rb', line 246

def self.unpack_sockaddr_un(addr)
  struct = RubySL::Socket::Foreign::SockaddrUn.with_sockaddr(addr)

  begin
    struct[:sun_path]
  ensure
    struct.free
  end
end

Instance Method Details

#acceptObject



341
342
343
# File 'lib/socket/socket.rb', line 341

def accept
  RubySL::Socket.accept(self, Socket)
end

#accept_nonblockObject



345
346
347
# File 'lib/socket/socket.rb', line 345

def accept_nonblock
  RubySL::Socket.accept_nonblock(self, Socket)
end

#bind(addr) ⇒ Object



275
276
277
278
279
280
281
282
283
284
285
# File 'lib/socket/socket.rb', line 275

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



287
288
289
290
291
292
293
294
295
296
297
# File 'lib/socket/socket.rb', line 287

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



299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/socket/socket.rb', line 299

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



337
338
339
# File 'lib/socket/socket.rb', line 337

def listen(backlog)
  RubySL::Socket.listen(self, backlog)
end

#local_addressObject



313
314
315
316
317
# File 'lib/socket/socket.rb', line 313

def local_address
  sockaddr = RubySL::Socket::Foreign.getsockname(descriptor)

  Addrinfo.new(sockaddr, @family, @socket_type, 0)
end

#recvfrom(bytes, flags = 0) ⇒ Object



325
326
327
328
329
# File 'lib/socket/socket.rb', line 325

def recvfrom(bytes, flags = 0)
  message, addr = recvmsg(bytes, flags)

  return message, addr
end

#recvfrom_nonblock(bytes, flags = 0) ⇒ Object



331
332
333
334
335
# File 'lib/socket/socket.rb', line 331

def recvfrom_nonblock(bytes, flags = 0)
  message, addr = recvmsg_nonblock(bytes, flags)

  return message, addr
end

#remote_addressObject



319
320
321
322
323
# File 'lib/socket/socket.rb', line 319

def remote_address
  sockaddr = RubySL::Socket::Foreign.getpeername(descriptor)

  Addrinfo.new(sockaddr, @family, @socket_type, 0)
end

#sysacceptObject



349
350
351
352
353
# File 'lib/socket/socket.rb', line 349

def sysaccept
  socket, addrinfo = accept

  return socket.fileno, addrinfo
end