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 [email protected]$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
[email protected]_$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
+