Abrir multiples conexiones Sockets C

Iniciado por mester, 9 Noviembre 2015, 12:32 PM

0 Miembros y 2 Visitantes están viendo este tema.

mester

Hola.
Estoy introduciéndome en el mundo de los sockets en Linux y haciendo tanto cliente como servidor quise iniciar mi primer chat. Hasta ahí bien, pero ahora lo que quiero es que el servidor del chat pueda soportar varias conexiones (que hacerlo, lo hace) pero que lea también de éstas. Es decir, este "chat" solo funciona por turnos jeje. Escribo mi código:
Servidor:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
int main(int argc, char **argv){
if(argc<2){
printf("%s <puerto>\n",argv[0]);
return 1;
}
int con_ser,con_cli,puerto;
int I;
socklen_t clilen;
char *buffer;
struct sockaddr_in cliente;
struct sockaddr_in servidor;
con_ser=socket(AF_INET,SOCK_STREAM,0);
if(con_ser<0){
printf("Error creando el socket\n");
close(con_ser);
return 1;
}
bzero((char *)&servidor,sizeof(servidor));
puerto=atoi(argv[1]);
servidor.sin_family = AF_INET;
servidor.sin_port = htons(puerto);
servidor.sin_addr.s_addr = INADDR_ANY;
I=bind(con_ser,(struct sockaddr *)&servidor,sizeof(servidor));
if(I<0){
printf("Error al asignar el puerto\n");
close(con_ser);
return 1;
}
listen(con_ser,3);
clilen = sizeof(cliente);
con_cli=accept(con_ser,(struct sockaddr *)&cliente,&clilen);
if(con_cli<0){
printf("Error en la transaccion\n");
close(con_ser);
return 1;
}
buffer=calloc(220,sizeof(char));
do{
I=read(con_cli,buffer,220);
if(I<0){
printf("Error recibiendo información\n");
close(con_ser);
return 1;
}
if(strcmp(buffer,"101001001")!=0)
printf("%s",buffer);
else{
printf("EOF\n");
break;
}
free(buffer);
buffer=calloc(220,sizeof(char));
printf("%s: ",getlogin());
fgets(buffer,220,stdin);
I=write(con_cli,buffer,220);
if(I<0){
                       printf("Error enviando información\n");
                       close(con_ser);
                       return 1;
               }
}while(1);
return 0;
}


Cliente:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#define RED     "\x1b[31m"
#define GREEN   "\x1b[32m"
#define YELLOW  "\x1b[33m"
#define BLUE    "\x1b[34m"
#define MAGENTA "\x1b[35m"
#define CYAN    "\x1b[36m"
#define RESET   "\x1b[0m"
int cmp(char *s1, char *s2, char *s3){
int ret = 0;
strcat(s3,": ");
strcat(s3,s2);
while(!(ret=*(unsigned char *)s1 - *(unsigned char *)s3) && *s3)
++s1, ++s3;
if(ret<0)
ret=-1;
if(ret > 0)
ret=1;
return ret;
}
int main(int argc, char **argv){
if(argc<3){
printf(YELLOW"%s <host> <puerto>\n"RESET,argv[0]);
return 1;
}
int conexion,puerto,i;
char *frase;
char *buffer;
struct hostent *server;
struct sockaddr_in cliente;
server=gethostbyname(argv[1]);
if(server==NULL){
printf(RED"Host erróneo, introduce el host\n"RESET);
return 1;
}
conexion=socket(AF_INET,SOCK_STREAM,0);
puerto=atoi(argv[2]);
cliente.sin_family = AF_INET;
cliente.sin_port = htons(puerto);
bcopy((char *)server->h_addr,(char *)&cliente.sin_addr.s_addr,sizeof(server->h_length));
i=connect(conexion,(struct sockaddr *)&cliente,sizeof(cliente));
if(i<0){
printf("Error conectando con el host\n");
close(conexion);
return 1;
}
printf(YELLOW"Escribe "RED"\"EOF\""RESET" para salir\n"RESET);
do{
buffer=calloc(220,sizeof(char));
if(buffer==NULL){
printf("No se ha podido reservar memoria\n");
close(conexion);
return 1;
}
frase=calloc(200,sizeof(char));
if(frase==NULL){
printf("No se ha podido reservar memoria\n");
                       close(conexion);
                       return 1;
               }
printf(YELLOW"%s: "RESET,getlogin());
fgets(frase,200,stdin);
strcat(buffer,getlogin());
strcat(buffer,": ");
strcat(buffer,frase);
if(cmp(buffer,"EOF\n",getlogin())==0){
write(conexion,"101001001",10);
break;
}
i=write(conexion,buffer,220);
if(i<0){
printf(RED"Error al enviar los datos\n"RESET);
break;
}
free(frase);
i=read(conexion,buffer,220);
if(i<0){
                       printf(RED"Error al recibir los datos\n"RESET);
                       break;
               }
printf("%s",buffer);
}while(1);
close(conexion);
free(buffer);
return 0;
}


Se agradece de antemano
Justicia es dar a cada uno lo que se merece

ivancea96

Los sockets abiertos, los conservas en un array. Luego, basta iterar por cada socket del array, y leer en caso de que haya bytes que leer, y enviar datos si es necesario.

Deberías estructurar tu programa. Crea funciones para hacer el código legible.

mester

Cita de: ivancea96 en  9 Noviembre 2015, 13:35 PM
Los sockets abiertos, los conservas en un array. Luego, basta iterar por cada socket del array, y leer en caso de que haya bytes que leer, y enviar datos si es necesario.

Deberías estructurar tu programa. Crea funciones para hacer el código legible.
Supongo que entiendo lo que dices, pero no sé aplicarlo. ¿puedes explicármelo de diferente manera si es posible?
Se agradece
Justicia es dar a cada uno lo que se merece

ivancea96

Te pongo un ejemplo práctico:


socket *sockets;
listener li;

crearArray(&sockets);
inicializarSocketDeEscucha(li);

while(true){
    socket sock;
    sock = nuevoCliente(listener);
    if(sock) añadirSocket(&sockets, sock);
    porCadaSocket -i-{
        escucharYResponder(sockets[i]);
    }
}


En fin, es un esquema, con funciones que creo que entiendes lo que hacen. En bucle, haces estas acciones:
-1: Encuentras nuevos clientes si los hay.
-2: Escuchas y respondes a cada cliente. Si por ejemplo un cliente te envía un mensaje de chat, reenvías ese mensaje a todos los demás clientes.

mester

Cita de: ivancea96 en 10 Noviembre 2015, 09:17 AM
Te pongo un ejemplo práctico:


socket *sockets;
listener li;

crearArray(&sockets);
inicializarSocketDeEscucha(li);

while(true){
    socket sock;
    sock = nuevoCliente(listener);
    if(sock) añadirSocket(&sockets, sock);
    porCadaSocket -i-{
        escucharYResponder(sockets[i]);
    }
}


En fin, es un esquema, con funciones que creo que entiendes lo que hacen. En bucle, haces estas acciones:
-1: Encuentras nuevos clientes si los hay.
-2: Escuchas y respondes a cada cliente. Si por ejemplo un cliente te envía un mensaje de chat, reenvías ese mensaje a todos los demás clientes.
El problema es que cuando espero una conexión entrante con la función 'accept()' el programa se detiene esperando una conexión entrante. Y con el cliente, cuando le indico que reciba con 'recv()' hasta que no recibe información no puedes seguir escribiendo.
Agradezco el ejemplo de codigo, ahora mismo me pongo a organizarlo.
Justicia es dar a cada uno lo que se merece

ivancea96

Los sockets pueden ser "bloqueantes" o "no bloqueantes". En el caso que comentas, probablemente sea bloquenate. Para ponerlo "non-blocking", no te puedo ayudar, que no utilizo unix. Busca por ahí, y encontrarás.

De todos modos, tal ocmo los tienes, otra opción es tener un thread para leer del servidor, y luego otro thread para los sockets ya abiertos (o un thread por socket, tú verás).

Claro que, para recv, me parece más cómodo un socket no bloqueante. Lo dicho, echa un vistazo para ver cómo cambiar.

En caso de sockets de Windows, se haría así:
u_long block = 1 // 1 or 0, bloquing or non blocking;
ioctlsocket(sock, FIONBIO, &block);

mester

Cita de: ivancea96 en 10 Noviembre 2015, 10:53 AM
Los sockets pueden ser "bloqueantes" o "no bloqueantes". En el caso que comentas, probablemente sea bloquenate. Para ponerlo "non-blocking", no te puedo ayudar, que no utilizo unix. Busca por ahí, y encontrarás.

De todos modos, tal ocmo los tienes, otra opción es tener un thread para leer del servidor, y luego otro thread para los sockets ya abiertos (o un thread por socket, tú verás).

Claro que, para recv, me parece más cómodo un socket no bloqueante. Lo dicho, echa un vistazo para ver cómo cambiar.

En caso de sockets de Windows, se haría así:
u_long block = 1 // 1 or 0, bloquing or non blocking;
ioctlsocket(sock, FIONBIO, &block);


He estado probando a ponerlo non-blocking, de momento funciona, ahora, no sabría como desarrollar el programa para que tanto cliente como servidor puedan enviar mensajes sin parar, y el otro los reciba, es decir, que me gustaría que el fgets() no bloquease el programa a la espera de una entrada de datos, para hacerlo más fácil, sino, se acumulan todos los mensajes. Escribo las funciones de la conexión:


int conectar(int sock, struct sockaddr_in client){
        char *frase;
        char *buffer;
        if(connect(sock,(struct sockaddr *)&client,sizeof(client))<0)
                error(1,inet_ntoa(client.sin_addr));
        else
                printf("Conectando con %s:%d\n\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
        printf("\t\t\t\t%s\n",inet_ntoa(client.sin_addr));
        fcntl(sock,F_SETFL,O_NONBLOCK);
        frase=(char *)calloc(200,sizeof(char));
        buffer=(char *)calloc(220,sizeof(char));
        while(1){
                printf("%s: ",getlogin());
                if(leer(sock)!=0)
                        continue;
                fgets(frase,200,stdin);
                if(strcmp(frase,"EOF\n")==0){
                        free(frase);
                        free(buffer);
                        break;
                }
                buffer=cmp(frase,getlogin());
                if(send(sock,buffer,220,0)<0)
                        error(2,inet_ntoa(client.sin_addr));
        }
        free(frase);
        free(buffer);
return 0;
}
int leer(int conexion){
        int c;
        char *buffer;
        buffer=(char *)calloc(200,sizeof(char));
        if(recv(conexion,buffer,200,0)>0){
                printf("%s",buffer);
                c=0;
        }
        else
                c=1;
return c;
}


Agradezco tu ayuda, me es muy útil para desarrollar mi programa.
Justicia es dar a cada uno lo que se merece

ivancea96

Para hacerlo con consola, y querer leer de consola, lo puedes hacer con 2 threads, uno para leer la consola, y otro para trabajar el/los sockets.

De todos modos, ten en cuenta algo: si recibes un mensaje mientras escribes, la consola escribirá sobre lo que tú estás escribiendo. Para practicar está bien, pero bueno.

Para eso, lo dicho, threads.

mester

#8
Cita de: ivancea96 en 10 Noviembre 2015, 18:32 PM
Para hacerlo con consola, y querer leer de consola, lo puedes hacer con 2 threads, uno para leer la consola, y otro para trabajar el/los sockets.

De todos modos, ten en cuenta algo: si recibes un mensaje mientras escribes, la consola escribirá sobre lo que tú estás escribiendo. Para practicar está bien, pero bueno.

Para eso, lo dicho, threads.

Vale ya lo tengo, ahora necesito saber cómo matar un fork. He estado buscando, pero con kill(pid,SIGKILL); no me funciona, te muestro el codigo:
(MODIFICADO)
Ya lo he hecho para que funcione. Funciona mal, porque mato directamente el proceso, pero bueno, ya lo mejoraré. Posteo aquí el codigo tanto de cliente como de servidor.

Cliente:

#include<stdio.h>
#include<wait.h>
#include<signal.h>
#include<sys/types.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#include<fcntl.h>
void usage(const char *arg){
printf("%s <host> <puerto>\n",arg);
exit(1);
}
void error(int codigo, const char *err){
switch(codigo){
case 0: printf("La dirección del Host no es válida\n"); break;
case 1: printf("Error conectando con el host %s\n",err); break;
case 2: printf("Error enviando los datos a %s\n",err); break;
}
exit(1);
}
char *cmp(char *palabra, char *nombre){
        char *mem=(char *)calloc(220,sizeof(char));
        strcat(mem,nombre);
        strcat(mem,": ");
        strcat(mem,palabra);
return mem;
}
void leer(int);
int escribir(int,struct sockaddr_in);
int conectar(int, struct sockaddr_in);
int main(int argc, char **argv){
if(argc<3)
usage(argv[0]);
int conexion, puerto;
struct sockaddr_in cliente;
struct hostent *servidor;
servidor=gethostbyname(argv[1]);
if(servidor==NULL)
error(0," ");
conexion=socket(AF_INET,SOCK_STREAM,0);
puerto=atoi(argv[2]);
cliente.sin_family=AF_INET;
cliente.sin_port=htons(puerto);
bcopy((char *)servidor->h_addr,(char *)&cliente.sin_addr,sizeof(servidor->h_length));
conectar(conexion, cliente);
send(conexion,"78421687541295",15,0);
close(conexion);
return 0;
}
int conectar(int sock, struct sockaddr_in client){
int c;
int id;
int estado;
if(connect(sock,(struct sockaddr *)&client,sizeof(client))<0)
error(1,inet_ntoa(client.sin_addr));
else
printf("Conectando con %s:%d\n\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
printf("Escribe \"EOF\" para salir\n");
printf("\t\t\t\t%s\n",inet_ntoa(client.sin_addr));
fcntl(sock,F_SETFL,O_NONBLOCK);
id=fork();
if(id==0){
while(1){
leer(sock);
sleep(1);
}
}
while(1){
c=escribir(sock,client);
if(c==1)
break;
}
kill(id,SIGKILL);
return 0;
}
void leer(int conexion){
char *cadena;
cadena=(char *)calloc(220,sizeof(char));
if(recv(conexion,cadena,220,0)>0)
printf("\n%s",cadena);
else
free(cadena);
}
int escribir(int conexion,struct sockaddr_in client){
char *buffer=(char *)calloc(220,sizeof(char));
char *cadena=(char *)calloc(200,sizeof(char));
printf("%s: ",getlogin());
fgets(cadena,200,stdin);
buffer=cmp(cadena,getlogin());
if(strcmp(cadena,"EOF\n")==0)
return 1;
if(send(conexion,buffer,220,0)<0)
error(2,inet_ntoa(client.sin_addr));
free(buffer);
free(cadena);
return 0;
}


Servidor:

#include<stdio.h>
#include<wait.h>
#include<sys/types.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<netdb.h>
#include<fcntl.h>
void usage(const char *arg){
printf("%s <puerto>\n",arg);
exit(1);
}
char *cmp(char *palabra, char *nombre){
        char *mem=(char *)calloc(220,sizeof(char));
        strcat(mem,nombre);
        strcat(mem,": ");
        strcat(mem,palabra);
return mem;
}
void escribir(int);
int recibir(int);
int escuchar(int, struct sockaddr_in);
int main(int argc, char **argv){
if(argc<2)
usage(argv[0]);
int conexion_servidor, puerto;
char *buffer;
struct sockaddr_in servidor;
puerto=atoi(argv[1]);
conexion_servidor=socket(AF_INET,SOCK_STREAM,0);
servidor.sin_family=AF_INET;
servidor.sin_port=htons(puerto);
servidor.sin_addr.s_addr=INADDR_ANY;
escuchar(conexion_servidor,servidor);
printf("Hasta luego\n");
close(conexion_servidor);
return 0;
}
int escuchar(int conexion_servidor, struct sockaddr_in servidor){
struct sockaddr_in cliente;
int c;
int id;
int conexion_cliente;
int clilong;
if(bind(conexion_servidor,(struct sockaddr *)&servidor,sizeof(servidor))<0){
                printf("Error. El puerto está en uso\n");
                close(conexion_servidor);
                return 1;
        }
        listen(conexion_servidor,5);
        clilong=sizeof(cliente);
        printf("Escuchando en el puerto %d\n",htons(servidor.sin_port));
        conexion_cliente=accept(conexion_servidor,(struct sockaddr *)&cliente,&clilong);
        if(conexion_cliente<0){
                printf("Error en la asociacion con el host %s\n",inet_ntoa(cliente.sin_addr));
                close(conexion_servidor);
                return 1;
        }
        else
                printf("Conectado con %s:%d\n",inet_ntoa(cliente.sin_addr),ntohs(servidor.sin_port));
fcntl(conexion_cliente,F_SETFL,O_NONBLOCK);
id=fork();
if(id==0){
while(1){
sleep(1);
c=recibir(conexion_cliente);
if(c==1)
break;
}
}
while(c!=1)
escribir(conexion_cliente);
kill(id,SIGKILL);
return 0;
}
int recibir(int conexion){
int c=0;
char *buffer=(char *)calloc(220,sizeof(char));
if(recv(conexion,buffer,220,0)>0){
if(strcmp(buffer,"78421687541295")!=0)
printf("\n%s",buffer);
else
c=1;
}
free(buffer);
return c;
}
void escribir(int conexion){
char *buffer=(char *)calloc(220,sizeof(char));
char *cadena=(char *)calloc(200,sizeof(char));
printf("SERVIDOR: ");
fgets(cadena,200,stdin);
buffer=cmp(cadena,"SERVIDOR");
if(send(conexion,buffer,220,0)<0)
printf("Error enviando la información\n");
}
Justicia es dar a cada uno lo que se merece

ivancea96

Matarlo es muy bruto. En vez de eso, cambia el while(1) por una variable que compartan, y cuando quieras que se detenga, pones esa variable a 0, por ejemplo.