Class: TinyTCPService

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

Overview

usage:

s = TinyTCPService.new(
  1234,
  ->(m) { puts m }
)

s.start!     # everything runs in background threads
s.stop!      # gracefully shutdown the server

NOTE: the msg_handler does not need to be a proc/lambda, it just needs to be an object that responds_to?(:call), and accepts a single message object. however, if your msg_handler is simple enough to fit into something as tiny as a proc then more power to you.

Instance Method Summary collapse

Constructor Details

#initialize(port, msg_handler) ⇒ TinyTCPService

Returns a new instance of TinyTCPService.



17
18
19
20
21
22
23
24
25
# File 'lib/tiny_tcp_service.rb', line 17

def initialize(port, msg_handler)
  @port = port
  @msg_handler = msg_handler

  @server = TCPServer.new(port)
  @clients = []
  @running = false
  @error_handlers = {}
end

Instance Method Details

#add_error_handler(klass, block) ⇒ Object

add the error handler and block for the specified class

you can assume that the local variable name of the error will be ‘e’



36
37
38
# File 'lib/tiny_tcp_service.rb', line 36

def add_error_handler(klass, block)
  @error_handlers[klass] = block
end

#num_clientsObject

returns the number of connected clients



46
47
48
# File 'lib/tiny_tcp_service.rb', line 46

def num_clients
  @clients.length
end

#remove_error_handler(klass) ⇒ Object

remove the error handler associated with klass



41
42
43
# File 'lib/tiny_tcp_service.rb', line 41

def remove_error_handler(klass)
  @error_handlers.delete(klass)
end

#running?Boolean

returns true if the server is running false otherwise

Returns:

  • (Boolean)


29
30
31
# File 'lib/tiny_tcp_service.rb', line 29

def running?
  @running
end

#start!Object

starts the server



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
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/tiny_tcp_service.rb', line 51

def start!
  return if running?
  @running = true

  # client accept thread
  Thread.new do |t|
    loop do
      break unless running?
      @clients << @server.accept
    end

    @clients.each{|c| c.close if c && !c.closed? }
    @server.close
  end

  # service thread
  Thread.new do |t|
    loop do
      break unless running?

      readable, _, errored = IO.select(@clients, nil, @clients, 1)
      readable&.each do |client|
        begin
          @msg_handler.call(client.gets.chomp)
        rescue => e
          handler = @error_handlers[e.class]

          if handler
            handler.call(e)
          else
            stop!
            raise e unless handler
          end
        end
      end

      errored&.each do |client|
        @clients.delete(client)
        client.close if client && !client.closed?
      end
    end
  end
end

#stop!Object

stops the server gracefully



96
97
98
# File 'lib/tiny_tcp_service.rb', line 96

def stop!
  @running = false
end