Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Varios (https://www.clubdelphi.com/foros/forumdisplay.php?f=11)
-   -   Recorrer un pchar (https://www.clubdelphi.com/foros/showthread.php?t=32008)

Status Quo 24-05-2006 08:21:59

Recorrer un pchar
 
Tengo un buffer de bytes guardado en un pchar (de una aplicación cliente servidor, que envía este buffer como mensajes divididos en trozos y con cabecera). Me gustaria poder recorrerlo, hacer como cuando se hace delete en un string (de una posicion a otra que yo quiera), pero no entiendo el funcionamiento de pchar y me vuelvo loco. Por favor, alguien puede explicarme cómo lo hago? Cómo cojo los bytes que me interesan? (los de la cabecera al recibir) y borrarlos luego para quedarme sólo con el buffer de bytes de datos correspondientes al archivo que he enviado?

Muchas gracias.

seoane 24-05-2006 11:52:13

Un PChar es un puntero a un caracter, se utilizan para manejar cadenas terminadas en caracter nulo como en C. El "truco" esta es mover el puntero por la cadena, adelante o atras segun convenga.

Por ejemplo:
Código Delphi [-]
var
  P: PChar;
  Aux: PChar;
  i: integer;
  s: string;
begin
  GetMem(P, 256);
  try
    FillChar(P^,256,0);
    StrCopy(P, 'Hola mundo');
    // Recorremos el texto sabiendo su longitud
    Aux:= P;
    s:= '';
    for i:= 1 to StrLen(P) do
    begin
      s:= s + Aux^;
      inc(Aux);
    end;
    ShowMessage(s);
    // Recorremos el texto hasta encontrar el caracter nulo
    Aux:= P;
    s:= '';
    while Aux^ <> #0 do
    begin
      s:= s + Aux^;
      inc(Aux);
    end;
    ShowMessage(s);
    // En cualquiera de los 2 casos Aux queda apuntando
    // al caracter nulo
    inc(Aux);
    // Ahora Aux esta apuntando al siguiente caracter si lo hubiese.
  finally
    FreeMem(P);
  end;
end;

Es solo un ejemplo de como recorrer la cadena, para copiar el contenido de un PChar en un String hay metodos mas eficaces:
Código Delphi [-]
var
  P: PChar;
  Aux: PChar;
  s: string;
begin
  GetMem(P, 256);
  try
    FillChar(P^,256,0);
    StrCopy(P, 'Hola mundo');
    // Copaimos el texto en S
    S:= String(P);
    // Colocamos el puntero Aux apuntando al caracter siguiente al nulo
    Aux:= P + StrLen(P) + 1;
    ShowMessage(S);
  finally
    FreeMem(P);
  end;
end;

Y algo parecido a lo que quieres hacer tu, metemos en una misma porcion de memoria un texto y un integer, y luego los volvemos a separar.
Código Delphi [-]
var
  P: PChar;
  Aux: PChar;
  s: string;
  i,j: Integer;
begin
  GetMem(P, 256);
  try
    // Aqui metemos en un mismo buffer un texto y a continuacion un integer
    FillChar(P^,256,0);
    StrCopy(P, 'Hola mundo');
    i:= 1234;
    // Hacemos que Aux apunte al carater siguiente al nulo
    Aux:= P + StrLen(P) + 1;
    move(i,Aux^,sizeof(i));

    // Ahora sacamos de nuevo la informacion
    S:= String(P);
    Aux:= P + StrLen(P) + 1;
    move(Aux^,j,sizeof(j));
    ShowMessage(S + ',' + IntTostr(j));
  finally
    FreeMem(P);
  end;
end;

Son varios ejemplos de como tratar con PChar, revisa tanbien la ayuda de delphi, encontraras un monton de funciones para tratar con este tipo de cadenas de texto. ;) Y si no encuentras lo que buscas vuelve por aqui haber que podemos hacer.

Lepe 24-05-2006 13:31:31

Quizás fuera más facil pasar el Pchar a String o a un TStringList, mediante TStringList.DelimitedText puedes partir en trozos la cadena (por espacios, por un punto y coma, por una coma, etc).

Al pasarlo a un String, puedes usar la archifamosa función "Pos" o "copy" para copiar los caracteres.

Saludos

marcoszorrilla 24-05-2006 15:04:42

Código Delphi [-]
procedure TForm1.Button1Click(Sender: TObject);
var
p:Pchar;
S:String;
begin
P:='ABCDEFG';
ShowMessage(p[3]);
s:=P[2];
ShowMessage(s);
end;

Si es para un solo caracter te puede valer un índice para recorrer la variable.

Si extraes más de uno a la vez, quizás con Copy puedas hacerlo, en cuanto a explicaciones las que te da Seoane y Lepe son amplias y doctas.

Un Saludo.

Status Quo 24-05-2006 16:27:53

Gracias!!!!
 
Gracias a todos!!! Ya voy viendo la luz al final del tunel....lo mismo a medio camino debo hacer otra paradita por aqui...

:)


