Socket.rb


#1

Socket e$B$r;H$C$F$$$k$H!"@8@.$9$k$H$3$m$,e(B TCPSocket e$BEy$KHf$Y$Fe(B
e$BITJX$J$N$G!"0J2<$N$h$&$J%a%=%C%I$r$D$1$F$b$$$$$G$7$g$&$+!#e(B

Socket.tcp(host, port, local_host=nil, local_port=nil) {|socket| … }
Socket.tcp_server_loop(host=nil, port) {|socket, client_addrinfo| …
}
Socket.unix(path) {|socket| … }
Socket.unix_server_loop(path) {|socket, client_addrinfo| … }

Socket.tcp e$B$He(B Socket.unix e$B$Oe(B
TCPSocket.open e$B$He(B UNIXSocket.open e$B$KBP1~$7$^$9!#e(B

Socket.tcp_server_loop e$B$He(B Socket.unix_server_loop e$B$Oe(B
TCPServer.open, UNIXServer.open e$B$K2C$($F!"e(Baccept e$B$9$k%k!<%We(B
e$B$^$GF~$C$F$$$^$9!#e(B

accept e$B$^$G9T$&$N$O!"e(BTCPServer.open e$BFb$G!“e(Bgetaddrinfo
e$B$,JV$7e(B
e$B$?$9$Y$F$N%”%I%l%9$r07$&$?$a$G$9!#%5!<%P%=%1%C%H$r8F=P85$KJVe(B
e$B$9e(B TCPServer.open e$B$N7A$@$H!“e(BIPv4 e$B$He(B IPv6
e$B$rJ,$1$F07$&$3$H$,e(B
e$B$G$-$^$;$s!#JL!9$K%=%1%C%H$r:n$C$FJ,$1$F07$&$H$$$&$N$,e(B
RFC 4038 e$B$N$A&$a$J$h$&$G$9!#$J$!”>-MhE*$K$O!"0z?t$r3HD%$7e(B
e$B$Fe(B port e$B$rJ#?t;XDj$G$-$k$H$+$7$F$b$$$$$+$b$7$l$^$;$s!#e(B

e$B$"$H!“e(BSocket.unix_server_loop e$B$O4{B8$N%=%1%C%H%U%!%$%k$re(B
(owner e$B$N8!::$r$7$?>e$Ge(B) e$B>C$9$h$&$K$7$F$”$j$^$9!#e(Bone liner
e$B$G%F%9%H$9$k$H$-$K$O$=$C$A$N$[$&$,JXMx$G$9$7!"$^$8$a$K%5!<%Pe(B
e$B$r=q$/$J$i$I$&$;B>$N%5!<%P$,@8$-$F$$$k$+$I$&$+$OJL$N<jCJ$G3Ne(B
e$BG’$7$J$1$l$P$J$i$:!"B>$K%5!<%P$,$$$J$$>u67$G8F$S=P$9$H$9$l$Pe(B
e$B>C$7$A$c$C$FLdBj$J$$$H;W$&$N$G!#e(B

e$B<BAu$Oe(B Ruby e$B$G=q$$$F$"$j$^$9$N$G!"e(Bsocket.rb
e$B$N?7@_$K$J$j$^$9!#e(B

% svn diff --diff-cmd diff -x ‘-u -p’
Index: ext/socket/lib/socket.rb

— ext/socket/lib/socket.rb (revision 0)
+++ ext/socket/lib/socket.rb (revision 0)
@@ -0,0 +1,262 @@
+require ‘socket.so’
+
+class AddrInfo

  • iterates over the list of AddrInfo objects obtained by

AddrInfo.getaddrinfo.

  • Example:

  • AddrInfo.foreach(nil, 80) {|x| p x }

  • #=> #<AddrInfo: 127.0.0.1:80 TCP (:80)>

  • # #<AddrInfo: 127.0.0.1:80 UDP (:80)>

  • # #<AddrInfo: [::1]:80 TCP (:80)>

  • # #<AddrInfo: [::1]:80 UDP (:80)>

  • def self.foreach(nodename, service, family=nil, socktype=nil,
    protocol=nil, flags=nil, &block)
  • AddrInfo.getaddrinfo(nodename, service, family, socktype, protocol,
    flags).each(&block)
  • end
    +end

+class Socket

  • creates a new socket object connected to host:port using TCP.

  • If local_host:local_port is given,

  • the socket is bound to it.

  • 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.

  • Example:

  • Socket.tcp(“www.ruby-lang.org”, 80) {|sock|

  • sock.print “GET / HTTP/1.0\r\n\r\n”

  • sock.close_write

  • print sock.read

  • }

  • def self.tcp(host, port, local_host=nil, local_port=nil) # :yield:
    socket
  • last_error = nil
  • ret = nil
  • 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).each {|ai|
  •  begin
    
  •    sock = self.new(ai.pfamily, ai.socktype, ai.protocol)
    
  •  rescue SystemCallError
    
  •    last_error = $!
    
  •    next
    
  •  end
    
  •  if local_addr_list
    
  •    if local_addr = local_addr_list.find {|local_ai| 
    

local_ai.afamily == ai.afamily }

  •      begin
    
  •        sock.bind(local_addr)
    
  •      rescue SystemCallError
    
  •        last_error = $!
    
  •        next
    
  •      end
    
  •    else
    
  •      next
    
  •    end
    
  •  end
    
  •  begin
    
  •    sock.connect(ai)
    
  •  rescue SystemCallError
    
  •    last_error = $!
    
  •    sock.close
    
  •    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
  • creates a TCP 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.

  • Example:

  • # 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.

  • Socket.tcp_server_loop(16807) {|sock, client_addrinfo|

  • Thread.new {

  • begin

  • IO.copy_stream(sock, sock)

  • ensure

  • sock.close

  • end

  • }

  • }

  • def self.tcp_server_loop(host=nil, port) # :yield: socket,
    client_addrinfo
  • last_error = nil
  • sockets = []
  • AddrInfo.foreach(host, port, nil, :STREAM, nil, Socket::AI_PASSIVE)
    {|ai|
  •  begin
    
  •    s = self.new(ai.pfamily, ai.socktype, ai.protocol)
    
  •  rescue SystemCallError
    
  •    last_error = $!
    
  •    next
    
  •  end
    
  •  sockets << s
    
  •  s.setsockopt(:SOCKET, :REUSEADDR, 1)
    
  •  s.setsockopt(:IPV6, :V6ONLY, 1) if ai.ipv6?
    
  •  begin
    
  •    s.bind(ai)
    
  •  rescue SystemCallError
    
  •    last_error = $!
    
  •    next
    
  •  end
    
  •  begin
    
  •    s.listen(5)
    
  •  rescue SystemCallError
    
  •    last_error = $!
    
  •    next
    
  •  end
    
  • }
  • if sockets.empty?
  •  raise last_error
    
  • end
  • loop {
  •  readable, _, _ = IO.select(sockets)
    
  •  readable.each {|r|
    
  •    begin
    
  •      sock, addr = r.accept_nonblock
    
  •    rescue Errno::EWOULDBLOCK
    
  •      next
    
  •    end
    
  •    yield sock, addr
    
  •  }
    
  • }
  • ensure
  • sockets.each {|s|
  •  s.close if !s.closed?
    
  • }
  • end
  • 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.

  • Example:

  • # 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

  • }

  • def self.unix(path) # :yield: socket
  • addr = AddrInfo.unix(path)
  • sock = self.new(:UNIX, :STREAM, 0)
  • begin
  •  sock.connect(addr)
    
  • ensure
  •  sock.close if $!
    
  • end
  • if block_given?
  •  begin
    
  •    yield sock
    
  •  ensure
    
  •    sock.close if !sock.closed?
    
  •  end
    
  • else
  •  sock
    
  • end
  • end
  • 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.

  • Example:

  • # 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

  • }

  • def self.unix_server_loop(path) # :yield: socket, client_addrinfo
  • begin
  •  st = File.lstat(path)
    
  • rescue Errno::ENOENT
  • end
  • if st && st.socket? && st.owned?
  •  File.unlink path
    
  • end
  • serv = self.new(:UNIX, :STREAM, 0)
  • serv.bind(AddrInfo.unix(path))
  • serv.listen(5)
  • loop {
  •  sock, addr = serv.accept
    
  •  yield sock, addr
    
  • }
  • ensure
  • serv.close if serv && !serv.closed?
  • end

+end
+


#2

In article removed_email_address@domain.invalid,
Tanaka A. removed_email_address@domain.invalid writes:

Socket e$B$r;H$C$F$$$k$H!"@8@.$9$k$H$3$m$,e(B TCPSocket e$BEy$KHf$Y$Fe(B
e$BITJX$J$N$G!"0J2<$N$h$&$J%a%=%C%I$r$D$1$F$b$$$$$G$7$g$&$+!#e(B

e$BFC$KH?1~$O$"$j$^$;$s$,!";d$,M_$7$$$N$GF~$l$F$_$^$7$?!#e(B