Оглавление

Сетевые приложения
Адрес IP
Адрес URL
Класс URL
Сокеты
Потоковые сокеты
Класс Socket
Датаграммные сокеты
Связь с Web
Аплет ShowChart
Приложения SocketServ и SocketClient
Аплет Form

    Приложения SocketServ и SocketClient

    В качестве примера мы приведем исходные тексты двух приложений Java, работающих с потоковыми сокетами. Одно из этих приложений называется SocketServ и выполняет роль сервера, второе называется SocketClient и служит клиентом.

    Приложение SocketServ выводит на консоль строку "Socket Server Application" и затем переходит в состояние ожидания соединения с клиентским приложением SocketClient.

    Приложение SocketClient устанавливает соединение с сервером SocketServ, используя потоковый сокет с номером 9999 (этот номер выбран нами произвольно). Далее клиентское приложение выводит на свою консоль приглашение для ввода строк. Введенные строки отображаются на консоли и передаются серверному приложению. Сервер, получив строку, отображает ее в своем окне и посылает обратно клиенту. Клиент выводит полученную от сервера строку на консоли.

    Когда пользователь вводит строку "quit", цикл ввода и передачи строк завершается.

    Весь процесс показан на рис. 3 и 4.

    pic03.gif (4444 bytes)

    Рис. 3. Окно клиентского приложения

    pic04.gif (3495 bytes)

    Рис. 4. Окно серверного приложения

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

    Исходный текст серверного приложения SocketServ

    Исходный текст серверного приложения SocketServ приведен в листинге 3.

    Листинг 3. Файл SocketServ.java

    import java.io.*;
    import java.net.*;
    import java.util.*;
    public class SocketServ
    {
      public static void main(String args[])
      {
        byte bKbdInput[] = new byte[256];
        ServerSocket ss;
        Socket s;
        InputStream is;
        OutputStream os;
        try
        {
          System.out.println(
            "Socket Server Application");
        }
        catch(Exception ioe)
        {
          System.out.println(ioe.toString());
        }   
        try
        {
          ss = new ServerSocket(9999);
          s = ss.accept();
          is = s.getInputStream();
          os = s.getOutputStream();
          byte buf[] = new byte[512];
          int lenght;
          while(true)
          {
            lenght = is.read(buf);
            if(lenght == -1)
              break;
            String str = new String(buf, 0);
            StringTokenizer st;
            st   = new StringTokenizer(
               str, "\r\n");
            str = new String(
              (String)st.nextElement());
            System.out.println(">  " + str);
            os.write(buf, 0, lenght);
            os.flush();
          }
          is.close();
          os.close();
          s.close();
          ss.close();
        }
        catch(Exception ioe)
        {
          System.out.println(ioe.toString());
        }    
        try
        {
          System.out.println(
            "Press <Enter> to terminate
     application...");
          System.in.read(bKbdInput);
        }
        catch(Exception ioe)
        {
          System.out.println(ioe.toString());
        }
      }
    }

    Описание исходного текста серверного приложения SocketServ

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

    Массив bKbdInput размером 256 байт предназначен для хранения строк, введенных при помощи клавиатуры.

    В переменную ss класса ServerSocket будет записана ссылка на объект, предназначенный для установления канала связи через потоковый сокет (но не ссылка на сам сокет):

    ServerSocket ss;

    Ссылка на сокет, с использованием которого будет происходить передача данных, хранится в переменной с именем s класса Socket:

    Socket s;

    Кроме того, мы определили переменные is и os, соответственно, классов InputStream и OutputStream:

    InputStream is; 
    OutputStream os;

    В эти переменные будут записаны ссылки на входной и выходной поток данных, которые связаны с сокетом.

    После отображения на консоли строки названия приложения, метод main создает объект класса ServerSocket, указывая конструктору номер порта 9999:

    ss = new ServerSocket(9999);

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

    Канал устанавливается методом accept:

    s = ss.accept();

    Этот метод переводит приложение в состояние ожидания до тех пор, пока не будет установлен канал передачи данных.

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

    На следующем этапе сервер создает входной и выходной потоки, вызывая для этого методы getInputStream и getOutputStream, соответственно:

    is = s.getInputStream();
    os = s.getOutputStream();

    Далее приложение подготавливает буфер buf для приема данных и определяет переменную length, в которую будет записываться размер принятого блока данных:

    byte buf[] = new byte[512];
    int lenght;

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

    Для чтения строки мы вызываем метод read применительно ко входному потоку:

    lenght = is.read(buf);

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

    Метод read возвращает размер принятого блока данных или -1, если поток исчерпан. Мы воспользовались этим обстоятельством для завершения цикла приема данных:

    if(lenght == -1)
      break;

    После завершения приема блока данных мы преобразуем массив в текстовую строку str класса String, удаляя из нее символ перевода строки, и отображаем результат на консоли сервера:

    System.out.println(">  " + str);

    Затем полученная строка отправляется обратно клиентскому приложению, для чего вызывается метод write:

    os.write(buf, 0, lenght);

    Методу write передается ссылка на массив, смещение начала данных в этом массиве, равное нулю, и размер принятого блока данных.

    Для исключения задержек в передаче данных из-за накопления данных в буфере (при использовании буферизованных потоков) необходимо принудительно сбрасывать содержимое буфреа метдом flush:

    os.flush();

    И хотя в нашем случае мы не пользуемся буферизованными потоками, мы включили вызов этого метода для примера.

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

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

    is.close();
    os.close();
    s.close();
    ss.close();

    Исходный текст клиентского приложения SocketClient

    Исходный текст клиентского приложения SocketClient приведен в листинге 4.

    Листинг 4. Файл SocketClient.java

    import java.io.*;
    import java.net.*;
    import java.util.*;
    public class SocketClient
    {
      public static void main(String args[])
      {
        byte bKbdInput[] = new byte[256];
        Socket s;
        InputStream is;
        OutputStream os;
        try
        {
          System.out.println(
            "Socket Client Application" +
            "\nEnter any string or" +
            " 'quit' to exit...");
        }
        catch(Exception ioe)
        {
          System.out.println(ioe.toString());
        }
        try
        {
          s = new Socket("localhost",9999);
          is = s.getInputStream();
          os = s.getOutputStream();
          byte buf[] = new byte[512];
          int length;
          String str;
          while(true)
          {
            length = System.in.read(bKbdInput);
            if(length != 1)
            {
              str = new String(bKbdInput, 0);
              StringTokenizer st;
              st   = new StringTokenizer(
                str, "\r\n");
              str = new String(
                (String)st.nextElement());
              System.out.println(">  " + str);
              os.write(bKbdInput, 0, length);
              os.flush();
              length = is.read(buf);
              if(length == -1)
                break;
              str = new String(buf, 0);
              st   = new StringTokenizer(
                str, "\r\n");
              str = new String(
                  (String)st.nextElement());
              System.out.println(">> " + str);
              if(str.equals("quit"))
                break;
            }
          }
          is.close();
          os.close();
          s.close();
        }
        catch(Exception ioe)
        {
          System.out.println(ioe.toString());
        }    
        try
        {
          System.out.println(
            "Press <Enter> to " +
             "terminate application...");
          System.in.read(bKbdInput);
        }
        catch(Exception ioe)
        {
          System.out.println(ioe.toString());
        }
      }
    }

    Описание исходного текста клиентского приложения SocketClient

    Внутри метода main клиентского приложения SocketClient определены переменные для ввода строки с клавиатуры (массив bKbdInput), сокет s класса Socket для работы с сервером SocketServ, входной поток is и выходной поток os, которые связаны с сокетом s.

    После вывода на консоль приглашающей строки клиентское приложение создает сокет, вызывая конструктор класса Socket:

    s = new Socket("localhost",9999);

    В процессе отладки мы запускали сервер и клиент на одном и том же узле, поэтому в качестве адреса сервера указана строка "localhost". Номер порта сервера SocketServ равен 9999, поэтому мы и передали конструктору это значение.

    После создания сокета наше клиентское приложение создает входной и выходной потоки, связанные с этим сокетом:

    is = s.getInputStream();
    os = s.getOutputStream();

    Теперь клиентское приложение готово обмениваться данными с сервером.

    Этот обмен выполняется в цикле, условием завершения которого является ввод пользователем строки "quit".

    Внутри цикла приложение читает строку с клавиатуры, записывая ее в массив bKbdInput:

    length = System.in.read(bKbdInput);

    Количество введенных символов сохраняется в переменной length.

    Далее если пользователь ввел строку, а не просто нажал на клавишу <Enter>, эта строка отображается на консоли и передается серверу:

    os.write(bKbdInput, 0, length);
    os.flush();

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

    Далее приложение читает ответ, посылаемый сервером, в буфер buf:

    length = is.read(buf);

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

    Если сервер закрыл канал, то метод read возвращает значение -1. В этом случае мы прерываем цикл ввода и передачи строк:

    if(length == -1)
      break;

    Если же ответ сервера принят успешно, принятые данные записываются в строку str, которая отображается на консоли клиента:

    System.out.println(">> " + str);

    Перед завершением своей работы клиент закрывает входной и выходной потоки, а также сокет, на котором выполнялась передача данных:

    is.close();
    os.close();
    s.close();


Java | Продукты и решения | Технологии | Сервис и обучение
О компании | Партнеры | Новости | Поиск
Sun Microsystems Inc. Corporate Information Our partners News and Events Search on site Java Computing Products and Solutions Technologies and Researches Education and Service Content



Сайт создан в системе uCoz