Web client server application in java

Пример простого клиент-серверного приложения на Java

«Клиент-сервер» это очень распространенная и логичная архитектура приложений. Мне кажется, что в наши дни редко можно встретить standalone-клиентское приложение. Поэтому я принял решение рассмотреть пример построения клиент-серверного приложения на Java без привязки к конкретной задаче. Сначала вкратце пробежимся по классовой структуре приложения, потом посмотрим на отдельную реализацию каждого класса. В самом конце статьи я дам ссылку на скачивание архива с готовой структурой приложения. Итак, начнем.

Основные компоненты приложения

Основными компонентами, естественно, являются непосредственно клиент и сервер. Однако, кроме них необходим еще пакет вспомогательных классов, которые, в простейшем случае, будут отвечать за обмен сообщениями между клиентом и сервером. В минимальной комплектации нужны такие классы: MessageReader/MessageWriter(считывает/записывает сообщение в поток на сокете), MessageFactory(содержит идентификаторы всех возможных сообщений), набор сообщений-запросов(Request) и набор сообщений-ответов(Response). Все они будут размещены в пакете «core», который должны иметь у себя и клиент и сервер.

Рассмотрим классовую структуру всего проекта, а потом перейдем к реализации.

Классовая структура клиент-серверного приложения

- client (пакет файлов клиента) Client.java (логика клиента) ClientLauncher.java (запуск клиента) - core (вспомогательные классы) - communication (обмен сообщениями) MessageFactory.java (здесь хранятся все сообщения) MessageReader.java (чтение сообщений из потока) MessageWriter.java (запись сообщений в поток) - requests HandshakeRequest.java (запрос на обмен рукопожатиями) - responses HandshakeResponse.java (ответ на обмен рукопожатиями) IMessage.java (интерфейс для сообщения) Request.java (абстрактный класс "запрос") Response.java (абстрактный класс "ответ") - server (файлы сервера) ClientSession.java (сессия отдельного клиента - обработка запросов этого клиента) Context.java (контекст - общая информация сервера для всех клиентов) Server.java (логика сервера) ServerLauncher.java (запуск сервера) SessionsManager.java (хранит все текущие сессии)

Исходный код клиента на Java

Разобраться с клиентом гораздо проще, он по сути своей не делает ничего супер сложного, просто создает сокет и подключается к сервер-сокету с помощью связки host:port. Лаунчер создает объект класса Client и запускает его работу. Исходный код привожу без импортов, ибо любая IDE вам их подключит(те, кто пишет на Java точно знают, что без IDE очень сложно). Кроме того, в конце статьи вы сможете скачать архив с этим проектом.

Читайте также:  Update python on mac terminal

ClientLauncher.java

public class ClientLauncher < public static void main(String[] args) < try < InetAddress host = InetAddress.getByName(args[0]); int port = Integer.parseInt(args[1]); //System.out.println(id); Client client = new Client(host, port); //Запускаем логику клиента client.start(); >catch (UnknownHostException e) < e.printStackTrace(); >> >

Client.java

public class Client < private final InetAddress host; private final int port; public Client(InetAddress host, int port) < this.host = host; this.port = port; >//Создает сокет, ридер райтер и запускает логику public void start() < //Создаем клиентский сокет try (Socket socket = new Socket(this.host, this.port)) < //Создаем ридер и райтер для обмена сообщениями MessageReader reader = new MessageReader(socket.getInputStream()); MessageWriter writer = new MessageWriter(socket.getOutputStream()); //Шлем серверу первое сообщение "рукопожатие" writer.writeRequest(new HandshakeRequest()); //Получаем ответ UniqueMessage msg = reader.readMessage(); //Проверяем, что это ответ на рукопожатие if(!(msg.message instanceof HandshakeResponse)) < return; >//Запуск логики приложения this.logicStart(); //socket.close(); > catch (IOException e) < e.printStackTrace(); >> public void logicStart() < //Логика приложения //. >>

Под словами «логика приложения» я подразумеваю протокол обмена сообщениями с сервером, передачу каких-либо данных для достижения конечной цели.

Исходный код сервера на Java

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

Напомню, что в классе ClientSession описан основной алгоритм работы с клиентом, обмен сообщениями, данными и прочее. В классе Context содержится общая информация для всех клиентов сервера, например, пути для сохранения логов.

ServerLauncher.java

public class ServerLauncher < public static void main(String[] args) < Server server = new Server(); server.run(); >>

Server.java

public class Server implements Runnable < private final int port; private Context context; public Server() < this.port = 5000; this.context = new Context(); >@Override public void run() < try < ServerSocket ss = new ServerSocket(this.port); //Цикл ожидания подключений while(!this.context.stopFlag) < System.out.println("Waiting connection on port:" + this.port); //Момент ухода в ожидание подключения Socket clientSocket = ss.accept(); System.out.println("New client connected to server"); //Создается клиентская сессия ClientSession clientSession = new ClientSession(clientSocket, this.context); this.context.getSessionsManger().addSession(clientSession); //Запуск логики работы с клиентом clientSession.start(); >ss.close(); > catch (IOException e) < e.printStackTrace(); >> >

Context.java

//Данные, общие для всех клиентских сессий public class Context < private SessionsManager sessinonsManager; public boolean stopFlag; //Другие важные поля, которые должны знать все клиенты //. public Context() < this.stopFlag = false; this.sessinonsManager = new SessionsManager(); >public SessionsManager getSessionsManger() < return this.sessinonsManager; >>

ClientSession.java

//Основная логика клиента public class ClientSession extends Thread < private final Socket socket; private final MessageReader reader; private final MessageWriter writer; private final Context context; public ClientSession(final Socket socket, final Context context) throws IOException < this.socket = socket; this.reader = new MessageReader(socket.getInputStream()); this.writer = new MessageWriter(socket.getOutputStream()); this.context = context; >public void run() < UniqueMessage msg; try < msg = reader.readMessage(); //Рукопожатие if(msg.message instanceof HandshakeRequest) < if(((HandshakeRequest)msg.message).match()) < writer.writeResponse(new HandshakeResponse(), msg.uniqueId); >> //Обменялись рукопожатиями, начинаем работу this.doWork(); //выход this.socket.close(); > catch (IOException e) < e.printStackTrace(); >> private void doWork() <> >

SessionsManager.java

public class SessionsManager < private final Setsessions = new HashSet(); public SessionsManager() <> public synchronized void addSession(ClientSession session) < sessions.add(session); >public synchronized void removeSession(ClientSession session) < sessions.remove(session); >>

Вспомогательные классы из пакета «core»

Помещу все вспомогательные классы под один кат, название классов в точности соответствует названиям из списка «классовая структура» выше, по нему вы можете определить пакет каждого класса.

Читайте также:  Python list comprehensions filter

public class MessageFactory < //Идентификаторы запросов public static final int REQUEST_HANDSHAKE = 1; //Идентификаторы ответов private static final int RESPONSE_BASE = 0x40000000; public static final int RESPONSE_HANDSHAKE = RESPONSE_BASE + 1; //Ассоциативный массив: класс сообщения =>идентификатор сообщения private static final Map, Integer> idMap = new HashMap, Integer>(); static < idMap.put(HandshakeRequest.class, REQUEST_HANDSHAKE); idMap.put(HandshakeResponse.class, RESPONSE_HANDSHAKE); >private MessageFactory() < >//Создает сообщение по идентификатору public static IMessage createMessage(int messageId) throws IOException < if (messageId >RESPONSE_BASE) < switch (messageId) < case RESPONSE_HANDSHAKE: return new HandshakeResponse(); default: throw new IOException("Unknown message type " + messageId); >> else < switch (messageId) < case REQUEST_HANDSHAKE: return new HandshakeRequest(); default: throw new IOException("Unknown message type " + messageId); >> > public static int getMessageId(final IMessage message) < Integer return id.intValue(); >> public class MessageReader < //Длина заголовка сообщения public static final int HEADER_LENGTH = 12; private final DataInputStream dis; public MessageReader(InputStream is) < this.dis = new DataInputStream(is); >public UniqueMessage readMessage() throws IOException < //Читаем длину пакета из начала int packageLength = dis.readInt(); if (packageLength < HEADER_LENGTH) < throw new IOException("Wrong package length"); >//Считываем сообщение byte[] buf = new byte[packageLength — 4]; dis.readFully(buf); DataInputStream messageIS = new DataInputStream(new ByteArrayInputStream(buf)); int uniqueId = messageIS.readInt(); int message_id = messageIS.readInt(); IMessage message = MessageFactory.createMessage(message_id); message.readExternal(messageIS); System.out.println(«Message » + message.getClass().getName() + » received.»); return new UniqueMessage(message, uniqueId); > public static class UniqueMessage < public final IMessage message; public final int uniqueId; private UniqueMessage(IMessage message, int uniqueId) < this.message = message; this.uniqueId = uniqueId; >> > public class MessageWriter < private static final int INITIAL_BUFFER_SIZE = 128; private final DataOutputStream out; private Integer requestIdCounter = 0; public MessageWriter(OutputStream os) < this.out = new DataOutputStream(os); >private int getNewRequestId() < synchronized (requestIdCounter) < return ++requestIdCounter; >> private void writeMessage(final IMessage message, final int uniqueId) throws IOException < int messageId = MessageFactory.getMessageId(message); ByteArrayOutputStream baos = new ByteArrayOutputStream( INITIAL_BUFFER_SIZE); message.writeExternal(new DataOutputStream(baos)); int messageLength = baos.size() + MessageReader.HEADER_LENGTH; synchronized (out) < out.writeInt(messageLength); out.writeInt(uniqueId); out.writeInt(messageId); baos.writeTo(out); out.flush(); >System.out.println(«Message » + message.getClass().getName() + » sent.»); > public int writeRequest(final Request request) throws IOException < int uniqueId = getNewRequestId(); writeMessage(request, uniqueId); return uniqueId; >public void writeResponse(final Response response, int requestId) throws IOException < writeMessage(response, requestId); >> public class HandshakeRequest extends Request < public static final String HANDSHAKE_STRING = "handshake request"; private String handshake; @Override public void readExternal(DataInputStream dis) throws IOException < handshake = dis.readUTF(); >@Override public void writeExternal(DataOutputStream dos) throws IOException < dos.writeUTF(HANDSHAKE_STRING); >public boolean match() < return HANDSHAKE_STRING.equals(handshake); >> public class HandshakeResponse extends Response < public static final String HANDSHAKE_RESPONSE_STRING = "handshake response"; private String handshake; @Override public void readExternal(DataInputStream dis) throws IOException < handshake = dis.readUTF(); >@Override public void writeExternal(DataOutputStream dos) throws IOException < dos.writeUTF(HANDSHAKE_RESPONSE_STRING); >public boolean match() < return HANDSHAKE_RESPONSE_STRING.equals(handshake); >> public interface IMessage < public void writeExternal(DataOutputStream dos) throws IOException; public void readExternal(DataInputStream dis) throws IOException; >public abstract class Request implements IMessage < >public abstract class Response implements IMessage

