Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
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); } } }