You are creating a new instance inside the ClientDataSet1 variable, but none of the other components on your form will reference to that.
That's not the cause of the "Missing data provider or datapackage" error message: in order to find that out you should post a reproducible case.
The easiest way to get that reproducible case going would be to:
- Put two TClientDataSets on your server (ClientDataSet1 and ClientDataSet2)
- Load the data into that ClientDataSet1 at design time using a provider and such
- Save that data from ClientDataSet1 to a .CDS file
- Load the .CDS file into ClientDataSet2 at design time
- Send over ClientDataSet2 to your client
If it still reproduces, then post that .pas/.dfm somewhere.
If it does not reproduce, then start cutting out portions until it reproduces.
Good luck!
--jeroen
Edit: I downloaded your sources and got them working in Delphi 2009.
It contains a few issues, and Synapse library contains an issues as well.
The majority of your issues come down to being not precise.
I already had a vague feeling for that, based on your source code formatting, the mix of naming conventions you were using for your stuff, and the lack of freeing allocated objects.
Before I forget: the very first thing I did was making sure that I enabled use debug dcus and disable optimizations in the compiler options. Those are invaluable settings for digging deeper into problems.
I didn't add any cleanup to your existing code, as I wanted it to be as close as possible.
But I did run a source code formatter so that at least the then blocks were on separate lines.
Lets start with the sending code.
I got rid of your database connection, copied the ClientDataSet1 from the client to the server, and refactored your SServer unit to that both TTCPSocketDaemon and TTCPSocketThrd now have access to FClientDataSet: TClientDataSet
That got me a kind of reproducible case that I asked for before.
Then I started running the client.
There it appeared that LogThis(strmReply.Size); was outputting 0 (zero!), so it was not receiving anything.
That made me look into the server again.
It seems you were splitting the incoming arguments using Delimited text, but after that you were refering to sl.Names[0] in stead of if sl[0]. That fails in Delphi 2009, and probably in other Delphi versions as well.
In addition to that, you were not checking for an empty s, so it would fail with an List index out of bounds after each time out.
Then I added some debugging code: I wanted to see what was actually being sent.
This caused me to stream using XML in stead of binary using S_CLIENT.SaveToStream(strm, dfXMLUTF8); and to copy the buffer to a file locally, so I could watch that file.
The file was OK: it appeared to be a valid XML representation of a ClientDataSet.
Finally, I noticed that you were using SendBuffer in the server, and RecvStream in the client. Those do not match: you have to choose, so either use buffers or streams at both sides of the connection! I chose for streams.
So the sending code becomes this:
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;
So I went to revisit the receiving code.
Since you were already logging strmReply.Size, I wondered why you didn't react on the special case where it was 0 (zero), so I added it: LogThis('empty stream received')
Then I started debugging, and found out that strmReply.Size was still 0 (zero) every time.
So I started digging into the Synapse code (I took the copy I already had that is included in the Habari components) I have been using lately.
There I found out two things:
- upon sending the stream, it was encoding the length of the stream in the first 4 bytes
- the encoding was done in a different byte order than the decoding
This is no doubt a bug in Synapse, so feel free to report it to them.
The result is that RecvStream is not the counterpart of SendStream, but RecvStreamIndy is.
After all these changes, it still didn't work.
The reason is that ClientDataSet1.LoadFromStream(strmReply); is starting to read from the current position in the stream (you can check that out yourself; the core loading logic is in TCustomClientDataSet.ReadDataPacket).
So it seems you forgot adding strmReply.Seek(0, soBeginning);, and after I added that, it suddenly worked.
So the receiving code is this:
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;
In the end it was not difficult to solve your issues.
The hardest part was finding out about RecvStreamIndy, the rest was a mere cleanup of your code and being precise in what happens when: that took most of the time.
In this case, stream handling requires you to watch your stream positions carefully.
Regards,
--jeroen