Пара слов о сообщениях, классы Request и Response являются абстрактными и играют роль классификаторов сообщения. Благодаря этому очень удобно разграничивать «запросы» от «ответов». В этом примере я привел только одно сообщение — Handshake, которое отвечает за первое «рукопожатие» клиента и сервера. Все последующие сообщения должны быть прописаны в классе MessageFactory по примеру этих двух.

Скачать архив с шаблоном клиент-серверного приложения на Java

Заключение

Главная цель, которую я преследовал при написании этой статьи заключается в том, чтобы дать возможность мне или кому-либо еще за считанные минуты «собрать» готовое клиент-серверное приложение. Кажется, я с этим справился, если будут дополнения или замечания, пишите в комментариях или на почту. А на сегодня у меня все, спасибо за внимание!

Источник

Http Client server application GET/POST in Java Example

bytetips.com

This tutorial will cover a Http Client server application GET/POST in Java Example. The goal of this tutorial is to create a GUI application that connects to the web and shows addition of two numbers. This is tutorial part one for creating the server side application. Part one will cover the web application. The next part will show the GUI creation. and In this tutorial, I am using NetBeans IDE and Glassfish Server. The focus of this application is the following:

  • Create Web application that can add two numbers
  • Create Java application that can take input from webserver

The first part is to create Web server application for our client server application. Create a new web application from File > New Project > Java Web and select Web application. Then click Next and type in Project name.

Now click Next and select Glassfish Server and click finish

You will see that a new web application project have been created. Do a right-click on the project and click on run. You will see that your configured browser have started a localhost link using Glassfish Server. My application is showing http://localhost:8080/ClientServerWeb/ according to above project settings. If the server started and localhost url is showing demo web page, then we are in right track. Next step is to start coding out server side application that can return addition of two number using http.

Http Client server application GET/POST in Java Example

Do a right-click on the project and click on New>Servlet . Type in class name as MyServlet Package name as com.web.server then click next and Finish. You will see some auto generated code for your class. At first check that java package name and imports looks like following. Or else import the missing ones. :

package com.web.server; import java.io.IOException; import java.io.PrintWriter; import static java.lang.Double.sum; import static java.lang.System.out; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;

Now delete any following from auto generated code:

/* TODO output your page here. You may use following sample code. */ out.println(» «); out.println(» «); out.println(» «); out.println(«»); out.println(» «); out.println(» «); out.println(«

Servlet test at » + request.getContextPath() + «

Источник

Оцените статью