Давайте проясним некоторые вещи. Сервер может вернуть HTML, но это браузер, который отображает HTML. Например, если вы вводите адрес в адресную строку браузера, это заставляет браузер отправлять запрос на сервер, а сервер отправляет обратно ответ - содержащий некоторый html в теле ответа - который анализирует браузер. из ответа и интерпретируется как цветной текст, картинки и т. д., который известен как , отображающий HTML .
Обратите внимание, что HTML - это просто текст, который отформатирован определенным образом, например <div>hello</div>
, поэтому для сервера возврат html-файла ничем не отличается от возврата текстового файла. Сервер фактически возвращает содержимое файла - и содержимое не имеет расширения. Вы должны понимать, что это всего лишь текст, который отправляется «по проводам».
Вот что вам нужно сделать:
Создайте tcp-сервер, который прослушивает какой-либо порт и возвращает html при поступлении запроса.
В адресной строке браузера укажите адрес, на котором слушает сервер (например, localhost
, который является адресом 127.0.0.1, который является адресом на вашем компьютере), а также порт, на котором находится сервер. прослушивание, например http://localhost:3456
.
Есть одна большая проблема: не существует универсального протокола для взаимодействия серверов и клиентов (например, браузеров). Проблема вызвана тем, как текст отправляется в сокет TCP. Когда вы отправляете некоторый текст в сокет tcp, вы не представляете, будет ли текст разбиваться на куски, и как долго будет длиться каждый кусок. Данные могут быть отправлены как один кусок, или данные могут быть разделены на десять частей. Это создает проблему для получателя данных: как получатель узнает, когда он должен прекратить попытки считывать данные из сокета, потому что данные больше не поступают?
Чтобы решить эту проблему, и клиент, и сервер должны согласовать протокол , который является согласованным форматом для данных, чтобы другая сторона могла легко синтаксический анализ данных, а также согласованный сигнал о том, что конец данных достигнут, так что другая сторона может прекратить попытки чтения данных из сокета. Самым простым протоколом для отправителя является закрытие сокета по окончании отправки данных. Затем получатель будет пытаться продолжать считывать данные из сокета, пока не получит ошибку сокета, а затем он узнает, что данные больше не поступают.
Или, протокол может быть, когда в данных встречается слово «конец» - тогда получатель должен прекратить попытки чтения из сокета, когда он читает «конец». Но вот пример того, как это может вызвать проблемы:
msg = "I finished the end of the book. It was great!"
to_send = msg <> "end"
Получатель будет думать, что оно дошло до конца сообщения, как только будет написано «Я закончил конец», что является только частью сообщения. Конечный маркер типа **&&=>END1234<=!!**
будет работать лучше. Точно так же протокол для обозначения конца данных может быть новой строкой ("\ n"). Например, опция прослушивания сокета packet: :line
устанавливает все так, что :get_tcp.recv()
будет читать одну строку из сокета.
Еще один протокол - использовать первые 4 байта данных, чтобы указать целое число, представляющее собой число байтов, которое другая сторона должна впоследствии прочитать из сокета. Получатель ожидает, пока он не прочитает 4 байта из сокета, затем получатель считывает дополнительные N байтов (целое число, содержащееся в первых 4 байтах), и как только будет прочитано еще N байтов, получатель знает, что это конец данных.
Протокол, согласованный веб-браузерами и серверами, - это протокол 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