[Red informática] Protocolo UDP, programación UDP, UDP conectado

Conocimiento basico

UDP exporta la capa de transporte en el protocolo TCP / IP en capas.
UDP es un protocolo de comunicación poco confiable sin retransmisión y reconocimiento, sin control de órdenes y sin control de congestión.
UDP no garantiza la entrega efectiva de paquetes, ni garantiza el orden de los paquetes, es decir, cuando usamos UDP, necesitamos hacer un buen trabajo de pérdida de paquetes, retransmisión y ensamblaje de mensajes.
UDP es relativamente simple y hay muchos escenarios adecuados. Nuestros servicios DNS comunes y servicios SNMP se basan en el protocolo UDP. Estos escenarios no son particularmente sensibles al retraso y la pérdida de paquetes. Además, los escenarios de comunicación para varias personas, como salas de chat, juegos para varios jugadores, etc., también utilizarán el protocolo UDP.
OSI y TCP / IP

Formato de mensaje

El encabezado UDP tiene una longitud de 8 bytes, que son el puerto de origen, el puerto de destino, la longitud del paquete UDP y la suma de comprobación .
Como la longitud de un mensaje UDP solo se registra con 2 bytes, la longitud máxima de un mensaje, incluida la longitud del encabezado, es de 65535 bytes.

Cuando se envía con el protocolo UDP, la longitud máxima de datos que se puede enviar con la función sendto es: 65535-IP header (20) -UDP header (8) = 65507 bytes. Al enviar datos con la función sendto, si la longitud de los datos enviados es mayor que este valor, la función devuelve un error.
Como IP tiene la MTU máxima,
el tamaño del paquete UDP debe ser 1500-IP header (20) -UDP header (8) = 1472 (Bytes)

Mensaje UDP

Programación UDP

Servidor:
1. Cree un socket.
2. Enlace la ip y el puerto a monitorear.
3. Bucle:
3.1. Llame a recvfrom para leer el mensaje recibido y bloquéelo si no hay ningún mensaje.
3.2 Después de recibir el mensaje, llame a sendto y envíelo al cliente en consecuencia.

Cliente:
1. Cree un socket.
2. Bucle:
2.1. Llame a sendto para enviar la solicitud.
2.2. Llame a recvfrom para recibir el correspondiente.

#include <sys/socket.h>
// 返回值:收到数据的字节数
// 参数:
// 		sockfd:socket描述符
// 		buff:本地缓存
// 		nbytes:缓存最大接收字节数
// 		flags:I/O 相关的参数,一般使用 0 即可
// 		from:发送端的 ip 和 port 等信息
// 		addrlen:from 的大小
ssize_t 
recvfrom(int sockfd, void *buff, size_t nbytes, int flags, 
          struct sockaddr *from, socklen_t *addrlen); 
    
// 返回值:发送了多少字节
// 参数:和上面的 recvfrom 类似      
ssize_t
sendto(int sockfd, const void *buff, size_t nbytes, int flags,
                const struct sockaddr *to, socklen_t addrlen); 

Programación UDP
El código se refiere a mi artículo anterior: programa de eco UDP https://blog.csdn.net/Hanoi_ahoj/article/details/105358383

La función "sin conexión" de los paquetes UDP le permite continuar enviando paquetes después de reiniciar el servidor UDP. Esta es la mejor explicación del "no contexto" de los paquetes UDP.

UDP conectado

A través de lo anterior, en UDP, no es necesario establecer una conexión similar para conectarse en la programación TCP.
De hecho, UDP también se puede "conectar".

Probémoslo a través de un programa:
Cliente: Debe tenerse en cuenta que después de que se crea el socket, está conectado y vinculado a la IP y al puerto del servidor.

// UDP connect 测试客户端

#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

