Ver Mensaje Individual
  #9  
Antiguo 20-07-2010
Avatar de sintecsl
sintecsl sintecsl is offline
Miembro
 
Registrado: jun 2008
Ubicación: Barcelona - Spain
Posts: 40
Reputación: 0
sintecsl Va por buen camino
Cita:
Empezado por Faust Ver Mensaje
Muy ilustrativo amigo Sintecsl...

Me gustó mucho tu ejemplo, me ha aclarado algunas cosas, solo algunas preguntas... en cuanto al tamaño del buffer para el envío y recepción ¿te refieres a la propiedad RecvBufferSize y SendBufferSize del client? ¿el server tiene alguna parecida o se ajusta al client?, ¿este tamaño puede ser modificado una vez que se ha conectado el cliente?

Bueno... creo que ya he dado mucha lata.

Por último, veo que eres nuevo en el club y sería bueno que cooperaras más seguido con nosotros y también a nombre del club te ofrezco el apoyo de los demás compañeros .

Gracias de antemano y buen inicio de semana.

A tu pregunta el buffer, la respuesta es que si se puede manipular tanto en el cliente (lo mas facil) como en el server.
En la propiedad del buffer cuando te pasa la variable el cliente se establece para dicha conexión esa variable de forma automática en Indy.

En el server si quieres cambiar ese buffer puedes poner una instrucción o comando :
Código Delphi [-]
procedure TServerForm.ServerCAMBIATAMANOBUFFERCommand(ASender: TIdCommand); 
begin 
     // Comando CAMBIATAMANOBUFFER 
     // Pondremos el tamaño de buffer que nos pase para la 
     // conexión actual, importante para sincronizar mejor 
     // los envíos y recepciones. 
     // Nos tiene que enviar un Integer solamente, ya que lo 
     // consideraremos de forma simetrica Envío <-> Recepción 
     Try 
     ASender.Thread.Connection.RecvBufferSize:=ASender.Thread.Connection.ReadInteger(); 
     ASender.Thread.Connection.SendBufferSize:=ASender.Thread.Connection.RecvBufferSize; 
     Except //Por si se desmadra el tema capturamos excepciones 
     End 
     // End 
end;
Esto es útil porque en mi caso envío/recepciono de forma comprimida ficheros.

Y establezco la compresión al conectar con esta instrucción :
Código Delphi [-]
procedure TServerForm.ServerNIVELCOMPRESIONCommand(ASender: TIdCommand); 
begin 
     //Cambiamos la forma de comprimir No, Fast, Best, Max 
     NivelCompress:=TZCompressionLevel(ASender.Thread.Connection.ReadInteger()); 
     //Colocamos en el ini el nivel de compresión 
     FormStorage1.StoredValues.Values['NivelCompresion'].Value:= 
               Integer(NivelCompress); 
     FormStorage1.StoredValues.SaveValues; 
end;
Lo cual me permite ajustar el ancho de banda de conexión.

Por otro lado, en el envío/recepción de ficheros determino el buffer a la mitad del que tiene cada conexión (el buffer está guardado en cada variable tipo hilo (ASender)). Esto es motivo para evitar desbordamientos en ficheros muy comprimidos y que superan el tamaño original, lo cual te daría errores insalvables.

Como ejemplo un boton :

EL CLIENTE :
Código Delphi [-]
procedure TCopiaFiles.CopiaAlServer(const Origen, Destino: String); 
Var 
   Error, TamanoPaq, NPaq, Leidos, Escritos, x, Parar : Integer; 
   FHandle : Integer; 
   Buf, BufOut : Pointer; 
   SFile : TFileStream; 
begin 
     try //Capturamos errores graves 
     If (Not Cliente.Connected)Or(EnEjecucion)Or(PararEnvioRecepcion) Then Exit; 
     EnEjecucion:=True; //Prevenimos rellamadas 
     LimpiaPantalla; //Ponemos la pantalla en estado inicial 
     Enviando; // Retardo para simular y animación 
     Cliente.Writeln('COPIAFILEDECLIENTE'); //ENVIAR UN FICHERO AL SERVER 
     // El fichero que se enviará en destino 
     Cliente.Writeln(Destino); 
     // Nos tiene que retornar primero si hay error 
     Recibiendo; // Retardo para simular y animación 
     Error:=Cliente.ReadInteger(); 
     // Si el error simplemente en el 13000 significa que se debe confirmar 
     // la ejecución, puesto que existe en destino (Server) 
     // Los otros errores son insalvables, como que el directorio no existe 
     // o no lo puede crear. 
     If Error=13000 Then  // Si existe en destino 
      Begin 
       Parar:=0; 
       If Sobreescritura Then //Activamos confirmación 
          Parar:=Application.MessageBox('¿ SOBREESCRIBIR ?','ATENCIÓN', 
                                        MB_YESNOCANCEL+mb_IconQuestion); 
       If Parar=idNo Then 
          Begin 
               Cliente.WriteInteger(1); //Le decimos que termine 
               EnEjecucion:=False; 
               Exit 
          End 
       Else 
       If Parar=idCancel Then 
          Begin 
               Cliente.WriteInteger(1); //Le decimos que termine 
               PararEnvioRecepcion:=True; 
               EnEjecucion:=False; 
               Exit 
          End 
      End 
     Else //Para otros errores terminar 
     If Error<>0 Then 
      Begin 
           Cliente.WriteInteger(1); //Le decimos que termine 
           //Comprobamos error 
            If Error=800 Then 
               ShowMessage(' NO SE PUDO CREAR EL DIR :'#13 
                             +ExtractFilePath(Destino)); 
           EnEjecucion:=False; 
           Exit 
      End; 
     //Le decimos que continue 
     Enviando; // Retardo para simular y animación 
     Cliente.WriteInteger(0); 
     //Abrimos fichero de origen 
     SFile:=TFileStream.Create(Origen,fmShareDenyRead); 
     TamanoPaq:=Cliente.SendBufferSize Div 2; 
     GetMem(Buf,TamanoPaq); 
     // Establecemos los paquetes que vamos a enviar 
     // Número de paquetes de envío 
     NPaq:=SFile.Size Div TamanoPaq; 
     If (SFile.Size/TamanoPaq)>0.0 Then Inc(NPaq); 
     // Preparamos barra visual de evolución 
     BarraProgresoFichero.ValorMax:=SFile.Size; 
     // Le informamos para que prepare el bucle de lecturas 
     Cliente.WriteInteger(NPaq); 
     //Iniciamos Cronómetro de precisión 
     //Utilizaremos 
     //  * el cronometro 1 para el tiempo total 
     //  * el cronometro 2 para tiempos intermedios 
     //Reinicializamos el contador general 
     CronoHP1.Activa(1); 
     For x:=1 To NPaq Do 
      Begin 
           //Guardamos el tiempo al comenzar el proceso de envío del paquete 
           CronoHP1.Activa(2); 
           Leidos:=SFile.Read(Buf^,TamanoPaq); 
           ZCompress(Buf,Leidos,BufOut,Escritos,NivelCompress); 
           //Procesamos mensages 
           Application.ProcessMessages; 
           //Le respondemos si se debe cancelar en función 
           //de la variable de cancelación del proceso de recepción 
           If PararEnvioRecepcion Then 
            Begin //Debemos salir eliminando variables temporales 
                 Cliente.WriteInteger(1); //Cancelación en el server 
                 FreeMem(BufOut); 
                 FreeMem(Buf); 
                 SFile.Free; 
                 BarraProgresoFichero.ValorMax:=0; 
                 EnEjecucion:=False; 
                 Exit 
            End 
           Else Cliente.WriteInteger(0); 
           //Le enviamos información de la cantidad que debe leer 
           Cliente.WriteInteger(Escritos); 
           Retardo; // Retardo para simular y animación 
           //Le enviamos datos 
           Cliente.WriteBuffer(BufOut^,Escritos); 
           //Liberamos memoria 
           FreeMem(BufOut); 
           //Damos datos de velocidad y tiempos 
           BarraProgresoFichero.Progress:=SFile.Position; 
           Velocidad.Caption:=Format('%n KBytes/Seg', 
                                     [Leidos/(CronoHP1.Read_r(2,fcSeg)*1024)]); 
           If x>1 Then 
           Begin 
               VelocidadMedia.Caption:=Format('%n KBytes/Seg', 
                                              [SFile.Position/ 
                                                (CronoHP1.Read_r(1,fcSeg)*1024)]); 
               TiempoRestante.Caption:=DameTiempoEnHMS(Round(((SFile.Size-SFile.Position)* 
                                           CronoHP1.Read_r(1,fcSeg) 
                                          /SFile.Position))); 
               TiempoTranscurrido.Caption:=DameTiempoEnHMS(CronoHP1.Read_i(1,fcSeg)); 
           End; 
      End; 
     BarraProgresoFichero.Progress:=0; 
     //Cerramos y liberamos 
     FreeMem(Buf); 
     SFile.Free; 
     Enviando; // Retardo para simular y animación 
     //Le enviamos los aributos del fichero y la fecha de modificación 
     FHandle:=FileOpen(Origen, fmShareDenyRead); 
     Cliente.WriteInteger(FileGetAttr(Origen)); 
     Cliente.WriteInteger(FileGetDate(FHandle)); 
     FileClose(FHandle); 
     EnEjecucion:=False; 
     Except //El peor de los casos 
          EnEjecucion:=False; 
          ShowMessage('¡¡¡  ERROR GRAVE  !!!'#13'SALDREMOS DEL PROGRAMA'); 
          Self.Close; 
          Halt 
     End 
end; 

procedure TCopiaFiles.CopiaDelServer(const Origen, Destino: String); 
Var 
   Error, NPaq, TamanoPaq, TamFichero, 
   Leidos, Escritos, x, Parar : Integer; 
   FHandle : Integer; 
   SFile : TFileStream; 
   Buf, BufIn : Pointer; 
   Dir : String; 
begin 
     Try //Capturamos errores graves 
     If (Not Cliente.Connected)Or(EnEjecucion) Then Exit; 
     EnEjecucion:=True; //Prevenimos rellamadas 
     LimpiaPantalla; //Ponemos la pantalla en estado inicial 
     Enviando; // Retardo para simular y animación 
     Cliente.Writeln('COPIAFILEDESERVER'); //PIDE FICHERO DEL SERVER 
     // El fichero que buscará 
     Cliente.Writeln(Origen); 
     Recibiendo; // Retardo para simular y animación 
     // Nos tiene que retornar primero si hay error al localizar el fichero 
     Error:=Cliente.ReadInteger(); 
     // Si hubo error no procesamos en caso contrario continuamos proceso 
     If Error<>0 Then Exit; 
     //Si no hay error realizamos Comprobaciones 
     // Primero si el Dir existe 
     Dir:=ExtractFilePath(Destino); 
     If Not DirectoryExists(Dir) Then //Si no existe el dir crearlo 
       Begin 
            If Not ForceDirectories(Dir) Then 
              Begin 
                   Cliente.WriteInteger(1); //Le decimos que termine 
                   EnEjecucion:=False; 
                   Exit 
              End 
       End; 
     //Ahora si el fichero existe 
     If FileExists(Destino) Then 
        Begin 
             Parar:=0; //Inicializamos por si no es necesario 
             If Sobreescritura Then //Activamos confirmación 
                Parar:=Application.MessageBox('¿ SOBREESCRIBIR ?','ATENCIÓN', 
                                              MB_YESNOCANCEL+mb_IconQuestion); 
             If Parar=idNo Then 
                Begin 
                     Cliente.WriteInteger(1); //Le decimos que termine 
                     EnEjecucion:=False; 
                     Exit 
                End 
             Else 
             If Parar=idCancel Then 
                Begin 
                     Cliente.WriteInteger(1); //Le decimos que termine 
                     PararEnvioRecepcion:=True; 
                     EnEjecucion:=False; 
                     Exit 
                End 
        End; 
     Enviando; // Retardo para simular y animación 
     Cliente.WriteInteger(0); //Le decimos que continue 
     // Establecemos el tamaño de paquete en función del buffer 
     TamanoPaq:=Cliente.SendBufferSize Div 2; 
     //Creamos fichero de destino 
     //Ahora si el fichero existe lo borramos para evitar conflictos 
     BorraFichero(Destino); 
     //Asignamos 
     SFile:=TFileStream.Create(Destino,fmCreate); 
     //Leemos el número de paquetes que nos enviará 
     Recibiendo; // Retardo para simular y animación 
     // Recibimos el tamaño total del fichero para que pueda calcular 
     // tiempos de recepción-envío 
     TamFichero:=Cliente.ReadInteger(); 
     //Recibimos el número de paquetes comprimidos  que nos enviará 
     NPaq:=Cliente.ReadInteger(); 
     //Asignamos inicialmente la variable que contendrá los Bytes recibidos, 
     //la velocidad media de recepción de datos y el tiempo acumulado en proceso 
     //Configuramos barra visual 
     BarraProgresoFichero.ValorMax:=TamFichero; 
     //Iniciamos Cronómetro de precisión 
     //Utilizaremos 
     //  * el cronometro 1 para el tiempo total 
     //  * el cronometro 2 para tiempos intermedios 
     //Reinicializamos el contador general 
     //Reinicializamos el contador general y lo dejamos parado 
     CronoHP1.Activa(1); 
     For x:=1 To NPaq Do 
      Begin 
           //Guardamos el tiempo al comenzar el proceso de recepción del paquete 
           CronoHP1.Activa(2); 
           //Procesamos mensages 
           Application.ProcessMessages; 
           //Le respondemos si se debe cancelar en función 
           //de la variable de cancelación del proceso de recepción 
           Enviando; // Retardo para simular y animación 
           If PararEnvioRecepcion Then 
            Begin //Debemos salir eliminando variables temporales 
                 Cliente.WriteInteger(1); //Cancelación en el server 
                 SFile.Free; 
                 DeleteFile(Destino); //Borramos el fichero 
                 BarraProgresoFichero.ValorMax:=0; 
                 EnEjecucion:=False; 
                 Exit 
            End 
           Else Cliente.WriteInteger(0); 
           //Le requerimos cantidad a recibir 
           Recibiendo; // Retardo para simular y animación 
           Leidos:=Cliente.ReadInteger(); 
           //Leemos del server 
           GetMem(BufIn,Leidos); 
           Cliente.ReadBuffer(BufIn^,Leidos); 
           Retardo; //PARA PRUEBAS SIMULADAS LOCALES 
           //Descomprimimos 
           ZDecompress(BufIn,Leidos,Buf,Escritos,TamanoPaq); 
           //Escribimos en el fichero de salida 
           SFile.Write(Buf^,Escritos); 
           //Liberamos memoria 
           FreeMem(Buf); 
           FreeMem(BufIn); 
           //Damos datos de velocidad y tiempos 
           BarraProgresoFichero.Progress:=SFile.Size; 

           Velocidad.Caption:=Format('%n KBytes/Seg', 
                                     [Escritos/(CronoHP1.Read_r(2,fcSeg)*1024)]); 
           If x>1 Then 
           Begin 
               VelocidadMedia.Caption:=Format('%n KBytes/Seg', 
                                              [SFile.Size/ 
                                              (CronoHP1.Read_r(1,fcSeg)*1024)]); 
               TiempoRestante.Caption:=DameTiempoEnHMS(Round((TamFichero-SFile.Size) 
                                          *CronoHP1.Read_r(1,fcSeg) 
                                          /SFile.Size)); 
               TiempoTranscurrido.Caption:=DameTiempoEnHMS(CronoHP1.Read_i(1,fcSeg)); 
           End; 
      End; 
     //Cerramos y liberamos 
     SFile.Free; 
     Recibiendo; // Retardo para simular y animación 
     //Recibimos los aributos del fichero y la fecha de modificación 
     FHandle:=FileOpen(Destino, fmOpenWrite); 
     FileSetAttr(Destino,Cliente.ReadInteger); 
     FileSetDate(FHandle,Cliente.ReadInteger); 
     FileClose(FHandle); 
     BarraProgresoFichero.ValorMax:=0; 
     EnEjecucion:=False; 
     Except //El peor de los casos 
          EnEjecucion:=False; 
          ShowMessage('¡¡¡  ERROR GRAVE  !!!'#13'SALDREMOS DEL PROGRAMA'); 
          Self.Close; 
          Halt 
     End 
end;
EL SERVER (solo es la copia desde el cliente al server) :
Código Delphi [-]
procedure TServerForm.ServerCOPIAFILEDECLIENTECommand(ASender: TIdCommand); 
Var 
   Fichero, Dir : String; 
   Error, NPaq, Leidos, Escritos, TamanoPaq, x : Integer; 
   FHandle : Integer; 
   SFile : TFileStream; 
   BufIn, Buf : Pointer; 
begin 
     // Se esperan los siguientes parámetros 
     // Fichero (Contendrá todo el Path entero, Ejem : C:\MIDIR\FILE.XXX). 
     // Retorna si hubo error en formato integer 
     // Miraremos de comprimir los datos para darle velocidad de envío 
     // Cargamos parámetros 
     // El fichero que recibirá 
     Try 
     Fichero:=ASender.Thread.Connection.ReadLn; 
     // Realizamos Comprobaciones 
     // Primero si el Dir existe 
     Dir:=ExtractFilePath(Fichero); 
     If Not DirectoryExists(Dir) Then //Si no existe el dir crearlo 
       Begin 
            If Not ForceDirectories(Dir) Then Error:=800 //No se pudo crear Dir 
            Else Error:=0; //No hubo error 
       End 
     Else 
     If FileExists(Fichero) Then Error:=13000 //El fichero ya existe 
     Else Error:=0; 
     // Le retornamos el posible error (si existe para que decida) 
     ASender.Thread.Connection.WriteInteger(Error); 
     // Si nos retorna con error paramos proceso y si no procesamos 
     Error:=ASender.Thread.Connection.ReadInteger(); 
     //Error=0 por parte del cliente se puede continuar si no salimos 
     If Error<>0 Then Exit; 
     //No hay error por parte del cliente se puede continuar 
     BufIn:=Nil; 
     //Ahora si el fichero existe lo borramos 
     //**** Podriamos utilizar la papelera pero de momento NO 
     UtilidadFiles.BorraFichero(Fichero); 
       //De momento no hacemos nada; 
     //Creamos fichero de destino 
     SFile:=TFileStream.Create(Fichero,fmCreate); 
     // Me debe decir el tamaño de paquete que quiere MODIFICADO !!!!! 
     TamanoPaq:=ASender.Thread.Connection.RecvBufferSize Div 2; 
     //Leemos el número de paquetes de recepción 
     NPaq:=ASender.Thread.Connection.ReadInteger(); 
     For x:=1 To NPaq Do 
      Begin 
           //Verificamos si me cancela la recepción 
           If ASender.Thread.Connection.ReadInteger>0 Then 
           Begin //Se pidio cancelar, eliminamos variables 
               SFile.Free; 
               //Evitamos problemas borrando el fichero destino 
               //por estar incompleto 
               UtilidadFiles.BorraFichero(Fichero); 
               Exit 
           End; 
           //Le requerimos cantidad de bytes a recibir 
           Leidos:=ASender.Thread.Connection.ReadInteger(); 
           // Establecemos el bufer de lectura que vamos a recibir 
           GetMem(BufIn,Leidos); 
           //Leemos del cliente 
           ASender.Thread.Connection.ReadBuffer(BufIn^,Leidos); 
           //Descomprimimos 
           ZDecompress(BufIn,Leidos,Buf,Escritos,TamanoPaq); 
           //Escribimos en el fichero de salida 
           SFile.Write(Buf^,Escritos); 
           //Liberamos memoria 
           FreeMem(Buf); 
           FreeMem(BufIn); 
      End; 
     If Assigned(SFile) Then SFile.Free; //Cerramos y liberamos 
     //Recibimos los aributos del fichero y la fecha de modificación 
     FHandle:=FileOpen(Fichero, fmOpenWrite); 
     SetFileAttributes(PChar(Fichero),ASender.Thread.Connection.ReadInteger); 
     FileSetDate(FHandle,ASender.Thread.Connection.ReadInteger); 
     FileClose(FHandle); 
     Except //Por si se desmadra el tema capturamos excepciones 
     If Assigned(SFile) Then SFile.Free; //Cerramos y liberamos 
     End 
end;
Como verás cada conexión tiene su propio buffer pero que lo que importa es como se gestiona.

Ya sé que hay parámetros que no entenderás o que no están documentados pero pertenecen a mi programa y son muy extensos de explicar y de documentar....

Espero que te sea ilustrativo lo anteriormente mencionado.

No soy programador. Solo ingeniero. Por lo que esto para mí es una aficción útil pero nada mas.

Saludos a todos.
__________________
www.sintecsl.es

Última edición por nlsgarcia fecha: 15-07-2015 a las 23:24:28. Razón: Sintaxis Delphi
Responder Con Cita