Servidor TCP y UDP usando select

Requisitos previos: TCP UDP

En artículos anteriores, hemos visto un servidor TCP y un servidor UDP . Pero ahora podemos combinar nuestro servidor de eco TCP simultáneo y el servidor UDP iterativo en un solo servidor que usa select para multiplexar TCP y UDP socket.

La función Seleccionar se utiliza para seleccionar entre conectores TCP y UDP. Esta función le da instrucciones al kernel para que espere a que ocurra cualquiera de los múltiples eventos y activa el proceso solo después de que ocurra uno o más eventos o pase un tiempo específico.

Ejemplo : el kernel regresará solo cuando ocurra una de estas condiciones

  • Cualquier descriptor de {1, 2, 3} está listo para leer
  • Cualquier Descriptor de {4, 5, 6} está listo para escribir
  • Ha pasado el tiempo de 5 segundos

Todo el proceso se puede dividir en los siguientes pasos:

Servidor:

  1. Crear TCP, es decir, socket de escucha
  2. Crear un enchufe UDP
  3. Enlace ambos sockets a la dirección del servidor.
  4. Inicialice un conjunto de descriptores para seleccionar y calcule un máximo de 2 descriptores por los cuales esperaremos
  5. Seleccione la llamada y obtenga el descriptor listo (TCP o UDP)
  6. Manejar nueva conexión si el descriptor listo es de TCP O recibir datagrama si el descriptor listo es de UDP

Cliente UDP:

  1. Cree un conector UDP.
  2. Envía un mensaje al servidor.
  3. Espere hasta que se reciba una respuesta del servidor.
  4. Cierra el descriptor de socket y sal.

Cliente TCP:

  1. Cree un conector TCP.
  2. Llame a connect para establecer una conexión con el servidor.
  3. Cuando se acepta la conexión escribe un mensaje a un servidor.
  4. Lea la respuesta del servidor.
  5. Cierra el descriptor de socket y sal.

Funciones necesarias:

int select(int maxfd, fd_set *readsset, fd_set *writeset, 
fd_set *exceptset, const struct timeval *timeout);
Returns: positive count of descriptors ready, 0 on timeout, -1 error

Argumentos:

  • maxfd: número máximo de descriptores listos.
  • timeout: cuánto tiempo esperar para que select regrese.
struct timeval{
long tv_sec;
long tv_usec;
};
if timeout==NULL then wait forever
if timeout == fixed_amount_time then wait until specified time
if timeout == 0 return immediately.
  • readset: conjunto de descriptores que queremos que el kernel pruebe para leer.
  • conjunto de escritura: conjunto de descriptores que queremos que el kernel pruebe para escribir.
  • exceptset: conjunto de descriptores que queremos que el kernel pruebe para condiciones de excepción.
int read(int sockfd, void * buff, size_t nbytes);
Returns:  number of bytes read from the descriptor. -1 on error

Argumentos:

  1. sockfd: Descriptor que recibe los datos.
  2. buff: los datos del descriptor de socket del búfer de la aplicación se copian en este búfer.
  3. nbytes: número de bytes que se copiarán en el búfer de la aplicación.

Servidor.c

C