Gracias!!!

marcoszorrilla 24-05-2006 16:30:47

Ten en cuenta que hemos inaugurado hace poco el Foro "La Taberna", lugar apto para paraditas y lo que salga, en donde te puedes poner de bebida virtual hasta los tuétanos, es gratis y no afecta a la conducción.

Un Saludo.

Status Quo 24-05-2006 16:55:08

Duda sobre StrCopy
 
Hola,

no se por qué me da error al poner:

StrCopy(bufferAux,(formatFloat('0000',length(bufferAux)) + 't'));

Da "incompatible types string and Pchar".

seoane 24-05-2006 17:21:25

Pues el error lo dice todo ;) . Estas intentando usar un string como si fuera un PChar, que se puede, pero hay que indicarselo al compilador que si no se atraganta :D , asi que debemos hacer el conveniente typecast.

Código Delphi [-]
StrCopy(bufferAux,PChar(formatFloat('0000',length(bufferAux)) + 't'));

Status Quo 25-05-2006 19:36:14

mas sobre pchar
 
es verdad!!! gracias...

No os lo vais a creer pero sigo teniendo problemas
Seoane lo que me has recomendado me ha parecido genial pero ahora estoy intentando añadir un pchar a otro de la siguiente manera y no lo consigo:

Código Delphi [-]
// Antes de esto hago: leidos:=stream.Read(buffer^,SizeOf(buffer));

repeat
            Application.ProcessMessages;
            GetMem(Cabecera,256);
            GetMem(Bufferaux,3072);
            try
                Fillchar(bufferaux^,2304,0);
                Cabecera:=PChar(formatFloat('0000',length(buffer))+ 't');
                StrCopy(Bufferaux,Cabecera);
                Aux:=Bufferaux + StrLen(Bufferaux) +1;
                move(buffer^,Aux^,sizeof(buffer));
                ClientSocket2.Socket.SendBuf(BufferAux^,SizeOf(BufferAux)); 
                //En BufferAux solo tengo el valor de la cabecera, pero no me  
               //añade el de buffer

            finally
                FreeMem(Cabecera);
                FreeMem(Bufferaux);
            end;
              
            leidos:=stream.Read(buffer^,SizeOf(buffer));
        until(leidos=0);
        stream.Free;

En BufferAux solo tengo el valor de la cabecera, pero no me añade el de buffer. Lo he comprobado paso a paso y en buffer si mete contenido...me estoy montando unos lios...Perdonar por ser tan pesao

seoane 25-05-2006 20:08:56

Creo que el problema debe de estar aqui:

Código Delphi [-]
move(buffer^,Aux^,sizeof(buffer));

Date cuenta que sizeof(buffer) siempre valdra 4 ya que ese es el tamaño de un PChar (si buffer es un PChar que tampoco lo aclaras), y no tiene nada que ver con el contenido de la memoria al que apunta ese puntero. Pero la verdad es que ya me estoy empezando a perder con tu codigo :) No estoy seguro de lo que estas intentando hacer pero tiene que haber alguna manera mas sencilla de hacerlo.

Status Quo 25-05-2006 20:42:39

aclaración
 
