usocketのFAQでは、a から読み取って結果socket-stream
を確認する方法が推奨されています。end-of-file
これは、ソケットごとにアクティブなスレッドが 1 つある場合には機能しますが、同じスレッドで複数のソケットにサービスを提供しようとしている場合には満足できないようです。
次のようなものを検討してください
(defparameter *socket* (socket-listen "127.0.0.1" 123456))
(defparameter *client-connections*
(list (socket-accept *socket*)
(socket-accept *socket*)
(socket-accept *socket*)
(socket-accept *socket*)))
この演習では、実際に 4 つのクライアントが接続していると仮定します。1つのスレッドからそれらを提供する方法は次のようなものです
(wait-for-input *client-connections*)
(loop for sock in *client-connections*
for stream = (socket-stream sock)
when (listen stream)
do (let ((line (read-line stream nil :eof)))
(if (eq line :eof)
(progn (delete sock *client-connections*)
(socket-close sock))
(handle sock line))))
ただし、切断されたソケットは引き続き に戻りnil
、メッセージのないアクティブなソケットからlisten
の試みはブロックされますが、他のソケットにメッセージの準備ができていない場合でも、ミックス内に閉じられたソケットがある場合はすぐに戻ります。 (ただし、どのソケットが返されたかを特定できないようです)。read
wait-for-intput
しばらくクライアントが発言しておらず、3 番目のクライアントが切断された状況では、それを見つけてその特定のソケット接続を閉じる良い方法はないようです。read
入力がないブロックであるため、最初の 2 つのクライアントが両方ともメッセージを送信するまでスレッドが待機することを除いて、それらを順番に読み取る必要があります。
私が念頭に置いているが、いくつかの決定的なグーグル検索の後に見つけられなかった解決策は、次のとおりです(好みの降順):
- ターゲットのストリームの読み取りがマーカー
listen
を返す場合、それ以外の場合はそれと同等の関数が返されます。(上記をこの概念的な関数に置き換えると、残りの部分は書かれたとおりに機能します)t
end-of-file
listen
- それ以外の場合はそれと同等の関数
wait-for-input
は、トリップの原因となった閉じられたソケットのリストを返します。(この場合、閉じたソケットのリストを反復処理し、提案されたread
手法でそれらが実際に閉じられていることを確認し、必要に応じて閉じたりポップしたりできます) - それ以外は同等の関数
wait-for-input
は、トリップの原因となった最初に閉じられたソケットを返します。(#2と同様ですが、反復ごとに非アクティブな接続を最大1つプルーニングするため、遅くなります) - 各ソケット接続から入力を受信してからの経過時間を追跡し、一定期間非アクティブになった後は関係なくそれらを閉じます。(とにかくやりたいと思うかもしれませんが、これだけを行うと、必要以上に多くの切断された接続が保持される可能性があります)
read-char
インスタント タイムアウトでストリームからt
取得しよう:eof
とする関数。(自明ではないが致命的な方法で簡単に破れるように思われるため、これは最後の手段です)。unread-char
nil
また、これについて正確に間違った方法で考えている場合は、それも指摘してください。