3

usocket FAQ建议我应该这样做的方式是从 a 中读取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))))

除了这不起作用,因为断开连接的套接字仍然返回nillisten,并且尝试read从没有消息的活动套接字将阻塞但当 wait-for-intput混合中有一个关闭的套接字时立即返回,即使没有其他套接字准备好消息(尽管它似乎没有指定哪些套接字导致它返回)。

在一段时间内没有客户端说话并且第三个客户端断开连接的情况下,似乎没有一种很好的方法可以找出并关闭该特定套接字连接。我必须按顺序阅读它们,除了由于read没有输入的阻塞,这将导致线程等待前两个客户端都发送消息。

我想到的解决方案是(按偏好降序排列),但经过一番确定的谷歌搜索后仍未找到:

  1. 如果对目标流的读取将返回标记,则listen返回与该函数等效的函数。(用这个名义函数替换上面的函数会让它的其余部分按所写的那样工作)tend-of-filelisten
  2. 一个与此等效的函数wait-for-input返回一个导致它跳闸的关闭套接字列表。(在这种情况下,我可以遍历关闭的套接字列表,检查它们是否真的用建议的read技术关闭,并根据需要关闭/弹出它们)
  3. 一个与此等效的函数wait-for-input返回导致它跳闸的第一个关闭的套接字。(与 #2 一样,但速度较慢,因为它每次迭代最多修剪一个非活动连接)
  4. 跟踪我从每个套接字连接收到输入以来的时间,并在一段时间不活动后将其关闭。(无论如何我可能都想做,但是这样做可能会使一堆死连接比必要的时间长得多)
  5. 一个尝试read-char从具有即时超时的流中取出的函数,t如果遇到,则返回:eof,并且unread-chars 其他任何内容(nil在超时或未读取后返回)。(这是最后的手段,因为它似乎很容易以一种不明显但致命的方式破解)

另外,如果我以完全错误的方式思考这个问题,也请指出这一点。

4

1 回答 1

3

事实证明,我上面提到的选项 2 是存在的。

wait-for-input默认返回完整的跟踪连接列表以用于内存管理(据报道有人非常关心cons结果的新列表),但它有一个&key参数告诉它只返回有话要说的连接。

(wait-for-input (list conn1 conn2 conn3 conn4) :ready-only t)

is what I was looking for there. This returns all the ready connections, not just ones that are going to signal end-of-file, so the loop still needs to handle both cases. Something like

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

should do nicely.

于 2012-07-22T07:38:30.623 回答