我正在尝试为elixir设置回显服务器,为了简单起见,它采用简单的HTML并仅显示它。为此,我必须在服务器中做什么?
我试图看看其他人如何在Elixir中为运行中的服务器进行最小的构建,例如https://www.jungledisk.com/blog/2018/03/19/tutorial-a-simple-http-server- in-elixir /似乎很有希望,但是我仍然不知道如何在其中实现它。
提供的EchoServer
defmodule EchoServer do
require Logger
def accept(port) do
{:ok, socket} = :gen_tcp.listen(port,
[:binary, packet: :line, active: false, reuseaddr: true])
Logger.info "Accepting connections on port #{port}"
loop_acceptor(socket)
end
defp loop_acceptor(socket) do
{:ok, client} = :gen_tcp.accept(socket)
Task.start_link(fn -> serve(client) end)
loop_acceptor(socket)
end
defp serve(socket) do
socket |> read_line() |> write_line(socket)
:ok = :gen_tcp.close(socket)
end
defp read_line(socket) do
{:ok, data} = :gen_tcp.recv(socket, 0)
data
end
defp write_line(line, socket) do
:gen_tcp.send(socket, line)
end
def main(args \\ []) do
accept(9999)
end
end
让我们弄清楚一些事情。服务器可以返回一些html,但是它是显示html的浏览器。例如,如果您在浏览器的地址栏中键入一个地址,这将导致浏览器向服务器发送请求,然后服务器发送回一个响应(在响应主体中包含一些html),浏览器将对其进行解析。从响应中提取出来,并将其解释为彩色文本,图片等,这称为渲染html。
请注意,html只是以某种方式格式化的文本,例如<div>hello</div>
,因此对于服务器而言,返回html文件与返回文本文件没有什么不同。服务器实际上正在返回文件的内容-内容没有扩展名。您需要了解,所有都是通过“有线”发送的文本。
这是您需要做的:
创建一个侦听某些端口的tcp服务器,并在请求进入时返回一些html。
在浏览器的地址栏中,指定服务器正在侦听的地址(例如localhost
,该地址是127.0.0.1,这是您计算机上的地址)以及服务器正在侦听的端口,例如http://localhost:3456
。
有一个很大的障碍:没有用于服务器和客户端(例如浏览器)相互通信的通用协议。问题是由文本发送到TCP套接字的方式引起的。当您将一些文本发送到tcp套接字时,您不知道文本是否会分解成块,也不清楚每个块将持续多长时间。数据可能会以一个块的形式发送,或者数据会被切成十个块。这给数据的接收者带来了一个问题:接收者如何知道什么时候应该停止尝试从套接字读取数据,因为没有更多的数据了?
为了解决该问题,客户端和服务器都必须就协议达成一致,该协议是数据的约定格式,以便另一端可以轻松地解析数据,以及约定的结束信号。已达到数据,以便另一端可以停止尝试从套接字读取数据。最简单的协议是发送方在完成数据发送后关闭其套接字。然后,接收器将尝试继续从套接字读取数据,直到收到套接字错误为止,然后它再也没有数据了。
或者,协议可能是在数据中遇到“ end”一词时,那么接收器在读取“ end”时应停止尝试从套接字读取。但是,这是一个可能导致问题的示例:
msg = "I finished the end of the book. It was great!"
to_send = msg <> "end"
接收者将在阅读“我完成了结局”时认为它已经到达消息末尾,这只是消息的一部分。像**&&=>END1234<=!!**
这样的结束标记会更好。同样,标记数据结尾的协议可以是换行符(“ \ n”)。例如,listen socket选项packet: :line
设置了东西,以便:get_tcp.recv()
从套接字读取一行。
还有另一种协议是使用数据的前4个字节来指定一个整数,该整数是另一端随后应从套接字读取的字节数。接收器等待,直到它从套接字读取了4个字节,然后接收器又读取了N个字节(前4个字节中包含的整数),一旦读取了另外N个字节,接收器便知道数据已结束。
Web浏览器和服务器已同意的协议是http request
和http response
协议。您可以在此处查看http请求和响应格式的一些示例。因为您正在编码tcp服务器,所以可以通过完全忽略请求(这样就不必在意格式)并为传入的任何请求返回相同的响应来简化操作。此外,这样做无需弄清楚服务器应停止尝试从套接字读取数据的时间。
您的tcp服务器发回的响应必须遵守http响应协议,因为您将需要一个浏览器来接收响应才能查看呈现的html,即您要http://localhost:33444
在浏览器的地址栏中键入内容。
以下是修改后的回显服务器的示例,使其遵循http响应协议,并且在响应正文中还返回了一些html:
~/elixir_programs/tcp_server$ tree .
.
├── page.html
├── resp_header.txt
└── s1.ex
s1.ex:
defmodule HtmlServer do
require Logger
def accept(port) do
{:ok, socket} = :gen_tcp.listen(
port,
[:binary,
packet: :line,
active: false,
reuseaddr: true]
)
Logger.info "Accepting connections on port #{port}"
loop_acceptor(socket)
end
defp loop_acceptor(socket) do
{:ok, client} = :gen_tcp.accept(socket)
Task.start_link(fn -> serve(client) end)
loop_acceptor(socket)
end
defp serve(socket) do
line = read_socket(socket) #blocks until something is read from the socket
IO.puts "[ME] Got some data! #{line}"
resp_header = File.read! "./resp_header.txt"
resp_body = File.read! "./page.html"
content_len = String.length(resp_body)
resp =
resp_header <>
"Content-Length: #{content_len}\n" <>
"\n" <>
resp_body
#IO.inspect resp
write_socket(resp, socket)
:ok = :gen_tcp.close(socket)
end
defp read_socket(socket) do
{:ok, data} = :gen_tcp.recv(socket, 0)
data
end
defp write_socket(data, socket) do
:gen_tcp.send(socket, data)
end
def start() do
accept(9999)
end
end
resp_header.txt:
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
page.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello</title>
<style>
.greeting {
color: green;
}
</style>
</head>
<body>
<div class="greeting">Hello World</div>
</body>
</html>
在IEX中:
~/elixir_programs/tcp_server$ iex s1.ex
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> HtmlServer.start
10:08:34.016 [info] Accepting connections on port 9999
然后打开浏览器窗口,并将以下地址添加到浏览器的地址栏中:
http://localhost:9999
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句