想了又想,还是没明白咋回事!问题如下:1、IdTCPServerExecute过称中,
Client := TUser(AContext.Data);
........
Memo.Lines.Add(Format('IP:%s的%s用户说:"%s"', [Client.IP,Client.UserName, Msg]));自定义类Clinet(Tuser)的属性,IP,UserName,难道是在IdTCPServerConnect过程中
Client := TUser.Create(AContext.Binding.PeerIP, AUserName,AContext.Binding.PeerPort, AContext);
赋值的吗?2、如果上面的是对的,哪么IdTCPServerExecute,IdTCPServerConnect,IdTCPServerDisconnect,3个过程中的参数AContext
都是同一个?USER类的代码如下type
TUser = class(TObject)
private
FIP, FUserName: string;
FPort: Integer;
FSelected: Boolean;
FContext: TIdContext;
FLock: TCriticalSection;
FCommandQueues: TThreadList;
FListItem: TListItem;
FWorkSize: Int64;
procedure SetContext(const Value: TIdContext);
procedure SetListItem(const Value: TListItem);
protected
procedure DoWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
public
constructor Create(const AIP, AUserName: string; APort: Integer;
AContext: TIdContext); reintroduce;
destructor Destroy; override;
procedure Lock;
procedure Unlock;
property IP: string read FIP;
property Port: Integer read FPort;
property UserName: string read FUserName;
property Selected: Boolean read FSelected write FSelected;
property Context: TIdContext read FContext write SetContext;
property CommandQueues: TThreadList read FCommandQueues;
property ListItem: TListItem read FListItem write SetListItem;
end;implementation{$R *.dfm}function GetPercentFrom(Int, Total: Int64): Double;
begin
if (Int = 0) or (Total = 0) then
Result := 0
else if Int = Total then
Result := 100
else
begin
Result := Int / (Total / 100);
end;
end;{ TUser }
constructor TUser.Create(const AIP, AUserName: string; APort: Integer;
AContext: TIdContext);
begin
FLock := TCriticalSection.Create;
FIP := AIP;
FPort := APort;
FUserName := AUserName;
Context := AContext;
FCommandQueues := TThreadList.Create;
end;destructor TUser.Destroy;
begin
FCommandQueues.Free;
FLock.Free;
inherited;
end;procedure TUser.DoWork(ASender: TObject; AWorkMode: TWorkMode;
AWorkCount: Int64);
var
NewPercent: string;
begin
if ListItem <> nil then
begin
NewPercent := IntToStr(Trunc(GetPercentFrom(AWorkCount, FWorkSize))) + '%';
if ListItem.SubItems[1] <> NewPercent then
ListItem.SubItems[1] := NewPercent;
end;
end;procedure TUser.Lock;
begin
FLock.Enter;
end;procedure TUser.Unlock;
begin
FLock.Leave;
end;procedure TUser.SetContext(const Value: TIdContext);
begin
if FContext <> nil then
FContext.Data := nil;
if Value <> nil then
Value.Data := Self;
FContext := Value;
end;procedure TUser.SetListItem(const Value: TListItem);
begin
if FListItem <> Value then
FListItem := Value;
if Value <> nil then
Value.Data := Self;
end;窗体的部分代码如下:
procedure TFIndex.IdTCPServerConnect(AContext: TIdContext);
var
Client: TUser;
AUserName: string;
lst: TList;
I: Integer;
begin
AUserName := AContext.Connection.IOHandler.ReadLn;
if AUserName = '' then
begin
AContext.Connection.IOHandler.WriteLn('NO_USER_NAME');
AContext.Connection.Disconnect;
Exit;
end;
lst := FUsers.LockList;
try
for I := 0 to lst.Count - 1 do
if SameText(TUser(lst[I]).UserName, AUserName) then
begin
AContext.Connection.IOHandler.WriteLn('USER_ALREADY_LOGINED');
AContext.Connection.Disconnect;
Exit;
end;
Client := TUser.Create(AContext.Binding.PeerIP, AUserName,
AContext.Binding.PeerPort, AContext);
lst.Add(Client);
Client.Lock;
try
Client.Context.Connection.IOHandler.WriteLn('LOGINED');
finally
Client.Unlock;
end;
finally
FUsers.UnlockList;
end;
SendMessage(FormHanlde, WM_REFRESH_USERS, Ord(rpAppendItem), Integer(Client));
end;procedure TFIndex.IdTCPServerDisconnect(AContext: TIdContext);
var
Client: TUser;
begin
Client := TUser(AContext.Data);
if Client <> nil then
begin
Client.Lock;
try
Client.Context := nil;
finally
Client.Unlock;
end;
FUsers.Remove(Client);
SendMessage(FormHanlde, WM_REFRESH_USERS, Ord(rpDeleteItem),
Integer(Client));
Client.Free;
end;
end;procedure TFIndex.IdTCPServerExecute(AContext: TIdContext);
var
Client: TUser;
Msg, Cmd: string;
cmds: TList;
CmdRec: PCmdRec;
begin
Client := TUser(AContext.Data);
if Client <> nil then
begin
Client.Lock;
try
AContext.Connection.IOHandler.CheckForDataOnSource(250);
if not AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
Msg := AContext.Connection.IOHandler.ReadLn;
if FormHanlde <> 0 then
begin
LockUI;
try
Memo.Lines.Add(Format('IP:%s的%s用户说:"%s"', [Client.IP,
Client.UserName, Msg]));
finally
UnlockUI;
end;
end;
end;
cmds := Client.CommandQueues.LockList;
try
if cmds.Count > 0 then
begin
CmdRec := cmds[0];
Cmd := CmdRec.Cmd;
cmds.Delete(0);
Dispose(CmdRec);
end
else
Cmd := '';
finally
Client.CommandQueues.UnlockList;
end;
if Cmd = '' then
Exit;
if Pos('SENDF', Cmd) = 1 then
begin
if FormHanlde <> 0 then
begin
LockUI;
try
Memo.Lines.Add(Format('发送文件到%s(IP:%s)', [Client.UserName,
Client.IP]));
finally
UnlockUI;
end;
end;
// SendFileToUser(Client,Trim(Copy(Cmd,6,Length(Cmd))));
end
else if Pos('SENDT', Cmd) = 1 then
begin
if FormHanlde <> 0 then
begin
LockUI;
try
Memo.Lines.Add(Format('发送文本信息到%s(IP:%s),文本内容:"%s"',
[Client.UserName, Client.IP,
Trim(Copy(Cmd, 6, Length(Cmd)))]));
finally
UnlockUI;
end;
end;
SendTextToUser(Client, Trim(Copy(Cmd, 6, Length(Cmd))));
end;
finally
Client.Unlock;
end;
end;
end;
A、IdTCPServerConnect 过程中,
Context.Data := TUser.Create(AContext);B、IdTCPServerDisconnect 过程中,
FreeAndNil(Context.Data);C、IdTCPServerExecute过称中,
Client := TUser(AContext.Data);
Client.UserName = "myname"; // 当然要在登录后赋值结论:Context.Data 是用户数据指针,未赋值时,该数据为 nil , 在不同的事件函数中,可快速地指向用户数据地址,仅此而已。
恩,其实我一直不明白的就是IdTCPServerExecute过程中,Client := TUser(AContext.Data);
后,Client.UserName 会有值。
我开始以为,这是强行转换。
后来才明白,这是个指针对象,在IdTCPServerConnect给了值。在这里,只不过转换下形式,引用这个指针对象而已。
所以,我开始猜测,难道AContext,在3个过程中,是同一个?只有这样,才能说的过去的……看来,我猜测对了!
这时候,与客户端与服务器联系的方式就是通过这个acontext,有几个客户端连接,就有几个acontext,你要跟哪个客户端“对话”就使用对应的context,实际完成“对话”的线程在Indy10里被“封装”起来,采用线程池管理,比如有10个客户端与服务器连接,那么相应就会有10个idcontext,而线程池可能有5个(也可能更少或者更多)线程完成这些acontext的“对话”。因此管理好这些连接的idcontext是idtcp编程的关键,但是仅仅idcontext又是不够的,还需要连接的客户端更多的信息,所以通常就自定义一个TUser类来记录连接的客户端更多的信息,然后把acontext.data指针指示这个TUser对象。这样客户端用户信息就完整了。