刚敲了很多文字,叙述不清,还是发代码吧。问题在代码里写
服务端代码:
unit ServerFrmMainUnit;interfaceuses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, IdTCPServer, IdThreadMgr, IdThreadMgrDefault, IdBaseComponent,
IdComponent;type
PClient = ^TClient;
TClient = record // Object holding data of client (see events)
DNS: string[20]; { Hostname }
Connected, { Time of connect }
LastAction: TDateTime; { Time of last transaction }
Thread: Pointer; { Pointer to thread }
end; TServerFrmMain = class(TForm)
Server: TIdTCPServer;
CBServerActive: TCheckBox;
Protocol: TMemo;
IdThreadMgrDefault1: TIdThreadMgrDefault; procedure CBServerActiveClick(Sender: TObject);
procedure ServerConnect(AThread: TIdPeerThread);
procedure ServerExecute(AThread: TIdPeerThread);
procedure ServerDisconnect(AThread: TIdPeerThread);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction); private public
end;var
ServerFrmMain: TServerFrmMain;
Clients: TThreadList; // Holds the data of all clientsimplementationuses GlobalUnit;{$R *.DFM}procedure TServerFrmMain.CBServerActiveClick(Sender: TObject);
begin
Server.Active := CBServerActive.Checked;
end;procedure TServerFrmMain.ServerConnect(AThread: TIdPeerThread);
var
NewClient: PClient;begin
GetMem(NewClient, SizeOf(TClient)); NewClient.DNS := AThread.Connection.LocalName;
NewClient.Connected := Now;
NewClient.LastAction := NewClient.Connected;
NewClient.Thread := AThread; AThread.Data := TObject(NewClient); try
Clients.LockList.Add(NewClient);
finally
Clients.UnlockList;
end; Protocol.Lines.Add(TimeToStr(Time) + ' Connection from "' + NewClient.DNS +
'"');
end;procedure TServerFrmMain.ServerExecute(AThread: TIdPeerThread);
var
ActClient, RecClient: PClient;
CommBlock, NewCommBlock: TCommBlock;
RecThread: TIdPeerThread;
i: Integer;begin
if not AThread.Terminated and AThread.Connection.Connected then
begin
AThread.Connection.ReadBuffer(CommBlock, SizeOf(CommBlock));
ActClient := PClient(AThread.Data);
ActClient.LastAction := Now; // update the time of last action if (CommBlock.Command = 'MESSAGE') or (CommBlock.Command = 'DIALOG') then
begin // 'MESSAGE': A message was send - forward or broadcast it
// 'DIALOG': A dialog-window shall popup on the recipient's screen
// it's the same code for both commands... if CommBlock.ReceiverName = '' then
begin // no recipient given - broadcast
Protocol.Lines.Add(TimeToStr(Time) + ' Broadcasting ' + CommBlock.Command
+ ': "' + CommBlock.Msg + '"');
NewCommBlock := CommBlock; // nothing to change ;-)) with Clients.LockList do
try
for i := 0 to Count - 1 do // iterate through client-list
begin
RecClient := Items[i]; // get client-object
RecThread := RecClient.Thread; // get client-thread out of it
RecThread.Connection.WriteBuffer(NewCommBlock, SizeOf(NewCommBlock),
True); // send the stuff
end;
finally
Clients.UnlockList;
end;
end
else
begin // receiver given - search him and send it to him
NewCommBlock := CommBlock; // again: nothing to change ;-))
Protocol.Lines.Add(TimeToStr(Time) + ' Sending ' + CommBlock.Command +
' to "' + CommBlock.ReceiverName + '": "' + CommBlock.Msg + '"');
with Clients.LockList do
try
for i := 0 to Count - 1 do
begin
RecClient := Items[i];
if RecClient.DNS = CommBlock.ReceiverName then
// we don't have a login function so we have to use the DNS (Hostname)
begin
RecThread := RecClient.Thread;
RecThread.Connection.WriteBuffer(NewCommBlock,
SizeOf(NewCommBlock), True);
end;
end;
finally
Clients.UnlockList;
end;
end;
end
else
begin // unknown command given
Protocol.Lines.Add(TimeToStr(Time) + ' Unknown command from "' +
CommBlock.MyUserName + '": ' + CommBlock.Command);
NewCommBlock.Command := 'DIALOG';
// the message should popup on the client's screen
NewCommBlock.MyUserName := '[Server]'; // the server's username
NewCommBlock.Msg := 'I don''t understand your command: "' +
CommBlock.Command + '"'; // the message to show
NewCommBlock.ReceiverName := '[return-to-sender]'; // unnecessary AThread.Connection.WriteBuffer(NewCommBlock, SizeOf(NewCommBlock), true);
// and there it goes...
end;
end;
end;procedure TServerFrmMain.ServerDisconnect(AThread: TIdPeerThread);
var
ActClient: PClient;begin
ActClient := PClient(AThread.Data);
Protocol.Lines.Add(TimeToStr(Time) + ' Disconnect from "' + ActClient^.DNS +
'"');
try
Clients.LockList.Remove(ActClient);
finally
Clients.UnlockList;
end;
FreeMem(ActClient);
AThread.Data := nil;
end;procedure TServerFrmMain.FormCreate(Sender: TObject);
begin
Clients := TThreadList.Create;
end;procedure TServerFrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Server.Active := False;
Clients.Free;
end;end.客户端代码:unit ClientFrmMainUnit;interfaceuses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, StdCtrls,
GlobalUnit;type
TClientFrmMain = class(TForm)
CBClientActive: TCheckBox;
IncomingMessages: TMemo;
Label1: TLabel;
Client: TIdTCPClient;
Label2: TLabel;
EditCommand: TComboBox;
Label3: TLabel;
EditMessage: TEdit;
Label4: TLabel;
EditRecipient: TEdit;
ButtonSend: TButton; procedure CBClientActiveClick(Sender: TObject);
procedure ButtonSendClick(Sender: TObject); private public end; TClientHandleThread = class(TThread)
private
CB: TCommBlock;
procedure HandleInput;
protected
procedure Execute; override;
end;var
ClientFrmMain: TClientFrmMain;
ClientHandleThread: TClientHandleThread; // variable (type see above)implementation{$R *.DFM}procedure TClientHandleThread.HandleInput;
begin
if CB.Command = 'MESSAGE' then
ClientFrmMain.IncomingMessages.Lines.Add(CB.MyUserName + ': ' + CB.Msg)
else if CB.Command = 'DIALOG' then
MessageDlg('"' + CB.MyUserName + '" sends you this message:' + #13 + CB.Msg,
mtInformation, [mbOk], 0)
else // unknown command
MessageDlg('Unknown command "' + CB.Command + '" containing this message:' +
#13 + CB.Msg, mtError, [mbOk], 0);
end;procedure TClientHandleThread.Execute;
begin
while not Terminated do
begin
if not ClientFrmMain.Client.Connected then
Terminate
else
try
ClientFrmMain.Client.ReadBuffer(CB, SizeOf(CB));
Synchronize(HandleInput); //??? 问题在这里,这一句什么时候会执行,是不是执行完上一句就会执行此句,
//我理解的是,这个线程函数会一直执行如果没有设Terminated = True,既然会一直执行这个Execute,这个
// while 循环也会一直执行,那么ClientFrmMain.Client.ReadBuffer(CB, SizeOf(CB)); 也会执 行,
// Synchronize(HandleInput) 这一行也会执行,那就是说 客户端在一运行的时候即使不点Send按钮给服务端发
// 数据 也应该执行 HandleInput 过程里的内容啊,最起码会弹出个对话框之类的啊,可事实上他没有执行,
// 这个Synchronize 在这里怎么理解呢??,只知道是线程周步用,在不点发送按钮的前提上跟踪,只走到
// ClientFrmMain.Client.ReadBuffer(CB, SizeOf(CB)); 这一行 程序不再走了,也会执行HandleInput 过程里的内容
// 各位大哥帮看一下啊。谢谢。。
except
end;
end;
end;procedure TClientFrmMain.CBClientActiveClick(Sender: TObject);
begin
if CBClientActive.Checked then
begin
try
Client.Connect(10000); // in Indy < 8.1 leave the parameter away ClientHandleThread := TClientHandleThread.Create(True);
ClientHandleThread.FreeOnTerminate := True;
ClientHandleThread.Resume;
except
on E: Exception do
MessageDlg('Error while connecting:' + #13 + E.Message, mtError, [mbOk],
0);
end;
end
else
begin
ClientHandleThread.Terminate;
Client.Disconnect;
end; ButtonSend.Enabled := Client.Connected;
CBClientActive.Checked := Client.Connected;
end;procedure TClientFrmMain.ButtonSendClick(Sender: TObject);
var
CommBlock: TCommBlock;begin
CommBlock.Command := EditCommand.Text; // assign the data
CommBlock.MyUserName := Client.LocalName;
CommBlock.Msg := EditMessage.Text;
CommBlock.ReceiverName := EditRecipient.Text; Client.WriteBuffer(CommBlock, SizeOf(CommBlock), true);
end;end.
服务端代码:
unit ServerFrmMainUnit;interfaceuses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, IdTCPServer, IdThreadMgr, IdThreadMgrDefault, IdBaseComponent,
IdComponent;type
PClient = ^TClient;
TClient = record // Object holding data of client (see events)
DNS: string[20]; { Hostname }
Connected, { Time of connect }
LastAction: TDateTime; { Time of last transaction }
Thread: Pointer; { Pointer to thread }
end; TServerFrmMain = class(TForm)
Server: TIdTCPServer;
CBServerActive: TCheckBox;
Protocol: TMemo;
IdThreadMgrDefault1: TIdThreadMgrDefault; procedure CBServerActiveClick(Sender: TObject);
procedure ServerConnect(AThread: TIdPeerThread);
procedure ServerExecute(AThread: TIdPeerThread);
procedure ServerDisconnect(AThread: TIdPeerThread);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction); private public
end;var
ServerFrmMain: TServerFrmMain;
Clients: TThreadList; // Holds the data of all clientsimplementationuses GlobalUnit;{$R *.DFM}procedure TServerFrmMain.CBServerActiveClick(Sender: TObject);
begin
Server.Active := CBServerActive.Checked;
end;procedure TServerFrmMain.ServerConnect(AThread: TIdPeerThread);
var
NewClient: PClient;begin
GetMem(NewClient, SizeOf(TClient)); NewClient.DNS := AThread.Connection.LocalName;
NewClient.Connected := Now;
NewClient.LastAction := NewClient.Connected;
NewClient.Thread := AThread; AThread.Data := TObject(NewClient); try
Clients.LockList.Add(NewClient);
finally
Clients.UnlockList;
end; Protocol.Lines.Add(TimeToStr(Time) + ' Connection from "' + NewClient.DNS +
'"');
end;procedure TServerFrmMain.ServerExecute(AThread: TIdPeerThread);
var
ActClient, RecClient: PClient;
CommBlock, NewCommBlock: TCommBlock;
RecThread: TIdPeerThread;
i: Integer;begin
if not AThread.Terminated and AThread.Connection.Connected then
begin
AThread.Connection.ReadBuffer(CommBlock, SizeOf(CommBlock));
ActClient := PClient(AThread.Data);
ActClient.LastAction := Now; // update the time of last action if (CommBlock.Command = 'MESSAGE') or (CommBlock.Command = 'DIALOG') then
begin // 'MESSAGE': A message was send - forward or broadcast it
// 'DIALOG': A dialog-window shall popup on the recipient's screen
// it's the same code for both commands... if CommBlock.ReceiverName = '' then
begin // no recipient given - broadcast
Protocol.Lines.Add(TimeToStr(Time) + ' Broadcasting ' + CommBlock.Command
+ ': "' + CommBlock.Msg + '"');
NewCommBlock := CommBlock; // nothing to change ;-)) with Clients.LockList do
try
for i := 0 to Count - 1 do // iterate through client-list
begin
RecClient := Items[i]; // get client-object
RecThread := RecClient.Thread; // get client-thread out of it
RecThread.Connection.WriteBuffer(NewCommBlock, SizeOf(NewCommBlock),
True); // send the stuff
end;
finally
Clients.UnlockList;
end;
end
else
begin // receiver given - search him and send it to him
NewCommBlock := CommBlock; // again: nothing to change ;-))
Protocol.Lines.Add(TimeToStr(Time) + ' Sending ' + CommBlock.Command +
' to "' + CommBlock.ReceiverName + '": "' + CommBlock.Msg + '"');
with Clients.LockList do
try
for i := 0 to Count - 1 do
begin
RecClient := Items[i];
if RecClient.DNS = CommBlock.ReceiverName then
// we don't have a login function so we have to use the DNS (Hostname)
begin
RecThread := RecClient.Thread;
RecThread.Connection.WriteBuffer(NewCommBlock,
SizeOf(NewCommBlock), True);
end;
end;
finally
Clients.UnlockList;
end;
end;
end
else
begin // unknown command given
Protocol.Lines.Add(TimeToStr(Time) + ' Unknown command from "' +
CommBlock.MyUserName + '": ' + CommBlock.Command);
NewCommBlock.Command := 'DIALOG';
// the message should popup on the client's screen
NewCommBlock.MyUserName := '[Server]'; // the server's username
NewCommBlock.Msg := 'I don''t understand your command: "' +
CommBlock.Command + '"'; // the message to show
NewCommBlock.ReceiverName := '[return-to-sender]'; // unnecessary AThread.Connection.WriteBuffer(NewCommBlock, SizeOf(NewCommBlock), true);
// and there it goes...
end;
end;
end;procedure TServerFrmMain.ServerDisconnect(AThread: TIdPeerThread);
var
ActClient: PClient;begin
ActClient := PClient(AThread.Data);
Protocol.Lines.Add(TimeToStr(Time) + ' Disconnect from "' + ActClient^.DNS +
'"');
try
Clients.LockList.Remove(ActClient);
finally
Clients.UnlockList;
end;
FreeMem(ActClient);
AThread.Data := nil;
end;procedure TServerFrmMain.FormCreate(Sender: TObject);
begin
Clients := TThreadList.Create;
end;procedure TServerFrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Server.Active := False;
Clients.Free;
end;end.客户端代码:unit ClientFrmMainUnit;interfaceuses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, StdCtrls,
GlobalUnit;type
TClientFrmMain = class(TForm)
CBClientActive: TCheckBox;
IncomingMessages: TMemo;
Label1: TLabel;
Client: TIdTCPClient;
Label2: TLabel;
EditCommand: TComboBox;
Label3: TLabel;
EditMessage: TEdit;
Label4: TLabel;
EditRecipient: TEdit;
ButtonSend: TButton; procedure CBClientActiveClick(Sender: TObject);
procedure ButtonSendClick(Sender: TObject); private public end; TClientHandleThread = class(TThread)
private
CB: TCommBlock;
procedure HandleInput;
protected
procedure Execute; override;
end;var
ClientFrmMain: TClientFrmMain;
ClientHandleThread: TClientHandleThread; // variable (type see above)implementation{$R *.DFM}procedure TClientHandleThread.HandleInput;
begin
if CB.Command = 'MESSAGE' then
ClientFrmMain.IncomingMessages.Lines.Add(CB.MyUserName + ': ' + CB.Msg)
else if CB.Command = 'DIALOG' then
MessageDlg('"' + CB.MyUserName + '" sends you this message:' + #13 + CB.Msg,
mtInformation, [mbOk], 0)
else // unknown command
MessageDlg('Unknown command "' + CB.Command + '" containing this message:' +
#13 + CB.Msg, mtError, [mbOk], 0);
end;procedure TClientHandleThread.Execute;
begin
while not Terminated do
begin
if not ClientFrmMain.Client.Connected then
Terminate
else
try
ClientFrmMain.Client.ReadBuffer(CB, SizeOf(CB));
Synchronize(HandleInput); //??? 问题在这里,这一句什么时候会执行,是不是执行完上一句就会执行此句,
//我理解的是,这个线程函数会一直执行如果没有设Terminated = True,既然会一直执行这个Execute,这个
// while 循环也会一直执行,那么ClientFrmMain.Client.ReadBuffer(CB, SizeOf(CB)); 也会执 行,
// Synchronize(HandleInput) 这一行也会执行,那就是说 客户端在一运行的时候即使不点Send按钮给服务端发
// 数据 也应该执行 HandleInput 过程里的内容啊,最起码会弹出个对话框之类的啊,可事实上他没有执行,
// 这个Synchronize 在这里怎么理解呢??,只知道是线程周步用,在不点发送按钮的前提上跟踪,只走到
// ClientFrmMain.Client.ReadBuffer(CB, SizeOf(CB)); 这一行 程序不再走了,也会执行HandleInput 过程里的内容
// 各位大哥帮看一下啊。谢谢。。
except
end;
end;
end;procedure TClientFrmMain.CBClientActiveClick(Sender: TObject);
begin
if CBClientActive.Checked then
begin
try
Client.Connect(10000); // in Indy < 8.1 leave the parameter away ClientHandleThread := TClientHandleThread.Create(True);
ClientHandleThread.FreeOnTerminate := True;
ClientHandleThread.Resume;
except
on E: Exception do
MessageDlg('Error while connecting:' + #13 + E.Message, mtError, [mbOk],
0);
end;
end
else
begin
ClientHandleThread.Terminate;
Client.Disconnect;
end; ButtonSend.Enabled := Client.Connected;
CBClientActive.Checked := Client.Connected;
end;procedure TClientFrmMain.ButtonSendClick(Sender: TObject);
var
CommBlock: TCommBlock;begin
CommBlock.Command := EditCommand.Text; // assign the data
CommBlock.MyUserName := Client.LocalName;
CommBlock.Msg := EditMessage.Text;
CommBlock.ReceiverName := EditRecipient.Text; Client.WriteBuffer(CommBlock, SizeOf(CommBlock), true);
end;end.
客户端,服务端大体思路了解,但迷惑的是,如果客户端执行了IdTcpClient.Connect后,即使没有给服务端发送数据,我理解的是也应该会执行 TClientHandleThread.Execute 这个线程函数,而这个函数里 有 Synchronize(HandleInput); 这一句代码,而HandleInput里面 有一句 弹出对话框MessageDlg()代码。也就是说,我客户端即使不点发送按钮给服务端,客户端在IdTcpClient.Connect后也得会弹出个对话框啊,可客户端程序好像没有执行HandleInput这个过程,为什么呢。
刚又测试了一下把 TClientHandleThread.Execute; 这过程里的 ClientFrmMain.Client.ReadBuffer(CB, SizeOf(CB)); 注释掉 就可以了,各位帮解释一下啊。不太明白了啊!!!
必需接收到数据才会执行下一行代码 ,如果服务器没返回数据,会一直等在那
这是阻塞的
必需接收到数据才会执行下一行代码 ,如果服务器没返回数据,会一直等在那
这是阻塞的
//---------------------------------------------------------------
上面的说的很正确。而且必需是收到你指定的大小或者客户端断开才会继续
否则会一直阻塞在这里.