| Ver tema anterior :: Ver siguiente tema | | Autor | Mensaje |
|---|
Diabliyo
Registrado: 14 Abr 2004 Mensajes: 317 Ubicación: En la Luna
| Publicado: 05/03/2007 10:39 pm | | | Título: Chat Servidor-Cliente !! Ayuda en algunas partes del codigo! |
| Hola:
Estoy realizando un chat a nivel Consola, utilizo la plataforma Linux (distro Slackware) y esto haciendo un solo ejecutable para poder utilizarlo o iniciarlo ya sea como Servidor o como Cliente.
Ya tengo lista la parte logica del servidor, utilizando la funcion SELECT() chequeo si existen datos por leer en algun socket de los clientes conectados, y posteriormente envio la informacion del socket que envia, hacia los demas Sockets Clientes.
El unico problema es la logica cuando quiero inciar el programa como Cliente, estoy batallando un poco para saber como hacer que el Cliene haga 2 tareas, ejemplo:
1- Si existen datos en el socket por leer, pues que los lea y muestre en ventana (oviamente SELECT() soluciona el problema).
2- En caso que NO existan datos por leer, que monitoree o bien cheque que el usuario este escribiendo algo para ser enviado al chat (el mensaje).
He aqui mi problema....
Intente utilizar la funcion alarm(segundos), pero esta no respeta que el usuario este escribiendo algo (fgets()), simplamente al sobre pasar el tiempo en segundos, este envia una senal y finaliza la aplicacion.
La unica que se me ocurre es que fgets() NO se bloquee, osea que fgets() no se quede esperando datos del usuario, en caso que NO existan datos, que este continue con sus operaciones.
Pero mi pregunta seria: Como Hago a fgets() no bloqueante ???...
byee _________________
|
| | Volver arriba | |  | rir3760

Registrado: 01 Oct 2004 Mensajes: 3841 Ubicación: Mexico
| Publicado: 06/03/2007 7:36 am | | | Título: |
| No es posible ya que conceptos como multiples hilos de ejecucion y funciones re-entrantes no existen en el lenguaje C y su biblioteca estandar.
Tienes que utilizar extensiones provistas por el compilador y/o SO.
Un saludo _________________ The capacity to learn is a gift; The ability to learn is a skill; The willingness to learn is a choice. -- Rebec of Ginaz |
| | Volver arriba | |  | tonilope
Registrado: 16 Oct 2005 Mensajes: 249
| | | Volver arriba | |  | | Diabliyo
Registrado: 14 Abr 2004 Mensajes: 317 Ubicación: En la Luna
| Publicado: 08/03/2007 11:02 pm | | | Título: |
|
Hola:
Oyes esta muy chido el programa que hiciste de cateaitor, es justamente lo que pretendo hacer yo, pero como veo, por mas que analizo el code, aun no logro entenderlo :S, ya que no tiene mucha explicacion al parecer y bueno... como kiera me imprimie el code e ire analizandolo poco a poco !!...
Ya hasta me siento al mostrarte mi code jijiji, apenas empeze hace 3 dias mi chat (tipo el tuyo), pero me interesaria aprender como le haces para eso que hiciste con chateitor.... Acaso usaste hilos ???... porke aun no leo todo el code, ya que apenas HOY lo baje !!!...
En cuestion pues mis unica dos preguntas son:
1- usaste solo hilos ?? 2- utilizaste la funcion select() ??
byee _________________
|
| | Volver arriba | |  | tonilope
Registrado: 16 Oct 2005 Mensajes: 249
| Publicado: 09/03/2007 7:03 am | | | Título: |
| Hola. Pues yo uso 2 hilos. Uno para enviar y otro para recibir. Así de simple. En mi caso, no uso select() ya que el chat sólo admite una conexión al mismo tiempo entre servidor y cliente. Si tienes dudas concisas, pregúntalas.
Salu2  _________________ Be (void *) my friend |
| | Volver arriba | |  | Diabliyo
Registrado: 14 Abr 2004 Mensajes: 317 Ubicación: En la Luna
| Publicado: 09/03/2007 4:09 pm | | | Título: |
| | tonilope escribió: | Hola. Pues yo uso 2 hilos. Uno para enviar y otro para recibir. Así de simple. En mi caso, no uso select() ya que el chat sólo admite una conexión al mismo tiempo entre servidor y cliente. Si tienes dudas concisas, pregúntalas.
Salu2  |
Ok, estre leyendo el codigo y entendiendolo para preguntar de una forma mas solida, solo tengo una ultima duda antes de empezar a imprimirme el code jojojo !!!..
Me imagino que utilizaste un socket no-bloqueado, osease para no tener que estar esperando por un recv ??? cierto ????
byee _________________
|
| | Volver arriba | |  | | tonilope
Registrado: 16 Oct 2005 Mensajes: 249
| Publicado: 09/03/2007 4:25 pm | | | Título: |
| | Cita: | | Me imagino que utilizaste un socket no-bloqueado, osease para no tener que estar esperando por un recv ??? cierto ???? |
Justo al "verrés" Un hilo está continuamente esperando con recv(), mientras que el otro está a la espera de que el usuario teclee algo y lo envíe son send().
Me piro a dormir que mañana madrugo.
Salu2  _________________ Be (void *) my friend |
| | Volver arriba | |  | Diabliyo
Registrado: 14 Abr 2004 Mensajes: 317 Ubicación: En la Luna
| Publicado: 19/03/2007 10:06 am | | | Título: |
| Bueno... He realizado un programaita de ejemplo muy simple para ver si asi va el uso de Hilos en Sockets sobre Linux !!!.... Aki dejo las 3 funciones para que chequen el poke de mi error al intentar realizar el objeto despues de haber compilado:
Error:
| Código: | shell# gcc -c 007_2.c shell# gcc -o 007_2 007_2.o 007_2.c(.text+0x3ef):In function 'inicia_servidor' : undefined reference to 'pthread_create' 007_2.c(.text+0x438):In function 'inicia_servidor' : undefined reference to 'pthread_create' |
- inicia_servidor :: funcion que inicia el servidor y crea 2 hilos, donde 1 hilo estara reciviendo datos (recv) y el 2do hilo estara tomando datos del teclado y enviandolos al cliente de forma directa.
- recivir_datos :: funcion que invoca a recv() e imprime datos.
- enviar_datos :: funcion que espera datos del teclado para despues enviarlos (send()).
Funcion inicia_servidor:
| Código: | void inicia_servidor( void ) { int sockds, clientds, bytes_r=0; struct sockaddr_in servidor; struct sockaddr_in cliente; int size_c=0, aux=1; pthread_t hilo_s, hilo_r;
servidor.sin_family= AF_INET; servidor.sin_port= htons(PUERTO); inet_aton( "127.0.0.1", &servidor.sin_addr ); memset( &servidor.sin_zero, '\0', 8 );
if( (sockds=socket( AF_INET, SOCK_STREAM, 0 ))==-1 ) fprintf( stderr, "\nProblemas para Crear Socket :: %s", strerror(errno) ); else { if( setsockopt( sockds, SOL_SOCKET, SO_REUSEADDR, &aux, sizeof(int) )==-1 ) fprintf( stderr, "\nProblemas para Reutilizar Direccion :: %s", strerror(errno) );
if( bind( sockds, (struct sockaddr *)&servidor, sizeof(struct sockaddr) )==-1 ) fprintf( stderr, "\nProblemas para Asociar Socket :: %s", strerror(errno) ); else { printf( "\n\nEsperando Cliente.....\n" ); if( listen( sockds, LIMITE )==-1 ) fprintf( stderr, "\nProblemas para Escuchar en el Socket :: %s", strerror(errno) ); else { size_c= sizeof(struct sockaddr); if( (clientds= accept( sockds, (struct sockaddr *)&cliente, &size_c ))==-1 ) fprintf( stderr, "\nProblemas para Aceptar Cliente :: %s", strerror(errno) ); else { if( pthread_create( &hilo_r, NULL, recivir_datos, (void *)clientds )!=0 ) fprintf( stderr, "\nProblemas para crear Hilo de Recepcion :: %s", strerror(errno) ); if( pthread_create( &hilo_s, NULL, enviar_datos, (void *)clientds )!=0 ) fprintf( stderr, "\nProblemas para crear Hilo de Teclado :: %s", strerror(errno) ); } pthread_exit( &hilo_r ); pthread_exit( &hilo_s ); close(clientds); } } close(sockds); } } |
Funcion enviar_datos:
| Código: | void *enviar_datos( void *sock ) { char buf[N]; int clientds;
clientds= *(int *)sock;
memset( &buf, '\0', N );
printf( "\nDato a Enviar: " ); fgets( buf, N, stdin ); buf[strlen(buf)-1]='\0';
if( send( clientds, buf, strlen(buf), 0 )==-1 ) fprintf( stderr, "\nProblema al Enviar Datos :: %s", strerror(errno) ); else { printf( "\n---> Mensaje Enviado con exito...\n" ); memset( &buf, '\0', N ); } }
|
Funcion recive_datos:
| Código: | void *recivir_datos( void *sock ) { char buf[N]; int clientds, bytes_r=0;
clientds= *(int *)sock; memset( &buf, '\0', N );
if( (bytes_r=recv( clientds, buf, N, 0 ))==-1 ) fprintf( stderr, "\nProblemas para Recivir Datos :: %s", strerror(errno) ); else { printf( "\nMensaje Recivido: %s", buf ); printf( "\nDimension: %i", strlen(buf) ); printf( "\nBytes: %i", bytes_r ); } }
|
Y las includes que uso son:
| Código: | #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>
#define N 1024 |
byee _________________
|
| | Volver arriba | |  | Diabliyo
Registrado: 14 Abr 2004 Mensajes: 317 Ubicación: En la Luna
| Publicado: 20/03/2007 4:21 pm | | | Título: |
| Hola:
Listo, solo era cuestion de compilar linkeando pthread, asi:
| Código: | shell# gcc -c codico.c shell# gcc -o codigo codigo.0 -lpthread shell#./codigo |
Pero me marca este error:
| Código: | Segmnetation fault |
El error esta en la parte del codigo del servidor, bueno, NO es un error de CODIGO, es de ejecucion al momento de crear los hilos, pero no encuentro la logica del error :S, alguien me podria ayudar ???
Esto es un poco del code donde hago los hilos:
| Código: | void inicia_servidor( void ) { int sockds, clientds, bytes_r=0; struct sockaddr_in servidor; struct sockaddr_in cliente; int size_c=0, aux=1; pthread_t hilo_s, hilo_r;
servidor.sin_family= AF_INET; servidor.sin_port= htons(PUERTO); inet_aton( "127.0.0.1", &servidor.sin_addr ); memset( &servidor.sin_zero, '\0', 8 );
if( (sockds=socket( AF_INET, SOCK_STREAM, 0 ))==-1 ) fprintf( stderr, "\nProblemas para Crear Socket :: %s", strerror(errno) ); else { if( setsockopt( sockds, SOL_SOCKET, SO_REUSEADDR, &aux, sizeof(int) )==-1 ) fprintf( stderr, "\nProblemas para Reutilizar Direccion :: %s", strerror(errno) );
if( bind( sockds, (struct sockaddr *)&servidor, sizeof(struct sockaddr) )==-1 ) fprintf( stderr, "\nProblemas para Asociar Socket :: %s", strerror(errno) ); else { printf( "\n\nEsperando Cliente.....\n" ); if( listen( sockds, LIMITE )==-1 ) fprintf( stderr, "\nProblemas para Escuchar en el Socket :: %s", strerror(errno) ); else { size_c= sizeof(struct sockaddr); if( (clientds= accept( sockds, (struct sockaddr *)&cliente, &size_c ))==-1 ) fprintf( stderr, "\nProblemas para Aceptar Cliente :: %s", strerror(errno) ); else { if( pthread_create( &hilo_r, NULL, recivir_datos, (void *)clientds )!=0 ) fprintf( stderr, "\nProblemas para crear Hilo de Recepcion :: %s", strerror(errno) ); if( pthread_create( &hilo_s, NULL, enviar_datos, (void *)clientds )!=0 ) fprintf( stderr, "\nProblemas para crear Hilo de Teclado :: %s", strerror(errno) ); } //pthread_join( &hilo_r ); //pthread_join( &hilo_s ); pthread_exit( &hilo_r ); pthread_exit( &hilo_s ); close(clientds); } } close(sockds); } }
void *recivir_datos( void *sock ) { char buf[N]; int clientds, bytes_r=0;
clientds= *(int *)sock; memset( &buf, '\0', N );
if( (bytes_r=recv( clientds, buf, N, 0 ))==-1 ) fprintf( stderr, "\nProblemas para Recivir Datos :: %s", strerror(errno) ); else { printf( "\nMensaje Recivido: %s", buf ); printf( "\nDimension: %i", strlen(buf) ); printf( "\nBytes: %i", bytes_r ); } }
void *enviar_datos( void *sock ) { char buf[N]; int clientds;
clientds= *(int *)sock;
memset( &buf, '\0', N );
printf( "\nDato a Enviar: " ); fgets( buf, N, stdin ); buf[strlen(buf)-1]='\0';
if( send( clientds, buf, strlen(buf), 0 )==-1 ) fprintf( stderr, "\nProblema al Enviar Datos :: %s", strerror(errno) ); else { printf( "\n---> Mensaje Enviado con exito...\n" ); memset( &buf, '\0', N ); } } |
byee _________________
|
| | Volver arriba | |  | | quique
Registrado: 30 Mar 2004 Mensajes: 720 Ubicación: Por ahí
| Publicado: 22/03/2007 8:24 am | | | Título: |
| Un par de comentarios:
solo le he echado un vistazo por encima, pero a ver si puedo servir de ayuda.
1) pthread_exit () debe ir en la funcion que se ejecuta en la thread. Es decir:
| Código: | void *recivir_datos( void *sock ) { /* Your code */ pthread_exit (0); }
|
2) en tu loop principal, falta eso, el loop: comienzas una nueva conexion con accept, y sigues para adelante, sin ningun loop infinito ni nada de nada. Ten en cuenta que cuando termina el programa principal, todas tus threads se eliminaran, ademas, estas cerrando un socket por ahí....
3) por qué usas threads y no fork () ? (curiosidad, normalmente los daemons usan fork)
En fin, que a ver si esto ayuda
Salu2 _________________ /* no comment */ |
| | Volver arriba | |  | Diabliyo
Registrado: 14 Abr 2004 Mensajes: 317 Ubicación: En la Luna
| Publicado: 22/03/2007 8:34 am | | | Título: |
| Hola:
Problema resuelto, solo se necesitaba corregir asi, dentro de las funciones de enviar_datos() y recivir_datos():
| Código: | clientds= *(int *)sock; //esta es incorrecta clientds= (int)sock; //correcta xD |
-- quique Sobre tu comentario del pthread_exit(), esta funcion tiene 2 operaciones dependiendo desde donde se invoque, en este caso al ser invocada desde el hilo (dentro de la funcion) nos sirve como un: return. Si pthread_exit() es invocado con un valor NULL o con el ID del Hilo, este retorna y finaliza el HILO, encambio si es invocado con un valor cualquiera, nos sirve para posteriormente fuera del hilo o su funcion que invoca, utilizar la funcion pthread_join() para esperara un hilo o recepcionar el valor del hilo. Posteriormente si se invoca a pthread_exit() desde fuera del hilo, esto solo nos servira para finalizar el hilo.
Sobre lo del fork(), pues porke es mas costoso hacerlo con fork(), ya que fork() hace una copia identica de todo el programa lo cual seria un gasto inutil de recursos, en cambio el hilo es mas rapido y con menos gasto de recursos !!
byee byee _________________
|
| | Volver arriba | |  | quique
Registrado: 30 Mar 2004 Mensajes: 720 Ubicación: Por ahí
| Publicado: 23/03/2007 4:42 am | | | Título: |
| Por partes:
| Cita: | | Sobre tu comentario del pthread_exit(), esta funcion tiene 2 operaciones dependiendo desde donde se invoque, en este caso al ser invocada desde el hilo (dentro de la funcion) nos sirve como un: return. Si pthread_exit() es invocado con un valor NULL o con el ID del Hilo, este retorna y finaliza el HILO, encambio si es invocado con un valor cualquiera, nos sirve para posteriormente fuera del hilo o su funcion que invoca, utilizar la funcion pthread_join() para esperara un hilo o recepcionar el valor del hilo. Posteriormente si se invoca a pthread_exit() desde fuera del hilo, esto solo nos servira para finalizar el hilo. |
Podemos intentar leer juntos "man pthread_exit" y "man pthread_join", o mirar otras opiniones, porque no entendemos lo mismo: http://www.chuidiang.com/clinux/procesos/procesoshilos.php http://www.chuidiang.com/clinux/procesos/mashilos.php (vierte opiniones personales, sin indicar nada de kernel, pero para empezar no está mal)
| Cita: | Sobre lo del fork(), pues porke es mas costoso hacerlo con fork(), ya que fork() hace una copia identica de todo el programa lo cual seria un gasto inutil de recursos, en cambio el hilo es mas rapido y con menos gasto de recursos !!
|
qué recursos? a que te refieres, a RAM o memoria virtual? o a tiempo de respuesta? o a tiempo de creacion? a que linux kernel haces referencia? 2.4 o 2.6 u otro? Que entiendes por copia identica?
Estas apreciaciones que haces dependen de tantos factores que hay que tener cuidado, puede que alguien se las crea.
Probablemente estés hablando de un P4 con 1.7 GHz y 512 Mb RAM al menos, y que tengas un kernel 2.6.... _________________ /* no comment */ |
| | Volver arriba | |  | | Diabliyo
Registrado: 14 Abr 2004 Mensajes: 317 Ubicación: En la Luna
| Publicado: 29/03/2007 10:26 pm | | | Título: |
| | quique escribió: | Por partes:
| Cita: | | Sobre tu comentario del pthread_exit(), esta funcion tiene 2 operaciones dependiendo desde donde se invoque, en este caso al ser invocada desde el hilo (dentro de la funcion) nos sirve como un: return. Si pthread_exit() es invocado con un valor NULL o con el ID del Hilo, este retorna y finaliza el HILO, encambio si es invocado con un valor cualquiera, nos sirve para posteriormente fuera del hilo o su funcion que invoca, utilizar la funcion pthread_join() para esperara un hilo o recepcionar el valor del hilo. Posteriormente si se invoca a pthread_exit() desde fuera del hilo, esto solo nos servira para finalizar el hilo. |
Podemos intentar leer juntos "man pthread_exit" y "man pthread_join", o mirar otras opiniones, porque no entendemos lo mismo: http://www.chuidiang.com/clinux/procesos/procesoshilos.php http://www.chuidiang.com/clinux/procesos/mashilos.php (vierte opiniones personales, sin indicar nada de kernel, pero para empezar no está mal)
| Cita: | Sobre lo del fork(), pues porke es mas costoso hacerlo con fork(), ya que fork() hace una copia identica de todo el programa lo cual seria un gasto inutil de recursos, en cambio el hilo es mas rapido y con menos gasto de recursos !!
|
qué recursos? a que te refieres, a RAM o memoria virtual? o a tiempo de respuesta? o a tiempo de creacion? a que linux kernel haces referencia? 2.4 o 2.6 u otro? Que entiendes por copia identica?
Estas apreciaciones que haces dependen de tantos factores que hay que tener cuidado, puede que alguien se las crea.
Probablemente estés hablando de un P4 con 1.7 GHz y 512 Mb RAM al menos, y que tengas un kernel 2.6.... |
En fin me refiero en que FORK() me crea una copia completa del programa y por lo cual NO quisiera crear una copia del programa por cada cliente que entra :S... solo kieor crear hilos para hacer tareas !!!...
Por ejemplo:
un hilos para: SELECT() un hilos para: fgets(), RECV()
... _________________
|
| | Volver arriba | |  | Diabliyo
Registrado: 14 Abr 2004 Mensajes: 317 Ubicación: En la Luna
| | | Volver arriba | |  | quique
Registrado: 30 Mar 2004 Mensajes: 720 Ubicación: Por ahí
| Publicado: 30/03/2007 5:32 am | | | Título: |
| Dejemos de lado la discusion de si fork o thread, porque no es cuestion de este hilo: tu te has decidido por threads, así que adelante.
Respecto al ejemplo, si no llamas a la funcion pthread_exit () DENTRO de la thread, puedes tener "memory leaks". Tu mismo.
El código de un programilla sería
| Código: | #include <stdio.h> #include <pthread.h>
void *saludo (void *buf) { char *local_buf = (char *) buf; printf ("Thread: %s\n", local_buf); pthread_exit (0); }
int main (int argc, char **argv) { pthread_t mythread; char stAux [] = "Hello World!"; pthread_create (&mythread, NULL, saludo, stAux); pthread_join (mythread, NULL); return 0; }
|
que compila con: gcc CFILE -o EXECFILE -pthread
Salu2 _________________ /* no comment */ |
| | Volver arriba | |  | | |
| No puede crear mensajes No puede responder temas No puede editar sus mensajes No puede borrar sus mensajes No puede votar en encuestas
|
|
| |