Perdona..esque no me aclaro ni yo.
Quiero mandar un fichero de un pc a otro por medio de sockets.
Eso es fácil. Me exigen que no se envíe el fichero entero si no a trozos. He conseguido mandarlos a trozos(me ha costado pero lo logré...) y ahora lo que quiero es incluir en cada paquete que mando una cabecera con tamaño de paquete y tipo...(En realidad no sirve para nada pero lo tengo que hacer)
vamos que tengo que mantar paquetes que tengan:
-tamaño
-tipo
-datos

cuando se reciban estos paquetes tengo que extraer la cabecera y incluir los datos en el fichero...
Y eso es todo...

He trabajado otras veces con sockets pero siempre he mandado texto y trabajaba bastante bien, pero como tengo que poder mandar ficheros de todo tipo..
Otros compañeros míos lo han hecho con Java mandando un objeto trama con los campos de tamaño...y bytes y les ha salido, pero yo me he empeñado en hacerlo con delphi.:mad:

Me esta agobiando un montón esto, gracias por echarme una mano de verdad...(:

por cierto, el buffer...anteriormente hago getmen(buffer,1024)

seoane 25-05-2006 21:31:14

Se me ocurre que puedes usar records para hacer lo que te pidieron. Me explico, primero declaras un tipo de record que sera el que utilices para enviar y recibir los trozos:

Código Delphi [-]
type
  Paquete = record
    Tipo: Integer;           // Uso un integer pero un char tambien valdria
    Tamano: Integer;
    Buffer: array[0..1024] of Byte;
  end;

Luego solo tienes que usar ese paquete durante la transmision:
Código Delphi [-]
var
  P: Paquete;
begin
  P.Tipo:= 1; 
  P.Tamano:= stream.Read(P.Buffer,SizeOf(P.Buffer));  
  Cliente.Socket.SendBuf(P,sizeof(P))
end;

Y durante la recepcion
Código Delphi [-]
var
  P: Paquete;
begin
  Socket.ReceiveBuf(P, Sizeof(P));
  // Si lo quisieramos guardar en un stream
  stream.WriteBuffer(P.Buffer, P.Tamano);
end;

El unico problema es que si el ultimo paquete no lo llenamos por completo, estaremos enviando mas bytes de los necesarios, pero como tambien enviamos el tamaño ese no seria un problema. Y es enormemente mas sencillo que andar haciendo malabares con punteros

:) ¿que te parece?

Status Quo 25-05-2006 21:42:20

Gracias
 
Gracias!!!!!!!!! :) nunca he trabajado con eso (soy muyyyy novato):o
Me parece que lo que me has dicho va a ser la solución a mis males...
ya me temía yo que me estaba complicando demasiado la vida...
Gracias, gracias de verdad...

voy a ponerme a ello inmediatamente...:D
Ni te imaginas el favor que me acabas de hacer!!!!!!!!!!!!

seoane 25-05-2006 22:23:09

:D No podiamos consentir que tus compañeros lo hiceran en Java y tu no lo hiceras en Delphi, por favor ... :cool:

Status Quo 26-05-2006 10:57:09

Más consultas sobre el tema
 
jejeje...delphi al poder!!!:D

He utilizado lo de los record (que buena idea!:) ) y normalmente me va bien sobre todo en los ficheros jpg pequeños...
Los word los estropea...no se. Vamos que a veces me va bien y a veces mal...Esta tarde cuando llegue a mi casa añado el código.

Yo creeo que envíar, los envía bien...el problema creo que es al recibirlos porque a lo mejor no llegan completos, no sé...:confused:

Yo cuando he utilizado socket para mandar texto lo iba encolando en un string, si la variable cola(de tipo string) era mayor que 2(tamaño del campo de tamaño de mensaje) cojía el tamaño (lo pasaba a entero). Luego comparaba el tamaño de la cola de mensajes con el campo de tamaño y si era mayor o igual extraía el mensaje y lo borraba de la cola... y de esta manera aseguraba que los mensajes llegaran completos
Bueno eso hacía e iba muy bien.
No se si será necesario encolar los bytes...(no tengo ni idea de como encolar, recoger en mi estructura y eliminar bytes de una cola de bytes pero bueno...)

Bueno esta tarde os lo cuento mejor con código incluido a ver que os parece...;)

