Розробка клієнт-серверного чату на java

Короткий опис

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

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

трохи теорії

Отже, для початку. Проект складатиметься з двох частин: клієнта і сервера. Клієнт матиме GUI, написаний за допомогою бібліотеки Swing. Сервер GUI мати не буде, тільки log-файл і невеликий висновок в консоль.

Для написання чату, нам знадобляться деякі знання.

Оскільки взаємозв'язок клієнта і сервера у нас буде реалізована за допомогою сокетів, познайомимося з ними ближче.

Сокети (англ. Socket - поглиблення, гніздо, роз'єм) - назва програмного інтерфейсу для забезпечення обміну даними між процесами. Сокет - абстрактний об'єкт, який представляє кінцеву точку з'єднання.

Кожен процес може створити слухає сокет (серверний сокет) і прив'язати його до якого-небудь порту операційної системи. Хто слухає процес зазвичай знаходиться в циклі очікування, тобто прокидається при появі нового з'єднання.

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

Передача об'єктів по мережі

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

Отже, що таке сериализация об'єктів.

Серіалізация - процес перекладу будь-якої структури даних в послідовність бітів. Зворотною до операції сериализации є операція десеріалізациі - відновлення початкового стану структури даних з бітової послідовності.

Серіалізация використовується для передачі об'єктів по мережі і для збереження їх у файли.

В Java єдине, що потрібно для сериализации об'єкта - імплементувати інтерфейс Serializable, який є інтерфейсом-маркером, тобто не містить методів.

пишемо Сервер

Отже, короткий опис роботи сервера.

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

Але крім цього, треба не забути про те, що клієнти можуть і відключатися. Тобто ми періодично повинні обмінюватися з клієнтами сигналами (ping'овать один одного), щоб у разі відключення клієнта (або сервера) все про це дізналися.

Отже, приступимо. Створимо наш перший клас

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

Найзручніше зберігати параметри в properties-файлі, благо Java надає зручний інтерфейс для роботи з ними.

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

PORT = 1234

Вся завантаження параметрів відбувається в блоці статичної ініціалізації, оскільки повноцінний конструктор в нашому випадку - недозволена розкіш.

Залишилося тільки замінити в Server.java
ServerSocket socketListener = new ServerSocket ( "тисячі двісті тридцять чотири"); на ServerSocket socketListener = new ServerSocket (Config.PORT); і додати потрібні import'и

Надалі, import'и в коді буду випускати, оскільки будь-яка IDE їх сама підставить.

Отже, ми написали new ClientThread () ;. Але що це таке, поки не вирішили. Пора виправити це.

Цей клас у нас буде відповідати за прийом і передачу повідомлень між клієнтом і сервером, а значить, найголовніший клас в чаті - саме ClientThread.

Оскільки він працює в окремому потоці, перше, що ми повинні зробити - написати

А тепер подумаємо, що ж написати в методі run ().
Отже, по порядку. Для початку, ми повинні отримати від клієнта інформацію «Ти хто такий?» В іншому випадку - «Давай, до побачення!». Як тільки ми дізналися хто він такий, ми повинні відправити йому останні повідомлення в нашому чаті.

Далі, періодично, ми повинні його ping'овать його якимось запитом, щоб переконатися, що не ведемо спілкування з трупом.

Потім, ми повинні отримувати від нього повідомлення, про які повинні повідомляти всіх підключених клієнтів.

Ну і, як тільки ми перестали отримувати від нього запити - клієнта слід видалити зі списку доступнх користувачів.

На час забудемо про ClientThread, а замислимося «Яким чином буде відбуватися спілкування?»
Ми вже вирішили, що будемо передавати серіалізовані об'єкт. Отже, чтоже має бути в цьому об'єкті?

Я зупинився на наступному варіанті:
1. Логін користувача, що відправив це повідомлення (або Server-Bot)
2. Власне, саме повідомлення
3. Час відправки
4. Список доступних сервера клієнтів (для відображення у користувача)

Для успішної сериализации / десеріалізациі, клас в клієнті і сервері повинен бути однаковим. Тому подбаємо відразу і про те, і про інше.

Думаю, все очевидно. Також, крім повідомлень ми хочемо передавати щось на зразок ping'ов.

Может кому пригодится клієнт :)
Написав його за пару годин, і досвіду в роботі з потоками немає, тому не чіпляйтеся.

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

private static String userName = «»;
static Socket socket = null;

public static void main (String [] args) System.out.println ( «Вас вітає клієнт чату! \ n");
System.out.println ( «Введіть свій нік і натисніть \" Enter \ »»);

// Створюємо потік для читання з клавіатури
BufferedReader keyboard = new BufferedReader (new InputStreamReader (System.in));
try // Чекаємо поки користувач введе свій нік і натисне кнопку Enter
userName = keyboard.readLine ();
System.out.println ();
> Catch (IOException e)

// Беремо вхідний і вихідний потоки сокета, тепер можемо отримувати і відсилати дані клієнтом
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 \ "\ n");

while (true) message = keyboard.readLine (); // чекаємо поки користувач введе щось і натисне кнопку Enter.
objectOutputStream.writeObject (new Message (userName, message)); // відсилаємо введений рядок тексту сервера.
>
> Catch (Exception e)
>
finally try if (socket! = null)
> Catch (IOException e)
>
>
>

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)
catch (ClassNotFoundException e)
catch (IOException e)
>
>

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

Власне ось і весь клієнт. А далі вже самі нарощуйте функціонал і придумуйте GUIню яку хочете)