从客户端,我将字符串发送到服务器,他应该把我发送回去。这次是由客户端创建的流。不幸的是,目前接收(或发送??)不起作用。

注意:我正在使用Synapse与阻塞插座。服务器是多线程。

服务器:

procedure TTCPSocketThrd.Execute;
var s: String;
    strm: TMemoryStream;
    ADO_QUERY: TADOQuery;
    DS_PROV: TDataSetProvider;
    DS_CLIENT: TClientDataSet;
begin
    CoInitialize(nil);
    Sock := TTCPBlockSocket.Create;
  try
    Sock.Socket := CSock;
    Sock.GetSins;
    with Sock do
        begin
        repeat
        if terminated then break;
          //if within 60s no data is input, close connection.
          s := RecvTerminated(60000,'|');
          if s = 'getBENUds' then
            begin
              //ini ADO_QUERY
            ADO_QUERY := TADOQuery.Create(Form1);                    
                ADO_QUERY.ConnectionString := 'private :)';
                ADO_QUERY.SQL.Clear;
                ADO_QUERY.SQL.Add('sql_query');
                ADO_QUERY.Open;
              //ini DS_PROV
                DS_PROV := TDataSetProvider.Create(ADO_QUERY);
                DS_PROV.DataSet := ADO_QUERY;
              //ini DS_CLIENT
                DS_CLIENT := TClientDataSet.Create(ADO_QUERY);
                DS_CLIENT.ProviderName := 'DS_PROV';
                DS_CLIENT.SetProvider(DS_PROV);
              //DSCLIENTDATASET bauen
                strm := TMemoryStream.Create;;
                DS_CLIENT.Open;

                DS_CLIENT.SaveToStream(strm);
                SendStream(strm);
             end;

客户端:

procedure TForm1.btnConnectClick(Sender: TObject);
begin
  CSocket := TTCPBlockSocket.Create;
  strmReply := TMemoryStream.Create;
  ClientDataSet1 := TClientDataSet.Create(Form1);
    try
    CSocket.Connect('ip', 'port');
    if CSocket.LastError = 0 then
    begin
      //Sending to the server I want data
      CSocket.SendString('getBENUds|');
      if CSocket.LastError = 0 then
        begin
            //Waiting for data
            //Receiving the Stream in strmReply, 5000ms timeout
            CSocket.RecvStream(strmReply, 5000);
            //Loading the data into ClientDataSet
            ClientDataSet1.LoadFromStream(strmReply);
            ClientDataSet1.Open;
        end;
    end;
  except
    on E:Exception do
        ShowMessage(E.Message);
    end;
  CSocket.Free;
end;

现在,每当我启动服务器并单击客户端上的“连接”按钮时,它应该将数据集传输到客户端,并且TDBGRID应该栩栩如生。

不幸的是,这没有发生。取而代之的是,我得到标题中所述的错误:缺少数据提供商或数据包。但是数据提供商设置为DataSetProvider1(在对象检查器中)。

如何使客户端TDBGRID充满数据的工作?

有帮助吗?

解决方案

您正在clientdataset1变量内创建一个新实例,但是表单上的其他组件都不会参考。

这不是“丢失数据提供商或DataPackage”错误消息的原因:为了发现您应该发布可重复的情况。

获得可再现案例的最简单方法是:

  1. 将两个TCLIENTDATASET放在服务器上(ClientDataSet1和ClientDataSet2)
  2. 使用提供商等在设计时间将数据加载到该客户端DataSet1中
  3. 将该数据从客户clientdataset1保存到.cds文件
  4. 在设计时间将.cds文件加载到clientdataset2中
  5. 将客户端DataSet2发送给您的客户端

如果仍然复制,请在某个地方发布.pas/.dfm。如果不复制,请开始切割部分,直到重现为止。

祝你好运!

- 杰罗恩

编辑:我下载了您的资源,并让他们在Delphi 2009中工作。

它包含一些问题,突触库也包含一个问题。

您的大多数问题都不精确。根据您的源代码格式,您为您使用的命名约定的组合以及缺乏释放分配的对象,我已经对此感到模糊的感觉。

在我忘记之前:我做的第一件事就是确保我启用了 使用DEBUG DCUS 并禁用 优化 在编译器选项中。这些是无价的设置,可深入研究问题。我没有在您的现有代码中添加任何清理,因为我希望它尽可能接近。但是我确实运行了一个源代码格式器,所以至少 然后阻止 在单独的线上。

让我们从发送代码开始。

我摆脱了您的数据库连接,复制了 ClientDataSet1 来自 客户服务器, 并重构 SSERVER 这两个单位 ttcpsocketdaemonttcpsocketthrd 现在可以访问 fclientDataset:tclientDataset

这给了我以前要求的一种可再现的案例。

然后我开始运行客户端。似乎在那里 logthis(strmreply.size); 输出0(零!),因此没有收到任何东西。

