Jos*_*lin 5 lisp sockets http sbcl
我正在尝试学习更多关于在SBCL中处理套接字和网络连接的知识; 所以我为HTTP写了一个简单的包装器.到目前为止,它只是制作流并执行请求以最终获得网站的标题数据和页面内容.
到目前为止,它已经有点体面.没有什么可以吹嘘回家,但它至少有效.
然而,我遇到了一个奇怪的问题; 我一直收到"400 Bad Request"错误.
起初,我对如何处理HTTP请求(或多或少地将请求字符串作为函数参数传递)有点怀疑,然后我创建了一个函数,使用我需要的所有部分格式化查询字符串并返回它以供使用以后......但我仍然得到错误.
更奇怪的是,每次都不会发生错误.如果我在像Google这样的页面上尝试脚本,我会得到"200 Ok"的返回值...但在其他网站的其他时间,我会得到"400 Bad Request".
我确定我的代码存在问题,但如果我确切地知道导致它的原因,我会被诅咒.
这是我正在使用的代码:
(use-package :sb-bsd-sockets)
(defun read-buf-nonblock (buffer stream)
(let ((eof (gensym)))
(do ((i 0 (1+ i))
(c (read-char stream nil eof)
(read-char-no-hang stream nil eof)))
((or (>= i (length buffer)) (not c) (eq c eof)) i)
(setf (elt buffer i) c))))
(defun http-connect (host &optional (port 80))
"Create I/O stream to given host on a specified port"
(let ((socket (make-instance 'inet-socket
:type :stream
:protocol :tcp)))
(socket-connect
socket (car (host-ent-addresses (get-host-by-name host))) port)
(let ((stream (socket-make-stream socket
:input t
:output t
:buffering :none)))
stream)))
(defun http-request (stream request &optional (buffer 1024))
"Perform HTTP request on a specified stream"
(format stream "~a~%~%" request )
(let ((data (make-string buffer)))
(setf data (subseq data 0
(read-buf-nonblock data
stream)))
(princ data)
(> (length data) 0)))
(defun request (host request)
"formated HTTP request"
(format nil "~a HTTP/1.0 Host: ~a" request host))
(defun get-page (host &optional (request "GET /"))
"simple demo to get content of a page"
(let ((stream (http-connect host)))
(http-request stream (request host request)))
Run Code Online (Sandbox Code Playgroud)
一些东西。首先,对于您返回的 400 错误的担忧,我想到了几种可能性:
其他一些更通用的指导可以帮助您:
(read-buf-nonblock) 非常令人困惑。符号“c”在哪里定义的?为什么对 'eof' (gensym) 进行了处理,然后没有分配任何值?它看起来非常像直接从命令式程序中取出的逐字节副本,然后放入 Lisp 中。看起来您在这里重新实现的是(读取序列)。去看看Common Lisp Hyperspec,看看这是否是您所需要的。另一半是将您创建的套接字设置为非阻塞。这非常简单,尽管 SBCL 文档几乎没有提及该主题。用这个:
(socket-make-stream socket
:input t
:output t
:buffering :none
:timeout 0)
(http-connect) 的最后一个 (let) 形式不是必需的。只是评价
(socket-make-stream socket
:input t
:output t
:buffering :none)
如果没有 let,http-connect 仍然应该返回正确的值。
代替:
(format stream "~a~%~%" request )
(let ((data (make-string buffer)))
(setf data (subseq data 0
(read-buf-nonblock data
stream)))
(princ data)
(> (length data) 0)))
Run Code Online (Sandbox Code Playgroud)
和
(format stream "~a~%~%" request )
(let ((data (read-buf-nonblock stream)))
(princ data)
(> (length data) 0)))
Run Code Online (Sandbox Code Playgroud)
并使 (read-buf-nonblock) 返回数据字符串,而不是让它在函数内分配。因此,在您被buffer分配的地方,创建一个变量buffer,然后返回它。您所做的事情称为依赖“副作用”,并且往往会产生更多错误并且更难发现错误。仅在必要时才使用它,尤其是使用一种可以轻松不依赖它们的语言。
哎呀,手受伤了。但希望这会有所帮助。打字完毕。:-)