19 septiembre 2008

Enviar mensajes entre aplicaciones con IdUDPClient

En el último artículo vimos como enviar mensajes entre dos aplicaciones sin utilizar ningún protocolo de comunicación cuando los dos ejecutables se encuentran en el mismo PC. Pero si el mensaje hay que enviarlo entre dos ejecutables que se encuentran en dos PC distintos estamos obligados a utilizar un protocolo de comunicación y el más sencillo es el protocolo UDP.

El protocolo UDP goza de la ventaja de que no se establece una conexión entre la IP origen y la IP destino, sino que envía la información sin saber si va a llegar o no (al contrario de TCP que si establece una conexión segura entre ambos puntos).

El protocolo UDP es utilizado comúnmente en aplicaciones P2P para envío rápido de mensajes entre clientes y servidores así como en juegos online donde es necesario transmitir las coordenadas de cada personaje en tiempo real sin que existan demoras.

CREANDO LA APLICACIÓN QUE ENVÍA EL MENSAJE

Creamos un nuevo proyecto y en el formulario principal insertamos estos componentes:


Va a constar de dos componentes TEdit para guardar la IP del destinatario y su puerto (que por defecto le hemos puesto el 80). También va a tener un componente TMemo llamado Mensaje que va a contener las líneas de texto a enviar. Luego añadimos el componente IdUDPClient que se encuentra dentro de la paleta de componentes Indy Clients y que vamos a llamar Cliente.

En el evento OnClick del botón Enviar escribimos el siguiente código:

procedure TFEnviar.BEnviarClick(Sender: TObject);
begin
Cliente.Host := IP.Text;
Cliente.Port := StrToIntDef( Puerto.Text, 0 );
Cliente.Send( Mensaje.Text );
end;

Es algo tan sencillo como pasarle la IP destino (el Host), el puerto y ejecutamos el método Send que envía la información.

El único inconveniente que tiene este método es que no sabe si el destinatario existe o si le ha llegado el mensaje, pero es muy rápido.

CREANDO LA APLICACIÓN QUE RECIBE EL MENSAJE

Volvemos a crear un nuevo proyecto con este formulario:


Sólo va a contener un componente TMemo llamado Mensaje y el componente IdUPDServer que llamaremos Servidor. Al servidor le vamos a poner en su propiedad Port el valor 80. Después lo activamos poniendo su propiedad Active a True.

Ahora escribimos este código en el evento OnUDPRead:

procedure TFRecibir.ServidorUDPRead(Sender: TObject; AData: TBytes;
ABinding: TIdSocketHandle);
begin
Mensaje.Lines.Add( 'De: ' + ABinding.PeerIP );
Mensaje.Lines.Add( 'Mensaje: ' + PChar( AData ) );
end;

Mediante la propiedad ABinding.PeerIP obtenemos la IP del que envía el mensaje y la variable AData contiene el mensaje enviado en bytes.

Al ejecutar ambos programas y pulsar el botón Enviar este sería el resultado:


Si el mensaje se va a enviar entre dos aplicaciones que se encuentran en el mismo PC sólo hay que poner como dirección IP la 127.0.0.1 y funciona perfectamente. Incluso si seguimos escribiendo mensajes se añadirán a los que ya están en el destino.

Con algo tan simple como esto ya tenemos un pequeño cliente de mensajería entre dos equipos de la red local o en Internet. Para que la conversación pudiera ser bidireccional habría que crear un solo programa que tuviera un cliente UDP y un servidor UDP. También habría que dejar a elegir al usuario que IP y puertos va a utilizar para enviar y recibir.

Si la información que vamos a enviar es confidencial también sería interesante que las cadenas de caracteres que se envíen fueran encriptadas de algún modo.

Pruebas realizadas en RAD Studio 2007.

24 comentarios:

Anónimo dijo...

uooo gracias,tienes pensado algun articulos sobre tidy, wininet o bien curl?

Jhonatan dijo...

mi amigo, este articulo se ve que esta jevi jevi pero quisiera por favor k como lo hago cuando se hace en delphi 7, ya que cuando pongo las lineas para recivir el mensaje me sale un mensaje de error. espero k Dios te ayude a aprender mas para k tambien nos sigan enzañando:-)

Administrador dijo...

Hola anómino, respecto a escribir sobre tidy, wininet o curl, lo primero que tengo que hacer es investigarlos y luego escribo sobre esto. No se puede saber todo de todo.

Primero me gustaría abarcar todo lo que se pueda hacer nativamente con Delphi y Win32 antes de tirarme a librerías externas.

Ahora bien, si conoces algún artículo en inglés donde pueda sacar la información para aplicarla a Delphi estaré encantado de escribir sobre cualquier tema.

Saludos.

Administrador dijo...

Hola Jhonatan, me alegro de que te guste el artículo.

El problema que tienes es que los que programan la librería Indy no saben lo que significa "compatiblidad hacia atrás". Con cada versión nueva cambian los nombres de los métodos de las clases y eso hace que tu código fuente deje de funcionar.

Por eso me he pasado a Delphi 2007 donde al parecer los componentes Indy se han estabilizado (aunque si te fijas en el código fuente verás que sigue siendo igual de pestoso).

Si tengo tiempo, ampliaré este artículo para indicar como enviar mensajes con Delphi 7 y IdUDPClient.

Saludos.

Unknown dijo...

Estimados amigos:

Antes que nada, felicidades Administrador por tu blog!!!

De los ejemplos de Indy 9 obtuve la respuesta para poderlo trabajar en D7:
-------------
procedure TFRecibir.ServidorUDPRead(Sender: TObject; AData: TBytes;
ABinding: TIdSocketHandle);
var
LMsg: String;
begin
// Mover de un Stream a un String
SetLength(LMsg, AData.Size);
AData.ReadBuffer(LMsg[1], AData.Size);

Mensaje.Lines.Add( 'De: ' + ABinding.PeerIP );
Mensaje.Lines.Add( 'Mensaje: ' + Copy(LMsg, 1, MaxInt) );
end;
------------
Saludos.

Administrador dijo...

Muchas gracias Jorge por tu aportación.

A ver si entre todos conseguimos una buena artillería para programar en todas las versiones de Delphi.

Saludos.

Anónimo dijo...

hola. puse las 2 ventanas en ña misma aplicacion y me da este error:


[DCC Error] Unit3.pas(28): E2003 Undeclared identifier: 'TIdSocketHandle'

en la linea
procedure ServidorUDPRead(Sender: TObject; AData: TBytes;
ABinding: TIdSocketHandle);

si me pudieras ayudar denuevo :D

gracias de antemano.

Anónimo dijo...

Encontre la solucion!

era algo muy parecido al problema que da el idtcpserver.
solo hay que agregar a uses

idSocketHandle
idGlobal


:)

Anónimo dijo...

primero os felicito por ayudar a la gente y por su calidad,tengo un problema por que no puedo enviar informacion por puertos que no comunes por ejemplo uno alto 3456.no se como hacerlo para abrirlo.un saludo

Daniel dijo...

Hola a todos, esta aplicacion esta muy bien y podria valer. Pero que ocurre si las dos Ip's de las dos maquinas estan en una red privada, es decir 192.168.x.x contra otra 10.0.0.x en otra red. Y lo que tampoco quiero hacer es andar modificando la configuracion del NAT en mi router, redireccionando puertos. Imaginemos que instalo mi aplicacion en mi empresa, y quiero, pongamos que chatear o p2p, con mi casa, no podria pedirle a administrador de sistemas que me habra el puerto xx y que me lo redirija a mi maquina.
Como se hace esto?

gracias por adelantado, Daniel.

Administrador dijo...

Hace tiempo tuve que resolver este problema para establecer un chat entre clientes de una empresa en los que habían varios centros.

La solución más rápida y fiable es mediante redes privadas virtuales (VPN).
Instalé en ambos centros el programa Hamachi y pude conectar mi programa con el puerto 80 sin problemas y sin redidecccionar puertos en el NAT de los router ni abrir cortafuegos.

A veces nos empeñamos en reinventar la rueda cuando hay aplicaciones gratuitas y complementarias que nos pueden solucionar la vida.

Saludos.

Anónimo dijo...

probe tal como esta, pero al recibir el mensaje me manda asi
De: 127.0.0.1
Mensaje: ð A.
aclaro que hice la prueba en un solo pc. si alguien puede decirme por q pasa esto? gracias.

Administrador dijo...

Como cada versión Indy es de su padre y de su madre, ocurre que el mensaje no llega limpio y hay que averiguar exactamente el tamaño del mismo en el evento OnRead:

procedure TFPrincipal.ServidorUDPUDPRead(Sender: TObject; AData: TBytes;
ABinding: TIdSocketHandle);
var
sMensaje: String;
begin
// Cogemos del buffer el mensaje enviado
sMensaje := Copy(PChar(AData), 1, Length(AData));

Así la variable sMensaje tiene el mensaje limpio.

Saludos.

Anónimo dijo...

quiero hacer en una sola ventana el servidor y el cliente solo que necesito escoger cuando implemento el servidor y cuando el cliente. Quisiera saber como lo podria hacer
gracias

Administrador dijo...

Puedes hacer que dentro de la ventana tenga dos pestañas: una para el cliente y otra para el servidor.

Yo tengo algo así para enviar mensajes dentro de la red local y utilizo dos componentes: un servidor UDP y un cliente UDP (todo dentro del mismo programa). Con el servidor recibo mensajes de otros clientes y con el cliente envío mensajes a otros servidores.

Así se produce una comunicación bidireccional entre ambos.

Anónimo dijo...

el problema con las pestañas que dice es q me crea los componentes fuera de las ventanas de cada una osea fuera de la ventana envio y recibo
ahi que ??

Anónimo dijo...

Hola gente...

Para todos los que preguntan como hacer el programa y que tenga el cliente y server en uno solo tienen q usar los frame.

tienen q ir a file->new->frame
y en el q se habre crean el enviar, crean otro para el recibir, desp en el form ponen un page control, crean las dos pestañas, y cuando estan en la pestaña enviar por ejemplo, van al componente frames que se encuentra en standard y les aparece una ventana donde seleccionan el frame enviar que crearon, y lo mismo hacen con la pestaña recibir, y eso es suficiente

HES dijo...

el mensaje me sale como caracteres extraños

HES dijo...

ya hay que colocar el stringof(data) en delphi 2010

Administrador dijo...

Gracias por la aclaración. Al final lo has solucionado tu solo.

Sopongo que será como siempre por el tema de los caracteres unicode que añadieron a partir de Delphi 2009.

Saludos.

S. Guirao dijo...

Me ha servido vuestros comentarios y quiero darles las gracias por la aportacion

S. Guirao dijo...

Muchas gracias por vuestras aportaciones desinteresadas que me han ayudao mucho
Saludos
sebas

Unknown dijo...

hola , estoy en delphi xe4, me salen caracteres extraños en el memo, como lo puedo resolver?

gracias.

Administrador dijo...

Eso es porque en las versiones nuevas de Delphi los caracteres son unicode.

Tendrás que convertirlo a string con caracteres ASCII para que salga normal.

Publicidad