这使我再次查看服务器。看来您是在使用划界文本分配传入的论点,但是在此之后 sl.names [0 代替 如果SL [0. 。在2009年的Delphi中,这也可能失败,也可能在其他Delphi版本中。除此之外,您没有检查空的 s, ,所以它会失败 列表索引越界 每次出去之后。

然后我添加了一些调试代码:我想看看实际发送的内容。这导致我使用XML代替二进制流媒体 s_client.savetostream(strm,dfxmlutf8); 并将缓冲区复制到本地文件,因此我可以观看该文件。该文件还可以:它似乎是客户端DataSet的有效XML表示。

最后,我注意到您正在使用 sendbuffer 在服务器中,以及 recvstream 在客户中。这些不匹配:您必须选择,因此要么在连接的两侧使用缓冲区或流!我选择了溪流。

因此,发送代码成为以下:

procedure TTCPSocketThrd.Execute;
var
  s: string;
  strm: TMemoryStream;
  ADO_QUERY: TADOQuery;
  DS_PROV: TDataSetProvider;
  DS_CLIENT: TClientDataSet;
  sl: TStringList;
  adoconnstr: string;
  CDSStream: TFileStream;
begin
  CoInitialize(nil);
  Sock := TTCPBlockSocket.Create;
  adoconnstr := 'POST YOUR CONNECTION STRING HERE, IF TOO LONG adoconnstr := adoconnstr + ''nwestring''';
  try
    Sock.Socket := CSock;
    sl := TStringList.Create;
    Sock.GetSins;
    with Sock do
    begin
      repeat
        if terminated then
          break;
        s := RecvTerminated(60000, '|');

        if s = '' then
          Exit;

        //Den Text mit Leerzeichen splitten zur besseren Verarbeitung
        sl.Delimiter := ' ';
        sl.DelimitedText := s;

        LogThis(sl.Names[0]);
        if sl[0] = 'getBENUds' then // jpl: not sl.Names[0] !!!!
        begin
          //          //ini ADO_QUERY
          //          ADO_QUERY := TADOQuery.Create(Form1);
          //          ADO_QUERY.ConnectionString := adoconnstr;
          //          ADO_QUERY.SQL.Clear;
          //          ADO_QUERY.SQL.Add('SELECT * FROM BENU');
          //          ADO_QUERY.Open;
          //          LogThis('ADO_QUERY fertig');
          //          //ini DS_PROV
          //          DS_PROV := TDataSetProvider.Create(ADO_QUERY);
          //          DS_PROV.DataSet := ADO_QUERY;
          //          LogThis('DS_DSPROV fertig');
          //          //ini DS_CLIENT
          //          DS_CLIENT := TClientDataSet.Create(ADO_QUERY);
          //          DS_CLIENT.ProviderName := 'DS_PROV';
          //          DS_CLIENT.SetProvider(DS_PROV);
          DS_CLIENT := FClientDataSet;
          LogThis('DS_CLIENT fertig');
          //DSCLIENTDATASET bauen
          strm := TMemoryStream.Create;
          DS_CLIENT.Open;
          //DS_CLIENT.Open;
          DS_CLIENT.SaveToStream(strm, dfXMLUTF8); // jpl: easier debugging than binary
          strm.Seek(0, soBeginning);
          SendStream(strm); // jpl: not SendBuffer(strm.Memory, strm.Size);  !!!

          strm.Seek(0, soBeginning);
          CDSStream := TFileStream.Create('Server.cds', fmCreate);
          CDSStream.CopyFrom(strm, strm.Size);
          CDSStream.Free();

          LogThis('gesendet');
        end
        else if sl[0] = 'validuser' then // jpl: not sl.Names[0] !!!!
        begin
          //do stuff
        end;
        if lastError <> 0 then
          break;
      until false;
      Form1.Memo1.Lines.Add('down');
    end;
  finally
    Sock.Free;
  end;

end;

因此,我去重新审视接收代码。既然您已经登录了 strmreply.size, ,我想知道为什么您没有在0(零)的特殊情况下做出反应,所以我添加了: logthis(“接收到空的流”)

然后我开始调试,发现 strmreply.size 每次仍然是0(零)。因此,我开始挖掘突触代码(我采用了已经包含的副本 哈巴里 组件)我最近一直在使用。在那里我发现了两件事:

  1. 发送流后,它正在编码前4个字节中的流的长度
  2. 编码以与解码不同的字节顺序完成

毫无疑问,这是突触的错误,因此请随时向他们报告。结果是 recvstream 不是 sendstream, , 但 recvstreamindy 是。

在所有这些更改之后,它仍然无效。原因是 clientdataset1.loadfromstream(strmreply); 开始从流中的当前位置读取(您可以自己检查一下;核心加载逻辑在 tcustomclientdataset.readdatapacket)。所以看来您忘了添加 strmreply.seek(0,sobeginning);, ,在我添加之后,它突然起作用。

因此,接收代码是:

procedure TForm1.btnConnectClick(Sender: TObject);
var
  CDSStream: TFileStream;
begin
  CSocket := TTCPBlockSocket.Create;
  strmReply := TMemoryStream.Create;
  ClientDataSet1 := TClientDataSet.Create(Form1);
  try
    //      CSocket.Connect('10.100.105.174', '12345');
    CSocket.Connect('127.0.0.1', '12345');
    if CSocket.LastError = 0 then
    begin
      LogThis('verbunden');
      CSocket.SendString('getBENUds|');
      LogThis('''getBENUds|'' gesendet');
      if CSocket.LastError = 0 then
      begin
        CSocket.RecvStreamIndy(strmReply, 50000); // jpl: this fails, because SendStream defaults to Indy: CSocket.RecvStream(strmReply, 50000);
        LogThis(strmReply.Size);

        if strmReply.Size = 0 then
          LogThis('empty stream received')
        else
        begin
          strmReply.Seek(0, soBeginning);
          CDSStream := TFileStream.Create('Client.cds', fmCreate);
          CDSStream.CopyFrom(strmReply, strmReply.Size);
          CDSStream.Free();

          strmReply.Seek(0, soBeginning); // jpl: always make sure that the stream position is right!
          ClientDataSet1.LoadFromStream(strmReply);
          ClientDataSet1.Open;
        end;
      end;
    end;
  except
    on E: Exception do
      ShowMessage(E.Message);
  end;
  CSocket.Free;
end;

最后,解决您的问题并不难。最难的部分是发现 recvstreamindy, ,其余的仅仅是对您的代码的清理,并且在大多数情况下发生的情况下会进行精确。在这种情况下,流处理需要您仔细观察流的位置。

问候,

- 杰罗恩

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top