Shammer's Philosophy

My private adversaria

バイナリデータの送信

HTTP GET に続き、POST を書こうとしていたら壁にぶち当たった。どうやら ASCII テキストとバイナリデータでは送信方法に違いがあるようだ。まあ、どの言語でもそうか。sbcl の場合は、データの送受信は

  1. socket の作成
  2. 作成した socket から stream を生成
  3. 作成した stream からデータを送信

という流れになるが、stream を生成する時点で送受信するデータが ASCII テキストなのか、バイナリデータなのかを決定しないといけないようだ。ただ、これは裏技的なものがあって、どちらも扱うことができる stream も存在するようだ。ただ、若干パフォーマンスは下がるらしい。まあ、これは致し方ないな。具体的には、socket-make-stream の引数に :element-type :default を指定すれば兼用の stream になる。
そして、送受信で使用する関数も異なる。ascii の場合は、princ で送信することができるが、バイナリの場合はこれではダメなようだ。write-byte を使用することで正しく送信できているようだった。試した送信の実装は以下。

(defun send-http-postdata (stream post-data)
  (format t "post-data is ~A~%" post-data)
  (when (typep post-data 'array)
    (dolist (i (coerce post-data 'list))
      (if (typep i '(unsigned-byte 8)) ; binary data
	  (write-byte i stream)
	; post-data is string
	(write-byte (aref (string-to-octets (string i) :external-format :UTF-8) 0) stream)))))

とりあえず、ASCII の範囲の文字なら正しく送信できた。でも日本語とかマルチバイト文字では試していない。その辺も考慮して書き換えが必要になるだろう。