【问题标题】:Delphi: Clientdataset: EDatabaseError: Missing Data-Package using SynapseDelphi: Clientdataset: EDatabaseError: Missing Data-Package using Synapse
【发布时间】:2010-11-19 14:31:33
【问题描述】:

我正在从客户端向服务器发送一个字符串,他应该将其发回给我。这次它是由 ClientDataSet 创建的流。不幸的是,目前无法接收(或发送??)。

注意:我正在使用带有阻塞的 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;

现在,每当我启动服务器并单击客户端上的连接按钮时,它都应该将 DataSet 传输到客户端,并且 TDBGrid 应该会活跃起来。

不幸的是,这并没有发生。相反,我收到标题中所述的错误:缺少数据提供者或数据包。但数据提供者设置为 DataSetProvider1(在对象检查器中)。

如何使客户端 TDBGrid 充满数据?

【问题讨论】:

  • 你的服务器源的一部分没有成功。
  • 下载代码并解决您的问题

标签: delphi tclientdataset synapse


【解决方案1】:

您正在 ClientDataSet1 变量中创建一个新实例,但表单上的其他组件都不会引用它。

这不是“缺少数据提供者或数据包”错误消息的原因:为了找出这一点,您应该发布一个可重现的案例。

让这个可重现的案例继续下去的最简单方法是:

  1. 在您的服务器上放置两个 TClientDataSet(ClientDataSet1 和 ClientDataSet2)
  2. 在设计时使用提供程序等将数据加载到该 ClientDataSet1 中
  3. 将该数据从 ClientDataSet1 保存到 .CDS 文件中
  4. 在设计时将 .CDS 文件加载到 ClientDataSet2
  5. 通过 ClientDataSet2 发送给您的客户端

如果它仍然可以复制,则将 .pas/.dfm 发布到某处。 如果它不复制,则开始剪切部分直到它复制。

祝你好运!

--杰罗恩

编辑:我下载了您的源代码并让它们在 Delphi 2009 中运行。

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

您的大部分问题归结为不精确。 我已经对此有一种模糊的感觉,基于您的源代码格式、您为您的东西使用的命名约定的混合以及缺乏释放分配的对象。

在我忘记之前:我做的第一件事就是确保我在编译器选项中启用了use debug dcus并禁用了optimizations。这些是深入挖掘问题的宝贵设置。 我没有对您现有的代码添加任何清理,因为我希望它尽可能接近。 但我确实运行了一个源代码格式化程序,以便至少 then 块位于不同的行上。

让我们从发送代码开始。

我摆脱了您的数据库连接,将 ClientDataSet1client 复制到 server,并重构了您的 SServer 单元表示 TTCPSocketDaemonTTCPSocketThrd 现在都可以访问 FClientDataSet: TClientDataSet

这让我得到了我之前要求的一种可重复的案例。

然后我开始运行客户端。 那里似乎 LogThis(strmReply.Size); 正在输出 0(零!),所以它没有收到任何东西。

这让我再次查看服务器。 您似乎使用分隔文本拆分传入的参数,但之后您引用的是 sl.Names[0] 而不是 if sl[0]。这在 Delphi 2009 中失败了,可能在其他 Delphi 版本中也是如此。 除此之外,您没有检查空 s,因此每次超时后它都会失败,并显示 List index out of bounds

然后我添加了一些调试代码:我想看看实际发送了什么。 这导致我使用 XML 而不是二进制使用 S_CLIENT.SaveToStream(strm, dfXMLUTF8); 进行流式传输,并将缓冲区复制到本地文件,以便我可以观看该文件。 文件没问题:它似乎是 ClientDataSet 的有效 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('empty stream received ')

然后我开始调试,发现 strmReply.Size 每次都还是 0(零)。 所以我开始深入研究 Synapse 代码(我拿了我已经拥有的副本,它包含在 Habari 组件中)我最近一直在使用。 在那里我发现了两件事:

  1. 在发送流时,它在前 4 个字节中对流的长度进行编码
  2. 编码的字节顺序与解码不同

这无疑是 Synapse 中的一个错误,因此请随时向他们报告。 结果是 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,剩下的只是清理您的代码并准确了解发生的情况:大部分时间。 在这种情况下,流处理需要您仔细观察流位置。

问候,

--杰罗恩

【讨论】:

  • 通过文件使用此方法,它可以按预期完美运行。那么datatransfer失败的原因应该是synapse sendstream函数的原因吧?
  • 我已经上传了项目。您可以手动将 synapse blcksock 添加到您的 delphis 库搜索路径中。 xup.in/dl,58729270/SServer_MTHRD.zip
  • 第一:非常感谢。第二:我学到了一些关于流的知识:)
  • 没问题。如果您有更多问题,只需将它们发布到 stackoverflow 上,一大群人正在尝试回答它们!顺便说一句:请将我的回答标记为“已接受”。
  • 现在效果更好了。我仍然无法将数据放入 TDBGrid,即使我使用数据源正确设置并连接了 tdbgrid -> 数据源和数据源 -> 客户端数据集。但是,strmReply.Size 使用 RecvStream 而不是 RecvStreamIndy 返回正确的大小。使用 RecvStreamIndy,strmReply.Size 始终为 0。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-06
相关资源
最近更新 更多