Мультизадачность
Наверное, сегодня уже нет необходимости объяснять, что такое мультизадачность. Все современные операционные системы, такие как Microsoft Windows 95, Microsoft Windows NT, IBM OS/2 или UNIX способны работать в мультизадачном режиме, повышая общую производительность системы за счет эффективного распараллеливания выполняемых задач. Пока одна задача находится в состоянии ожидания, например, завершения операции обмена данными с медленным периферийным устройством, другая может продолжать выполнять свою работу.
Пользователи уже давно привыкли запускать параллельно несколько приложений для того чтобы делать несколько дел сразу. Пока одно из них занимается, например, печатью документа на принтере или приемом электронной почты из сети Internet, другое может пересчитывать электронную таблицу или выполнять другую полезную работу. При этом сами по себе запускаемые приложения могут работать в рамках одной задачи - операционная система сама заботится о распределении времени между всеми запущенными приложениями.
Создавая приложения для операционной системы Microsoft Windows на языках программирования С или С++, вы могли решать многие задачи, такие как анимация или работа в сети, и без использования мультизадачности. Например, для анимации можно было обрабатывать сообщения соответствующим образом настроенного таймера.
Приложениям Java такая методика недоступна, так как в этой среде не предусмотрено способов периодического вызова каких-либо процедур. Поэтому для решения многих задач вам просто не обойтись без мультизадачности.
Работа с файлами
Библиотека классов языка программирования Java содержит многочисленные средства, предназначенные для работы с файлами. И хотя аплеты не имеют доступа к локальным файлам, расположенным на компьютере пользователя, они могут обращаться к файлам, которые находятся в каталоге сервера Web. Автономные приложения Java могут работать как с локальными, так и с удаленными файлами (через сеть Internet или Intranet).
В любом случае, будете ли вы создавать автономные приложения Java или аплеты, взаимодействующие с сервером Web через сеть, вы должны познакомиться с классами, предназначенными для организации ввода и вывода.
Создание сетевых приложений
Когда в 30 томе “Библиотеки системного программиста” мы начинали разговор про язык программирования Java, то отмечали, что он специально ориентирован на глобальные сети, такие как Internet. В этой главе мы начнем знакомство с конкретными классами Java, разработанными для сетевого программирования. На примере наших приложений вы сможете убедиться, что классы Java действительно очень удобны для создания сетевых приложений.
В этой главе мы рассмотрим два аспекта сетевого программирования. Первый из них касается доступа из приложений Java к файлам, расположенным на сервере Web, второй - создания серверных и клиентских приложений с использованием сокетов.
Напомним, что из соображений безопасности алпетам полностью запрещен доступ к локальным файлам рабочей станции, подключенной к сети. Тем не менее, аплет может работать с файлами, расположенными на серверах Web. При этом можно использовать входные и выходные потоки, описанные нами в предыдущей главе.
Для чего аплетам обращаться к файлам сервера Web?
Таким аплетам можно найти множество применений.
Представьте себе, например, что вам нужно отображать у пользователя диаграмму, исходные данные для построения которой находятся на сервере Web. Эту задачу можно решить, грубо говоря, двумя способами.
Первый заключается в том, что вы создаете расширение сервера Web в виде приложения CGI или ISAPI, которое на основании исходных данных динамически формирует графическое изображение диаграммы в виде файла GIF и посылает его пользователю. Что касается расширения сервера Web, то вы сможете его создать, пользуясь 29 томом “Библиотеки системного программиста”, который называется “Сервер Web своими руками”.
Однако на пути решения задачи с помощью расширения сервера Web вас поджидают две неприятности. Во-первых, создать из программы красивый цветной графический файл в стандарте GIF не так-то просто - вы должны разобраться с форматом этого файла и создать все необходимые заголовки. Во-вторых, графический файл занимает много места и передается по каналам Internet достаточно медленно - средняя скорость передачи данных в Internet составляет 1 Кбайт в секунду.
В то же время файл с исходными данными может быть очень компактным. Возникает вопрос - нельзя ли передавать через Internet только исходные данные, а построение графической диаграммы выполнять на рабочей станции пользователя?
В этом заключается второй способ, который предполагает применение аплетов. Ниже мы приведем исходные тексты соотвестсвующего аплета в разделе “Приложение ShowChart”. Это приложение получает через сеть файл исходных данных, а затем на основании содержимого этого файла рисует в своем окне цветную круговую диаграмму. Объем передаваемых данных при этом по сравнению с использованием расширения сервера Web сокращается десятки раз.
Помимо работы с файлами, расположенными на сервере Web, в этой главе мы расскажем о создании каналов между приложениями Java, работающими на различных компьютерах в сети, с использованием сокетов.
Сокеты позволяют организовать тесное взаимодействие аплетов и полноценных приложений Java, при котором аплеты могут предавать друг другу данные через сеть Internet. Это открывает широкие возможности для обработки информации по схеме клиент-сервер, причем в роли серверов здесь может выступать любой компьютер, подключенный к сети, а не только сервер Web. Каждая рабочая станция может выступать одновременно и в роли сервера, и в роли клиента.
Растровые изображения и анимация
Одно из наиболее распространенный применений аплетов связано с рисованием простых или анимированных растровых изображений. На серверах Web изображения обычно хранятся в форматах GIF или JPEG. Оба эти формата обеспечивают сжатие изображения, что весьма актуально из-за невысокой скорости передачи данных в сети Internet.
Рисование растровых изображений в приложениях для операционной системы Microsoft Windows, составленных на языке программирования С - не простая задача. В классическом программном интерфейсе этой операционной системы отсутствуют функции, с помощью которых можно было бы непосредственно рисовать содержимое файлов с растровыми изображениями.
Программист был вынужден работать с заголовками таких файлов, выделять таблицу цветов и биты изображений, создавать и реализовывать палитру, заниматься восстановлением сжатых данных и так далее. Мы описали этот непростой процесс для файлов формата BMP в 14 томе “Библиотеки системного программиста”, который называется “Графический интерфейс GDI в Microsoft Windows”.
Разработчик приложений Java находится в намного лучшем положении, так как библиотеки классов Java содержат простые в использовании и мощные средства, предназначенные для работы с растровыми изображениями. В этой главе мы научим вас рисовать в окне аплета содержимое файлов GIF и JPEG, выполняя при необходимости масштабирование, а также создавать на базе таких файлов анимационные изображения, показывая по очереди отдельные кадры небольшого видеофильма.
Звук в аплетах Java
Нельзя сказать, что звуковые возможности аплетов Java чрезмерно велики. Скорее наоборот, они минимальны. Тем не менее, аплеты могут проигрывать звуковые клипы, записанные в файлах формата AU, который пришел из мира компьютеров фирмы Sun.
Сказанное, однако, не означает, что если у вас нет рабочей станции Sun, то вы не сможете озвучить свои аплеты. Во-первых, в сети Internet можно найти много готовых звуковых файлов AU, а во-вторых, там же есть программы для преобразования форматов звуковых файлов. Одну из таких условно-бесплатных программ, которая называется GoldWave, вы можете загрузить с сервера ftp.winsite.com.
Взаимодействие между аплетами
До сих пор наши аплеты жили в документах HTML сами по себе. В этой главе мы расскажем о том, как организовать взаимодействие между аплетами, расположенными в одном и том же документе HTML.
Как аплеты могут взаимодействовать друг с другом?
С помощью интерфейса AppletContext, о котором вы скоро узнаете, аплеты могут получать списки всех аплетов для текущего документа HTML. Эти списки состоят из ссылок на соответствующие объекты класса Applet, пользуясь которыми можно получить доступ к полям и методам, определенным в этих аплетах как public, могут получать строку описания аплета, описание параметров и значения, передаваемые аплетам через параметры в документе HTML.
Таким образом, аплеты могут взаимодействовать друг с другом достаточно тесным образом.
Комбинированные приложения Java
Наши предыдущие приложения были либо аплетами, либо автономными приложениями с консольным окном. Основной класс аплетов был унаследован от класса Applet, а в классе консольных приложений был определен метод main.
В этой главе мы расскажем о том, как создавать комбинированные приложения, двоичный файл .class которых способен работать и как аплет, встроенный в документ HTML, и как автономное приложение. Система автоматизированного проектирования приложений Java Applet Wizard, встроенная в Microsoft Visual J++, позволяет автоматически создавать исходные тексты шаблонов таких комбинированных приложений.
Адрес IP и класс InetAddress
Прежде чем начинать создание сетевых приложений для Internet, вы должны разобраться с адресацией компьютеров в сети с протоколом TCP/IP, на базе которого построена сеть Internet. Подробную информацию об адресации вы можете получить из только что упомянутого 29 тома “Библиотеки системного программиста”. Здесь мы приведем только самые необходимые сведения.
Все компьютеры, подключенные к сети TCP/IP, называются узлами (в оригинальной терминологии узел - это host). Каждый узел имеет в сети свой адрес IP, состоящий из четырех десятичных цифр в диапазоне от 0 до 255, разделенных символом “точка “, например:
193.120.54.200
Фактически адрес IP является 32-разрядным двоичным числом. Упомянутые числа представляют собой отдельные байты адеса IP.
Так как работать с цифрами удобно лишь компьютеру, была придумана система доменных имен. При использовании этой системы адресам IP ставится в соответсвие так называемый доменный адрес, такой как www.microsoft.com.
В сети Internet имеется распределенная по всему миру база доменных имен, в которой установлено соответствие между доменными именами и адресами IP в виде четырех чисел.
Для работы с адресами IP в библиотеке классов Java имеется класс InetAddress, определение наиболее интересных методов которого приведено ниже:
public static InetAddress getLocalHost();
public static InetAddress getByName(String host);
public static InetAddress[] getAllByName(String host);
public byte[] getAddress();
public String toString();
public String getHostName();
public boolean equals(Object obj);
Рассмотрим применение этих методов.
Прежде всего вы должны создать объект класса InetAddress. Эта процедура выполняется не с помощью оператора new, а с применением статических методов getLocalHost, getByName и getAllByName.
Базовые классы для работы с файлами и потоками
Количество классов, созданных для работы с файлами, достаточно велико, чтобы привести начинающего программиста в растерянность. Прежде чем мы займемся конкретными классами и приведем примеры приложений, работающих с потоками и файлами, рассмотрим иерархию классов, предназначенных для орагнизации ввода и вывода.
Все основные классы, интересующие нас в этой главе, произошли от класса Object (рис. 2.1).
Рис. 2.1. Основные классы для работы с файлами и потоками
Блокировка на заданный период времени
С помощью метода sleep можно заблокировать задачу на заданный период времени. Мы уже пользовались этим методом в предыдущих приложениях, вызывая его в цикле метода run:
try
{
Thread.sleep(500);
}
catch (InterruptedException ee)
{
. . .
}
В данном примере работа задачи Thread приостанавливается на 500 миллисекунд. Заметим, что во время ожидания приостановленная задача не отнимает ресурсы процессора.
Так как метод sleep может создавать исключение InterruptedException, необходимо предусмотреть его обработку. Для этого мы использовали операторы try и catch.
Блокировка задачи
Синхронизированная задача, определенная как метод типа synchronized, может переходить в заблокированное состояние автоматически при попытке обращения к ресурсу, занятому другим синхронизированным методом, либо при выполнении некоторых операций ввода или вывода. Однако в ряде случаев полезно иметь более тонкие средства синхронизации, допускающие явное использование по запросу приложения.
Добавление буферизации
А что, если нам нужен не простой выходной поток, а буферизованный?
Здесь нам может помочь класс BufferedOutputStream. Вот два конструктора, предусмотренных в этом классе:
public BufferedOutputStream(OutputStream out);
public BufferedOutputStream(OutputStream out, int size);
Первый из них создает буферизованный выходной поток на базе потока класса OutputStream, а второй делает то же самое, но дополнительно позволяет указать размер буфера в байтах.
Если вам нужно создать выходной буферизованный поток для записи форматированных данных, создание потока выполняется в три приема:
создается поток, связанный с файлом, как объект класса FileOutputStream;
ссылка на этот поток передается конструктору класса BufferedOutputStream, в результате чего создается буферизованный поток, связанный с файлом;
ссылка на буферизованный поток, созданный на предыдущем шаге, передается конструктору класса DataOutputStream, который и создает нужный поток
Вот фрагмент исходного текста программы, который создает выходной буферизованный поток для записи форматированных данных в файл с именем output.txt:
DataOutputStream OutStream;
OutStream = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("output.txt")));
Аналогичным образом создается входной буферизованный поток для чтения форматированных данных из того же файла:
DataInputStream InStream;
InStream = new DataInputStream(
new BufferedInputStream(
new FileInputStream("output.txt")));
Добавление изображений в объект класса MediaTracker
Далее метод init должен создать все необходимые объекты класса Image и добавить их в объект MediaTracker методом addImage. Ниже мы показали фрагмент кода, в котором выполняется добавление трех изображений:
Image img1;
Image img2;
Image img3;
img1 = getImage(getCodeBase(), "pic1.gif");
img2 = getImage(getCodeBase(), "pic2.gif");
img3 = getImage(getCodeBase(), "pic3.gif");
mt.addImage(img1 , 0);
mt.addImage(img2 , 0);
mt.addImage(img3 , 0);
В качестве первого параметра методу addImage передается ссылка на изображение, загрузку которого необходимо отслеживать, а в качестве второго - идентификатор, который можно будет использовать в процессе отслеживания. Если все, что вам нужно, это дождаться окончания загрузки изображений, то для второго параметра вы можете указать нулевое значение.
Другие методы класса MediaTracker
Какие другие полезные методы, кроме методов addImage и waitForAll есть в классе MediaTracker?
public boolean waitForAll(long ms);
Метод waitForAll с параметром ms позволяет выполнять ожидание в течение заданного времени. Время ожидания задается в миллисекундах. При этом если за указанное время все изображения были успешно загружены, метод waitForAll возвращает значение true, если нет - false.
Вариант метода checkAll с параметром load позволяет проверить, завершилась ли загрузка отслеживаемых изображений:
public boolean checkAll(boolean load);
Если значение параметра load равно true, метод инициирует загрузку изображений.
Если при добавлении изображений методом addImage вы использовали второй параметр этого метода для присваивания разным группам изображений различные идентификаторы, то с помощью метода checkID можно дождаться завершения загрузки отдельной группы изображений:
public boolean checkID(int id);
Есть также вариант этого метода, позволяющий проверить загрузку группы изображений с заданным идентификатором:
public boolean checkID(int id, boolean load);
Метод waitForID с параметрами id и ms позволяет выполнять ожидание загрузки группы изображений с заданным идентификатором в течении указанного периода времени:
public boolean waitForID(int id, long ms);
Класс MediaTracker предоставляет также возможность прослеживать сам процесс загрузки всех добавленных в него изображений или отдельных групп изображений с помощью методов statusAll и statusID:
public int statusAll(boolean load);
public int statusID(int id, boolean load);
В зависимости от значения параметра load эти методы могут инициировать загрузку изображений. Если параметр равен true, загрузка изображений инициируется, если false - выполняется только проверка текущего состояния загрузки.
Методы statusAll и statusID возвращают значение, составленное из отдельных битов состояния при помощи логической операции ИЛИ. Ниже мы перечислили эти биты состояния и привели их краткое описание.
Биты состояния | Описание | ||
MediaTracker.LOADING | Один или несколько отслеживаемых файлов продолжают загружаться | ||
MediaTracker.ABORTED | Загрузка одного или нескольких файлов была прервана | ||
MediatTracker.ERRORED | При загрузке одного или нескольких файлов произошла ошибка | ||
MediaTracker.COMPLETE | Загрузка всех отслеживаемых файлов произошла полностью и успешно |
Еще четыре метода, определенных в классе MediaTracker, связаны с обработкой ошибок:
public boolean isErrorAny();
public boolean isErrorID(int id);
public Object[] getErrorsAny();
public Object[] getErrorsID(int id);
Методы isErrorAny и isErrorID позволяют проверить, возникла ли ошибка при загрузке, соответственно, любого из отслеживаемых изображений или изображений из заданной группы. Если ошибка произошла, возвращается значение true, если нет - значение false.
Методы getErrorsAny и getErrorsID возвращают массив объектов, при ожидании загрузки которых произошла ошибка. Первый из этих методов возвращает массив для всех отслеживаемых объектов, второй - только объектов из заданной группы.
Главный класс комбинированного приложения
Класс Combi создан на базе класса Applet, что необходимо для обеспечения работы этого приложения под управлением навигатора Internet:
public class Combi extends Applet
{
. . .
public static void main(String args[])
{
. . .
}
public String getAppletInfo()
{
. . .
}
public void init()
{
. . .
}
public void paint(Graphics g)
{
. . .
}
}
Обратите внимание, что наряду с методами, которые обычно определяются аплетами, такими как getAppletInfo, init и paint, в классе комбинированного приложения определен метод main. Если приложение запускается как аплет, метод main не получает управления. Если же приложение запущено автономно, этот метод первым получает управление и выполняет все необходимые инициализирующие действия.
Главное из этих действий - создание окна на базе класса Frame для размещения в нем аплета, создание аплета и вызов функций инициализации аплета. Ниже мы привели исходный текст метода main приложения Combi, созданный для нас системой Java Applet Wizard:
public static void main(String args[])
{
CombiFrame frame = new CombiFrame("Combi");
frame.show();
frame.hide();
frame.resize(
frame.insets().left + frame.insets().right + 320,
frame.insets().top + frame.insets().bottom + 240);
Combi applet_Combi = new Combi();
frame.add("Center", applet_Combi);
. . .
applet_Combi.init();
applet_Combi.start();
frame.show();
}
Прежде всего, метод main создает объект frame класса CombiFrame, определенного в нашем приложении на базе класса Frame (окно фрейма). Напомним, что класс Frame, который был нами описан в 30 томе “Библиотеки системного программиста”, позволяет приложениям Java создавать окна, напоминающие окна обычных приложений Windows.
Метод show отображает окно фрейма.
Далее в методе main выполняется изменение размеров окна фрейма, перед чем окно скрывается методом hide. Для изменения размеров окна применяется метод resize, которому через первый и второй параметры передаются новые значения, соответственно, ширины и высоты окна.
Инициализация клиента
Процесс инициализации клиентского приложения выглядит весьма просто. Клиент должен просто создать сокет как объект класса Socket, указав адрес IP серверного приложения и номер порта, используемого сервером:
Socket s;
s = new Socket("localhost",9999);
Здесь в качестве адреса IP мы указали специальный адрес localhost, предназначенный для тестирования сетевых приложений, а в качестве номера порта - ззначение 9999, использованное сервером.
Теперь можно создавать входной и выходной потоки. На стороне клиента эта операция выполняется точно также, как и на стороне сервера:
InputStream is;
OutputStream os;
is = s.getInputStream();
os = s.getOutputStream();
Инициализация сервера
Вначале мы рассмотрим действия приложения, которое на момент инициализации является сервером.
Первое, что должно сделать серверное приложение, это создать объект класса ServerSocket, указав конструктору этого класса номер используемого порта:
ServerSocket ss;
ss = new ServerSocket(9999);
Заметим, что объект класса ServerSocket вовсе не является сокетом. Он предназначен всего лишь для установки канала связи с клиентским приложением, после чего создается сокет класса Socket, пригодный для передачи данных.
Установка канала связи с клиентским приложением выполняется при помощи метода accept, определенного в классе ServerSocket:
Socket s;
s = ss.accept();
Метод accept приостанавливает работу вызвавшей его задачи до тех пор, пока клиентское приложение не установит канал связи с сервером. Если ваше приложение однозадачное, его работа будет блокирована до момента установки канала связи. Избежать полной блокировки приложения можно, если выполнять создание канала передачи данных в отдельной задаче.
Как только канал будет создан, вы можете использовать сокет сервера для образования входного и выходного потока класса InputStream и OutputStream, соответственно:
InputStream is;
OutputStream os;
is = s.getInputStream();
os = s.getOutputStream();
Эти потоки можно использовать таким же образом, что и потоки, связанные с файлами.
Обратите также внимание на то, что при создании серверного сокета мы не указали адрес IP и тип сокета, ограничившись только номером порта.
Что касается адреса IP, то он, очевидно, равен адресу IP узла, на котором запущено приложение сервера. В классе ServerSocket определен метод getInetAddress, позволяющий определить этот адрес:
public InetAddress getInetAddress();
Тип сокета указывать не нужно, так как для работы с датаграммными сокетами предназначен класс DatagramSocket, который мы рассмотрим позже.
Исходные тексты приложения
В листинге 1.1 представлены исходные тексты мультизадачного приложения MultiTask, созданного системой автоматизированной разработки аплетов, слегка измененного и снабженного нашими комментариями. В дальнейшем мы создадим блее сложные мультизадачные аплеты.
Листинг 1.1. Файл MultiTask\MultiTask.java
// =========================================================
// Периодическое обновление окна аплета
// с использованием мультизадачности
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class MultiTask extends Applet implements Runnable
{
// Задача, которая будет обновлять окно аплета
Thread m_MultiTask = null;
// -------------------------------------------------------
// MultiTask
// Конструктор класса MultiTask. Не используется
// -------------------------------------------------------
public MultiTask()
{
}
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: MultiTask\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Author: Alexandr Frolov\r\n" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Метод, получающий управление при инициализации аплета
// -------------------------------------------------------
public void init()
{
}
// -------------------------------------------------------
// destroy
// Метод, получающий управление при
// завершении работы аплета
Исходные тексты приложения
Исходный файл приложения Rectangles приведен в листинге 1.3.
Листинг 1.3. Файл Rectangles\Rectangles.java
// =========================================================
// Рисование прямоугольников в отдельной задаче
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
import java.util.*;
public class Rectangles extends Applet implements Runnable
{
// Ссылка на задачу рисования прямоугольников
Thread m_Rectangles = null;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: Rectangles\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// paint
// Метод paint, выполняющий рисование в окне аплета
// -------------------------------------------------------
public void paint(Graphics g)
{
// Определяем текущие размеры окна аплета
Dimension dimAppWndDimension = size();
// Выбираем в контекст отображения желтый цвет
g.setColor(Color.yellow);
// Закрашиваем внутреннюю область окна аплета
g.fillRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
// Выбираем в контекст отображения черный цвет
g.setColor(Color.black);
// Рисуем рамку вокруг окна аплета
g.drawRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
}
Исходные тексты приложения
Исходный текст приложения вы найдете в листинге 1.5.
Листинг 1.5. Файл MultiTask2\ MultiTask2.java
// =========================================================
// Рисование прямоугольников и эллипсов
// в разных задачах
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
// =========================================================
// Основной класс аплета
// =========================================================
public class MultiTask2 extends Applet
{
// Ссылка на задачу рисования прямоугольников
DrawRectangles m_DrawRectThread = null;
// Ссылка на задачу рисования эллипсов
DrawEllipse m_DrawEllipseThread = null;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: MultiTask2\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// paint
// Метод paint, выполняющий рисование в окне аплета
// -------------------------------------------------------
public void paint(Graphics g)
{
// Определяем текущие размеры окна аплета
Dimension dimAppWndDimension = size();
// Выбираем в контекст отображения желтый цвет
g.setColor(Color.yellow);
// Закрашиваем внутреннюю область окна аплета
g.fillRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
// Выбираем в контекст отображения черный цвет
Исходные тексты приложения
Исходный текст приложения Scroller представлен в листинге 1.7.
Листинг 1.7. Файл Scroller\Scroller.java
// =========================================================
// Просмотр текста в режиме динамической свертки
// по вертикали
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class Scroller extends Applet implements Runnable
{
// Ссылка на задачу, выполняющую свертку
Thread m_Scroller = null;
// Отображаемые строки
private String m_String1 = "Мы представляем наши новые книги";
private String m_String2 =
"Том 29. Сервер Web своими руками";
private String m_String3 =
"Том 30. Microsoft Visual J++. Создание приложений на языке Java. Часть 1";
private String m_String4 =
"Том 31. Разработка приложений для Internet с Visual C++ и MFC";
private String m_String5 =
"Том 32. Microsoft Visual J++. Создание приложений на языке Java. Часть 2";
private String m_String6 = "";
// Имена параметров
private final String PARAM_String1 = "String1";
private final String PARAM_String2 = "String2";
private final String PARAM_String3 = "String3";
private final String PARAM_String4 = "String4";
private final String PARAM_String5 = "String5";
private final String PARAM_String6 = "String6";
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: Scroller\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
Исходные тексты приложения
Исходные тексты приложения HorzScroll приведены в листинге 1.9.
Листинг 1.9. Файл HorzScroll\HorzScroll.java
// =========================================================
// Рисование строки текста по буквам
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class HorzScroll extends Applet implements Runnable
{
// Ссылка на задачу рисования строки текста
Thread m_HorzScroll = null;
// Значения параметров по умолчанию
private String m_Str = "Hello from Java!";
private String m_Fnt = "Helvetica";
private String m_style = "BOLD";
private int m_size = 12;
private String m_color = "red";
private int m_delay = 50;
// Имена параметров
private final String PARAM_str = "str";
private final String PARAM_font = "font";
private final String PARAM_style = "style";
private final String PARAM_size = "size";
private final String PARAM_color = "color";
private final String PARAM_delay = "delay";
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: HorzScroll\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// getParameterInfo
// Иинформация о параметрах
// -------------------------------------------------------
Исходные тексты приложения
Исходные тексты приложения Synchro приведены в листинге 1.11.
Листинг 1.11. Файл Synchro\Synchro.javal
// =========================================================
// Демонстрация синхронизации двух задач
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
// =========================================================
// Основной класс аплета
// =========================================================
public class Synchro extends Applet
{
// Ссылка на задачу рисования прямоугольников
DrawRectangles m_DrawRectThread = null;
// Ссылка на задачу, периодически разблокирующую задачу
// рисования прямоугольников
NotifyTask m_NotifyTaskThread = null;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: Synchro\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// paint
// Метод paint, выполняющий рисование в окне аплета
// -------------------------------------------------------
public void paint(Graphics g)
{
// Определяем текущие размеры окна аплета
Dimension dimAppWndDimension = size();
// Выбираем в контекст отображения желтый цвет
g.setColor(Color.yellow);
// Закрашиваем внутреннюю область окна аплета
g.fillRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
Исходные тексты приложения
Исходный текст аплета MemStream приведен в листинге 2.3.
Листинг 2.3. Файл MemStream\MemStream.java
// =========================================================
// Работа с потоками в оперативной памяти
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
import java.io.*;
public class MemStream extends Applet
{
// Выходной поток
DataOutputStream OutStream;
// Входной поток
DataInputStream InStream;
// Строка, которая будет записана в поток
String sOut;
// Массив, в который будет копироваться содержимое
// выходного потока
byte[] bMemStream;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: MemStream\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Метод init, получает управление при инициализации
// аплета
// -------------------------------------------------------
public void init()
{
// Инициализируем строку для записи в поток
sOut = "Hello, Java!";
try
{
// Создаем выходной поток в оперативной памяти
ByteArrayOutputStream baStream =
new ByteArrayOutputStream(255);
// Создаем буферизованный форматированный поток
// на базе потока baStream
OutStream = new DataOutputStream(
new BufferedOutputStream(baStream));
Исходные тексты приложения
Исходные тексты приложения DirectFileAccess приведены в листинге 2.9.
Листинг 2.9. Файл DirectFileAccess\DirectFileAccess.java
// =========================================================
// Прямой доступ к файлу с помощью класса RandomAccessFile
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.io.*;
import java.util.*;
// =========================================================
// Класс DirectFileAccess
// Главный класс приложения
// =========================================================
public class DirectFileAccess
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
try
{
// Создаем новую базу данных
SimpleDBMS db = new SimpleDBMS(
"dbtest.idx", "dbtest.dat");
// Добавляем в нее три записи
db.AddRecord("Ivanov", 1000);
db.AddRecord("Petrov", 2000);
db.AddRecord("Sidoroff", 3000);
// Получаем и отображаем содержимое первых трез
// записей с номерами 2, 1 и 0
System.out.println(db.GetRecordByNumber(2));
System.out.println(db.GetRecordByNumber(1));
System.out.println(db.GetRecordByNumber(0));
// Закрываем базу данных
db.close();
// После ввода любой строки завершаем работу программы
System.out.println("Press <Enter> to terminate...");
System.in.read(bKbdInput);
}
catch(Exception ioe)
{
System.out.println(ioe.toString());
}
}
Исходные тексты приложения
Исходные тексты приложения ShowChart приведены в листинге 3.3.
Листинг 3.3. Файл ShowChart\ShowChart.java
// =========================================================
// Рисование круговой диаграммы, данные для которой
// получены от сервера Web через сеть
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
import java.net.*;
import java.io.*;
import java.util.*;
public class ShowChart extends Applet
{
// Адрес URL файла с данными для круговой диаграммы
URL SrcURL;
// Содержимое этого файла
Object URLContent;
// Код ошибки
int errno = 0;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: ShowChart\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Метод, получающий управление при инициализации аплета
// -------------------------------------------------------
public void init()
{
try
{
// Создаем объект класса URL для файла с данными
// для круговой диаграммы
SrcURL = new URL("http://frolov/chart.txt");
try
{
// Получаем содержимое этого файла
URLContent = SrcURL.openConnection().getContent();
}
catch (IOException ioe)
{
showStatus("getContent exception");
// При возникновении исключения во время получения
Исходные тексты приложения
Исходный текст приложения ImageDraw вы найдете в листинге 4.1.
Листинг 4.1. Файл ImageDraw\ImageDraw.java
// =========================================================
// Рисование растровых изображений
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class ImageDraw extends Applet
{
// Изображение флоппи-диска
Image FloppyDiskImg;
// Изображение компакт-диска
Image CDDiskImg;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: ImageDraw\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Метод init, получает управление
// при инициализации аплета
// -------------------------------------------------------
public void init()
{
// Загружаем изображение флоппи-диска
FloppyDiskImg = getImage(getCodeBase(), "disk.gif");
// Загружаем изображение флоппи-диска
CDDiskImg = getImage(getCodeBase(), "cd.gif");
}
// -------------------------------------------------------
// paint
// Метод paint, выполняющий рисование в окне аплета
// -------------------------------------------------------
public void paint(Graphics g)
{
// Определяем текущие размеры окна аплета
Dimension dimAppWndDimension = size();
// Выбираем в контекст отображения белый цвет
Исходные тексты приложения
Исходные тексты приложения ImageDrawWait вы найдете в листинге 4.3.
Листинг 4.3. Файл ImageDrawWait\ImageDrawWait.java
// =========================================================
// Рисование растровых изображений с ожиданием их загрузки
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class ImageDrawWait extends Applet
{
// Фоновое изображение
Image BkgImg;
// Изображение флоппи-диска
Image FloppyDiskImg;
// Изображение компакт-диска
Image CDDiskImg;
// Ссылка на MediaTracker
MediaTracker mt;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: ImageDrawWait\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Метод init, получает управление
// при инициализации аплета
// -------------------------------------------------------
public void init()
{
// Создаем объект класса MediaTracker
mt = new MediaTracker(this);
// Загружаем фоновое изображение
BkgImg = getImage(getCodeBase(), "bkg.gif");
// Добавляем его в список объекта MediaTracker
mt.addImage(BkgImg , 0);
// Загружаем изображение флоппи-диска
FloppyDiskImg = getImage(getCodeBase(), "disk.gif");
// Добавляем его в список объекта MediaTracker
mt.addImage(FloppyDiskImg, 0);
Исходные тексты приложения
Главный файл исходных текстов приложения DrawImageObserver приведен в листинге 4.5.
Листинг 4.5. Файл DrawImageObserver\DrawImageObserver.java
// =========================================================
// Рисование растровых изображений с ожиданием их загрузки
// Для ожидания применяется интерфейс ImageObserver
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class DrawImageObserver extends Applet
{
// Фоновое изображение
Image BkgImg;
boolean fAllLoaded = false;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: DrawImageObserver\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Метод init, получает управление при
// инициализации аплета
// -------------------------------------------------------
public void init()
{
// Загружаем фоновое изображение
BkgImg = getImage(getCodeBase(), "bkg.gif");
}
// -------------------------------------------------------
// imageUpdate
// Вызывается, когда появляется информация об изображении
// -------------------------------------------------------
public boolean imageUpdate(Image img, int flags,
int x, int y, int w, int h)
{
// Проверяем, все ли изображение загружено
fAllLoaded = ((flags & ALLBITS) != 0);
// Если все, перерисовываем окно
Исходные тексты приложения
Главный файл исходных текстов приложения CDRotation представлен в листинге 4.7.
Листинг 4.7. Файл CDRotation\CDRotation.java
// =========================================================
// Рисование вращающегося компакт-диска
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class CDRotation extends Applet implements Runnable
{
// Ссылка на задачу рисования
// вращающегося компакт-диска
Thread m_CDRotation = null;
// Контекст отображения для рисования
private Graphics m_Graphics;
// Массив изображений компакт-диска
private Image m_Images[];
// Номер текущего изображения
private int m_nCurrImage;
// Ширина изображения
private int m_nImgWidth = 0;
// Высота изображения
private int m_nImgHeight = 0;
// Флаг загрузки всех изображений
private boolean m_fAllLoaded = false;
// Общее количество изображений
private final int NUM_IMAGES = 11;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: CDRotation\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// displayImage
// Рисование текущего изображения, если все изображения
// уже загружены
// -------------------------------------------------------
private void displayImage(Graphics g)
{
// Если не все изображения загружены,
Исходные тексты приложения
Основной файл исходных текстов приложения приведен в листинге 5.1.
Листинг 5.1. Файл Audio\ Audio.java
// =========================================================
// Проигрывание аудиофайла
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class Audio extends Applet
{
// -------------------------------------------------------
// Поля класса.
// Создаются автоматически для всех параметров аплета
// -------------------------------------------------------
// Строка для хранения имени аудиофайла
private String m_ClipName = "kaas.au";
// -------------------------------------------------------
// Имена параметров
// -------------------------------------------------------
// Строка имени параметра
private final String PARAM_ClipName = "ClipName";
// Аудиоклип
AudioClip auClip;
// Кнопка для однократного проигрывания
Button btPlay;
// Кнопка для проигрывания в цикле
Button btLoop;
// Кнопка для остановки проигрывания
Button btStop;
// Флаг проигрывания в цикле
boolean fLoopPlay = false;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: Audio\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// getParameterInfo
// Метод, возвращающий описание параметров
Исходные тексты приложения
Основной файл исходных текстов вы найдете в листинге 6.1.
Листинг 6.1. Файл Inspector\Inspector.java
// =========================================================
// Аплет, который получает список всех аплетов
// и управляет аплетом Audio
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
import java.util.*;
// Импортируем класс Audio, так как нам нужен доступ к
// его полям
import Audio;
public class Inspector extends Applet
{
// Контекст аплетов
AppletContext appContext;
// Список аплетов, расположенных в документе HTML
Enumeration eApplets;
// Ссылка на аплет Audio
Audio appAudio = null;
// Кнопка для однократного проигрывания
Button btPlay;
// Кнопка для проигрывания в цикле
Button btLoop;
// Кнопка для остановки проигрывания
Button btStop;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: Inspector\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Вызывается во время инициализации аплета
// -------------------------------------------------------
public void init()
{
// Создаем кнопку для однократного проигрывания
btPlay = new Button("Play");
// Создаем кнопку для проигрывания в цикле
btLoop = new Button("Loop");
Исходные тексты приложения
Исходные текст основного класса приложения Combi представлен в листинге 7.1.
Листинг 7.1. Файл Combi\Combi.java
// =========================================================
// Приложение, способное работать как аплет, включенный
// в документ HTML, а также как автономное приложение Java
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
// Импортируем класс CombiFrame
import CombiFrame;
// =========================================================
// Combi
// Основной класс приложения
// =========================================================
public class Combi extends Applet
{
// Режим работы приложения. Если это поле содержит
// значение true, приложение работает автономно, если
// false - в составе документа HTML под управлением
// навигатора
boolean m_fStandAlone = false;
// -------------------------------------------------------
// main
// Метод main получает управление, когда приложение
// работает автономно
// -------------------------------------------------------
public static void main(String args[])
{
// Создаем окно класса CombiFrame, унаследованного от
// класса Frame (окно фрейма)
CombiFrame frame = new CombiFrame("Combi");
// Отображаем окно фрейма
frame.show();
// Выполняем изменение размеров окна
// Скрываем окно фрейма
frame.hide();
// Изменяем размеры окна фрейма
frame.resize(
frame.insets().left + frame.insets().right + 320,
frame.insets().top + frame.insets().bottom + 240);
// Создаем аплет класса Combi
Combi applet_Combi = new Combi();
// Добавляем окно этого аплета в окно фрейма
frame.add("Center", applet_Combi);
Исходные тексты приложения InetAddressDemo
Исходные тексты приложения InetAddressDemo приведены в листинге 3.1.
Листинг 3.1. Файл InetAddressDemo\InetAddressDemo.java
// =========================================================
// Работа с адресами IP с помощью класса InetAddress
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.net.*;
import java.io.*;
import java.util.*;
// =========================================================
// Класс InetAddressDemo
// Главный класс приложения
// =========================================================
public class InetAddressDemo
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Введенная строка
String sIn;
// Рабочая строка
String str;
// Адрес локального узла
InetAddress iaLocal;
// Массив байт адреса локального узла
byte[] iaLocalIP;
// Массив всех адресов удаленного узла
InetAddress[] iaRemoteAll;
try
{
// Получаем адрес локального узла
iaLocal = InetAddress.getLocalHost();
// Отображаем имя локального узла на консоли
System.out.println("Local host name: " +
iaLocal.getHostName());
// Определяем адрес IP локального узла
iaLocalIP = iaLocal.getAddress();
// Отображаем отдельные байты адреса IP
// локального узла
System.out.println("Local host IP address: " +
(0xff & (int)iaLocalIP[0]) + "." +
(0xff & (int)iaLocalIP[1]) + "." +
Исходные тексты программы CGI
В лситинге 3.10 мы привели исходный текст программы CGI с именем controls. Он несколько упрощен по сравнению с исходным текстом одноименного приложения, описанного в 29 томе “Библиотеки системного программиста” - мы выбросили обработку метода передачи данных GET, так как наше приложение CallCGI передает данные только методом POST. Описание этой программы вы найдете в упомянутом 29 томе.
Листинг 3.10. Файл controls\controls.c
// ===============================================
// Программа CGI controls.c
// Демонстрирует методы получения и обработки
// данных от форм, расположенных в документах HTML
//
// (C) Фролов А.В., 1997
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// ===============================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Прототипы функций перекодировки
void DecodeStr(char *szString);
char DecodeHex(char *str);
// ------------------------------------------------
// Функция main
// Точка входа программы CGI
// ------------------------------------------------
void main(int argc, char *argv[])
{
int lSize;
FILE * fileReceived;
char * szMethod;
char szBuf[8196];
char szSrcBuf[8196];
char * szPtr;
char * szParam;
// Вывод заголовка HTTP и разделительной строки
printf("Content-type: text/html\n\n");
// Вывод начального форагмента документа HTML,
// формируемого динамически
printf("<!DOCTYPE HTML PUBLIC"
" \"-//W3C//DTD HTML 3.2//EN\">");
printf("<HTML><HEAD><TITLE>Call CGI from Java"
"</TITLE></HEAD><BODY BGCOLOR=#FFFFFF>");
// Определяем метод передачи данных
szMethod = getenv("REQUEST_METHOD");
// Обработка метода POST
if(!strcmp(szMethod, "POST"))
{
// Определяем размер данных, полученных от навигатора
// при передаче данных из полей формы
lSize = atoi(getenv("CONTENT_LENGTH"));
// Читаем эти данные в буфер szBuf из
// стандартного потока ввода STDIN
fread(szBuf, lSize, 1, stdin);
// Создаем файл, в который будут записаны
// принятые данные
fileReceived = fopen("received.dat", "w");
// Выполняем запись принятых данных
fwrite(szBuf, lSize, 1, fileReceived);
// Закрываем файл принятых данных
fclose(fileReceived);
// Отображаем значения некоторых переменных среды
printf("<H2>Environment variables</H2>");
// Метод доступа
printf("REQUEST_METHOD = %s", getenv("REQUEST_METHOD"));
// Размер полученных данных в байтах
printf("<BR>CONTENT_LENGTH = %ld", lSize);
// Тип полученных данных
printf("<BR>CONTENT_TYPE = %s", getenv("CONTENT_TYPE"));
// Закрываем буфер данных двоичным нулем,
// превращая его таким образом в строку
szBuf[lSize] = '\0';
// Делаем копию принятых данных в буфер szSrcBuf
strcpy(szSrcBuf, szBuf);
// Отображаем принятые данные без обработки
printf("<H2>Received data</H2>");
printf("<P>%s", szSrcBuf);
// Выполняем перекодировку принятых данных
DecodeStr(szSrcBuf);
// Отображаем результат перекодировки
printf("<H2>Decoded data</H2>");
printf("<P>%s", szSrcBuf);
// Выводим список значений полей формы
printf("<H2>Filds list</H2>");
// Дописываем в конец буфера принятых данных
// символ "&", который используется в качестве
// разделителя значений полей
szBuf[lSize] = '&';
szBuf[lSize + 1] = '\0';
// Цикл по полям формы
for(szParam = szBuf;;)
{
// Ищем очередной разделитель
szPtr = strchr(szParam, '&');
// Если он найден, раскодируем строку параметров
if(szPtr != NULL)
{
*szPtr = '\0';
DecodeStr(szParam);
// Выводим в документ значение параметра
printf("%s<BR>", szParam);
// Переходим к следующему параметру
szParam = szPtr + 1;
// Если достигнут конец буфера, завершаем цикл
if(szParam >= (szBuf + lSize))
break;
}
else
break;
}
// Выводим завершающий фрагмент документа HTML
printf("</BODY></HTML>");
return;
}
}
// ------------------------------------------------
// Функция DecodeStr
// Раскодирование строки из кодировки URL
// ------------------------------------------------
void DecodeStr(char *szString)
{
int src;
int dst;
char ch;
// Цикл по строке
for(src=0, dst=0; szString[src]; src++, dst++)
{
// Получаем очередной символ перекодируемой строки
ch = szString[src];
// Заменяем символ "+" на пробел
ch = (ch == '+') ? ' ' : ch;
// Сохраняем результат
szString[dst] = ch;
// Обработка шестнадцатеричных кодов вида "%xx"
if(ch == '%')
{
// Выполняем преобразование строки "%xx"
// в код символа
szString[dst] = DecodeHex(&szString[src + 1]);
src += 2;
}
}
// Закрываем строку двоичным нулем
szString[dst] = '\0';
}
// ------------------------------------------------
// Функция DecodeHex
// Раскодирование строки "%xx"
// ------------------------------------------------
char DecodeHex(char *str)
{
char ch;
// Обрабатываем старший разряд
if(str[0] >= 'A')
ch = ((str[0] & 0xdf) - 'A') + 10;
else
ch = str[0] - '0';
// Сдвигаем его влево на 4 бита
ch <<= 4;
// Обрабатываем младший разряд и складываем
// его со старшим
if(str[1] >= 'A')
ch += ((str[1] & 0xdf) - 'A') + 10;
else
ch += str[1] - '0';
// Возвращаем результат перекодировки
return ch;
}
Исходный текст клиентского приложения SocketClient
Исходный текст клиентского приложения SocketClient приведен в листинге3.6.
Листинг 3.6. Файл SocketClient\SocketClient.java
// =========================================================
// Использование потоковых сокетов.
// Приложение клиента
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.io.*;
import java.net.*;
import java.util.*;
public class SocketClient
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
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)
{
// Преобразуем строку в формат String
str = new String(bKbdInput, 0);
// Обрезаем строку, удаляя символ конца строки
StringTokenizer st;
st = new StringTokenizer(str, "\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, "\n");
str = new String((String)st.nextElement());
System.out.println(">> " + str);
// Если введена строка 'quit', завершаем
// работу приложения
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());
}
}
}
Исходный текст приложения
Исходный текст приложения приведен в листинге 2.1.
После трансляции исходного текста вы можете запустить его на выполнение непосредственно из среды разработки приложений Microsoft Visual J++. При этом, когда на экране появится диалоговая панель Information For Running Class (рис. 2.5), вы должны указать в поле Class file name имя Standard, а в поле Run project under включить переключатель Stand-alone interpreter.
Рис. 2.5. Заполнение диалоговой панели Information For Running Class
При этом приложение будет запущено под управлением автономного интерпретатора Java jview.exe.
Листинг 2.1. Файл Standard\Standard.java
// =========================================================
// Демонстрация использования стандартных потоков
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.io.*;
public class Standard
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Введенная строка
String sOut;
// Выполняем попытку вывода на консоль
// строки приглашения
try
{
// Выводим строку приглашения
System.out.println("Hello, Java!\n" +
"Enter string and press <Enter>...");
// Читаем с клавиатуры строку
System.in.read(bKbdInput);
// Преобразуем введенные символы в строку типа String
sOut = new String(bKbdInput, 0);
// Выводим только что введенную строку на консоль
System.out.println(sOut);
}
catch(Exception ioe)
{
// При возникновении исключения выводим его описание
// на консоль
System.err.println(ioe.toString());
}
// Ожидаем ввода с клавиатуры, а затем завершаем
// работу приложения
try
{
System.out.println(
"Press <Enter> to terminate application...");
System.in.read(bKbdInput);
}
catch(Exception ioe)
{
System.err.println(ioe.toString());
}
}
}
Исходный текст приложения
Исходный текст приложения StreamDemo представлен в листинге 2.2.
Листинг 2.2. Файл StreamDemo\StreamDemo.java
// =========================================================
// Демонстрация использования потоков для работы с файлами
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.io.*;
public class StreamDemo
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Выходной поток
DataOutputStream OutStream;
// Входной поток
DataInputStream InStream;
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Введенная строка, которая будет записана в поток
String sOut;
// Выполняем попытку вывода на консоль строки
// приглашения
try
{
// Выводим строку приглашения
System.out.println("Hello, Java!\n" +
"Enter string and press <Enter>...");
// Читаем с клавиатуры строку для записи в файл
System.in.read(bKbdInput);
// Преобразуем введенные символы в строку типа String
sOut = new String(bKbdInput, 0);
}
catch(Exception ioe)
{
// При возникновении исключения выводим его описание
// на консоль
System.out.println(ioe.toString());
}
// Выполняем попытку записи в выходной поток
try
{
// Создаем выходной буферизованный поток данных
OutStream = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("output.txt")));
// Записываем строку sOut в выходной поток
OutStream.writeBytes(sOut);
Исходный текст приложения
Исходный текст приложения StreamToken представлен в листинге 2.5.
Листинг 2.5. Файл StreamToken\StreamToken.java
// =========================================================
// Разбор входного потока при помощи класса
// StreamTokenizer
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.io.*;
// =========================================================
// Класс StreamToken
// Главный класс приложения
// =========================================================
public class StreamToken
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Выходной поток
DataOutputStream OutStream;
// Входной поток
DataInputStream InStream;
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Введенная строка, которая будет записана в поток
String sOut;
try
{
// Выводим строку приглашения
System.out.println("Enter string to parse...");
// Читаем с клавиатуры строку для записи в файл
System.in.read(bKbdInput);
// Преобразуем введенные символы в строку типа String
sOut = new String(bKbdInput, 0);
// Создаем выходной буферизованный поток данных
OutStream = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("output.txt")));
// Записываем строку sOut в выходной поток
OutStream.writeBytes(sOut);
// Закрываем выходной поток
OutStream.close();
// Создаем входной буферизованный поток данных
InStream = new DataInputStream(
Исходный текст приложения
Исходный текст приложения StringToken вы найдете в листинге 2.6.
Листинг 2.6. Файл StringToken\StringToken.java
// =========================================================
// Разбор текстовой строки при помощи класса
// StringTokenizer
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.io.*;
import java.util.*;
// =========================================================
// Класс StringToken
// Главный класс приложения
// =========================================================
public class StringToken
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Введенная строка, которая будет записана в поток
String sOut;
String str;
try
{
// Выводим строку приглашения
System.out.println("Enter string to parse...");
// Читаем с клавиатуры строку для записи в файл
System.in.read(bKbdInput);
// Преобразуем введенные символы в строку типа String
sOut = new String(bKbdInput, 0);
// Создаем разборщик текстовой строки
StringTokenizer st;
st = new StringTokenizer(sOut, ",.; ");
// Запускаем цикл разборки строки
while(st.hasMoreElements())
{
// Получаем очередной жлемент
str = new String((String)st.nextElement());
// Записываем его в стандартный поток вывода
System.out.println(str);
}
System.out.println("Press <Enter> to terminate...");
System.in.read(bKbdInput);
}
catch(Exception ioe)
{
System.out.println(ioe.toString());
}
}
}
Исходный текст приложения
Исходный текст приложения DirList представлен в листинге 2.8.
Листинг 2.8. Файл DirList\DirList.java
// =========================================================
// Просмотр содержимого каталога при помощи класса File
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.io.*;
import java.util.*;
// =========================================================
// Класс DirList
// Главный класс приложения
// =========================================================
public class DirList
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Путь к каталогу, содержимое которого
// мы будем просматривать
String sDirPath;
// Маска для просмотра
String sMask;
// Массив строк содержимого каталога
String[] dirlist;
try
{
// Выводим строку приглашения для ввода пути
// к каталогу, содержимое которого будем просматривать
System.out.println("Enter directory path...");
System.in.read(bKbdInput);
sDirPath = new String(bKbdInput, 0);
StringTokenizer st;
st = new StringTokenizer(sDirPath, "\r\n");
sDirPath = new String((String)st.nextElement());
// Вводим строку маски
System.out.println("Enter mask...");
System.in.read(bKbdInput);
sMask = new String(bKbdInput, 0);
st = new StringTokenizer(sMask, "\r\n");
sMask = new String((String)st.nextElement());
// Создаем объект класса File, соответствующий
Исходный текст приложения
Исходный текст приложения URLDemo приведены в листинге 3.2.
Листинг 3.2. Файл URLDemo\URLDemo.java
// =========================================================
// Копирование файла, расположенного в каталоге
// сервера Web, с помощью класса URL
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.net.*;
import java.io.*;
import java.util.*;
// =========================================================
// Класс InetAddressDemo
// Главный класс приложения
// =========================================================
public class URLDemo
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Введенная строка
String sIn;
// Строка адреса URL
String sURL;
// Адрес URL удаленного узла
URL u;
// Рабочий буфер для копирования файла
byte buf[] = new byte[1024];
try
{
// Вводим адрес URL удаленного узла
System.out.println("Enter remote host name...");
System.in.read(bKbdInput);
sIn = new String(bKbdInput, 0);
// Обрезаем строку, удаляя символ конца строки
StringTokenizer st;
st = new StringTokenizer(sIn, "\r\n");
sURL = new String((String)st.nextElement());
// Создаем объект класса URL
u = new URL(sURL);
// Создаем входной поток, связанный с объектом,
// адрес URL которого хранится в поле u
InputStream is = u.openStream();
// Создаем выходной буферизованный форматированный
Исходный текст приложения CallCGI
Исходный текст приложения CallCGI приведен в листинге 3.9.
Листинг 3.9. Файл CallCGI\CallCGI.java
// =========================================================
// Вызов расширения сервера Web на базе интерфейса CGI
// из приложения Java
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.io.*;
import java.net.*;
import java.util.*;
public class CallCGI
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Размер принятого блока данных
int length;
// Рабочая строка
String str;
// Адрес URL вызываемой программы CGI
URL u;
// Канал связи с расширением CGI
URLConnection c;
// Выходной поток для передачи данных расширению CGI
PrintStream ps;
// Входной поток для получения данных от расширения CGI
DataInputStream is;
try
{
// Выводим строку приглашения
System.out.println("CGI extension call" +
"\nEnter any string for send to CGI...");
// Читаем строку, передаваемую расширению,
// с клавиатуры
length = System.in.read(bKbdInput);
// Если строка не пустая, обрабатываем ее
if(length != 1)
{
// Преобразуем строку в формат String
str = new String(bKbdInput, 0);
// Обрезаем строку, удаляя символ конца строки
StringTokenizer st;
st = new StringTokenizer(str, "\n");
str = new String((String)st.nextElement());
// Выполняем кодировку URL для передаваемой строки
String StrEncoded = URLEncoder.encode(str);
// Отображаем перекодированную строку
System.out.println("Encoded string: >" +
StrEncoded + "<");
// Создаем объект класса URL для расширения CGI
u = new URL(
"http://frolov/frolov-cgi/controls.exe");
// Открываем канал связи с расширением CGI
c = u.openConnection();
// Создаем выходной поток данных для передачи
// введенной строки серверу CGI
ps = new PrintStream(c.getOutputStream());
// Передаем закодированную строку расширению CGI
ps.println(StrEncoded);
// Закрываем выходной поток
ps.close();
// Создаем входной поток для приема данных от
// расширения CGI
is = new DataInputStream(c.getInputStream());
System.out.println(
"\n------------------------------------------" +
"\n Data from CGI extension" +
"\n------------------------------------------\n");
// Прием данных выполняем в цикле
while (true)
{
// Получаем очередную строку
str = is.readLine();
// Если последняя строка, прерываем цикл
if(str == null)
break;
// Отображаем принятую строку
System.out.println(str);
}
// Закрываем входной поток
is.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());
}
}
}
Исходный текст приложения DatagramClient
В листинге 3.8 приведен исходный текст приложения DatagramClient.
Листинг 3.8. Файл DatagramClient\DatagramClient.java
// =========================================================
// Использование датаграммных сокетов
// Приложение клиента
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.io.*;
import java.net.*;
import java.util.*;
public class DatagramClient
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Размер введенной строки
int length;
// Рабочая строка
String str;
// Сокет клиента
DatagramSocket s;
// Передаваемый пакет
DatagramPacket pout;
try
{
// Выводим строку приглашения
System.out.println(
"Datagram Socket Client Application" +
"\nEnter any string or 'quit' to exit...");
}
catch(Exception ioe)
{
// При возникновении исключения выводим его описание
// на консоль
System.out.println(ioe.toString());
}
try
{
// Получаем адрес локального узла
InetAddress OutAddress = InetAddress.getLocalHost();
// Создаем сокет с использованием любого
// свободного порта
s = new DatagramSocket();
// Создаем передаваемый пакет
pout = new DatagramPacket(bKbdInput, bKbdInput.length,
OutAddress, 9998);
// Цикл передачи команд серверу
while(true)
{
// Читаем строку команды с клавиатуры
length = System.in.read(bKbdInput);
// Если строка не пустая, обрабатываем ее
if(length != 1)
{
// Преобразуем строку в формат String
str = new String(bKbdInput, 0);
// Обрезаем строку, удаляя символ конца строки
StringTokenizer st;
st = new StringTokenizer(str, "\n");
str = new String((String)st.nextElement());
// Выводим передаваемую строку команды
// на консоль для контроля
System.out.println("> " + str);
// Посылаем пакет серверу
s.send(pout);
// Если введена команда 'quit', прерываем цикл
if(str.equals("quit"))
break;
}
}
// Закрываем сокет
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());
}
}
}
Исходный текст приложения DatagramServer
Исходный текст приложения DatagramServer вы найдете в листинге 3.7.
Листинг 3.7. Файл DatagramServer\DatagramServer.java
// =========================================================
// Использование датаграммных сокетов
// Приложение сервера
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.io.*;
import java.net.*;
import java.util.*;
public class DatagramServer
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Буфер для чтения команд
byte buf[] = new byte[512];
// Сокет сервера
DatagramSocket s;
// Принимаемый пакет
DatagramPacket pinp;
// Адрес узла, откуда пришел принятый пакет
InetAddress SrcAddress;
// Порт, откуда пришел принятый пакет
int SrcPort;
try
{
// Выводим строку приглашения
System.out.println(
"Datagramm Socket Server Application");
}
catch(Exception ioe)
{
// При возникновении исключения выводим его описание
// на консоль
System.out.println(ioe.toString());
}
try
{
// Создаем сокет сервера
s = new DatagramSocket(9998);
// Создаем пакет для приема команд
pinp = new DatagramPacket(buf, 512);
// Цикл обработки команд, полученных от клиента
while(true)
{
// Принимаем пакет от клиента
s.receive(pinp);
// Получаем адрес узла, приславшего пакет
SrcAddress = pinp.getAddress();
// Получаем порт, на котором был передан пакет
SrcPort = pinp.getPort();
// Отображаем принятую команду на консоли сервера
// Формируем строку из принятого блока
String str = new String(buf, 0);
// Обрезаем строку, удаляя символ конца строки
StringTokenizer st;
st = new StringTokenizer(str, "\r\n");
str = new String((String)st.nextElement());
// Выводим строку команды на консоль
System.out.println("> " + str + " < " +
"port: " + SrcPort);
// Если пришла команда 'quit', прерываем цикл
if(str.equals("quit"))
break;
}
// Закрываем сокет сервера
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());
}
}
}
Исходный текст приложения FileInfo
Исходный текст приложения FileInfo приведен в листинге 2.7.
Листинг 2.7. Файл FileInfo\FileInfo.java
// =========================================================
// Просмотр атрибутов файла при помощи класса File
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.io.*;
import java.util.*;
// =========================================================
// Класс FileInfo
// Главный класс приложения
// =========================================================
public class FileInfo
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Введенная строка
String sFilePath;
try
{
// Выводим строку приглашения
System.out.println("Enter file path...");
// Читаем с клавиатуры строку для записи в файл
System.in.read(bKbdInput);
// Преобразуем введенные символы в строку типа String
sFilePath= new String(bKbdInput, 0);
// Отбрасываем символ конца строки
StringTokenizer st;
st = new StringTokenizer(sFilePath, "\r\n");
sFilePath = new String((String)st.nextElement());
// Создаем объект класса File, соответствующий
// введенному пути
File fl = new File(sFilePath);
// Если указанный файл или каталог не существует,
// выводим сообщение и завершаем работу
if(!fl.exists())
{
System.out.println("File not found: " + sFilePath);
}
// Если путь существует, определяем параметры
// соответствующего файла или каталога
else
{
// Проверяем, был указан файл или каталог
if(fl.isDirectory())
System.out.println("File " + sFilePath +
" is directory");
else if (fl.isFile())
System.out.println("File " + sFilePath +
" is file");
// Получаем и выводим атрибуты файла или каталога
System.out.println(
"Parent: " + fl.getParent() +
"\nLength: " + fl.length() +
"\nRead op. available: " + fl.canRead() +
"\nWrite op. available: " + fl.canWrite());
}
System.out.println("Press <Enter> to terminate...");
System.in.read(bKbdInput);
}
catch(Exception ioe)
{
System.out.println(ioe.toString());
}
}
}
Исходный текст серверного приложения SocketServ
Исходный текст серверного приложения SocketServ приведен в листинге3.5.
Листинг 3.5. Файл SocketServ\SocketServ.java
// =========================================================
// Использование потоковых сокетов.
// Приложение сервера
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.io.*;
import java.net.*;
import java.util.*;
public class SocketServ
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Объект класса ServerSocket для создания канала
ServerSocket ss;
// Сокет сервера
Socket s;
// Входной поток для приема команд от клиента
InputStream is;
// Выходной поток для передачи ответа клиенту
OutputStream os;
try
{
System.out.println("Socket Server Application");
}
catch(Exception ioe)
{
// При возникновении исключения выводим его описание
// на консоль
System.out.println(ioe.toString());
}
try
{
// Создаем объект класса ServerSocket
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());
}
}
}
Исключения при создании потоков
При создании потоков на базе классов FileOutputStream и FileInputStream могут возникать исключения FileNotFoundException, SecurityException, IOException.
Исключение FileNotFoundException возникает при попытке открыть входной поток данных для несуществующего файла, то есть когда файл не найден.
Исключение SecurityException возникает при попытке открыть файл, для которого запрещен доступ. Например, если файл можно только читать, а он открывается для записи, возникнет исключение SecurityException.
Если файл не может быть открыт для записи по каким-либо другим причинам, возникает исключение IOException.
Использование датаграммных сокетов
Как мы уже говорили, датаграммные сокеты не гарантируют доставку пакетов данных. Тем не менее, они работают быстрее потоковых и обеспечивают возможность широковещательной расслыки пакетов данных одновременно всем узлам сети. Последняя возможность используется не очень широко в сети Internet, однако в корпоративной сети Intranet вы вполне можете ей воспользоваться.
Для работы с датаграммными сокетами приложение должно создать сокет на базе класса DatagramSocket, а также подготовить объект класса DatagramPacket, в который будет записан принятый от партнера по сети блок данных.
Канал, а также входные и выходные потоки создавать не нужно. Данные передаются и принимаются методами send и receive, определенными в классе DatagramSocket.
Использование интерфейса AppletContext
Для того чтобы получить список всех аплетов, расположенных в текущем документе HTML, ваш аплет прежде всего должен получить ссылку на интерфейс AppletContext. Затем нужно вызвать метод getApplets, возвращающий искомый список.
Рассмотрим этот процесс подробно.
Как связаться с авторами
Полную информацию о всех наших книгах серий “Библиотека системного программиста” и “Персональный компьютер. Шаг за шагом”, а также дискеты к книгам, статьи и другую информацию вы можете найти в сети Internet на серверах Web по следующим адресам:
http://www.glasnet.ru/~frolov
http://www.dials.ccas.ru/frolov
Вы можете передать нам свои замечания и предложения по содержанию этой и других наших книг через электронную почту по адресам:
frolov@glas.apc.org
frolov.alexandr@usa.net
Если электронная почта вам недоступна, присылайте ваши отзывы в АО “Диалог-МИФИ” по адресу:
115409, Москва, ул. Москворечье, 31, корп. 2,
тел. 324-43-77
Приносим свои извинения за то что не можем ответить на каждое письмо. Мы также не занимаемся продажей и рассылкой книг, дискет, рекламы, отдельных фрагментов наших книг и исходных текстов к книгам. По этим вопросам обращайтесь непосредственно в издательство “Диалог-МИФИ”.
Класс BufferedInputStream
Буферизация операций ввода и вывода в большинстве случаев значительно ускоряет работу приложений, так как при ее использовании сокращается количество обращений к системе для обмена данными с внешними устройствами.
Класс BufferedInputStream может быть использован приложениями Java для организации буферизованных потоков ввода. Заметим, что конструкторы этого класса в качестве параметра получают ссылку на объект класса InputStream. Таким образом, вы не можете просто создать объект класса BufferedInputStream, не создав перед этим объекта класса InputStream. Подробности мы обсудим позже.
Класс BufferedOutputStream
Класс BufferedOutputStream предназначен для создания буферизованных потоков вывода. Как мы уже говорили, буферизация ускоряет работу приложений с потоками.
Класс ByteArrayInputStream
При необходимости вы можете создать в приложениях Java входной поток данных не на базе локального или удаленного файла, а на базе массива, расположенного в оперативной памяти. Класс ByteArrayInputStream предназначен именно для этого - вы передаете конструктору класса ссылку на массив, и получаете входной поток данных, связанный с этим массивом.
Потоки в оперативной памяти могут быть использованы для временного хранения данных. Заметим, что так как аплеты Java не могут обращаться к локальным файлам, для создания временных файлов можно использовать потоки в оперативной памяти на базе класса ByteArrayInputStream. Другую возможность предоставляет класс StringBufferInputStream, рассмотренный ниже.
С помощью класса ByteArrayInputStream вы можете создать входной поток на базе массива байт, расположенного в оперативной памяти. В этом классе определено два конструктора:
public ByteArrayInputStream(byte buf[]);
public ByteArrayInputStream(byte buf[], int offset,
int length);
Первый конструктор получает через единственный параметр ссылку на массив, который будет использован для создания входного потока. Второй позволяет дополнительно указать смещение offset и размер области памяти length, которая будет использована для создания потока.
Вот несколько методов, определенных в классе ByteArrayInputStream:
public int available();
public int read();
public int read(byte b[], int off, int len);
public void reset();
public long skip(long n);
Наиболее интересен из них метод available, с помощью которого можно определить, сколько байт имеется во входном потоке для чтения.
Обычно класс ByteArrayInputStream используется вместе с классом DataInputStream, что позволяет организовать форматный ввод данных.
Класс ByteArrayOutputStream
С помощью класса ByteArrayOutputStream можно создать поток вывода в оперативной памяти.
Класс ByteArrayOutputStream создан на базе класса OutputStream. В нем имеется два конструктора, прототипы которых представлены ниже:
public ByteArrayOutputStream();
public ByteArrayOutputStream(int size);
Первый из этих конструкторов создает выходной поток в оперативной памяти с начальным размером буфера, равным 32 байта. Второй позволяет указать необходимый размер буфера.
В классе ByteArrayOutputStream определено несколько достаточно полезных методов. Вот некоторые из них:
public void reset();
public int size();
public byte[] toByteArray();
public void writeTo(OutputStream out);
Метод reset сбрасывает счетчик байт, записанных в выходной поток. Если данные, записанные в поток вам больше не нужны, вы можете вызвать этот метод и использовать память, выделенную для потока, для записи других данных.
С помощью метода size можно определить количество байт данных, записанных в поток.
Метод toByteArray позволяет скопировать данные, записанные в поток, в массив байт. Этот метод возвращает адрес созданного для этой цели массива.
С помощью метода writeTo вы можете скопировать содержимое данного потока в другой выходной поток, ссылка на который передается методу через параметр.
Для выполнения форматированного вывода в поток, вы должны создать поток на базе класса DataOutputStream, передав соответствующему конструктору ссылку на поток класса ByteArrayOutputStream.
Класс DatagramPacket
Перед тем как принимать или передавать данные с использованием методов receive и send вы должны подготовить объекты класса DatagramPacket. Метод receive запишет в такой объект принятые данные, а метод send - перешлет данные из объекта класса DatagramPacket узлу, адрес которого указан в пакете.
Подготовка объекта класса DatagramPacket для приема пакетов выполняется с помощью следующего конструктора:
public DatagramPacket(byte ibuf[], int ilength);
Этому конструктору передается ссылка на массив ibuf, в который нужно будет записать данные, и размер этого массива ilength.
Если вам нужно подготовить пакет для передачи, воспользуйтесь конструктором, который дополнительно позволяет задать адрес IP iaddr и номер порта iport адресата:
public DatagramPacket(byte ibuf[], int ilength,
InetAddress iaddr, int iport);
Таким образом, информация о том, в какой узел и на какой порт необходимо доставить пакет данных, хранится не в сокете, а в пакете, то есть в объекте класса DatagramPacket.
Помимо только что описанных конструкторов, в классе DatagramPacket определены четыре метода, позволяющие получить данные и информацию об адресе узла, из которого пришел пакет, или для которого предназначен пакет.
Метод getData возвращает ссылку на массив данных пакета:
public byte[] getData();
Размер пакета, данные из которого хранятся в этом массиве, легко определить с помощью метода getLength:
public int getLength();
Методы getAddress и getPort позволяют определить адрес и номер порта узла, откуда пришел пакет, или узла, для которого предназначен пакет:
public InetAddress getAddress();
public int getPort();
Если вы создаете клиент-серверную систему, в которой сервер имеет заранее известный адрес и номер порта, а клиенты - произвольные адреса и различные номера портов, то после получения пакета от клиента сервер может определить с помощью методов getAddress и getPort адрес клиента для установления с ним связи.
Если же адрес сервера неизвестен, клиент может посылать широковещательные пакеты, указав в объекте класса DatagramPacket адрес сети. Такая методика обычно используется в локальных сетях.
Как указать адрес сети?
Напомним, что адрес IP состоит из двух частей - адреса сети и адреса узла. Для разделения компонент 32-разрядного адреса IP используется 32-разрядная маска, в которой битам адреса сети соответствуют единицы, а битам адреса узла - нули.
Например, адрес узла может быть указан как 193.24.111.2. Исходя из значения старшего байта адреса, это сеть класса С, для которой по умолчанию используется маска 255.255.255.0. Следовательно, адрес сети будет такой: 193.24.111.0. Подробнее об адресации в сетях TCP/IP вы можете прочитать в 23 томе “Библиотеки системного программиста”, о котором мы упоминали в разделе “Передача данных с использованием сокетов” нашей книги.
Класс DatagramSocket
Рассмотрим конструкторы и методы класса DatagramSocket, предназначенного для создания и использования датаграммных сокетов.
В классе DatagramSocket определены два конструктора, прототипы которых представлены ниже:
public DatagramSocket(int port);
public DatagramSocket();
Первый из этих конструкторов позволяет определить порт для сокета, второй предполагает использование любого свободного порта.
Обычно серверные приложения работают с использованием какого-то заранее определенного порта, номер которого известен клиентским приложениям. Поэтому для серверных приложений больше подходит первый из приведенных выше конструкторов.
Клиентские приложения, напротив, часто применяют любые свободные на локальном узле порты, поэтому для них годится конструктор без параметров.
Кстати, с помощью метода getLocalPort приложение всегда может узнать номер порта, закрепленного за данным сокетом:
public int getLocalPort();
Прием и передача данных на датаграммном сокете выполняется с помощью методов receive и send, соответственно:
public void receive(DatagramPacket p);
public void send(DatagramPacket p);
В качестве параметра этим методам передается ссылка на пакет данных (соответственно, принимаемый и передаваемый), определенный как объект класса DatagramPacket. Этот класс будет рассмотрен в следующем разделе нашей книги.
Еще один метод в классе DatagramSocket, которым вы будете пользоваться, это метод close, предназначенный для закрытия сокета:
public void close();
Напомним, что сборка мусора в Java выполняется только для объектов, находящихся в оперативной памяти. Такие объекты, как потоки и сокеты, вы должны закрывать после использования самостоятельно.
Класс DataInputStream
Составляя программы на языке программирования С, вы были вынуждены работать с потоками на уровне байт или, в лучшем случае, на уровне текстовых строк. Однако часто возникает необходимость записывать в потоки данных и читать оттуда объекты других типов, например, целые числа и числа типа double, числа в формате с плавающей десятичной точкой, массивы байт и символов и так далее.
Класс DataInputStream содержит методы, позволяющие извлекать из входного потока данные в перечисленных выше форматах или, как говорят, выполнять форматированный ввод данных. Он также реализует интерфейс DataInput, служащий для этой же цели. Поэтому класс DataInputStream очень удобен и часто применяется в приложениях для работы с потоками ввода.
Так же как и конструктор класса BufferedInputStream, конструктор класса DataInputStream должен получить через свои параметр ссылку на объект класса InputStream.
Класс DataOutputStream
С помощью класса DataOutputStream приложения Java могут выполнять форматированный вывод данных. Для ввода форматированных данных вы должны создать входной поток с использованием класса DataInputStream, о котором мы уже говорили. Класс DataOutputStream реализует интерфейс DataOutput.
Класс DrawEllipse
Исходный текст класса DrawEllipse лишь немного отличается от исходного текста класса DrawRectangles. Отличие есть в методе run - этот метод рисует не прямоугольники, а закрашенные эллипсы, вызывая для этого метод fillOval.
Класс File
Класс File предназначен для работы с оглавлениями каталогов. С помощью этого класса можно получить список файлов и каталогов, расположенных в заданном каталоге, создать или удалить каталог, переименовать файл или каталог, а также выполнить некоторые другие операции.
Класс FileDescriptor
C помощью класса FileDescriptor вы можете проверить идентификатор открытого файла.
Класс FileInputStream
Этот класс позволяет создать поток ввода на базе класса File или FileDescriptor.
Класс FileOutputStream
Этот класс позволяет создать поток вывода на базе класса File или FileDescriptor.
Класс FilterInputStream
Класс FilterInputStream, который происходит непосредственно от класса InputStream, является абстрактным классом, на базе которого созданы классы BufferedInputStream, DataInputStream, LineNumberInputStream и PushBackInputStream. Непосредственно класс FilterInputStream не используется в приложениях Java, так как, во-первых, он является абстрактным и предназначен для переопределения методов базового класса InputStream, а во-вторых, наиболее полезные методы для работы с потоками ввода имеются в классах, созданных на базе класса FilterInputStream.
Класс FilterOutputStream
Абстрактный класс FilterOutputStream служит прослойкой между классом OutputStream и классами BufferedOutputStream, DataOutputStream, а также PrintStream. Он выполняет роль, аналогичную роли рассмотренного ранее класса FilterIntputStream.
Класс фрейма для комбинированного приложения
Теперь о классе CombiFrame.
Определение этого класса выглядит достаточно просто:
class CombiFrame extends Frame
{
public CombiFrame(String str)
{
super (str);
}
public boolean handleEvent(Event evt)
{
switch (evt.id)
{
case Event.WINDOW_DESTROY:
{
dispose();
System.exit(0);
return true;
}
default:
return super.handleEvent(evt);
}
}
}
Класс CombiFrame создан на базе класса Frame и предназначен для создания окна фрейма, в которое будет добавлен аплет. В этом классе определен конструктор и метод handleEvent.
Конструктор выполняет простую задачу - создание окна фрейма. Для этого он вызывает конструктор базового класса Frame, передавая ему через параметр строку заголовка окна.
Задача метода handleEvent - удаление окна фрейма, когда пользователь пытается его закрыть, сделав, например, щелчок мышью по правой кнопке в заголовке окна. В случае такой попытки методу handleEvent передается извещение с кодом Event.WINDOW_DESTROY.
В процессе обработки этого извещения метод handleEvent удаляет окно фрейма, вызывая метод dispose, а затем завершает работу приложения, вызывая статический метод exit из класса System.
Все прочие извещения передаются методу handleEvent, определенному в базовом классе.
Класс Image
Как видите, процесс рисования растрового изображения в окне аплета предельно прост - вам достаточно загрузить изображение методом getImage и затем нарисовать его методом drawImage.
Но не забывайте, что метод getImage в действительности только создает объект класса Image, но не загружает его. Давайте посмотрим на класс Image.
В этом классе имеется единственный конструктор без параметров:
public Image();
Вы, однако, скорее всего будете создавать объекты класса Image при помощи метода getImage.
Методы getHeight и getWidth, определенные в классе Image, позволяют определить, соответственно, высоту и ширину изображения:
public abstract int getHeight(ImageObserver observer);
public abstract int getWidth(ImageObserver observer);
Так как при вызове этих методов изображение еще может быть не загружено, в качестве параметров методам передается ссылка на объект ImageObserver. Этот объект получит извещение, когда будет доступна высота или ширина изображения.
Метод getGraphics позволяет получить так называемый внеэкранный контекст отображения для рисования изображения не в окне аплета, а в оперативной памяти:
public abstract Graphics getGraphics();
Эта техника используется для того, чтобы вначале подготовить изображение в памяти, а затем за один прием отобразить его на экране.
Еще один метод класса Image, который мы рассмотрим, называется flush:
public abstract void flush();
Он освобождает ресурсы, занятые изображением.
Класс InputStream
Класс InputStream является базовым для большого количества классов, на базе которых создаются потоки ввода. Именно эти, производные классы применяются программистами, так как в них имеются намного более мощные методы, чем в классе InputStream. Эти методы позволяют работать с потоком ввода не на уровне отдельных байт, а на уровне объектов различных классов, например, класса String и других.
Класс LineNumberInputStream
С помощью класса LineNumberInputStream вы можете работать с текстовыми потоками, состоящими из отдельных строк, разделенных символами возврата каретки \r и перехода на следующую строку \n. Методы этого класса позволяют следить за нумерацией строк в таких потоках.
Класс OutputStream
Аналогично, класс OutputStream служит в качестве базового для различных классов, имеющих отношение к потокам вывода.
Класс PipedInputStream
С помощью классов PipedInputStream и PipedOutputStream можно организовать двухстороннюю передачу данных между двумя одновременно работающими задачами мультизадачного аплета.
Класс PipedOutputStream
Как мы уже говорили, классы PipedInputStream и PipedOutputStream предназначены для организации двухсторонней передачи данных между двумя одновременно работающими задачами мультизадачного аплета.
Класс PrintStream
Потоки, созданные с использованием класса PrintStream, предназначены для форматного вывода данных различных типов с целью их визуального представления в виде текстовой строки. Аналогичная операция в языке программирования С выполнялась функцией printf.
Класс PushBackInputStream
Класс PushBackInputStream позволяет возвратить в поток ввода только что прочитанный оттуда символ, с тем чтобы после этого данный символ можно было прочитать снова.
Класс RandomAccesFile
С помощью класса RandomAccesFile можно организовать работу с файлами в режиме прямого доступа, когда программа указывает смещение и размер блока данных, над которым выполняется операция ввода или вывода. Заметим, кстати, что классы InputStream и OutputStream также можно использовать для обращения к файлам в режиме прямого доступа.
Класс SequenceInputStream
Класс SequenceInputStream позволяет объединить несколько входных потоков в один поток. Если в процессе чтения будет достигнут конец первого потока такого объединения, в дальнейшем чтение будет выполняться из второго потока и так далее.
Класс SimpleDBMS
Рассмотрим теперь класс SimpleDBMS.
В этом классе определено три поля с именами idx, dat и idxFilePointer, а также три метода.
Класс StreamTokenizer
Очень удобен класс StreamTokenizer. Он позволяет организовать выделение из входного потока данных элементов, отделенных друг от друга заданными разделителями, такими, например, как запятая, пробел, символы возврата каретки и перевода строки.
Класс StreamTokenizer для разбора входных потоков
Если вы создаете приложение, предназначенное для обработки текстов (например, транслятор или просто разборщик файла конфигурации, содержащего значения различных параметров), вам может пригодиться класс StreamTokenizer. Создав объект этого класса для входного потока, вы можете легко решить задачу выделения из этого потока отдельных слов, символов, чисел и строк комментариев.
Класс StringBufferInputStream
Класс StringBufferInputStream позволяет создавать потоки ввода на базе строк класса String, используя при этом только младшие байты хранящихся в такой строке символов. Этот класс может служить дополнением для класса ByteArrayInputStream, который также предназначен для создания потоков на базе данных из оперативной памяти.
Класс StringBufferInputStream предназначен для создания входного потока на базе текстовой строки класса String. Ссылка на эту строку передается конструктору класса StringBufferInputStream через параметр:
public StringBufferInputStream(String s);
В классе StringBufferInputStream определены те же методы, что и в только что рассмотренном классе ByteArrayInputStream. Для более удобной работы вы, вероятно, создадите на базе потока класса StringBufferInputStream поток класса DataInputStream.
Класс StringTokenizer
Рассказывая о классе StreamTokenizer, нельзя не упомянуть о другом классе с похожим названием и назначением, а именно о классе StringTokenizer.
Определение этого класса достаточно компактно, поэтому мы приведем его полностью:
public class java.util.StringTokenizer
extends java.lang.Object
implements java.util.Enumeration
{
// ---------------------------------------------------
// Конструкторы класса
// ---------------------------------------------------
public StringTokenizer(String str);
public StringTokenizer(String str, String delim);
public StringTokenizer(String str, String delim,
boolean returnTokens);
// ---------------------------------------------------
// Методы
// ---------------------------------------------------
public String nextToken();
public String nextToken(String delim);
public int countTokens();
public boolean hasMoreElements();
public boolean hasMoreTokens();
public Object nextElement();
}
Класс StringTokenizer не имеет никакого отношения к потокам, так как предназначен для выделения отдельных элементов из строк типа String.
Конструкторы класса получают в качетсве первого параметра str ссылку на разбираемую строку. Второй параметр delim, если он есть, задает разделители, с испльзованием которых в строке будут выделяться элементы. Параметр returnTokens определяет, надо ли вовзвращать обнаруженные разделители как элементы разбираемой строки.
Рассмотрим кратко методы класса StringTokenizer.
Для разбора строки приложение должно организовать цикл, вызывая в нем метод nextToken. Условием завершения цикла может быть либо возникновение исключения NoSuchElementException, либо возврат значения false методами hasMoreElements или hasMoreTokens.
Метод countTokens позволяет определить, сколько раз был вызван метод nextToken перед возникновением исключения NoSuchElementException.
Класс URL в библиотеке классов Java
Для работы с ресурсами, заданными своими адресами URL, в библиотеке классов Java имеется очень удобный и мощный класс с названием URL. Простота создания сетевых приложений с использованием этого класса в значительной степени опровергает общераспространенное убеждение в сложности сетевого программирования. Инкапсулируя в себе достаточно сложные процедуры, класс URL предоставляет в распоряжение программиста небольшой набор простых в использовании конструкторов и методов.
Класс URLConnection
Напомним, что в классе URL, рассмотренном нами в начале этой главы, мы привели прототип метода openConnection, возвращающий для заданного объекта класса URL ссылку на объект URLConnection:
public URLConnection openConnection();
Что мы можем получить, имея ссылку на этот объект?
Прежде всего, пользуясь этой ссылкой, мы можем получить содержимое объекта, адресуемое соответствующим объектом URL, методом getContent:
public Object getContent();
Заметим, что метод с таким же названием есть и в классе URL. Поэтому если все, что вы хотите сделать, это получение содержимое файла, адресуемое объектом класса URL, то нет никакой необходимости обращаться к классу URLConnection.
Метод getInputStream позволяет открыть входной поток данных, с помощью которого можно считать файл или получить данные от расширения сервера Web:
public InputStream getInputStream();
В классе URLConnection определен также метод getOutputStream, позволяющий открыть выходной поток данных:
public OutputStream getOutputStream();
Не следует думать, что этот поток можно использовать для записи файлов в каталоги сервера Web. Однако для этого потока есть лучшее применение - с его помощью можно передать данные расширению сервера Web.
Рассмотрим еще несколько полезных методов, определенных в классе URLConnection.
Метод connect предназначен для установки соединения с объектом, на который ссылается объект класса URL:
public abstract void connect();
Перед установкой соединения приложение может установить различные параметры соединения. Некоторые из методов, предназначенных для этого, приведены ниже:
// Включение или отключение кэширования по умолчанию
public void
setDefaultUseCaches(boolean defaultusecaches);
// Включение или отключение кэширования
public void setUseCaches(boolean usecaches);
// Возможность использования потока для ввода
public void setDoInput(boolean doinput);
// Возможность использования потока для вывода
public void setDoOutput(boolean dooutput);
// Установка даты модификации документа
public void setIfModifiedSince(long ifmodifiedsince);
В классе URLConnection есть методы, позволяющие определить значения параметров, установленных только что описанными методами:
public boolean getDefaultUseCaches();
public boolean getUseCaches();
public boolean getDoInput();
public boolean getDoOutput();
public long getIfModifiedSince();
Определенный интерес могут представлять методы, предназначенные для извлечения информации из заголовка протокола HTTP:
// Метод возвращает содержимое заголовка content-encoding
// (кодировка ресурса, на который ссылается URL)
public String getContentEncoding();
// Метод возвращает содержимое заголовка content-length
// (размер документа)
public int getContentLength();
// Метод возвращает содержимое заголовка content-type
// (тип содержимого)
public String getContentType();
// Метод возвращает содержимое заголовка date
// (дата посылки ресурса в секундах с 1 января 1970 года)
public long getDate();
// Метод возвращает содержимое заголовка last-modified
// (дата изменения ресурса в секундах с 1 января 1970 года)
public long getLastModified();
// Метод возвращает содержимое заголовка expires
// (дата устаревания ресурса в секундах с
// 1 января 1970 года)
public long getExpiration();
Другие методы, определенные в классе URLConnection, позволяют получить все заголовки или заголовки с заданным номером, а также другую информацию о соединении. Мы не будем их рассматривать для экономии места в книге. При необходимости вы найдете описание этих методов в справочной системе Microsoft Visual J++.
Классы Java для работы с потоками
Программист, создающий автономное приложение Java, может работать с потоками нескольких типов:
стандартные потоки ввода и вывода;
потоки, связанные с локальными файлами;
потоки, связанные с файлами в оперативной памяти;
потоки, связанные с удаленными файлами
Рассмотрим кратко классы, связанные с потоками.
Конструктор класса DrawRectangles
В качестве параметра конструктору передается ссылка на класс аплета. Конструктор использует эту ссылку для получения и сохранения в полях класса контекста отображения и размеров окна аплета:
g = Appl.getGraphics();
dimAppWndDimension = Appl.size();
Для получения контекста отображения окна аплета, ссылка на который передается через единственный параметр, конструктор класса DrawRectangles вызывает метод getGraphics:
public DrawRectangles(Applet Appl)
{
g = Appl.getGraphics();
dimAppWndDimension = Appl.size();
}
Размеры окна аплета определяются с помощью метода size.
Конструктор класса SimpleDBMS
Конструктор класса SimpleDBMS выглядит достаточно просто. Все, что он делает, - это создает два объекта класса RandomAccessFile, соответственно, для индекса и данных:
idx = new RandomAccessFile(IndexFile, "rw");
dat = new RandomAccessFile(DataFile, "rw");
Так как в качестве второго параметра конструктору класа RandomAccessFile передается строка "rw", файлы открываются и для чтения, и для записи.
Конструктор класса StreamTokenizer
Для создание объектов класса StreamTokenizer предусмотрен всего один конструктор:
public StreamTokenizer(InputStream istream);
В качестве параметра этому конструктору необходимо передать ссылку на заранее созданный входной поток.
Конструкторы и методы класса Socket
После краткого введения в сокеты приведем описание наиболее интересных конструкторов и методов класса Socket.
Конструкторы класса Socket
Чаще всего для создания сокетов в клиентских приложениях вы будете использовать один из двух конструкторов, прототипы которых приведены ниже:
public Socket(String host, int port);
public Socket(InetAddress address, int port);
Первый из этих конструкторов позволяет указывать адрес серверного узла в виде текстовой строки, второй - в виде ссылки на объект класса InetAddress. Вторым параметром задается номер порта, с использованием которого будут передаваться данные.
В классе Socket определена еще одна пара конструкторов, которая, однако не рекомендуется для использования:
public Socket(String host, int port, boolean stream);
public Socket(InetAddress address, int port,
boolean stream);
В этих конструкторах последний параметр определяет тип сокета. Если этот параметр равен true, создается потоковый сокет, а если false - датаграммный. Заметим, однако, что для работы с датаграммными сокетами следует использовать класс DatagramSocket.
Конструкторы класса URL
Сначала о конструкторах. Их в классе URL имеется четыре штуки.
public URL(String spec);
Первый из них создает объект URL для сетевого ресурса, адрес URL которого передается конструктору в виде текстовой строки через единственный параметр spec:
public URL(String spec);
В процессе создания объекта проверяется заданный адрес URL, а также наличие указанного в нем ресурса. Если адрес указан неверно или заданный в нем ресурс отсутствует, возникает исключение MalformedURLException. Это же исключение возникает при попытке использовать протокол, с которым данная система не может работать.
Второй вариант конструктора класса URL допускает раздельное указание протокола, адреса узла, номера порта, а также имя файла:
public URL(String protocol, String host, int port,
String file);
Третий вариант предполагает использование номера порта, принятого по умолчанию:
public URL(String protocol, String host, String file);
Для протокола HTTP это порт с номером 80.
И, наконец, четвертый вариант конструктора допускает указание контекста адреса URL и строки адреса URL:
public URL(URL context, String spec);
Строка контекста позволяет указывать компоненты адреса URL, отсустсвующие в строке spec, такие как протокол, имя узла, файла или номер порта.
Литература
1. Фролов А.В., Фролов Г.В. Библиотека системного программиста. М.: ДИАЛОГ-МИФИ
Т.11 - 13. Операционная система Microsoft Windows 3.1 для программиста, 1994
Т.14. Графический интерфейс GDI в Microsoft Windows, 1994
Т.15. Мультимедиа для Windows, 1994
Т.22. Операционная система Windows 95 для программиста, 1996
Т.23. Глобальные сети компьютеров. Практическое введение в Internet, E-Mail, FTP, WWW и HTML, программирование для Windows Sockets
Т.29. Сервер Web своими руками. Язык HTML, приложения CGI и ISAPI, установка серверов Web для Windows, 1997
Т.30. Microsoft Visual J++. Создание приложений на языке Java. Часть 1, 1997
2. Д. Родли, Создание Java-апплетов: Пер. с англ., К: НИПФ “ДиаСофт Лтд.”, 1996
3. S. Davic, Learn Lava Now, Microsoft Press, One Microsoft Way, 1996
4. К. Джамса, Java: Пер. с англ., Мн.: ООО “Поппури”, 1996
5. Баженова И.Ю.,Язык программирования Java, М.: ДИАЛОГ-МИФИ, 1997
Метод action
Метод action получает управление, когда пользователь нажимает на одну из кнопок, расположенных в окне аплета. В зависимости от того, какая именно кнопка была нажата, выполняются различные действия.
Если пользователь нажал кнопку Play, вызывается метод play для запуска однократного проигрывания звукового файла:
auClip.play();
btStop.enable();
Сразу после того как проигрывание будет запущено, приложение разблокирует кнопку Stop, предоставляя пользователю возможность прервать звучание.
В том случае, когда пользователь нажал кнопку Loop, вызывается метод loop, запусчкающий проигрывание звукового файла в цикле:
auClip.loop();
fLoopPlay = true;
btStop.enable();
После запуска устанавливается флаг fLoopPlay и разблокируется кнопка Stop.
И, наконец, если пользователь нажимает кнопку Stop, выполняется остановка проигрывания методом stop:
auClip.stop();
fLoopPlay = false;
btStop.disable();
Флаг fLoopPlay сбрасывается, после чего кнопка Stop блокируется.
Метод action обрабатывает события, вызванные нажатием кнопок в окне аплета Inspector. Обработка заключается в вызове сооветствующего метода с использованием ссылки на аплет Audio. Например, если пользователь нажал кнопку Play, метод action вызывает метод play:
if(evt.target.equals(btPlay))
{
appAudio.auClip.play();
btStop.enable();
}
Обратите внимание, что здесь мы ссылаемся через поле appAudio на поле auClip, определенное в аплете Audio.
Метод AddRecord
Метод AddRecord добавляет новую запись в конец файла данных, а смещение этой записи - в конец файла индекса. Поэтому перед началом своей работы текущая позиция обоих указанных файлов устанавливается на конец файла.
Для установки мы применили метод seek из класса RandomAccessFile, передав ему в качестве параметра значение длины файла в байтах, определенное при помощи метода length из того же класса:
idx.seek(idx.length());
dat.seek(dat.length());
Перед тем как добавлять новую запись в файл данных, метод AddRecord определяет текущую позицию в файле данных (в данном случае это позиция конца файла) и записывает эту позицию в файл индекса:
idxFilePointer = dat.getFilePointer();
idx.writeLong(idxFilePointer);
Далее метод AddRecord выполняет сохранение полей записи в файле данных. Для записи строки вызывается метод writeBytes, а для записи численного значения типа int - метод writeInt:
dat.writeBytes(name + "\r\n");
dat.writeInt(account);
Обратите внимение, что к строке мы добавляем символы возврата каретки и перевода строки. Это сделано исключительно для того чтобы обозначить конец строки текстового поля.
Метод close
Метод close закрывает файлы индекса и данных, вызывая метод close из класса RandomAccessFile:
idx.close();
dat.close();
Метод destroy
При завершении работы аплета управление передается методу destroy. Мы его не используем.
Метод displayImage
Метод displayImage вызывается из двух мест - из метода paint при перерисовке окна аплета и из метода run (периодически).
Если кадры видеофильма не загружены, содержимое флага m_fAllLoaded равно false и метод displayImage просто возвращает управление, ничего не делая:
if(!m_fAllLoaded)
return;
Если же загрузка изображений завершена, этот метод рисует в центре окна текущий кадр видеофильма, вызывая для этого знакомый вам метод drawImage:
g.drawImage(m_Images[m_nCurrImage],
(size().width - m_nImgWidth) / 2,
(size().height - m_nImgHeight) / 2, null);
После того как кадр нарисован, мы надписываем на нем его порядковый номер, вызывая для этого метод drawString:
g.drawString((new Integer(m_nCurrImage)).toString(),
(size().width - m_nImgWidth) / 2,
((size().height - m_nImgHeight) / 2) + 10);
Метод equals
Вы можете использовать метод equals для определения идентичности адресов URL, заданных двумя объектами класса URL:
public boolean equals(Object obj);
Если адреса URL идентичны, метод equals возвращает значение true, если нет - значение false.
Метод getAppletInfo
Метод getAppletInfo возвращает информацию об аплете.
Метод getAppletInfo предоставляет другим заинтересованным аплетам (и себе, в частности) строку информации об аплете Inspector. Эта строка аналогична строкам, которые возвращают другие наши аплеты.
Метод getContent
Очень интересен метод getConten. Этот метод определяет и получает содержимое сетевого ресурса, для которого создан объект URL:
public final Object getContent();
Практически вы можете использовать метод getContent для получения текстовых файлов, расположенных в сетевых каталогах.
К сожалению, данный метод непригоден для получения документов HTML, так как для данного ресурса не определен обработчик соедржимого, предназначенный для создания объекта. Метод getContent не способен создать объект ни из чего другого, кроме текстового файла.
Данная проблема, тем не менее, решается очень просто - достаточно вместо метода getContent использовать описанную выше комбинацию методов openStream из класса URL и read из класса InputStream.