2009-08-14 48 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