Культурна клієнт-серверна аплікація на сокетах у Java 5


Попередній пост варто було б витерти взагалі, настільки він жахливий. Чесно кажучи, я трохи поміркував і прикинув, що маю реальні шанси отримати по голові чимось важким за таку, перепрошую, писанину, тому поспішив виправитися. Ну, але не зразу 🙂

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

Серверна частина тепер складається уже з двох класів. Перший клас імплементує інтерфейс Runnable і тому може виконуватися у вигляді “нитки”:

public class SSocket implements Runnable {
private Socket s;
private int iD;
private OutputStream os;

Атрибут s – сокет, через який буде встановлено підключення, os – потік виводу даного сервера – сюди будемо писати поточний час, iD – це просто для зручності (якщо на порт причепиться кілька клієнтів, то сервер зможе їх якось розрізнити, а от для користувача краще бачити якраз оцю айдішку).

public SSocket(int iD, Socket s) throws Throwable {
this.s = s;
this.iD = iD;
this.os = s.getOutputStream();
}

Конструктор нічого особливого із себе не представляє. Може хіба зацікавити вираз “throws Throwable”. Throwable – це суперклас над ексепшенами і помилками, а, оскільки, ми в деталі можливих виключних ситуацій вникати не будемо і не хочемо, аби програма аварійно завершилася в разі чого, то я написав так – всі збої в результаті гребемо в один кошик.

public void run() {
try {
writeResponse();
} catch (Throwable t) {
System.out.println("Read Request Error!");
} finally {
try {
s.close();
} catch (Throwable t) {
System.out.println("Error Closing Socket!");
}
}
System.out.println("Client "+iD+" processing finished");
}

Метод run() просто намагається викликати інший метод – writeResponse(), про який нижче. В разі проблем, він викличе якусь виняткову ситуацію, яку ми опрацюємо дуже просто – напишемо повідомлення про помилку 🙂 І, звісно, намагаємося закрити сокет (якщо і це не вдасться, то, знову ж таки, повідомимо користувача).

private void writeResponse() throws Throwable {
DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
Date date = new Date();
String response = dateFormat.format(date);
System.out.println("Sending "+response+" to client "+iD);
os.write(response.getBytes());
os.flush();
}

Останній метод якраз служить для надсилання до клієнта точного часу. В попередньому варіанті програми всі речі, які тут використовуються, наче вже описувалися. Вартий уваги хіба метод getBytes() – він перетворює нашу текстову величину у байти (цього разу робимо навпаки відносно попереднього – передаватимемо таки байти).

Другий клас нічого особливого не робить у принципі, а лише створює “нитку” із нашим сокетом, в результаті чого його можна буде, наприклад, призупинити.

public class SServer {
public static final int PORT = 3664;
public static void main(String[] args) throws Throwable {
ServerSocket ss = new ServerSocket(PORT);
int i = 0; //Just to know how many client connections were
System.out.println("Server Started");
while (true) {
Socket s = ss.accept();
if (!(s == null)){i++;};
System.out.println("Client "+i+" accepted");
SSocket sss= new SSocket (i, s);
Thread th1 = new Thread(sss);
th1.start();
}
}
}

При кожному новому прийнятому підключенні змінна-лічильник i збільшується на 1, відповідно новий сокет створюється уже із айдішкою, рівною i. Щоправда, припинення підключення лічильник не скидає, тому в релізі його варто забрати – щоб не переповнив чогось там несподівано. Я його собі залишив для наглядності.

Нарешті клас-клієнт. Тут взагалі все просто:

public class SClient {
public static final int PORT = 3664;
public static void main(String[] args) throws Throwable {
Socket ss = new Socket("localhost", PORT);
System.out.println("Connecting to server");
InputStream is = ss.getInputStream();
System.out.println("Reading Server Response");
BufferedReader br = new BufferedReader(new InputStreamReader(is));
while (true) {
String s = br.readLine();
if ((s == null) || (s.length() == 0)) {
break;
}
System.out.println();
System.out.println("Current Time Is: " + s);
}
}
}

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

У якості бонусу така фіча: при запущеному сервері можна зайти на адресу localhost:3664 у браузері і там побачити поточний час (у Опері і Хромі працює точно).

Почитайте ще оце:


Залиште коментар

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *

5 thoughts on “Культурна клієнт-серверна аплікація на сокетах у Java