Delphi:Clientdataset:EDatabaseError:Falta de Dados de Pacote usando o Sinapse
-
16-09-2019 - |
Pergunta
A partir do cliente, estou enviando uma seqüência de caracteres para o servidor que ele deve me enviar de volta.Desta vez uma sequência criada por um ClientDataSet.Infelizmente a receber (ou a enviar??) não trabalho no momento.
Nota:Eu estou usando Sinapse com bloqueio sockets.O servidor é multithreaded.
Servidor:
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;
Cliente:
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;
Agora, sempre que eu iniciar o servidor e clique o botão de ligar o cliente, ele DEVE transferir os dados para o cliente e o TDBGrid DEVE vir à vida.
Infelizmente isso não acontece.Em vez disso, eu recebo o erro, como indicado no título:Falta provedor de dados ou datapackage.Mas o provedor de dados é definido para DataSetProvider1 (no object inspector).
Como posso fazer o trabalho que o cliente TDBGrid é preenchida com os dados?
Solução
Você está criando uma nova instância dentro do ClientDataSet1 variável, mas nenhum dos outros componentes em seu formulário de referência, para que.
Essa não é a causa da "Falta de provedor de dados ou datapackage" mensagem de erro:para descobrir isso, você deve colocar um reproduzível caso.
A maneira mais fácil de obter que reproduzível caso seria:
- Colocar dois TClientDataSets no seu servidor (ClientDataSet1 e ClientDataSet2)
- Carregar os dados em que ClientDataSet1 em tempo de design usando um provedor e tal
- Salve os dados de ClientDataSet1 para um .CDS arquivo
- Carregar .CDS arquivo em ClientDataSet2 em tempo de design
- Enviar por ClientDataSet2 para o seu cliente
Se ele ainda reproduz-se, em seguida, posto que .pas/.dfm em algum lugar.Se ele não reproduz, em seguida, começar a cortar partes até que ele reproduz.
Boa sorte!
--jeroen
Editar:Eu baixei os fontes e os levou a trabalhar em Delphi 2009.
Ele contém alguns problemas, e Sinapse biblioteca contém uma questões.
A maioria dos seus problemas vêm para baixo para não ser preciso.Eu já tinha uma vaga sensação de que, com base em seu código fonte, formatação, a mistura de convenções de nomenclatura que você estava usando para as suas coisas, e a falta de libertar os objetos alocados.
Antes que eu esqueça:a primeira coisa que eu fiz foi certificando-se de que eu ativado utilizar a depuração dcus e desactivar otimizações nas opções do compilador.Aqueles que são de valor inestimável definições para aprofundar-se mais em problemas.Eu não adicione qualquer limpeza para o seu código existente, como eu queria que fosse o mais próximo possível.Mas eu fiz executar um formatador de código-fonte, de modo a que pelo menos a em seguida, os blocos foram em linhas separadas.
Vamos começar com o envio de código.
Eu me livrei da sua conexão de banco de dados, copiou o ClientDataSet1 do cliente para o servidor, e refatorado seu SServer unidade para que tanto TTCPSocketDaemon e TTCPSocketThrd agora têm acesso a FClientDataSet:TClientDataSet
Que me fez uma espécie de reprodutíveis caso que eu pedi antes.
Então eu comecei a correr o cliente.Não parecia que LogThis(strmReply.Tamanho); foi a saída de 0 (zero!), então ele não estava recebendo nada.
Que me fez olhar para o servidor novamente.Parece que você estava dividindo a entrada de argumentos usando de texto Delimitado, mas depois que você estava se referindo a sl.Nomes[0] em lugar de se o sl[0].Que falha no Delphi 2009, e provavelmente em outras versões do Delphi bem.Além de que, você não verificação de um vazio s, então ele iria falhar com um List index out of bounds depois de cada vez para fora.
Então eu adicionei alguns depuração de código:Eu queria ver o que estava realmente a ser enviada.Isso me fez fluxo usando XML no lugar do binário usando S_CLIENT.SaveToStream(strm, dfXMLUTF8); e para copiar o conteúdo do buffer para um arquivo localmente, para que eu pudesse assistir a esse arquivo.O arquivo estava OK:ele parecia ser um válido representação XML de um ClientDataSet.
Finalmente, eu notei que você estava usando SendBuffer no servidor, e RecvStream no cliente.Aqueles que não correspondem a:você tem que escolher, então, usar tampões ou fluxos em ambos os lados da conexão!Eu escolhi para fluxos.
Para o envio de código torna-se a este:
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;
Então eu fui para revisitar o recebimento de código.Desde já log strmReply.Tamanho, Eu me perguntava o porquê de não reagir no caso especial onde foi 0 (zero), então eu adicionei ele: LogThis ("vazio de fluxo recebido')
Então eu comecei a depuração, e descobri que strmReply.Tamanho ainda estava 0 (zero) de cada vez.Então eu comecei a cavar a Sinapse código (tirei a cópia que eu tinha, que está incluído no Habari componentes) eu tenho usado ultimamente.Lá eu descobri duas coisas:
- após o envio da sequência, foi a codificação da duração do fluxo nos primeiros 4 bytes
- a codificação foi feita em uma diferente ordem de byte de decodificação
Este é, sem dúvida, um erro na Sinapse, então sinta-se livre para comunicar-lhes.O resultado é que RecvStream não é a contrapartida de SendStream, mas RecvStreamIndy é.
Depois de todas essas mudanças, ainda assim não funcionou.A razão é que ClientDataSet1.LoadFromStream(strmReply); está começando a ler a partir da posição atual na sequência (você pode verificar isso a si mesmo;o núcleo de carregamento lógica está na TCustomClientDataSet.ReadDataPacket).Assim, parece que você esqueceu de adicionar strmReply.Procuramos(0, soBeginning);, e , depois acrescentou que, de repente trabalhou.
Assim, o recebimento de código é este:
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;
No final, não foi difícil de resolver seus problemas.A parte mais difícil foi descobrir sobre RecvStreamIndy, o resto foi uma mera limpeza do seu código e sendo precisos, o que acontece quando:que tomou a maior parte do tempo.Neste caso, a manipulação do fluxo requer que você assistir a sua sequência de posições cuidadosamente.
Cumprimentos,
--jeroen