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

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

Лучший способ понять устройство и принцип работы чего-либо – сделать это что-то самому.

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

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

Таким образом, собрав эти знания воедино и написав таки удобоваримое серверное прилоение, спобоное обрабатывать браузерные запросы, я решил сделать выжимку из подобных знаний и поэтапно описать процесс создания простейшего web-сервера на Java.

Надеюсь, в этой статье найдутся полезные знания для начинающих Java-программистов и других людей, изучающих связанные технологии.

Итак, поскольку программа предполагает простейшие функции сервера, она будет состоять из одного класса без графического интерфейса. Этот класс (Server) наследует поток и имеет одно поле – сокет:

В главном методе создаём новый ServerSocket и задаём для него порт (в данном случае использован порт 1025) и в бесконечном цикле ожидаем соединения с клиентом. При наличии соединения мы создаем новый поток, передавая ему соответствующий сокет. В случае неудачи выводим сообщение об ошибке:

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

Далее описываем функционал потока в методе run(): в первую очередь, создаем из сокета поток исходящих и входящих данных:

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

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

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

Не вдаваясь в подробности структуры http-запроса скажу лишь, что нужная нам информация будет находиться в первой строчке данного запроса приблизительно в таком виде:

В данном примере запрашивается страница на сервере по адресу

Страница с таким адресом выдается на большинстве серверов по умолчанию. Наша задача – с помощью собственноручно написанного метода getPath() вычленить этот адрес из запроса. Существует множество вариантов подобного метода и здесь их приводить нет смысла. Ключевой момент здесь состоит в том, что получив путь до нужного файла и записав его в строковую переменную path, мы можем попробовать создать на основе этих данных файл и, в случае успеха, вернуть этот файл, а в случае неудачи – вернуть специфическое сообщение об ошибке:

Проверяем, является ли данный файл дирекорией. Если такой файл существует и является директорией, то мы возвращаем упомянутый выше файл по умолчанию – index.html:

Если файла по указанному адресу не существует, то мы создаем http-ответ в строке response с указанием того, что файл не найден, добавляя в нужном порядке следующие заголовки:

После формирования строки response мы отправляем их клиенту и закрываем соединение:

Если же файл существует, то перед формированием ответа необходимо выяснить его расширение и, следовательно, MIME-тип. Для начала мы выясним индекс точки, стоящей перед расширением файла и сохраним его в int-переменную.

Затем вычленим расширение файла, стоящее после неё. Список возможным MIME-типов можно расширить, но в данном случае буде использовать всего по одной из форм 3-х форматов: html, jpeg и gif. По умолчанию будем использовать MIME-тип для текста:

Формируем ответ клиенту:

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

Для отправки самого файла можно использовать следующую конструкцию:

Наконец, завершаем блок try-catch, указанный в начале.

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

Вы можете помочь и перевести немного средств на развитие сайта

Краткое описание

На данный момент заканчиваю 2-й курс универститета, одной из лабораторных работ по курсу Java было написание чата. После того, как разобрался в теме сокетов, сериализации объектов и MVC, хотелось бы поделиться с читателим, тем более, что оно мне несказанно помогло при написании проекта.

Ну и, разумеется, учту все ошибки и недочеты, которые будут озвучены.

Немного теории

Итак, для начала. Проект будет состоять из двух частей: клиента и сервера. Клиент будет иметь GUI, написанный с помощью библиотеки Swing. Сервер GUI иметь не будет, только log-файл и небольшой вывод в консоль.

Читайте также:  Как сделать зачеркивание текста в инстаграм

Для написания чата, нам понадобятся некоторые знания.

Сокеты

Поскольку взаимосвязь клиента и сервера у нас будет реализована с помощью сокетов, познакомимся с ними поближе.

Со́кеты (англ. socket — углубление, гнездо, разъём) — название программного интерфейса для обеспечения обмена данными между процессами. Сокет — абстрактный объект, представляющий конечную точку соединения.

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

Для нас это означает, что сервер будет слушать какой-либо порт (для большей универсальности, мы будем задавать порт в конфигурационном файле) и после подключения клиента, будем производить с ним какие-то действия.

Передача объектов по сети

Но просто получать сообщение от клиента нам мало. Нам хочется знать его имя, IP-адрес, а также передавать ему в ответ список подключенных пользователей. Таким образом, просто передача текстовых данных нас не устроит. У нас есть 2 выхода:
1. Передавать xml-строку с описанием всей информации
2. Передавать сериализованный объект, в котором будут храниться все необходимые нам данные в виде полей.

Не будем говорить о плюсах и минусах каждого подхода. Воспользуемся вторым вариантом. (А если понадобится, когда-нибудь потом, я напишу второй)

Итак, что такое сериализация объектов.

