« ^ »

LispでTCPエコーサーバーを書く

所要時間: 約 2分

私は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()
>>>