Working with TCP Sockets 読書メモ 目次
Prefork
- Preforkパターンでは、接続が来る度にforkするのではなく、サーバ起動時にプロセスをまとめて用意する
- メインのサーバプロセスがlistenソケットを作成する
- メインのサーバプロセスが一群の子プロセスをforkする
- それぞれの子プロセスが共有されたソケットから接続を受け取って、独立して処理する
- メインのサーバプロセスは子プロセスを監視する
- メインのサーバプロセス自身は接続を受け付けない点に注意
実装
require 'socket'
require_relative 'command_handler'
module FTP
class Preforking
CRLF = "\r\n"
CONCURRENCY = 4
def initialize(port = 21)
@control_socket = TCPServer.new(port)
trap(:INT) { exit }
end
def gets
@client.gets(CRLF)
end
def respond(message)
@client.write(message)
@client.write(CRLF)
end
def run
child_pids = []
CONCURRENCY.times do
child_pids << spawn_child
end
trap(:INT) {
child_pids.each do |cpid|
begin
Process.kill(:INT, cpid)
rescue Errno::ESRCH
# do nothing
end
end
exit
}
loop do
pid = Process.wait
$stderr.puts "Process #{pid} quit unexpectedly"
child_pids.delete(pid)
child_pids << spawn_child
end
end
def spawn_child
fork do
loop do
@client = @control_socket.accept
respond "220 OHAI"
handler = CommandHandler.new(self)
loop do
request = gets
if request
respond handler.handle(request)
else
@client.close
break
end
end
end
end
end
end
end
server = FTP::Preforking.new(4481)
server.run
- 親プロセスが終了する際は、必ず子プロセスも合わせて終了するように注意する
考察
- 利点:プロセス数を制限できる、(スレッドに比べて)プロセス単位で分離されている
- 欠点:(スレッドに比べて)メモリ消費量が多い