Html как составить сервер

Время на прочтение
2 мин

Количество просмотров 194K

Задача

За минимальное время написать HTTP сервер, который после запуска сможет корректно ответить браузеру и отдать простую HTML страничку (минимальное время, чтобы кода было мало, чтобы новичку вникать было проще).
У меня это заняло около 15 минут. Сервер вроде справляется с поставленной задачей.

Суть примера — показать что такое Socket, ServerSocket, InputStream, OutputStream, и Thread.

Решение

import java.net.ServerSocket;
import java.net.Socket;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;

/**
 * Created by yar 09.09.2009
 */
public class HttpServer {

    public static void main(String[] args) throws Throwable {
        ServerSocket ss = new ServerSocket(8080);
        while (true) {
            Socket s = ss.accept();
            System.err.println("Client accepted");
            new Thread(new SocketProcessor(s)).start();
        }
    }

    private static class SocketProcessor implements Runnable {

        private Socket s;
        private InputStream is;
        private OutputStream os;

        private SocketProcessor(Socket s) throws Throwable {
            this.s = s;
            this.is = s.getInputStream();
            this.os = s.getOutputStream();
        }

        public void run() {
            try {
                readInputHeaders();
                writeResponse("<html><body><h1>Hello from Habrahabr</h1></body></html>");
            } catch (Throwable t) {
                /*do nothing*/
            } finally {
                try {
                    s.close();
                } catch (Throwable t) {
                    /*do nothing*/
                }
            }
            System.err.println("Client processing finished");
        }

        private void writeResponse(String s) throws Throwable {
            String response = "HTTP/1.1 200 OKrn" +
                    "Server: YarServer/2009-09-09rn" +
                    "Content-Type: text/htmlrn" +
                    "Content-Length: " + s.length() + "rn" +
                    "Connection: closernrn";
            String result = response + s;
            os.write(result.getBytes());
            os.flush();
        }

        private void readInputHeaders() throws Throwable {
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            while(true) {
                String s = br.readLine();
                if(s == null || s.trim().length() == 0) {
                    break;
                }
            }
        }
    }
}

Как запустить

1) Создать файл HttpServer.java
2) В этот файл вставить текст исходника
3) Скомпилировать командой javac HttpServer.java
4) Запустить командой java -cp . HttpServer (при этом порт 8080 должен быть свободным)
5) Открыть браузер и пойти по ссылке http://localhost:8080/


Введение

Интернет играет в нашей жизни большую роль. Мы берем почту, узнаем свежую информацию, отлавливаем своих знакомых через ICQ… Но что ассоциируется со словом Интернет у большинства людей в первую очередь? Сайты. Многие при слове «Интернет» вспоминают свои любимые сайты, а некоторые (не слишком продвинутые) ставят знак равенства между WWW и Интернетом, хотя в последнем есть много другого интересного: email, IRC, p2p-сети, MUD’ы и так далее. Но World Wide Web играет доминирующую роль.

В основе WWW лежит протокол HyperText Transfer Protocol. Надо сказать, что HTTP может использоваться не только для передачи сайтов, но и для передачи всего чтобы то ни было. С помощью протокола HTTP мы можем скачать, например, недавно вышедший фильм или свежие mp3 :). В p2p-сетях HTTP-протокол применяется именно в этих целях.

В данном пособии мы рассмотрим, как написать простой веб-сервер. Я предполагаю, что вы знакомы с основами программирования winsock и умеете создавать сокеты и коннектиться к чему-нибудь :).

Основы HyperText Transfer Protocol

Идея HTTP довольно проста. Клиент шлет запрос серверу, тот рассматривает его и шлет соответствующий ответ. Ответом может быть запрошенный файл, сообщение о том, что такого файла на сервере нет или что-то еще. Примерная структура запроса следующая:

<метод> <запрашиваемый_ресурс> HTTP/1.1<n>
<заголовочное_поле>: <значение><n>
<заголовочное_поле>: <значение><n>
[..заголовочных полей может быть много..]<n>
<заголовочное_поле>: <значение><n>
<n>

<метод> — вид запроса. Основных два: GET и POST. Друг от друга они отличаются, главным образом, способом передачи дополнительной информации, отсылающейся вместе с запросом. В этом туториале мы рассмотрим только метод GET — функционально он похож на POST, но несколько проще. О методе POST я расскажу во второй части данного туториала, если, конечно, таковая вообще появится на свет :).

<n> — это два байта 0Dh, 0Ah, несомненно, хорошо знакомые всем ассемблерщикам :).

Таким образом, с методом мы определились. На данный момент запрос, который мы (в качестве клиента) должны будем послать серверу выглядит так:

GET <запрашиваемый_ресурс> HTTP/1.1<n>
<заголовочное_поле>: <значение><n>
<заголовочное_поле>: <значение><n>
[...]
<заголовочное_поле>: <значение><n>
<n>

Теперь нам нужно задать <запрашиваемый_ресурс>. Возьмем типичную ссылка на одном из лучших сайтов по программированию в Рунете WASM.RU (немного рекламы не помешает 🙂 ):

http://www.wasm.ru/article.php?article=1016002

Здесь «http://» указывает на то, что используется протокол HTTP, «www.wasm.ru» говорит о том, что необходимо подсоединиться к сайту www.wasm.ru, а все, что идет после знака вопроса — это дополнительные параметры страницы. Последние используются не всегда и не на всех сайтах.

Предположим, что мы подсоединились к www.wasm.ru и хотим получить article.php с необходимыми параметрами. Очевидно, что раз мы уже подсоединились к данному серверу и знаем, что необходимо использовать протокол HTTP, то пересылать «http://www.wasm.ru» не нужно, а значит, мы пошлем только «/article.php?article=1016002». Теперь наш запрос к серверу выглядит так:

GET /article.php?article=1016002 HTTP/1.1<n>
<заголовочное_поле>: <значение><n>
<заголовочное_поле>: <значение><n>
[...]
<заголовочное_поле>: <значение><n>
<n>

Теперь осталось разобраться с заголовочными полями. С их помощью клиент передает дополнительную информацию о себе или характере запроса. Например, довольно часто используемым заголовочным полем является ‘User-Agent’. Опера, скажем, шлет следующее:

User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows 98) Opera 6.02 [en]

Другим еще более важным полем является ‘Host’. В нем задается имя веб-сервера, с которого мы хотим получить документ. «Как же так», — можете сказать вы, — «Ведь мы же уже указывали имя веб-сервера, когда создавали сокет и коннектились к нему!» Да, это так. Когда мы коннектились к серверу, мы вызвали функцию gethostbyname, которой передали имя веб-сервера, а она возвратила нам адрес структуры, содержащей адрес сервера. Дело в том, что одному IP-адресу может соответствовать несколько имен сайтов, поэтому когда веб-сервер получает запрос, он должен знать к какому сайту обращается клиент (один веб-сервер может обслуживать тысячи различных сайтов, которые физически будут расположены на одной машине с одним адресом). Для этого мы пишем в ‘Host’ адрес сайта, к которому обращаемся:

Host: www.wasm.ru

Вот как теперь выглядит наш запрос:

GET /article.php?article=1016002 HTTP/1.1<n>
User-Agent: ManualSender/1.0 :)<n>
Host: www.wasm.ru<n>
<n>

Надо заметить, что хотя эти и другие заголовочные поля являются очень желательными, но, строго говоря, они не являются обязательными, то есть их может и не быть. Также я хочу обратить ваше внимание на то, что за последним заголовочным файлом следуют _два_ <n>, а не один. Это важно.

Теперь взглянем на все вышеизложенное с точки зрения веб-сервера (ведь мы же собирались писать веб-сервер, помните? 🙂 ). Он ждет, пока к нему не поступит запрос, обрабатывает его и посылает ответ, который выглядит примерно так:

HTTP/1.1 <код_ответа> <сообщение><n>
<заголовочное_поле>: <значение><n>
<заголовочное_поле>: <значение><n>
[..заголовочных полей может быть много..]<n>
<заголовочное_поле>: <значение><n>
<n>
<тело_документа>

Получив запрос, сервер должен выдать клиенту код ответа (это трехзначное число), а также сопутствующее ему сообщение. Например, если запрошенный документ был найден, первая строка ответа будет примерно следующей:

HTTP/1.1 200 Ok

Если коды ответов жестко заданы стандартом (200 означает, что запрос был успешно обработан и будет возвращен соответствующий документ/информация), то <сообщение> зависит только от вашей фантазии (конечно, по смыслу оно должно совпадать с <кодом_ответа>. Например, вместо «HTTP/1.1 200 Ok» мы можем послать «HTTP/1.1 200 You want it, you’ll get it» :).

В ответе сервера также могут быть заголовочные поля. Они содержат дополнительные сведения об ответе и данных, идущих вместе с ним. Далее я приведу важнейшие (для нас).

Content-Type — этот заголовок задает тип отдаваемых данных. Главнейшие типы заданы в стандарте, и от того, что вы напишите в данном поле, зависит поведение клиента при приеме данных. Например, если вы посылаете браузеру пользователя html-файл, то необходимо указать тип данных ‘text/html’, иначе браузер может отобразить файл неправильно, либо вообще не будет его отображать, а предложит пользователю его скачать.

Content-Length — здесь указывается длина данных (не включая сам ответ с заголовочными данными) в байтах.

Исходя из вышесказанного, ответ сервера будет выглядеть примерно так:

HTTP/1.1 200 Ok<n>
Content-Type: text/html<n>
Content-Length: <длина_документа><n>
<n>
<тело_документа>

Если мы хотим отослать простой html-файл, то можем сократить ответ до следующего:

HTTP/1.1 200 Ok<n>
Content-Type: text/html<n>
<n>
<тело_документа>

В результате получаем следующее. Клиент шлет запрос на получение документа, лежащего, например, в корне сервера:

GET / HTTP/1.1<n>
User-Agent: ManualSender/1.0 :)<n>
Host: www.someoneserver.com<n>
<n>

Сервер получает этот запрос, обрабатывает и выдает корневую страницу:

HTTP/1.1 200 Ok<n>
Content-Type: text/html<n>
<n>
<html>
<head><title>Добро пожаловать на HTTP-сервер!</title></head>
<body>Вы находитесь на нашем http-сервере</body>
</html>

Код приложения

;---------------------------------------------------------------------
; http.asm
;---------------------------------------------------------------------

format PE console

entry start

; Подключаемые файлы

include '....includekernel.inc'
include '....includeuser.inc'
include '....includemacrostdcall.inc'
include '....includemacroimport.inc'
include 'winsock.inc'
include 'macros.inc'

; Используемые значения

WM_SOCKET = WM_USER+100
INBUF_LEN     = 100000

; Секции программы

section '.data' data readable writeable

include 'strings.inc'

  hInstance dd ?
  http_class dd ?
  hwnd dd ?
  msg dd ?
  sock dd ?
  rv dd ?
  wc WNDCLASS
  wsadata WSADATA
  saddr SOCKADDR_TCP
  iaddr SOCKADDR_TCP
  buf rb INBUF_LEN

section '.code' code executable readable

include 'http_window.asm'

start:

      invoke GetModuleHandle, 0
      mov [hInstance], eax

      ; Инициализируем сокеты
      invoke WSAStartup, 101h, wsadata
      test eax, eax
      jnz error.wsanotinit

      ; Создаем окно
      jmp make_http_window

      ; Цикл обработки сообщений
   .msg_loop:
        invoke GetMessage,msg,NULL,0,0
        test eax,eax
        jz .exit
        invoke TranslateMessage,msg
        invoke DispatchMessage,msg
        jmp .msg_loop

   .exit:
      ; Очищаем сокеты
      invoke WSACleanup
      invoke ExitProcess, 0

error:

   .recv_error:

      ccall [printf], _recv_error
      jmp start.exit

   .connection_was_closed:

      ccall [printf], _connection_was_closed
      jmp start.exit

   .wsanotinit:

      ccall [printf], _wsanotinit, eax, wsadata.size
      jmp start.exit

   .bind_error:

      ccall [printf], _bind_error
      jmp start.exit

   .listen_error:

      ccall [printf], _listen_error
      jmp start.exit

   .cant_createsocket:

      invoke WSAGetLastError
      ccall [printf], _cant_createsocket, eax
      jmp start.exit

   .cant_resolve:

      ccall [printf], _fmtstr, _cant_resolve
      jmp start.exit

   .select_error:
      invoke WSAGetLastError
      cinv printf, _select_error, eax
      jmp start.exit

   .accept_error:
      invoke WSAGetLastError
      cinv printf, _accept_error, eax
      jmp start.exit


section '.idata' import data readable writeable

  library kernel32, 'kernel32.dll', 
          winsock, 'ws2_32.dll', 
          msvcrt, 'msvcrt.dll', 
          user32, 'user32.dll'

  kernel32:
  import ExitProcess, 'ExitProcess', 
         GetModuleHandle, 'GetModuleHandleA', 
         GetLastError, 'GetLastError', 
         WriteFile, 'WriteFile', 
         GetStdHandle, 'GetStdHandle', 
         RtlZeroMemory, 'RtlZeroMemory', 
         lstrlen, 'lstrlen', 
         lstrcmp, 'lstrcmp'

  user32:
  import RegisterClass, 'RegisterClassA', 
         DefWindowProc, 'DefWindowProcA', 
         CreateWindowEx, 'CreateWindowExA', 
         DestroyWindow, 'DestroyWindow', 
         GetMessage, 'GetMessageA', 
         TranslateMessage, 'TranslateMessage', 
         DispatchMessage, 'DispatchMessageA', 
         MessageBox, 'MessageBoxA', 
         ShowWindow, 'ShowWindow'

  winsock:
  import WSAStartup, 'WSAStartup', 
         WSACleanup, 'WSACleanup', 
         socket, 'socket', 
         gethostbyname, 'gethostbyname', 
         connect, 'connect', 
         WSAGetLastError, 'WSAGetLastError', 
         recv, 'recv', 
         send, 'send', 
         htons, 'htons', 
         bind, 'bind', 
         listen, 'listen', 
         WSAAsyncSelect, 'WSAAsyncSelect', 
         accept, 'accept', 
         closesocket, 'closesocket'

  msvcrt:
  import printf, 'printf'

;---------------------------------------------------------------------
; http.asm
;---------------------------------------------------------------------
; Создание скрытого окна, которое будет получать сообщения от сокета
make_http_window:

     ; Регистрируем класс окна
     mov eax, [hInstance]
     mov [wc.hInstance], eax
     mov [wc.hIcon], 0
     mov [wc.hCursor], 0
     mov [wc.style], 0
     mov [wc.lpfnWndProc], http_window_proc
     mov [wc.cbClsExtra], 0
     mov [wc.cbWndExtra], 0
     mov [wc.hbrBackground], COLOR_BTNFACE+1
     mov [wc.lpszMenuName], 0
     mov [wc.lpszClassName], _http_window_class
     invoke  RegisterClass, wc
     mov [http_class], eax
     test eax, eax
     jnz .create_http_window
     cinv printf, _fmtstr, _class_not_registered
     jmp start.exit
  .create_http_window:
     ; Создаем окно
     invoke GetModuleHandle, 0
     invoke CreateWindowEx, NULL, [http_class], _http_window_name, 
                            WS_SYSMENU, 100, 100, 100, 100, NULL, NULL, 
                            [hInstance], 0
     mov [hwnd], eax
     test eax, eax
     jnz start.msg_loop
     cinv printf, _fmtstr, _window_not_created
     br
     invoke GetLastError
     cinv printf, _fmtnum, eax

     jmp start.exit

proc http_window_proc, hWnd, wmsg, wparam, lparam
     enter
     push ebx esi edi
     cmp [wmsg], WM_CREATE
     je .wmcreate
     cmp [wmsg], WM_DESTROY
     je .wmdestroy
     cmp [wmsg], WM_SOCKET
     je .wmsocket

  .defwndproc:
     invoke DefWindowProc, [hWnd], [wmsg], [wparam], [lparam]
     jmp .finish

  .wmcreate:

     cinv printf, _start_sockets
     ; Создаем сокет
     invoke socket, AF_INET, SOCK_STREAM, 0
     cmp eax, -1
     je error.cant_createsocket
     mov [sock], eax
     ; Указываем Windows, чтобы она извещала нас о входящих соединениях
     invoke WSAAsyncSelect, [sock], [hWnd], WM_SOCKET, FD_ACCEPT
     cmp eax, -1
     je error.select_error

     ; Получаем адрес localhost
     invoke gethostbyname, _localhost
     test eax, eax
     jz error.cant_resolve
     mov eax, [eax+12]
     mov eax, [eax]
     mov eax, [eax]

     ; Подготавливаем saddr
     mov [saddr.sin_addr], eax
     mov [saddr.sin_family], AF_INET
     invoke htons, 80
     mov [saddr.sin_port], ax
     ; Биндим сокет к локальному адресу
     invoke bind, [sock], saddr, saddr.size
     test eax, eax
     jnz error.bind_error

     ; Начинаем слушать порт
     invoke listen, [sock], 10
     test eax, eax
     jnz error.listen_error

     jmp .done

  .wmdestroy:
     jmp .done

  ; Сообщение от WSAAsyncSelect
  .wmsocket:
     mov eax, [lparam]
     and eax, 0FFFFh
     cmp eax, FD_ACCEPT
     je .fd_accept
     cmp eax, FD_READ
     je .fd_read
     cmp eax, FD_CLOSE
     je .fd_close
     jmp .done

  .fd_accept:
     invoke accept, [wparam], iaddr, 0
     cmp eax, -1
     je error.accept_error
     invoke WSAAsyncSelect, eax, [hwnd], WM_SOCKET, FD_READ + FD_CLOSE
     jmp .done

  .fd_read:
     ; Обнуляем буфер
     invoke RtlZeroMemory, buf, INBUF_LEN
     ; Читаем данные из сокета
     invoke recv, [wparam], buf, INBUF_LEN, 0
     push eax
     invoke GetStdHandle, -11
     pop edx
     invoke WriteFile, eax, buf, edx, rv, 0
     invoke send, [wparam], index, index_size, 0
     invoke closesocket, [wparam]
     jmp .done

  .fd_close:
     invoke closesocket, [wparam]
     jmp .done

  .done:
     xor eax, eax

  .finish:
     pop ebx esi edi
     return
;---------------------------------------------------------------------
; strings.inc
;---------------------------------------------------------------------
_http_window_class db 'http_server_window_class', 0
_http_window_name db 'Noname', 0
_fmtstr db '%s', 0
_fmtnum db '%d', 0
_br db 0Dh, 0Ah, 0
_window_not_created db 'Window is not created', 0
_class_not_registered db 'Class is not registered', 0
_localhost db 'localhost', 0
_cant_resolve db "Can't resolve host", 0
_cant_createsocket db "Can't create socket: %d", 0
_bind_error db 'Error binding to host', 0
_listen_error db 'Error starting listening', 0
_wsanotinit db 'WSA not initialized: %d, %d', 0
_connection_was_closed db 'Connection was closed', 0
_recv_error db 'Receiving error', 0
_select_error db 'WSAAsyncSelect error %d', 0
_accept_error db 'Accept error %d', 0
_start_sockets db 'Starting sockets', 0Dh, 0Ah, 0
_shutdown_sockets db 'Shutdowning sockets', 0
_inbound_connection db 'Inbound connection', 0
_method_get db 'GET', 0

index db 'HTTP/1.1 200 Ok', 0Dh, 0Ah
      db 'Content-type: text/html', 0Dh, 0Ah
      db 0Dh, 0Ah
      db '<html>', 0Dh, 0Ah
      db '<head>', 0Dh, 0Ah
      db '<title>Welcome to HTTP Server!</title>', 0Dh, 0Ah
      db '</head>', 0Dh, 0Ah
      db '<body>', 0Dh, 0Ah
      db '<h2>HTTP Server Online</h2>', 0Dh, 0Ah
      db 'Best http server in the world!', 0Dh, 0Ah
      db '</body>', 0Dh, 0Ah
      db '</html>'
index_size = $-index

Анализ кода

«Веб-сервер», чей исходный код был приведен выше, очень примитивен. Он умеет только принимать запрос и, не проверяя его на правильность, выдавать только приветственную html-страницу.

В http.asm все, я надеюсь, достаточно понятно. Мы инициализируем сокеты, создаем окно (для чего, я поясню позже), входим в цикл обработки сообщений, а перед тем, как завершить работу приложения, вызываем WSACleanup.

Самое интересное находится в http_window.asm. При обработке сообщения WM_CREATE мы создаем сокет:

     ; Создаем сокет
     invoke socket, AF_INET, SOCK_STREAM, 0
     cmp eax, -1
     je error.cant_createsocket
     mov [sock], eax

А потом вызываем следующую функцию:

     ; Указываем Windows, чтобы она извещала нас о входящих соединениях
     invoke WSAAsyncSelect, [sock], [hWnd], WM_SOCKET, FD_ACCEPT

Вот что об этой функции говорит Platform SDK:

[ начало описания функции WSAAsynctSelect ]

WSAAsyncSelect

Функция WSAAsyncSelect указывает Windows посылать сообщения о событиях, касающихся определенного сокета.

int WSAAsyncSelect(
  SOCKET <>,           
  HWND hWnd <>,          
  unsigned int wMsg <>,  
  long lEvent <>

);

Параметры:

s — Дескриптор сокета, о событиях, связанных с которым, будет сообщаться.

hWnd — Хэндл окна, которому будут посылаться эти сообщения.

wMsg — Сообщение, которое будет посылаться.

lEvent — Битовая маска, в которой задаются интересующие события.

Возвращаемые значения

Если вызов функции WSAAsyncSelect прошел успешно, возвращаемое значение будет равно нулю. В противном случае будет возвращено SOCKET_ERROR, а код ошибки можно будет получить, вызвав WSAGetLastError.

[ конец описания ]

Учтите, что после того, как WSAAsyncSelect отошлет вам сообщение о конкретном событии, связанном с сокетом, то пока вы не предпримите определенных действий, нового сообщения о таком же событии вы не получите. Например, если вы получили сообщение FD_ACCEPT (кто-то пытается законнектиться к вам), то сообщения о другой попытки коннекта вы не получите до тех пор, пока не вызовите функцию accept.

Мы задаем WM_SOCKET, определенное в http.asm, в качестве сообщение, которое будет присылаться Windows, когда произойдет интересующее нас сообщение. Необходимая информация будет находиться в wParam (дескриптор сокета, с которым связано событие) и в lParam (в нижнем слове — код события).

Теперь, когда кто-нибудь попытаемся приконнектиться к сокету, наше окно получит соответствующее уведомление от операционной системы. Впрочем, сначала нужно ассоциировать созданный сокет с определенным адресом и портом, к которым и должны будут коннектиться посетители веб-сервера.

     ; Получаем адрес localhost
     invoke gethostbyname, _localhost
     test eax, eax
     jz error.cant_resolve
     mov eax, [eax+12]
     mov eax, [eax]
     mov eax, [eax]

     ; Подготавливаем saddr
     mov [saddr.sin_addr], eax
     mov [saddr.sin_family], AF_INET
     invoke htons, 80
     mov [saddr.sin_port], ax

Веб-сервер будет «висеть» на localhost’е (т.е. на локальной машине) на 80-ом порту, который является стандартным HTTP-портом. Если в адресе сайта прямо не указан порт, то браузер будет обращаться к 80-ому порту.

     ; Начинаем слушать порт
     invoke listen, [sock], 10
     test eax, eax
     jnz error.listen_error

Собственно, в данных строчках и содержится ответ на то, как сделать из приложения сервер (не обязательно web). Это делает функция listen.

[ начало описания функции listen ]

listen

Функция listen устанавливает сокет в состояние, в котором он слушает порт на предмет входящих соединений.

int listen(
  SOCKET <>,    
  int backlog <>

);

Параметры

s — Дескриптор сокета

backlog — Максимальное количество входящих соединений.

Возвращаемые значения

Если во время вызова не произошло никакой ошибки, listen возвратит ноль. В противном случае будет возвращено значение SOCKET_ERROR, а код ошибки можно будет получить с помощью функции WSAGetLastError.

[ конец описания ]

  ; Сообщение от WSAAsyncSelect
  .wmsocket:
     mov eax, [lparam]
     and eax, 0FFFFh
     cmp eax, FD_ACCEPT
     je .fd_accept
     cmp eax, FD_READ
     je .fd_read
     cmp eax, FD_CLOSE
     je .fd_close
     jmp .done

Было получено сообщение WM_SOCKET. Это значит, что произошло какое-то интересующее нас событие, связанное со слушающим сокетом.

  .fd_accept:
     invoke accept, [wparam], iaddr, 0
     cmp eax, -1
     je error.accept_error

Кто-то пытается подсоединиться к нашему веб-серверу. Вызываем функцию accept, чтобы разрешить входящее соединение.

[ начало описания функции accept ]

accept

Функция accept разрешает входящее соединение.

SOCKET accept(
  SOCKET s,
  struct sockaddr FAR *addr,
  int FAR *addrlen

);

Параметры

s — Дескриптор сокета, который ранее был помещен в состояние прослушивания с помощью функции listen. Фактическое соединение осуществляется с помощью сокета, который возвращается accept’ом.

addr — Необязательный указатель на буфер, который получит адрес того, кто пытается подсоединиться к серверу.

addrlen — Необязательный указатель на двойное слово, которое содержит длину addr.

Возвращаемые значения

Если не произошло никакой ошибки, accept возвратит дескриптор нового сокета, через который и будет происходить соединение.

В противном случае будет возвращен INVALID_SOCKET, а код ошибки можно будет получить с помощью функции WSAGetLastError.

Переменная, на которую указывает addrlen, вначале содержит объем, занятый структурой, на которую указывает addr. По возвращении она будет содержать длину возвращенного адреса в байтах.

[ конец описания ]

     invoke WSAAsyncSelect, eax, [hwnd], WM_SOCKET, FD_READ + FD_CLOSE
     jmp .done

Соединение разрешено, и мы вызываем функцию WSAAsyncSelect, чтобы получить соответствующее уведомление, когда можно будет читать из сокета или он будет закрыт.

  .fd_read:
     ; Обнуляем буфер
     invoke RtlZeroMemory, buf, INBUF_LEN
     ; Читаем данные из сокета
     invoke recv, [wparam], buf, INBUF_LEN, 0
     push eax
     invoke GetStdHandle, -11
     pop edx
     invoke WriteFile, eax, buf, edx, rv, 0
     invoke send, [wparam], index, index_size, 0
     invoke closesocket, [wparam]
     jmp .done

Здесь все просто. Пришло сообщение о том, что можно читать из сокета, что мы и делаем. Все считанное мы выводим на консоль (интересно же, что клиент прислал). По-хорошему, здесь мы должны были бы провести синтаксический разбор запроса: выяснить, какой конкретно документ он хочет, отдать его, если такого документа нет, послать сообщение об ошибке и т.п. Но поскольку я минимализировал сервер почти до предела в плане функциональности :), ничего этого здесь нет. Вместо этого мы шлем клиенту приветственный html.

  .fd_close:
     invoke closesocket, [wparam]
     jmp .done

Если сокет был закрыт клиентом, то мы его тоже закрываем со своей стороны.

Дополнительная литература

Для получения подробной информации о протоколе HTTP я рекомендую вам обратиться к RFC 2068.

Заключение

Надеюсь, вы почерпнули из этого туториала какую-нибудь полезную информацию. Напоследок мне хотелось бы сказать, что хотя составлять конкуренцию таким грандам как Apache и IIS без веских на то оснований, возможно, и не стоит, тем не менее, собственный маленький веб-сервер может быть очень полезен. Мне, например, предложили встроить в него механизм самораспространения, «чтобы он сам приходил к людям на дом» и устанавливался «через упрощенную процедуру инсталляции» ака Outlook. Другим, менее чреватым в плане возможных последствий для автора, вариантом может быть создание утилиты удаленного (не обязательно скрытого) администрирования, причем в качестве клиента будет выступать браузер, что весьма удобно, так как отпадет надобность в написании сопутствующей серверу клиентской программы. Возможно, вы найдете еще какое-нибудь применение для http-сервера. Все в ваших руках!

(c) Aquila / Hi-Tech, 2002

[C] Aquila / WASM.RU

Источник: wasm.ru /05.08.2002/


Поделиться в соц сетях

Создание простого веб-сервера с помощью Node.js и Express

Node.js с Express  —  это популярный дуэт, используемый многими приложениями во всем мире. Данный урок познакомит вас с функциональностью этих инструментов на примере сборки простого веб-сервера.

Skillfactory.ru

Создаваемый сервер будет обслуживать HTML-страницу, доступную другим людям. К концу статьи вы усвоите базовые знания о:

  • Node.js;
  • Express;
  • npm;
  • создании маршрутов Express;
  • обслуживании HTML;
  • настройке статических ресурсов Express.

Совет: не копируйте код урока, а напишите его сами. Это позволит вам лучше его понять и усвоить.

Создание и инициализация проекта

Первым шагом будет создать пустой каталог для проекта. Это можно сделать обычным способом либо из терминала с помощью следующих команд:

mkdir express-server
cd express-server

После создания проекта нужно его инициализировать:

npm init -y

Эта команда создает файл package.json и инициализирует его с предустановленными значениями. Если вы захотите сами заполнить его поля, удалите флаг -y и следуйте инструкциям. 

После инициализации проекта Node.js мы переходим к следующему шагу  —  добавлению Express. Установка пакетов в Node.js выполняется командой npm install packageName.

Для добавления последней стабильной версии Express выполните:

npm install express

После установки Express файл package.json будет выглядеть так:

{
  "name": "express-server",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1"
  }
}

Express перечислен среди dependencies, значит, он установился успешно. Переходим к третьему шагу  —  созданию сервера.

Создание сервера Express

Прежде чем продолжать, нужно создать для сервера JS-файл. Выполните в терминале следующую команду:

touch index.js

Теперь откройте этот файл и пропишите в нем:

const express = require('express');
const app = express();

Что эти строки делают?

  1. Первая импортирует Express в проект, чтобы его можно было использовать. При каждом добавлении в проект пакета необходимо импортировать его туда, где он будет использоваться.
  2. Вторая строка вызывает функцию express, которая создает новое приложение, после чего присваивает результат константе app.

Создание маршрутов и прослушивание порта

Говоря простым языком, маршрут представляет конечную точку, к которой могут обращаться пользователи. Он ассоциируется с HTTP-глаголом (например, GET, POST и пр.) и получает путь URL. Кроме того, он получает функцию, которая вызывается при обращении к этой конечной точке.

Пропишите в файле следующий код:

app.get('/', (req, res) => {
    res.send({ message: 'Hello WWW!' });
});

Разберем его согласно приведенной ранее структуре:

  • Он связан с HTTP-глаголом  —  в данном случае GET.
  • Он получает URL  —  здесь это домашняя страница (/).
  • Он получает функцию, которая будет вызываться при обращении к конечной точке.

Следовательно, когда пользователь выполняет запрос GET к домашней странице, т.е. localhost:3333, вызывается стрелочная функция и отображается фраза “Hello WWW!”

Последним шагом подготовки сервера к работе будет настройка слушателя. Понадобится указать для приложения конкретный порт. Напишите нижеприведенный код в конец JS-файла.

app.listen(3333, () => {
    console.log('Application listening on port 3333!');
});

Чтобы иметь возможность запускать сервер, вам нужно будет вызывать метод listen. При этом вы также можете изменить номер порта (3333) на любой другой.

Доступ к приложению в браузере

Для запуска приложения выполните в терминале node index.js. Имейте в виду, что index.js  —  это произвольное имя, которое я выбрал для данного урока, так что можете назвать его app.js или как-угодно иначе.

Теперь, когда сервер запущен, можно обратиться к нему в браузере. Перейдите по адресу http://localhost:3333/, перед вами должно отобразиться следующее сообщение:

Скриншот приложения

Вы отлично справились с настройкой веб-сервера Node.js + Express. В следующем разделе мы настроим статическое содержимое, а именно JavaScript, CSS, HTML, изображения и т.д.

Статические файлы

Наше приложение пока что выглядит не очень. Почему бы не добавить ему структуру и стилизацию? Но куда все это нужно добавлять?

В этом разделе вы узнаете, как настраивать и передавать статические ресурсы, такие как HTML, JavaScript, CSS и изображения.

Импорт модуля path

Первым делом нужно импортировать в приложение модуль path. Устанавливать ничего не нужно, потому что path предустановлен в Node изначально.

Пропишите в начале файла эту строку:

const path = require('path');

Зачем вообще этот модуль? Он позволяет генерировать абсолютные пути, которые необходимы для передачи статических файлов. Добавьте следующую строку в приложение перед определением маршрутов:

app.use(express.static(path.join(__dirname, 'public')));

path.join получает два аргумента:

  • Текущую рабочую директорию (cwd).
  • Вторую директорию, которую нужно объединить с cwd.

В качестве упражнения попробуйте вывести в консоль path.join(__dirname, 'public') и посмотрите, что получится.

На данный момент сервер должен выглядеть так:

const path = require('path');
const express = require('express');
const app = express();

app.use(express.static(path.join(__dirname, 'public')))

app.get('/', (req, res) => {
    res.send({ message: 'Hello WWW!' });
});

app.listen(3333, () => {
    console.log('Application listening on port 3333!');
});

Создание каталога public и добавление ресурсов

Создайте каталог и перейдите в него с помощью следующих команд:

mkdir public
cd public

Теперь создадим пустые файлы, куда затем добавим HTML, CSS и JavaScript. Выполните в терминале следующие команды:

touch app.js
touch index.html
touch styles.css

Мы оставим app.js максимально простым, добавив только сообщение, подтверждающее, что он работает: 

alert('it works');

То же и с styles.css. Для проверки его работоспособности мы установим цвет фона на синий:

html {
    background-color: blue;
}

В завершении нужно написать HTML, чтобы отображать все это на домашней странице. Откройте index.js и добавьте следующий HTML-код:

Skillfactory.ru

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="/app.js"></script>
    <link rel="stylesheet" href="/styles.css">
    <title>My server</title>
</head>
<body>
    <h1>My server</h1>
    <p>Server built with Node.js and Express</p>
</body>
</html>

Теперь остается всего один шаг. 

Передача HTML-файла

Мы почти закончили. Осталось только обработать HTML-код. Для этого нужно перейти в файл index.js и прописать в нем следующее:

app.get('/', (req, res) => {
res.sendFile(`${__dirname}/public/index.html`);
});

Если вы уже ранее работали с Node.js и Express, то можете спросить: “Что здесь делает метод sendFile и почему мы не используем render?” Метод render мы использовать не можем, так как не задействуем никакой движок (например, Pug, EJS и т.д.). Следовательно, когда кто-либо обращается к домашней странице, мы отправляем назад HTML-файл.

Итоговый код сервера должен выглядеть так:

const path = require('path');
const express = require('express');
const app = express();

app.use(express.static(path.join(__dirname, 'public')))

app.get('/', (req, res) => {
    res.sendFile(`${__dirname}/public/index.html`);
});

app.listen(3333, () => {
    console.log('Application listening on port 3333!');
});
COPY

Если теперь вы перейдете по http://localhost:3333, то увидите домашнюю страницу с синим фоном. Естественно, сначала нужно будет закрыть надоедливое всплывающее окошко.

Скриншот приложения

Заключение

К завершению статьи у вас должно получиться простое веб-приложение.

В этом уроке мы узнали:

  • о Node.js;
  • об Express и о том, как использовать его для создания небольшого веб-приложения;
  • как создавать маршруты;
  • как настраивать статическое содержимое в приложении Node.js + Express;
  • как передавать простой HTML-файл в Express.

Читайте также:

  • Создаем Telegram-бота с помощью Node.js за 3 минуты
  • Найти и обезвредить: утечки памяти в Node.js
  • Создание многопользовательской игры с использованием Socket.io при помощи NodeJS и React

Читайте нас в Telegram, VK и Яндекс.Дзен


Перевод статьи Catalin Pit: Node.js + Express Tutorial for 2021 — Build a Web Server Using Node.js and Express

Запуск HTTP-сервера с Node

Код для этого примера вы можете найти здесь.

Node не только содержит возможность обрабатывать JS-файлы, как мы только что сделали, он также может создать HTTP-сервер. Мы собираемся рассмотреть создание HTTP-сервера с Node-фреймворком Express для обработки HTML-файла.

В первом приложении мы только выводили что-то в консоль. Двигаясь вперёд, мы учтём чему уже научились для следующего шага, в котором мы можем организовать веб-сайт для наших пользователей. На один шаг мы станем ближе к полноценному веб-приложению.

Давайте придерживаться того же приложения, добавив фреймворк Express с выдачей HTML-файла. Нам понадобятся те же файлы (package.json, server.js) и к ним мы добавим новый файл index.html.

Express: Node-фреймворк

Одним из самых больших преимуществ Node является то, что он содержит поддержку множества пакетов. Сообщество отправляет много пакетов в npm и на момент написания в нём хранится 129257 пакетов, которые загрузили свыше 17694831 раз за последний день. Это большое достижение!

Пакеты расширяют функциональность нашего приложения и найдутся пакеты для многих разных случаев применения.

Вы, возможно, слышали о Grunt, Gulp или даже препроцессорах CSS вроде Less — всё это может быть пакетом.

Express — лёгкая платформа для создания веб-приложений с использованием Node.js. Express помогает организовать веб-приложение на стороне сервера. Сайт ExpressJS описывает его как «минимальной и гибкий Node-фреймворк для веб-приложений».

Express скрывает много внутренней работы Node, которая позволяет вам погрузиться в код приложения, получать всякие штуки и работать намного быстрее. Он прост для изучения и ещё даёт вам немного гибкости с его структурой.

Существует причина, почему в настоящее время это самый популярный фреймворк для Node. Вот несколько больших имён использующих Express:

  • MySpace
  • LinkedIn
  • Klout
  • Segment.io

Для просмотра полного списка зайдите на эту страницу.

Express поставляется с несколькими замечательными возможностями, которые добавят лёгкости в вашу разработку:

  • маршрутизация;
  • обработка запросов;
  • настройки приложения;
  • связующее программное обеспечение.

Не волнуйтесь, если эти термины являются для вас новыми. Просто знайте, что Express делает разработку намного проще и работать с ним в радость.

Установка Express

Пакеты для конкретного приложения Node определяются в package.json. Для получения установленных пакетов вы можете использовать один из двух методов:

  • Метод 1: Написать пакет в package.json.
  • Метод 2: В командной строке использовать npm install.

Мы собираемся использовать второй метод. Перейдите в командную строку и наберите:

$ npm install express --save

Модификатор –save сообщает npm, что он должен записать этот пакет в ваш файл package.json. Если вы выполните эту команду и посмотрите файл package.json, то заметите, что пакет появился в разделе dependencies. Вы также заметите, что была создана новая папка с именем node_modules. В ней Node хранит пакеты для конкретного проекта.

Меняться проектами между разработчиками и сотрудниками очень легко. Просто отправьте другим пользователям ваш проект и они запустят npm install чтобы установить всё из раздела dependencies.

Поскольку у нас уже есть Node и готов Express, используем их для создания HTTP-сервера и выдачи HTML-файла нашим пользователям.

Создание HTTP-сервера и отправка HTML-файла

Начнём с лёгкой части на нашем пути — с HTML-файла. В проекте создайте новый файл index.html и поместите внутрь следующее:

<!DOCTYPE html>
<html lang="ru">
<head>
  <meta charset="UTF-8">
  <title>Моё Node-приложение!</title>

  <!-- CSS -->
  <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
  <style>
  body { padding-top:50px; }
  </style>
</head>
<body class="container">
  
  <div class="jumbotron">
    <h1>Моё приложение!</h1>
  </div>
</body>
</html>

Мы будем ссылаться на CSS из фреймворка Bootstrap через Bootstrap CDN, это поможет нам быстро сделать стилизацию для этой демонстрации.

Двинемся вперёд и создадим наш HTTP-сервер в Node с помощью Express. Удалите всё из файла server.js и добавьте то что нам понадобится:

// берём Express
var express = require('express');

// создаём Express-приложение
var app = express();

// создаём маршрут для главной страницы
// http://localhost:8080/
app.get('/', function(req, res) {
  res.sendfile('index.html');
});

// запускаем сервер на порту 8080
app.listen(8080);
// отправляем сообщение
console.log('Сервер стартовал!');

Кроме этого файла больше ничего не требуется, чтобы использовать Express для запуска HTTP-сервера и отправки HTML-файла!

require() является основным путём вызова пакета в Node. После создания Express-приложения в app, мы можем определить маршрут с помощью переменной HTTP. app.get() создаёт GET маршрут /.

При создании маршрутов, мы всегда будем иметь доступ к req (запрос) и res (ответ). Запрос содержит информацию из браузера. Ответ — это то, что мы отправим обратно пользователю. Мы используем sendfile(), но гораздо больше вещей можно сделать, отправляя данные обратно в формате JSON с помощью res.json().

Сервер запускается через app.listen() и туда же передаётся желаемый порт 8080.

Чтобы убедиться что всё работает, перейдите в командную строку, чтобы обработать этот файл и запустить сервер.

$ nodemon server.js

Теперь мы можем посмотреть на наш сайт в браузере по адресу http://localhost:8080.

Всякий раз, когда мы запускаем сервер с Node, он будет размещён по адресу http://localhost:НОМЕР_ПОРТА.

Это очень лёгкий и быстрый способ создать HTTP-сервер и начать разработку. Node и Express могут применяться для создания удивительных приложений или при необходимости они просто запускают простой сервер для работы.

Отлично! Мы уже много сделали с Node:

  • установили Node;
  • обработали очень простой файл;
  • использовали npm для установки пакета;
  • создали HTTP-сервер с Express;
  • отобразили HTML-файл.

Давайте сделаем следующий шаг и создадим приложение, которое на самом деле показывает соответствующие данные.

Когда заходит речь о сервере, то под этим словом может подразумеваться либо программа (сервер), либо компьютер (сервер), на котором работает одна или несколько программ-серверов. Здесь, очевидно, рассматривается программа-сервер.

Сама по себе программа-сервер — это лишь часть распределенной программы, которую называют клиент-серверным приложением. Здесь слово «распределенная» означает, что функциональность программы разделена на две части: клиентскую и серверную. Клиентская часть (клиент) отправляет запросы, а серверная часть (сервер) отвечает на них. Клиент и сервер могут находиться как на одном и том же компьютере, так и на разных компьютерах в компьютерной сети.

В вебе клиент и сервер общаются посредством запросов от клиента и ответов клиенту от сервера в рамках протокола HTTP (если рассматривать общение по компьютерной сети на прикладном уровне сетевой модели). В вебе клиент и сервер вместе называют распределенным веб-приложением (также называют просто «веб-приложением»). Клиент могут называть веб-клиентом, а сервер — веб-сервером. Также веб-приложением могут называть как отдельно клиент, так и отдельно сервер. Еще веб-приложением могут называть часть кода сервера, которую пишет программист со стороны сервера, используя библиотеки (фреймворки), написанные другими. Так что с термином «веб-приложение» есть некоторая путаница. Нужно понимать, что он означает, ориентируясь по контексту, в котором он применяется.

Таким образом, нужно понимать, что для демонстрации работы веб-сервера понадобится и веб-клиент. У нас в качестве веб-клиента будет выступать браузер. Браузер и веб-сервер, который будет написан и запущен далее, составят распределенное веб-приложение.

* * *

Программист, который хочет написать веб-сервер, должен понимать, что сетевая модель состоит из уровней и при каждом запросе от клиента и ответе клиенту от сервера сообщение проходит все уровни сетевой модели. На прикладном уровне (самом верхнем) общение проходит по протоколу HTTP, как было указано выше. На уровне ниже (транспортном) общение проходит по протоколу TCP, что значит, что веб-сервер должен манипулировать номером порта. На уровне ниже транспортного (сетевой уровень) общение проходит по протоколу IP, что значит, что веб-сервер должен манипулировать IP-адресом.

Обычно в учебниках и руководствах, когда речь заходит о написании простейшего веб-сервера, используется IP-адрес «127.0.0.1». Это неспроста. Во-первых, нужно отметить, что тут подразумевается общение по сети в рамках протокола IP версии 4 (IPv4), это видно по строению указанного IP-адреса. Во-вторых, указанный IP-адрес входит в группу IP-адресов (подсеть) 127.0.0.0 – 127.255.255.255. Эта группа IP-адресов предназначена для локального использования, то есть для использования в рамках одного компьютера (IP-адреса этой группы не могут использоваться в интернете).

Таким образом, речь идет о написании и запуске локального веб-сервера. То есть обе части нашего распределенного веб-приложения (клиентская и серверная) будут находиться на одном и том же компьютере — нашем компьютере. Для общения между клиентом и сервером у нас, как я понимаю, даже не будет задействована сетевая карта нашего компьютера.

Почему в учебниках используется этот IP-адрес? Потому что локальный веб-сервер обычно используется для отладки распределенных веб-приложений. Это логично: пока наше распределенное веб-приложение не отлажено, нет смысла соваться в интернет или локальную сеть (в том числе и по соображениям безопасности). Кроме этого, для отладки распределенного веб-приложения в локальной сети или в интернете потребуется доступ к частям распределенного веб-приложения с обеих сторон (с двух компьютеров): и со стороны клиента, и со стороны сервера. Конечно же, удобнее отлаживать и тестировать распределенное веб-приложение на одном и том же нашем компьютере, так что логично, что мы будем использовать локальный веб-сервер, а сетевое общение будет происходить без выхода в сеть.

После отладки веб-сервера в качестве локального (с IP-адресом «127.0.0.1») этот же веб-сервер можно легко применить в локальной сети или в интернете, просто поменяв IP-адрес на нужный.

* * *

Текст программы (простейшего веб-сервера) на языке JavaScript я взял из статьи «Introduction to Node.js» (по-русски «Введение в среду выполнения Node.js»), которая является частью учебника по работе со средой выполнения «Node.js»:

https://nodejs.dev/learn/introduction-to-nodejs

Саму эту среду выполнения я установил ранее, об этом я написал несколько отдельных постов (например, вот этот). Напомню, у меня на компьютере установлена операционная система «Windows 10 Pro» (64-разрядная).

Итак, текст программы на языке JavaScript:

const http = require('http'); // включение модуля из стандартной библиотеки Node.js

const hostname = '127.0.0.1'; // веб-сервер работает локально
const port = 3000;
                              // веб-сервер возвратит такой ответ на любой запрос
const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello Worldn');
});
                              // запуск веб-сервера
server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

Программа никак не анализирует параметр req, содержащий объект с информацией о запросе от клиента. Ведь наш веб-сервер очень примитивен: на любой запрос он лишь возвращает ответ (объект в переменной res) с кодом состояния 200 (запрос обработан успешно) и с текстом «Hello World» в теле ответа.

Этот текст я поместил в текстовый файл с именем index.js (в кодировке UTF-8). В принципе, имя файла может быть и другим, это не имеет значения для работы веб-сервера. Имя файла понадобится при запуске программы в среде выполнения «Node.js». Этот файл я поместил в следующий каталог:

C:inetpubwwwrootnode

По идее, каталог тоже не имеет значения, наша программа может быть запущена из любого каталога. Главное — правильно указать путь к файлу с текстом программы при запуске ее в среде выполнения «Node.js».

Запустим нашу программу в среде выполнения «Node.js» через интерфейс командной строки. Я для этого использую программу «Windows PowerShell», но, конечно, это не единственный способ. Предварительно я захожу в нужный каталог в программе «Проводник Windows», а затем с помощью комбинации клавиши «Shift» и правой кнопки мыши (хотя тут нужно помнить, что функциональность кнопок мыши можно менять местами) открываю контекстное меню, в котором выбираю пункт «Открыть окно PowerShell здесь» (хотя тут нужно помнить, что этот пункт в контекстном меню может быть настроен по-другому). В результате этих манипуляций у меня открывается окно программы «Windows PowerShell», в котором не требуется переходить в другой каталог, мы уже и так находимся в том каталоге, где нужно.

Вводим следующую команду:

node index.js

После чего получаем следующее:

Наш веб-сервер запущен и работает, об этом свидетельствует сообщение, выдачу которого мы запланировали в своей программе:

Server running at http://127.0.0.1:3000/

Чтобы проверить работу нашего веб-сервера, откроем браузер (у меня — «Microsoft Edge» на движке «Chromium»). Откроем в браузере новую вкладку (по-английски «tab») и введем в адресной строке следующий адрес URL:

127.0.0.1:3000

Полностью (с указанием протокола — «HTTP») адрес URL вводить необязательно (хотя, можно ввести и полностью http://127.0.0.1:3000/, это не будет ошибкой), потому что браузер по умолчанию считает, что сетевое общение будет происходить по протоколу HTTP.

После введения этого адреса URL и нажатия клавиши «Enter» браузер (клиент нашего распределенного веб-приложения) отправит запрос с методом «GET» по протоколу HTTP на указанный IP-адрес и указанный порт. Наш веб-сервер (серверная часть нашего распределенного веб-приложения) вернет в ответ на запрос сообщение, в теле которого будет текст «Hello World». Итак, вот что у меня получилось:

Браузер может отображать не только HTML-страницы, но и другие веб-документы, в том числе текстовые (обычный текст). В нашем случае, как раз, браузер отобразил текстовый документ, состоящий из фразы «Hello World».

Таким образом, пока наш веб-сервер работает, мы можем сколько угодно раз открывать сколько угодно вкладок в браузере с указанным адресом URL и будем получать в ответ от веб-сервера текст «Hello World».

Как прекратить работу нашего веб-сервера? Если закрыть окно программы «Windows PowerShell», в котором мы запускали веб-сервер (или не закрывать, а ввести в этом окне комбинацию клавиш «Ctrl+C»), то работа нашей программы (простейшего веб-сервера) будет прекращена. После этого при вводе в адресную строку браузера адреса URL http://127.0.0.1:3000/ мы уже не получим в ответ текст с фразой «Hello World».

Добавить комментарий