目次
はじめてのクライアント/サーバ
サーバ
- 以下は、シンプルなNoSQLのクライアントとサーバの実装例
# cloud_hash/server.rb
require 'socket'
module CloudHash
class Server
def initialize(port)
# 基礎になるサーバソケットを作成する
@server = TCPServer.new(port)
puts "Listening on port #{@server.local_address.ip_port}"
@storage = {}
end
def start
Socket.accept_loop(@server) do |connection|
handle(connection)
connection.close
end
end
def handle(connection)
# EOFまでをconnectionから読み取り
request = connection.read
# ハッシュを処理した結果を返す
connection.write process(request)
end
# サポートされている操作は以下
# SET key value
# GET key
def process(request)
command, key, value = request.split
case command.upcase
when 'GET'
@storage[key]
when 'SET'
@storage[key] = value
end
end
end
end
server = CloudHash::Server.new(4481)
server.start
# cloud_hash/client.rb
require 'socket'
module CloudHash
class Client
class << self
attr_accessor :host, :port
end
def self.get(key)
request "GET #{key}"
end
def self.set(key, value)
request "SET #{key} #{value}"
end
def self.request(string)
# 処理のためのconnectionを作成
@client = TCPSocket.new(host, port)
@client.write(string)
# リクエストの書き込み後にEOFを送信する
@client.close_write
# レスポンスを受け取ってEOFまで読み込む
@client.read
end
end
end
CloudHash::Client.host = 'localhost'
CloudHash::Client.port = 4481
puts CloudHash::Client.set 'prez', 'obama'
puts CloudHash::Client.get 'prez'
puts CloudHash::Client.get 'vp'
- 現在のCloudHashには欠陥もある
- そのうちの1つは、クライアントがリクエストの送信の度に接続する必要があること
- サーバに並列性(concurrency)を導入する必要がある
ソケットのオプション
- ソケットの挙動を細かくカスタマイズするにはオプションを使用する
SO_TYPE
require 'socket'
socket = TCPSocket.new('google.com', 80)
# Socket::Optionインスタンスを取得
opt = socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_TYPE)
# Optionの種類を調べる
p opt.int == Socket::SOCK_STREAM #=> true
p opt.int == Socket::SOCK_DGRAM #=> false
getsockopt
を実行するとSocket::Option
のインスタンスが取得できる
SocketOption#int
を実行すると、オプションの値(整数)が取得できる
SO_REUSE_ADDR
- 全ての サーバはこのオプションを設定すべき
SO_REUSE_ADDR
オプションを使うと、同一のローカルアドレスの使い回しができる
- 条件は、サーバがTCPの
TIME_WAIT
という状態であること
TIME_WAITとは
close
を実行すると、ソケットはTIME_WAIT
状態になる
- ソケットはバッファー内で待ち状態(pending)になっている
- デフォルトでは、TIME_WAIT状態のソケットと同じアドレスは使えない
SO_REUSE_ADDR
を設定するとこの問題が発生しなくなる
require 'socket'
server = TCPServer.new('localhost', 4481)
server.setsockopt(:SOCKET, :REUSEADDR, true)
p server.getsockopt(:SOCKET, :REUSEADDR) #=> true
TCPServer.new
やSocket.tcp_server_loop
などではこのオプションがデフォルトで有効化されている
本章で扱ったシステムコール
- setsockopt(2)
- getsockopt(2)
コメントをどうぞ