seoane 26-05-2006 13:18:58

No tengo mucha experiencia con los componentes que usas (TClientSocket y TServerSocket, corrigeme si me equivoco), ni si quiera lo tengo en la paleta de componentes :D Pero he estado haciendo unas pruebas y parece que no funciona mal del todo:

Código Delphi [-]
unit ufrmMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ScktComp;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
    Cliente: TClientSocket;
    Servidor: TServerSocket;
    Destino: TFileStream;
    procedure OnClientRead(Sender: TObject; Socket: TCustomWinSocket);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

type
  Paquete = record
    Tipo: Integer;
    Tamano: Integer;
    Buffer: array[0..1024] of Byte;
  end;

Const
  Normal = 0;
  Comienzo = 1;
  
procedure TForm1.OnClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
  P: Paquete;
begin
  Socket.ReceiveBuf(P, Sizeof(P));
  if P.Tipo = Comienzo then
  begin
    FreeAndNil(Destino);
    Destino:= TFileStream.Create('d:\2.jpg', fmCreate);
  end;
  Destino.WriteBuffer(P.Buffer,P.Tamano);
  if P.Tamano < Sizeof(P.Buffer) then
  begin
    FreeAndNil(Destino);
    ShowMessage('Recibido');
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Cliente:= TClientSocket.Create(Self);
  Servidor:= TServerSocket.Create(Self);
  Cliente.ClientType:= ctBlocking;
  Servidor.ServerType:= stNonBlocking;
  Servidor.OnClientRead:= OnClientRead;
  Servidor.Port:= 3000;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Servidor.Active:= TRUE;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  P: Paquete;
begin
  Cliente.Active:= FALSE;
  Cliente.Port:= 3000;
  Cliente.Host:= '127.0.0.1';
  Cliente.Active:= TRUE;
  with TFileStream.Create('d:\1.jpg', fmOpenRead) do
  try
    P.Tipo:= Comienzo;
    repeat
      Application.ProcessMessages;
      P.Tamano:= Read(P.Buffer,Sizeof(P.Buffer));
      Cliente.Socket.SendBuf(P,sizeof(P));
      P.Tipo:= Normal;
    until P.Tamano < Sizeof(P.Buffer);
  finally
    Free;
  end;
end;

end.

He utilizado el tipo NonBlocking aunque no me gusta este tipo de sockets, mas que nada porque me resultaba mas sencillo, ¿que tipo usas tu? Me gustaria echarle un vistazo a tu codigo.

Status Quo 26-05-2006 20:39:54

Llevo toda la tarde sin internet
 
Gracias Seoane:) , ahora te paso mi código...Perdona, me he tenido que ir a casa de mi primo...me he cambiado de proveedor de internet...y me han tenido sin internet hasta ahora:mad: ... Voy a preparar el código para ponerlo...Si lo quieres todo dime dónde lo subo o dónde te lo mando...
Gracias por la ayuda de verdad...Este año tengo unos compañeros bastante pasotas y no estoy recibiendo ninguna ayuda por parte de ellos...todo lo contrario...es competencia pura...

Status Quo 26-05-2006 21:03:15

Código
 
Hola Seoane.

Esto no está montado en mi aplicación porque me daba tantos poblemas que lo hice aparte. Está basado en uno que recomendaban en el club en una determinada página (no recuerdo cual). Voy a mirar lo que me has mandado y voy a tratar de montarlo todo en mi apliación, porque aquí por ejemplo mando los ficheros eligiendo el que quiero y en mi aplicación debo hacer que cada cierto tiempo mire a ver cuales han sido modificados, creados o borrados y los envíe al cliente. Una sincroniación de ficheros, todo lo que haga en el servidor debe hacerse en el cliente.

En lo que te mando tengo dos socket, uno dedicado a mandar el tamaño del fichero (servidor -> cliente) a enviar y otro que envía el fichero (cliente ->servidor). Lo he hecho así por los problemas que me daba el meter la información en los fragmentos del fichero, pero ahora viendo que funciona lo que me estás aconsejando, voy a unificarlo todo y a emplear sólo un socket (que es cómo me han pedido que lo haga).


El código para enviar ficheros es:

