Goal
- gen_tcpの簡単なサンプルを実装する
Dev-Environment
- OS: Windows8.1
- Erlang: Eshell V7.3, OTP-Version 18.3
- Elixir: v1.2.3
Using gen_tcp module in elixir
gen_tcpを使ってみたいと思います。
pingと文字列を渡して、pongと文字列を返すだけの簡単なサンプルの実装になります。
(ローカルホスト内で行っています)
pingと文字列を渡して、pongと文字列を返すだけの簡単なサンプルの実装になります。
(ローカルホスト内で行っています)
gen_tcp module sample
みんな大好き(?)tcpを使ってサーバとクライアントで通信を取ってみましょう。
全体の処理における流れは以下のようになります。
全体の処理における流れは以下のようになります。
- [server]:gen_tcp.listen/2を呼び出し、1234ポートで待ち受けする。(メッセージなどの仕様に関する取り決めはここで設定)
- [server]:gen_tcp.accept/1を呼び出す。ここでクライアントが接続するまで待機する。
- [client]:gen_tcp.connect/3でサーバへ接続する。
- [server]:gen_tcp.close/1でソケットを閉じる。(別になくてもいい。ソケットの制御プロセスが死ぬとソケットも閉じるため)
- [client]:gen_tcp.send/2でサーバ側へデータを送る。(送信データはバイナリ)
- [server]クライアント側からの送信データを受信し処理する。
- [server]:gen_tcp.send/2でクライアント側へデータを送る。
- [client]サーバ側からの返答を受信し処理する。
- [client]:gen_tcp.close/1でソケットを閉じる。
ざっと処理を書いていくとこんな感じになります。
ちなみに、データの送受信はプロセスと同じやり方で送受信できるので、かなり分かりやすいです。
ちなみに、データの送受信はプロセスと同じやり方で送受信できるので、かなり分かりやすいです。
サーバの実装は以下のようになります。
File: simple_tcp_server.ex
defmodule SimpleTcpServer do
def start_server do
{:ok, listen} = :gen_tcp.listen(1234, [:binary, {:packet, 4},
{:reuseaddr, :true},
{:active, :true}])
{:ok, _socket} = :gen_tcp.accept(listen)
:gen_tcp.close(listen)
loop
end
defp loop do
receive do
{:tcp, socket, bin} ->
:io.format("Server receive binary = ~p~n", [bin])
str = :erlang.binary_to_term(bin)
:io.format("Server (unpacked) ~p~n", [str])
:gen_tcp.send(socket, :erlang.term_to_binary('pong'))
loop
{:tcp_closed, _socket} ->
IO.puts "Server socket closed"
end
end
end
次は、クライアントの実装になります。
File: simple_tcp_client.ex
defmodule SimpleTcpClient do
def ping do
{:ok, socket} = :gen_tcp.connect(String.to_char_list("localhost"), 1234,
[:binary, {:packet, 4}])
:ok = :gen_tcp.send(socket,
String.to_char_list("ping") |> :erlang.term_to_binary)
receive do
{:tcp, socket, bin} ->
:io.format("Client received binary = ~p~n", [bin])
value = :erlang.binary_to_term(bin)
:io.format("Client result = ~p~n", [value])
:gen_tcp.close(socket)
end
end
end
注意点としては、バイナリでやり取りしているのでバイナリへ変換していることと、
stringではなく、char_listで送っている点です。
stringではなく、char_listで送っている点です。
実行してみましょう。ターミナルが二つ必要になります。
Example:
- ターミナル1(Server)
iex> SimpleTcpServer.start_server
...
# ターミナル2を実行すると下記のように出力される
Server receive binary = <<131,107,0,4,112,105,110,103>>
Server unpacked = "ping"
Server socket closed
:ok
- ターミナル2(Client)
iex> SimpleTcpClient.ping
Client received binary = <<131,107,0,4,112,111,110,103>>
Client result = "pong"
:ok
Note:
gen_tcp使うなら、Erlangのinetモジュールも覚えておいて損はないと思う。
TCP/IPプロトコルのアクセスを行えるモジュールのようなので、アクセス元のIPアドレスとか色々情報引っ張れる。