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?

Foi útil?

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:

  1. Colocar dois TClientDataSets no seu servidor (ClientDataSet1 e ClientDataSet2)
  2. Carregar os dados em que ClientDataSet1 em tempo de design usando um provedor e tal
  3. Salve os dados de ClientDataSet1 para um .CDS arquivo
  4. Carregar .CDS arquivo em ClientDataSet2 em tempo de design
  5. 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:

  1. após o envio da sequência, foi a codificação da duração do fluxo nos primeiros 4 bytes
  2. 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

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top