Сериализация — процесс перевода какой-либо структуры данных в последовательность битов. Обратной к операции сериализации является операция десериализации — восстановление начального состояния структуры данных из битовой последовательности.

Сериализация используется для передачи объектов по сети и для сохранения их в файлы.

В Java единственное, что нужно для сериализации объекта — имплементировать интерфейс Serializable, который является интерфейсом-маркером, т.е не содержит методов.

Пишем Сервер

Итак, краткое описание работы сервера.

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

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

Итак, приступим. Создадим наш первый класс

Думаю, данный код, пока что, не нуждается в комментариях. Я не стал особо заморачиваться на обработке исключений, но нам ведь не это важно, верно?

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

Удобнее всего хранить параметры в properties-файле, благо Java предоставляет удобный интерфейс для работы с ними.

Итак, наш properties-файл будет состоять всего из одной строки (пока что)

PORT=1234

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

Осталось только заменить в Server.java
ServerSocket socketListener = new ServerSocket("1234"); на ServerSocket socketListener = new ServerSocket(Config.PORT); и добавить нужные import’ы

В дальнейшем, import’ы в коде буду упускать, поскольку любая IDE их сама подставит.

Итак, мы написали new ClientThread(); . Но что это такое, пока не решили. Пора исправить это.

Этот класс у нас будет отвечать за прием и передачу сообщений между клиентом и сервером, а значит, самый главный класс в чате — именно ClientThread.

Поскольку он работает в отдельном потоке, первое, что мы должны сделать — написать

А теперь подумаем, что же написать в методе run().
Итак, по порядку. Для начала, мы должны получить от клиента информацию «Ты кто такой?» в противном случае — «Давай, до свидания!». Как только мы узнали кто он такой, мы должны отправить ему последние сообщения в нашем чате.

Далее, периодически, мы должны его ping’овать его каким-нибудь запросом, чтобы убедиться, что не ведем общение с трупом.

Затем, мы должны получать от него сообщения, о которых должны уведомлять всех подключенных клиентов.

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

На время забудем о ClientThread, а задумаемся «Каким образом будет происходить общение?»
Мы уже решили, что будем передавать сериализованный объект. Итак, чтоже должно быть в этом объекте?

Я остановился на следующем варианте:
1. Логин пользователя, отправившего это сообщение (или Server-Bot)
2. Собственно, само сообщение
3. Время отправки
4. Список доступных серверу клиентов (для отображения у пользователя)

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

Думаю, всё очевидно. Также, помимо сообщений мы хотим передавать нечто вроде ping’ов.

По сути, этот класс нам не сильно-то нужен, просто потом код будет удобнее читать

Читайте также:  Устранение неполадок windows 8 при запуске долго

Итак, приступим к написанию ClientThread

Код немного комментирован, поэтому все должны разобраться. Осталось дописать только несколько функций.
Итак, начнем по порядку.

Данная функция рассылает какое-то сообщение всем клиентам

Ах да, мы еще забыли вписать некоторые поля класса ClientThread, которые активно использовали. Итак, класс ClientThread,java целиком

Осталось разобраться с функциями getUserList() и getChatHistory
Для начала, определим еще 3 класса

По сути, сервер готов к работе, осталось только немного модифицировать 2 класса.

Методы getChatHistory() и getUserList() сделаны синхронизированными, потому что с ними могут работать несколько потоков

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

Заключение

Теперь наш сервер готов к использованию. Мы познакомились с сериализацией объектов и работой с сокетами в Java.

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

В любом случае, данная статья не призвана стать эталоном написания чата на Java, это лишь инструмент для понимания как вообще создавать такие приложения на хорошем примере, а не на эхо-чате в 10 строк.

Рекомендованный контент

Все ясно, но проблема NetBeans ругается 🙁 Можно мне архив с сорсами?)

Ты нашел решение проблемы?

Поддержу вышеотписавшегося, можете ли дать ссылку на проет?

у меня осталась одна ошибка вот в этом месте this.timer = new Timer(DELAY, new ActionListener() < , как её исправить?

то Игорь: Подключайте таймер из библиотеки awt.
Вопрос к автору: где клиент.

Ребят, автор выложил клиент или нет еще?

У socketListener в конструкторе указан порт в виде String.Надо бы исправить.
ServerSocket socketListener = new ServerSocket(“1234”);

у кого-нидь была проблема с timer.stop()?
как ее решить?

getUserList() не видит этот метод, предлагает его объявить.Что делать?

Где продолжение? Ссылку, пожалуйста.

Перелыл весь сайт так и не нашел продолжения этой хорошей статьи! Может кто нибудь подскажет. дайте ссылку плизззз…..

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

Хотя если не пытаться вдаваться в код, а просто понять общий принцип работы, то более менее понятно

Ну и где продолжение

хорошая статья, а вот продолжение где?

В пакете java.awt я не нашел ни одного таймера…

Алекс надо смотреть тут import java.awt.event.*;

К слову я что-то не догоняю о почему не видно вызовы getUserList и getChatHistory ? в какой класс зырить ?

В Server, там они определены. Просто из ClientThread они не видны…

Ребята кто-нибудь написал клиент к этому серверу? Поделитесь, у меня постоянно ошибки вылетают.

Ребят, вот я честно дуб-дубом в этом всем….. но мне нужна помощ как раз тех кто шарит. Хотим с кентами просто сделать свой чатик что бы там трещать на досуге, но вот проблема никто в этом не шарит, денег платить как то не охота,

кто может помочь, киньте инструкцию полную на почту tarasovdaniil22@gmail.com

Вопрос автору: а как поведёт себя сервер, если будет 10 000 000+ клиентов онлайн? сервак от количества потоков не упадёт? и сколько надо оперативной памяти под это? и ещё один вопрос не лучше ли сообщения передавать в хотя бы xml формате, на серваке брать парсером его статус(ping | text | login |…) и дальше зависимости от статуса его обрабатывать или всё таки лучше сериализованный объект передавать и на сервере не парсить а десериализовывать?

Где можно найти клиент?

ThreadClient не очень написан. Запускать поток в конструкторе как-то не очень.

Что бы не было ошибки связанной с с timer.stop() надо подключить import javax.swing.Timer;

Проблема с классом СlientTread.
Искал ошибки, но не нашел

ServerSocket socketListener = new ServerSocket(Config.PORT);
Не пойму почему ругается, в классе Server проблема, почему то не видит в классе config, не могли бы вы помочь разобраться?

В классе ClientThread не видит методы getChatHistory и getUserList. Как решить проблему?

Эти методы находятся в классе Server, и являются статическими. Вызываются так: Server.getChatHistory(), Server.getUserList().

порт в кавычках. мдаааааа….бб ламер

в общем иди нахер…твой код безнадежно устарел…в топку его и тебя

Может кому пригодится клиент 🙂
Написал его за пару часов, и опыта в работе с потоками нет, поэтому не придирайтесь.

Всего в проекте 4 файла:

public class SocketClient <
private final static String address = “127.0.0.1”; // это IP-адрес компьютера, где исполняется наша серверная программа
private final static int serverPort = 6666; // здесь обязательно нужно указать порт к которому привязывается сервер

private static String userName = “”;
static Socket socket = null;

public static void main( String[] args ) <
System.out.println(“Вас приветствует клиент чата!
”);
System.out.println(“Введите свой ник и нажмите ”Enter””);

// Создаем поток для чтения с клавиатуры
BufferedReader keyboard = new BufferedReader( new InputStreamReader( System.in ) );
try <
// Ждем пока пользователь введет свой ник и нажмет кнопку Enter
userName = keyboard.readLine();
System.out.println();
> catch ( IOException e )

Читайте также:  Не подключается к wifi получение ip адреса

try <
try <
InetAddress ipAddress = InetAddress.getByName( address ); // создаем объект который отображает вышеописанный IP-адрес
socket = new Socket( ipAddress, serverPort ); // создаем сокет используя IP-адрес и порт сервера

// Берем входной и выходной потоки сокета, теперь можем получать и отсылать данные клиентом
InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();

// Конвертируем потоки в другой тип, чтоб легче обрабатывать текстовые сообщения
ObjectOutputStream objectOutputStream = new ObjectOutputStream( outputStream );
ObjectInputStream objectInputStream = new ObjectInputStream( inputStream );

new PingThread( objectOutputStream, objectInputStream );

// Создаем поток для чтения с клавиатуры
String message = null;
System.out.println(“Наберите сообщение и нажмите ”Enter”
”);

while (true) < // Бесконечный цикл
message = keyboard.readLine(); // ждем пока пользователь введет что-то и нажмет кнопку Enter.
objectOutputStream.writeObject( new Message( userName, message ) ); // отсылаем введенную строку текста серверу.
>
> catch ( Exception e ) < e.printStackTrace(); >
>
finally <
try <
if ( socket != null ) < socket.close(); >
> catch ( IOException e ) < e.printStackTrace(); >
>
>
>

public class ServerListenerThread implements Runnable <
private Thread thread = null;
private ObjectOutputStream objectOutputStream = null;
private ObjectInputStream objectInputStream = null;

public ServerListenerThread( ObjectOutputStream objectOutputStream, ObjectInputStream objectInputStream ) <
this.objectOutputStream = objectOutputStream;
this.objectInputStream = objectInputStream;

thread = new Thread( this );
thread.start();
>

@Override
public void run() <
try <
while (true) <
Message messageIn = (Message) objectInputStream.readObject();
if ( messageIn instanceof Ping ) <
Ping ping = (Ping) messageIn;
objectOutputStream.writeObject( new Ping() );
> else <
System.out.println(“[ ” + messageIn.getDate().toString() + ” ] ” + messageIn.getLogin() + ” : ” + messageIn.getMessage() );
>
>
>
catch ( SocketException e ) < e.getMessage(); >
catch ( ClassNotFoundException e ) < e.getMessage(); >
catch ( IOException e ) < e.getMessage(); >
>
>

3, 4) Это файла протокола обмена с сервером: Message.java и Ping.java

