Главная » Java, Web » Работа с сетью. Потоки ввода и вывода

0

Сеть предоставляет возможность осуществлять ввод и вывод данных, взаимодействуя с удаленным компьютером. Работа с сетью несколько отличается от работы с файлами (работе с файлами уделено внимание ниже). В Java работа с сетью может быть осуществлена с использованием потоков ввода и вывода во многом таким же образом, как это происходит при работе с файлами. Открытие сетевого соединения между двумя компьютерами, тем не менее, усложняет задачу, так как эти компьютеры должны каким-то об­разом "договориться" между собой о том, как они будут общаться. Но преж­де чем мы перейдем к изучению принципов работы с сетью, остановимся на рассмотрении основных моментов работы с файлами в Java.

Потоки ввода и вывода

Без возможности взаимодействия с окружающим миром программа не представляла бы никакого интереса, она попросту стала бы бесполезной. Взаимодействие программы с миром осуществляется посредством ввода/вывода. Компьютер включает в себя множество различных устройств. И если бы программирование каждого устройства требовало к себе индивидуального подхода, то создание программ стало бы слишком сложным делом. Для работы с вводом/выводом используются абстракции. В языке Java с вводом и выводом используется абстракция потоков ввода/вывода. Мы не станем рассматривать в деталях строение потоков. При работе с вводом/выводом всегда следует понимать, что данные могут быть представлены как в машинном виде, так и в таком виде, который удобен и легко читаем человеком. Сформатированные в машинном виде данные представлены также, как и данные, которые хранятся в памяти машины, то есть в виде последовательностей двоичных состояний (последовательностей нулей и единиц). Для чтения человеком данные представляются в виде символов. Мы читаем число я как 3.141592654, которое представлено в виде набора символов-цифр. В Java существует две категории представления данных. Это байтовые потоки, данные в которых представлены в машинном виде, и символьные потоки, которые более удобны для чтения человеком. Существует множество заранее определенных классов, предназначенных для работы как с той, так и с другой категорией данных.

Каждый объект, который имеет возможность выводить данные в байтовый поток, принадлежит одному из подклассов абстрактного класса Outputstream. Объекты, которые читают данные байтового потока, относятся к подклассам класса input stream. Для чтения и записи обычных символов, которые может читать человек, используются классы Reader и writer. Все классы символьных потоков являются подклассами одного из этих классов. Если необходимо работать с данными, которые может читать человек, то используются символьные потоки, в противном случае используются байтовые потоки.

Стандартные классы для работы с потоками определены в пакете java.io, здесь же определен ряд вспомогательных классов. Перед работой с потоками следует импортировать этот пакет в программу при помощи инструкции

import java.io.*;

Эта инструкция должна быть помещена в самом начале файла, содержащего код программы. При работе с графическим интерфейсом пользователя потоки не используются, там работает свой собственный механизм ввода/вывода. Потоки необходимы при работе с файлами. Сетью потоки могут быть использованы для установления взаимодействия между параллельно выполняемыми подпроцессами (которые мы также называем потоками, но это не потоки ввода/вывода, а программные потоки). Достоинство потоков в том и состоит, что с их помощью можно легко записывать данные в файл или пересылать данные по сети, выводить данные на экран.

Базовые классы ввода/вывода, Reader, Writer, Inputstream, Outputstream предоставляют только основные операции ввода/вывода. Так, класс Inputstream объявляет мсго.I

public int read() throws IOException

при помощи которого происходит чтение одного байта данных (0—255) потока ввода. Если достигнут конец потока ввода, то метод read () возвращает значение —1. Класс inputstream определяет методы, которые позволяют читать несколько байт в один прием, помещая прочитанные байты в байтовый массив. Вывод может быть осуществлен при помощи метода write ():

public void write(int b) throws IOException

Классы Reader и writer предоставляют весьма схожие методы. Но здесь чтение и запись происходит с символьными данными. Так, подкласс Printwriter класса writer предоставляет удобные методы для вывода читаемых символов всех примитивных типов, используемых в Java. Если мы располагаем объектом, принадлежащим классу writer или его подклассу, и хотим использовать метод Printwriter для вывода данных, то для этого

необходимо объект типа writer (в примере ниже это объект charsink) передать конструктору PrintWriter:

PrintWriter printableCharSink = new PrintWriter(charSink);

Перечислим методы, используемые в классе PrintWriter для осуществления вывода:

public void print(String s) // вывод стандартных типов public void print(char с) //в читаемой форме

public void print(int i) public void print(long 1) public void print(float f) public void print(double d) public void print(boolean b)

public void println()           // возврат каретки

public void println(String s) //то же, что и выше, public void println(char с)  //но после вывода символа

public void println(int i)      // выводится возврат каретки

public void println(long 1) public void println(float f) public void println(double d public void println(boolean b)

Эти методы не реагируют на ошибку ioException. Для определения ошибки используется метод

public boolean checkError()

Метод возвращает true, если произошла ошибка. В дальнейшем мы будем

ссылаться на класс TextReader. Этот класс описан в файле

TextReader. java. В этом классе содержатся полезные методы (листинг 2.1).

Листинг 2.1. Файл TextReader.java

Класс TextReader — подкласс класса FilterReader.

Класс TextReader содержит методы чтения данных в удобочитаемом формате в виде символов ASCII.

В файле определено три подкласса public от класса RuntimeException для работы с ошибками, которые могут произойти во время ввода. Эти классы являются статическими вложенными классами для класса TextReader.

import java.io.*;

public class TextReader extends FilterReader {

// Задание исключений, с которыми работает класс, исключения — подклассы // класса RuntimeException. Обработка исключений необязательна, public static class Error extends RuntimeException {

// любое исключение, возникающее в классе TextReader Error(String errorMessage) { super(errorMessage) ;

}

}

public static class FormatError extends Error {

// неверные данные: неверное число или неверное логическое значение FormatError(String errorMessage) { super(errorMessage);

}

}

public static class EOFError extends Error { // попытка чтения EOFError(String errorMessage) { super(errorMessage);

}

}

// конструкторы

public TextReader(BufferedReader s) { super(s);

}

public TextReader(Reader s) { super(new BufferedReader(s)) ;

}

public TextReader(InputStream s) {

super(new BufferedReader(new InputStreamReader(s)));

}

// проверка ошибок

public void IOCheck(boolean throwExceptions) { // вызов IOCheck(false) // для выключения ошибок throwExceptionOnError = throwExceptions;

}

//и проверка с вызовом checkError() public boolean error() {

11 возвращает true, если последняя операция ввода //в TextReader привела к ошибке return errorFlag;

}

// сообщение об ошибке можно получить от getErrorMessage() public String getErrorMessage() {

return errorFlag ? errorMessage : null;

}

// методы ввода

// peek() – следующий символ для ввода из потока.

// Символ не убирается из потока, если следующий символ — конец строки,

// то возвращается ‘\п’.

// ‘\0′ соответствует концу файла.

//Не существует способа различить EOF и null.

// Для обнаружения конца файла используется eof().

// getAnyChar() — читает и возвращает следующий символ, в том числе // символ пробела. Было бы ошибкой читать символы после конца файла. // Отметим, что getChar() пропускает пробелы перед тем, как прочитает // символы.

// getChar(), getByte(), и прочие — пропускают пробелы и читают значения // соответствующего типа. Если данные указанного типа не будут найдены, // то возникает ошибка. "word" — последовательность символов после // пробела, "boolean" — одно из слов "true", "false", "yes", "no", // "t", "f", "у", "n", "0", "1". "Alpha" — последовательность букв. // getAlpha() — пропускает все небуквенные символы перед началом чтения. // getIn() — читает и выводит все символы до конца строки, записывая их //в String. Символ конца строки читается, но игнорируется. // getlnChar(), getlnByte()— работает так же, как getChar(), // с тем исключением, что после чтения значений все символы в // строке, включая символ конца строки, читаются, но затем игнорируются. // eoln() — читает и игнорирует все пробелы и табуляции. Проверяет, // является ли следующий символ символом конца строки. Конец файла также // считается концом строки. Если необходимо проверить на наличие конца // строки не пренебрегая пробелами и табуляциями, то следует использовать // еще метод peek() == ‘\n’ or ‘\0′.

// eof() — Читает и игнорирует все пробелы, концы строк и табуляции, // затем проверяет, является ли следующий символ символом конца файла. // Чтобы не игнорировать пробелы, концы строк, табуляции, используйте // peek() == ‘\0′.

