如何将这个简单的Elixir回显服务器转换为简单的Web服务器?

N

我正在尝试为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
7stud

让我们弄清楚一些事情。服务器可以返回一些html,但是它是显示html的浏览器。例如,如果您在浏览器的地址栏中键入一个地址,这将导致浏览器向服务器发送请求,然后服务器发送回一个响应(在响应主体中包含一些html),浏览器将对其进行解析。从响应中提取出来,并将其解释为彩色文本,图片等,这称为渲染html

请注意,html只是以某种方式格式化的文本,例如<div>hello</div>,因此对于服务器而言,返回html文件与返回文本文件没有什么不同。服务器实际上正在返回文件的内容-内容没有扩展名。您需要了解,所有都是通过“有线”发送的文本。

这是您需要做的:

  1. 创建一个侦听某些端口的tcp服务器,并在请求进入时返回一些html。

  2. 在浏览器的地址栏中,指定服务器正在侦听的地址(例如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 requesthttp 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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

简单的Java HTTPS服务器

Java的简单Web服务器和MVC框架

如何将Android设备转换为Web服务器

如何构建可启动的Linux映像以仅运行Golang简单应用或简单Web服务器?

套接字回显服务器

简单的Web服务器实现:GoLang

如何将简单的Spring Boot(带有Gradle构建系统)部署到Apache Tomcat(真实服务器,而不是嵌入式服务器)?

简单的Web服务器的c自定义netcat

简单的非阻塞式Web服务器

简单的UDP服务器OCaml /异步

Swift:如何将Date()转换为苹果服务器的时区以验证收据?

如何将node.js快速服务器转换为AWS Lambda?

如何将Lightailail服务器(基本帐户)域转换为https?

如何将无服务器应用程序模型(SAM)模板转换为Cloudformation?

如何将频道/服务器ID转换为其名称?

如何将服务器图像URL转换为Drawable Int

C语言中的简单非并行Web服务器

向简单节点js Web服务器的请求

如何使用nc作为扩展回显服务器?

如何将本地DNS服务器转换为IP地址?

如何将文件放置在Web服务器上?

如何将Datasnap服务器从VCL应用程序转换为Windows服务

Python中的简单多线程Web服务器

多线程回显服务器

如何将 mvc c# 服务器错误 +number 转换为行号

Javascript - 以与 SQL 服务器相同的方式将日期转换为浮动的简单方法

如何将 FetchXML 数据源转换为 SQL 服务器数据源?

如何将 GMT 日期从服务器转换为当前用户的时区?

如何将本地服务器转换为在线服务器 Node.js