Programación de Sockets y Threads en C: Servidor de Votaciones, Chat y Gestión de Clientes Conectados

Informática7,51 KB

Servidor concurrente en C: votaciones, clientes conectados y chat

Este documento describe la lógica de un servidor concurrente implementado en lenguaje C mediante sockets y hilos POSIX (pthread). El servidor atiende a varios clientes simultáneamente, gestiona un sistema de votaciones, mantiene la lista de clientes conectados y reenvía mensajes entre ellos.

1. Procesamiento del código de votación

Cuando el código recibido es 1, el servidor acumula los votos de un partido político:

if (codigo == 1) // acumular votos
{ 
    // recupero el nombre del partido 
    p = strtok( NULL, "/");
    char nombre[20];
    strcpy (nombre, p);

    // recupero los votos
    p = strtok( NULL, "/");
    int votos = atoi (p);

    // acumulamos votos
    pthread_mutex_lock( &mutex );
    AcumularVotos (&listaPartidos, nombre, votos);
    pthread_mutex_unlock( &mutex);

    

// Ahora notifico a todos los conectados el nombre del partido que gana en este momento

char ganador [20]; strcpy (ganador, DameGanador (&listaPartidos)); sprintf (respuesta, "2/%s,", ganador); int j; for (j=0; j<i; j++) write (sockets[j], respuesta, strlen(respuesta)); }

Consulta de votos de un partido

Con el código 2, el cliente solicita los votos que lleva un partido:

else if (codigo == 2) // nos piden los votos que lleva un partido
{
    // recupero el nombre del partido
    p = strtok( NULL, "/");
    char nombre[20];
    strcpy (nombre, p);

    int votos = DameVotos (&listaPartidos, nombre);

    sprintf (respuesta, "1/%d,", votos);
    write (sock_conn, respuesta, strlen(respuesta));
}
else if (codigo == 0) // desconexión
    fin = 1;

2. Consulta de clientes conectados

Este bloque atiende las peticiones relacionadas con los clientes conectados:

if (codigo == 1) // El cliente nos pide el anterior conectado
{
    Nombre anterior;
    strcpy (anterior, DameAnterior (&conectados, nombre));

    sprintf (respuesta, "1/%s,", anterior);
    write (sock_conn, respuesta, strlen(respuesta));
}

Número de clientes conectados

else if (codigo == 2) // Nos pide el número de conectados
{
    sprintf (respuesta, "2/%d,", conectados.num);
    write (sock_conn, respuesta, strlen(respuesta));
}
else if (codigo == 0) // desconexión
{
    // Enviamos a todos el nombre del cliente que se desconecta
    sprintf (respuesta, "3/%s,", nombre);
    int j;
    for (j=0; j<i; j++)
        write (sockets[j], respuesta, strlen(respuesta));
    fin = 1;
}

3. Protocolo de mensajes del servicio de chat

El servidor implementa los siguientes códigos de mensaje:

  1. Reenvío de frases: recibe una frase de un cliente y la reenvía a todos los clientes activos.
    Mensaje recibido: 1/Frase
    No envía ninguna respuesta al cliente que envía la frase.
  2. Desactivar cliente: marca a un cliente como no activo; a partir de ese momento no recibirá las frases de los demás.
    Mensaje recibido: 2/
    No hay respuesta.
  3. Activar cliente: marca de nuevo a un cliente como activo.
    Mensaje recibido: 3/
    No hay respuesta.
  4. Desconexión:
    Mensaje recibido: 0/
    No hay respuesta.

El servidor también envía notificaciones: cuando recibe la frase de un cliente, la reenvía a todos los clientes activos en ese momento. El mensaje es directamente la frase, sin código, porque es el único tipo de mensaje que el servidor envía a los clientes.

4. Variables globales y estructuras de control

int sockets [100]; // vector para guardar los sockets
int estado [100]; // vector para guardar el estado de cada cliente (1 es activo y 0 es no activo)
int num;

Procesamiento de los mensajes del chat

if (codigo == 1) // El cliente envía una frase
{
    // recupero la frase
    p = strtok( NULL, "/");

    char frase[100];
    strcpy (frase, p);

    // Ahora envío la frase a todos los clientes activos (menos al que envía la frase)
    int j = 0;
    // Envío a todos
    for (j=0; j<num; j++)
        if ((j != i) && (estado[j] == 1))
            write (sockets[j], frase, strlen(frase));
}
else if (codigo == 2) // pasamos al cliente a estado no activo
{
    pthread_mutex_lock( &mutex);
    estado[i] = 0;
    pthread_mutex_unlock( &mutex);
}
else if (codigo == 3) // pasamos al cliente a estado activo
{
    pthread_mutex_lock( &mutex);
    estado[i] = 1;
    pthread_mutex_unlock( &mutex);
}
else // desconexión
{
    // Aprovecho para marcar al cliente que se desconecta como no activo
    pthread_mutex_lock( &mutex);
    estado[i] = 0;
    pthread_mutex_unlock( &mutex);

    fin = 1;
}

5. Función principal (MAIN): aceptación de clientes

Cada vez que se acepta una nueva conexión, el servidor registra el socket, marca al cliente como activo y crea un hilo dedicado para atenderlo:

pthread_mutex_lock( &mutex);
sockets[num] = sock_conn;
estado[num] = 1; // inicialmente el cliente está activo
pthread_mutex_unlock( &mutex);

indices[num] = num;
pthread_create(&thread[num], NULL, AtenderCliente, &indices[num]);

num = num + 1;

6. Registro de nuevos conectados y mensajes de felicitación

Alta de un nuevo cliente

Con el código 1, el servidor recibe los datos del nuevo cliente con el formato 1/nombre/año/ciudad:

if (codigo == 1) // Recibo datos del nuevo conectado ("1/nombre/año/ciudad")
{
    p = strtok( NULL, "/");
    strcpy (nombre, p);

    p = strtok( NULL, "/");
    ano = atoi (p);

    p = strtok( NULL, "/");
    strcpy (ciudad, p);

    pthread_mutex_lock( &mutex );
    PonConectado (&miLista, nombre, ciudad, ano, sock_conn);
    pthread_mutex_unlock( &mutex);
}

Envío de felicitaciones

Los códigos 2 y 3 permiten enviar una felicitación a los clientes del mismo año (código 2) o de la misma ciudad (código 3):

else if ((codigo == 2) || (codigo == 3))
{
    char felicitacion[100];

    p = strtok( NULL, "/");
    strcpy (felicitacion, p);

    // preparo la notificación
    sprintf (notificacion, "%s quiere felicitarte con el siguiente mensaje: %s", nombre, felicitacion);

    int i;
    if (codigo == 2) // felicitamos a los del mismo año
    {
        for (i=0; i<miLista.num; i++)
            if (miLista.conectados[i].ano == ano)
                write (miLista.conectados[i].socket, notificacion, strlen(notificacion));
    }
    else // felicitamos a los de la misma ciudad
    {
        for (i=0; i<miLista.num; i++)
            if (strcmp(miLista.conectados[i].ciudad, ciudad) == 0)
                write (miLista.conectados[i].socket, notificacion, strlen(notificacion));
    }
}

Solicitud de desconexión

else // solicitan desconexión
{
    pthread_mutex_lock( &mutex );
    EliminaConectado (&miLista, sock_conn);
    pthread_mutex_unlock( &mutex);

    fin = 1;
}

Descargar

PDF Word HTML