在编写网络应用的时候数据缓冲区是应该比较常用的方式,主要用构建一个内存区用于存储发送的数据和接收的数据;为了更好的利用已有数据缓冲区所以构造一个缓冲池来存放相关数据方便不同连接更好地利用缓冲区,节省不停的构造新的缓冲区所带的损耗问题。
缓冲区
其实构造一个缓冲区非常简单,根据需分本相关大小的byte数组即可;既然是用于存放数据那就自然要实现读和写方法,看一下具体实现
public class DataBuffer : IDisposable { public byte[] Data; private int mLength; private int mPostion = 0; internal int mCount = 0; public DataBuffer(byte[] data) { Data = data; mLength = data.Length; mPostion = 0; mCount = data.Length; } public DataBuffer(int length) { mLength = length; Data = new byte[length]; } public void From(Array source, int index, int count) { Array.Copy(source, index, Data, 0, count); mPostion = 0; mCount = count; } public int Write(byte[] data) { return Write(data, 0); } public int Write(byte[] data, int index) { int count = 0; if (mPostion + (data.Length-index) > mLength) { count = mLength - mPostion; } else { count = data.Length - index; } if (count > 0) { Array.Copy(data, index, Data, mPostion, count); mPostion += count; mCount += count; } return count; } public ArraySegment<byte> Read(int count) { int end = count; if (mPostion + count > mCount) end = mCount - mPostion; ArraySegment<byte> result= new ArraySegment<byte>(Data, mPostion, end); mPostion += end; return result; } public void Seek() { Seek(0); } public void Seek(int postion) { mPostion = 0; } public ArraySegment<byte> GetSegment() { return new ArraySegment<byte>(Data, 0, mCount); } internal BufferPool Pool { get; set; } public void Dispose() { if (Pool != null) { mPostion = 0; mCount = 0; Pool.Push(this); } } }
为了方便使用,Buffer实现了IDisposable接口,其作为就是当释放的时候把Buffer放回到Pool里.
Buffer提供了两个方法分别是Write和Read用于写和读数据,由于缓冲区有大小限制,所以在写的时候会返回一个成功写入的数量;而read则返回一个ArraySegment<byte>用于描述其位置。为什么要这样做呢,其实有些情况一个数据成员会被写入到不同的缓冲区,当读出来的时候就会存要在多个缓冲区中获取。
缓冲池
缓冲池用于发放和回收级冲区,实现一个重用的目的。池的实现并不复杂,封装一个简单的队列操作即可。
public class BufferPool : IDisposable { private static List<BufferPool> mPools = new List<BufferPool>(); private static int mIndex = 0; public static void Setup(int pools, int buffers) { Setup(pools, buffers, 2048); } public static void Setup(int pools, int buffers, int bufferlength) { lock (mPools) { for (int i = 0; i < pools; i++) { mPools.Add(new BufferPool(buffers, bufferlength)); } } } public static void Clean() { lock (mPools) { foreach (BufferPool item in mPools) { item.Dispose(); } mPools.Clear(); } } public static BufferPool GetPool() { lock (mPools) { if (mIndex == mPools.Count) { mIndex = 0; } return mPools[mIndex]; } } Queue<DataBuffer> mBuffers; private int mBufferLength; public BufferPool(int count, int bufferlength) { mBufferLength = bufferlength; mBuffers = new Queue<DataBuffer>(count); for (int i = 0; i < count; i++) { mBuffers.Enqueue(createBuffer(bufferlength)); } } private DataBuffer createBuffer(int length) { DataBuffer item = new DataBuffer(length); item.Pool = this; return item; } public DataBuffer Pop() { lock (mBuffers) { return mBuffers.Count > 0 ? mBuffers.Dequeue() : createBuffer(mBufferLength); } } public void Push(DataBuffer buffer) { lock (mBuffers) { mBuffers.Enqueue(buffer); } } private bool mDisposed = false; private void OnDispose() { lock (mBuffers) { while (mBuffers.Count > 0) { mBuffers.Dequeue().Pool = null; } } } public void Dispose() { lock (this) { if (!mDisposed) { OnDispose(); mDisposed = true; } } } }
BufferPool实现了几个静态方法
Setup
主要目的是用于构造多个缓冲池,缓冲区数量和缓冲区大小。为什么会考虑多个池呢,主要原因是在高并发的来分配处理减低池的负载。
Clean
用于清除释放缓冲池
GetPool
平均地分发缓冲池给使用者
一个简单的数据缓冲区和数据缓冲池已经实现了,在后面的文章里会讲述如何构造BufferWriter和BufferReader,根据对象的需要把信息分别写入多个缓冲区和在多个缓冲区中读取信息还原对象。