您正在ClientDataSet1變量中創建一個新實例,但表單上的其他組件都不會引用該實例。
這不是「缺少數據提供程序或數據包」錯誤消息的原因:爲了找到該問題,您應該發佈一個可重現的案例。
最簡單的方式來獲得可重現的情況下將會將是:
- 留兩個TClientDataSets您的服務器上(ClientDataSet1和ClientDataSet2)
- 負荷使用提供的數據轉換成ClientDataSet1在設計時這樣
- 保存從ClientDataSet1到.CDS數據文件
- 裝入.CDS文件到ClientDataSet2在設計時
- ClientDataSet2發送到您的客戶端
如果它仍然重現,那麼在某處發佈該.pas/.dfm。 如果不能複製,則開始剪切部分,直到複製。
祝你好運!
--jeroen
編輯:我下載了你的源代碼,並得到了他們在2009年德爾福
它包含的幾個問題的工作,和突觸庫包含的問題爲好。
大部分問題歸結爲不準確。 我已經有了一個模糊的感覺,基於你的源代碼格式,你爲你的東西使用的命名約定的組合,以及缺乏釋放分配的對象。
之前我忘了:我做的第一件事是確保我能使用調試的DCU和禁用優化的編譯器選項。這些是深入挖掘問題的寶貴設置。 我沒有添加任何清理到您現有的代碼,因爲我希望它儘可能接近。 但我確實運行過一個源代碼格式化程序,因此至少,然後塊分別位於不同的行上。
讓我們從發送代碼開始。
我擺脫你的數據庫的連接,複製ClientDataSet1從客戶到服務器,並重構你的SServer單位,無論TTCPSocketDaemon和TTCPSocketThrd現在可以訪問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組件中的副本)。 有我發現了兩兩件事:
- 在發送的流,它被編碼在第一個4個字節
- 編碼是在一個不同的字節順序進行比解碼的流的長度
這無疑是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
一部分。 – 2009-08-14 10:40:01
下載代碼並解決了您的問題 – 2009-08-16 12:30:46