class DRb::DRbTCPSocket
def self.getservername
def self.getservername host = Socket::gethostname begin Socket::getaddrinfo(host, nil, Socket::AF_UNSPEC, Socket::SOCK_STREAM, 0, Socket::AI_PASSIVE)[0][3] rescue 'localhost' end end
def self.open(uri, config)
recognized protocol. See DRb::DRbServer.new for information on built-in
This can raise DRb::DRbBadScheme or DRb::DRbBadURI if +uri+ is not for a
+config+.
Open a client connection to +uri+ (DRb URI string) using configuration
def self.open(uri, config) host, port, = parse_uri(uri) soc = TCPSocket.open(host, port) self.new(uri, soc, config) end
def self.open_server(uri, config)
Open a server listening for connections at +uri+ using
def self.open_server(uri, config) uri = 'druby://:0' unless uri host, port, _ = parse_uri(uri) config = {:tcp_original_host => host}.update(config) if host.size == 0 host = getservername soc = open_server_inaddr_any(host, port) else soc = TCPServer.open(host, port) end port = soc.addr[1] if port == 0 config[:tcp_port] = port uri = "druby://#{host}:#{port}" self.new(uri, soc, config) end
def self.open_server_inaddr_any(host, port)
If +port+ is 0 the first available port is used. IPv4 servers are
For the families available for +host+, returns a TCPServer on +port+.
def self.open_server_inaddr_any(host, port) infos = Socket::getaddrinfo(host, nil, Socket::AF_UNSPEC, Socket::SOCK_STREAM, 0, Socket::AI_PASSIVE) families = Hash[*infos.collect { |af, *_| af }.uniq.zip([]).flatten] return TCPServer.open('0.0.0.0', port) if families.has_key?('AF_INET') return TCPServer.open('::', port) if families.has_key?('AF_INET6') return TCPServer.open(port) # :stopdoc: end
def self.parse_uri(uri)
def self.parse_uri(uri) if /\Adruby:\/\/(.*?):(\d+)(\?(.*))?\z/ =~ uri host = $1 port = $2.to_i option = $4 [host, port, option] else raise(DRbBadScheme, uri) unless uri.start_with?('druby:') raise(DRbBadURI, 'can\'t parse uri:' + uri) end end
def self.uri_option(uri, config)
def self.uri_option(uri, config) host, port, option = parse_uri(uri) return "druby://#{host}:#{port}", option end
def accept
accept a client connection and return a new instance to handle
On the server side, for an instance returned by #open_server,
def accept while true s = accept_or_shutdown return nil unless s break if (@acl ? @acl.allow_socket?(s) : true) s.close end if @config[:tcp_original_host].to_s.size == 0 uri = "druby://#{s.addr[3]}:#{@config[:tcp_port]}" else uri = @uri end self.class.new(uri, s, @config) end
def accept_or_shutdown
def accept_or_shutdown readables, = IO.select([@socket, @shutdown_pipe_r]) if readables.include? @shutdown_pipe_r return nil end @socket.accept end
def alive?
def alive? return false unless @socket if @socket.to_io.wait_readable(0) close return false end true end
def close
returned by #open or by #accept, then it closes this particular
listening for new connections altogether. If this is an instance
If this is an instance returned by #open_server, then this stops
Close the connection.
def close shutdown if @socket @socket.close @socket = nil end close_shutdown_pipe end
def close_shutdown_pipe
def close_shutdown_pipe @shutdown_pipe_w.close @shutdown_pipe_r.close end
def initialize(uri, soc, config={})
+soc+ is the tcp socket we are bound to. +config+ is our
+uri+ is the URI we are connected to.
Create a new DRbTCPSocket instance.
def initialize(uri, soc, config={}) @uri = uri @socket = soc @config = config @acl = config[:tcp_acl] @msg = DRbMessage.new(config) set_sockopt(@socket) @shutdown_pipe_r, @shutdown_pipe_w = IO.pipe end
def peeraddr
Get the address of our TCP peer (the other end of the socket
def peeraddr @socket.peeraddr end
def recv_reply
def recv_reply @msg.recv_reply(stream) end
def recv_request
def recv_request @msg.recv_request(stream) end
def send_reply(succ, result)
def send_reply(succ, result) @msg.send_reply(stream, succ, result) end
def send_request(ref, msg_id, arg, b)
def send_request(ref, msg_id, arg, b) @msg.send_request(stream, ref, msg_id, arg, b) end
def set_sockopt(soc) # :nodoc:
def set_sockopt(soc) # :nodoc: soc.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) rescue IOError, Errno::ECONNRESET, Errno::EINVAL # closed/shutdown socket, ignore error end
def shutdown
def shutdown @shutdown_pipe_w.close end
def stream; @socket; end
def stream; @socket; end