int main() {
  int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
  if (socket_fd < 0) {
    perror("socket");
    return -1;
  }

  struct sockaddr_in server_addr;
  bzero(&server_addr, sizeof(server_addr));
  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons(9090);
  server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

  int ret = connect(socket_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
  if (ret < 0) {
    perror("connect");
    return -1;
  }

  while (1) {
    char buf[1024] = {0};
    printf("input>");
    scanf("%s", buf);
    ssize_t n = sendto(socket_fd, buf, strlen(buf), 0, (struct sockaddr*)&server_addr, sizeof(server_addr));
    if (n < 0) {
      perror("sendto");
      continue;
    }

    printf("%zd bytes sent to [%s:%d]\n", n, inet_ntoa(server_addr.sin_addr), ntohs(server_addr.sin_port));

    bzero(buf, sizeof(buf));
    n = recvfrom(socket_fd, buf, sizeof(buf), 0, NULL, NULL);
    if (n < 0) {
      perror("recvfrom");
      return -1;
    }
    printf("resp: %s\n", buf);
  }

  close(socket_fd);
  return 0;
}

Servidor: es un servidor normal y la solicitud se devuelve sin cambios.

#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

int main() {
  int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
  if (socket_fd < 0) {
    perror("socket");
    return -1;
  }

  struct sockaddr_in server_addr;
  bzero(&server_addr, sizeof(server_addr));
  server_addr.sin_family = AF_INET;
  server_addr.sin_addr.s_addr = inet_addr("0.0.0.0");
  server_addr.sin_port = htons(9090);
  int ret = bind(socket_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
  if (ret < 0) {
    perror("bind");
    return -1;
  }

  // 一般服务器不进行 connect 操作
  while (1) {
    char buf[1024] = {0};
    struct sockaddr_in client_addr;
    bzero(&client_addr, sizeof(client_addr));
    socklen_t client_addr_len = sizeof(client_addr);
    ssize_t n = recvfrom(socket_fd, buf, sizeof(buf) - 1, 0,
        (struct sockaddr*)&client_addr, &client_addr_len);
    if (n < 0) {
      perror("recvfrom");
      continue;
    }
    buf[n] = '\0';

    printf("req->[%s:%d] %s\n", inet_ntoa(client_addr.sin_addr),
        ntohs(client_addr.sin_port), buf);

    n = sendto(socket_fd, buf, strlen(buf), 0, (struct sockaddr*)&client_addr, client_addr_len);
    if (n < 0) {
      perror("sendto");
      continue;
    }
  }

  close(socket_fd);
  return 0;
}
gcc client.c -o client
gcc server.c -o server

Prueba:
1. No ejecute el servidor, solo ejecute el cliente.
Como puede ver, se envió cuando se llamó a sendto, pero hubo un error de conexión rechazada al caminar hacia recvfrom.

cliente
2. Ejecute el servidor y el cliente.
Recibir y procesar solicitudes normalmente.
servidor && cliente

El rol de UDP connect

Si no se conecta, no inicie el servidor, ejecute el cliente, el programa se bloqueará en recvfrom. Hasta que el servidor se reinicie o agote el tiempo de espera.
Generalmente, el servidor UDP no necesita conectarse, porque el servidor solo puede conectarse al cliente después de conectarse.

La función de conectar es dejar que el programa reciba el mensaje de error lo antes posible y devolver:

Al realizar una operación de conexión en un socket UDP, se establece un "contexto" para el socket UDP, que tiene una conexión con la dirección y el puerto del lado del servidor. Es esta relación de enlace la que proporciona al núcleo del sistema operativo la información necesaria , Puede asociar la información recibida por el núcleo del sistema operativo con el zócalo correspondiente.

Cuando se llama a la función de operación sendto o send, se envía el mensaje de la aplicación, nuestra aplicación regresa, el núcleo del sistema operativo se hace cargo del mensaje, y luego el sistema operativo comienza a intentar enviar a la dirección y puerto correspondientes, porque la dirección y el puerto correspondientes no son Una vez alcanzado, se devolverá un mensaje ICMP al núcleo del sistema operativo, que contiene información como la dirección de destino y el puerto.

La operación de conexión se llevó a cabo para ayudar al kernel del sistema operativo a establecer la relación de mapeo entre (dirección de destino de socket UDP + puerto). Cuando se recibe un mensaje ICMP inalcanzable, el kernel del sistema operativo puede encontrarlo en la tabla de mapeo Fuera de qué socket UDP tiene la dirección y el puerto de destino, no olvide que el socket es globalmente único dentro del sistema operativo, cuando llamamos al método recvfrom o recv en el socket nuevamente, podemos recibir la operación El mensaje "Conexión rechazada" devuelto por el núcleo del sistema.

Después de conectarse a UDP, se recomiendan muchos libros sobre el uso de las funciones de envío y recepción:
envíe o escriba la función para enviar, si usa sendto necesita establecer la información relevante de dirección en cero;
use la función recv o read para recibir, si El uso de recvfrom necesita establecer la información correspondiente de la dirección de cero.
De hecho, diferentes implementaciones de UNIX se comportan de manera diferente.

Factor de eficiencia:

Porque si no utiliza el método de conexión, cada vez que envíe un mensaje, necesitará este proceso:
conectar socket → enviar mensaje → desconectar socket → conectar socket → enviar mensaje → desconectar socket → ...... ...

Si utiliza el método de conexión, se convertirá en el siguiente:
zócalo de conexión → enviar mensaje → enviar mensaje → ... → finalmente desconecte el zócalo

Sabemos que la conexión de enchufes requiere una cierta cantidad de sobrecarga, como la necesidad de buscar información de la tabla de enrutamiento. Por lo tanto, el programa cliente UDP puede obtener una cierta mejora de rendimiento a través de la conexión.


Referencia: Geek Time-Practical Network Programming ( https://time.geekbang.org/column/article/129807 )

EOF

Se han publicado 98 artículos originales · 91 alabanzas · Más de 40,000 visitas

Supongo que te gusta

Origin blog.csdn.net/Hanoi_ahoj/article/details/105460489
Recomendado
Clasificación