Собственно вот и весь клиент. А дальше уже сами наращивайте функционал и придумывайте GUIню какую хотите)

Решения конкретных задач программирования. Java, Android, JavaScript, Flex и прочее. Настройка софта под Linux, методики разработки и просто размышления.

среда, 21 января 2015 г.

Самый "легкий" Java — сервер

Когда я начинал работать в Java EE, серверная инфраструктура казалась мне загадочным капризным монстром. И это при том, что в программирование я пришёл из админов. Залил ear в Resin, проверь, поднялся ли? Успешно ли распаковался? Однажды мы с админами потратили полночи чтобы понять, почему после успешного вроде бы деплоя на запросы отвечает по-прежнему старая копия приложения. При таких танцах с бубном переход на GlassFish казался хорошим решением, а отказ от ejb — ещё лучшим. Кто-то из моих коллег радовался могуществу Spring, кто-то искал что-то попроще. Я писал сервлеты под tomcat, а когда делал что-то совсем маленькое — просто встраивал jetty.
Чем проще, тем лучше. Понятно, что есть нетривиальные требования, есть сложные задачи. Но признаемся себе, сколько простых ненагруженных сервисов вы реализовали используя слишком мощные и сложные инструменты? "Я привык к этому фреймфорку и не хочу терять скилл" — слышу я обычно. "Ты уйдёшь и тот, кто будет поддерживать этот монстрокод, проклянёт тебя" — думаю я в ответ.
Может я и не убедил вас писать простые вещи простыми средствами, но посмотрите все-таки как можно сделать серверное java-приложение вообще без сервера. Это, в конце концов, интересно.

Как, вообще без сервера? Писать обработку запросов самому?

Ну, нет, что вы. Я за простые решения, но не за велосипеды. Все уже написано и, более того, уже установлено. Java-сервер уже есть в вашем jdk. Просто используйте его )

com.sun.net.HttpServer — вот все что нам нужно. Наше приложение будет слушать запросы на порту 9090 или том, что будет указан java -jar наш.jar тут. Наши обработчики мы навешиваем на контексты перед стартом сервера. Все предельно просто. Чтобы в обработчиках сосредоточиться на бизнес-логике, сделаем им абстрактный предок, где порешаем все мелочи вроде вычитывания параметров зарпроса и логирования пары запрос-ответ:

Имея "за плечами" такого предка, нам останется реализовать метод doGet, в каждом конкретном "сервлете". Метод POST я тут не обрабатываю потому что он мне не нужен. Но обработать его не сложнее, можете попробовать сами.

Итак, что же мы получаем?

Для "секретного web-сервера", по сути внутреннего API java, мы имеем очень приятные инструменты для работы с данными запроса, заголовками ответа и т.п. Все это не требует никакой установки, настройки, есть прямо "в коробке" и готово к работе. Чем не инструмент для простого web-сервиса? Конечно, когда мы проверим свою идею и задумаемся о продакшене, можно перенести свою бизнес-логику в "честные" сервлеты и использовать тот же tomcat или resin. Почему бы и нет? Но если проект не пойдёт в продакшн мы скажем себе спасибо за то, что не привлекали админов, не тратили дни на развертывание и настройку окружения, а просто взяли и сделали то что было нужно. И не более того.

Ссылка на основную публикацию
Сколько рублей получают ютуберы
Видеохостинг YouTube — не только развлекательная площадка, но и хороший источник дохода. Тысячи пользователей выкладывают ролики, пытаясь привлечь внимание аудитории....
Самый дорогой самсунг 2018
Samsung / Самсунг - южнокорейская компания, ведущий производитель смартфонов в мире. В первом квартале 2018 года доля Самсунг на мировом...
Самый лучший smart tv
Ежегодные обновления телевизионных технологий делают телевизоры уже больше, чем обычным экраном для демонстрации каналов. Растет популярность функции Smart TV, которая...
Сколько света мотает компьютер
Выбирая комплектующие для персонального компьютера (ПК) обычно обращают внимание на производительность и объем памяти, порой забывая о том, сколько же...
Adblock detector