• Publicidad

Comunicación Sockets

¿Ya sabes lo que es una referencia? Has progresado, el nível básico es cosa del pasado y ahora estás listo para el siguiente nivel.

Comunicación Sockets

Notapor RastaCat » 2012-05-04 07:37 @359

¡Buenos días!

Estoy trabajando con mi proyecto final de carrera, y el asunto que me trae ahora entre manos es el tema de la comunicación entre sockets.

He creado un paquete para establecer la comunicación de los sockets y el envío de los datos a través de él. El problema lo tengo cuando intento enviar un mensaje a través del nuevo socket entre cliente-servidor, en el que se me queda bloqueado el servidor esperando a recibir el mensaje del cliente, y el cliente, una vez enviado el mensaje (que no llega al servidor, porque se queda esperando la respuesta), éste se queda bloqueado porque espera la respuesta del servidor.

Aquí pongo el código del paquete para establecer las conexiones:

Sintáxis: (Conex.pm) [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. package Conex;
  2.  
  3. use strict;
  4. use warnings;
  5. use Socket;
  6.  
  7. #####################
  8. # Variables privades
  9. #####################
  10.  
  11. my $port;
  12. my $proto = getprotobyname('tcp');
  13. my $socket;
  14. my $nouSock;
  15.  
  16. #$VERSION = '0.01';
  17.  
  18. #Definim la classe del constructor.
  19. sub new{
  20.         my $class = shift;
  21.     return bless {}, $class;   
  22. }
  23.  
  24. # Crea un socket i el fem reutilitzable
  25. sub crearSock{
  26.         my $self = shift;
  27.         socket($socket, PF_INET, SOCK_STREAM, $proto)
  28.                 or die "Can't open socket $!\n";
  29.         print "Socket del modul Conex.pm: $socket\n";
  30.         return($socket);
  31. }
  32.  
  33. sub reusaAdresa{  
  34.         my $self = shift;
  35.         my $nomSock = shift;
  36.         print "Socket del modul Conex.pm a nivell reusaAdresa: $nomSock\n";
  37.         setsockopt($nomSock, SOL_SOCKET, SO_REUSEADDR, 1)
  38.                 or die "No es pot establir l'opcio del socekt: SO_REUSEADDR $!\n";
  39. }
  40.  
  41. sub bindListen{
  42.         my $self = shift;
  43.         my $nomSock = shift;
  44.         $port = shift;
  45.         my $MAXCON = shift;
  46.         print "Socket del modul Conex.pm a nivell bindListen: $nomSock\n";
  47.         print "maxim connexions: $MAXCON\n";
  48.         # bind a un port, i despres escolta
  49.         print "bind port: $port ------------- bind MAXCON: $MAXCON \n\n\n\n";
  50.         bind($nomSock, pack_sockaddr_in($port, "\0\0\0\0"))
  51.                 or die "No es pot fer el bind amb el port: $port!\n";
  52.                    
  53.         listen($nomSock, $MAXCON) or die "No es pot escoltar en aquest port!\n";
  54. }
  55.  
  56. sub acceptarClient{
  57.         # Acceptant connexions
  58.        
  59.         my $self = shift;
  60.         my $nomSock = shift;
  61.         my $prova;
  62.         print "Socket del modul Conex.pm a nivell Acceptar Connexions: $nomSock\n";
  63.         accept($nouSock, $nomSock);
  64.         return($nouSock);
  65. }
  66.  
  67. sub enviaDada{
  68.         my $self = shift;
  69.         my $socket = shift;
  70.         my $missatge = shift;
  71.         sleep 1;
  72.         print $socket $missatge;
  73. }
  74.  
  75. sub tancarClient{
  76.         # Acceptant connexions
  77.        
  78.         my $self = shift;
  79.         my $nomSock = shift;
  80.         close $nomSock;
  81. }
  82.  
  83. # La ultima linia sempre ha de retornar True;
  84. 1;
  85.  
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4


Y éste es el código que utilizaran los clientes:

Sintáxis: (ConexCli.pm) [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. package ConexCli;
  2.  
  3. use lib ("**********/Projecte Final de Carrera/Xat/Conexio");
  4. use Conex;
  5.  
  6. # '@ISA = qw(Conex)' es llegeix com                          
  7. # aquest mòdul 'is a Conex',
  8. # és a dir, utilitza els mètodes del mòdul Conex
  9. # i n'hereda la seva classe.
  10. @ISA = qw(Conex);
  11.  
  12. use strict;
  13. use warnings;
  14.  
  15. use Socket;
  16.  
  17. my $iaddr;
  18. my ($paddr,$port,$proto,$adresa,$socket,$dada);
  19.  
  20. sub conectarServ{
  21.        
  22.         # Primer paràmetre és el socket a connectar-se.
  23.         # Segon paràmetre d'entrada es l'adresa del host.
  24.         # Tercer paràmetre el port a connectar-se.
  25.         print @_;
  26.         my ($self,$socket,$adresa,$port) = @_;
  27.        
  28.         $iaddr = inet_aton($adresa) || die "No Host: $adresa";
  29.         $paddr = pack_sockaddr_in($port, $iaddr);
  30.        
  31.         print "\nSocket de crear socket en client: $socket\n";
  32.        
  33.         connect($socket, $paddr) || die "connect: $!";
  34.        
  35.         print "Socket de connectar socket de client en servidor: $socket\n";
  36.         return($socket);
  37. }
  38.  
  39. sub llegirDada{
  40.         my $self = shift;
  41.         my $socket = shift;
  42.         sleep 1;
  43.         print $socket "Hello, world!\n";
  44.        
  45.         print "\nPreparar per llegir la dada! ($socket)\n";
  46.        
  47.         print <$socket>;
  48.        
  49.         print "\nS'ha rebut la dada!\n";
  50. }
  51. # La ultima linia sempre ha de retornar True;
  52. 1;
  53.  
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4



P.D.: Si lo queréis probar, donde pone:

use lib ("**********/Projecte Final de Carrera/Xat/Conexio");

debéis introducir el nombre de la carpeta donde se encuentran los códigos que he puesto en el texto.

Muchas gracias de antemano.
Adjuntos
serverX.pl
Y este es el servidor.
(2.46 KiB) 145 veces
clientX.pl
Este es el fichero cliente por si lo quereis probar.
(2.34 KiB) 129 veces
RastaCat
Perlero nuevo
Perlero nuevo
 
Mensajes: 16
Registrado: 2010-10-27 04:08 @214
Ubicación: Tarragona

Publicidad

Re: Comunicación Sockets

Notapor explorer » 2012-05-04 13:16 @595

Bienvenido de nuevo, RastaCat, después de un año... :)

Aunque ahora mismo no sé porqué se queda parado en accept() -si es que realmente está ahí-, sí que puedo darte algunas ideas...

* en algunas partes del programa usas send(), y en otras print(). No lo recuerdo dónde lo leí, pero creo recordar que decía que no había que mezclar ambas.

* no tienes activado el autoflush()... así, si no se llenan los búfer, no se envía ni se reciba nada, salvo que vayan acompañados de los "\n"... pero... también se los quitas :)

