【问题标题】:Single-threaded sequence-reading multi-user usocket server单线程序列读取多用户usocket服务器
【发布时间】:2015-12-07 02:07:23
【问题描述】:

我正在尝试使用 usocket 库编写一个简单的服务器程序,该程序将执行一项相对微不足道的任务 - 例如,回显数据。我想让它能够对多个客户端执行此操作,而不是在等待来自任何单个客户端的输入时阻止单个线程。我发现可以使用wait-for-input:timeout 0 检查给定的套接字是否已准备好输入。但是我很难让read-sequence 以我想要的方式工作。如果我给它一个包含 50 个元素的数组,但只有 5 个可用,它会等到 50 个可用时才将它们放入数组中。

有没有什么方法可以一次(有效地)只用一个线程读取块,而不必一直等待输入?还是我真的必须一遍又一遍地打电话给read-byte,直到我得到所有东西?

如果有一些等同于read-sequence 的内容只能读取当时可用的内容,或者如果有一些函数可以告诉我有多少元素可以读取,那么我可以绕过这个问题适当地调整数组大小。但我不知道这两个。

更新:我专门寻找不需要读取字符的二进制解决方案,因此涉及read-char-no-hanglisten 等的解决方案不会有太大帮助,除非它们具有二进制等效项。我不想使用字符,因为某些字符编码(例如 UTF-8)可能具有没有字符表示的无效字节序列,并且我希望能够处理 any 序列字节。而且我特别在寻找不需要一次又一次地读取一个字节的解决方案,或者确认不存在这样的解决方案(在标准中),在这种情况下,我很想听听最方便的可以提供实现该目标所需的最低限度的库。不仅一次读取一个字节不是最快的方法,它还需要我编写的任何函数以非阻塞方式执行此操作,以对每个字节使用usocketwait-for-input 函数(因为@ 987654330@ 不适用于字节流),这将需要函数了解套接字,我将不得不编写一个过于具体的 read-all-bytes 函数,该函数不适用于文件流。有可能,但我希望有更通用的方法。

【问题讨论】:

    标签: common-lisp polling usocket


    【解决方案1】:

    好吧,我尝试了来自rosetta code 的代码,一个示例usocket echo 服务器,诀窍是创建自己的读取函数。在这种情况下,阅读所有内容并等待:eof,我使用 telnet 对其进行了测试,它可以正常工作:

    代码:

    ;; Sample usocket echo server from Rosetta Code
    ;; http://rosettacode.org/wiki/Echo_server#Common_Lisp
    
    (ql:quickload (list :usocket))
    
    (defpackage :echo (:use :cl :usocket))
    
    (in-package :echo)
    
    (defun read-all (stream)
      (loop for char = (read-char-no-hang stream nil :eof)
         until (or (null char) (eq char :eof)) collect char into msg
         finally (return (values msg char))))
    
    (defun echo-server (port &optional (log-stream *standard-output*))
      (let ((connections (list (socket-listen "127.0.0.1" port :reuse-address t))))
        (unwind-protect
         (loop (loop for ready in (wait-for-input connections :ready-only t)
              do (if (typep ready 'stream-server-usocket)
                 (push (socket-accept ready) connections)
                 (let* ((stream (socket-stream ready))
                    (msg (concatenate 'string "You said: " (read-all stream))))
                   (format log-stream "Got message...~%")
                   (write-string msg stream)
                   (socket-close ready)
                   (setf connections (remove ready connections))))))
          (loop for c in connections do (loop while (socket-close c))))))
    

    在 lisp 中初始化:

    CL-USER> (in-package :echo)
    #<PACKAGE "ECHO">
    ECHO> (echo-server 12321)
    Got message...
    

    使用 telnet 进行测试:

    ╭─toni@Antonios-MBP  ~ ‹ruby-2.2.3@laguna› ‹1.7› ‹SBCL 1.3.0›
    ╰─$ telnet 127.0.0.1 12321
    Trying 127.0.0.1...
    Connected to localhost.
    Escape character is '^]'.
    Hello, TCP                       #<= Press enter
    You said: Hello, TCP
    Connection closed by foreign host.
    

    希望对你有帮助

    【讨论】:

    • 不幸的是,这需要以字符序列的形式获取数据。假设我的实现使用 UTF-8 编码,那么某些字节序列很可能会到达,但没有字符表示。由于某种原因,该标准不包括(据我所知)read-byte-no-hang。我将更新问题以明确我正在寻找二进制解决方案。
    【解决方案2】:

    这个我也很纠结。

    我最终在项目中使用的一个不可移植选项默认为我正在使用的 SBCL 的实现。

    使用缓冲区:

    (defparameter buf-in (make-array 1024 :element-type '(unsigned-byte 8)))
    ...
    ;; suppose variable new-client is your usocket object
    (setf my-out (multiple-value-list (sb-bsd-sockets:socket-receive
                         (usocket:socket new-client) bufin nil)))
    

    输出将包含:

    (Your buffer, length, address of peer who sent it)
    

    SBCL 套接字实现的更多信息是here

    【讨论】:

      【解决方案3】:

      更新:意识到这实际上不适用于 clisp - 仅在 SBCL 上进行了测试,其中 (listen) 似乎至少对网络流执行“您可能认为的”。因此,这不是一个可移植的解决方案.. 除非您在下面的循环中替换 '(listen)' 以使用某种形式的 #+ 功能。

      因为监听不适用于字节流

      (listen) 与字节流在 sbcl 上完美配合。这应该可以解开 Gordion 的结,让人们可以轻松地写出以下内容:

      (defun read-sequence-no-hang (seq stream start end)
        (loop
           for i from start below end
           for num-bytes-read = 0 then (1+ num-bytes-read)
           while (listen stream)
           do (setf (elt seq i) (read-byte stream))
           finally (return num-bytes-read)))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-09-17
        • 2016-01-10
        • 1970-01-01
        • 2014-12-21
        • 2011-04-28
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多