// Server program
#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define PORT 5000
#define MAXLINE 1024
int max(int x, int y)
{
    if (x > y)
        return x;
    else
        return y;
}
int main()
{
    int listenfd, connfd, udpfd, nready, maxfdp1;
    char buffer[MAXLINE];
    pid_t childpid;
    fd_set rset;
    ssize_t n;
    socklen_t len;
    const int on = 1;
    struct sockaddr_in cliaddr, servaddr;
    char* message = "Hello Client";
    void sig_chld(int);
 
    /* create listening TCP socket */
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(PORT);
 
    // binding server addr structure to listenfd
    bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
    listen(listenfd, 10);
 
    /* create UDP socket */
    udpfd = socket(AF_INET, SOCK_DGRAM, 0);
    // binding server addr structure to udp sockfd
    bind(udpfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
 
    // clear the descriptor set
    FD_ZERO(&rset);
 
    // get maxfd
    maxfdp1 = max(listenfd, udpfd) + 1;
    for (;;) {
 
        // set listenfd and udpfd in readset
        FD_SET(listenfd, &rset);
        FD_SET(udpfd, &rset);
 
        // select the ready descriptor
        nready = select(maxfdp1, &rset, NULL, NULL, NULL);
 
        // if tcp socket is readable then handle
        // it by accepting the connection
        if (FD_ISSET(listenfd, &rset)) {
            len = sizeof(cliaddr);
            connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &len);
            if ((childpid = fork()) == 0) {
                close(listenfd);
                bzero(buffer, sizeof(buffer));
                printf("Message From TCP client: ");
                read(connfd, buffer, sizeof(buffer));
                puts(buffer);
                write(connfd, (const char*)message, sizeof(buffer));
                close(connfd);
                exit(0);
            }
            close(connfd);
        }
        // if udp socket is readable receive the message.
        if (FD_ISSET(udpfd, &rset)) {
            len = sizeof(cliaddr);
            bzero(buffer, sizeof(buffer));
            printf("\nMessage from UDP client: ");
            n = recvfrom(udpfd, buffer, sizeof(buffer), 0,
                        (struct sockaddr*)&cliaddr, &len);
            puts(buffer);
            sendto(udpfd, (const char*)message, sizeof(buffer), 0,
                (struct sockaddr*)&cliaddr, sizeof(cliaddr));
        }
    }
}

Cliente_TCP.c

C

// TCP Client program
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#define PORT 5000
#define MAXLINE 1024
int main()
{
    int sockfd;
    char buffer[MAXLINE];
    char* message = "Hello Server";
    struct sockaddr_in servaddr;
 
    int n, len;
    // Creating socket file descriptor
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("socket creation failed");
        exit(0);
    }
 
    memset(&servaddr, 0, sizeof(servaddr));
 
    // Filling server information
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(PORT);
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
 
    if (connect(sockfd, (struct sockaddr*)&servaddr,
                            sizeof(servaddr)) < 0) {
        printf("\n Error : Connect Failed \n");
    }
 
    memset(buffer, 0, sizeof(buffer));
    strcpy(buffer, "Hello Server");
    write(sockfd, buffer, sizeof(buffer));
    printf("Message from server: ");
    read(sockfd, buffer, sizeof(buffer));
    puts(buffer);
    close(sockfd);
}

UDP_cliente.c

C

// UDP client program
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/types.h>
#define PORT 5000
#define MAXLINE 1024
int main()
{
    int sockfd;
    char buffer[MAXLINE];
    char* message = "Hello Server";
    struct sockaddr_in servaddr;
 
    int n, len;
    // Creating socket file descriptor
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        printf("socket creation failed");
        exit(0);
    }
 
    memset(&servaddr, 0, sizeof(servaddr));
 
    // Filling server information
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(PORT);
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    // send hello message to server
    sendto(sockfd, (const char*)message, strlen(message),
        0, (const struct sockaddr*)&servaddr,
        sizeof(servaddr));
 
    // receive server's response
    printf("Message from server: ");
    n = recvfrom(sockfd, (char*)buffer, MAXLINE,
                0, (struct sockaddr*)&servaddr,
                &len);
    puts(buffer);
    close(sockfd);
    return 0;
}

Pasos para compilar y ejecutar los códigos anteriores:

  1. Compile el programa del servidor (gcc server.c -o ser)
  2. Ejecutar servidor usando (./ser)
  3. En otro terminal, compile el programa cliente tcp (gcc tcp_client.c -o tcpcli)
  4. Ejecute el cliente tcp (./tcpcli)
  5. En otro terminal, compile el programa cliente udp (gcc udp_client.c -o udpcli)
  6. Ejecute el cliente UDP (./udpcli)

Salida de los códigos anteriores:

Publicación traducida automáticamente

Artículo escrito por Mohityadav 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 *