2009-08-14 55 views
2

从客户端我发送一个字符串到服务器他应该送我回来。这次它是一个由ClientDataSet创建的流。不幸的是接收(或发送??)目前不起作用。德尔福:客户端数据库:EDatabaseError:缺少数据包使用Synapse

Note: I am using Synapse with blocking sockets. The server is multithreaded.

服务器:

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充满数据?

+0

一部分。 – 2009-08-14 10:40:01

+0

下载代码并解决了您的问题 – 2009-08-16 12:30:46

回答

4

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

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

最简单的方式来获得可重现的情况下将会将是:

  1. 留两个TClientDataSets您的服务器上(ClientDataSet1和ClientDataSet2)
  2. 负荷使用提供的数据转换成ClientDataSet1在设计时这样
  3. 保存从ClientDataSet1到.CDS数据文件
  4. 装入.CDS文件到ClientDataSet2在设计时
  5. ClientDataSet2发送到您的客户端

如果它仍然重现,那么在某处发布该.pas/.dfm。 如果不能复制,则开始剪切部分,直到复制。

祝你好运!

--jeroen

编辑:我下载了你的源代码,并得到了他们在2009年德尔福

它包含的几个问题的工作,和突触库包含的问题为好。

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

之前我忘了:我做的第一件事是确保我能使用调试的DCU和禁用优化的编译器选项。这些是深入挖掘问题的宝贵设置。 我没有添加任何清理到您现有的代码,因为我希望它尽可能接近。 但我确实运行过一个源代码格式化程序,因此至少,然后块分别位于不同的行上。

让我们从发送代码开始。

我摆脱你的数据库的连接,复制ClientDataSet1客户服务器,并重构你的SServer单位,无论TTCPSocketDaemonTTCPSocketThrd现在可以访问FClientDataSet:TClientDataSet

这让我有一种可重复的情况下,我以前问。

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

这让我再次查看服务器。 看来你使用分隔文本拆分传入的参数,但在那之后你在代替[0]指的sl.Names如果SL [0]。这在Delphi 2009中失败了,也可能在其他Delphi版本中出现。 除此之外,你不检查一个空小号,所以它每次出去后与列表索引不能越界的。

然后我添加了一些调试代码:我想看看实际发送了什么。 这使我使用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。大小,我想知道你为什么没有在0(零)的特殊情况下它的反应,所以我加了它:LogThis(“收到空流”)

然后我开始调试,并发现那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,其余的只是清理你的代码,并精确地发生在什么时候:大部分时间都是这样。 在这种情况下,流处理需要您仔细观察流位置。

的问候,你的服务器源没有做它的

--jeroen

+0

通过文件使用此方法,它可以按预期完美工作。那么数据传输失败的原因应该在突触sendstream函数中吗? – Acron 2009-08-14 12:10:55

+0

我上传了该项目。您可以手动将突触blcksock添加到您的delphis库搜索路径。 http://www.xup.in/dl,58729270/SServer_MTHRD.zip/ – Acron 2009-08-14 12:29:07

+0

首先:非常感谢。第二:我学到了一些关于流的东西:) – Acron 2009-08-17 00:25:23