全部博文(282)
分类: WINDOWS
2008-10-20 09:07:54
{*************************************************************************
功能描述:实现环形缓冲区的通用类,适用于流式通信模式
*************************************************************************}
{*************************************************************************
环形缓冲区通常有一个读指针和一个写指针。读指针指向环形缓冲区中可读的数据,
写指针指向环形缓冲区中可写的缓冲区。通过移动读指针和写指针就可以实现缓冲
区的数据读取和写人。在通常情况下,环形缓冲区的读用户仅仅会影响读指针,而
写用户仅仅会影响写指针。如果仅仅有一个读用户和一个写用户,那么不需要添加
互斥保护机制就可以保证数据的正确性。如果有多个读写用户访问环形缓冲区,那
么必须添加互斥保护机制来确保多个用户互斥访问环形缓冲区。
*************************************************************************}
unit u_Share_RingBuffer;
interface
uses Windows;
const
MAX_BUFFER_LEN = 4096;
type
//环形缓冲区类--可变数组缓冲区类
TRingBuffer = class(TObject)
private
FBuffer : array of Char; //缓冲区
FBufferLen : Longint; //缓冲区长度
FisWrap : Boolean; //是否返回头部
FCritical : TRTLCriticalSection; //临界区
FIsCritical : Boolean; //是否需要临界区
procedure Require(); //共享锁内存
procedure Release(); //共享解锁
function GetEmptyCount():Longint; //剩余空白数据量
public
FReadPos : LongInt; //读取点
FWritePos : LongInt; //写入点
function GetDataCount():Longint; //取得可用数据量
function Readbuffer(var Buffer; Count: Longint):Longint; //读取数据
function WriteBuffer(const Buffer; Count: Longint):Longint; //写入数据
function CopyBuffer(var Buffer; Count: Longint):Longint; //复制数据
function ResizeBuffer(ABufferLen:Longint):Boolean; //更改缓冲区大小
function ReSet():Boolean; //复位缓冲区
constructor Create(ABufferLen:Longint);
destructor Destory();
published
property BufferLen: Longint read FBufferLen; //缓冲区总小小
property IsCritical:Boolean read FIsCritical write FIsCritical; //多线程情况下需要考虑互斥访问共享缓冲区时使用
end;
//环形缓冲类--阻塞(同步)模式读写--继承自TRingBuffer
//适合于多线程读写
TRingBufferSyn = class(TRingBuffer)
private
FSynEvent : THandle; //同步读写事件句柄
public
function ReadbufferSyn(var Buffer; Count: Longint;TimeOut:Longint):Integer; //读取数据
function WriteBufferSyn(const Buffer; Count: Longint;TimeOut:Longint):Integer; //写入数据
constructor Create(ABufferLen:Longint);
destructor Destory();
end;
implementation
{ TRingBuffer }
constructor TRingBuffer.Create(ABufferLen:Longint);
begin
inherited Create();
FBufferLen := ABufferLen;
SetLength( FBuffer,ABufferLen );
FReadPos := 0;
FWritePos := 0;
FIsCritical := False;
FisWrap := False; //是否返回头部
InitializeCriticalSection(FCritical);
end;
destructor TRingBuffer.Destory;
begin
DeleteCriticalSection(FCritical);
inherited Destroy;
end;
procedure TRingBuffer.Release();
begin
if FIsCritical then
begin
LeaveCriticalSection(FCritical);
end;
end;
function TRingBuffer.CopyBuffer(var Buffer; Count: Longint):Longint; //复制数据
var
Li_RemainCount,Litemp : Integer;
tempbuf:array[0..MAX_BUFFER_LEN] of Char;
Datacount:Integer;
begin
try
Result := 0;
Require();
Datacount := GetDataCount(); //获取实际数据量
if (Datacount <= 0) or ( Count = 0 ) then //无数据可读的情况
begin
Result := 0;
Exit;
end;
FillChar(tempbuf,MAX_BUFFER_LEN-1,0);
if Datacount >= Count then //有足够数据可读
begin
if FWritePos > FReadPos then
begin
Move(FBuffer[FReadPos],Buffer,Count);
Result := Count;
FisWrap := false;
end else begin
//先判断尾部剩余空间有多少可读数据
Li_RemainCount := FBufferLen - FReadPos; //到缓冲区尾部还剩的缓冲量
if Count > Li_RemainCount then //尾部剩余数据都可读,把尾部数据全部读出
begin
Move(FBuffer[FReadPos],tempbuf,Li_RemainCount); //暂存数据到临时buf
Litemp := Count - Li_RemainCount; //剩余要读的数据
Move(FBuffer[0],tempbuf[Li_RemainCount],Litemp); //从头部读取剩余数据
Move(tempbuf,Buffer,Count);
Result := Count;
FisWrap := false;
end else begin //当可用数据不到尾部,则直接读取
Move(FBuffer[FReadPos],tempbuf,Count); //暂存数据到临时buf
Move(tempbuf,Buffer,Count);
Result := Count;
FisWrap := True;
end;
end;
end else begin //无足够数据可读,可读取剩余数据
if FWritePos > FReadPos then
begin
Move(FBuffer[FReadPos],Buffer,Datacount); //暂存数据到临时buf
Result := Datacount;
FisWrap := false;
end else begin
//先判断尾部剩余空间有多少可读数据
Li_RemainCount := FBufferLen - FReadPos; //到缓冲区尾部还剩的缓冲量
FillChar(tempbuf,MAX_BUFFER_LEN-1,0);
Move(FBuffer[FReadPos],tempbuf,Li_RemainCount); //暂存数据到临时buf
Litemp := Datacount - Li_RemainCount; //剩余要读的数据
Move(FBuffer[0],tempbuf[Li_RemainCount],Litemp);
Move(tempbuf,Buffer,Datacount);
Result := Datacount;
FisWrap := false;
end;
end;
finally
Release();
end;
end;
function TRingBuffer.ReSet():Boolean;
begin
FReadPos := 0;
FWritePos := 0;
FIsCritical := False;
FisWrap := False; //是否返回头部
fillchar(FBuffer[0],FBufferLen-1,0);
Result := True;
end;
procedure TRingBuffer.Require();
begin
if FIsCritical then
begin
EnterCriticalSection(FCritical);
end;
end;
{-------------------------------------------------------------------------------
过程名: Readbuffer
作者: 黄健
日期: 2007.08.25
参数: var Buffer 需要返回的数据;
Count: Integer 需要读取的数据
返回值: Longint类型, 返回实际读取的数据量 <=0 表示读取错误或缓冲区空
-------------------------------------------------------------------------------}
function TRingBuffer.Readbuffer(var Buffer; Count: Integer): Longint;
var
Li_RemainCount,Litemp : Integer;
tempbuf:array[0..MAX_BUFFER_LEN] of Char;
Datacount:Integer;
begin
try
Result := -1;
Require();
//if (Count < 0) or (Count > FBufferLen) then Exit;
Datacount := GetDataCount(); //获取实际数据量
if (Datacount <= 0) or ( Count = 0 ) then //无数据可读的情况
begin
Result := 0;
Exit;
end;
FillChar(tempbuf,MAX_BUFFER_LEN-1,0);
if Datacount >= Count then //有足够数据可读
begin
if FWritePos > FReadPos then
begin
Move(FBuffer[FReadPos],Buffer,Count);
Inc(FReadPos,Count);
Result := Count;
FisWrap := false;
end else begin
//先判断尾部剩余空间有多少可读数据
Li_RemainCount := FBufferLen - FReadPos; //到缓冲区尾部还剩的缓冲量
if Count > Li_RemainCount then //尾部剩余数据都可读,把尾部数据全部读出
begin
Move(FBuffer[FReadPos],tempbuf,Li_RemainCount); //暂存数据到临时buf
Litemp := Count - Li_RemainCount; //剩余要读的数据
Move(FBuffer[0],tempbuf[Li_RemainCount],Litemp); //从头部读取剩余数据
FReadPos := Litemp;
Move(tempbuf,Buffer,Count);
Result := Count;
FisWrap := false;
end else begin //当可用数据不到尾部,则直接读取
Move(FBuffer[FReadPos],tempbuf,Count); //暂存数据到临时buf
Inc(FReadPos,Count);
Move(tempbuf,Buffer,Count);
Result := Count;
FisWrap := True;
end;
end;
end else begin //无足够数据可读,可读取剩余数据
if FWritePos > FReadPos then
begin
Move(FBuffer[FReadPos],Buffer,Datacount); //暂存数据到临时buf
Inc(FReadPos,Datacount);
Result := Datacount;
FisWrap := false;
end else begin
//先判断尾部剩余空间有多少可读数据
Li_RemainCount := FBufferLen - FReadPos; //到缓冲区尾部还剩的缓冲量
FillChar(tempbuf,MAX_BUFFER_LEN-1,0);
Move(FBuffer[FReadPos],tempbuf,Li_RemainCount); //暂存数据到临时buf
Litemp := Datacount - Li_RemainCount; //剩余要读的数据
Move(FBuffer[0],tempbuf[Li_RemainCount],Litemp);
FReadPos := Litemp;
Move(tempbuf,Buffer,Datacount);
Result := Datacount;
FisWrap := false;
end;
end;
finally
Release();
end;
end;
{-------------------------------------------------------------------------------
过程名: WriteBuffer
作者: 黄健
日期: 2007.08.25
参数: const Buffer; 要写入的数据
Count: Integer 要写入数据大小
返回值: Longint 返回实际写入的数据量,<=0 表示写入错误或缓冲区满
-------------------------------------------------------------------------------}
function TRingBuffer.WriteBuffer(const Buffer; Count: Integer): Longint;
var
Li_RemainCount,Litemp : Integer;
tempbuf:array[0..MAX_BUFFER_LEN] of Char;
EmptyCount:Integer;
begin
try
Require();
EmptyCount := GetEmptyCount(); //获取可写入的数据大小
if (Count < 0) or (Count > FBufferLen) then
begin
Result := -1;
Exit;
end;
if (EmptyCount <=0) then //无数据可写
begin
Result := 0;
Exit;
end;
//如果写入的缓冲区超过尾部,就跳回头部写入
if EmptyCount >= Count then
begin
if FWritePos < FReadPos then
begin
Move(Buffer,FBuffer[FWritePos],Count);
Inc(FWritePos,Count);
Result := Count;
FisWrap := True;
end else begin
//判断是否到末尾,还有足够缓冲
Li_RemainCount := FBufferLen - FWritePos;
if (Li_RemainCount >= Count ) then
begin
Move(Buffer,FBuffer[FWritePos],Count);
Inc(FWritePos,Count);
Result := Count;
FisWrap := False;
end else begin //末尾缓冲不够,返回头部读取
Litemp :=Count - Li_RemainCount ; //尾部无法写入返回头部写入的数据
FillChar(tempbuf,MAX_BUFFER_LEN-1,0);
Move(Buffer,tempbuf,Count); //暂存数据到临时buf
Move(tempbuf,FBuffer[FWritePos],Li_RemainCount);
FWritePos := Litemp;
Move(tempbuf[Li_RemainCount],FBuffer[0],Litemp); //返回头部写入剩余的数据
Result := Count;
FisWrap := false;
end;
end;
end else begin //缓冲区不足以写入全部数据,可以写入部分数据
if FWritePos < FReadPos then
begin
Result := FReadPos - FWritePos;
Move(Buffer,FBuffer[FWritePos],Result);
Inc(FWritePos,Result);
FisWrap := True;
end else begin
//判断是否到末尾,还有足够缓冲
Li_RemainCount := FBufferLen - FWritePos;
FillChar(tempbuf,MAX_BUFFER_LEN-1,0);
Move(Buffer,tempbuf,Count); //暂存数据到临时buf
Move(tempbuf,FBuffer[FWritePos],Li_RemainCount);
if FReadPos > 0 then
begin
FWritePos := FReadPos;
Move(tempbuf[Li_RemainCount],FBuffer[0],FReadPos); //返回头部写入剩余的数据
Result := Li_RemainCount + FReadPos;
FisWrap := true;
end else begin
Inc(FWritePos,Li_RemainCount);
Result := Li_RemainCount;
FisWrap := False;
end;
end;
end;
finally
Release();
end;
end;
{-------------------------------------------------------------------------------
过程名: TRingBuffer.GetDataCount
作者: 黄健
日期: 2007.08.25
参数: 无
返回值: Longint 返回当前可读的数据量
-------------------------------------------------------------------------------}
function TRingBuffer.GetDataCount: Longint;
begin
try
Require();
if (FWritePos = 0) and (FReadPos = FWritePos) then
begin
Result := 0;
Exit;
end;
if (FWritePos > FReadPos ) then
begin
Result := FWritePos - FReadPos;
end else if FWritePos < FReadPos then begin
Result := FBufferLen - FReadPos + FWritePos;
end else if FWritePos = FReadPos then begin
if FisWrap then Result := FBufferLen else Result := 0;
end;
finally
Release();
end;
end;
{-------------------------------------------------------------------------------
过程名: TRingBuffer.GetEmptyCount
作者: 黄健
日期: 2007.08.25
参数: 无
返回值: Longint 返回当前可写的数据量
-------------------------------------------------------------------------------}
function TRingBuffer.GetEmptyCount: Longint;
begin
try
Require();
if FWritePos > FReadPos then
Result := FBufferLen - FWritePos + FReadPos
else if FWritePos < FReadPos then
Result := FReadPos - FWritePos
else if FWritePos = FReadPos then
begin
if FisWrap then Result := 0 else Result := FBufferLen;
end;
finally
Release();
end;
end;
{-------------------------------------------------------------------------------
过程名: TRingBuffer.ResizeBuffer
作者: 黄健
日期: 2007.08.25
参数: ABufferLen: Longint
返回值: Boolean 改变缓冲区大小,返回false表示失败,true表示成功
-------------------------------------------------------------------------------}
function TRingBuffer.ResizeBuffer(ABufferLen: Longint): Boolean;
begin
Result := False;
try
Require();
if ABufferLen < FBufferLen then Exit;
SetLength(FBuffer,ABufferLen);
FBufferLen := ABufferLen;
finally
Release();
end;
end;
{ TRingBufferSyn }
constructor TRingBufferSyn.Create(ABufferLen: Integer);
begin
inherited Create(ABufferLen);
FIsCritical := True;
FSynEvent := CreateEvent(nil,true, false,pchar('RingBuffer'));
end;
destructor TRingBufferSyn.Destory;
begin
CloseHandle(FSynEvent);
inherited Destory();
end;
{-------------------------------------------------------------------------------
过程名: TRingBufferSyn.ReadbufferSyn
作者: 黄健
日期: 2007.08.26
参数: var Buffer; Count, TimeOut: Integer
返回值: Integer ,返回-1表示错误,-2表示超时 ,>0表示实际读取的字节
-------------------------------------------------------------------------------}
function TRingBufferSyn.ReadbufferSyn(var Buffer; Count, TimeOut: Integer):Integer;
var
ReadCount,RemainCount,ReadPos:Integer;
Abuf:array[0..MAX_BUFFER_LEN-1] of Char;
ATimeOut:Integer;
begin
Result := -1;
FillChar(Abuf,MAX_BUFFER_LEN-1,0);
ReadCount := Readbuffer(Abuf,Count);
if ReadCount < 0 then Exit;
if ReadCount = Count then
begin
Move(Abuf,Buffer,Count);
Result := Count;
end else if ReadCount < Count then
begin
RemainCount := Count - ReadCount;
ReadPos := ReadCount;
ATimeOut := 0;
while (RemainCount <= 0) do
begin
ReadCount := Readbuffer(Abuf[ReadPos],RemainCount);
Inc(ReadPos,ReadCount);
RemainCount := Count - ReadPos;
//Sleep(1);
Inc(ATimeOut);
if ATimeOut >= TimeOut then
begin
Result := -2;
Exit;
end;
end;
Result := Count;
end;
end;
{-------------------------------------------------------------------------------
过程名: TRingBufferSyn.WriteBufferSyn
作者: 黄健
日期: 2007.08.26
参数: const Buffer; Count, TimeOut: Integer
返回值: Integer Integer ,返回-1表示错误,-2表示超时 ,>0表示实际读取的字节
-------------------------------------------------------------------------------}
function TRingBufferSyn.WriteBufferSyn(const Buffer; Count, TimeOut: Integer):Integer;
var
WriteCount,RemainCount,WritePos:Integer;
Abuf:array[0..MAX_BUFFER_LEN-1] of Char;
ATimeOut:Integer;
begin
Result := -1;
FillChar(Abuf,MAX_BUFFER_LEN-1,0);
WriteCount := WriteBuffer(Buffer,Count);
if WriteCount < 0 then Exit;
if WriteCount = Count then
begin
Result := Count;
end else if WriteCount < Count then
begin
Move(Buffer,Abuf,Count);
RemainCount := Count - WriteCount;
WritePos := WriteCount;
ATimeOut := 0;
while (RemainCount <= 0) do
begin
WriteCount := WriteBuffer(Abuf[WritePos],RemainCount);
Inc(WritePos,WriteCount);
RemainCount := Count - WritePos;
//Sleep(1);
Inc(ATimeOut);
if ATimeOut >= TimeOut then
begin
Result := -2;
Exit;
end;
end;
Result := Count;
end;
end;
end.
转载地址: