- 浏览: 984731 次
文章分类
- 全部博客 (428)
- Hadoop (2)
- HBase (1)
- ELK (1)
- ActiveMQ (13)
- Kafka (5)
- Redis (14)
- Dubbo (1)
- Memcached (5)
- Netty (56)
- Mina (34)
- NIO (51)
- JUC (53)
- Spring (13)
- Mybatis (17)
- MySQL (21)
- JDBC (12)
- C3P0 (5)
- Tomcat (13)
- SLF4J-log4j (9)
- P6Spy (4)
- Quartz (12)
- Zabbix (7)
- JAVA (9)
- Linux (15)
- HTML (9)
- Lucene (0)
- JS (2)
- WebService (1)
- Maven (4)
- Oracle&MSSQL (14)
- iText (11)
- Development Tools (8)
- UTILS (4)
- LIFE (8)
最新评论
-
Donald_Draper:
Donald_Draper 写道刘落落cici 写道能给我发一 ...
DatagramChannelImpl 解析三(多播) -
Donald_Draper:
刘落落cici 写道能给我发一份这个类的源码吗Datagram ...
DatagramChannelImpl 解析三(多播) -
lyfyouyun:
请问楼主,执行消息发送的时候,报错:Transport sch ...
ActiveMQ连接工厂、连接详解 -
ezlhq:
关于 PollArrayWrapper 状态含义猜测:参考 S ...
WindowsSelectorImpl解析一(FdMap,PollArrayWrapper) -
flyfeifei66:
打算使用xmemcache作为memcache的客户端,由于x ...
Memcached分布式客户端(Xmemcached)
Java Socket编程实例:http://donald-draper.iteye.com/blog/2356695
在上一篇Java Socket编程实例,我们实战Java Socket编程中,用到
BufferedInput/OutputStream去包装Input/OutputStream读写socket的缓冲区,这种是通过
FilterInput/OutputStream方式;今天我们来看一下在HttpServletResponse中用的比较多的方式
PrintWriter/BufferedReader,即Writer/Reader方式。
服务器:
客户端:
服务器端控制台输出:
服务器启动......
收到客户端信息:Hello Server!
客户端控制台输出:
连接服务器成功......
收到服务端信息:Welcome Client!
控制台的输出,不是我们今天所要探讨的,我们要关心的是下面这段代码
这个分两部分来看1.PrintWriter,写缓冲区,2.BufferedReader,读缓冲区
第一部分:PrintWriter,写缓冲区
从下面这句话开始
构造PrintWriter
根据OutputStream构造OutputStreamWriter,再包装成BufferedWriter
根据Writer(BufferedWriter)和autoFlush构造PrintWriter
//Writer
回到PrintWriter(OutputStream out, boolean autoFlush)方法的这一句
先来看OutputStreamWriter
//OutputStreamWriter
//StreamEncoder
再回到BufferedWriter的构造
//BufferedWriter
这里我们小节一下PrintWriter:
构造PrintWriter实际为初始化writer和是否自动刷新缓存,及初始化换行符,同时
通过父类Writer,初始化写缓存同步锁;在构造PrintWriter的过程中,writer实际为
BufferedWriter,初始化BufferedWriter的过程,实际为初始化writer,缓冲区,缓冲区大小及位置和换行符,在构造BufferedWriter也需要传入writer,这个writer实际为OutputStreamWriter,OutputStreamWriter初始化主要是初始化字节流编码器,字节流编码器StreamEncoder初始化,实际为初始化输出流是否打开状态,字节编码集,
字节编码器,字节缓冲区,输出流OutputStream(从socket获取)。
构造PrintWriter到这里已经结束,下面来看一下如何发送字符串
//PrintWriter
//BufferedWriter
//OutputStreamWriter
//StreamEncoder
小节:
PrintWriter发送字符串,实际为将字符串通过BufferedWriter发送,BufferedWriter现将
字符串写入到其字节缓冲区中,如果缓冲区满,则发送缓存数据,发送委托给OutputStreamWriter,而OutputStreamWriter委托给StreamEncoder,有StreamEncoder将字节数组包装成CharBuffer,在通过编码器,编码字节到编码到字节流缓冲区bb(ByteBuffer),如果字节流缓冲区bb(ByteBuffer)已满,则发送缓存数据。
再来看
//PrintWriter
//BufferedWriter
//OutputStreamWriter
//StreamEncoder
从上来看PrintWriter的flush为发送缓存数据
最后来看一下
//PrintWriter
再来看如何输入流InputStream读取数据
第二部分:BufferedReader,读缓冲区
先看InputStreamReader的构造
//StreamDecoder
再看BufferedReader的构造
//Reader
小节:
从InputStreamReader的构造实际上为初始化流解码器StreamDecoder,
StreamDecoder初始化主要是,初始化输入流状态,字节流缓冲区,socket输入流;
BufferedReader构造的主要是,初始化缓冲区,缓冲区大小,及位置和创建Reader读缓冲区同步锁
下面来看从socket输入流缓冲区,读取数据
//BufferedReader
//InputStreamReader
//StreamDecoder
从上面可以看出从缓存读取数据实际上,先从socket输入流缓冲区通过流解码器StreamDecoder读取数据,解码填充到BufferedReader缓冲区中,BufferedReader从缓冲区中,读取一行数据。
总结:
构造PrintWriter实际为初始化writer和是否自动刷新缓存,及初始化换行符,同时
通过父类Writer,初始化写缓存同步锁;在构造PrintWriter的过程中,writer实际为
BufferedWriter,初始化BufferedWriter的过程,实际为初始化writer,
缓冲区,缓冲区大小及位置和换行符,在构造BufferedWriter也需要传入writer,
这个writer实际为OutputStreamWriter,OutputStreamWriter初始化主要是初始化字节流编码器,字节流编码器StreamEncoder初始化,实际为初始化输出流是否打开状态,字节编码集,
字节编码器,字节缓冲区,输出流OutputStream(从socket获取)。
PrintWriter发送字符串,实际为将字符串通过BufferedWriter发送,BufferedWriter现将
字符串写入到其字节缓冲区中,如果缓冲区满,则发送缓存数据,发送委托给OutputStreamWriter,而OutputStreamWriter委托给StreamEncoder,有StreamEncoder将字节数组包装成CharBuffer,在通过编码器,编码字节到编码到字节流缓冲区bb(ByteBuffer),如果字节流缓冲区bb(ByteBuffer)已满,则发送缓存数据。
InputStreamReader的构造实际上为初始化流解码器StreamDecoder,
StreamDecoder初始化主要是,初始化输入流状态,字节流缓冲区,socket输入流;
BufferedReader构造的主要是,初始化缓冲区,缓冲区大小,及位置和创建Reader读缓冲区同步锁。从缓存读取数据实际上,先从socket输入流缓冲区通过流解码器StreamDecoder读取数据,解码填充到BufferedReader缓冲区中,BufferedReader从缓冲区中,读取一行数据。
附:
在上一篇Java Socket编程实例,我们实战Java Socket编程中,用到
BufferedInput/OutputStream去包装Input/OutputStream读写socket的缓冲区,这种是通过
FilterInput/OutputStream方式;今天我们来看一下在HttpServletResponse中用的比较多的方式
PrintWriter/BufferedReader,即Writer/Reader方式。
服务器:
package socket; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; /** * Server * @author donald * 2017年2月13日 * 下午4:51:53 */ public class TestServer { public static final int PORT = 4003; public static void main(String[] args) { try { startServer(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } // 服务端代码 public static void startServer() throws IOException, InterruptedException { ServerSocket serverSocket = new ServerSocket(PORT); System.out.println("服务器启动......"); while (true) { Socket socket = serverSocket.accept(); // 获取输入流,并读取服务器端的响应信息 InputStream inputStream = socket.getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String message = null; while ((message = bufferedReader.readLine()) != null) { System.out.println("收到客户端信息:" + message); } // 这里向网络进行两次写入 OutputStream outputStream = socket.getOutputStream(); // 将输出流包装为打印流 PrintWriter printWriter = new PrintWriter(outputStream); printWriter.write("Welcome Client!"); printWriter.flush(); // 关闭输出流 socket.shutdownOutput(); // 关闭资源 bufferedReader.close(); inputStream.close(); printWriter.close(); outputStream.close(); socket.close(); } } }
客户端:
package socket; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.Socket; import java.net.UnknownHostException; /** * Client * @author donald * 2017年2月13日 * 下午4:52:27 */ public class TestClient { private static final int PORT = 4003; private static final String ip = "192.168.132.126"; public static void main(String[] args) { try { client(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public static void client() throws UnknownHostException, IOException { // 创建socket连接 Socket socket = new Socket(ip, PORT); System.out.println("连接服务器成功......"); // 这里向网络进行两次写入 OutputStream outputStream = socket.getOutputStream(); // 将输出流包装为打印流 PrintWriter printWriter = new PrintWriter(outputStream); printWriter.write("Hello Server!"); printWriter.flush(); // 关闭输出流 socket.shutdownOutput(); // 获取输入流,并读取服务器端的响应信息 InputStream inputStream = socket.getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String message = null; while ((message = bufferedReader.readLine()) != null) { System.out.println("收到服务端信息:" + message); } // 关闭资源 bufferedReader.close(); inputStream.close(); printWriter.close(); outputStream.close(); socket.close(); } }
服务器端控制台输出:
服务器启动......
收到客户端信息:Hello Server!
客户端控制台输出:
连接服务器成功......
收到服务端信息:Welcome Client!
控制台的输出,不是我们今天所要探讨的,我们要关心的是下面这段代码
// 这里向网络进行两次写入 OutputStream outputStream = socket.getOutputStream(); // 将输出流包装为打印流 PrintWriter printWriter = new PrintWriter(outputStream); printWriter.write("Hello Server!"); printWriter.flush(); // 关闭输出流 socket.shutdownOutput(); // 获取输入流,并读取服务器端的响应信息 InputStream inputStream = socket.getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String message = null; while ((message = bufferedReader.readLine()) != null) { System.out.println("收到服务端信息:" + message); }
这个分两部分来看1.PrintWriter,写缓冲区,2.BufferedReader,读缓冲区
第一部分:PrintWriter,写缓冲区
// 这里向网络进行两次写入 OutputStream outputStream = socket.getOutputStream(); // 将输出流包装为打印流 PrintWriter printWriter = new PrintWriter(outputStream); printWriter.write("Hello Server!"); printWriter.flush();
从下面这句话开始
PrintWriter printWriter = new PrintWriter(outputStream);
public class PrintWriter extends Writer { /** * The underlying character-output stream of this * <code>PrintWriter</code>. * * @since 1.2 */ protected Writer out;//输出流 private final boolean autoFlush;//是否自动刷新缓冲区,默认为false private boolean trouble = false; private Formatter formatter; private PrintStream psOut = null; /** * Line separator string. This is the value of the line.separator * property at the moment that the stream was created. * 系统默认换行符 */ private final String lineSeparator; }
构造PrintWriter
public PrintWriter (Writer out) { //委托给PrintWriter(OutputStream out, boolean autoFlush) this(out, false); }
根据OutputStream构造OutputStreamWriter,再包装成BufferedWriter
public PrintWriter(OutputStream out, boolean autoFlush) { //委托给PrintWriter(Writer out,boolean autoFlush) this(new BufferedWriter(new OutputStreamWriter(out)), autoFlush); // save print stream for error propagation if (out instanceof java.io.PrintStream) { psOut = (PrintStream) out; } }
根据Writer(BufferedWriter)和autoFlush构造PrintWriter
public PrintWriter(Writer out,boolean autoFlush) { //初始化父类 super(out); this.out = out; this.autoFlush = autoFlush; //初始化系统换行符 lineSeparator = java.security.AccessController.doPrivileged( new sun.security.action.GetPropertyAction("line.separator")); }
//Writer
public abstract class Writer implements Appendable, Closeable, Flushable { /** Temporary buffer used to hold writes of strings and single characters */ private char[] writeBuffer; /** Size of writeBuffer, must be >= 1*/ private final int writeBufferSize = 1024; /** * The object used to synchronize operations on this stream. For * efficiency, a character-stream object may use an object other than * itself to protect critical sections. A subclass should therefore use * the object in this field rather than <tt>this</tt> or a synchronized * method. */ protected Object lock; /** * Creates a new character-stream writer whose critical sections will * synchronize on the writer itself. */ protected Writer() { this.lock = this; } /** * Creates a new character-stream writer whose critical sections will * synchronize on the given object. * * @param lock * Object to synchronize on */ //初始化Writer的同步锁,控制缓冲区的写操作 protected Writer(Object lock) { if (lock == null) { throw new NullPointerException(); } this.lock = lock; } }
回到PrintWriter(OutputStream out, boolean autoFlush)方法的这一句
this(new BufferedWriter(new OutputStreamWriter(out)), autoFlush);
先来看OutputStreamWriter
//OutputStreamWriter
public class OutputStreamWriter extends Writer { private final StreamEncoder se;//输出字节流编码器 //构造OutputStreamWriter public OutputStreamWriter(OutputStream out) { //这个前面Writer看过 super(out); try { //初始化输出字节流编码器 se = StreamEncoder.forOutputStreamWriter(out, this, (String)null); } catch (UnsupportedEncodingException e) { throw new Error(e); } } }
//StreamEncoder
public class StreamEncoder extends Writer { private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192; private volatile boolean isOpen;//输出流是否打开 private Charset cs;//字节编码集 private CharsetEncoder encoder;//字节编码器 private ByteBuffer bb;//字节缓冲区 private final OutputStream out;//输出流,从Socket获取的 private WritableByteChannel ch;/字节流通道 private boolean haveLeftoverChar; private char leftoverChar; private CharBuffer lcb; static final boolean $assertionsDisabled = !sun/nio/cs/StreamEncoder.desiredAssertionStatus(); //初始化字符集,及输出流 public static StreamEncoder forOutputStreamWriter(OutputStream outputstream, Object obj, String s) throws UnsupportedEncodingException { String s1; s1 = s; if(s1 == null) s1 = Charset.defaultCharset().name(); if(Charset.isSupported(s1)) return new StreamEncoder(outputstream, obj, Charset.forName(s1)); break MISSING_BLOCK_LABEL_39; IllegalCharsetNameException illegalcharsetnameexception; illegalcharsetnameexception; throw new UnsupportedEncodingException(s1); } private StreamEncoder(OutputStream outputstream, Object obj, Charset charset) { this(outputstream, obj, charset.newEncoder().onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE)); } //初始化输出流是否打开状态,字节编码集,字节编码器,字节缓冲区,输出流 private StreamEncoder(OutputStream outputstream, Object obj, CharsetEncoder charsetencoder) { super(obj); isOpen = true; haveLeftoverChar = false; lcb = null; out = outputstream; ch = null; cs = charsetencoder.charset(); encoder = charsetencoder; if(ch == null) bb = ByteBuffer.allocate(8192); } }
再回到BufferedWriter的构造
this(new BufferedWriter(new OutputStreamWriter(out)), autoFlush);
//BufferedWriter
public class BufferedWriter extends Writer { private Writer out; private char cb[];//缓存区 private int nChars, nextChar;//缓冲区大小,及写位置 private static int defaultCharBufferSize = 8192;//默认缓冲区大小 /** * Line separator string. This is the value of the line.separator * property at the moment that the stream was created. */ private String lineSeparator; /** * Creates a buffered character-output stream that uses a default-sized * output buffer. * * @param out A Writer */ public BufferedWriter(Writer out) { this(out, defaultCharBufferSize); } /** * Creates a new buffered character-output stream that uses an output * buffer of the given size. * * @param out A Writer * @param sz Output-buffer size, a positive integer * * @exception IllegalArgumentException If sz is <= 0 */ //初始化writer,缓冲区,缓冲区大小及位置和换行符 public BufferedWriter(Writer out, int sz) { super(out); if (sz <= 0) throw new IllegalArgumentException("Buffer size <= 0"); this.out = out; cb = new char[sz]; nChars = sz; nextChar = 0; lineSeparator = java.security.AccessController.doPrivileged( new sun.security.action.GetPropertyAction("line.separator")); }
这里我们小节一下PrintWriter:
构造PrintWriter实际为初始化writer和是否自动刷新缓存,及初始化换行符,同时
通过父类Writer,初始化写缓存同步锁;在构造PrintWriter的过程中,writer实际为
BufferedWriter,初始化BufferedWriter的过程,实际为初始化writer,缓冲区,缓冲区大小及位置和换行符,在构造BufferedWriter也需要传入writer,这个writer实际为OutputStreamWriter,OutputStreamWriter初始化主要是初始化字节流编码器,字节流编码器StreamEncoder初始化,实际为初始化输出流是否打开状态,字节编码集,
字节编码器,字节缓冲区,输出流OutputStream(从socket获取)。
构造PrintWriter到这里已经结束,下面来看一下如何发送字符串
printWriter.write("Hello Server!");
//PrintWriter
public void write(String s) { write(s, 0, s.length()); } public void write(String s, int off, int len) { try { synchronized (lock) { //确保输出流打开 ensureOpen(); //out为BufferedWriter out.write(s, off, len); } } catch (InterruptedIOException x) { Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true; } }
//BufferedWriter
//发送字符串 public void write(String s, int off, int len) throws IOException { synchronized (lock) { ensureOpen(); int b = off, t = off + len; while (b < t) { int d = min(nChars - nextChar, t - b); //将字符串发送字节流缓冲区中 s.getChars(b, b + d, cb, nextChar); b += d; nextChar += d; if (nextChar >= nChars) //如果缓存区已满,则发送缓存区 flushBuffer(); } } } //刷新缓存 void flushBuffer() throws IOException { synchronized (lock) { ensureOpen(); if (nextChar == 0) return; //将缓存中数据发送出去,out为OutputStreamWriter out.write(cb, 0, nextChar); nextChar = 0; } } //发送字节数组 public void write(char cbuf[], int off, int len) throws IOException { synchronized (lock) { ensureOpen(); if ((off < 0) || (off > cbuf.length) || (len < 0) || ((off + len) > cbuf.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return; } if (len >= nChars) { /* If the request length exceeds the size of the output buffer, flush the buffer and then write the data directly. In this way buffered streams will cascade harmlessly. */ //如果字节流长度大于缓冲区大小则,刷新缓存区 flushBuffer(); //将缓存中数据发送出去,out为OutputStreamWriter out.write(cbuf, off, len); return; } int b = off, t = off + len; while (b < t) { int d = min(nChars - nextChar, t - b); //将发送字节流,写入缓存区 System.arraycopy(cbuf, b, cb, nextChar, d); b += d; nextChar += d; if (nextChar >= nChars) //如果缓存区已满,则发送缓存区 flushBuffer(); } } }
//OutputStreamWriter
public void write(char cbuf[], int off, int len) throws IOException { //委托个字节流编码器 se.write(cbuf, off, len); }
//StreamEncoder
//写缓存 public void write(char ac[], int i, int j) throws IOException { label0: { synchronized(lock) { ensureOpen(); if(i < 0 || i > ac.length || j < 0 || i + j > ac.length || i + j < 0) throw new IndexOutOfBoundsException(); if(j != 0) break label0; } return; } //委托给implWrite implWrite(ac, i, j); obj; JVM INSTR monitorexit ; goto _L1 exception; throw exception; _L1: } //编码字节流 void implWrite(char ac[], int i, int j) throws IOException { //将字节流,包装成CharBuffer CharBuffer charbuffer = CharBuffer.wrap(ac, i, j); if(haveLeftoverChar) flushLeftoverChar(charbuffer, false); do { if(!charbuffer.hasRemaining()) break; //将包装后的字节流缓冲区,编码到字节流缓冲区bb(ByteBuffer) CoderResult coderresult = encoder.encode(charbuffer, bb, false); if(coderresult.isUnderflow()) { if(!$assertionsDisabled && charbuffer.remaining() > 1) throw new AssertionError(charbuffer.remaining()); if(charbuffer.remaining() == 1) { haveLeftoverChar = true; leftoverChar = charbuffer.get(); } break; } if(coderresult.isOverflow()) { if(!$assertionsDisabled && bb.position() <= 0) throw new AssertionError(); //如果字节流缓冲区bb(ByteBuffer)已满,则发送缓存数据 writeBytes(); } else { coderresult.throwException(); } } while(true); } //发送缓存数据 private void writeBytes() throws IOException { bb.flip(); int i = bb.limit(); int j = bb.position(); if(!$assertionsDisabled && j > i) throw new AssertionError(); int k = j > i ? 0 : i - j; if(k > 0) if(ch != null) { if(ch.write(bb) != k && !$assertionsDisabled) throw new AssertionError(k); } else { //通过OutputStream发送字节流 out.write(bb.array(), bb.arrayOffset() + j, k); } bb.clear(); }
小节:
PrintWriter发送字符串,实际为将字符串通过BufferedWriter发送,BufferedWriter现将
字符串写入到其字节缓冲区中,如果缓冲区满,则发送缓存数据,发送委托给OutputStreamWriter,而OutputStreamWriter委托给StreamEncoder,有StreamEncoder将字节数组包装成CharBuffer,在通过编码器,编码字节到编码到字节流缓冲区bb(ByteBuffer),如果字节流缓冲区bb(ByteBuffer)已满,则发送缓存数据。
再来看
printWriter.flush();
//PrintWriter
public void flush() { try { synchronized (lock) { ensureOpen(); out.flush(); } } catch (IOException x) { trouble = true; } }
//BufferedWriter
public void flush() throws IOException { synchronized (lock) { //发送缓冲数据 flushBuffer(); out.flush(); } }
//OutputStreamWriter
public void flush() throws IOException { se.flush(); }
//StreamEncoder
public void flush() throws IOException { synchronized(lock) { ensureOpen(); implFlush(); } } void implFlush() throws IOException { implFlushBuffer(); if(out != null) out.flush(); } void implFlushBuffer() throws IOException { if(bb.position() > 0) //发送缓存数据 writeBytes(); }
从上来看PrintWriter的flush为发送缓存数据
最后来看一下
//PrintWriter
//新建一行 private void newLine() { try { synchronized (lock) { ensureOpen(); //发送换行符,委托给BufferedWriter out.write(lineSeparator); if (autoFlush) out.flush(); } } catch (InterruptedIOException x) { Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true; } } //BufferedWriter public void newLine() throws IOException { write(lineSeparator); }
再来看如何输入流InputStream读取数据
第二部分:BufferedReader,读缓冲区
// 获取输入流,并读取服务器端的响应信息 InputStream inputStream = socket.getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String message = null; while ((message = bufferedReader.readLine()) != null) { System.out.println("收到服务端信息:" + message); }
先看InputStreamReader的构造
public class InputStreamReader extends Reader { //流解码器 private final StreamDecoder sd; /** * Creates an InputStreamReader that uses the default charset. * * @param in An InputStream */ public InputStreamReader(InputStream in) { super(in); try { //初始化流解码器 sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object } catch (UnsupportedEncodingException e) { // The default encoding should always be available throw new Error(e); } } }
//StreamDecoder
public class StreamDecoder extends Reader { private static final int MIN_BYTE_BUFFER_SIZE = 32;//最小缓冲区大小 private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192;//默认缓冲区大小 private volatile boolean isOpen;//输入流状态 private boolean haveLeftoverChar; private char leftoverChar; private static volatile boolean channelsAvailable = true; private Charset cs; private CharsetDecoder decoder; private ByteBuffer bb;//字节流缓冲区 private InputStream in;//Socket 输入流 private ReadableByteChannel ch; static final boolean $assertionsDisabled = !sun/nio/cs/StreamDecoder.desiredAssertionStatus(); //初始化字节集,构造StreamDecoder public static StreamDecoder forInputStreamReader(InputStream inputstream, Object obj, String s) throws UnsupportedEncodingException { String s1; s1 = s; if(s1 == null) s1 = Charset.defaultCharset().name(); if(Charset.isSupported(s1)) return new StreamDecoder(inputstream, obj, Charset.forName(s1)); } StreamDecoder(InputStream inputstream, Object obj, Charset charset) { this(inputstream, obj, charset.newDecoder().onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE)); } //初始化输入流状态,字节流缓冲区,输入流 StreamDecoder(InputStream inputstream, Object obj, CharsetDecoder charsetdecoder) { super(obj); isOpen = true; haveLeftoverChar = false; cs = charsetdecoder.charset(); decoder = charsetdecoder; if(ch == null) { in = inputstream; ch = null; bb = ByteBuffer.allocate(8192); } bb.flip(); } }
再看BufferedReader的构造
public class BufferedReader extends Reader { private Reader in;//Reader private char cb[];//缓冲区 private int nChars, nextChar;//缓冲区大小,及位置 private static final int INVALIDATED = -2; private static final int UNMARKED = -1; private int markedChar = UNMARKED; private int readAheadLimit = 0; /* Valid only when markedChar > 0 */ /** If the next character is a line feed, skip it */ private boolean skipLF = false; /** The skipLF flag when the mark was set */ private boolean markedSkipLF = false; private static int defaultCharBufferSize = 8192; private static int defaultExpectedLineLength = 80; /** * Creates a buffering character-input stream that uses a default-sized * input buffer. * * @param in A Reader */ public BufferedReader(Reader in) { this(in, defaultCharBufferSize); } //初始化缓冲区,缓冲区大小,及位置 public BufferedReader(Reader in, int sz) { super(in); if (sz <= 0) throw new IllegalArgumentException("Buffer size <= 0"); this.in = in; cb = new char[sz]; nextChar = nChars = 0; }
//Reader
public abstract class Reader implements Readable, Closeable { protected Object lock; /** * Creates a new character-stream reader whose critical sections will * synchronize on the given object. * 创建读缓冲区同步锁 * @param lock The Object to synchronize on. */ protected Reader(Object lock) { if (lock == null) { throw new NullPointerException(); } this.lock = lock; }
小节:
从InputStreamReader的构造实际上为初始化流解码器StreamDecoder,
StreamDecoder初始化主要是,初始化输入流状态,字节流缓冲区,socket输入流;
BufferedReader构造的主要是,初始化缓冲区,缓冲区大小,及位置和创建Reader读缓冲区同步锁
下面来看从socket输入流缓冲区,读取数据
String message = null; while ((message = bufferedReader.readLine()) != null) { System.out.println("收到服务端信息:" + message); }
//BufferedReader
//从缓冲区读取一行数据 String readLine(boolean ignoreLF) throws IOException { StringBuffer s = null; int startChar; synchronized (lock) { ensureOpen(); boolean omitLF = ignoreLF || skipLF; bufferLoop: for (;;) { if (nextChar >= nChars) //填充BufferedReader缓冲区 fill(); if (nextChar >= nChars) { /* EOF */ if (s != null && s.length() > 0) return s.toString(); else return null; } boolean eol = false; char c = 0; int i; /* Skip a leftover '\n', if necessary */ if (omitLF && (cb[nextChar] == '\n')) nextChar++; skipLF = false; omitLF = false; charLoop: for (i = nextChar; i < nChars; i++) { c = cb[i]; if ((c == '\n') || (c == '\r')) { eol = true; break charLoop; } } startChar = nextChar; nextChar = i; //一行数据 if (eol) { String str; if (s == null) { str = new String(cb, startChar, i - startChar); } else { s.append(cb, startChar, i - startChar); str = s.toString(); } nextChar++; if (c == '\r') { skipLF = true; } return str; } if (s == null) s = new StringBuffer(defaultExpectedLineLength); s.append(cb, startChar, i - startChar); } } } //填充BufferedReader缓冲区 private void fill() throws IOException { int dst; //确定标记位置 if (markedChar <= UNMARKED) { /* No mark */ dst = 0; } else { /* Marked */ int delta = nextChar - markedChar; if (delta >= readAheadLimit) { /* Gone past read-ahead limit: Invalidate mark */ markedChar = INVALIDATED; readAheadLimit = 0; dst = 0; } else { if (readAheadLimit <= cb.length) { /* Shuffle in the current buffer */ System.arraycopy(cb, markedChar, cb, 0, delta); markedChar = 0; dst = delta; } else { /* Reallocate buffer to accommodate read-ahead limit */ char ncb[] = new char[readAheadLimit]; System.arraycopy(cb, markedChar, ncb, 0, delta); cb = ncb; markedChar = 0; dst = delta; } nextChar = nChars = delta; } } int n; do { //从InputStreamReader,读取数据到BufferedReader缓存区cb //private char cb[];缓冲区 n = in.read(cb, dst, cb.length - dst); } while (n == 0); if (n > 0) { nChars = dst + n; nextChar = dst; } }
//InputStreamReader
public int read(char cbuf[], int offset, int length) throws IOException { //委托给StreamDecoder return sd.read(cbuf, offset, length); }
//StreamDecoder
//从输入流缓存读数据 public int read(char ac[], int i, int j) throws IOException { ... i1 + implRead(ac, k, k + l); ... } int implRead(char ac[], int i, int j) throws IOException { if(!$assertionsDisabled && j - i <= 1) throw new AssertionError(); //包装socket输入流缓存数据为CharBuffer CharBuffer charbuffer = CharBuffer.wrap(ac, i, j - i); if(charbuffer.position() != 0) charbuffer = charbuffer.slice(); boolean flag = false; do { //解码包装后的字节流到字节流缓冲区ByteBuffer bb CoderResult coderresult = decoder.decode(bb, charbuffer, flag); if(coderresult.isUnderflow()) { if(flag || !charbuffer.hasRemaining() || charbuffer.position() > 0 && !inReady()) break; //读取缓存数据 int k = readBytes(); if(k >= 0) continue; flag = true; if(charbuffer.position() == 0 && !bb.hasRemaining()) break; decoder.reset(); continue; } if(coderresult.isOverflow()) { if(!$assertionsDisabled && charbuffer.position() <= 0) throw new AssertionError(); break; } coderresult.throwException(); } while(true); if(flag) decoder.reset(); if(charbuffer.position() == 0) { if(flag) return -1; if(!$assertionsDisabled) throw new AssertionError(); } return charbuffer.position(); } //读取缓存数据 private int readBytes() throws IOException { //这个我们放在以后说 bb.compact(); int l; if(ch == null) break MISSING_BLOCK_LABEL_48; int i = ch.read(bb); if(i >= 0) break MISSING_BLOCK_LABEL_236; l = i; bb.flip(); return l; int i1; int j1; int k1; //获取缓存可读的字节数 int j = bb.limit(); //记录读取位置 l = bb.position(); if(!$assertionsDisabled && l > j) throw new AssertionError(); i1 = l > j ? 0 : j - l; if(!$assertionsDisabled && i1 <= 0) throw new AssertionError(); // j1 = in.read(bb.array(), bb.arrayOffset() + l, i1); if(j1 >= 0) break MISSING_BLOCK_LABEL_160; k1 = j1; //转换读写状态 bb.flip(); return k1; if(j1 == 0) throw new IOException("Underlying input stream returned zero bytes"); if(!$assertionsDisabled && j1 > i1) throw new AssertionError((new StringBuilder()).append("n = ").append(j1).append(", rem = ").append(i1).toString()); bb.position(l + j1); bb.flip(); }
从上面可以看出从缓存读取数据实际上,先从socket输入流缓冲区通过流解码器StreamDecoder读取数据,解码填充到BufferedReader缓冲区中,BufferedReader从缓冲区中,读取一行数据。
总结:
构造PrintWriter实际为初始化writer和是否自动刷新缓存,及初始化换行符,同时
通过父类Writer,初始化写缓存同步锁;在构造PrintWriter的过程中,writer实际为
BufferedWriter,初始化BufferedWriter的过程,实际为初始化writer,
缓冲区,缓冲区大小及位置和换行符,在构造BufferedWriter也需要传入writer,
这个writer实际为OutputStreamWriter,OutputStreamWriter初始化主要是初始化字节流编码器,字节流编码器StreamEncoder初始化,实际为初始化输出流是否打开状态,字节编码集,
字节编码器,字节缓冲区,输出流OutputStream(从socket获取)。
PrintWriter发送字符串,实际为将字符串通过BufferedWriter发送,BufferedWriter现将
字符串写入到其字节缓冲区中,如果缓冲区满,则发送缓存数据,发送委托给OutputStreamWriter,而OutputStreamWriter委托给StreamEncoder,有StreamEncoder将字节数组包装成CharBuffer,在通过编码器,编码字节到编码到字节流缓冲区bb(ByteBuffer),如果字节流缓冲区bb(ByteBuffer)已满,则发送缓存数据。
InputStreamReader的构造实际上为初始化流解码器StreamDecoder,
StreamDecoder初始化主要是,初始化输入流状态,字节流缓冲区,socket输入流;
BufferedReader构造的主要是,初始化缓冲区,缓冲区大小,及位置和创建Reader读缓冲区同步锁。从缓存读取数据实际上,先从socket输入流缓冲区通过流解码器StreamDecoder读取数据,解码填充到BufferedReader缓冲区中,BufferedReader从缓冲区中,读取一行数据。
附:
/** * Abstract class for writing to character streams. The only methods that a * subclass must implement are write(char[], int, int), flush(), and close(). * Most subclasses, however, will override some of the methods defined here in * order to provide higher efficiency, additional functionality, or both. * * @see Writer * @see BufferedWriter * @see CharArrayWriter * @see FilterWriter * @see OutputStreamWriter * @see FileWriter * @see PipedWriter * @see PrintWriter * @see StringWriter * @see Reader * * @author Mark Reinhold * @since JDK1.1 */ public abstract class Writer implements Appendable, Closeable, Flushable {
package java.io; /** * Abstract class for reading character streams. The only methods that a * subclass must implement are read(char[], int, int) and close(). Most * subclasses, however, will override some of the methods defined here in order * to provide higher efficiency, additional functionality, or both. * * * @see BufferedReader * @see LineNumberReader * @see CharArrayReader * @see InputStreamReader * @see FileReader * @see FilterReader * @see PushbackReader * @see PipedReader * @see StringReader * @see Writer * * @author Mark Reinhold * @since JDK1.1 */ public abstract class Reader implements Readable, Closeable {
public abstract class ByteBuffer extends Buffer implements Comparable<ByteBuffer> { // These fields are declared here rather than in Heap-X-Buffer in order to // reduce the number of virtual method invocations needed to access these // values, which is especially costly when coding small buffers. // final byte[] hb; // Non-null only for heap buffers final int offset; boolean isReadOnly; }
public abstract class CharBuffer extends Buffer implements Comparable<CharBuffer>, Appendable, CharSequence, Readable { // These fields are declared here rather than in Heap-X-Buffer in order to // reduce the number of virtual method invocations needed to access these // values, which is especially costly when coding small buffers. // final char[] hb; // Non-null only for heap buffers final int offset; boolean isReadOnly; // Valid only for heap buffers }
public abstract class Buffer { // Invariants: mark <= position <= limit <= capacity private int mark = -1; private int position = 0; private int limit; private int capacity; // Used only by direct buffers // NOTE: hoisted here for speed in JNI GetDirectBufferAddress long address; }
发表评论
-
文件通道解析二(文件锁,关闭通道)
2017-05-16 23:17 1075文件通道解析一(读写操作,通道数据传输等):http://do ... -
文件通道解析一(读写操作,通道数据传输等)
2017-05-16 10:04 1172Reference定义(PhantomRefere ... -
文件通道创建方式综述
2017-05-15 17:39 1074Reference定义(PhantomReference,Cl ... -
文件读写方式简单综述后续(文件,流构造)
2017-05-14 23:04 1496Java Socket通信实例:http://donald-d ... -
文件读写方式简单综述
2017-05-14 11:13 1142Java Socket通信实例:http://donald-d ... -
FileChanne定义
2017-05-12 23:28 948文件读写方式简单综述:http://donald-draper ... -
SeekableByteChannel接口定义
2017-05-11 08:43 1245ByteChannel,分散聚集通道接口的定义(SocketC ... -
FileChannel示例
2017-05-11 08:37 1003前面我们看过socket通道,datagram通道,以管道Pi ... -
PipeImpl解析
2017-05-11 08:41 941ServerSocketChannel定义:http://do ... -
Pipe定义
2017-05-10 09:07 918Channel接口定义:http://donald-drape ... -
NIO-Pipe示例
2017-05-10 08:47 916PipeImpl解析:http://donald-draper ... -
DatagramChannelImpl 解析四(地址绑定,关闭通道等)
2017-05-10 08:27 792DatagramChannelImpl 解析一(初始化):ht ... -
DatagramChannelImpl 解析三(多播)
2017-05-10 08:20 1927DatagramChannelImpl 解析一(初始化):ht ... -
NIO-UDP实例
2017-05-09 12:32 1593DatagramChannelImpl 解析一(初始化):ht ... -
DatagramChannelImpl 解析二(报文发送与接收)
2017-05-09 09:03 1418DatagramChannelImpl 解析一(初始化):ht ... -
DatagramChannelImpl 解析一(初始化)
2017-05-08 21:52 1422Channel接口定义:http://donald-drape ... -
MembershipKeyImpl 简介
2017-05-08 09:11 932MembershipKey定义:http://donald-d ... -
DatagramChannel定义
2017-05-07 23:13 1238Channel接口定义:http://donald-drape ... -
MulticastChanne接口定义
2017-05-07 13:45 1147NetworkChannel接口定义:ht ... -
MembershipKey定义
2017-05-06 16:20 927package java.nio.channels; i ...
相关推荐
- **字节流与字符流**:Java提供了处理二进制数据的InputStream/OutputStream和处理文本数据的Reader/Writer。根据数据类型选择合适的流进行操作。 4. **异常处理** - **网络异常**:网络通信中可能出现...
Java的I/O流系统提供了一套完整的类库,包括InputStream和OutputStream类用于处理字节流,以及Reader和Writer类用于处理字符流。在Socket编程中,通常会使用Socket对象的getInputStream()和getOutputStream()方法...
`InputStream`和`OutputStream`用于字节流,`Reader`和`Writer`用于字符流。 2. **聊天功能实现** - **客户端**:客户端通过Socket连接到服务器,并打开输入输出流,用户输入的文本信息会被封装为字节或字符流发送...
在这个“Java Socket 多线程实例”中,我们将会探讨如何结合这两个关键概念来创建一个聊天服务器和客户端应用。 首先,`ChatServer.java` 文件通常包含了服务器端的代码。在Java中,通过`java.net.ServerSocket` 类...
在Java中,Socket类和ServerSocket类是进行网络通信的核心。ServerSocket用于监听客户端的连接请求,而Socket则代表了服务器与客户端之间的一个连接。 创建一个Java Socket服务端的步骤通常包括: 1. **创建...
总结来说,Java Socket的多线程通信是一个关键的网络编程概念,它使得服务器能够同时处理多个客户端请求,提高了服务的响应速度和并发能力。结合特定的工具如XtreamXml,可以进一步优化数据的传输和处理效率。
总结,Java中的Socket通信涉及网络编程的基本概念和操作,包括Socket对象的创建、数据流的获取和管理、数据的读写以及资源的释放。同时,结合服务器端的`ServerSocket`和相应的工具,可以构建出复杂且可靠的网络通信...
Java Socket 是Java编程语言中用于网络通信的核心API,它提供了低级别的、面向连接的、双向的通信机制。在Java中,Socket是基于TCP/IP协议的,可以用来实现客户端与服务器之间的通信。本教程将深入讲解Java Socket...
在Java中,Socket类和ServerSocket类是进行网络通信的核心。本教程将深入讲解这两个类以及如何构建基本的Socket通信模型。 首先,我们来看`ServerSocket`类。这个类主要用于创建服务器端的监听套接字,它等待来自...
Java Socket聊天系统是一种基于TCP/IP协议的网络通信方式,它允许两台计算机通过网络...通过阅读提供的"java socket编程.doc"文档,你可以获得更详细的步骤和示例代码,帮助你进一步理解和实践Java Socket聊天系统。
Java的IO流库提供了丰富的类来处理输入输出,包括字符流(`Reader`和`Writer`)和字节流(`InputStream`和`OutputStream`)。在SOCKET通信中,数据通常是通过流进行传输的。例如,可以使用`BufferedReader`和`...
`Socket`提供了`getInputStream()`和`getOutputStream()`方法来获取输入流和输出流,用于读写数据。 4. **登录实现**:在这个例子中,我们可以设想服务器需要验证客户端发送的用户名和密码。服务器可以从输入流读取...
在完成通信后,务必正确关闭Socket、InputStream、OutputStream以及相关的Reader和Writer。不正确的关闭可能会导致资源泄漏,影响程序的稳定性和性能。 九、实践案例 在实际开发中,Socket编程常用于实现聊天室、...
3. `java.io.InputStream` 和 `java.io.OutputStream`:Socket通信中的输入流和输出流,用于读写数据。 4. `java.lang.Runtime.exec()` 或 `java.lang.ProcessBuilder`:用于执行操作系统命令。 五、安全与优化 1....
- **字节流与字符流**:Java提供了`InputStream`和`OutputStream`处理字节数据,以及`Reader`和`Writer`处理字符数据。在实际应用中,通常会使用缓冲流如`BufferedReader`和`BufferedWriter`提高效率。 4. **异常...
Java Socket编程是构建网络应用的重要工具,通过掌握`Socket`、`ServerSocket`和`DatagramSocket`类的使用,开发者能够构建出功能强大且灵活的网络应用程序。无论是简单的文本通信还是复杂的数据传输,Socket都能...
5. **java.io.Reader/Writer**:用于处理字符数据流。 6. **java.nio.channels.SocketChannel/ServerSocketChannel**:支持非阻塞模式的Socket编程。 #### 三、实现Socket编程的三个步骤 ##### 步骤一:创建...