如何从单个线程检查另一端是否已关闭我的套接字流?

时间:2012-07-22 00:20:16

标签: lisp common-lisp single-threaded usocket

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在没有输入时阻塞,这将导致线程等到前两个客户端都发送消息。

我想到的解决方案,但经过一些坚定的谷歌搜索后没有找到(按优先级降序排列):

  1. listen等效的函数,如果对目标的流上的读取将返回t标记,则返回end-of-file(用这个名义函数替换上面的listen会让其余部分按照书面形式工作)
  2. 一个等效于wait-for-input的函数,它返回一个导致它跳转的已关闭套接字列表。 (在这种情况下,我可以遍历已关闭的套接字列表,检查它们是否实际上已使用建议的read技术关闭,并根据需要关闭/弹出它们
  3. 一个等效于wait-for-input的函数,它返回导致它跳转的第一个闭合套接字。 (作为#2,但速度较慢,因为每次迭代最多可以修剪一个非活动连接)
  4. 跟踪我从每个套接字连接收到输入后的时间,并在一段时间不活动后关闭它们。 (无论如何我可能都想这样做,但是只是这可能会使一堆死连接远远超过必要的时间)
  5. 从即时超时的流尝试read-char的函数,如果遇到t则返回:eof,并返回unread-char其他任何内容(返回{{ 1}}在超时或未读之后)。 (这是最后的手段,因为它似乎很容易以非显而易见但致命的方式打破)
  6. 另外,如果我以错误的方式思考这个问题,请指出这一点。

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))))

应该做得很好。