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

TinyTCPService implements a line-based, call and response protocol, where every incoming message must be a newline-terminated (“n”) String, and for every received message the service responds with a newline-terminated String. been set).

If you need more complex objects to be sent over the wire, consider something like JSON.

NOTE: if you’re running a TinyTCPService and a client of your system violates your communication protocol, you should raise an instance of TinyTCPService::BadClient, and the TinyTCPService instance will take care of safely removing the client.

Defined Under Namespace

Classes: BadClient

Instance Method Summary collapse

Constructor Details

#initialize(port) ⇒ TinyTCPService

Returns a new instance of TinyTCPService.



25
26
27
28
29
30
31
32
33
34
# File 'lib/tiny_tcp_service.rb', line 25

def initialize(port)
  @port = port

  @server = TCPServer.new(port)
  @clients = []
  @running = false

  @msg_handler = nil
  @error_handlers = {}
end

Instance Method Details

#_remove_client!(c) ⇒ Object



64
65
66
67
# File 'lib/tiny_tcp_service.rb', line 64

def _remove_client!(c)
  @clients.delete(c)
  c.close if c && !c.closed?
end

#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’



50
51
52
# File 'lib/tiny_tcp_service.rb', line 50

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

#msg_handler=(h) ⇒ Object

h - some object that responds to #call



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

def msg_handler=(h)
  @msg_handler = h
end

#num_clientsObject

returns the number of connected clients



60
61
62
# File 'lib/tiny_tcp_service.rb', line 60

def num_clients
  @clients.length
end

#remove_error_handler(klass) ⇒ Object

remove the error handler associated with klass



55
56
57
# File 'lib/tiny_tcp_service.rb', line 55

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

#running?Boolean

returns true if the server is running false otherwise

Returns:

  • (Boolean)


43
44
45
# File 'lib/tiny_tcp_service.rb', line 43

def running?
  @running
end

#start!Object

starts the server



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/tiny_tcp_service.rb', line 70

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| _remove_client!(c) 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 |c|
        begin
          c.puts(@msg_handler&.call(c.gets.chomp))
        rescue TinyTCPService::BadClient => e
          _remove_client!(c)
        rescue => e
          handler = @error_handlers[e.class]

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

      errored&.each do |c|
        _remove_client!(c)
      end
    end
  end
end

#stop!Object

stops the server gracefully



116
117
118
# File 'lib/tiny_tcp_service.rb', line 116

def stop!
  @running = false
end