type
TTCPBag = class
private
FSocket: TCustomSocket;
FIdentity: Integer; //Socket标识为接收封包用 jfyes 2006-05-22 modified;
FCurrentLength: Integer;
FTotalLength: Integer;
FLastRecvTime: Cardinal;
FRecvText: string;
FStrList: TStrings; //接受数据时保存数据的
private
procedure ClearCurrentTotalLength(const ATotalLength: Integer = 0);
public
constructor Create(ASocket: TCustomSocket);
destructor Destroy; override;
//接收一个数据包
function RecvABag(var AText: string; var ACurrentLength: Integer): Boolean;
//得到合并后的字符串
function UnitRecvStr: string;
property Identity: Integer read FIdentity write FIdentity; //为接收封包用 jfyes 2006-05-22 modified;
property LastRecvTime: Cardinal read FLastRecvTime write FLastRecvTime;
property RecvText: string read FRecvText write FRecvText;
end;
//TCP 检查接收包是否完整
function TCustomSocket.TCPCheckRecvFinish(ASocket: TCustomSocket; ABuffer: PChar;
var TotalLength: Integer; var AMsg: string): Boolean;
var
bFind: Boolean;
i: Integer;
AFCUDPBag: TTCPBag;
TempTotalLen: Integer;
begin
bFind := False;
Result := False;
///这里要重新修正缓存包,将数据队列放到列表中去,并在头端读取相应的长度就行。
EnterCriticalSection(FSliceBagLock);
try
try
for I := FSliceBagList.Count - 1 downto 0 do
begin
AFCUDPBag := TTCPBag(FSliceBagList);
//如果收了一半没有发剩下的,不就死掉了, 还是要为一个包的建立超时
//不是删除掉,而是清空它
//超过6s的,因为TCP数据包是按排 队的发送不可有错误包顺序,所以7延时就不要
if ((GetTickCount - AFCUDPBag.LastRecvTime) > 20000) // 20秒
{ and (AFCUDPBag.FCurrentLength > 0) and (AFCUDPBag.FTotalLength > 0)}then
begin
AFCUDPBag.ClearCurrentTotalLength; //清空上一次的包
if AFCUDPBag.Identity <> ASocket.Identity then
Continue;
end;
//找到了该包, 这里是为每个客户端建立一个缓存包
if AFCUDPBag.Identity = ASocket.Identity then begin
bFind := True;
AFCUDPBag.LastRecvTime := GetTickCount;
if AFCUDPBag.RecvABag(AMsg, TotalLength) then
begin
AMsg := AFCUDPBag.RecvText; //返回结果AMsg 给用户
Result := True;
Break;
end
else begin //还没接受完毕,等待下一次接受
Exit;
end;
end // if AFCUDPBag.Identity = AIdentity then begin
end; //end for
if not bFind then begin //没找到,则新增一条进去
//注意第一次接收可能会是完整的包。
TempTotalLen := PInteger(ABuffer)^;
if TotalLength = TempTotalLen then
begin
Result := True;
//是个完整的数据包
Exit;
end;
AFCUDPBag := TTCPBag.Create(ASocket);
AFCUDPBag.LastRecvTime := GetTickCount;
AFCUDPBag.FIdentity := ASocket.Identity;
if AFCUDPBag.RecvABag(AMsg, TotalLength) then
begin
AMsg := AFCUDPBag.RecvText;
FreeAndNil(AFCUDPBag);
Exit;
end;
FSliceBagList.Add(AFCUDPBag);
end;
except on E: Exception do begin
Result := False;
ASocket.DoException(ASocket, 'TCPCheckRecvFinish: ' + E.Message);
end;
end;
finally
LeaveCriticalSection(FSliceBagLock);
end;
end;
{ TTCPBag }
procedure TTCPBag.ClearCurrentTotalLength(const ATotalLength: Integer);
begin
FCurrentLength := 0;
FTotalLength := 0;
FLastRecvTime := GetTickCount;
FStrList.Clear;
end;
constructor TTCPBag.Create(ASocket: TCustomSocket);
begin
inherited Create;
FSocket := ASocket;
FTotalLength := 0;
FCurrentLength := 0;
FIdentity := -1;
Identity := -1;
FLastRecvTime := GetTickCount;
FRecvText := '';
FStrList := TStringList.Create;
FStrList.Clear;
end;
destructor TTCPBag.Destroy;
begin
FTotalLength := 0;
FCurrentLength := 0;
FIdentity := -1;
Identity := -1;
FRecvText := '';
FStrList.Free;
inherited;
end;
function TTCPBag.RecvABag(var AText: string; var ACurrentLength: Integer): Boolean;
var
i_Len: Integer;
ATempLen: Integer;
Temp: string;
AFcur: Integer;
begin
Result := False;
try
if (FTotalLength < 0) or (FTotalLength > 10000000) then
begin
FSocket.DoException(FSocket,
Format('TTCPBag.FTotalLength: ABB TotalLen: %d, ATextLen: %d, CurLen: %d',
[FTotalLength, ACurrentLength, FCurrentLength]));
ClearCurrentTotalLength; //清除内存中的
Exit;
end;
//如果初发始化或清空就取前4字节为总长度
if (FTotalLength = 0) then
begin
Move(AText[1], FTotalLength, SizeOf(Integer));
end;
//这里为什么会有包分析出错,大部分原因是数据包处理不及时,发生冗长的原因,
//在客户端进行Sleep(20)后发送就正常,但总觉得不是个好办法。
if (FTotalLength < 0) or (FTotalLength > 10000000) then
begin
if FSocket is TServerClientSocket then
FSocket.DoException(FSocket,
Format('TTCPBag.FTotalLength: ThreadCount: %d, BufferCount: %d',
[TServerClientSocket(FSocket).FServerSocket.FDealThreads.Count,
TServerClientSocket(FSocket).FServerSocket.FBufferQueue.Count]));
ClearCurrentTotalLength; //清除内存中的
Exit;
end;
//如果等于就正好
if ACurrentLength = FTotalLength then begin
FRecvText := AText;
Result := True;
ClearCurrentTotalLength;
Exit;
end;
i_Len := FTotalLength - FCurrentLength;
AFcur := FCurrentLength;
//累计当前长度和总长度对比
FCurrentLength := FCurrentLength + ACurrentLength;
//如果等于就正好 ,这里是对的,看来下面的大于长度还是有点问题,但是经过测试,好
//像是藕合问题,有时会有问题,有时又没有,为什么呢?又在如何长度的情况下会这样呢?
if (FCurrentLength = FTotalLength) then begin
// FSocket.DoException(FSocket, Format('TTCPBag.ToatlLen = %d, OldAFcurLen: %d, FCurLen: %d AtextLen: %d , ListTextLen: %d',
// [FTotalLength, AFcur, FCurrentLength, ACurrentLength, Length(FStrList.Strings[0])]));
ACurrentLength := FTotalLength;
FRecvText := '';
FRecvText := UnitRecvStr; //先取得前面的部分
FRecvText := FRecvText + AText; //取得后面部分
Result := True;
ClearCurrentTotalLength;
Exit;
end;
//这里计算,如果大于,就取大于的部分,剩下的部分在放进去。
if FCurrentLength > FTotalLength then begin
FRecvText := UnitRecvStr; //先取得前面的部分
// 剩下的部分的长度
ATempLen := ACurrentLength;
FRecvText := FRecvText + Copy(AText, 1, i_Len); //取大于部分
ACurrentLength := FTotalLength; //返回总长度
ClearCurrentTotalLength; //清除内存中的
//新的当前等于来的总长减去上一次剩于的部分长度就是了。
FCurrentLength := ATempLen - i_Len;
Temp := Copy(AText, i_Len + 1, FCurrentLength);
Move(Temp[1], FTotalLength, SizeOf(Integer));
//FSocket.DoException(FSocket,
//Format('TTCPBag. > FTotalLength: A %d, OldAFcurLen: %d,, FCurLen: %d TextLen: %d',
// [FTotalLength, AFcur, FCurrentLength, ATempLen]));
if (FTotalLength < 0) or (FTotalLength > 100000) then
begin
FSocket.DoException(FSocket, Format('TTCPBag.FTotalLength: B ReadLen Error: %d, AText Len: %d, CurLen: %d',
[FTotalLength, ATempLen, FCurrentLength]));
ClearCurrentTotalLength; //清除内存中的
Exit;
end;
FStrList.Add(Temp);
//Temp := '';
Result := True;
Exit;
end;
//否则就是小于了,直接添加进去
FStrList.Add(AText);
except on E: Exception do begin
Result := False;
FSocket.DoException(FSocket, 'TTCPBag.RecvABag: ' + E.Message);
end;
end;
end;
function TTCPBag.UnitRecvStr: string;
var
i: Integer;
begin
Result := '';
for i := 0 to FStrList.Count - 1 do
begin
Result := Result + FStrList;
end;
end;