Manejo de múltiples clientes en el servidor con subprocesos múltiples mediante la programación de sockets en C/C++

Este tutorial asume que el lector tiene conocimientos básicos de programación de sockets , es decir, está familiarizado con los modelos básicos de servidores y clientes . En el modelo básico, el servidor maneja solo un cliente a la vez, lo cual es una gran suposición si uno quiere desarrollar cualquier modelo de servidor escalable. La forma más sencilla de manejar varios clientes sería generar un nuevo hilo para cada nuevo cliente conectado al servidor. 

Semáforos: el semáforo es simplemente una variable que no es negativa y se comparte entre subprocesos . Esta variable se utiliza para resolver el problema de la sección crítica y lograr la sincronización de procesos en el entorno de multiprocesamiento .

sem_post: sem_post() incrementa (desbloquea) el semáforo apuntado por sem. Si el valor del semáforo en consecuencia se vuelve mayor que cero, entonces otro proceso o subproceso bloqueado en una llamada sem_wait(3) se despertará y procederá a bloquear el semáforo.

#include <semaphore.h>
int sem_post(sem_t *sem);

sem_wait: sem_wait() disminuye (bloquea) el semáforo al que apunta sem. Si el valor del semáforo es mayor que cero, entonces el decremento continúa y la función regresa, inmediatamente. Si el semáforo actualmente tiene el valor cero, entonces la llamada se bloquea hasta que sea posible realizar la disminución (es decir, el valor del semáforo sube por encima de cero) o un controlador de señal interrumpe la llamada.

#include <semaphore.h>
int sem_wait(sem_t *sem);

En este artículo, el algoritmo Reader-Writers se implementa en el lado del servidor.

Implementación: para el lado del servidor, cree dos subprocesos diferentes; un hilo lector y un hilo escritor. Primero, declare un serverSocket , un número entero, una variable para contener el retorno de la función de socket.

int serverSocket = socket(domain, type, protocol);
  • serverSocket: descriptor de socket , un número entero (como un identificador de archivo).
  • dominio: Entero, dominio de comunicación, por ejemplo, AF_INET ( protocolo IPv4 ), AF_INET6 ( protocolo IPv6 ).
  • tipo : Tipo de comunicación.
  • SOCK_STREAM : TCP (confiable, orientado a la conexión).
  • SOCK_DGRAM : UDP (no confiable, sin conexión).
  • protocol : Valor de protocolo para el Protocolo de Internet (IP), que es 0. Este es el mismo número que aparece en el campo de protocolo en el encabezado IP de un paquete. (Protocolos de hombre para más detalles).

Luego, después de inicializar todas las variables necesarias, vincule el socket .

bind: después de la creación del socket, la función bind vincula el socket a la dirección y el número de puerto especificados en addr (estructura de datos personalizada). En el código de ejemplo, vinculamos el servidor al host local, por lo que se usa INADDR_ANY para especificar la dirección IP.

int bind(int sockfd, const struct sockaddr *addr, 
         socklen_t addrlen);

listen: Pone el socket del servidor en un modo pasivo, donde espera a que el cliente se acerque al servidor para hacer una conexión. El backlog define la longitud máxima a la que puede crecer la cola de conexiones pendientes para sockfd. Si llega una solicitud de conexión cuando la cola está llena, el cliente puede recibir un error con una indicación de ECONNREFUSED.

int listen(int sockfd, int backlog);

Para obtener más funciones de conexión utilizadas en este artículo, consulte este artículo para la programación de sockets en C.

Acercarse:

  • Después de aceptar la conexión al puerto deseado, recibe un número entero del cliente que define la opción de lectura o escritura. La opción 1 indica lector, mientras que la opción 2 indica escritor.
  • Después de recibir correctamente los datos, llame a pthread_create para crear subprocesos de lectura y escritura.
  • Después de hacer conexiones exitosas con el servidor-cliente, le pide al usuario que ingrese la variable de elección.
  • Después de obtener la elección del usuario, el cliente envía esta elección al servidor para llamar al subproceso de lectura o escritura creando un subproceso de cliente para la solicitud.

A continuación se muestra la implementación del enfoque anterior:

Código para el lado del servidor:

C

// C program for the Server Side
 
// inet_addr
#include <arpa/inet.h>
 
// For threading, link with lpthread
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
 
// Semaphore variables
sem_t x, y;
pthread_t tid;
pthread_t writerthreads[100];
pthread_t readerthreads[100];
int readercount = 0;
 
// Reader Function
void* reader(void* param)
{
    // Lock the semaphore
    sem_wait(&x);
    readercount++;
 
    if (readercount == 1)
        sem_wait(&y);
 
    // Unlock the semaphore
    sem_post(&x);
 
    printf("\n%d reader is inside",
           readercount);
 
    sleep(5);
 
    // Lock the semaphore
    sem_wait(&x);
    readercount--;
 
    if (readercount == 0) {
        sem_post(&y);
    }
 
    // Lock the semaphore
    sem_post(&x);
 
    printf("\n%d Reader is leaving",
           readercount + 1);
    pthread_exit(NULL);
}
 
// Writer Function
void* writer(void* param)
{
    printf("\nWriter is trying to enter");
 
    // Lock the semaphore
    sem_wait(&y);
 
    printf("\nWriter has entered");
 
    // Unlock the semaphore
    sem_post(&y);
 
    printf("\nWriter is leaving");
    pthread_exit(NULL);
}
 
// Driver Code
int main()
{
    // Initialize variables
    int serverSocket, newSocket;
    struct sockaddr_in serverAddr;
    struct sockaddr_storage serverStorage;
 
    socklen_t addr_size;
    sem_init(&x, 0, 1);
    sem_init(&y, 0, 1);
 
    serverSocket = socket(AF_INET, SOCK_STREAM, 0);
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(8989);
 
    // Bind the socket to the
    // address and port number.
    bind(serverSocket,
         (struct sockaddr*)&serverAddr,
         sizeof(serverAddr));
 
    // Listen on the socket,
    // with 40 max connection
    // requests queued
    if (listen(serverSocket, 50) == 0)
        printf("Listening\n");
    else
        printf("Error\n");
 
    // Array for thread
    pthread_t tid[60];
 
    int i = 0;
 
    while (1) {
        addr_size = sizeof(serverStorage);
 
        // Extract the first
        // connection in the queue
        newSocket = accept(serverSocket,
                           (struct sockaddr*)&serverStorage,
                           &addr_size);
        int choice = 0;
        recv(newSocket,
             &choice, sizeof(choice), 0);
 
        if (choice == 1) {
            // Creater readers thread
            if (pthread_create(&readerthreads[i++], NULL,
                               reader, &newSocket)
                != 0)
 
                // Error in creating thread
                printf("Failed to create thread\n");
        }
        else if (choice == 2) {
            // Create writers thread
            if (pthread_create(&writerthreads[i++], NULL,
                               writer, &newSocket)
                != 0)
 
                // Error in creating thread
                printf("Failed to create thread\n");
        }
 
        if (i >= 50) {
            // Update i
            i = 0;
 
            while (i < 50) {
                // Suspend execution of
                // the calling thread
                // until the target
                // thread terminates
                pthread_join(writerthreads[i++],
                             NULL);
                pthread_join(readerthreads[i++],
                             NULL);
            }
 
            // Update i
            i = 0;
        }
    }
 
    return 0;
}

Código para el lado del cliente:

C

// C program for the Client Side
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
 
// inet_addr
#include <arpa/inet.h>
#include <unistd.h>
 
// For threading, link with lpthread
#include <pthread.h>
#include <semaphore.h>
 
// Function to send data to
// server socket.
void* clienthread(void* args)
{
 
    int client_request = *((int*)args);
    int network_socket;
 
    // Create a stream socket
    network_socket = socket(AF_INET,
                            SOCK_STREAM, 0);
 
    // Initialise port number and address
    struct sockaddr_in server_address;
    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = INADDR_ANY;
    server_address.sin_port = htons(8989);
 
    // Initiate a socket connection
    int connection_status = connect(network_socket,
                                    (struct sockaddr*)&server_address,
                                    sizeof(server_address));
 
    // Check for connection error
    if (connection_status < 0) {
        puts("Error\n");
        return 0;
    }
 
    printf("Connection established\n");
 
    // Send data to the socket
    send(network_socket, &client_request,
         sizeof(client_request), 0);
 
    // Close the connection
    close(network_socket);
    pthread_exit(NULL);
 
    return 0;
}
 
// Driver Code
int main()
{
    printf("1. Read\n");
    printf("2. Write\n");
 
    // Input
    int choice;
    scanf("%d", &choice);
    pthread_t tid;
 
    // Create connection
    // depending on the input
    switch (choice) {
    case 1: {
        int client_request = 1;
 
        // Create thread
        pthread_create(&tid, NULL,
                       clienthread,
                       &client_request);
        sleep(20);
        break;
    }
    case 2: {
        int client_request = 2;
 
        // Create thread
        pthread_create(&tid, NULL,
                       clienthread,
                       &client_request);
        sleep(20);
        break;
    }
    default:
        printf("Invalid Input\n");
        break;
    }
 
    // Suspend execution of
    // calling thread
    pthread_join(tid, NULL);
}

 Producción:

Output

Complejidad de tiempo: O(1)

Espacio Auxiliar: O(1)
 

Publicación traducida automáticamente

Artículo escrito por regiscaelum y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *