刚敲了很多文字,叙述不清,还是发代码吧。问题在代码里写
服务端代码:
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.

解决方案 »

  1.   

    问题我再描述一下,
    客户端,服务端大体思路了解,但迷惑的是,如果客户端执行了IdTcpClient.Connect后,即使没有给服务端发送数据,我理解的是也应该会执行 TClientHandleThread.Execute 这个线程函数,而这个函数里 有 Synchronize(HandleInput); 这一句代码,而HandleInput里面 有一句 弹出对话框MessageDlg()代码。也就是说,我客户端即使不点发送按钮给服务端,客户端在IdTcpClient.Connect后也得会弹出个对话框啊,可客户端程序好像没有执行HandleInput这个过程,为什么呢。
    刚又测试了一下把 TClientHandleThread.Execute; 这过程里的 ClientFrmMain.Client.ReadBuffer(CB, SizeOf(CB)); 注释掉 就可以了,各位帮解释一下啊。不太明白了啊!!!
      

  2.   

    ClientFrmMain.Client.ReadBuffer
    必需接收到数据才会执行下一行代码 ,如果服务器没返回数据,会一直等在那
    这是阻塞的
      

  3.   

    ClientFrmMain.Client.ReadBuffer
    必需接收到数据才会执行下一行代码 ,如果服务器没返回数据,会一直等在那
    这是阻塞的 
    //---------------------------------------------------------------
    上面的说的很正确。而且必需是收到你指定的大小或者客户端断开才会继续
    否则会一直阻塞在这里.