Sockets - Linux - UDP - Cliente-Servidor

 

Servidor UDP en Linux:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <arpa/inet.h>


#define PORT 8080

#define BUFFER_SIZE 1024


int main() {

    int sockfd;

    char buffer[BUFFER_SIZE];


struct sockaddr_in: Es una estructura para manejar direcciones IPv4 en sockets.

server_addr: Almacena la dirección IP y el puerto del servidor. Se configura para indicar dónde se reciben los datos.

client_addr: Almacena la dirección IP y el puerto del cliente. Se usa para identificar quién envió los datos (en el servidor UDP).

    struct sockaddr_in server_addr, client_addr;


client_addr_len indica el tamaño de la estructura client_addr y es necesario para funciones como recvfrom() y sendto() para gestionar correctamente las direcciones del cliente.

    socklen_t client_addr_len = sizeof(client_addr);


socket(AF_INET, SOCK_DGRAM, 0): Crea un socket UDP:

AF_INET: Usa direcciones IPv4.

SOCK_DGRAM: Define el socket como sin conexión (UDP).

0: Selecciona el protocolo predeterminado para UDP.

    // Creating socket file descriptor

    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {

        perror("Socket creation failed");

        exit(EXIT_FAILURE);

    }


Se configura la estructura server_addr para que el servidor reciba mensajes UDP en IPv4, en cualquier interfaz de red local y en el puerto especificado.

* Es una configuración estándar (no personalizada) necesaria para el funcionamiento de la conexión. 

    // Setting up the server address

    memset(&server_addr, 0, sizeof(server_addr));

    server_addr.sin_family = AF_INET;

    server_addr.sin_addr.s_addr = INADDR_ANY;

    server_addr.sin_port = htons(PORT);

Esta línea enlaza el socket al puerto y dirección configurados. Si falla, muestra un error y termina el programa.


sockfd: Descriptor del socket creado previamente. Identifica el socket que se va a enlazar a una dirección y puerto.

(struct sockaddr *)&server_addr: Dirección y puerto a los que el socket se enlaza.

server_addr contiene:

sin_family: IPv4 (AF_INET).

sin_addr.s_addr: Acepta mensajes de cualquier IP (INADDR_ANY).

sin_port: Puerto configurado para el servidor (htons(PORT)).

El cast (struct sockaddr *) adapta el tipo específico sockaddr_in al genérico sockaddr.

sizeof(server_addr): Indica el tamaño de la estructura server_addr para que bind() lea correctamente toda la información.

    // Binding the socket

    if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {

        perror("Bind failed");

        close(sockfd);

        exit(EXIT_FAILURE);

    }


    printf("Server is listening on port %d...\n", PORT);


    while (1) {

        // Receiving a message from the client

recvfrom(): Recibe un mensaje enviado por un cliente:

sockfd: Descriptor del socket.

buffer: Espacio para almacenar el mensaje recibido.

BUFFER_SIZE: Tamaño máximo que puede recibir.

0: Opciones adicionales (generalmente 0).

(struct sockaddr *)&client_addr: Dirección del cliente que envió el mensaje (se almacena aquí).

&client_addr_len: Tamaño de la estructura de la dirección del cliente.

n: Cantidad de bytes recibidos.

        int n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&client_addr, &client_addr_len);

        buffer[n] = '\0';  // Null-terminate the received message

        printf("Message received from client: %s\n", buffer);


        // Sending a response back to the client

        const char *response = "Hello from the UDP server!";

sendto(): Envía el mensaje al cliente.

sockfd: Descriptor del socket usado para enviar datos.

response: Mensaje que se quiere enviar.

strlen(response): Longitud del mensaje en bytes.

0: Opciones adicionales (usualmente 0 para envío estándar).

(struct sockaddr *)&client_addr: Dirección del cliente que recibirá el mensaje.

client_addr_len: Tamaño de la estructura de dirección del cliente.

        sendto(sockfd, response, strlen(response), 0, (struct sockaddr *)&client_addr, client_addr_len);

        printf("Response sent to client.\n");

    }


    close(sockfd);

    return 0;

}

// Cliente UDP en Linux:


#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <arpa/inet.h>


#define PORT 8080

#define BUFFER_SIZE 1024


int main() {

    int sockfd;

    char buffer[BUFFER_SIZE];

    struct sockaddr_in server_addr;

addr_len almacena el tamaño de server_addr. Es necesario para que las funciones de sockets sepan cuánta memoria deben manejar al trabajar con la dirección del servidor.

    socklen_t addr_len = sizeof(server_addr);

    const char *message = "Hello from the UDP client!";


    // Creating socket file descriptor

socket(AF_INET, SOCK_DGRAM, 0): Crea un socket UDP:

AF_INET: Protocolo IPv4.

SOCK_DGRAM: Define el socket como orientado a datagramas (UDP, sin conexión).

0: Selecciona el protocolo predeterminado para SOCK_DGRAM, que es UDP.


    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {

        perror("Socket creation failed");

        exit(EXIT_FAILURE);

    }


Se configura la estructura server_addr para que el servidor reciba mensajes UDP en IPv4, en cualquier interfaz de red local y en el puerto especificado.

* Es una configuración estándar (no personalizada) necesaria para el funcionamiento de la conexión. 

    // Setting up the server address

    memset(&server_addr, 0, sizeof(server_addr));

    server_addr.sin_family = AF_INET;

    server_addr.sin_port = htons(PORT);

    if (inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr) <= 0) {

        perror("Invalid address or address not supported");

        close(sockfd);

        exit(EXIT_FAILURE);

    }


    // Sending a message to the server

sendto(): Envía un mensaje al servidor utilizando el socket UDP.

sockfd: Descriptor del socket desde el cual se envía el mensaje.

message: El mensaje que se quiere enviar (en este caso, una cadena de texto).

strlen(message): Tamaño del mensaje en bytes (longitud de la cadena).

0: Opciones adicionales (generalmente 0 para envío estándar).

(struct sockaddr *)&server_addr: Dirección y puerto del servidor al que se envía el mensaje.

addr_len: Tamaño de la estructura de dirección server_addr.

    sendto(sockfd, message, strlen(message), 0, (struct sockaddr *)&server_addr, addr_len);

    printf("Message sent to server: %s\n", message);


*) recvfrom() espera recibir un mensaje desde el servidor, lo guarda en buffer, y también almacena la dirección del servidor en server_addr. El tamaño del mensaje recibido se almacena en n.

    // Receiving a response from the server

    int n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&server_addr, &addr_len);

    buffer[n] = '\0';  // Null-terminate the received message

    printf("Message received from server: %s\n", buffer);


    close(sockfd);

    return 0;

}



Paso

TCP

UDP

Crear el socket

socket(AF_INET, SOCK_STREAM, 0)

socket(AF_INET, SOCK_DGRAM, 0)

Configurar la dirección

Configurar sockaddr_in (IP, puerto)

Configurar sockaddr_in (IP, puerto)

Enlazar el socket (bind)

bind() (servidor)

bind() (servidor)

Establecer conexión (cliente)

connect() (cliente)

No necesario

Escuchar conexiones (servidor)

listen() (servidor)

No necesario

Aceptar conexiones (servidor)

accept() (servidor)

No necesario

Enviar datos

send()

sendto()

Recibir datos

recv()

recvfrom()

Cerrar conexión

close()

close()