私はEmacsユーザーでEmacs Lispを毎日のように書いている。Lispは好きな言語だと思っているけれど、実際にはCommon LispもSchemeもそれほど使う訳じゃない。Clojureは少しだけ使う。そんなにわかLisperだが、常々もっと知りたいとは思っている。今回はその一環としてSchemeでTCPエコーサーバーを実装する。
Lispは言語仕様が文書化されているものもあれば、されていないものもある。SchemeやCommon Lispは文書化されている。Emacs LispやClojureは、公式のドキュメントはあるが、これは言語仕様が文書化されているのかと言われると微妙な所だ。ただ、これらの文書も結構分厚い。
SchemeはLisp方言の1つで、言語仕様がとても小さくシンプルだ。Schemeは言語仕様が文書化されていて、RnRSという名前でまとめられている。R5RSがよく使用されている。その次のバージョンとしてまとめられたR6RSは言語仕様が大きくなり、世のschemer達から、あまり受け入れられない結果となったらしい。その結果としてR5RSの流れからR7RSがまとめられた。こういう経緯を知ると面白いなと感じる。
これまでもSchemeを使って色々と作ってみたいと思っていた。こういう時にエコーサーバーを書く事がよくある。エコーサーバーは、何かを受信すると、その受信したデータをそのまま送信元に送り返すサーバーの事だ。
Schemeの処理系はGauche、GNU Guile、DrRacketなど複数存在する。それぞれ追加されている機能など特徴が異なる。Scheme処理系に特にこだわりはないのだけれど今回はGaucheを使う。ちなみにGaucheはScheme処理系の中でも人気のある方だ。
とりあえず、Gaucheをインストールする。
brew install gauche
次にエコーサーバーを実装する。
(use gauche.net)
(define soc '())
(set! soc (make-server-socket
(make <sockaddr-in>
:host "localhost"
:port 8000)))
(unwind-protect
(while #t (let* ((conn (socket-accept soc)))
(print "connect")
(unwind-protect
(write-string
(read-block 1024 (socket-input-port conn :buffering :modest))
(socket-output-port conn :buffering :none))
(socket-close conn))))
(socket-close soc))
(display "finished.")
Gaucheの実行にはgoshというコマンドを使用する。起動するとTCPの8000番のポートを開く。
gosh echo.scm
例えば次のようにメッセージを送信すると、同じメッセージを返して切断される。
>>> from socket import *
from socket import *
>>> soc = socket()
soc = socket()
>>> soc.connect(("localhost", 8000))
>>> soc.send(b"ok") # ここで送信する
2
>>> soc.recv(100) # ここで受信する
soc.recv(100)
b'ok'
>>> soc.close()
>>>