Клиент WebSocket в Common Lisp с библиотекой usocket
Я пытаюсь обновить протокол, переключаясь с HTTP 1.1 на WebSockets. Я пытался использовать usocket. Мой код до сих пор следует (и доступен как GitHub гист). После чтения рукопожатия функции возвращаются NIL
или же unexpected EOF
ошибка.
;; Define parameter sock for usocket stream
;; echo.websocket.org is a site for testing websockets
(defparameter sock (usocket:socket-connect "echo.websocket.org" 80))
;; Output confirms WebSocket protocol handshake as this implemented in browsers
(format (usocket:socket-stream sock) "~A~%~A~%~A~%~A~%~A~A~%~A~%~A~%~%"
"GET /?encoding=text HTTP/1.1"
"Connection: Upgrade"
"Host: echo.websocket.org"
"Origin: http://www.websocket.org"
"Sec-WebSocket-Key: " (generate-websocket-key)
"Sec-WebSocket-Version: 13"
"Upgrade: websocket")
;; Write output to stream
(force-output (usocket:socket-stream sock))
;; Returns NIL
(do ((line
(read-line (usocket:socket-stream sock) nil)
(read-line (usocket:socket-stream sock) nil)))
((not line))
(format t "~A" line))
;; Returns error: unexpected EOF
(read-line (usocket:socket-stream sock))
;; Returns NIL
(listen (usocket:socket-stream sock))
1 ответ
~%
в FORMAT
оператор не является переносимым для целей протоколов HTTP. Выводит символ новой строки #\newline
, Новая строка зависит от платформы, на которой работает код: cr (старые Mac, Lisp Machines), crlf (Windows) или lf (Unix). Таким образом, если вы записываете символ новой строки в поток, результат зависит от платформы, на которой вы работаете (или от того, что, по мнению системы Lisp, она должна делать). Windows имеет то же соглашение о конце строки, что и HTTP.
Замечания:
- в системе Unix обычно
#\newline
такой же как#\linefeed
, Единый персонаж. - в системе Windows часто
#\newline
такой же, как последовательность#\return
#\linefeed
, Два персонажа. Некоторые системы Lisp могут игнорировать это и использовать соглашения Unix.
HTTP использует crlf. Чтобы надежно написать crlf, вы должны написать символы #\return
а также #\linefeed
: (format stream "~a~a" #\return #\linefeed)
,
(defun write-crlf (stream)
(write-char #\return stream)
(write-char #\linefeed stream))
Однако некоторые серверы могут быть достаточно тупыми для чтения ввода, который не соответствует стандартным соглашениям HTTP crlf.
Могут быть способы указать соглашение об окончании строки при открытии потока. usocket
однако не предоставляет такой функциональности.
Я бы также использовал finish-output
(ждет завершения) а не force-output
(НЕ ожидает завершения операций ввода-вывода).