-
Notifications
You must be signed in to change notification settings - Fork 18
/
server_evented.rb
94 lines (77 loc) · 2.4 KB
/
server_evented.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
82
83
84
85
86
87
88
89
90
91
92
93
94
require 'socket'
require './lib/evented'
Thread.abort_on_exception = true
puts "Starting server on port 2000 with pid #{Process.pid}"
server = TCPServer.open(2000)
$client_handlers = {}
$messages = []
def create_client_handler(nickname, socket)
Fiber.new do
last_write = Time.now
loop do
state = Fiber.yield
if state == :readable
# Read a message from the socket
incoming = read_line_from(socket)
# If the message is nil the client disconnected
if incoming.nil?
puts "Disconnected #{nickname}"
# Remove the client from storage
$client_handlers.delete(socket)
# Exit fiber by breaking the loop
break
end
# All good, add it to the list to write
$messages.push(
:time => Time.now,
:nickname => nickname,
:text => incoming
)
elsif state == :writable
# Write messages to the socket
get_messages_to_send(last_write, nickname, $messages).each do |message|
socket.puts "#{message[:nickname]}: #{message[:text]}"
end
last_write = Time.now
end
end
end
end
# Our event loop
loop do
# Step 1: See if we have any new incoming connections
begin
socket = server.accept_nonblock
nickname = socket.gets.chomp
$client_handlers[socket] = create_client_handler(nickname, socket)
puts "Accepted connection from #{nickname}"
rescue IO::WaitReadable, Errno::EINTR
# No new incoming connections at the moment
end
# Step 2: Ask the OS to inform us when a connection is ready, wait for 10ms for this to happen
# A "real" event loop system would register interest instead of calling this every tick
readable, writable = IO.select(
$client_handlers.keys,
$client_handlers.keys,
$client_handlers.keys,
0.01
)
# Step 3: See if any of our connections are readable and trigger the client
if readable
readable.each do |ready_socket|
# Get the client from storage
client = $client_handlers[ready_socket]
client.resume(:readable)
end
end
# Step 4: See if any of our connections are writable and trigger the client
if writable
writable.each do |ready_socket|
# Get the client from storage
client = $client_handlers[ready_socket]
next unless client
client.resume(:writable)
end
end
# Done! Onto the next tick of the event loop
end