如何使用 Erlang 制作 Web 服务器
How to make a web server using Erlang
我是 Erlang 新手
我尝试用 Erlang 制作一个网络服务器。如何用 Erlang 做到这一点?
我正在使用此代码制作本地:
-module(test).
-export([start/0,service/3]).
start() ->
inets:start(httpd, [
{modules, [
mod_auth,
mod_esi,
mod_actions,
mod_cgi,
mod_dir,
mod_get,
mod_head,
mod_log,
mod_disk_log
]},
{port,8082},
{server_name,"helloworld"},
{server_root,"C://xampp//tmp"},
{document_root,"C://xampp//htdocs"},
{erl_script_alias, {"/erl", [test]}},
{error_log, "error.log"},
{security_log, "security.log"},
{transfer_log, "transfer.log"},
{mime_types,[
{"html","text/html"}, {"css","text/css"}, {"js","application/x-javascript"} ]}
]).
service(SessionID, _Env, _Input) -> mod_esi:deliver(SessionID, [
"Content-Type: text/html\r\n\r\n",
"<DOCTYPE html>
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<title>HTML1</title>
<script
src='https://code.jquery.com/jquery-3.2.1.js'
integrity='sha256-DZAnKJ/6XZ9si04Hgrsxu/8s717jcIzLy3oi35EouyE='
crossorigin='anonymous'></script>
<link href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css' rel='stylesheet'/>
<link href='css/test1.css' rel='stylesheet'/>
<script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js'></script>
</head>
<html>
<body>Ham oc cho!
<div class='header'>
<ul class='first'>
<li class='col-md-4'><a href='#' >Tai khoan cua toi</a></li>
<li class='col-md-4'><a href='#' >Trang thai don hang</a></li>
<li class='col-md-4'><a href='#' >Danh sach ua thich</a></li>
<li class='col-md-4'><a href='#' >Gio hang</a></li>
<li class='col-md-4'><a href='#' >Dang nhap</a></li>
<li class='col-md-4'><a href='#' >Dang ky</a></li>
</ul>
</div>
</body>
</html>" ]).
但我没有看到任何添加 css-js 文件的方法,也不知道如何为此编写后端。
如果你们有一些例子或文档请分享给我
有一些有用的 Erlang 工具,例如 Cowboy, Mochiweb, Chicagoboss and YAWS 可用于处理 Web 协议。
您可能会发现完成 sws
、我的 Erlang simple web server 很有启发性。它展示了如何处理连接、从套接字读取 HTTP 请求以及使用 Erlang 的 built-in 套接字支持和 HTTP 支持发送回复。
Web 服务器通过使用 Erlang 的 built-in 对 HTTP 请求解析的支持来接受传入连接和解析传入请求来工作 — 请参阅第 29 行:
ok = inet:setopts(S, [{packet,http_bin}]),
{packet, http_bin}
socket option 告诉 Erlang 尝试将传入的套接字数据解析为 HTTP。在第 36 行的 serve/3
函数中,出于流量控制和背压的目的,我们将套接字保持在 {active, once}
模式,这也意味着 sws
接收来自 Erlang 的传入数据作为消息——参见第 37 行-41:
ok = inet:setopts(S, [{active, once}]),
HttpMsg = receive
{http, S, Msg} -> Msg;
_ -> gen_tcp:close(S)
end,
serve/3
函数是递归的,接收 HTTP 请求数据,直到我们得到一个完整的请求或一个错误。一旦 serve/3
有一个完整的请求,它将把它传递给一个处理函数,您应该在调用 sws:start/1,2
时提供该函数。处理程序应 return HTTP 状态、HTTP 回复 headers 和 HTTP 回复 body 的三元组,其中 headers 或 body 可以是空取决于 return 状态。
例如,这是一个简单的 "Hello, World!" 应用程序 运行 在 Erlang shell:
1> c(sws).
{ok,sws}
2> Sws = spawn(sws, start, [fun(_,_) -> {200, [], <<"Hello, World!">>} end]).
<0.73.0>
这里,作为处理程序传递的 fun
总是 return 的 HTTP 状态 200,没有回复 headers,以及回复的二进制字符串 body。从 Unix shell 通过 curl 访问服务器显示预期的回复:
$ curl http://localhost:8000
Hello, World!
如果我们将 -v
传递给 curl 以显示更多详细信息,我们会看到:
$ curl -v http://localhost:8000
* Rebuilt URL to: http://localhost:8000/
* Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 8000 failed: Connection refused
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.51.0
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200
<
* Curl_http_done: called premature == 0
* Closing connection 0
Hello, World!
首先 curl 尝试通过 IPv6 连接,但由于 sws
不支持它而失败(虽然它可以),因此它通过 IPv4 重试,并成功。然后,Curl 发送 GET
对 /
资源的请求。当 curl 看到回复时,它会显示 200 状态代码,还要注意它看到回复是 HTTP 1.0,因此正确地假设连接将在 body 发送后关闭,因此在收到回复后它会关闭它的侧面也是。
您提供的处理函数有两个参数:客户端套接字和一个请求 object,它是一个 属性 列表,由 2 元组组成,其中第一个元组元素是一个原子,用于标识它的关联数据。例如,处理程序可以通过使用 lists:keyfind/3
:
在 Request
参数中查找 method
元组来确定调用的 HTTP 方法
{method, Method} = lists:keyfind(method, 1, Request),
对于我们上面的示例,Method
将具有 'GET'
(一个原子)的值。可以像这样发现的请求的其他属性是:
uri
请求的资源
version
客户端 HTTP 版本
headers
请求中的 HTTP headers 列表
您提供的处理函数可以简单也可以复杂。请注意,如果您的处理程序失败并导致异常,sws
会捕获它并且 returns HTTP 状态代码 500。
要停止 Web 服务器,回到 Erlang shell 我们向生成的 sws
进程发送一条 stop
消息:
3> Sws ! stop.
stop
=ERROR REPORT==== 19-Jul-2017::11:17:05 ===
Error in process <0.77.0> with exit value:
{{badmatch,{error,closed}},[{sws,accept,2,[{file,"sws.erl"},{line,28}]}]}
此处显示的错误(可以忽略)仅仅是因为 sws
始终假定 gen_tcp:accept/1
成功 — 参见第 28 行:
{ok, S} = gen_tcp:accept(LS),
将其改为 case
表达式并处理错误 return 会很容易。
请注意,sws
用于演示和学习,因此它的效率不是特别高,因为它仅支持 HTTP 1.0 并且每个连接仅处理一个请求。
考虑使用 http://phoenixframework.org/
它使用在 Erlang VM 上运行的 elixir。
我是 Erlang 新手
我尝试用 Erlang 制作一个网络服务器。如何用 Erlang 做到这一点? 我正在使用此代码制作本地:
-module(test).
-export([start/0,service/3]).
start() ->
inets:start(httpd, [
{modules, [
mod_auth,
mod_esi,
mod_actions,
mod_cgi,
mod_dir,
mod_get,
mod_head,
mod_log,
mod_disk_log
]},
{port,8082},
{server_name,"helloworld"},
{server_root,"C://xampp//tmp"},
{document_root,"C://xampp//htdocs"},
{erl_script_alias, {"/erl", [test]}},
{error_log, "error.log"},
{security_log, "security.log"},
{transfer_log, "transfer.log"},
{mime_types,[
{"html","text/html"}, {"css","text/css"}, {"js","application/x-javascript"} ]}
]).
service(SessionID, _Env, _Input) -> mod_esi:deliver(SessionID, [
"Content-Type: text/html\r\n\r\n",
"<DOCTYPE html>
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<title>HTML1</title>
<script
src='https://code.jquery.com/jquery-3.2.1.js'
integrity='sha256-DZAnKJ/6XZ9si04Hgrsxu/8s717jcIzLy3oi35EouyE='
crossorigin='anonymous'></script>
<link href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css' rel='stylesheet'/>
<link href='css/test1.css' rel='stylesheet'/>
<script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js'></script>
</head>
<html>
<body>Ham oc cho!
<div class='header'>
<ul class='first'>
<li class='col-md-4'><a href='#' >Tai khoan cua toi</a></li>
<li class='col-md-4'><a href='#' >Trang thai don hang</a></li>
<li class='col-md-4'><a href='#' >Danh sach ua thich</a></li>
<li class='col-md-4'><a href='#' >Gio hang</a></li>
<li class='col-md-4'><a href='#' >Dang nhap</a></li>
<li class='col-md-4'><a href='#' >Dang ky</a></li>
</ul>
</div>
</body>
</html>" ]).
但我没有看到任何添加 css-js 文件的方法,也不知道如何为此编写后端。
如果你们有一些例子或文档请分享给我
有一些有用的 Erlang 工具,例如 Cowboy, Mochiweb, Chicagoboss and YAWS 可用于处理 Web 协议。
您可能会发现完成 sws
、我的 Erlang simple web server 很有启发性。它展示了如何处理连接、从套接字读取 HTTP 请求以及使用 Erlang 的 built-in 套接字支持和 HTTP 支持发送回复。
Web 服务器通过使用 Erlang 的 built-in 对 HTTP 请求解析的支持来接受传入连接和解析传入请求来工作 — 请参阅第 29 行:
ok = inet:setopts(S, [{packet,http_bin}]),
{packet, http_bin}
socket option 告诉 Erlang 尝试将传入的套接字数据解析为 HTTP。在第 36 行的 serve/3
函数中,出于流量控制和背压的目的,我们将套接字保持在 {active, once}
模式,这也意味着 sws
接收来自 Erlang 的传入数据作为消息——参见第 37 行-41:
ok = inet:setopts(S, [{active, once}]),
HttpMsg = receive
{http, S, Msg} -> Msg;
_ -> gen_tcp:close(S)
end,
serve/3
函数是递归的,接收 HTTP 请求数据,直到我们得到一个完整的请求或一个错误。一旦 serve/3
有一个完整的请求,它将把它传递给一个处理函数,您应该在调用 sws:start/1,2
时提供该函数。处理程序应 return HTTP 状态、HTTP 回复 headers 和 HTTP 回复 body 的三元组,其中 headers 或 body 可以是空取决于 return 状态。
例如,这是一个简单的 "Hello, World!" 应用程序 运行 在 Erlang shell:
1> c(sws).
{ok,sws}
2> Sws = spawn(sws, start, [fun(_,_) -> {200, [], <<"Hello, World!">>} end]).
<0.73.0>
这里,作为处理程序传递的 fun
总是 return 的 HTTP 状态 200,没有回复 headers,以及回复的二进制字符串 body。从 Unix shell 通过 curl 访问服务器显示预期的回复:
$ curl http://localhost:8000
Hello, World!
如果我们将 -v
传递给 curl 以显示更多详细信息,我们会看到:
$ curl -v http://localhost:8000
* Rebuilt URL to: http://localhost:8000/
* Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 8000 failed: Connection refused
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.51.0
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200
<
* Curl_http_done: called premature == 0
* Closing connection 0
Hello, World!
首先 curl 尝试通过 IPv6 连接,但由于 sws
不支持它而失败(虽然它可以),因此它通过 IPv4 重试,并成功。然后,Curl 发送 GET
对 /
资源的请求。当 curl 看到回复时,它会显示 200 状态代码,还要注意它看到回复是 HTTP 1.0,因此正确地假设连接将在 body 发送后关闭,因此在收到回复后它会关闭它的侧面也是。
您提供的处理函数有两个参数:客户端套接字和一个请求 object,它是一个 属性 列表,由 2 元组组成,其中第一个元组元素是一个原子,用于标识它的关联数据。例如,处理程序可以通过使用 lists:keyfind/3
:
Request
参数中查找 method
元组来确定调用的 HTTP 方法
{method, Method} = lists:keyfind(method, 1, Request),
对于我们上面的示例,Method
将具有 'GET'
(一个原子)的值。可以像这样发现的请求的其他属性是:
uri
请求的资源version
客户端 HTTP 版本headers
请求中的 HTTP headers 列表
您提供的处理函数可以简单也可以复杂。请注意,如果您的处理程序失败并导致异常,sws
会捕获它并且 returns HTTP 状态代码 500。
要停止 Web 服务器,回到 Erlang shell 我们向生成的 sws
进程发送一条 stop
消息:
3> Sws ! stop.
stop
=ERROR REPORT==== 19-Jul-2017::11:17:05 ===
Error in process <0.77.0> with exit value:
{{badmatch,{error,closed}},[{sws,accept,2,[{file,"sws.erl"},{line,28}]}]}
此处显示的错误(可以忽略)仅仅是因为 sws
始终假定 gen_tcp:accept/1
成功 — 参见第 28 行:
{ok, S} = gen_tcp:accept(LS),
将其改为 case
表达式并处理错误 return 会很容易。
请注意,sws
用于演示和学习,因此它的效率不是特别高,因为它仅支持 HTTP 1.0 并且每个连接仅处理一个请求。
考虑使用 http://phoenixframework.org/ 它使用在 Erlang VM 上运行的 elixir。