Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Principal > Internet
Registrarse FAQ Miembros Calendario Guía de estilo Buscar Temas de Hoy Marcar Foros Como Leídos

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 17-10-2013
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 12.559
Poder: 27
dec Va camino a la famadec Va camino a la fama
Usar TServerSocket y TClientSocket para enviar "streams" más o menos "grandes"

Hola,

Por favor, ¿alguien podría proporcionarme un ejemplo de cómo usar los componentes "TServerSocket" y "TClientSocket" para enviar y recibir "streams" más o menos "grandes"? Hablo de enviar un "stream" que no pueda enviarse de una sola vez, en un sólo paquete "TCP/IP", sino que, necesariamente, haya que tratar (como parece exigir dicho protocolo) dicho "stream" con el cuidado correspondiente, tanto a la hora de enviarlo como de recibirlo.

Estoy usando estos componentes en uno de mis proyectos, pero, al limitarme a usar los métodos "SendText" y "ReceiveText" de los "Sockets", ahora me encuentro conque no tengo ni idea de cómo hacer para enviar cadenas de texto "grandes", tal vez en un "MemoryStream" o "StringStream". Comprendo el problema, he buscado soluciones, pero, al cabo no consigo algo que funcione y que me permita implementarlo en mi proyecto. Ya, ya sé que yo mismo podría hacer que funcionase,... pero me temo que a esto no llego...

¿Alguien se acuerda de tener guardado por ahí algún ejemplo que envíe y reciba archivos, por ejemplo, y donde se usen estos componentes?

Cualquier otra ayuda sería bien recibida, puesto que tengo que encontrar una solución para este asunto, y, pienso que lo del ejemplo podría ser ideal (claro, algo que funcione... debe ser sencillo de hacer funcionar en otro lugar), pero, no por ello dejaré de buscar (como ya he hecho) otras posibles soluciones. Así que si no es un ejemplo pero se os ocurre cualquier otra cosa, por favor, no dejéis de comentarla.

Muchas gracias de antemano a todos.
__________________
David Esperalta
www.davidesperalta.com
Responder Con Cita
  #2  
Antiguo 17-10-2013
Avatar de ecfisa
ecfisa ecfisa is offline
Moderador
 
Registrado: dic 2005
Ubicación: Tres Arroyos, Argentina
Posts: 9.875
Poder: 27
ecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to behold
Hola dec.

Te aclaro que mis roces con ClientSocket y ServerSocket no pasaron de una prueba hace algunos años. Así que la única ayuda que humildemente puedo brindarte en este caso es la búsqueda.

Por si alguno se te escurrió, encontré estos enlaces que me parecieron tener relación con el asunto: Espero que alguno te traiga algo de luz...

Saludos
__________________
Daniel Didriksen

Guía de estilo - Uso de las etiquetas - La otra guía de estilo ....
Responder Con Cita
  #3  
Antiguo 17-10-2013
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 12.559
Poder: 27
dec Va camino a la famadec Va camino a la fama
Hola,

Cita:
Empezado por ecfisa Ver Mensaje
Hola dec.

Te aclaro que mis roces con ClientSocket y ServerSocket no pasaron de una prueba hace algunos años. Así que la única ayuda que humildemente puedo brindarte en este caso es la búsqueda.

Por si alguno se te escurrió, encontré estos enlaces que me parecieron tener relación con el asunto: Espero que alguno te traiga algo de luz...

Saludos
Gracias ecfisa. Ayer me volví loco buscando información, así que a ver si hoy soy capaz de tomármelo con más calma. Por supuesto que voy a revisar esos enlaces que has encontrado. Muchas gracias por tu interés.
__________________
David Esperalta
www.davidesperalta.com
Responder Con Cita
  #4  
Antiguo 17-10-2013
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 12.559
Poder: 27
dec Va camino a la famadec Va camino a la fama
Hola,

