Skip to content

Instantly share code, notes, and snippets.

@niku
Last active March 26, 2020 01:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save niku/85a247be3d72fd7fa58d999f0903429f to your computer and use it in GitHub Desktop.
Save niku/85a247be3d72fd7fa58d999f0903429f to your computer and use it in GitHub Desktop.
# 比較用
# https://elixir-lang.org/getting-started/mix-otp/task-and-gen-tcp.html
defmodule KVServer do
require Logger
@doc """
Starts accepting connections on the given `port`.
"""
def accept(port) do
{:ok, socket} = :gen_tcp.listen(port,
[:binary, packet: :line, active: false, reuseaddr: true])
Logger.info "Accepting connections on port #{port}"
loop_acceptor(socket)
end
defp loop_acceptor(socket) do
{:ok, client} = :gen_tcp.accept(socket)
{:ok, pid} = Task.Supervisor.start_child(KVServer.TaskSupervisor, fn -> serve(client) end)
:ok = :gen_tcp.controlling_process(client, pid)
loop_acceptor(socket)
end
defp serve(socket) do
socket
|> read_line()
|> write_line(socket)
serve(socket)
end
defp read_line(socket) do
{:ok, data} = :gen_tcp.recv(socket, 0)
data
end
defp write_line(line, socket) do
:gen_tcp.send(socket, line)
end
end
# https://elixir-lang.org/getting-started/mix-otp/task-and-gen-tcp.html にある
# Elixir製のEchoサーバーをRubyのRactorを使って書いてみるとこうなるかなという想像。
#
# https://docs.ruby-lang.org/ja/latest/class/TCPServer.html
# にあるTCPServerの例も参考にした。
require "socket"
class EchoServer
def init(port)
@port = port
end
def accept()
socket = TCPServer.open(@port)
addr = socket.addr
addr.shift
printf("server is on %s\n", addr.join(":"))
loop_acceptor(socket)
end
private
def loop_acceptor(socket)
loop do
client = socket.accept
# もしEchoServerをシャットダウンするときに子の接続が全部なくなるまで待つならractorを配列で保持しておかないといけないな
Ractor.new client do |c|
# Ractor#receive しない限りエラーが親に影響を及ぼさないのでエラーハンドリングしなくてもいいや
serve(c)
end
# :gen_tcp.controlling_process(client, pid)
# は下記のとおりErlangVM特有の話だと思うのでRubyでは考慮しなくていいや
# This makes the child process the “controlling process” of the client socket.
# If we didn’t do this, the acceptor would bring down all the clients if it crashed
# because sockets would be tied to the process that accepted them (which is the default behaviour).
end
end
def serve(socket)
loop do
s.write(s.gets)
end
end
end
EchoServer.new(8080).accept()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment