- Инструкция по написанию HTTP-сервера на Java
- HTTPServer
- Очередь выполнения запросов
- Код сервера
- HTTPHandler
- Обработка запроса GET
- Обработка ответа
- Простой HTTP-сервер на Java
- HTTPServer
- Очередь выполнения запросов
- Код сервера
- HTTPHandler
- «). append(«Hello «) .append(requestParamValue) .append(«
- Первое знакомство с протоколом HTTP через написание простейшего Web сервера на Java
- Создание простого HTTP-сервера на Java с использованием только Java SE API
- Использование HttpServer из com.sun.net.httpserver
Инструкция по написанию HTTP-сервера на Java
Вы хотите реализовать HTTP-сервер , но не хотите рисковать написанием полноценного HTTP-сервера? Разработка HTTP-сервера с полной функциональностью не является тривиальной задачей. Но у Java есть решение этой проблемы. Java поддерживает встроенный HTTP-сервер. Просто написав 100 строк кода, мы можем разработать несколько приличный HTTP-сервер, который может обрабатывать запросы. Мы также можем использовать его для обработки других HTTP-команд.
HTTPServer
Java SDK предоставляет встроенный сервер под названием HttpServer . Этот класс относится к пакету com.sun.net .
Мы можем создать экземпляр сервера следующим образом:
HttpServer server = HttpServer.create(new InetSocketAddress("localhost", 8001), 0);
Приведенная выше строка создает экземпляр HTTPServer на локальном узле с номером порта 8001. Но есть еще один аргумент со значением 0. Это значение используется для обратной регистрации .
Очередь выполнения запросов
Когда сервер принимает запрос клиента, этот запрос сначала будет поставлен в очередь операционной системой. Позже он будет передан на сервер для обработки запроса. Все эти одновременные запросы будут поставлены в очередь операционной системой. Однако операционная система сама решит, сколько из этих запросов может быть поставлено в очередь в любой данный момент времени. Это значение представляет обратную регистрацию. В нашем примере это значение равно 0, что означает, что мы не ставим в очередь никаких запросов.
Код сервера
Мы собираемся разработать следующий HTTP-сервер:
server.createContext("/test", new MyHttpHandler()); server.setExecutor(threadPoolExecutor); server.start(); logger.info(" Server started on port 8001");
Мы создали контекст под названием test . Это не что иное, как корень контекста приложения. Второй параметр – это экземпляр обработчика, который будет обрабатывать HTTP-запросы. Мы рассмотрим этот класс в ближайшее время.
Мы можем использовать исполнителя пула потоков вместе с этим экземпляром сервера. В нашем случае мы создали пул из 10 потоков.
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor)Executors.newFixedThreadPool(10);
С помощью всего трех-четырех строк кода мы создали HTTP-сервер с корневым контекстом, который прослушивает порт!
HTTPHandler
Это интерфейс с вызванным методом handle(..) . Давайте посмотрим на нашу реализацию этого интерфейса.
private class MyHttpHandler implements HttpHandler < @Override public void handle(HttpExchange httpExchange) throws IOException < String requestParamValue=null; if("GET".equals(httpExchange.getRequestMethod())) < requestParamValue = handleGetRequest(httpExchange); >else if("POST".equals(httpExchange)) < requestParamValue = handlePostRequest(httpExchange); >handleResponse(httpExchange,requestParamValue); > private String handleGetRequest(HttpExchange httpExchange) < return httpExchange. getRequestURI() .toString() .split("\\?")[1] .split("=")[1]; >private void handleResponse(HttpExchange httpExchange, String requestParamValue) throws IOException < OutputStream outputStream = httpExchange.getResponseBody(); StringBuilder htmlBuilder = new StringBuilder(); htmlBuilder.append(""). append(""). append(""). append("Hello ") .append(requestParamValue) .append("
") .append("") .append(""); // encode HTML content String htmlResponse = StringEscapeUtils.escapeHtml4(htmlBuilder.toString()); // this line is a must httpExchange.sendResponseHeaders(200, htmlResponse.length()); outputStream.write(htmlResponse.getBytes()); outputStream.flush(); outputStream.close(); > >
Это код, который обрабатывает запрос и отправляет ответ клиенту. Запрос и ответ обрабатываются классом HttpExchange .
Обработка запроса GET
Запрос GET обрабатывается методом handleGETRequest() . Этот метод, в свою очередь, вызывает метод getRequestURI() класса HttpExchange для извлечения значения параметра запроса, содержащегося в URI. Это минимальный метод, который будет обрабатывать только один параметр, присутствующий в запросе. Тем не менее, это может быть изменено для удовлетворения различных требований.
Обработка ответа
Наконец, мы собираемся отправить наш ответ обратно клиенту. Это достигается методом handleResponse(..) . В этом методе мы получаем выходной поток, вызывая метод getResponseBody() класса HttpExchange . Позже мы можем записать содержимое HTML в выходной поток.
Наиболее важным моментом является отправка response header обратно клиенту. Если вы пропустите это, вы получите сообщение об ошибке в браузере ERR_EMPTY_RESPONSE .
Простой HTTP-сервер на Java
Вы хотите реализовать HTTP-сервер , но не хотите рисковать написанием полноценного HTTP-сервера? Разработка HTTP-сервера с полной функциональностью не является тривиальной задачей. Но у Java есть решение этой проблемы. Java поддерживает встроенный HTTP-сервер. Просто написав 100 строк кода, мы можем разработать несколько приличный HTTP-сервер, который может обрабатывать запросы. Мы также можем использовать его для обработки других HTTP-команд.
HTTPServer
Java SDK предоставляет встроенный сервер под названием HttpServer . Этот класс относится к пакету com.sun.net .
Мы можем создать экземпляр сервера следующим образом:
HttpServer server = HttpServer.create(new InetSocketAddress("localhost", 8001), 0);
Приведенная выше строка создает экземпляр HTTPServer на локальном узле с номером порта 8001. Но есть еще один аргумент со значением 0. Это значение используется для обратной регистрации .
Очередь выполнения запросов
Когда сервер принимает запрос клиента, этот запрос сначала будет поставлен в очередь операционной системой. Позже он будет передан на сервер для обработки запроса. Все эти одновременные запросы будут поставлены в очередь операционной системой. Однако операционная система сама решит, сколько из этих запросов может быть поставлено в очередь в любой данный момент времени. Это значение представляет обратную регистрацию. В нашем примере это значение равно 0, что означает, что мы не ставим в очередь никаких запросов.
Код сервера
Мы собираемся разработать следующий HTTP-сервер:
server.createContext("/test", new MyHttpHandler()); server.setExecutor(threadPoolExecutor); server.start(); logger.info(" Server started on port 8001");
Мы создали контекст под названием test . Это не что иное, как корень контекста приложения. Второй параметр — это экземпляр обработчика, который будет обрабатывать HTTP-запросы. Мы рассмотрим этот класс в ближайшее время.
Мы можем использовать исполнителя пула потоков вместе с этим экземпляром сервера. В нашем случае мы создали пул из 10 потоков.
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor)Executors.newFixedThreadPool(10);
С помощью всего трех-четырех строк кода мы создали HTTP-сервер с корневым контекстом, который прослушивает порт!
HTTPHandler
Это интерфейс с вызванным методом handle(..) . Давайте посмотрим на нашу реализацию этого интерфейса.
«). append(«Hello «) .append(requestParamValue) .append(«
Первое знакомство с протоколом HTTP через написание простейшего Web сервера на Java
Думаю что не будет преувеличением утверждать, что знание и понимание сути протокола HTTP необходимо любому, кто решил сколь-нибудь серьезно заняться любым из направлений современной Web разработки. Мой личный опыт говорит о том, что понимание это приходит не сразу. Стыдно сказать, что были времена, когда слова GET и POST были для меня сродни магическим заклинаниям, а о существовании PUT, PATCH и DELETE я даже не подозревал.
Несколько месяцев назад помимо собственно разработки я занялся также преподаванием, и возник вопрос о том, как проще и понятнее рассказать о сути протокола HTTP будущим Java разработчикам. После нескольких дней возни и ряда неудачных попыток сделать презентацию возникла идея, а почему бы не написать простейший HTTP сервер на Java, потому как ни что так хорошо не объясняет суть протокола, как его простейшая, но работающая реализация.
Как оказалось сделать это совсем не сложно. Ниже привожу код, которого будет достаточно для корректного взаимодействия с любым браузером! Все что нам понадобится это ServerSocket и немного стандартного ввода-вывода.
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.nio.charset.StandardCharsets; public class HttpServer < public static void main(String[] args) < try (ServerSocket serverSocket = new ServerSocket(8080)) < System.out.println("Server started!"); while (true) < // ожидаем подключения Socket socket = serverSocket.accept(); System.out.println("Client connected!"); // для подключившегося клиента открываем потоки // чтения и записи try (BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8)); PrintWriter output = new PrintWriter(socket.getOutputStream())) < // ждем первой строки запроса while (!input.ready()) ; // считываем и печатаем все что было отправлено клиентом System.out.println(); while (input.ready()) < System.out.println(input.readLine()); >// отправляем ответ output.println("HTTP/1.1 200 OK"); output.println("Content-Type: text/html; charset=utf-8"); output.println(); output.println("Привет всем!
"); output.flush(); // по окончанию выполнения блока try-with-resources потоки, // а вместе с ними и соединение будут закрыты System.out.println("Client disconnected!"); > > > catch (IOException ex) < ex.printStackTrace(); >> >
Пробуем запустить этот код. Стоит отметить, что порт, для которого создается ServerSocket должен быть свободным. Если указанный порт занят, то нужно или его освободить, или использовать другой свободный порт.
После запуска этого кода идем в окно браузера и набираем в адресной строке http://localhost:8080/ . Если все прошло удачно, то в окне браузера мы увидим текст «Привет всем», а в логе сервера текст, подобный приведенному ниже:
Server started! Client connected! GET / HTTP/1.1 Host: localhost:8080 Connection: keep-alive Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate, br Accept-Language: ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7,he;q=0.6,de;q=0.5,cs;q=0.4 Cookie: _ga=GA1.1.1849608036.1549463927; portainer.pagination_containers=100; _gid=GA1.1.80775985.1550669456; If-Modified-Since: Sat, 05 Jan 2019 12:10:16 GMT Client disconnected!
Каждый раз, когда мы что-то вводим в адресную строку браузера и нажимаем Enter не происходит ничего иного, как отправка текста, начинающегося словом GET и заканчивающегося переводом строки. После слова GET через пробел следует путь к запрашиваемому документу на сервере. Попробуйте ввести в браузере http://localhost:8080/something и посмотреть, как изменится текст запроса в логе.
В строках запроса, начиная со второй находятся т.н. заголовки при помощи которых осуществляется передача серверу информации о настройках клиента. Каждая строка заголовка имеет формат [имя заголовка] : [значение]; [значение]; . [значение] .
После того, как текст запроса полностью прочитан сервером, мы отправляем ему простейший ответ, структура которого довольно проста и аналогична структуре запроса. В первой строке версия протокола HTTP и код 200 OK, который сообщит браузеру о том, что запрос был успешно обработан (всем куда лучше знаком код 404, не правда ли 😉 ). Далее следует всего один заголовок Content-Type в котором передается информация о формате передаваемого документа (text/html) и его кодировке (charset=utf-8). После заголовка следует перевод строки (обязательное требование протокола HTTP) и собственно текст, который будет отображен в браузере.
На этом все! Разумеется это далеко не все, что нужно знать о протоколе HTTP и принципах разработки Web серверов, но мне бы не хотелось усложнять данный пример, т.к. главная его задача — продемонстрировать, простейшую коммуникацию по протоколу HTTP. В одном из следующих своих материалов постараюсь развить тему изучения протокола HTTP через его реализацию.
UPD. Гораздо более продвинутый пример подобного сервера можно найти в книге How Tomcat Works: A Guide to Developing Your Own Java Servlet Container by Paul Deck, Budi Kurniawan, глава 1 — Simple Web Server.
Создание простого HTTP-сервера на Java с использованием только Java SE API
Иногда разработчикам требуется создать очень базовый HTTP-сервер, который поддерживает только методы GET и POST. Java SE API предоставляет удобный инструментарий для работы с HTTP-клиентами, например, HttpURLConnection . Однако, разработчики часто сталкиваются с проблемой при попытке найти аналогичный инструмент для работы с HTTP-серверами.
Примером может служить ситуация, когда вам нужно создать приложение, которое обрабатывает HTTP-запросы от других приложений. При этом вы не хотите заниматься ручной обработкой HTTP-запросов и форматированием HTTP-ответов.
Использование HttpServer из com.sun.net.httpserver
Пакет com.sun.net.httpserver предоставляет базовую функциональность для создания простого HTTP-сервера. Этот пакет является частью Java SE, начиная с версии 6.
Вот пример создания простого HTTP-сервера, который слушает на порту 8000 и отвечает на все GET-запросы простым текстовым сообщением «Hello, World!»:
import com.sun.net.httpserver.HttpServer; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpExchange; import java.io.OutputStream; import java.io.IOException; import java.net.InetSocketAddress; public class SimpleHttpServer < public static void main(String[] args) throws Exception < HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0); server.createContext("/", new MyHandler()); server.setExecutor(null); // creates a default executor server.start(); >static class MyHandler implements HttpHandler < @Override public void handle(HttpExchange t) throws IOException < String response = "Hello, World!"; t.sendResponseHeaders(200, response.length()); OutputStream os = t.getResponseBody(); os.write(response.getBytes()); os.close(); >> >
Этот сервер поддерживает только GET-запросы. Для поддержки POST-запросов вам нужно будет добавить дополнительную логику в обработчик MyHandler .
Вместо того чтобы ручками обрабатывать HTTP-запросы и форматировать HTTP-ответы, вы можете использовать этот класс для создания простого HTTP-сервера. Однако, обратите внимание на то, что этот класс предоставляет ограниченные возможности и не подходит для создания полноценных веб-приложений.