Sigo liado con este asunto. Finalmente, parece que he conseguido algo que funciona relativamente bien. Me he limitado a copiar y pegar cierto código de Remy Lebeau, para qué vamos a engañarnos. Yo creo comprender "el conceto", pero, no soy capaz de implementar una solución por mí mismo. Ahora bien, el caso es que parece que ahora sí puedo enviar y recibir grandes cadenas de texto, de hecho cadenas de texto de más de 100 MB...

Aunque realmente esto no ha solucionado mi problema (porque mi proyecto es una DLL que funciona en una aplicación de terceros, y, aunque yo consigo enviar estas grandes cadenas de texto en la aplicación de Delphi que uso para las pruebas, el mismo código no funciona de la misma manera en dicha DLL -cuelga la aplicación si se envían digamos que más de 200 KB) me gustaría que echárais un vistazo a la aplicación de pruebas hecha en Delphi.

El objetivo de subir dicha aplicación de pruebas aquí es, sobre todo, que podáis echar un vistazo a dos métodos en particular:

1º TMainForm.SendFromClientButtonClick()

2º TMainForm.ServerClientRead()

El primer método envía la cadena de texto, teniendo en cuenta que acaso no pueda hacerse de una vez. Al mismo tiempo envía la cadena añadiendo al final de esta una especie de "token" (los caracteres #6#7) que después utilizaremos en el segundo método, de manera que podamos "leer" hasta llegar al final de la cadena, identificado por dichos caracteres.

Me interesa de veras que echéis un vistazo y acaso podáis decirme si existe alguna forma de mejorar dichos métodos, puesto que de este modo pueda lograr que dicho código fuente en mi proyecto de mejor manera. Me preocupa especialmente la siguiente línea del primer método:

Código Delphi [-]
sent := Client.Socket.SendBuf( Pointer(s)^, Length(s) - idx);

En efecto, el código original de Remy era tal que así:

Código Delphi [-]
sent := Client.Socket.SendBuf(PChar(s)+idx, Length(s)-idx);

Sin embargo, si dejo dicho código tal cual se produce el error:

Código:
[DCC Error] UMainForm.pas(225): E2197 Constant object cannot be passed as var parameter
Así que en mi ignorancia he hecho los cambios que se ven arriba, pensando que acaso se tratara de un error de Remy (no realmente un error, pero, tal vez él estaba escribiendo dicho código sin comprobarlo). El caso es que, como he dicho, el código parece funcionar bien (con el cambio que yo he introducido) y lo que quisiera es ver si se está haciendo alguna barbaridad o se encuentra forma de mejorarlo.

Para probar la aplicación que adjunto no tenéis sino compilarla y ejecutarla dos veces: la misma aplicación puede hacer de servidor y de cliente. Así que escoger la instancia que hará de servidor y hacer clic en el botón "Active the server". A continuación, en la otra instancia del programa clic en el botón "Connect to the server".

Hecho eso ya podréis hacer clic (también en esta última instancia, la que hacer de cliente) en el botón "Send from client". Si os fijáis lo que hago es leer un determinado archivo de texto (a.txt) y mandar su contenido al servidor. Este a su vez tomará dicho texto y lo guardará en un archivo de nombre "b.txt". Ambos archivos deben ser al final iguales.

Podéis probar a aumentar el tamaño del archivo "a.txt", simplemente, añadiendo más texto al mismo, para ver hasta dónde es capaz el programa. Y, como digo, sobre todo, acaso alguien pueda darme alguna indicación para mejorar los dos métodos que he mencionado arriba, y que resumen el problema de enviar y recibir grandes cantidades de texto mediante "sockets" TCP.

Por favor, no dejéis de comentar cualquier problema (puedo enviaros los binarios del programa si queréis) y/o si necesitáis más información o cualquier otra cosa.

Muchas gracias.
Archivos Adjuntos
Tipo de Archivo: zip TCPTest.zip (8,5 KB, 21 visitas)
__________________
David Esperalta
www.davidesperalta.com

Última edición por dec fecha: 17-10-2013 a las 22:15:56.
Responder Con Cita
  #5  
Antiguo 18-10-2013
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 12.559
Poder: 27
dec Va camino a la famadec Va camino a la fama
¡Arriba!
__________________
David Esperalta
www.davidesperalta.com
Responder Con Cita
  #6  
Antiguo 18-10-2013
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 12.559
Poder: 27
dec Va camino a la famadec Va camino a la fama
Hola,

No sé si al final lograré algo mejor que lo que tengo ahora, empero, me parece más menos claro que el "cuello de botella" está a la hora de recibir el texto enviado. Dicho texto se "recorre byte a byte", puesto que el "protocolo" manda, y, es preciso buscar los dos últimos "bytes" del final, que, son los que nos sirven para determinar que el texto se terminó de enviar.

Parece claro que el asunto sería mejor si en lugar de necesitar leer "byte a byte" pudiéramos leer directamente un "stream" mayor. Pero hete aquí que el problema sigue siendo que es menester conocer el tamaño de nuestro "stream", y, para esto sólo parece haber dos soluciones: enviar primero el tamaño de nuestro "stream" y a continuación enviar el "stream" mismo.

Sin embargo lo dicho no parece sencillo (para mí) de llevar a cabo. Tal como está ahora, incluso cuando el código no es mío y hay bastantes cosas que se me escapan, por lo menos creo comprender lo que se trata de hacer. Es lento, sí, pero, funciona. Lo malo es que en mi proyecto (una DLL que se encaja en otro programa) no funciona tan bien como en una aplicación de Delphi.

Aún cuando tenemos que recorrer byte a byte el texto que se envía, una aplicación Delphi puede hacerlo relativamente rápido incluso cuando hablamos de una cadena de texto de más de 100 MB... pero mi proyecto DLL no puede siquiera recibir 1 MB sin colgar a la aplicación en cuya DLL reside. De ahí la necesidad de probar con "streams", de enviar "algo" y sobre todo recibir "algo" que no tengamos que revisar "byte a byte".

Y en esas estamos... no sé si alguien ha leído todos estos mensajes y si me explico lo suficiente. En todo caso si logro algún avance lo comentaré por aquí.

Y si alguien puede y quiere echarme una mano será bienvenida.
__________________
David Esperalta
www.davidesperalta.com
Responder Con Cita
  #7  
Antiguo 18-10-2013
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 12.559
Poder: 27
dec Va camino a la famadec Va camino a la fama
Hola,

Parece claro que esto:

Código Delphi [-]
sent := Client.Socket.SendBuf( Pointer(s)^, Length(s) - idx);

... no tiene mucho sentido. Funciona, porque, las pruebas las estoy haciendo "en local", y, en realidad TODO el texto se envía a la vez, porque, en realidad es lo que estamos haciendo ahí... es decir, la primera llamada tendría sentido, pero, una segunda llamada no lo tendría. El error no se ve (si no es a simple vista, pero, a mí se me escapa) si no se sabe que, en verdad, dicha instrucción se ejecuta una sola vez y no, como se espera, varias veces. Las sucesivas llamadas no tendrían sentido, pues estaríamos enviando de nuevo TODO el texto... peor aún, todo el texto "menos idx"...

Es decir, que el código de Remy:

Código Delphi [-]
sent := Client.Socket.SendBuf(PChar(s)+idx, Length(s)-idx);

... tiene otro sentido. No compila, seguramente, porque está escrito sin probarlo, pero, el sentido está claro: enviar a cada llamada únicamente el texto restante, o sea, el que no se haya enviado ya. Así que, tratando de remediar dicho asunto, he escrito algo como esto:

Código Delphi [-]
procedure TMainForm.SendFromClientButtonClick(Sender: TObject);

  // Just to get the text to send from our "a.txt" file
  function GetTextToSend() : string;
  var
    t : TStrings;
  begin
    t := TStringList.Create();
    try
      t.LoadFromFile( 'a.txt' );
      result := t.Text;
    finally
      t.Free();
    end;
  end;

var
  s : string;
  bytesSent : integer;
  totalBytesSent : integer;
  textToSendLen : integer;
  textToSend : TStringStream;
  buffer : array [0..1023] of byte;
begin
  s := GetTextToSend() + #6#7; 

  textToSend := TStringStream.Create( EmptyStr );
  try
    textToSendLen := Length( s );
    textToSend.WriteString( s );
    textToSend.Position := 0;

    totalBytesSent := 0;
    repeat
      FillChar( buffer, 1024, 0 );
      textToSend.Read( buffer, 1024 );
      Application.ProcessMessages();

      bytesSent := Client.Socket.SendBuf( buffer, SizeOf( buffer ) );

      if bytesSent = -1 then // Wait for writing
      begin
        Sleep( 50 );
        Continue;
      end;

      if bytesSent = 0 then // Disconnected
      begin
        Exit;
      end;

      Inc( totalBytesSent, bytesSent );

    until textToSendLen <= totalBytesSent;

  finally
    textToSend.Free();
  end;
end;

Y, aunque ahora sí se supone que estamos haciendo las cosas mejor, lo cierto es que tampoco me termina de convencer, puesto que en realidad estamos forzando el envío de 1024 bytes cada vez, sin contar acaso conque a veces podría enviarse más... y a veces también menos...

En definitiva no soy capaz de dar con la tecla. No digamos ya implementar el asunto utilizando "streams", a cuyo inicio añadamos el tamaño del mismo. Lo sigo intentando, pero, no hay manera.
__________________
David Esperalta
www.davidesperalta.com
Responder Con Cita
  #8  
Antiguo 19-10-2013
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 12.559
Poder: 27
dec Va camino a la famadec Va camino a la fama
Hola,

Seguimos avanzando... De nuevo utilizando código de Remy (hablo de él como si le conociese de algo... je je je) creo, repito, creo haber conseguido que el envío se realice correctamente, esto es, teniendo en cuenta posibles errores y el hecho crucial: que no siempre se enviará TODO el texto de golpe, sino que dependerá de las necesidades del "socket" en cuestión.

Todo (el envío) funciona como se espera, excepto por el hecho de que realmente no puedo comprobar qué ocurriría de veras si el "socket" no admitiese todo el texto de golpe, porque, aunque todavía no me lo explico, al menos en las pruebas que hago (con servidor y cliente en la misma máquina) TODO el contenido se consigue enviar al mismo tiempo, de manera que el código añadido no llega realmente a comprobarse... aunque todo parezca indicar que funcionará.

Adjunto la actualización del programa de ejemplo, aunque, lo cierto es que seguimos con el mismo "cuello de botella" al recibir el texto. Y mientras siga leyendo lo que se recibe "byte a byte" voy a seguir con el mismo problema, me temo. En fin... seguiremos intentándolo a ver.
Archivos Adjuntos
Tipo de Archivo: zip TCP.zip (8,4 KB, 8 visitas)
__________________
David Esperalta
www.davidesperalta.com
Responder Con Cita
  #9  
Antiguo 04-08-2015
osmeg osmeg is offline
Miembro
 
Registrado: may 2014
Posts: 18
Poder: 0
osmeg Va por buen camino
Buenas noches Dec,

De antemano gracias por el recurso compartido de transferencia de archivos. Me tome el atrevimiento de tomar el código que adjuntaste para revisar su comportamiento porque necesito realizar algo similar. Sin embargo, a pesar de que realizo los pasos tal cual como los indicas en un mensaje anterior para el envío de archivos, no logro que se cree el archivo b.txt (ni en el computador local como en otro de la misma red). Sé que esta discusión es algo avanzada (llevo poco programando Delphi) pero me gustaría poder realizar esa transferencia de archivos. No he modificado el código que adjuntaste.

Con respecto al problema, el código indicado en la parte inferior perteneciente al método ServerClientRead. Este código nunca es ejecutado porque la cadena s llega vacía, pero no encuentro el motivo.

Código Delphi [-]
    if s <> '' then
    begin
    t := TStringList.Create();
    try
      t.Text := s;
      t.SaveToFile( 'b.txt' );
    finally
      t.Free();
    end;

    SLog( 'Server Client Read Received ' );
    end;

Entiendo que este post es un poco viejo pero agradezco si me puedes orientar en algun paso adicional que sea necesario realizar para que la transferencia del archivo sea satisfactoria.
Responder Con Cita
  #10  
Antiguo 04-08-2015
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 12.559
Poder: 27
dec Va camino a la famadec Va camino a la fama
Hola,

Probando el archivo adjunto de mi último mensaje el ejemplo no parece funcionar bien, al menos aquí en Windows 10. Sin embargo, probando el programa en que estoy usando "Sockets" y que originó este hilo sí que puedo enviar y recibir cadenas de un cliente a un servidor y viceversa. Lo cierto es que ahora mismo on sabría bien qué responderte. Quizá el código de abajo, usado en mi programa, te dé una idea y te sirva de algo:

Código Delphi [-]
unit USocketsCommom;

{
  The code of this unit is mainly based in Socket's
  related code found on Internet and who's author
  is Remy Lebeau, a TeamB team and Delphi developer.
}

interface

uses
  ScktComp;

procedure SendStringToSocket( Socket:
 TCustomWinSocket; Buffer: string );

implementation

uses
  Forms, SysUtils, Classes, WinSock;

procedure SendMemoryStreamToSocket( socket :
 TCustomWinSocket; buffer : TMemoryStream);
var
  dataPtr: PByte;
  numSent: integer;
begin
  buffer.Position := 0;
  dataPtr := PByte( buffer.Memory );
  while buffer.Position < buffer.Size do
  begin
    numSent := socket.SendBuf
    (
      dataPtr^,
      buffer.Size - buffer.Position
    );

    if numSent < 0 then
    begin
      if WSAGetLastError() = WSAEWOULDBLOCK then
      begin
        Sleep( 50 );
        Continue;
      end;
      Exit;
    end;

    if numSent = 0 then
    begin
      Exit; // socket disconnected
    end;

    Inc(dataPtr, numSent);
    buffer.Seek(numSent, soFromCurrent);
    Application.ProcessMessages();
  end;
end;

procedure SendStringToSocket( socket:
 TCustomWinSocket; buffer: string );
var
  ms : TMemoryStream;
begin
  ms := TMemoryStream.Create();
  try
    ms.WriteBuffer( Pointer( buffer )^, Length( buffer ) );
    SendMemoryStreamToSocket( socket, ms );
  finally
    ms.Free();
  end;
end;

end.

Ese es el código que uso en mi programa para enviar cadenas en ambos cliente y servidor. Dicho código parece el resumen de este hilo, en el sentido de que es el que al final estoy usando en mi programa, y, como digo, este funciona como se espera incluso aquí en Windows 10.
__________________
David Esperalta
www.davidesperalta.com
Responder Con Cita
Respuesta


Herramientas Buscar en Tema
Buscar en Tema:

Búsqueda Avanzada
Desplegado

Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro

Temas Similares
Tema Autor Foro Respuestas Último mensaje
El programa se queda "colgado" mientras copia y luego "despierta" NeWsP OOP 5 10-03-2010 23:05:40
"OBJECT OR CLASS TYPE REQUIRED" en "APPLICATION EXENAME" Xavierator Varios 3 27-10-2008 10:09:50
Necesito llamar a métodos de clases "hija" desde su clase "padre" Flecha OOP 17 20-04-2007 01:03:53
Firebir y usar "IF" en la clausula "SELECT" papulo SQL 6 25-07-2006 22:38:04


La franja horaria es GMT +2. Ahora son las 22:36:40.


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