Skip to content

Instantly share code, notes, and snippets.

@tank-bohr
Last active April 16, 2019 08:49
Show Gist options
  • Save tank-bohr/4e8e3a1f749372a1fd03d45d8d805134 to your computer and use it in GitHub Desktop.
Save tank-bohr/4e8e3a1f749372a1fd03d45d8d805134 to your computer and use it in GitHub Desktop.
require 'pp'
require 'socket'
puts "My pid is [#{Process.pid}]"
socket = Socket.new(:INET, :STREAM)
socket.setsockopt(:SOCKET, :REUSEADDR, true)
sockaddr = Socket.pack_sockaddr_in(2200, '127.0.0.1')
socket.bind(sockaddr)
socket.listen(_backlog = 3)
to_read, to_write = IO.pipe
Signal.trap('TERM') do
to_write.print 'term'
end
puts 'Listening...'
clients = []
run = true
while run
ready_to_read, ready_to_write, errors = select([socket, to_read] + clients)
if ready_to_read
ready_to_read.each do |item|
if item == socket
client_socket, client_addrinfo = socket.accept_nonblock
clients << client_socket
client_socket.puts '> Hello'
elsif clients.include?(item)
input = item.read_nonblock(4096).chomp
if input == 'quit'
item.puts '> Bye'
item.close
clients.delete(item)
else
item.puts "> #{input.reverse}"
end
elsif item == to_read
puts "Signal received: #{item.read_nonblock(8)}"
run = false
end
end
end
end
puts 'Done'
require 'pp'
require 'socket'
puts "My pid is [#{Process.pid}]"
socket = Socket.new(:INET, :STREAM)
socket.setsockopt(:SOCKET, :REUSEADDR, true)
sockaddr = Socket.pack_sockaddr_in(2200, '127.0.0.1')
socket.bind(sockaddr)
socket.listen(_backlog = 5)
workers_count = 3
children = []
children_pipes = []
role = :master
workers_count.times do
to_read, to_write = IO.pipe
fork_result = fork
if fork_result.nil?
run = true
role = :worker
while run do
ready, _, _ = select([to_read, socket])
ready.each do |item|
if item == to_read
to_read.read_nonblock(8)
puts "[#{Process.pid}] Bye..."
run = false
elsif item == socket
client_socket, client_addrinfo = socket.accept
client_socket.puts "[#{Process.pid}] Hello"
input = client_socket.gets.chomp
client_socket.puts "> #{input.reverse}"
client_socket.puts '> Bye'
client_socket.close
end
end
end
else
children << fork_result
children_pipes << to_write
end
break if role == :worker
end
if role == :master
pp children
puts 'Press Enter'
gets
children_pipes.each do |to_write|
to_write.print 'quit'
end
puts 'Done'
end
require 'socket'
class ListeningSocket
LOCALHOST = '127.0.0.1'
attr_reader :socket, :backlog
alias_method :fd, :socket
def initialize(port:, backlog: 3)
@socket = Socket.new(:INET, :STREAM)
.tap { |s| s.setsockopt(:SOCKET, :REUSEADDR, true) }
.tap { |s| s.bind(Socket.pack_sockaddr_in(port, LOCALHOST)) }
@backlog = backlog
end
def listen
socket.listen(backlog)
end
def accept
socket.accept
end
end
class Worker
attr_reader :listening_socket, :pipe, :number, :client
def initialize(listening_socket:, pipe:, number:, client:)
@listening_socket = listening_socket
@pipe = pipe
@number = number
@client = client
end
def run
@run = true
while run?
run_select
end
end
def stop
@run = false
end
private
def run_select
ready, _, _ = select([listening_socket.fd, pipe])
ready.each(&method(:process_fd))
end
def process_fd(fd)
if fd == listening_socket.fd
process_request
elsif fd == pipe
process_cmd
end
end
def process_request
socket, addrinfo = listening_socket.accept
client.process_request(socket: socket, addrinfo: addrinfo, worker: self)
end
def process_cmd
cmd = pipe.read_nonblock(8)
stop if cmd == 'stop'
end
def run?
@run
end
end
class WrokersPool
attr_reader :listening_socket, :workers_count, :worker_pids, :pipes, :client
def initialize(listening_socket:, client:, workers_count: 3)
@listening_socket = listening_socket
@client = client
@workers_count = workers_count
@worker_pids = []
@pipes = []
end
def run
listening_socket.listen
workers_count.times(&method(:spawn_worker))
end
def wait
puts 'Press Enter'
gets
stop_workers
puts 'Done'
end
private
def spawn_worker(number)
io_read, io_write = IO.pipe
worker_pids << fork do
Worker.new(listening_socket: listening_socket, client: client,
pipe: io_read, number: number).run
end
pipes << io_write
end
def stop_workers
pipes.each { |io| io.print 'stop' }
end
end
class Client
def process_request(socket:, addrinfo:, worker:)
socket.puts "[#{worker.number}] Hello"
input = socket.gets.chomp
socket.puts "> #{input.reverse}"
socket.puts '> Bye'
socket.close
end
end
WrokersPool.new(
listening_socket: ListeningSocket.new(port: 2200),
client: Client.new
)
.tap(&:run)
.tap(&:wait)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment