Class: Ionian::Server

Inherits:
Object
  • Object
show all
Defined in:
lib/ionian/server.rb

Overview

A convenient wrapper for TCP, UDP, and Unix server sockets.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(**kwargs, &block) ⇒ Server

A convenient wrapper for TCP and Unix server sockets (UDP doesn’t use a server).

Accepts an optional block that is passed to #register_accept_listener. Server opens listening socket on instantiation if this block is provided.

Args:

port:           Port number to listen for clients.
interface:      The address of the network interface to bind to.
                Defaults to all.
protocol:       :tcp, :unix. Default is :tcp.


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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/ionian/server.rb', line 35

def initialize **kwargs, &block
  @accept_listeners = []
  register_accept_listener &block if block_given?
  
  # @interface = kwargs.fetch :interface, nil
  @interface = kwargs.fetch :interface, nil
  @port      = kwargs.fetch :port, nil
  
  # Automatically select UDP for the multicast range. Otherwise default to TCP.
  default_protocol = :tcp
  
  if @interface
    # TODO: This ivar may be incorrect for UDP -- bound interface is not destination.
    default_protocol = :udp  if Ionian::Extension::Socket.multicast? @interface
    default_protocol = :unix if @interface.start_with? '/'
  end
  
  @protocol  = kwargs.fetch :protocol, default_protocol
  
  
  # TODO: Move this to #listen.
  case @protocol
  when :tcp
    @interface ||= '0.0.0.0' # All interfaces.
    
    # Parse host out of "host:port" if specified.
    host_port_ary = @interface.to_s.split ':'
    @interface = host_port_ary[0]
    @port ||= host_port_ary[1]
    
    raise ArgumentError, "Port not specified." unless @port
    @port = @port.to_i
    
    @server = TCPServer.new @interface, @port
    @server.setsockopt ::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, [1].pack('i')
    
  when :udp
    raise ArgumentError, "UDP should be implemented with Ionian::Socket."
    
  when :unix
    raise ArgumentError, "Path not specified." unless @interface
    
    @server = UNIXServer.new @interface
  end
  
  listen if block_given?
end

Instance Attribute Details

#interfaceObject (readonly)

Interface to listen for clients.



13
14
15
# File 'lib/ionian/server.rb', line 13

def interface
  @interface
end

#portObject (readonly)

Port number to listen for clients.



16
17
18
# File 'lib/ionian/server.rb', line 16

def port
  @port
end

#protocolObject (readonly) Also known as: protocol?

Returns a symbol of the type of protocol this socket uses: :tcp, :udp, :unix



20
21
22
# File 'lib/ionian/server.rb', line 20

def protocol
  @protocol
end

Instance Method Details

#closeObject

Shutdown the server socket and stop listening for connections.



107
108
109
110
111
# File 'lib/ionian/server.rb', line 107

def close
  @server.close if @server
  @accept_thread.kill if @accept_thread
  @accept_thread = nil
end

#closed?Boolean

Returns true if the server listener socket is closed.

Returns:

  • (Boolean)


114
115
116
# File 'lib/ionian/server.rb', line 114

def closed?
  @server.closed?
end

#listen(&block) ⇒ Object

Starts the socket server listening for connections. Blocks registered with #register_accept_listener will be run when a connection is accepted.



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/ionian/server.rb', line 86

def listen &block
  register_accept_listener &block if block_given?
  
  @accept_thread ||= Thread.new do
    loop do
      # Package in an Ionian::Socket
      begin
        client = Ionian::Socket.new @server.accept
        @accept_listeners.each { |listener| listener.call client }
      rescue Errno::EBADF
        # This ignores the connection if the client closed it before it
        # could be accepted.
      rescue IOError
        # This ignores the connection if the client closed it before it
        # could be accepted.
      end
    end
  end
end

#register_accept_listener(&block) ⇒ Object Also known as: on_accept

Register a block to be run when server accepts a client connection. The connected client is passed to the block as an Ionain::Client.



120
121
122
123
# File 'lib/ionian/server.rb', line 120

def register_accept_listener &block
  @accept_listeners << block unless @accept_listeners.include? block
  block
end

#unregister_accept_listener(proc) ⇒ Object

Unregisters a socket accept notifier block.



128
129
130
131
# File 'lib/ionian/server.rb', line 128

def unregister_accept_listener proc
  @accept_listeners.delete_if { |o| o == proc }
  proc
end