可重复的步骤

可重复的步骤

标题很明确。

可重复的步骤

* 在 AWS 上使用端口 80 上的 HTTP 规则启动 Ubuntu 实例

* 安装 sbcl

sudo apt install sbcl -y

* 为 root 安装 usocket 库

请参阅下一步以了解为什么我以 root 身份执行此操作

curl -O https://beta.quicklisp.org/quicklisp.lisp
sudo sbcl --load quicklisp.lisp

接下来的几行需要输入到 sbcl REPL 中

(quicklisp-quickstart:install)
(ql:add-to-init-file)
(ql:quickload "usocket")

* 使用 Land of Lisp 示例 (http://landoflisp.com/

在名为的文件中server.lisp

(require 'usocket)

(defun http-char (c1 c2 &optional (default #\Space))
  (let ((code (parse-integer
               (coerce (list c1 c2) 'string)
               :radix 16
               :junk-allowed t)))
    (if code
        (code-char code)
        default)))

(defun decode-param (s)
  (labels ((f (lst)
             (when lst
               (case (car lst)
                 (#\% (cons (http-char (cadr lst) (caddr lst))
                            (f (cdddr lst))))
                 (#\+ (cons #\space (f (cdr lst))))
                 (otherwise (cons (car lst) (f (cdr lst))))))))
    (coerce (f (coerce s 'list)) 'string)))

(defun parse-params (s)
  (let* ((i1 (position #\= s))
         (i2 (position #\& s)))
    (cond (i1 (cons (cons (intern (string-upcase (subseq s 0 i1)))
                          (decode-param (subseq s (1+ i1) i2)))
                    (and i2 (parse-params (subseq s (1+ i2))))))
          ((equal s "") nil)
          (t s))))

(defun parse-url (s)
  (let* ((url (subseq s
                      (+ 2 (position #\space s))
                      (position #\space s :from-end t)))
         (x (position #\? url)))
    (if x
        (cons (subseq url 0 x) (parse-params (subseq url (1+ x))))
        (cons url '()))))


(defun get-header (stream)
  (let* ((s (read-line stream))
         (h (let ((i (position #\: s)))
              (when i
                (cons (intern (string-upcase (subseq s 0 i)))
                      (subseq s (+ i 2)))))))
    (when h
      (cons h (get-header stream)))))

(defun get-content-params (stream header)
  (let ((length (cdr (assoc 'content-length header))))
    (when length
      (let ((content (make-string (parse-integer length))))
        (read-sequence content stream)
        (parse-params content)))))

(defun serve (request-handler)
  (let ((socket (usocket:socket-listen #(127 0 0 1) 80)))
    (unwind-protect
         (loop (with-open-stream (stream (usocket:socket-stream
                                          (usocket:socket-accept socket)))
                 (let* ((url    (parse-url (read-line stream)))
                        (path   (car url))
                        (header (get-header stream))
                        (params (append (cdr url)
                                        (get-content-params stream header)))
                        (*standard-output* stream))
                   (funcall request-handler path header params))))
      (usocket:socket-close socket))))

(defun hello-request-handler (path header params)
  (if (equal path "greeting")
      (let ((name (assoc 'name params)))
        (if (not name)
            (princ "<html><form>What is your name?<input name='name'/></form></html>")
            (format t "<html>Nice to meet you, ~a!</html>" (cdr name))))
      (princ "Sorry... I don't know that page")))

(serve #'hello-request-handler)

然后以 root 身份启动服务器:

sudo sbcl --load "server.lisp"

我使用 root 是因为我无法用普通用户摆脱以下错误信息

The condition Socket error in "bind": 13 (Permission denied) occurred with errno :0.

然后一切似乎都正确,但我无法使用以下方式从标准浏览器访问服务器:

http://IPv4.Public.IP:80

* 补充诊断:

AWS 安全组/入站规则

╔══════╦══════════╦════════════╦═══════════╗
║ Type ║ Protocol ║ Port Range ║  Source   ║
╠══════╬══════════╬════════════╬═══════════╣
║ HTTP ║ TCP      ║         80 ║ 0.0.0.0/0 ║
║ HTTP ║ TCP      ║         80 ║ ::/0      ║
║ SSH  ║ TCP      ║         22 ║ 0.0.0.0/0 ║
╚══════╩══════════╩════════════╩═══════════╝

iptables

sudo iptables -L -v
Chain INPUT (policy ACCEPT 346 packets, 23760 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 244 packets, 32428 bytes)
 pkts bytes target     prot opt in     out     source               destination
sudo iptables -t nat -L -v
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination

在服务器上 curl

curl 127.0.0.1
Sorry... I don't know that page

正如预期的那样!

从远程机器 ping

需要将自定义 ICMP 规则添加到入站安全组策略(现在我知道正在ping使用 ICMP...)

ping 35.180.138.87
64 bytes from 35.180.138.87: icmp_seq=1 ttl=49 time=173 ms
64 bytes from 35.180.138.87: icmp_seq=2 ttl=49 time=32.2 ms
^C
--- 35.180.138.87 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1005ms
rtt min/avg/max/mdev = 32.248/102.884/173.520/70.636 ms

从远程机器 curl

curl 35.180.138.87
curl: (7) Failed to connect to 35.180.138.87 port 80: Connection refused

网络状态

netstat -nlp
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0 127.0.0.1:80            0.0.0.0:*               LISTEN      -

只有当我的服务器运行时才会出现这一行端口为 80 的行。

答案1

尝试 ping 公共 IPV4 以确保可以访问它。

如果确实达到要求,请检查 iptables/firewalld 规则中的端口 80(例如iptables -L...)

如果防火墙没有问题,请检查服务器是否绑定在所有网络接口上,而不仅仅是绑定在环回 (127.0.0.1) 上netstat -nlp。还要检查是否有其他程序绑定到端口 80。

您还可以测试curl 127.0.0.1检查服务器是否实际提供该页面而您未访问该页面,或者服务器是否整体损坏。

还要确保您在 AWS 上设置的端口 80 上的 HTTP 规则位于与您的实例关联的安全组中。

答案2

问题出在用于打开套接字的 IP 地址上,即127.0.0.1

我尝试使用 AWS 提供的 IPv4 地址,但这不是解决方案

相反,必须从主机服务器搜索 IP 地址:

sudo ip addr

答案是例如111.111.111.111

然后在 lisp 代码中使用找到的地址

(let ((socket (usocket:socket-listen #(111 111 111 111) 80)))

相关内容