summaryrefslogtreecommitdiff
path: root/ext/socket/lib/socket.rb
diff options
context:
space:
mode:
authorakr <akr@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2009-02-02 23:36:43 +0000
committerakr <akr@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2009-02-02 23:36:43 +0000
commit13a468db1710405ccdff3358abf7d2aea396d9b5 (patch)
treec808a0c8e613f720c6c46341d30b5c4b6334fdb4 /ext/socket/lib/socket.rb
parent2df1053c1db22aa5693d4982814e79fcb040c5ab (diff)
* ext/socket/lib/socket.rb (Socket.tcp_server_sockets): extracted from
Socket.tcp_server_loop. (Socket.accept_loop): ditto. (Socket.unix_server_socket): extracted from Socket.unix_server_loop. (Socket.unix_server_loop): use Socket.accept_loop. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@21992 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'ext/socket/lib/socket.rb')
-rw-r--r--ext/socket/lib/socket.rb139
1 files changed, 96 insertions, 43 deletions
diff --git a/ext/socket/lib/socket.rb b/ext/socket/lib/socket.rb
index dc9cda97763..577abca0104 100644
--- a/ext/socket/lib/socket.rb
+++ b/ext/socket/lib/socket.rb
@@ -161,7 +161,7 @@ end
class Socket
# enable the socket option IPV6_V6ONLY if IPV6_V6ONLY is available.
def ipv6only!
- if Socket.const_defined?(:IPV6_V6ONLY)
+ if defined? Socket::IPV6_V6ONLY
self.setsockopt(:IPV6, :V6ONLY, 1)
end
end
@@ -226,6 +226,71 @@ class Socket
end
end
+ # creates TCP server sockets for _host_ and _port_.
+ # _host_ is optional.
+ #
+ # It returns an array of listening sockets.
+ #
+ # # 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>
+ #
+ def self.tcp_server_sockets(host=nil, port)
+ last_error = nil
+ sockets = []
+ 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
+ sockets
+ ensure
+ if $!
+ sockets.each {|s|
+ s.close if !s.closed?
+ }
+ end
+ end
+
+ # 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.
+ #
+ 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 Errno::EWOULDBLOCK
+ next
+ end
+ yield sock, addr
+ }
+ }
+ 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.
#
@@ -267,36 +332,15 @@ class Socket
# }
# }
#
- 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 = ai.listen
- rescue SystemCallError
- last_error = $!
- next
- end
- sockets << s
- }
- 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
- }
- }
+ def self.tcp_server_loop(host=nil, port, &b) # :yield: socket, client_addrinfo
+ sockets = tcp_server_sockets(host, port)
+ accept_loop(sockets, &b)
ensure
- sockets.each {|s|
- s.close if !s.closed?
- }
+ if sockets
+ sockets.each {|s|
+ s.close if !s.closed?
+ }
+ end
end
# creates a new socket connected to path using UNIX socket socket.
@@ -328,6 +372,25 @@ class Socket
end
end
+ # creates UNIX server sockets on _path_
+ #
+ # It returns a listening socket.
+ #
+ # socket = Socket.unix_server_socket("/tmp/s")
+ # p socket #=> #<Socket:fd 3>
+ # p socket.local_address #=> #<AddrInfo: /tmp/s SOCK_STREAM>
+ #
+ def self.unix_server_socket(path)
+ begin
+ st = File.lstat(path)
+ rescue Errno::ENOENT
+ end
+ if st && st.socket? && st.owned?
+ File.unlink path
+ end
+ AddrInfo.unix(path).listen
+ end
+
# creates a UNIX socket server on _path_.
# It calls the block for each socket accepted.
#
@@ -352,19 +415,9 @@ class Socket
# 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 = AddrInfo.unix(path).listen
- loop {
- sock, addr = serv.accept
- yield sock, addr
- }
+ def self.unix_server_loop(path, &b) # :yield: socket, client_addrinfo
+ serv = unix_server_socket(path)
+ accept_loop(serv, &b)
ensure
serv.close if serv && !serv.closed?
end