- 浏览: 950450 次
文章分类
- 全部博客 (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 991文件通道解析一(读写操作,通道数据传输等):http://do ... -
文件通道解析一(读写操作,通道数据传输等)
2017-05-16 10:04 1100Reference定义(PhantomRefere ... -
文件通道创建方式综述
2017-05-15 17:39 929Reference定义(PhantomReference,Cl ... -
文件读写方式简单综述后续(文件,流构造)
2017-05-14 23:04 1398Java Socket通信实例:http://donald-d ... -
文件读写方式简单综述
2017-05-14 11:13 1068Java Socket通信实例:http://donald-d ... -
FileChanne定义
2017-05-12 23:28 869文件读写方式简单综述:http://donald-draper ... -
SeekableByteChannel接口定义
2017-05-11 08:43 1118ByteChannel,分散聚集通道接口的定义(SocketC ... -
FileChannel示例
2017-05-11 08:37 904前面我们看过socket通道,datagram通道,以管道Pi ... -
PipeImpl解析
2017-05-11 08:41 834ServerSocketChannel定义:http://do ... -
Pipe定义
2017-05-10 09:07 837Channel接口定义:http://donald-drape ... -
NIO-Pipe示例
2017-05-10 08:47 846PipeImpl解析:http://donald-draper ... -
DatagramChannelImpl 解析四(地址绑定,关闭通道等)
2017-05-10 08:27 690DatagramChannelImpl 解析一(初始化):ht ... -
DatagramChannelImpl 解析三(多播)
2017-05-10 08:20 1680DatagramChannelImpl 解析一(初始化):ht ... -
NIO-UDP实例
2017-05-09 12:32 1513DatagramChannelImpl 解析一(初始化):ht ... -
DatagramChannelImpl 解析二(报文发送与接收)
2017-05-09 09:03 1340DatagramChannelImpl 解析一(初始化):ht ... -
DatagramChannelImpl 解析一(初始化)
2017-05-08 21:52 1326Channel接口定义:http://donald-drape ... -
MembershipKeyImpl 简介
2017-05-08 09:11 870MembershipKey定义:http://donald-d ... -
DatagramChannel定义
2017-05-07 23:13 1182Channel接口定义:http://donald-drape ... -
MulticastChanne接口定义
2017-05-07 13:45 1066NetworkChannel接口定义:ht ... -
MembershipKey定义
2017-05-06 16:20 827package java.nio.channels; i ...
相关推荐
java socket教程java socket教程java socket教程
在主线程中通过控制台读取键盘...服务器在收到一个socket连接之后,把该socket保存到队列中,并对队列中的每个socket开启各自的读写线程。测试可以在不同控制台运行server和client,服务器接收消息时,会显示消息来源
java socket client 断线重连的简单实现 有什么意见可以提哦
JAVA Socket教程 Java网络编程之传输控制协议 Socket套接字—Java套接字编程
Java Socket 聊天通信演示代码 Java Socket 聊天通信演示代码
java socket使用加密协议传输对象
java socket 学习资料java socket 学习资料java socket 学习资料java socket 学习资料java socket 学习资料
java socket USB和 串口通讯,使用java程序与硬件通讯的三种方式。
java socket源码解析 java socket源码解析 java socket源码解析 java socket源码解析
Java Socket编程.pdf Java Socket编程.pdf Java Socket编程.pdf
1. 满足具有Socket客户端需求的基本应用. 2. 满足具有Socket服务端的基本应用. 具备并发能力, 能满足可设定个数客户端连接. 参考个人博客: http://blog.csdn.net/ostrichmyself/article/details/6618349
Java Socket 操作 Demo import java.awt.Color; import java.awt.Dimension; import java.awt.Point; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.io.BufferedReader; import ...
Java实现Socket长连接和短连接,实现原理可参见个人博客
java socket处理硬件传过来的16进制数据的工具类,包含大小端转换、byte[]数组转换为16进制的字符串、byte[]转ByteBuffer、16进制字符串转换为byte数组等
Socket类是负责处理客户端通信的Java类。本文主要是介绍java使用Socket类接收和发送数据,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
使用java8 scoket 实现西门子s7协议 可以读写 s7系列全部plc 没有dll 文件 纯原生java
java socket nginx tcp转发 用户真实IP测试,文章请看我的博客
Java 基于socket聊天室客户端的代码
基于java的开发源码-Java Socket通信实现.zip 基于java的开发源码-Java Socket通信实现.zip 基于java的开发源码-Java Socket通信实现.zip 基于java的开发源码-Java Socket通信实现.zip 基于java的开发源码-Java ...
java socket传输demo 客户端和服务器端传输demojava socket传输demo 客户端和服务器端传输demo