Пользователь
0,0
рейтинг
9 сентября 2009 в 14:39

Разработка → HTTP сервер за 15 минут

Java*

Задача


За минимальное время написать 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 OK\r\n" +
                    "Server: YarServer/2009-09-09\r\n" +
                    "Content-Type: text/html\r\n" +
                    "Content-Length: " + s.length() + "\r\n" +
                    "Connection: close\r\n\r\n";
            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/
Яр @yar
карма
87,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (75)

  • +5
    Минусы — это значит больше не надо писать такое? Странно…
    Я же код сам писал, только что, тут ни байта копипаста. :)
    • +3
      я был бы рад если бы мне показывали такие примеры, когда я начинал изучать программирование. Суть примера — показать что такое Socket, Server Socket, Stream, и Thread

      Такой пример может помочь начинающему изучать программирование. А ваш, максимум поможет сдать лабу по сформулированной задаче, но не показывает ничего из того, что указано в сути.
      • +2
        Клёво, но много букв, реально.
        Даже мне, разбираещемуся человеку влом читать.
        Такое будут читать если приперло, а не в рамках общего развития.
        Anyway — я написал, а выбирать читателю, уверен и мой труд тоже окажется кому то полезен.
        • +7
          Возможно, Вы изучая программирование, читали в рамках общего развития код без комментариев и пояснений. И это даже помогло вам понять «что такое Socket, Server Socket, Stream, и Thread». Чтож, мне остается только снять шляпу.
          • –1
            Я так пытался OpenGL учить в 9 классе, просто читая чужой код без комментариев, в общем то с учетом кучи свободного времени очень не плохо получалось, причем такого рода занятие очень развивает логическое мышление. А кстати код который привел автор, в отличии от того же OpenGL под Delphi очень легко читается.
            • +7
              поучите перл по чужим исходникам ;)
              • 0
                вот вам смешно, а я сейчас вынужден именно этим и заниматься =)
                • +1
                  на самом деле — да, это смешно пока это делаешь не ты :)
                • 0
                  и я!
                • 0
                  Зато когда всё-таки изучите всё станет намного проще ;)
                  • 0
                    да у меня на перле поддержка одного небольшого проектика висит. А основной проект на яве ) На перл — как на каторгу =Ъ
              • 0
                Я так делал. Правда потом все равно приходилось почитывать доки на предмет выявления неправильного понимания или недопонимания некоторых вещей.
                Чего так перл всегда гнобят, без знания языка любой код на нем покажется страшным набором символов.
                • 0
                  Его гнобят потому что у него «входной порог» не самый низкий, а многим проще «пройти мимо» чем попробовать разобраться.
    • –5
      Минусы вероятно потому, что http сервером предложенный код можно назвать только с ооочень большой натяжкой.
      • +3
        Ну почему. Простой сервер с выводом статической информации. Можно заменить строку
        writeResponse("Hello from Habrahabr");
        На вывод содержимого файла, переданного как параметр запроса. Получим очень дырявый, но все же веб сервер (с выводом статических страниц). Добавьте простенький парсер имен запрошеных файлов, и запустите внешнюю программу, для определенных расширений, выведите результат ее работы — получите веб сервер с поддержкой, скажем php.
        • +3
          Гм. Судя по количеству пойманных минусов за пост и в карму — многие действительно считают это http сервером :/

          Что бы было «Можно заменить строку» и далее по тексту — придется открыть RFC 2068, придется парсить заголовки запроса и т.д. Тогда — да, действительно, это можно будет назвать http сервером. И человеку, который за 15 минут напишет такой код (с комментариями! а не как здесь) я скажу большое спасибо.

          А то что здесь — это, извините, профанация. Годится только гнуть пальцы перед девушками в стиле «да писал я свой http сервер, ниче сложного!»
          • 0
            Да не воспринимайте близко к сердцу. Недавно тут был пост про программку в 256 байт, которая 3D рисует.
            Обычно маленькие вещи очень далеки от гибкости/универсальности и т.п., и уж подавно не соответствуют никаким RFC, да и цели у них другие.
            Вы попробуйте скомпилировать, запустить и получить кайф от того что оно заработало :)
            • 0
              масштаб несколько отличается
    • 0
      Расслабьтесь, тут всегда полно троллей.
    • +1
      Подобный код можно найти по инету с первого клика. А чем, собственно, отличается ваш пост от поиска и самостоятельного анализа найденного? Нет комментариев о том почему так, что можно улучшить и т.д. Я сам не минусовал, но если бы спросили быть такому или нет на главной, то ответил бы отрицательно.
      • –1
        Я про многое на Хабре читаю что можно найти по инету. Однако далеко не все занимаются поиском всего.
        Я например про разбор нетбуков никогда не искал, но здесь почитываю, потому что пишут.
        А если говорить о том, место ли посту на главной или нет — я бы половине постов с главной сказал бы «нет» :)
        • 0
          Пара советов:
          * Оформление статьи (подсветка синтаксиса, отступы)
          * Коментарии в коде (если я новичок — ничего не понял)
          * Маленький обзор результата (если я не новичок, то возможно будет о чём поспорить/обсудить)

          А так, ваша статья не предполагает какого-то продолжения, она выглядит незаконченной. Заманивать в Java таким сложным кодом без коментариев? Сомнительный шаг :) Я думаю, если написать тоже на Perl, то код будет действительно заманивать…

          Пишите ещё, пишите лучше! :-)
          • +2
            #!/usr/bin/perl
             
            use strict;
            use warnings;
             
            use LWP::Socket;
            use FCGI::ProcManager qw/ pm_manage pm_pre_dispatch pm_post_dispatch /;
             
            # Prepare content and headers
            my $content = join "", map { $_ } <DATA>;
            my $headers ="HTTP/1.1 200 OK\r\n"
                        . "Server: FlaresunsFakeServer/2009-09-10\r\n"
                        . "Content-Type: text/html\r\n"
                        . "Content-Length: " . length($content). "\r\n"
                        . "Connection: close\r\n\r\n";
             
            # Prepare and open socket
            my $sock = new LWP::Socket();
            $sock->bind('127.0.0.1', '8080');
            # Set up listen queue for socket
            $sock->listen(10);
             
            # Create 5 childs
            pm_manage(n_processes => 5);
             
            # Accepts a new connection
            while ( my $socket = $sock->accept(10) ) {
                pm_pre_dispatch();
                $socket->write($headers);
                $socket->write($content);
                $socket->shutdown();
                pm_post_dispatch();
            }
             
            $sock->shutdown();
             
            __DATA__
            <html>
            <head><title>Hi</tiltle></head>
            <body>
                <h1>Hello from Habr</h1>
            <body>
            </html>
             

            • 0
              Подсветка отсюда
              с небольшим допиливаем «строк» и «html»
  • 0
    Я не очень разбираюсь в Java'е, но насколько я понимаю это многопоточный не асинхронный сервер получается… а можно ли средствами Java реализовать именно асинхронный сервер, но тоже что бы небольшой по размеру?
    Просто пишу сейчас асинхронный (не WEB) сервер на C# вот и интересуюсь…
    • +3
      Да, конечно можно.
      Я как раз лет 6 назад делал супер скоростной асинхронный сервер :)
      Он оформлен в виде библиотеки, его можно использовать как embeded и не только для http.
      Доступен любому желающему по лицензии LGPL, скачать здесь sourceforge.net/projects/ixstrim/

      Если Вам надо не HTTP, поищите там в исходниках по ключевому слову Echo, там можно TcpEcho сервер запустить, может даже в примерах есть.
    • +3
      www.jboss.org/netty/

      The Netty project is an effort to provide an asynchronous event-driven network application framework and tools for rapid development of maintainable high performance and high scalability protocol servers and clients.

      Отличная вещь. Асинхронное сетевое IO на базе Java NIO, в последних версиях появилась поддержка HTTP из коробки.
    • 0
      насколько я понимаю это многопоточный не асинхронный сервер получается…
      Что значит «не асинхронный»?

      Здесь после установление соединения (Socket s = ss.accept();) s получает значение рабочего сокета, далее создаётся новый поток (new Thread(new SocketProcessor(s))...) и начинает работать нить по обработке этого соединения, а сервер начинает слушать снова (в бесконечном цикле while).

      Минусы такой реализации: неуправляемая нагрузка. Такой сервер можно положить несколькими десятками запросов в секунду (атака DoS).

      Как лучше сделать: реализовать пул обработчиков соединений (thread poll) и очередей обработки (work queues), размерами которых можно управлять в зависимости от текущей загрузки сервера и имеющихся ресурсов.
      • 0
        «не асинхронный» имелось ввиду про IO, называется «блокирующий IO»
        То есть чтение и запись блокируют Thread
        • 0
          «блокирующий IO»
          Верно! Спасибо за поправку, из головы вылетело как оно зовется.
    • 0
      рекомендую rox-xmlrpc.sourceforge.net/niotut/
    • 0
      Асинхронный сервер под .NET можно легко реализовать используя XF.Server.

      code.msdn.microsoft.com/IOCPServer
  • +5
    Хорошее дело начали, но реально не зная что делают классы, работу которых вы попытались показать — разобраться в коде сложно. А если знать что есть что они делают — то практической пользы от примера мало.
    Удачи в начинании.
  • +2
    Примеры для обучения, как правило, дополняются массой комментариев к коду.
    • –4
      А это и не обучение — это замануха.
      Типа «смотрите ка, на джаве можно очень легко сделать http сервер» ;)
      Обучать я не собирался, это скорее очень поверхностное знакомство.
      • 0
        Поверхностными статьями интернет полон чуть менее, чем полностью. Давайте Вы в следующий раз напишите про то же самое но более глубоко?
  • 0
    Голый код мало поможет человеку разобраться. Если писать статью, так для начала желательно коротко рассказать про сетевые сервисы и их работу в java, описать как проектировался класс, потом расписать задачу каждого метода и свойства с показанием исходного кода как примера, ну а потом всю программу целиком.
    Именно такая статья (которую я описал) мне попалась в свое время про LAMP, что и определило мою карьеру, и можно сказать судьбу.
  • –4
    yar, Спасибо! Это офигенно!!!
  • НЛО прилетело и опубликовало эту надпись здесь
  • –1
    Не холивара ради, но просвящения для — на C# такая штука пишется за две минуты, используя System.Net.HttpListener.
    • –1
      Просто сам не так давно сталкивался с тем, что нужно было написать простенький proxy. Оказалось, в .NET такая штука уже есть.
    • 0
      Если 2 минуты, напишите пожалуйста.
      Мне было бы интересно посмотреть на подход, может и на java можно существенно упростить по подобию.
      • +6
        Сомневаюсь, потому как весь этот функционал используется «из коробки»:
        using System;
        using System.IO;
        using System.Net;
        
        namespace ConsoleApplication7 {
        	class Program {
        		private static HttpListener _listener;
        
                 	static void Main(string[] args) {
        			_listener = new HttpListener();
        			_listener.Prefixes.Add("http://localhost:88/");
        			_listener.Start();
        			while (true)
        			{
        				IAsyncResult asyncResult = _listener.BeginGetContext(Callback, null);
        				asyncResult.AsyncWaitHandle.WaitOne();
        			}
        		}
        
        		private static void Callback(IAsyncResult ar)
        		{
        			HttpListenerContext context = _listener.EndGetContext(ar);
        			using(var sw = new StreamWriter(context.Response.OutputStream))
        			{
        				sw.WriteLine("hello world");
        			}
        		}
        
        • 0
          Спасибо. Мощные компоненты однако :)
    • +1
      в sun java 6 встроенный сервер тоже есть:
      blogs.sun.com/michaelmcm/entry/http_server_api_in_java
    • 0
      Так не интересно юзать готовый компонент — с таким успехом можно взять Tomcat (или Jetty или что-нибудь ещё) и говорить что на джаве вообще ничего писать не надо. Ну и про HttpServer встроенный в 6-ю джаву уже написали ниже (там ещё HttpsServer есть кстати — есть в дотнете HttpsListener?)

      Идея как я понимаю была в том чтоб написать самому на сокетах.
  • +1
    Мягко говоря, это можно было бы назвать классом демона слушающим 8080 порт, общее у них с HTTP сервером только порт :)
    • –1
      Повесить можно на любой порт (в УРЛе можно же любой указать)
      Общее то что он отвечает браузеру, и страница отображается.
      • +1
        Про порт понятно, исходник кристально ясный, хотя Java я даже не знаю.

        Просто выше действительно правильно заметили, что это не HTTP сервер за 15 минут, создание сокета на порту и прослушка его в бесконечном цикле это демон всё же.

        Ну и подключится к нему можно хоть по телнету, хоть другим приложением, только зачем его называть http сервером?!

        Веб сервер за 5 минут я видел наверное HTTP::Server::Brick вот это да…
        • –1
          Это не «http сервер» за 15 минут, а «http сервер за 15 минут».
          Кстати о демонах, если Вы вот это имеете ввиду, то скажу что моя программа выполняется не в бэкграунде, а фореграунде, так что это скорее всего не демон.
          • +1
            Даёш статьи «сервер СУБД за 2 минуты», «система управления атомным реактором за 10 минут» и т.д.
          • +2
            Как бы вы кавычки ни расставляли, смысл, что это не HTTP сервер не изменит!
            Просто сервер к которому возможен доступ клиентом по порту 8080.

            А что такое форэграунд и бэкграунд мне не известно, резиденты, висеть в памяти на бесконечном цикле, ждать прерывания, это как-то ближе :)
            • 0
              Веб-сервер — это сервер, принимающий HTTP-запросы от клиентов, обычно веб-браузеров, и выдающий им HTTP-ответы, обычно вместе с HTML-страницей, изображением, файлом, медиа-потоком или другими данными. Веб-серверы — основа Всемирной паутины. © википедия
              • +1
                Полностью согласен.
                Наверное, следовало добавить ещё немного кода и назвать статью «Пример сетевого взаимодействия».
              • 0
                проблема видимо в том, что Ваш «http-сервер» не принимает HTTP-запросов и не отдает HTTP-ответы. Именно поэтому он не HTTP-сервер, а просто демон, который висит в памяти и слушает указаный порт…
                • –1
                  Проблемы нет, сервер принимает HTTP-запросы, и отдает HTTP ответы, проверить это Вы можете легко.
                  Может быть Вас это удивит, но не все программы слушающие порт являются демонами.
                  • +1
                    Ой, знаете, тогда уж стоит сказать что он не только HTTP запросы принимает, но и FTP и SMTP и все что угодно.
                    так давайте назовем Вашего демона FTP-сервером, почтовым сервером и «системой управления ядерным реактором» (с)

                    понимаете, http-сервер это чуть больше чем получить произвольные данные в сокет от клиента и передать ему произвольные данные. HTTP сервера соответствуют спецификациям, а Ваш даже не отдает заголовки ответа
                    • 0
                      Вы не правы, потому что скачать файл(FTP клиентом) или отправить почту(SMTP клиентом) не сможете при помощи этого сервера, но зайти браузером и увидеть HTML можно, Вы просто попробуйте и поймете.
                      И мой сервер кстати отдает заголовки, и имя сервера, и тип контента и длину контента, и даже предупреждает что закроет сокет (по протоколу HTTP/1.1 между прочим).
                      Это делает вот этот кусок кода:
                      String response = "HTTP/1.1 200 OK\r\n" +
                                          "Server: YarServer/2009-09-09\r\n" +
                                          "Content-Type: text/html\r\n" +
                                          "Content-Length: " + s.length() + "\r\n" +
                                          "Connection: close\r\n\r\n";
                      

                      Вы просто невнимательно смотрели в код.
                      • 0
                        Каюсь — этот участок я пропустил.

                        но продолжим — HTTP-сервер должен обращаться к ресурсам на серверной машине и в зависимости от доступности и типа ресурса возвращать заголовки, а не постоянно отдавать «зашитые» в сервер данные.
                        HTTP-сервер гараздо сложнее. Ваша абстракция очень далека от реальности :)
                        • 0
                          Давай тогда уж ссылку на источник что ли, откуда у вас взялось такое определение HTTP-сервера.
                          Выше в этой ветке я писал определение с ссылкой на википедию.
                          И кстати это не абстракция, а рабочая программа. Которая между прочим очень даже реальна, сервер можно запустить и зайти по ссылку с браузера и увидеть HTML.
                          • 0
                            ftp://ftp.isi.edu/in-notes/rfc2616.txt

                            открываем спецификацию и чатаем:
                            «поделки, которые не удовлетворяют хотя бы единственному правилу MUST или REQUIRED не являются сервером»

                            теперь отсалось выбрать любое неудовлетворенное правило. вот это нам подходит:
                            «The methods GET and HEAD MUST be supported by all general-purpose servers»

                            Да и давайте руководствоваться здравым смыслом: HTTP-сервер имеет правл на жизнь, если он удовлетворяет хотя бы основную потребность — получать доступ к удаленным ресурсам по URI

                            Ваш «http-сервер» это то же самое ечли бы я написал ftp-сервер, который всегда отдает один и тот же «файл». Толку никакого, но чем-то на ftp было бы похоже.
                            В любом случае, если Вам хочется называть ЭТО http-сервером — никто Вам этого не запретит
                            • 0
                              Rfc2616 это спецификация протокола HTTP/1.1 а не определение HTTP сервера.
                              Есть серверы которые не поддерживают спецификацию.
                              Не удивлюсь, если тот же Nginx не поддерживает спецификацию в полном объеме.
                              Или взять те же браузеры, далеко не все из них работают по спецификациям, и что же, значит они теперь не браузеры?
                              • 0
                                Можно поподробнее про браузеры которые работают не по спецификации?
  • 0
    Сам недавно копал тему, но статью писать не стал. Зачем, если написано N раз? ;)

    Чем-то напомнило
    onjava.com/pub/a/onjava/2003/04/23/java_webserver.html
    onjava.com/pub/a/onjava/2003/05/14/java_webserver.html
  • +3
    В JDK есть для этого com.sun.net.httpserver.*. Позволило бы автору не изобретать велосипед и сэкономить несколько минут =)

    Если требуется больше возможностей, использовать Jetty в embedded режиме (или другой подобный сервер).
    • –3
      Может проще nginx поставить, чем использовать com.sun.net.httpserver.*?
      • +1
        Это уже зависит от конкретной задачи. Если вам нужно в вашем Java приложении сделать HTTP сервер, то точно не проще. А если просто отдавать странички, то решение с проверенным отдельным сервером безусловно лучше чем самоделка. Т.к. ваш пост в блоге Java, то и мой ответ подразумевал решение на Java.
        • –1
          Ну если ограничиваться джавой то resin, tomcat, jetty, можно еще продолжить.
          Но я обозначил суть — показать что такое Socket, InputStream, OutpuStream, Thread.
          Http сервер был выбран мной лишь потому что это ближе хабрасообществу, чем какой то абстрактный tcp сервер.
          Хотя я бы никогда и ни кому не посоветовал бы использовать com.sun.net.httpserver, ни для каких целей вообще, есть вещи которые отмирают, это мертвый код, который абсолютно бесполезен, даже с точки зрения обучения.
          Хочу так же заметить, что написание примеров с велосипедами ни как не связано.
          А вообще, формально Вы конечно же правы, задача именно так ставилась (быстро сделать http-сервер).
          Но может я не совсем точно сформулировал постановку задачи к своему решению, но показать людям я хотел именно этот код, он полезен и понятен, а кому то даже понравился.
  • 0
    А разве тред на коннект это не то, чего делать нельзя? Я вообще не силен в нетворкинге, но мне почемуто это не очень…
    • 0
      Вообще то так делают почти все сервера, позволяющие выполнять бизнес логику во время запроса.
      Для бизнес логики ведь нужен тред, да и программировать проще, когда выполнение одного куска кода идет в одном триде.
      Просто обычно количество тридов ограничено, например не больше 100, и все они берутся из пула, а если все заняты, то пришедший запрос ожидает в очереди, пока освободится какой нибудь трид.
      :)
      • +1
        э… Чем оно ограничено в примере? По моему такой код это такая крутая уязвимость на медленный ддос. Я не хакер вовсе, просто мы читаем хедеры, если нам из будут долго посылать, то можно много много потоков наделать. А если их будет слишком то:
        а) у нас просто перестанет работать в лучшем случае
        б) свольчь сможет зафигачить експлойт в наш сервер (хотя надеюсь жавамашина спасет)

        Поймите, я не пытаюсь вас заклеймить, мне просто интересно что будет, как человеку который сейчас учит java.
        • 0
          В примере ни чего не ограничено, такой сервер не имеет права на жизнь :)
          Вы абсолютно правы, большинство серверов можно положить кучей способов, но так же существует и куча различных защит.
          Например, в качестве защиты от кучи коннектов с медленной отсылкой хедеров, можно установить максимальное количество коннектов с одного IP. И делать это можно как средствами самого сервера, так и средствами файрвола.
  • +1
    catch(Throwable t) { /* do nothing*/ } — очень дурной пример!
  • +1
    В свое время (1996й год) я написал (и Компьютерра опубликовала :) статью про веб-серверы, где упоминал про самодельный веб-сервер. 80 строк на Форте. На Смолтолке тоже около того. Не самые минималистические на уровне helloworld, а достаточные для раздачи файлов. Java тогда еще только-только появилась, на ней не пробовал. Теперь вижу, что минимум 70 строк на Java :)

    Когда-то в древности каждый программист начинал с того, что писал собственный текстовый редактор. Собственный веб-сервер — хорошее упражнение для современного программиста :)
  • 0
    я конечно некропостер, но вот HTTP сервер за 2 минуты :)
    dpaste.org/WLTv/

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.