2

我在为 IdTCPServer 的每个连接声明一个唯一的全局变量时遇到问题。我在这里要做的是。

TMyContext = class(TIdServerContext)
  public
    Tag: Integer;
    Queue: TIdThreadSafeList;
    FPacketBuffer: Pointer;
    PacketBufferPtr: Integer;

    constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TThreadList = nil); override;
    destructor Destroy; override;
  end;

然后使用 TMyContext(AContext).FPacketBuffer 访问变量,但是当连接处于活动状态并且新连接尝试连接时出现访问冲突错误。这是我的 idTcpConnect 和 idTcpDisconnect 中的内容

procedure TMainFrm.MainSckConnect(AContext: TIdContext);
begin
  TMyContext(AContext).Queue.Clear;
  TMyContext(AContext).Tag := -1;
  GetMem(TMyContext(AContext).FPacketBuffer,65536);
end;

procedure TMainFrm.MainSckDisconnect(AContext: TIdContext);
Var Client: TClientInfo;
begin
//If TMyContext(AContext).Queue.Count > 0 Then TMyContext(AContext).Queue.Clear;
TMyContext(AContext).Queue.Clear;
FreeMem(TMyContext(AContext).FPacketBuffer);
If AContext.Data <> nil Then Begin
  Client := Pointer(AContext.Data);
  Clients.Remove(Client);
  Client.Free;
  AContext.Data := nil;
End;
end;

在 idtcpconnect 中调用 getmem 时会发生错误,我认为我做错了,我不确定如何为每个上下文设置一个唯一的全局变量。

4

3 回答 3

5

TIdTCPServer.ContextClass确保在运行时激活服务器之前将类类型分配给属性,例如:

procedure TMainFrm.FormCreate(Sender: TObject);
begin
  MainSck.ContextClass := TMyContext;
end;
于 2013-01-19T00:30:56.633 回答
2

您不能将 [已创建] 对象实例的类更改为不同的类型。该对象属于它在创建时实例化的类。

您可以安全地将任何对象转换为它自己的类或它继承的任何类,因为该对象是该类的。在硬演员中(就像你正在做的那样),你告诉编译器你知道你在做什么,例如:

type
  TMyButton: TButton
  public
    FMyField: array[1..50] of byte;
  end;

var
  Button: TButton;
begin
  //next line is valid, a variable of type TButton can reference any object 
  //inheriting from TButton or a TButton instance directly
  Button := TMyButton.Create(nil);
  //next line contains a valid cast, because Button contains a reference to
  //a instance of TMyButton
  TMyButton(Button).FMyField[10] := 5;
  //valid, a TButton variable referencing a TButton instance
  Button := TButton.Create(nil);
  //next line is invalid and may cause an AV or in the worst case 
  //you may corrupt memory by doing that
  TMyButton(AButton).FMyField[20] := 5; 
end;

事实上,在您的 OnConnect 事件中,您获得了一个已经创建的 TIdContext 实例(或后代类型)。

如果您希望这个对象属于您的类,您必须首先通过 ContextClass 属性请求服务器创建该类的对象。您必须在服务器的 Active 属性设置为 true 之前执行此操作。

procedure TMyForm.Init;
begin
  MyServer.ContextClass := TMyContext;
  MyServer.Active := True;
end;

最后,如果您有对象引用,则必须在上下文构造函数中创建对象,或者如果您不想浪费内存并且不经常使用它,则添加一个后期创建机制:

TMyContext = class(TIdServerContext)
private
  FQueue: TIdThreadSafeList;
public
  constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TThreadList = nil); override;
  destructor Destroy; override;
  property Queue: TIdThreadSafeList read FQueue;
end;

constructor TMyContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TThreadList = nil);
begin
  inherited;
  FQueue := TIdThreadSafeList.Create(Parameters);
end;

destructor TMyContext.Destroy;
begin
  FQueue.Free;
  inherited;
end;
于 2013-01-19T00:32:13.397 回答
0

呸呸呸呸!我正在为出了什么问题而自杀,我在想 FPacetBuffer 变量并不是每个连接都是唯一的,但是经过大量调试和注释掉代码部分后,我发现了问题,我就像 WHATTT !!!!

在处理登录数据包数据时,我声明了一个 PChar 变量并使用 StrLCopy 将数据复制到它并检索数据的大小,然后我自己为其分配了一个空字符(这就是问题所在)。

Size := (Packet.BufferSize-SizeOf(TLoginPacket));
GetMem(UserName,Size);
StrLCopy(UserName, PChar(Cardinal(Packet)+SizeOf(TLoginPacket)),Size);
UserName[Size] := #0;  <--- This Line here

大小变量在其中保存实际大小 + 2。

感谢所有的帮助家伙:)

于 2013-01-19T02:23:44.127 回答