Download Streams

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
CISC 4700 L01
Network & Client-Server
Programming
Spring 2016
Harold, Chapter 2:
Streams
To understand network I/O, you must understand I/O.
Java I/O: stream oriented.
all output streams have same basic methods to write data;
all input streams have same basic methods to read data.
Filter streams: can be chained to I/O streams.
modify the data (e.g., encryption, compression)
Readers and writers: can be chained to input and output streams
allow programs to read/write chars, rather than bytes
can handle (e.g.) multibyte charsets
Default: streams are synchronous.
Can also do non-blocking (asynchronous) I/O.
Chapter 2: Streams
To understand network I/O, you must understand I/O.
Java I/O: stream oriented.
all output streams have same basic methods to write data;
all input streams have same basic methods to read data.
Filter streams: can be chained to I/O streams.
modify the data (e.g., encryption, compression)
Readers and writers: can be chained to input and output streams
allow programs to read/write chars, rather than bytes
can handle (e.g.) multibyte charsets
Default: streams are synchronous.
Can also do non-blocking (asynchronous) I/O.
Output Streams
java.io.OutputStream: basic (abstract) class
provides the folloiwng methods:
public abstract void(write int b) throws IOException
public void write(byte [] data) throws IOException
public void write(byte [] data, int offset, int length)
throws IOException
public void flush() throws IOException
public void close() throws IOException
Use subclasses of same for particular purposes, e.g.,
FileOutputStream, ByteArrayOutputStream.
The write(int b) method is fundamental.
It's abstract because it depends on kind of OutputStream.
Example: The chargen service (port 19) generates characters on the
remote host (if enabled in /etc/inetd.conf or /etc/xinetd.d):
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefgh
"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghi
#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghij
$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijk
%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijkl
&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklm
etc.
Here's a function that generates such a character stream.
It puts together each line of output, and then writes the line, for
the sake of efficiency:
import java.io.*;
public class CharGen {
public static void generateCharacters(OutputStream out)
throws IOException {
int firstPrintableChar = (int)'!';
int lastPrintableChar = (int)'~';
int numPrintableChars = lastPrintableChar - firstPrintableChar + 1;
int numCharsPerLine
= 72;
int start = firstPrintableChar;
byte[] line = new byte[numCharsPerLine + 2];
line[numCharsPerLine]
= (byte)'\r';
line[numCharsPerLine + 1] = (byte)'\n';
while (true) {
for (int i = start; i < start + numCharsPerLine; i++) {
line[i-start] = (byte)
((i - firstPrintableChar) % numPrintableChars
+ firstPrintableChar);
}
out.write(line);
start = ((start + 1) - firstPrintableChar) % numPrintableChars
+ firstPrintableChar;
}
}
}
The following works to demonstrate same:
public static void main(String arg[]) {
try {
CharGen.generateCharacters(System.out);
}
catch (IOException ex) {
System.err.println("exception thrown in generateCharacters");
System.exit(1);
}
}
Note that we don't write a byte at a time; too inefficient.
Note that the int result of generating the next char needs a (byte) cast.
flush()-ing output streams: I/O is often buffered for efficiency.
Consider output using a BufferedOutputStream or BufferedWriter. The
main idea is:
we write to a buffer
when buffer is filled, it gets shipped out
Suppose server and client are communicating back and forth.
server: sends (small) msg to client, waits for response
BufferedOutputStream: msg isn't big enough to send, waits for more
data from server
client: where's the msg from the server?
Cure: use the flush() method. Server does:
while (not done generating msg) {
generate next byte of msg, stores in buffer
}
flush the output stream
and all is well.
Generally, you should always flush (even if perhaps unnecessary).
It may be unclear whether a particular output stream is buffered.
When you're done with a stream, you should close() it.
Input Streams
java.io.InputStream: basic (abstract) class
Methods:
public abstract int read() throws IOException
public int read(byte[] input) throws IOException
public int read(byte[] input, int offset, int length)
throws IOException
public long skip(long n) throws IOException
public int available() throws IOException
public void close() throws IOException
Use subclasses for particular purposes, e.g.,
FileInputStream
ByteArrayInputStream.
The int read() method is fundamental.
It's abstract because it depends on kind of InputStream.
Example: read 10 bytes from the InputStream in
(terminate at end of stream if earlier)
byte[] input = new byte[10];
for (int i = 0; i < input.length; i++) {
int b = in.read();
if (b == -1) break;
input[i] = (byte) b;
}
More efficient:
byte[] input = new byte[10];
int bytesRead;
bytesRead = in.read(input);
We could then print the buffer doing:
System.out.write(input);
System.out.flush(); // play it safe
Example: Suppose
byte input[] = new byte[1024];
int bytesRead = in.read(input);
and we find that bytesRead is 512. The remaining bytes may be in transit.
May have to wait for them:
int bytesRead = 0;
int bytesToRead = 1024;
byte[] input = new byte[bytesToRead];
while (bytesRead < bytesToRead) {
int result = in.read(input, bytesRead,
bytesToRead - bytesRead);
if (result == -1) break; // end of stream
bytesRead += result;
}
available(): how many bytes are available now
The following is OK:
int bytesAvaialable = in.available();
byte[] input = new byte[bytesAvailable];
int bytesRead = in.read(input);
Note that bytesRead == bytesAvailable.
(It is possible that we could've read more bytes, since they may have
become available.)
skip(): skip over bytes without reading them
mainly used with sequential files
use seek() instead with random access files
not so useful with network I/O
stream is sequential, but no speed gained by skipping bytes
close(): when done with input stream
Marking and Resetting: allows backing up and rereading previously-read data.
Not so commonly used. The only input streams supporting same aer
BufferedInputStream and ByteArrayInputStream.
Filter Streams
For input: allow you to do something with the raw bytes that have been read
For output: allow you to output things other than raw bytes
Typically used in a "chain":
FileInputStream
fin = new FileInputStream("data.txt");
BufferedInputStream bin = new BufferedInputStream(fin);
Now can use fin.read() or bin.read(), but don't intermix them.
Alternatively, can do:
BufferedInputStream bin = new BufferedInputStream(
new FileInputStream("data.txt")
);
Buffered Streams
BufferedOutputStream: has a protected buffer named buf. It stores
written data in buf until buf is full or flushed. Then it writes the
data. More efficient than writing single bytes. Especially so for
network connections (think of overhead associated with packet header
info!).
ctors:
public BufferedOutputStream(OutputStream out)
public BufferedOutputStream(OutputStream out, int bufferSize)
methods: same as for OutputStream
BufferedInputStream: also has a protected buffer named buf. When
read() method is called, it tries to get the bytes from buf. If buf
doesn't have enough bytes, then the stream is read into buf.
ctors:
public BufferedInputStream(InputStream in)
public BufferedInputStream(InputStream in, int bufferSize)
methods: same as for InputStream.
Example: A simple "cp" program, used via
$ java Copy foo bar
import java.io.*;
public class Copy {
public static void main(String[] args) {
if (args.length != 2) {
System.err.println("Usage: java Copy source dest");
System.exit(1);
}
try {
FileInputStream fis = new FileInputStream(args[0]);
FileOutputStream fos = new FileOutputStream(args[1]);
BufferedInputStream in = new BufferedInputStream(fis);
BufferedOutputStream out = new BufferedOutputStream(fos);
int c;
while ((c = in.read()) != -1)
out.write(c);
in.close();
out.close();
}
catch (IOException ex) {
System.err.println(ex.getMessage());
System.exit(2);
}
}
}
PrintStream
A filter output stream; System.out is a PrintStream.
ctors:
public PrintStream(OutputStream out)
public PrintStream(OutputStream out, booean autoFlush)
autoFlush: stream is flushed if ...
byte array written
linefeed written
println() called
Has 9 overloaded print() methods, 10 overloaded println() methods:
public void print(int i)
public void println(int i)
etc.
These convert their argument to string and write onto the underlying
output stream. println() is like print(), but then prints a line
separator character (\n for Unix, \r for Mac, \r\n for Windows).
Author now goes on a tirade against PrintStream ...
(1) Output is platform-dependent ... bad in a networked environment
(2) Assumes default encoding of platform on which it's running ... if this
is (e.g.) a server, then said choice may not be what client expects
(3) PrintStream eats all exceptions ... bad in a network environment
Data Streams
DataInputStream, DataOutputStream provide methods for I/O of
primitive datatypes.
For example: DataInputStream has
public final int readInt(int i) throws IOException
and DataOutputStream has
public final void writeInt(int i) throws IOException
DataOutputStream:
Data is written in big-endian format (MSB first).
Integers: 2's complement form, using minimal possible number of bytes.
Floats, doubles: IEEE 754 form (4 bytes, 8 bytes)
Booleans: single byte (0, 1 for false, true)
DataInputStream: analogous to DataOutputStream.
There are a few additional bells and whistles.
Author suggests avoiding the readLine() method:
(1) Buggy implementation.
(2) Deprecated; the readLine() in BufferedReader is preferable.
However, both readLine() methods recognize only \n or \r\n as end-of-line.
If \r is last byte in a stream, it gets thrown away
even though it's possible (likely?) that next byte will be \n.
This is a big problem for network I/O.
(Also, \r is the Mac end-of-line!)
Example: Suppose you would like to create a file (say, invoice1.txt),
containing lines having the following form:
unit_price TAB number_of_units TAB description
e.g.:
19.99
12
Java T-shirt
9.99
8
Java Mug
The following program creates such a file:
import java.io.*;
public class DataOutTest {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("invoice1.txt");
DataOutputStream out = new DataOutputStream(fos);
double[] prices = { 19.99, 9.99, 15.99, 3.99, 4.99 };
int[] units = { 12, 8, 13, 29, 50 };
String[] descs = { "Java T-shirt",
"Java Mug",
"Duke Juggling Dolls",
"Java Pin",
"Java Key Chain" };
for (int i = 0; i < prices.length; i++) {
out.writeDouble(prices[i]);
out.writeChar('\t');
out.writeInt(units[i]);
out.writeChar('\t');
out.writeChars(descs[i]);
out.writeChar('\n');
}
out.close();
}
}
We now need a program that processes the file, producing the following output:
You've ordered 12 units of Java T-shirt at $19.99
You've ordered 8 units of Java Mug at $9.99
You've ordered 13 units of Duke Juggling Dolls at $15.99
You've ordered 29 units of Java Pin at $3.99
You've ordered 50 units of Java Key Chain at $4.99
For a TOTAL of: $892.88
Here's the program:
import java.io.*;
import java.text.*;
public class DataInTest {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("invoice1.txt");
DataInputStream in = new DataInputStream(fis);
DecimalFormat form = new DecimalFormat("#####.##");
double price;
int unit;
String desc;
double total = 0.0;
try {
while (true) {
price = in.readDouble();
in.readChar();
// throws out the tab
unit = in.readInt();
in.readChar();
// throws out the tab
desc = in.readLine();
System.out.println("You've ordered " + unit + " units of " +
desc + " at $" + form.format(price));
total += unit * price;
}
} catch (EOFException e) {
}
System.out.println("For a TOTAL of: $" + form.format(total));
in.close();
}
}
Note: The readInt(), writeInt() methods do not read and write String
representations of ints.
Writing a String representation: use print() and println() methods.
Reading same: use Scanner class
Readers and Writers
Unwarranted asumption: text = ASCII
With i18n, need other charsets (e.g., Chinese web pages).
Java native charset: 2-byte Unicode
byte text is to InputStream (resp, OutputStream) as char text is to
Reader (resp, Writer).
java.io provides
Reader abstract class, with concrete subclasses
FileReader
StringReader
CharArrayReader
Writer abstract class, with concrete subclasses
FileWriter
StringWriter
CharArrayWriter
Writers
Writer abstract class has
2 ctors
and
5 write methods
a flush method
a close method
For example, if w is a Writer object, then
w.write("foo");
w.write("blivit", 0, 4);
and so on. Exactly what byte sequence is written depends on the encoding of w;
see text for discussion. A list of valid encodings may be found at
http://docs.oracle.com/javase/7/docs/technotes/guides/intl/encoding.doc.html
OutputStreamWriter: most important concrete subclass of Writer.
Typical use:
String fileName = "foo.txt";
String encoding = "Cp856";
FileOutputStream fos = new FileOutputStream(fileName, encoding);
OutputStreamWriter w = new OutputSreamWriter(fos);
String hebrewString = ".....";
w.write(hebrewString);
Readers
Reader abstract class has
2 ctors
and
2 read methods
several utility methods:
skip(), ready(), markSupported(), mark(), reset(), close()
The simple read() method returns a single Unicode char as an int
(0 to 65535, or -1 for end of stream). The other methods are similiar
to their InputStream cousins, except that ready() replaces
available().
InputStreamReader: most important concrete subclass of reader.
Typical use:
public static String getIBMHebrewString(InputStream in)
throws IOException {
InputStreamReader r = newInputStreamReader(in, "Cp856");
StringBuffer sb = new StringBuffer();
int c;
while ((c = r.read()) != -1)
sb.append((char) c);
r.close();
return sb.toString();
}
Filter Readers and Writers
These include
BufferedReader BufferedWriter LineNumberReader
PushbackReader PrintWriter
Buffered readers and writers
analogous to BufferedInputStream and BufferedOutputStream
Example: More efficient version of previous example
public static String getIBMHebrewString(InputStream in)
throws IOException {
InputStreamReader r = new InputStreamReader
(in, "Cp856");
r = new BufferedReader(r, 1024);
StringBuffer sb = new StringBuffer();
int c;
while ((c = r.read()) != -1)
sb.append((char) c);
r.close();
return sb.toString();
}
BufferedReader has a readLine() method.
Somewhat better than that from DataInputStream, but still chokes
on lines ending with \r. Text has a SafeBufferedReader class,
which avoids this problem.
BufferedWriter has a newLine() method, used to add a newline
to the output stream. The newline inserted is platform-dependent,
so this shouldn't be used in network protocols; explicitly use the
line
terminator the protocol requires.
Example: A simple "cp" program, done with Readers and Writers.
Use:
$ java Copy foo bar
import java.io.*;
public class Copy {
public static void main(String[] args) {
if (args.length != 2) {
System.err.println("Usage: java Copy source dest");
System.exit(1);
}
try {
FileReader fin = new FileReader(args[0]);
FileWriter fout = new FileWriter(args[1]);
BufferedReader in = new BufferedReader(fin);
BufferedWriter out = new BufferedWriter(fout);
int c;
while ((c = in.read()) != -1)
out.write(c);
in.close();
out.close();
}
catch (IOException ex) {
System.err.println(ex.getMessage());
System.exit(2);
}
}
}