usocket FAQ表明我应该这样做的方法是从socket-stream
读取并检查end-of-file
结果。这适用于我每个套接字有一个活动线程的情况,但它似乎不满足我试图在同一个线程中服务多个套接字的情况。
考虑类似
的内容(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*)))
对于本练习,假设我实际上有四个客户端连接在那里。似乎从一个线程中提供服务的方式就像是
(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
会立即返回,即使没有其他套接字已准备好消息(尽管它似乎无法指定哪个套接字导致它返回)。
在没有客户端发言的情况下,第三个客户端断开连接,似乎没有一种好方法可以找到并关闭该特定套接字连接。我必须按顺序读取它们,除了因为read
在没有输入时阻塞,这将导致线程等到前两个客户端都发送消息。
我想到的解决方案,但经过一些坚定的谷歌搜索后没有找到(按优先级降序排列):
listen
等效的函数,如果对目标的流上的读取将返回t
标记,则返回end-of-file
。 (用这个名义函数替换上面的listen
会让其余部分按照书面形式工作) wait-for-input
的函数,它返回一个导致它跳转的已关闭套接字列表。 (在这种情况下,我可以遍历已关闭的套接字列表,检查它们是否实际上已使用建议的read
技术关闭,并根据需要关闭/弹出它们 wait-for-input
的函数,它返回导致它跳转的第一个闭合套接字。 (作为#2,但速度较慢,因为每次迭代最多可以修剪一个非活动连接) read-char
的函数,如果遇到t
则返回:eof
,并返回unread-char
其他任何内容(返回{{ 1}}在超时或未读之后)。 (这是最后的手段,因为它似乎很容易以非显而易见但致命的方式打破)。另外,如果我以错误的方式思考这个问题,请指出这一点。
答案 0 :(得分:3)
事实证明,上面提到的选项2中提到的东西存在。
wait-for-input
默认为了内存管理目的而返回跟踪连接的完整列表(据报道有人非常担心cons
结果的新列表,但它有一个&key
告诉它只返回有话要说的连接的参数。
(wait-for-input (list conn1 conn2 conn3 conn4) :ready-only t)
是我在那里寻找的。这将返回所有就绪连接,而不仅仅是那些将发出end-of-file
信号的连接,因此循环仍然需要处理这两种情况。像
(loop for sock in (wait-for-input *client-connections* :ready-only t)
for stream = (socket-stream sock)
do (let ((line (read-line stream nil :eof)))
(if (eq line :eof)
(progn (delete sock *client-connections*)
(socket-close sock))
(handle sock line))))
应该做得很好。