嗯,这会很长!
我为我正在研究的 LAN 模拟协议制作了一个用于管理丢弃的 UDP 数据包的重传方案。
数据包存储
TDataBuffer = record
PacketID : WORD;
Packet : TMemoryStream;
end;
PDataBuffer = ^TDataBuffer;
相关数据模块类成员
fRxDataPacketList : TThreadList20;
fTxDataPacketList : TThreadList20;
procedure CreateDataBuffer
(PacketID : WORD; Packet : TMemoryStream;
var DataBuffer : PDataBuffer);
procedure DestroyDataBuffer
(var DataBuffer : PDataBuffer);
procedure AddPacketToPacketList
(PacketID : WORD; Packet : TMemoryStream;
RecievedPacket : Boolean);
function GetPacketFromTxDataPacketList
(PacketID : WORD; var Packet : TMemoryStream): Boolean;
TThreadList20:这是我自己的线程友好,加密和压缩支持TList的包装类。
还有另一个处理 Rx 端的过程,它与我的问题无关,所以我跳过它。
创造
procedure TDataModuleClient.CreateDataBuffer
(PacketID : WORD; Packet : TMemoryStream;
var DataBuffer : PDataBuffer);
begin
New (DataBuffer);
DataBuffer.PacketID := PacketID;
DataBuffer.Packet := TMemoryStream.Create;
if Assigned (Packet) then
begin
DataBuffer.Packet.CopyFrom (Packet,Packet.Size); // NO AV HERE
DataBuffer.Packet.Position := 0;
end;
end;
破坏
procedure TDataModuleClient.DestroyDataBuffer
(var DataBuffer : PDataBuffer);
begin
DataBuffer.Packet.Free;
Dispose (DataBuffer);
end;
添加到列表
procedure TDataModuleClient.AddPacketToDataPacketList
(PacketID : WORD; Packet : TMemoryStream; RecievedPacket : Boolean);
var
DataBuffer : PDataBuffer;
begin
CreateDataBuffer (PacketID,Packet,DataBuffer);
if RecievedPacket then
fRxDataPacketList.Add (TObject (DataBuffer))
else
begin
fTxDataPacketList.Lock;
try
fTxDataPacketList.Add (TObject (DataBuffer));
if fRxDataPacketList.Count = 21 then
begin
DataBuffer := PDataBuffer (fTxDataPacketList [0]);
DestroyDataBuffer (DataBuffer);
fTxDataPacketList.Delete (0);
end;
finally fTxDataPacketList.Unlock;
end;
end;
end;
从列表中提取
function TDataModuleClient.GetPacketFromTxDataPacketList
(PacketID : WORD; var Packet : TMemoryStream): Boolean;
var
DataBuffer : PDataBuffer;
I : Integer;
begin
Result := False;
fTxDataPacketList.Lock;
try
for I := fTxDataPacketList.Count - 1 downto 0 do
begin
DataBuffer := PDataBuffer (fTxDataPacketList [I]);
if DataBuffer.PacketID < PacketID then
begin
DestroyDataBuffer (DataBuffer);
fTxDataPacketList.Delete (I);
end
else if DataBuffer.PacketID = PacketID then
begin
Result := True;
Packet := TMemoryStream.Create;
Packet.CopyFrom
(DataBuffer.Packet,DataBuffer.Packet.Size); // AV HERE
Packet.Position := 0;
DestroyDataBuffer (DataBuffer);
fTxDataPacketList.Delete (I);
break;
end;
end;
finally fTxDataPacketList.Unlock;
end;
end;
数据包:输出变量。
请帮助我,我知道由于代码量,推断出一些东西看起来是一项艰巨的任务。
基于这个有限的代码,我做了一些猜测:
1) 在调用CopyFrom()
之前,传递给GetPacketFromTxDataPacketList()
的输入Packet
可能实际上尚未实例化。这将解释AV。
2)由于Packet
参数被声明为var
,这对我来说表明它是GetPacketFromTxDataPacketList()
应该创建和返回的输出参数,而不是让调用者创建TMemoryStream
并将其传递到GetPacketFromTxDataPacketList()
中以填充数据。 这也将在#1中发挥作用,因为GetPacketFromTxDataPacketList()
没有创建新的TMemoryStream
对象,而是假设该对象已经事先创建。