java Socket读写缓存区Writer和Reader: http://donald-draper.iteye.com/blog/2356885
前一篇文章中,我们讲了java Socket io的Writer和Reader,在上一篇中,在流解码器和编码器中,经常用到字节缓冲ByteBuffer,今天我们就来看一ByteBuffer。
ByteBuffer有两个实现一个为,HeapByteBuffer,另一个为DirectByteBuffer,这两个有什么区别呢?
我们引入原文,不翻译以免都是原味;
1.HeapByteBuffer
-
- public static ByteBuffer allocate(int capacity) {
- if (capacity < 0)
- throw new IllegalArgumentException();
- return new HeapByteBuffer(capacity, capacity);
- }
HeapByteBuffer使用的java堆内存
2.DirectByteBuffer
-
- public static ByteBuffer allocateDirect(int capacity) {
- return new DirectByteBuffer(capacity);
- }
- *
A byte buffer is either direct or non-direct. Given a
- * direct byte buffer, the Java virtual machine will make a best effort to
- * perform native I/O operations directly upon it. That is, it will attempt to
- * avoid copying the buffer's content to (or from) an intermediate buffer
- * before (or after) each invocation of one of the underlying operating
- * system's native I/O operations.
- *
- *
A direct byte buffer may be created by invoking the {@link
- * #allocateDirect(int) allocateDirect} factory method of this class. The
- * buffers returned by this method typically have somewhat higher allocation
- * and deallocation costs than non-direct buffers. The contents of direct
- * buffers may reside outside of the normal garbage-collected heap, and so
- * their impact upon the memory footprint of an application might not be
- * obvious. It is therefore recommended that direct buffers be allocated
- * primarily for large, long-lived buffers that are subject to the underlying
- * system's native I/O operations. In general it is best to allocate direct
- * buffers only when they yield a measureable gain in program performance.
- *
- *
A direct byte buffer may also be created by {@link
- * java.nio.channels.FileChannel#map mapping
} a region of a file
- * directly into memory. An implementation of the Java platform may optionally
- * support the creation of direct byte buffers from native code via JNI. If an
- * instance of one of these kinds of buffers refers to an inaccessible region
- * of memory then an attempt to access that region will not change the buffer's
- * content and will cause an unspecified exception to be thrown either at the
- * time of the access or at some later time.
DirectByteBuffer使用的是:the Java virtual machine will make a best effort to
perform native I/O operations directly upon it.
使用时要注意:It is therefore recommended that direct buffers be allocated
primarily for large, long-lived buffers that are subject to the underlying
system's native I/O operations
今天我们只讲HeapByteBuffer,ByteBuffer,Buffer,我们先从测试实例来看
- public abstract class Buffer {
-
-
- private int mark = -1;
- private int position = 0;
- private int limit;
- private int capacity;
- }
mark,position,limit,capacity大小关系:
-1 <= mark <= position <= limit <= capacity;
0<= position <= limit <= capacity;
测试主类:
注意 main里面方法的调用顺序不要变,第一次测试我们先注释掉testCompact方法,控制台输出:
===============init status============
position:0
limit:32
capacity:32
===============put byte============
position:1
limit:32
capacity:32
======get byte:102
===============put char============
position:3
limit:32
capacity:32
======get Char:a
===============mark============
position:3
limit:32
capacity:32
===============put int============
position:7
limit:32
capacity:32
======get int:4
===============put float============
position:11
limit:32
capacity:32
======get float:10.0
===============put double============
position:19
limit:32
capacity:32
======get double:20.0
===============put long============
position:27
limit:32
capacity:32
======get long:30
======buffer 剩余空间大小:5
java.nio.BufferOverflowException
at java.nio.Buffer.nextPutIndex(Buffer.java:519)
at java.nio.HeapByteBuffer.putLong(HeapByteBuffer.java:417)
at socket.TestByteBuffer.testOverFlow(TestByteBuffer.java:150)
at socket.TestByteBuffer.main(TestByteBuffer.java:24)
===============reset============
position:3
limit:32
capacity:32
======get int from mark:4
position:7
limit:32
capacity:32
======get int from mark after put new int value:5
===============clear============
position:0
limit:32
capacity:32
======get int after clear:5
从控制台输出可看出,ByteBuffer的put*和get*(int index)方法不改变mark,limit和capacity的值;put则回改变position的位置,put操作后position的位置为,put操作之前position+length(put 操作数);mark操作会改变mark的值,reset操作,则是将position定位到mark;clear操作并不会清空缓冲空间,而是将position复位0,limit为capacity,mark为-1;remain操作返回的是可用的空间大小为capacity-position;
如put后,超出缓冲区大小,则抛出BufferOverflowException异常。
下面我们将mark,reset和clear注释掉,测试Compact操作如下:
- public static void main(String[] args) {
-
- initByteBuffer();
- testByte();
- testChar();
-
- testInt();
- testFloat();
- testDouble();
- testLong();
- testRemaining();
- testOverFlow();
-
-
- testCompact();
- }
关注控制的compact部分输出:
===============put long============
position:27
limit:32
capacity:32
======get long:30
======buffer 剩余空间大小:5
java.nio.BufferOverflowException
at java.nio.Buffer.nextPutIndex(Buffer.java:519)
at java.nio.HeapByteBuffer.putLong(HeapByteBuffer.java:417)
at socket.TestByteBuffer.testOverFlow(TestByteBuffer.java:150)
at socket.TestByteBuffer.main(TestByteBuffer.java:24)
===============compact============
position:5
limit:32
capacity:32
======get int:4
===============flip============
===============rewind============
从控制台输出可以看出,compact操作一般在一下情况调用,
当out发送数据,即读取buf的数据,write方法可能只发送了部分数据,buf里还有剩余,
这时调用buf.compact()函数将position与limit之间的数据,copy到buf的0到limit-position,
进行压缩(非实际以压缩,只是移动),以便下次 向写入缓存。当position与limit之间的数据为空时,则不改变原缓冲区,否则copy相应数据。
//HeapByteBuffer
- public ByteBuffer compact() {
-
- System.arraycopy(hb, ix(position()), hb, ix(0), remaining());
- position(remaining());
- limit(capacity());
- discardMark();
- return this;
- }
-
-
-
-
-
-
- public static native void arraycopy(Object src, int srcPos,
- Object dest, int destPos,
- int length);
线面我们来看一下Buffer的相关操作:
- public abstract class Buffer {
-
-
- private int mark = -1;
- private int position = 0;
- private int limit;
- private int capacity;
-
-
- public final int position() {
- return position;
- }
-
- public final int capacity() {
- return capacity;
- }
-
- public final Buffer position(int newPosition) {
- if ((newPosition > limit) || (newPosition < 0))
- throw new IllegalArgumentException();
- position = newPosition;
- if (mark > position) mark = -1;
- return this;
- }
-
- public final int limit() {
- return limit;
- }
-
- public final Buffer mark() {
- mark = position;
- return this;
- }
-
- public final Buffer reset() {
- int m = mark;
- if (m < 0)
- throw new InvalidMarkException();
- position = m;
- return this;
- }
-
-
- public final Buffer clear() {
- position = 0;
- limit = capacity;
- mark = -1;
- return this;
- }
-
-
-
-
-
-
-
-
- public final Buffer flip() {
- limit = position;
- position = 0;
- mark = -1;
- return this;
- }
-
-
-
-
-
-
- public final Buffer rewind() {
- position = 0;
- mark = -1;
- return this;
- }
-
- public final int remaining() {
- return limit - position;
- }
-
- final void discardMark() {
- mark = -1;
- }
- Buffer(int mark, int pos, int lim, int cap) {
- if (cap < 0)
- throw new IllegalArgumentException("Negative capacity: " + cap);
- this.capacity = cap;
- limit(lim);
- position(pos);
- if (mark >= 0) {
- if (mark > pos)
- throw new IllegalArgumentException("mark > position: ("
- + mark + " > " + pos + ")");
- this.mark = mark;
- }
- }
- }
再来看ByteBuffer
- public abstract class ByteBuffer
- extends Buffer
- implements Comparable
- {
-
-
-
-
-
- final byte[] hb;
- final int offset;
- boolean isReadOnly;
- ByteBuffer(int mark, int pos, int lim, int cap,
- byte[] hb, int offset)
- {
- super(mark, pos, lim, cap);
- this.hb = hb;
- this.offset = offset;
- }
- }
再来看
- class HeapByteBuffer
- extends ByteBuffer
- {
- HeapByteBuffer(byte[] buf, int off, int len) {
- super(-1, off, off + len, buf.length, buf, 0);
-
-
-
-
- }
- }
来看压缩函数
-
-
-
-
-
-
-
-
-
-
-
-
- public ByteBuffer compact() {
-
- System.arraycopy(hb, ix(position()), hb, ix(0), remaining());
-
- position(remaining());
-
- limit(capacity());
-
- discardMark();
- return this;
- }
在来看一下put函数
- public ByteBuffer putChar(char x) {
-
- Bits.putChar(this, ix(nextPutIndex(2)), x, bigEndian);
- return this;
- }
-
- public ByteBuffer putInt(int x) {
-
- Bits.putInt(this, ix(nextPutIndex(4)), x, bigEndian);
- return this;
- }
我们,详看一下putInt
-
-
- final int nextPutIndex(int nb) {
- if (limit - position < nb)
- throw new BufferOverflowException();
- int p = position;
- position += nb;
- return p;
- }
//HeapByteBuffer
-
- protected int ix(int i) {
- return i + offset;
- }
//Bits
将int值x,从bb的bi位置,写入
- static void putInt(ByteBuffer bb, int bi, int x, boolean bigEndian) {
- if (bigEndian)
- putIntB(bb, bi, x);
- else
- putIntL(bb, bi, x);
- }
-
-
- static void putIntL(ByteBuffer bb, int bi, int x) {
- bb._put(bi + 3, int3(x));
- bb._put(bi + 2, int2(x));
- bb._put(bi + 1, int1(x));
- bb._put(bi , int0(x));
- }
- private static byte int3(int x) { return (byte)(x >> 24); }
- private static byte int2(int x) { return (byte)(x >> 16); }
- private static byte int1(int x) { return (byte)(x >> 8); }
- private static byte int0(int x) { return (byte)(x ); }
从ByteBuffer bb的比位置获取int值
- static int getIntL(ByteBuffer bb, int bi) {
- return makeInt(bb._get(bi + 3),
- bb._get(bi + 2),
- bb._get(bi + 1),
- bb._get(bi ));
- static private int makeInt(byte b3, byte b2, byte b1, byte b0) {
- return (((b3 ) << 24) |
- ((b2 & 0xff) << 16) |
- ((b1 & 0xff) << 8) |
- ((b0 & 0xff) ));
- }
从上面可以看出向缓存中写入占多字节的原始类型Char,int,float等时,
HeapByteBuffer,通过Bit将原始类型字节拆分存入到ByteBuffer的缓存中。
总结:
get*(int index)方法不改变mark,limit和capacity的值;put则回改变position的位置,put操作后position的位置为,put操作之前position+length(put 操作数);
mark操作会改变mark的值,reset操作,则是将position定位到mark;clear操作并不会清空缓冲空间,而是将position复位0,limit为capacity,mark为-1;remain操作返回的是可用的空间大小为capacity-position;如put后,超出缓冲区大小,则抛出BufferOverflowException异常。
compact操作一般在一下情况调用,当out发送数据,即读取buf的数据,write方法可能只发送了部分数据,buf里还有剩余,这时调用buf.compact()函数将position与limit之间的数据,copy到buf的0到limit-position,进行压缩(非实际以压缩,只是移动),以便下次 向写入缓存。当position与limit之间的数据为空时,则不改变原缓冲区,否则copy相应数据。HeapByteBuffer向缓存中写入占多字节的原始类型Char,int,float等时,HeapByteBuffer,通过Bit将原始类型字节拆分存入到ByteBuffer的缓存中。
下面用图来模仿相关操作:
初始化:
写数据:
mark:
再次写数据:
reset操作:
flip操作,左图为操作前,右图为操作后;
rewind操作,左图为操作前,右图为操作后;
clear操作,上图为操作前,下图为操作后;
compact操作,有数据的情况下,上图为操作前,下图为操作后;
compact操作,无数据的情况下,上图为操作前,下图为操作后;