Pregunta

Desde el cliente Estoy enviando una cadena en el servidor de lo que me debe enviar de vuelta. Esta vez es un flujo creado por una ClientDataSet. Desafortunadamente recibir (o enviar ??) no funciona en este momento.

  

Nota: Estoy usando sinapsis con el bloqueo   zócalos.   El servidor es multiproceso.

El 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;

El 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;

Ahora, cada vez que inicio el servidor y haga clic en el botón de conexión en el cliente, es conveniente transferir el conjunto de datos al cliente y el TDBGrid debe venir a la vida.

Por desgracia, esto no sucede. En su lugar me sale el error como se indica en el título: Falta de datos o de datapackage. Sin embargo, el proveedor de datos se establece en DataSetProvider1 (en el inspector de objetos).

¿Cómo puedo hacer que funcione de que el cliente TDBGrid está lleno de datos?

¿Fue útil?

Solución

Usted está creando una nueva instancia dentro de la variable ClientDataSet1, pero ninguno de los otros componentes en su formulario hará referencia a eso.

Eso no es la causa del "proveedor de datos faltantes o datapackage" mensaje de error: con el fin de descubrir eso usted debe enviar un caso reproducible

.

La forma más fácil de conseguir ese caso reproducible ir sería:

  1. Ponga dos TClientDataSets en su servidor (ClientDataSet1 y ClientDataSet2)
  2. cargar los datos en que ClientDataSet1 en tiempo de diseño utilizando un proveedor y tal
  3. Guardar que los datos de un archivo a ClientDataSet1 .cds
  4. El archivo
  5. Cargar las .cds en ClientDataSet2 en tiempo de diseño
  6. Enviar más de ClientDataSet2 a su cliente

Si aún reproduce, a continuación, publicar que .pas / .dfm en alguna parte. Si no se reproduce, a continuación, empezar a cortar porciones hasta que se reproduce.

Buena suerte!

- Jeroen

Editar:. He descargado sus fuentes y de ellos trabajan en Delphi 2009

Contiene algunos problemas, y la biblioteca de la sinapsis contiene un problemas también.

La mayoría de los problemas se reducen a no siendo preciso. Yo ya tenía una vaga sensación de que, en función de su formato de código fuente, la mezcla de las convenciones de nomenclatura que se estaban utilizando para sus cosas, y la falta de desprendimiento de objetos asignados.

Antes de que me olvide: la primera cosa que hice fue asegurarse de que Habilité DCU uso de depuración y desactivar optimizaciones en las opciones del compilador. Esos son los ajustes de valor incalculable para investigar más profundamente en problemas. No añadí cualquier limpieza de su código existente, ya que quería que fuera lo más cerca posible. Pero yo he corrido un formateador de código fuente para que al menos los entonces bloquea estaban en líneas separadas.

Vamos a empezar con el código de envío.

Me deshice de su conexión a la base de datos, copiado el ClientDataSet1 del cliente al servidor , y refactorizado su sServer unidad para que tanto TTCPSocketDaemon y TTCPSocketThrd ahora tienen acceso a FClientDataSet: TClientDataSet

Eso me consiguió un tipo de caso reproducible, que pedí antes.

Entonces empezó a correr el cliente. No parecía que LogThis (strmReply.Size);. fue dar salida a 0 (! Cero), por lo que no recibía nada

Eso me hizo quedar en el servidor de nuevo. Parece que se estaban separando los argumentos de entrada utilizando texto delimitado, pero después de que estabas refiriendo a sl.Names [0] en lugar de si sl [0] . Eso falla en Delphi 2009, y probablemente en otras versiones de Delphi también. Además de eso, usted no estaba mirando para un vacío s , por lo que sería un error con un índice de lista fuera de límites después de cada tiempo de espera.

Luego he añadido algo de código de depuración: quería ver lo que realmente se está enviando. Esto me causó para transmitir el uso de XML en lugar de binario utilizando S_CLIENT.SaveToStream (STRM, dfXMLUTF8); y copiar el búfer en un archivo local, por lo que pude ver ese archivo. El archivo estaba bien: lo que parecía ser una representación XML válido de un ClientDataSet

.

Por último, me di cuenta de que estaba utilizando búfer de envío en el servidor, y RecvStream en el cliente. Los que no coinciden: hay que elegir, así que o tampones de uso o arroyos en ambos lados de la conexión! Elegí para los flujos.

Así que el código se convierte en el envío de esto:

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;

Así que fui a revisar el código de recepción. Puesto que ya estaba entrando strmReply.Size , me preguntaba por qué no reacciona en el caso especial donde era 0 (cero), por lo que añade que: LogThis ( 'corriente vacío recibieron ')

Entonces empecé depuración, y descubrió que strmReply.Size seguía siendo0 (cero) cada vez. Así que empecé a cavar en el código de la sinapsis (Tomé la copia que ya tenía que está incluido en el Habari componentes) he estado usando últimamente. Allí descubrí dos cosas:

  1. momento de envío de la corriente, se codifica la longitud de la corriente en los primeros 4 bytes
  2. la codificación se realiza en un orden de bytes diferente que la decodificación

Este es sin duda un error en la sinapsis, así que no dude en informar de ello a los mismos. El resultado es que RecvStream no es la contrapartida de SendStream , pero RecvStreamIndy es.

Después de todos estos cambios, todavía no ha funcionado. La razón es que ClientDataSet1.LoadFromStream (strmReply); está empezando a leer desde la posición actual en la corriente (se puede comprobar que fuera usted mismo, la lógica de la base de carga está en TCustomClientDataSet.ReadDataPacket ). Por lo que parece que se olvidó de añadir strmReply.Seek (0, soBeginning);. , y después añadí que, de repente se trabajó

Así que el código de recepción es el siguiente:

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;

Al final, no fue difícil de resolver sus problemas. La parte más difícil fue encontrar a cabo sobre RecvStreamIndy , el resto fue una mera limpieza de su código y ser preciso en lo que sucede cuando: que tomó la mayor parte del tiempo. En este caso, el manejo de flujo que se requiere para ver sus posiciones corriente con cuidado.

Saludos,

- Jeroen

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top