* en las subrutinas de pruebas, tienes código como este:

print <$socket>;

Al verlo, uno puede pensar que estamos esperando por la llegada de un mensaje (una línea de texto "¡Hola Mundo!"), con su carácter de retorno de carro, y lo imprimimos en pantalla... pero no es así...

Resulta que estás ejecutando <$socket> en contexto lista, por acción del print() (print admite muchos argumentos, así que, por defecto, ejecutará todos sus argumentos en contexto lista). Y un operador diamante, en contexto lista, lo que hace es leer todo lo que pueda, hasta que no haya más (se acabe el archivo o se cierre el canal).

Así que eso pasa ahí: el operador diamante quedará esperando hasta que se agote el canal (quede cerrado). Y luego le pasará todo el texto a print(), para que lo saque en pantalla.

En algunas situaciones es justo lo que queremos, pero en otras no.

Si solo queremos esperar por una sola línea de texto, debemos leer una sola línea.

Hay que cambiar la línea anterior por

my $msg = <$socket>;
print $msg;


o por esto:

print scalar <$socket>;

incluso mejor, esto:

while (<$socket>) {
print;
}


Aunque hay módulos para realizar conexiones de forma más cómoda que Socket (como Net::Server, por ejemplo), siempre está bien realizar este tipo de experiencias, para ver todo el trabajo necesario para, simplemente, conectar dos servicios.

La piedra filosofal de la comunicación con Socket, en Perl, está en el magnífico artículo de la Linux Journal del 1 de abril de 1999 (sí, trece años). Allí están todas las combinaciones de clientes y servidores, usando Socket.

Lo malo es que no pasa el tiempo, y seguro que más de una línea no se podrá ejecutar hoy en día, con los Perl modernos que tenemos ;) Y, al contrario: también hay muchas líneas que ahora sobran o se pueden abreviar, con las nuevas versiones de Socket.

Te recomiendo una lectura tranquila del artículo, ver los ejemplos que trae, y crear un pequeño código que funcione, extrayéndolo del código que tienes ahora, e ir incorporando líneas poco a poco, hasta saber el porqué falla en estos momentos.

En los Perl Cookbook, y en el libro "Network Programming with Perl", de Lincoln D.Stein, Addison Wesley, 0201615711, 2000, también hay abundante información. Si no lo encuentras, dímelo por mensaje privado.

Hoy en día, la mayor parte de la gente usa IO::Socket, IO::Socket::INET... que abrevian un poco más el código de Socket. Aquí tienes un ejemplo sencillo de cliente y servidor.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Comunicación Sockets

Notapor RastaCat » 2012-05-09 09:37 @442

Buenas tardes,

Muchas gracias por la respuesta, explorer.

He estado haciendo pruebas con las respuestas del tema, y la verdad es que no he obtenido un resultado positivo.

He observado que el accept() lo ejecuta correctamente y establece la comunicación, pero el problema, creo, es en el momento de recibir el mensaje del servidor, o el mensaje enviado del cliente al servidor. Se queda bloqueado en el "print scalar <$socket>;".

Hice una prueba de comunicación de sockets, en un mismo fichero, es decir, no usar el paquete que tengo hecho como Conex.pm y ConexCli.pm, sustituyendo la palabra "$socket: socket($socket, PF_INET..." por un nombre prueba "socket(PRUEBA, PF_INET..." al efectuar este cambio, me pasa los mensajes al servidor, y viceversa.

No sé qué hacer...

Gracias por adelantado.
RastaCat
Perlero nuevo
Perlero nuevo
 
Mensajes: 16
Registrado: 2010-10-27 04:08 @214
Ubicación: Tarragona

Re: Comunicación Sockets

Notapor Vertik » 2012-05-09 10:11 @465

Yo también ando liado con este tema ahora mismo y te aconsejo el uso de los módulos que dice explorer: IO::Socket::INET.

Aquí tienes un ejemplo básico: http://perldoc.perl.org/perlipc.html#TC ... IO::Socket

La pregunta que quería hacer (y para no abrir un tema nuevo de sockets lo publico aquí) es la siguiente:

¿Cómo puede el cliente saber si el servidor está encendido? ¿Hay una función del tipo isReady() o algo? ¿O simplemente al intentar hacer una conexión y ver que no funciona ya está? Es para indicar al cliente, Ok, no funciona porque el servidor no está encendido (sé que sale la excepción, pero me gustaría "personalizar" el mensaje de error).

¡Gracias!
Vertik
Perlero nuevo
Perlero nuevo
 
Mensajes: 41
Registrado: 2011-04-20 06:32 @314

Re: Comunicación Sockets

Notapor explorer » 2012-05-09 10:19 @472

RastaCat escribiste:Se queda bloqueado en el "print scalar <$socket>;"
Si se queda ahí... ¿quién se queda? ¿el servidor, el cliente, o los dos?

A la hora de hacer las pruebas, activa el autoflush del STDOUT (poniendo un $|=1; al principio del programa), para que, cuando hagas un print() hacia la salida estándar, salga inmediatamente. Así sabrás en qué punto están los programas.

RastaCat escribiste:Hice una prueba de comunicación de sockets, en un mismo fichero, es decir, no usar el paquete que tengo hecho como Conex.pm y ConexCli.pm, sustituyendo la palabra "$socket: socket($socket, PF_INET..." por un nombre prueba "socket(PRUEBA, PF_INET..." al efectuar este cambio, me pasa los mensajes al servidor, y viceversa.
Interesante... cambiaste el handle y funcionó... ¿seguro que el $socket estaba indefinido en el primer caso?
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Comunicación Sockets

Notapor explorer » 2012-05-09 10:23 @474

Vertik escribiste:(sé que sale la excepción, pero me gustaría "personalizar" el mensaje de error)
El mensaje de error lo puedes personalizar en el die(), del ejemplo que has enlazado.

Efectivamente: el cliente se entera de que el servidor no responde al intentar conectarse.

Se podría enterar de otra manera, si el servidor dejase otra información en otro sitio. Piensa en esas páginas web que son muy visitadas, y ponen un "indicador de carga" en la primera página, para que los usuarios vean visualmente que, si las páginas cargan más lentamente, es por la afluencia de todos los usuarios.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Comunicación Sockets

Notapor RastaCat » 2012-05-09 11:15 @510

explorer escribiste:
RastaCat escribiste:Se queda bloqueado en el "print scalar <$socket>;"
Si se queda ahí... ¿quién se queda? ¿el servidor, el cliente, o los dos?

A la hora de hacer las pruebas, activa el autoflush() del STDOUT (poniendo un $|=1; al principio del programa), para que, cuando hagas un print() hacia la salida estándar, salga inmediatamente. Así sabrás en qué punto están los programas.


Realmente se quedan los dos bloqueados, aun añadiendo el autoflush() al principio...

explorer escribiste:
RastaCat escribiste:Hice una prueba de comunicación de sockets, en un mismo fichero, es decir, no usar el paquete que tengo hecho como Conex.pm y ConexCli.pm, sustituyendo la palabra "$socket: socket($socket, PF_INET..." por un nombre prueba "socket(PRUEBA, PF_INET..." al efectuar este cambio, me pasa los mensajes al servidor, y viceversa.
Interesante... cambiaste el handle y funcionó... ¿seguro que el $socket estaba indefinido en el primer caso?


Si indefinido te refieres a pasarle directamente una variable llamada $socket al crear el socket sin inicializarla, SI, eso es lo que hago. Pero he probado de ponerle algún nombre, como el de "PRUEBA" para saber si me lo "pillaba" y podria enviar los mensajes, pero me parece que no me sirve de nada...

Este es el error que me sale al poner un nombre de parámetro en el socket del cliente y el servidor: Bad symbol for filehandle at ....../Conexio/ConexCli.pm line 36.

P.D.: Adjunto los dos ficheros de prueba que me funcionan.
Adjuntos
pcx.pl
Cliente de prueba
(1.45 KiB) 139 veces
psx.pl
Servidor de prueba
(1.91 KiB) 134 veces
RastaCat
Perlero nuevo
Perlero nuevo
 
Mensajes: 16
Registrado: 2010-10-27 04:08 @214
Ubicación: Tarragona

Re: Comunicación Sockets

Notapor explorer » 2012-05-09 16:07 @713

Pues el caso es que a mi no me funciona... tenías un error en el while() del cliente. Pusiste $PROVA, en lugar de PROVA. Pero al arreglarlo, ha dejado de funcionar (¡Aaag!)

Estás mezclando código de Socket e IO::Socket. Me parece que eso es fatal. IO::Socket ya importa Socket dentro de él, encapsulando la funcionalidad de sus funciones con otras un poco más cómodas.

Pregunta: ¿tienes, obligatoriamente, que realizar el proyecto fin de carrera con Socket?
¿No podrías cambiarlo a IO::Socket::NET?

Si ha de ser con Socket, los ejemplos que tienes que mirar y copiar son los que salen en la revista Linux Journal, enlazada antes.

A ver si puedo limpiar tu código... pero no prometo nada...
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Comunicación Sockets

Notapor RastaCat » 2012-05-10 17:23 @766

¡¡Muchas gracias, explorer!!

Me pondré manos a la obra a ver si lo puedo solucionar. ¡En cuanto tenga buenas noticias, os avisaré para compartir la solución con el foro!

Gracias de nuevo.
RastaCat
Perlero nuevo
Perlero nuevo
 
Mensajes: 16
Registrado: 2010-10-27 04:08 @214
Ubicación: Tarragona

Re: Comunicación Sockets

Notapor RastaCat » 2012-05-10 20:12 @883

Buenas noches,

Tras haber reflexionado un poco, y haberme mirado la referencia que me pasaste, explorer, he hechos grandes avances.

En primer lugar, informar que ya me funciona el servidor-cliente, y ya envía un mensaje al servidor, pero se cierra automáticamente. He hecho algunas pruebas más, y al intentar enviar un mensaje desde el servidor, los dos se quedan bloqueados (enviar mensaje tanto cliente como servidor...). He pensado en incorporar hilos en mi código, para que sea una ejecución independiente tanto como para recibir mensajes como para enviarlos.

Tengo una duda al obtener el nombre del host. Es curioso porque sólo me detecta el nombre del host si pongo el loopback (127.0.0.1), y no la IP privada de mi red (192.168...). ¿Esto es porque debo modificar el archivo hosts de mi ordenador y añadir esa IP? Si es así, ¿deberé añadir todas las direcciones IP, de los ordenadores que quiero que se quieran conectar a mi server con su IP pública?

Una vez planteada las dos preguntas, añado los códigos para que podáis ver los cambios efectuados.
Adjuntos
serverX.pl
Fichero Server
(2.77 KiB) 136 veces
clientX.pl
Fichero Cliente
(2.31 KiB) 133 veces
RastaCat
Perlero nuevo
Perlero nuevo
 
Mensajes: 16
Registrado: 2010-10-27 04:08 @214
Ubicación: Tarragona

Siguiente

Volver a Intermedio

¿Quién está conectado?

Usuarios navegando por este Foro: Google [Bot] y 9 invitados

cron