11 skipWhiteSpace() — читает и игнорирует пробелы, табуляции и концы // строк. Прекращает работу при нахождении другого символа или конца // файла.

// skipNonLetters() — читает и игнорирует все небуквенные символы, // прекращает работу по достижении буквенного символа или символа конца // файла.

public char peek() {

errorFlag = false; return lookChar();

}

public char getAnyChar() {

errorFlag = false; return readChar();

}

public char getChar() {

errorFlag = false; skipWhiteSpace (); return readChar();

}

public byte getByteO { errorFlag = falser-

return (byte)readlnteger(-128L,127L);

}

public short getShort() { errorFlag = falser-

return (short)readlnteger(-32768L,32767L);

}

public int getInt() { errorFlag = falser-

return (int)readlnteger((long)Integer.MIN_VALUE, (long)Integer.MAX_VALUE);

}

public long getLongO { errorFlag = falser-

return readlnteger(Long.MIN_VALUE, Long.MAX_VALUE);

}

public float getFloat() {

errorFlag = false; return readFloat();

}

public double getDouble() {

errorFlag = false; return readDouble();

public boolean getBoolean() {

errorFlag = false; return readBoolean();

}

public String getWord() {

errorFlag = false; return readWordO;

}

public String getAlpha() {

errorFlag = false; return readAlpha();

}

public String getIn() {

errorFlag = false; return readLine();

}

public char getlnChar() { char x=getChar(); dropLine() ; return x;

}

public byte getlnByte() { byte x=getByte() ; dropLine(); return x;

}

public short getlnShort() { short x=getShort(); dropLine(); return x;

}

public int getlnlnt() { int x=getlnt(); dropLine(); return x;

}

public long getlnLong() { long x=getLong() ; dropLine(); return x;

}

public float getlnFloat() { float x=getFloat();

dropLine (); return х;

}

public double getlnDouble() { double x=getDouble(); dropLine(); return x;

}

public boolean getlnBoolean() { boolean x=getBoolean(); dropLine(); return x;

}

public String getlnWord() { String x=getWord(); dropLine(); return x;

}

public String getlnAlpha() { String x=getAlpha() ; dropLine(); return x;

}

public boolean eoln() { char ch = lookChar();

while (!EOF && ierrorFlag && (ch == ‘ ‘ || ch == ‘\t’)) { readChar(); ch = lookChar() ;

}

return (ch == ‘\n’ || EOF);

}

public boolean eof() { char ch = lookChar();

while (!EOF && ierrorFlag && (ch == ‘ ‘ ||

ch = ‘\n’ || ch = ‘\t’)) {

readChar(); ch = lookChar();

}

return EOF;

}

public void skipWhiteSpace() { char ch = lookChar();

while (ierrorFlag && (ch == ‘ ‘ || ch == ‘\n’ || ch == ‘\t’)) { readChar(); ch = lookChar();

}

}

public void skipNonLetters() { char ch = lookChar();

while (ierrorFlag && !EOF && [Character.isLetter(ch)) { readChar(); ch = lookChar() ;

}

}

public void close() { e rro rFlag = false; try {

in.close ();

}

catch (IOException e) { e rro rFlag = true; errorMessage = e.toStringO;

}

}

private int lookAhead = -1; // буфер для одного символа;

// -1 — буфер пуст private boolean errorFlag = false; // true — если последняя

// операция была с ошибкой private String errorMessage = "";   // сообщение об ошибке для

// последней ошибки private boolean EOF = false;       // обнаружен ли конец потока?

private boolean throwExceptionOnError = true; // определяет тип

// обработки ошибки

// функции обработки ошибок private void doError(String message) { errorFlag = true; errorMessage = message;

if (throwExcept ionOnError) throw new Error(message);

}

private void doEormatError(String message) { errorFlag = true; errorMessage = message; if (throwExcept ionOnError)

throw new FormatError(message);

}

private void doEOFError(String message) { errorFlag = true; errorMessage = message; if (throwExcept ionOnError)

throw new FormatError(message);

}

// методы чтения основных типов данных из потока private char readChar() { char ch = lookChar(); if (EOF)

doEOFError("Attempt to read past end-of-data in input stream."); lookAhead = -1; return ch;

}

private boolean possibleLineFeedPending = false; // для работы

// с символами \r\n

private char lookChar() { if (lookAhead != -1) { if (lookAhead = ‘\r’)

return ‘\n'; else

return (char)lookAhead;

}

if (EOF)

return ‘\0′; try {

int n = in.read();

if (n == ‘\n’ && possibleLineFeedPending) { // игнорировать

//в сочетании \n of \r\n

n = in.read();

}

possibleLineFeedPending = (п == ‘\г’); lookAhead = п; if (lookAhead = -1) { EOF = true; return ‘\0′;

}

}

catch (IOException e) { doError(e.getMessage());

}

if (lookAhead = ‘\r’) // все eoln в виде \n

lookAhead = ‘\n'; return (char)lookAhead;

}

private void dropLine() { while (ierrorFlag) {

if (lookChar() == ‘\0′)

return; if (readChar() == ‘\n’) return;

}

}

private String readLineO {

StringBuffer s = new StringBuffer(100); char ch = readChar() ; while (ierrorFlag && ch != ‘\n’) { s.append(ch); ch = lookChar(); if (ch == ‘\0′)

break; ch = readChar() ;

}

return s.toString();

}

private String readWordO { skipWhiteSpace() ; if (errorFlag) return null;

StringBuffer s = new StringBuffer(50); char ch = lookChar(); if (EOF) {

doEOFError("Attempt to read past end-of-data"); return null;

}

while (!errorFlag && !EOF && ch != ‘\n’ && ch != ‘ ‘ && ch != ‘\t’) { s.append(readChar()) ; ch = lookChar() ;

}

return s.toString();

}

private String readAlphaO { skipNonLetters(); if (errorFlag) return null; StringBuffer s = new StringBuffer(50); char ch = lookChar() ; if (EOF) {

doEOFError("Attempt to read past end-of-data"); return null;

}

while (!errorFlag && Character.isLetter(ch)) { s.append(readChar()) ; ch = lookChar() ;

}

return s.toString();

}

public float readFloat() { double d = readDouble(); if (errorFlag)

return Float.NaN; if (Math.abs(d) > Float.MAX_VALUE)

doFormatError("Input number outside of legal range for values of type float"); return (float)d;

}

public double readDouble() { double x = Double.NaN;

StringBuffer s=new StringBuffer(50);

skipWhiteSpace();

char ch = lookChar();

if (ch ==         || ch == ‘+’) {

s.append(readChar()) ; skipWhiteSpace(); ch = lookChar();

}

if ((ch < ‘0’ || ch > ‘9’) && (ch != ‘.’)) { if (EOF)

doEOFError("Expecting a floating-point number and found end-of- data"); else

doFormatError("Expecting a floating-point number and found V" + ch +    ;

return Double.NaN;

}

boolean digits = falser-

while (ch >= ‘0’ && ch <= ‘9’) { s.append(readChar()); ch = lookChar(); digits = true;

}

if (ch == ‘.’) {

s.append(readChar()); ch = lookChar();

while (ch >= ‘0’ && ch <= ‘9’) { s.append(readChar()) ; ch = lookChar(); digits = true;

}

if (!digits) {

doFormatError("No digits found in floating-point input."); return Double.NaN;

}

if (ch == ‘E’ || ch == ‘e’) { s.append(readChar()); ch = lookChar(); if (ch == || ch == ‘+’) {

s.append(readChar()); ch = lookChar();

}

if ((ch < ‘0’ || ch > ‘9’) && (ch != ‘ . ‘) ) { if (EOF)

doEOFError("Expecting exponent for a floating-point number and found end-of-data"); else

doFormatError("Expecting exponent for a floating-point number and found V" + ch + return Double.NaN;

}

while (ch >= ‘0’ && ch <= ‘9’) { s.append(readChar()); ch = lookChar(); } String str = s.toString(); Double d; try {

d = new Double(str); x = d.doubleValue ();

}

catch (NumberFormatException e) { x = Double.NaN;

}

if (Double.isNaN(x) || Double.islnfinite(x)) { doFormatError("Illegal floating point number"); return Double.NaN;

}

return x;

}

public boolean readBoolean() { boolean ans = falser- String s = getWordO; if (errorFlag) return false;

if (s.equalsIgnoreCase("true") || s.equalsIgnoreCase("t") || s.equalsIgnoreCase("yes") || s.equalsIgnoreCase("у") II s.equals("1")) { ans = true;

}

else if (s.equalsIgnoreCase("false") || s.equalsIgnoreCase("f") II s.equalsIgnoreCase("no") || s.equalsIgnoreCase("n") || s.equals ("0")) { ans = false;

}

else

doFormatError("Illegal input for value of type boolean: \"" + s + return ans;

}

private long readlnteger(long min, long max) { // чтение большого целого, оговорен диапазон skipWhiteSpace(); if (errorFlag)

return 0; char sign = ‘ + ‘;

if (lookChar() ==           || lookChar() = ‘ + ‘) {

sign = getChar(); skipWhiteSpace() ;

}

long n = 0; char ch = lookChar(); if (ch < ‘0’ || ch > ‘9’) { if (EOF)

doEOFError("Expecting an integer and found end-of-data"); else

doFormatError("Expecting an integer and found                   +

ch +                               ;

return 0;

}

while (!errorFlag && ch >= ‘0’ && ch <= ‘9’) { readChar();

n = 10*n + (int)ch – (int)’0′; if (n < 0) {

doFormatError("Integer value outside of legal range"); return 0;

ch = lookChar();

}

if (sign == 1 -1)

n = -n; if (n < min || n > max) {

doFormatError("Integer value outside of legal range"); return 0;

}

return n;

}

// переопределение метода read() из FilterReader public int read() throws IOException { if (lookAhead = -1)

return super.read(); else {

int x = lookAhead; lookAhead = -1; return x;

}

}

public int read (char[] buffer, int offset, int count) throws IOException { if (lookAhead = || count <= 0)

return super.read(buffer,offset,count); else {

if (count == 1) {

buffer[offset] = (char)lookAhead; lookAhead = -1; return 1;

}

else {

buffer[offset] = (char)lookAhead; lookAhead = -1;

int ct = super.read(buffer,offset+1,count-1); return ct + 1;

Классы Printwriter, TextReader, DatalnputStream, DataOutputStream ПОЗВОЛЯЮТ легко работать с примитивными типами. Но что делать с объектами? По традиции используются те или иные способы представления объектов в виде наборов обычных примитивных типов, которые могут быть выведены как последовательность символов или байтов. Такой подход называется сериализацией объектов. При чтении сериализованного объекта необходимо его восстановить. Вся работа выполняется с помощью классов Objectlnputstream и ObjectOutputStream. Эти классы являются подклассаМИ классов Inputstream И Outputstream, С ИХ ПОМОЩЬЮ осуществляется запись и чтение сериализованных объектов. Классы obj ectlnputstream и obj ectOutputStream могут быть использованы с любыми потоками inputstreams и Outputstreams. Это позволяет записывать и читать объекты в любых байтовых потоках. Используемые при чтении и записи объектов методы — это методы readObjectO (в классе Obj ectlnputstream) И writeObject (Object obj ) (в класс Obj ectOutputStream). Оба метода могут вызывать исключение ioExceptions. Метод readObjecto возвращает значение типа object, которое, как правило, должно быть приведено к более приемлемому для использования и работы типу.

Классы Obj ectlnputstream И Obj ectOutputStream МОЖНО использовать только при работе с объектами, которые имплементируют интерфейс Seriaiizabie. Более того, все переменные экземпляра класса, к которому относится данный объект, также должны быть сериализуемы. Интерфейс Seriaiizabie не содержит других методов. Интерфейс как бы сообщает компилятору о том, что все члены класса, имплементирующего интерфейс seriaiizabie, могут быть как записаны, так и прочитаны с применением потоков. Что требуется сделать разработчику? Всего лишь вставить слова implements Seriaiizabie в описание класса! Многие стандартные классы Java уже объявлены как seriaiizabie, в том числе все компоненты Swing и AWT. Это, в частности, означает, что все компоненты графического интерфейса пользователя могут быть записаны в поток objectoutputstreams и прочитаны из потока ObjectlnputStreams.

Источник: Будилов В. А. Интернет-программирование на Java. — СПб.: БХВ-Петербург, 2003. — 704 е.: ил.

По теме:

  • Комментарии