Código Delphi [-]
procedure TForm1.Button2Click(Sender: TObject);
var
stream:TStream;

P: Paquete;

begin
   if ServerSocket1.Active = True then
   begin
      OpenDialog1.Filter := 'All Files (*.*)';
      OpenDialog1.FilterIndex := 1;
      if OpenDialog1.Execute then
      begin
         Edit1.Text := ExtractFileName(OpenDialog1.FileName); //He puesto un boton y un Dialog  para escojer el fichero

         ServerSocket1.Socket.Connections[0].SendText('FILE!'+Edit1.Text);
         sleep(2000);
         Streamsize := TFileStream.Create(OpenDialog1.FileName, fmopenread);
         Sleep(2000);
         ServerSocket1.Socket.Connections[0].SendText('SIZE!'+Edit2.Text);
         Streamsize.Position := 0;
         Streamsize.Free;
         sleep(2000);

         ClientSocket2.Address := Edit3.Text;
         ClientSocket2.Open; // Preparado para enviar el fichero en el segundo socket

         stream:=TFileStream.Create(OpenDialog1.FileName, fmopenRead);
         P.Tamano:=stream.Read(P.Buffer,SizeOf(P.Buffer));

        repeat
            Application.ProcessMessages;

            P.Tipo:= 1;
            ClientSocket2.Socket.SendBuf(P,sizeof(P)-1);
            P.Tamano:= stream.Read(P.Buffer,SizeOf(P.Buffer));

        until(P.Tamano=0);
        stream.Free;

      end;
   end
     else
     MessageDlg('Error de conexión', mtError, [MbOK],0);
end;



El código para recibir el fichero enviado es:


Código Delphi [-]
procedure TForm1.ServerSocket2ClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
  // Este segundo socket es el que se encarga de la recepción de los ficheros

var
    IncommingLen, RecievedLen: integer;
    Filepath: string;

    P: Paquete;
begin
   Timer1.Enabled := True;
   IncommingLen := socket.ReceiveLength;
   Filepath := ExtractFilePath(Edit1.Text)+Edit1.Text;



    while IncommingLen > 0 do
    // Asegurar que el proceso termina
    begin

       RecievedLen := Socket.ReceiveBuf(P, Sizeof(P));

    if RecievedLen <= 0 then
    begin
           Break
       end
    else

      IncommingStream.Write(P.Buffer, P.Tamano);

      ProgressBar1.StepBy(RecievedLen);

     if IncommingStream.Size >= strtoint(Edit2.Text) then
       begin

       IncommingStream.Free;
       memo1.Lines.Add('File '+Edit1.Text +' Recieved Successfuly');
       memo1.Lines.Add('Time Taken to Recieve File ' +IntToStr(TimeTaken)+' seconds');
       ClientSocket1.Socket.SendText('DONE!');
       Edit1.Text := '';

       Edit2.Text := '';
       ProgressBar1.Position := 0;
       Timer1.Enabled := False;
       TimeTaken := 0;
       if Messagedlg('Would you Like to open the recieved file?', mtConfirmation, [MbYes,MbNo],0) = MrYes then  
       begin
         ShellExecute(Form1.Handle, 'open', pchar(Filepath),nil, nil, SW_NORMAL); 
       end;
       Break;                                                                         
    end;
   end;
  end;

Voy a ponerme a currar como un loco y voy a mirarme bien lo que me has mandado.
El código que he puesto a lo mejor está un poco caótico...bueno..voy a ir trabajando y luego te cuento.

seoane 26-05-2006 21:15:19

Código Delphi [-]
ClientSocket2.Socket.SendBuf(P,sizeof(P)-1);

A simple vista me parece que en esta linea te sobra el "-1". El resto parece que esta bien, pero habria que mirarlo mas profundamente.

Status Quo 26-05-2006 22:49:55

Gracias!
 
Ahora estoy poniéndolo en un solol socket y haciendo que recorra un directorio y mande todos los ficheros que hay en el.

Miraré lo que me has dicho. Cuando complete todo te lo enseño.:)

Gracias!!:D


La franja horaria es GMT +2. Ahora son las 08:55:41.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi