Главная > Программирования сетевых взаимодействий в Java
<< Предыдущий параграф Следующий параграф >>
Пред.
След.
Макеты страниц

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

Также, советуем воспользоваться поиском по сайту, мы уверены, что вы сможете найти больше информации по нужной Вам тематике

3.2. Приложение, реализующее клиент-серверную архитектуру

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

Рис. 3.1. Окно сервера

Рис. 3.2. Окно клиента

Рассмотрим серверную часть приложения. Выполним импорт необходимых java-пакетов:

import java.net.*; //классы для работы с сетью

import java.io.*; //классы для взаимодействия с файлами

Объявим класс, реализующий сервер следующим образом:

public class ServerForm extends java.awt.Frame implements Runnable

Класс сервера ServerForm является наследником от стандартного класса Frame и реализует интерфейс Runnable. Реализация интерфейса Runnable необходима, если мы хотим, чтобы класс обеспечивал свою функциональность в параллельно выполняемых подпроцессах. Объявим константу, задающую номер порта, через который будет осуществлятся обмен данными:

public final static int PORT = 220;

Объявим следующие переменные класса ServerForm:

Socket sock = null; //Ссылка на сокетное соединение

BufferedReader in = null; //Буферизованный поток ввода

FileWriter fw = null; //Файловый поток вывода

BufferedWriter out = null;// Буферизованный поток вывода

Thread t = new Thread(this); //Новый подпроцесс выполнения

Буферизованные потоки BufferedWriter и BufferedReader будем использовать для обмена данными между сервером и клиентом. FileWrite для записи полученных сообщений в файл данных. Для того чтобы отделить процесс чтения данных из сети от основного процесса выполнения серверного приложения, нам потребуется новый подпроцесс. Для этого мы и создали объект t класса Thread.

Создадим метод connect(), реализующий сокетное соединение с клиентом.

public void connect() {

В процессе установления сокетных соединений возможно появление различных исключительных ситуаций. Поэтому защитим код от возможных исключений, добавив оператор try и реализуя блок catch (Exception ex).

Создадим серверный сокет и вызовем метод accept() для ожидания соединения со стороны клиента:

ServerSocket ss = new ServerSocket(PORT);

sock = ss.accept();

Получаем потоки ввода-вывода из созданного сокетного соединения:

in = new BufferedReader(new InputStreamReader(sock.getInputStream()));

out = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));

Запускаем на исполнение параллельный подпроцесс чтения данных:

t.start();

Открываем файл данных, для записи полученных сообщений:

fw = new FileWriter("messageServ.txt");

Реализуем метод run() интерфейса Runnable.

public void run(){

Объявим строковую переменную, для хранения полученного сообщения:

String indata_;

В процессе получения данных через поток ввода возможно появление исключительных ситуаций. Поэтому защитим код от возможных исключений, добавив оператор try и реализуя блок catch (Exception ex).

Будем читать полученные данные в следующем цикле:

while(true){

Запишем полученное сообщение в строку, прочитав его из потока ввода методом readLine(). Данный метод позволяет читать сообщение в виде строки текста, что является несомненным достоинством:

indata_ = in.readLine();

Отобразим сообщение в компоненте типа List:

list1.add(indata_);

Запишем сообщение в файл:

fw.write(indata_);

Реализуем методы-обработчики события ActionPerformed для компонентов-кнопок «Connect», «Send» и «Close». По щелчку на кнопке «Connect» будет вызван следующий обработчик, в котором производится вызов метода connect():

private void button2ActionPerformed(java.awt.event.ActionEvent evt) {

    connect();

}

По щелчку на кнопке «Send» будет вызван следующий обработчик:

private void button1ActionPerformed(java.awt.event.ActionEvent evt) {

    try {

        //читаем введенную строку сообщения

        String data = textField1.getText();

        //записывем сообщение в выходной поток

        out.write(data);

        out.newLine();

        //очищаем буфер

        out.flush();

    }

    catch(Exception e) {}

}

По щелчку на кнопке «Close» необходимо будет закрыть соединение, все потоки и файлы, вызвав следующий обработчик:

private void button3ActionPerformed(java.awt.event.ActionEvent evt) {

    // Закрываем файл данных, потоки и соединение

    try{

        fw.close();

        in.close();

        out.close();

        sock.close();

    }

    catch(Exception e) {}

}

Полный код серверной части приведен в приложении 3.

Рассмотрим клиентскую часть приложения. Как и в классе сервера выполним импорт java-такетов:

import java.net.*; //классы для работы с сетью

import java.io.*; //классы для взаимодействия с файлами

Объявим класс, реализующий клиента следующим образом:

public class ClientForm extends java.awt.Frame implements Runnable

Класс сервера ClientForm также является наследником от стандартного класса Frame и реализует интерфейс Runnable. Объявим следующие переменные класса ClientForm:

Socket ss = null; //Ссылка на сокетное соединение

BufferedReader in = null; //Буферизованный поток ввода

FileWriter fw = null; //Файловый поток вывода

BufferedWriter out = null;// Буферизованный поток вывода

Буферизованные потоки BufferedWriter и BufferedReader будем использовать для обмена данными между клиентом и сервером. FileWrite для записи полученных сообщений в файл данных. Для того чтобы отделить процесс чтения данных из сети от основного процесса выполнения клиентского приложения, нам потребуется новый подпроцесс. Для этого создадим объект process1 класса Thread.

Thread process1 = new Thread(this); //Новый подпроцесс выполнения

Создадим метод connect() для установления соединения клиента с сервером.

public void connect() {

    try {

        //Устанавливаем соединение с локальным хостом

        InetAddress addr = InetAddress.getLocalHost();

        ss = new Socket(addr, 220);

        //Получаем потоки ввода_вывода

        in = new BufferedReader(new InputStreamReader(ss.getInputStream()));

        out = new BufferedWriter(new OutputStreamWriter(ss.getOutputStream()));

        //Запускаем новый подпроцесс

        process1.start();

        //Открываем файл

        fw = new FileWriter("messageCli.txt");

    }

    catch(Exception e){

        System.out.println("ошибка ");

    }

}

Обратите внимание, что в данном примере мы устанавливаем соединение с локальным хостом, получая его Интернет-адрес с помощью метода getLocalHost() класса InetAddress. В случае установления соединения с удаленным хостом, нам потребовалось бы вызвать метод getByName("наименование хоста"). Также обратите внимание, что, мы открываем сокетное соединение по порту с номером 220, именно с тем номером порта, с которым создавали серверный сокет в серверной части приложения. Реализуем метод run() интерфейса Runnable.

public void run(){

    String s1; //строка для хранения текущего сообщения

    try{

        //В цикле читаем данные из потока

        while(true){

            //записываем данные из потока в строку

            s1 = in.readLine();

            //Отображаем в компоненте List

            list1.add(s1);

            //Записываем в файл

            fw.write(s1);

        }

    }

    catch(Exception e) {}

}

Реализуем методы-обработчики события ActionPerformed для компонентов-кнопок «Connect», «Send» и «Close». По щелчку на кнопке

«Connect» будет вызван следующий обработчик, в котором производится вызов метода connect():

private void button1ActionPerformed(java.awt.event.ActionEvent evt) {

    connect();

}

По щелчку на кнопке «Send» будет вызван следующий обработчик:

private void button2ActionPerformed(java.awt.event.ActionEvent evt) {

    //Получаем строку от пользователя и пересылаем серверу

    try {

        String data = textField1.getText();

        out.write(data);

        out.newLine();

        out.flush();

    }

    catch(Exception e){}

}

По щелчку на кнопке «Close» необходимо будет закрыть соединение, все потоки и файлы, вызвав следующий обработчик:

private void button3ActionPerformed(java.awt.event.ActionEvent evt) {

    //Закрываем файл, потоки и соединение

    try{

        fw.close();

        in.close();

        out.close();

        ss.close();

    }

    catch(Exception e) {}

}

Полный код клиентской части приведен в приложении 2.

Categories

1
email@scask.ru