问题:ServerSocket 采用stThreadBlocking方式,如何给指定的客户端发送数据,如何断开指定的Client?具体描述:当有三个客户端连接进来时,服务端可以显示三个连接进来的客户端,此时我如何能关闭指定(或选定)的已连接进来的客户端,或者说我如果才能向指定(或选定)的客户端发送相关的指令。说明:这里不考虑客户端的软件问题,客户端是一个产品。请各位帮忙看看如何可以实现我的那种方法。个人曾想过使用动态数组定义SvrSocketThr : array of TClientThread,可是不知道如何来做,因为客户端连接进来的个数是不定的,今天可能是三个客户端连接进来,明天也许是二十个客户端连接进来。而且在连接过程有可能断线重连。还请各个帮忙看看...
部份的源码如下://在主窗体文件中的部份代码如下:
var  //在这里定义窗体的全局变量
  frmMain: TfrmMain;
  SvrSocketThr :TClientThread;procedure TfrmMain.SSGetThread(Sender: TObject;
  ClientSocket: TServerClientWinSocket;
  var SocketThread: TServerClientThread);
begin
  // Add Client to lvClient compoment
  AddUser(ClientSocket);
  // Create a new thread for connection
  SvrSocketThr := TClientThread.Create(False, ClientSocket);
  SocketThread := SvrSocketThr;
end;//线程类中的代码如下:
unit UnitTClientThread;interfaceuses
  Classes,SysUtils, Messages,ScktComp;type
  TClientThread = class(TServerClientThread)
  private
    { Private declarations }
    FRecText : PChar;
    procedure SetRecText(const Value: PChar);
    procedure ShowMmoReceiveResult();
    procedure DeleteUser();
    procedure DisConnectClientSocket();
    procedure ReceiveClientData();
  protected
    procedure Execute; override;
  public
    SocketStream: TWinSocketStream;
    property RecText:PChar read FRecText write SetRecText;
  end;implementationuses UnitMain;procedure TClientThread.Execute;
Var
  Data: Array[0..1023] Of char;
Begin
  { Place thread code here }
  While (not Terminated) and (ClientSocket.Connected) Do
  Try
    FreeOnTerminate := True;  //线程执行完之后自动关闭并释放线程
    SocketStream := TWinSocketStream.Create(ClientSocket,30000);
    Try
      FillChar(Data, SizeOf(Data), 0);
      if SocketStream.WaitForData(30000) then  // give the client 30 seconds to start writing
      begin
        if Terminated then  //判断是线断是否被关闭
          DisConnectClientSocket //Close Socket
        else
        begin
          if SocketStream.Read(Data, SizeOf(Data)) = 0 then //if can't read in 60 seconds
            DisConnectClientSocket //Close Socket
          else
            ReceiveClientData(); // Receive Data
        end;  
      end
      else
        DisConnectClientSocket; //Close Socket
    Finally
      SocketStream.Free;
    End;
  Except
    HandleException;
  End;
End;procedure TClientThread.ShowMmoReceiveResult();
begin
  frmMain.mmoReceive.Lines.Add(RecText);
end;procedure TClientThread.SetRecText(const Value: PChar);
begin
  FRecText := Value;
end;procedure TClientThread.DeleteUser();
var
  i:Integer;
begin
  for i:=0 to frmMain.lvClient.Items.Count-1 do
   begin
     if frmMain.lvClient.Items[i].Caption=ClientSocket.RemoteAddress then
     begin
       frmMain.lvClient.Items.Item[i].delete;
       frmMain.mmoReceive.Lines.add('时间:'+datetimetostr(now())+' ===== IP为:'+ ClientSocket.RemoteAddress + ':' + IntToStr(ClientSocket.RemotePort) +' 的客户端与服务器断开连接!');
       Break;
     end;
   end;
end;procedure TClientThread.DisConnectClientSocket();
begin
  // If we didn't get any data after xx seconds then close the connection
  ClientSocket.SendText('SETUP END'+#13#10);
  //Wait a little time to allow sending of text before disconnect
  sleep(1);
  // Delete Client to lvClient compoment
  //Synchronize(DeleteUser);
  DeleteUser;
  // Close ClientSocket
  ClientSocket.Close;
  Terminate;
end;procedure TClientThread.ReceiveClientData();
beginend;    end.

解决方案 »

  1.   

    为每一个连接声明一个类frmMain.lvClient.Items[i].data 指向这个类在断开的时候可以取类的定义
      

  2.   

    connect 的时候建立1个回话信息。这样你就可以方便的操作了。
      

  3.   


    我根据你说的方法定义了一个类如下:TClientThreadData = record
        CSocketThread : TServerClientThread;
        ClientSocket : TServerClientWinSocket;
        ClientIP : string;
     end;
    我在这里的指向如下:procedure TfrmMain.SSGetThread(Sender: TObject;
      ClientSocket: TServerClientWinSocket;
      var SocketThread: TServerClientThread);
    begin
      // Add Client to lvClient compoment
      AddUser(ClientSocket,SocketThread);
      // Create a new thread for connection
      SvrSocketThr := TClientThread.Create(False, ClientSocket);
      SocketThread := SvrSocketThr;
    end;procedure TfrmMain.AddUser(ClientSocket : TServerClientWinSocket;SocketThread: TServerClientThread);
    var
     lvItem:TlistItem;
     ClientData : ^TClientThreadData;
     i : Integer;
    begin
      lvItem:=lvClient.Items.add;
      lvItem.Caption := ClientSocket.RemoteAddress;
      New(ClientData);
      ClientData.CSocketThread := SocketThread;
      ClientData.ClientSocket := ClientSocket;
      ClientData.ClientIP := lvItem.Caption;
      lvItem.Data := ClientData;
    end;
    根据上面的指定工作我进行断开的代码如下:procedure TfrmMain.btnUnConnectClick(Sender: TObject);
    var
      i : Integer;
      Data: Array[0..1023] Of char;
    begin
      for i:=0 to lvClient.Items.Count-1 do
      begin
        if TClientThreadData(lvClient.Selected.Data^).ClientIP = lvClient.Items[i].Caption then
        begin
          //发送断开指令
          TClientThreadData(lvClient.Selected.Data^).ClientSocket.SendText('SETUP END'+#13#10);
          Sleep(1);
          //断开连接
          TClientThreadData(lvClient.Selected.Data^).ClientSocket.Close;
          //释放线程
          TClientThreadData(lvClient.Selected.Data^).CSocketThread.Terminate;
          //删除节点
          lvClient.Items[i].Delete;
          Break;
        end;  
      end;
    end;
    在断开的代码中执行到TClientThreadData(lvClient.Selected.Data^).ClientSocket.Close语句时提示客户端线程类中读取数据时出错。客户端线程类的语句如下(断开时执行到这句出错):
    if SocketStream.Read(Data, SizeOf(Data)) = 0 then //if can't read in 60 seconds
    错误的提示信息:
    Read error 6, The handle is invalid.
      

  4.   

    根据上面的情况我停止进行监听的时候会出现如下代码:
    Project GPRSServer.exe raised exception class EThread with message 'Thread Error: The handle is invalid (6)'. Process stopped. Use Step or Run to continue.停止服务的代码如下:procedure TfrmMain.btnStopClick(Sender: TObject);
    var
      i : integer;
    begin
      for i:=0 to lvClient.Items.Count-1 do
      begin
        TClientThreadData(lvClient.Items[i].Data^).ClientSocket.Close;
        TClientThreadData(lvClient.Items[i].Data^).CSocketThread.Terminate;
      end;  
      //关闭服务器的端口监听服务
      ServerSocket.Active := False;
      //清除连接进来的客户端
      for i:=0 to lvClient.Items.Count-1 do
        lvClient.Items[0].Delete;    //删除客户端显示
      btnStart.Enabled := True;
      btnStop.Enabled := False;
    end;