我试图使用usocket库编写一个简单的服务器程序,该库将执行相对简单的任务 - 比如回显数据。我想让它能够在多个客户端上执行此操作,而不是在等待来自任何单个客户端的输入时阻塞单个线程。我发现可以使用wait-for-input
:timeout 0
检查给定套接字是否已准备就绪。但是我很难让read-sequence
按照我想要的方式工作。如果我给它一个包含50个元素的数组,并且只有5个可用,它将等到50可用于将它们放入数组中。
有没有办法只用一个线程一次(有效地)读取块而不必经常等待输入?或者我真的必须一遍又一遍地呼叫read-byte
,直到我得到了所有东西?
如果有一些等同于read-sequence
的内容只能读取当时可用的内容,或者是否有某些函数可以告诉我有多少元素可以读取,那么就可以避免这个问题我可以适当地调整数组的大小。但我不知道其中任何一个。
更新:我专门寻找不需要阅读字符的二进制解决方案,因此涉及read-char-no-hang
,listen
等的解决方案不会有太大帮助,除非他们有二进制等价物。我想不使用字符,因为一些字符编码,如UTF-8,可能有无效的字节序列,没有字符表示,我希望能够处理任何序列字节。而且,我特意寻找一次又一次不需要读取一个字节的解决方案,或者确认不存在这样的解决方案(在标准中),在这种情况下,我喜欢了解最方便的图书馆,它可以提供实现这一目标所需的最低限度。它不仅仅是一次读取一个字节不是最快的方式,它还需要我编写的任何函数以非阻塞方式执行此操作以使用usocket
'每个字节的wait-for-input
函数(因为listen
不使用字节流),这需要函数知道套接字,我将不得不写一个过于具体的{{ 1}}无法使用的功能,例如文件流。这是可能的,但我希望有更普遍的方式。
答案 0 :(得分:2)
我尝试使用rosetta代码中的代码,这是一个示例usocket echo服务器,其功能是创建自己的读取函数。在这种情况下,read-all并等待: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.
希望这有帮助
答案 1 :(得分:1)
我也在努力解决这个问题。
我最终在项目中使用的一个非便携式选项默认为我正在使用的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
答案 2 :(得分:0)
更新:意识到这一点实际上并没有发挥作用-仅在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)))