[Desarrollo de Linux: programación de redes]

  • Programación de redes, escritura de programas para intercambiar datos entre dos o más computadoras en red.

1. Red: Introducción

ctontrol + C: fuerza el final del proceso

1. Estructura física de la red

1. Red ordinaria

Insertar descripción de la imagen aquí

2. Red Gigabit de fibra óptica

  • Hay muchas redes Gigabit en el mercado, pero el rendimiento máximo de los cables de red normales es de solo 100 Mbits. Si desea llegar a Gigabit, debe reemplazar el cable de red de fibra óptica Gigabit y también necesita un adaptador de fibra óptica Gigabit.
  • El cable de red Gigabit es plano y tiene una pequeña interfaz similar al USB.
    Insertar descripción de la imagen aquí
    Insertar descripción de la imagen aquí
    Insertar descripción de la imagen aquí

2. Red: dirección IP

并不是所有的地址都是可以用的

  • 1. La dirección de red se compone de 4 bytes y 32 bits.

  • 2. No se pueden utilizar direcciones que comiencen con 0.

  • 3. Las direcciones que terminan en 0 representan segmentos de red, no direcciones específicas.

  • 4. Las direcciones que comienzan desde 224 a 239 son 组播direcciones y no se pueden utilizar para transmisiones punto a punto.

    • Multidifusión: entiende la transmisión a través de TCP/UDP
    • Ventajas: la multidifusión puede ahorrar mucho ancho de banda
    • Desventajas: es fácil formar una tormenta de red
  • 5. Las direcciones que van del 240 al 255 están reservadas para experimentos y generalmente no se utilizan como direcciones de servidor o terminal.

    • 127.0.0.1 es una dirección de red de loopback reservada. Cualquier dato enviado a esta red será devuelto. Generalmente se usa para aprendizaje y pruebas.
    • 0.0.0.0 está reservado, la dirección completa del segmento de red, generalmente utilizada para monitorear el servidor
  • Las categorías A (red grande), B (red mediana) y C (red pequeña) se utilizan para redes de diferentes tamaños.

  • La clase D se utiliza para multidifusión (similar a la transmisión)

  • Clase E reservada para redes privadas experimentales.
    Insertar descripción de la imagen aquí

3. Puerto de red

El puerto no sólo lo utiliza el servidor, sino también el cliente.

  • Puertos conocidos : también conocidos como puertos de uso común, del 0 al 1024, estrechamente vinculados a algunos servicios específicos. Normalmente la comunicación de estos puertos indica claramente el protocolo de un determinado servicio, este tipo de puerto no puede redefinir su función.netstat -an
    Insertar descripción de la imagen aquí

    • 443: servicio https
    • 80: comunicación http
    • 23: servicio Telnet
  • Puertos registrados : Los números de puerto van del 1025 al 49151. La mayoría de estos puertos no definen claramente los objetos de servicio. Se pueden personalizar diferentes programas según las necesidades reales. El software de control remoto y los programas troyanos se definirán mediante estos puertos.

  • Puertos dinámicos y/o privados : los números de puerto van del 49152 al 65535 (no lo utilice fácilmente como puerto de escucha del servidor). En particular, a algunos programas troyanos les gusta utilizar estos puertos, que a menudo pasan desapercibidos y son fáciles de ocultar.

4. Protocolo de red

El acuerdo es, 一种网络交互中数据格式和交互流程的约定. A través del protocolo, podemos interactuar con dispositivos remotos para obtener datos, solicitar servicios o completar los servicios de la otra parte.

1. Estructura del encabezado del protocolo TCP:

48 bytes
Insertar descripción de la imagen aquí

2. La estructura del encabezado del protocolo HTTP:

Solicitud GET de flujo de texto
, versión 1.1, Host: envía datos al sitio web; UA: información del navegador del cliente;
Aceptar: recibe información, formato, tipo de idioma
Insertar descripción de la imagen aquí

3. Estructura del encabezado del paquete SSH

6 bytes, flujo de bytes
Insertar descripción de la imagen aquí

5. Protocolo TCP

Protocolo de control de transmisión (TCP), un protocolo de comunicación de capa de transporte de flujo de bytes (en bytes), confiable y orientado a la conexión. Seguro y confiable pero sacrifica el rendimiento.

  • Proceso de interacción:
    Insertar descripción de la imagen aquí

    • Se requiere un tiempo de espera para resolver las interrupciones de la conexión física y el tiempo de espera predeterminado puede ser de hasta dos horas.
    • Solución al tiempo de espera: mecanismo de paquetes de latidos. Se ve obligado a preguntar si ambas partes están en línea, si la anomalía continúa más de un cierto número de veces y, en general, si no se pueden recibir o enviar 3 latidos, se fuerza a desconectar la conexión.

2. Enchufes: Introducción

  • Similar a la parte de red de la programación de red de Windows : programación de red de Windows
  • Socket: Dispositivo de software utilizado para la transmisión de datos de red. El sistema operativo nos proporciona soporte de socket.

1. Comprensión del zócalo

Insertar descripción de la imagen aquí

2. Creación de sockets

Hay muchos tipos de sockets, los más utilizados son los sockets TCP y UDP.

#include <sys/socket.h>

/* socket创建
- domain:使用的协议族(Protocol Family)信息
- type:数据传输类型信息
- protocol:计算机之间通信种使用的协议信息
*/
int socket(int domain, int type, int protocol);

3. función de enchufe

  • Socket orientado a la conexión TCP : es un método de transmisión de datos orientado a la conexión, confiable, de entrega en orden y basado en bytes.
  • Socket UDP orientado a mensajes : entrega poco confiable, desordenada y diseñada para transmisión de datos de alta velocidad.

Insertar descripción de la imagen aquí
Insertar descripción de la imagen aquí
Insertar descripción de la imagen aquíInsertar descripción de la imagen aquí

4. función de enlace

#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
/* socket绑定
- sockfd:socket套接字文件描述符
- myaddr:结构体,存放地址信息的结构体变量地址值,IPv4,IPV6
- addrlen:
*/
int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen);//绑定成功返回0,失败返回-1

ntohs puede entenderse como: convertir datos cortos del orden de bytes de la red al orden de bytes del host.

struct sockaddr_in addr;
char *serv_ip = "221.214.168.13";//声明IP地址字符串
char *serv_port = "9190";//声明端口号
memset(&add, e,sizeof(addr));//结构体变量addr的所有成员初始化为0

addr.sin_fimily = AF_INET;//指定地址族
/*
每次创建服务器端套接字都要输入IP地址,会很繁琐,此时可初始化地址信息为INADDRANY。
addr.sin_addr.s_addr = htonl(INADDRANY);
*/
add.sin_addr.s_addr = inet_addr(serv_ip);//基于字符串的IP地址初始化
add.sin_port=htons(atoi(serv_port));//基于字符串的端口号初始化

5. función de escucha

#include <sys/socket.h>

/*
- sock:套接字文件描述符
- backlog:连接请求等待队列的长度,若为5,则队列长度为5,最多使5个连接请求进入队列
*/
int listen(int sock, int backlog);//成功返回0,失败返回-1.

4. aceptar función

#include <sys/socket.h>

/*
- sock:套接字文件描述符
- addr:保存发起连接请求的客户端地址信息的变量地址值,调用函数后传递来的地址变量参数天才客户端地址信息
- addrlen: addr结构体参数的长度。
*/
int accept(int sock, struct sockaddr*addr, socklen_t *addrlen);//成功返回0,失败返回-1.

3. Programación TCP: servidor Echo

  • Servidor de eco : Devuelve los datos recibidos del cliente al cliente tal como están, es decir, "eco"

1. Pila de protocolos TCP/IP

Los sockets basados ​​en el protocolo de red TCP/IP se dividen en: sockets TCP y UDP.

2. Servidor TCP:

void server_func()
{
    
    
    printf("%s(%d):%s\n", __FILE__, __LINE__, __FUNCTION__);

    int server_sock, client_sock;
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;
    socklen_t client_addr_size;

    //此处省略了一些,socket的配置认证,eg:版本号等
    server_sock = socket(PF_INET, SOCK_STREAM, 0);
    if (server_sock < 0) {
    
    
        //error_handling("create socket error!");
        std::cout << "create socket error!\n";
        return;
    }

    memset(&server_addr, 0 , sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr("0.0.0.0");// htonl(INADDR_ANY);  0.0.0.0 表示监听全网段,包括内外网
    server_addr.sin_port = htons(9444);

    int ret = bind(server_sock, (struct  sockaddr*)&server_addr, sizeof(server_addr));

    if (ret == -1) {
    
    
        //error_handling("bind socket error!");
        std::cout << "server bind socket error!\n";

        close(server_sock);
        return;
    };
    client_addr_size = sizeof(client_addr);
    ret = listen(server_sock, 3);
    if (ret == -1) {
    
    
        //error_handling("listen socket error!");
        std::cout << "server listen socket error!\n";

        close(server_sock);
        return;
    };

    //回声服务器-原理:服务端循环接收客户端的消息
    char buffer[1024];
    while (1)
    {
    
    
        memset(buffer, 0, sizeof(buffer));

        client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &client_addr_size);
        if (client_sock == -1) {
    
    
            //error_handling("accept server socket error!");
            std::cout << "server accept socket error!\n";

            close(server_sock);
            return;
        }

        ssize_t len = 0;
        while ((len = read(client_sock, buffer, sizeof(buffer))) > 0)
        {
    
    
            len = write(client_sock, buffer, strlen(buffer));
            if (len != (ssize_t)strlen(buffer)) {
    
    
                //error_handling("write message failed!");
                std::cout << "server write message failed!\n";

                close(server_sock);
                return;
            }

            std::cout << "server read & write success!, buffer:" << buffer <<"__len:"<< len << std::endl;

            memset(buffer, 0, len);//清理
        }

        close(client_sock);//服务端关闭的时候,客户端会自动关闭
    };

  
    close(server_sock);
}

3. Cliente TCP:

void client_func()
{
    
    
    int client = socket(PF_INET, SOCK_STREAM, 0);
    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    servaddr.sin_port = htons(9444);
    int ret = connect(client, (struct sockaddr*)&servaddr, sizeof(servaddr));
    if (ret == -1) {
    
    
        std::cout << "client connect failed!\n";
        close(client);
        return;
    }
    std::cout << "client connect server is success!\n";

    char buffer[256] = "";
    while (1)
    {
    
    
        fputs("Input message(Q to quit):", stdout);//提示语句,输入Q结束
        fgets(buffer, sizeof(buffer), stdin);//对文件的标准输入流操作 读取buffer的256字节
        if (strcmp(buffer, "q\n") == 0 || (strcmp(buffer, "Q\n") == 0)) {
    
    
            break;
        }

        size_t len = strlen(buffer);
        size_t send_len = 0;

        //当数据量很大时,并不能一次把所有数据全部发送完,因此需要分包发送
        while (send_len < len)
        {
    
    
            ssize_t ret = write(client, buffer + send_len, len - send_len);//send_len 记录分包的标记
            if (ret <= 0) {
    
    //连接出了问题
                fputs("may be connect newwork failed,make client write failed!\n", stdout);
                close(client);
                return;
            }
            send_len += (size_t)ret;

            std::cout << "client write success, msg:" << buffer << std::endl;

        }
        memset(buffer, 0, sizeof(buffer));

        //当数据量很大时,并不能一次把所有数据全部读取完,因此需要分包读取
        size_t read_len = 0;
        while (read_len < len)
        {
    
    
            size_t ret = read(client, buffer + read_len, len - read_len);
            if (ret <= 0) {
    
    //连接出了问题
                fputs("may be connect newwork failed, make client read failed!\n", stdout);
                close(client);
                return;
            }
            read_len += (size_t)ret;
        }
        std::cout << "from server:" << buffer << std::endl;
    };
    
    close(client);
    std::cout << "client done!" << std::endl;
}

4. Interacción entre diferentes procesos entre el servidor y el cliente.

#include <sys/wait.h>
void server_client_connect()
{
    
    
    //创建进程
    pid_t pid = fork();
    if (pid == 0) {
    
    //为0时表示在子进程
        sleep(1);//为了让服务端先执行

        client_func();
        
    }
    else if (pid > 0) {
    
    //主进程为服务端进程
        server_func();

        int status = 0;
        wait(&status);
    }
    else
    {
    
    
        std::cout << "fork failed!" << pid << std::endl;
    }

}

5. Proyecto práctico Echo Server (calculadora de red): computación en la nube

  • Si necesita el código fuente, deje un mensaje.
  • Requisitos del proyecto :
/*
* 云计算器-需求:
1.客户端连接到服务器端后以1字节整数形式传递待算数字个数。      (个数是1字节)
2.客户端向服务器端传递的每个整数型数据占用4字节。             (每个数是4字节)
3.传递整数型数据后接着传递运算符。运算符信息占用1字节。       (运算符信息1字节)
4.选择字符+、-、*之一传递。
5. 服务器端以4字节整数型向客户端传回运算结果。
6. 客户端得到运算结果后终止与服务器端的连接。
*/
  • Salida final:
    Insertar descripción de la imagen aquí

4. Los principios subyacentes de TCP

1. Almacenamiento en búfer de E/S del socket TCP

  • Sabemos que el envío y recepción de datos por socket TCP no tiene límites . Incluso si el servidor llama a la función de escritura una vez para transferir 40 bytes de datos, el cliente puede leer 10 bytes cada vez mediante cuatro llamadas a la función de lectura. Pero aquí también hay algunas dudas: el servidor transmite 40 bytes a la vez, pero el cliente puede recibirlos lentamente en lotes. Después de que el cliente recibe 10 bytes, ¿dónde están esperando los 30 bytes restantes? ¿Es como un avión flotando en el cielo esperando aterrizar, con los 30 bytes restantes también deambulando por la red esperando ser recibidos?

  • De hecho, los datos no se transmiten inmediatamente después de que se llama a la función de escritura y los datos no se reciben inmediatamente después de que se llama a la función de lectura . Más precisamente, como se muestra en la figura siguiente, en el momento en que se llama a la función de escritura, los datos se moverán al búfer de salida ; en el momento en que se llama a la función de lectura, los datos se leen del búfer de entrada .
    Insertar descripción de la imagen aquí

    • 1. Desde el envío de datos hasta la recepción, hay cables físicos y buffers de entrada y salida en el medio, por lo que será más lento.
    • 2. Cuando el servidor envía datos, primero volverá a consultar con el cliente para ver si hay un búfer (protocolo de protocolo de enlace de tres vías), si lo hay, puede continuar enviando.
  • Cuando se llama a la función de escritura, los datos se moverán al búfer de salida y se transferirán al búfer de entrada de la otra parte en el momento adecuado (ya sea que se transmitan por separado o todos a la vez). En este momento, la otra parte llamará a la función de lectura para leer datos del búfer de entrada. Estas características del almacenamiento en búfer de E/S se pueden resumir de la siguiente manera.
    R: El almacenamiento en búfer de E/S existe individualmente dentro de cada socket TCP.
    B: El almacenamiento en búfer de E/S se genera automáticamente cuando se crea el socket.
    C: Los datos que quedan en el búfer de salida seguirán entregándose incluso si el socket está cerrado .
    D: Al cerrar el socket se perderán los datos en el búfer de entrada .
    Entonces, ¿qué sucederá en la siguiente situación? Después de comprender el almacenamiento en búfer de E/S, el proceso:
    "El búfer de entrada del cliente es de 50 bytes y el servidor transmite 100 bytes".
    Esto de hecho es un problema. El búfer de entrada tiene solo 50 bytes, pero se recibieron 100 bytes de datos. Se puede proponer la siguiente solución:
    llamar rápidamente a la función de lectura para leer los datos antes de llenar el búfer de entrada, esto liberará algo de espacio y el problema se resolverá .

  • De hecho, tales problemas no ocurrirán en absoluto porque TCP controlará el flujo de datos.
    Existe un protocolo de ventana deslizante (Sliding Window) en TCP, que se presenta de la siguiente manera en modo conversacional.
    Socket A: "Hola, puedes enviarme hasta 50 bytes."
    Socket B: "¡OK!"
    Socket A: "He liberado 20 bytes de espacio y puedes recibir hasta 70 bytes". .Socket B:
    "¡OK!"
    Lo mismo ocurre con el envío y la recepción de datos, por lo que los datos no se perderán debido al desbordamiento del búfer en TCP.
    Sin embargo, la eficiencia de la transmisión se verá afectada debido al almacenamiento en búfer.

2. Principios internos de TCP

  • Tres pasos principales en la comunicación TCP:
    • 1. Apretón de manos de tres vías para establecer conexión;
    • 2. Iniciar la comunicación e intercambiar datos;
    • 3. Salude cuatro veces para desconectarse;

1. Apretón de manos de tres vías

(El cliente inicia - el servidor responde)

  • [Primer apretón de manos] Socket A: "Hola, socket B. Tengo datos que enviarte, por favor establece una conexión. "
    [Segundo apretón de manos] Socket B: "Está bien, este lado está listo".
    [Tercer apretón de manos] Socket A : "Gracias por aceptar mi petición."

Insertar descripción de la imagen aquí
Primero , el host A que solicita una conexión transmite la siguiente información al host B:
[SYN] SEQ:1000, ACK: -La
SEQ en este mensaje es 1000, el ACK está vacío y el significado de SEQ 1000 es el siguiente:
"El El número de serie del paquete de datos que se está transmitiendo ahora es 1000. Si la recepción es correcta, notifíqueme para que le entregue el paquete de datos número 1001". Este es el mensaje que se utiliza al solicitar una conexión por primera vez, también conocido como SYN. . SYN es la abreviatura de Sincronización, que representa el mensaje de sincronización transmitido antes de enviar y recibir datos .

A continuación, el host B transmite el siguiente mensaje a A:
[SYN+ACK]SEQ:2000, ACK:1001.
En este momento, SEQ es 2000, ACK es 1001 y el significado de SEQ 2000 es el siguiente:
" El número de serie del paquete de datos que se transmite es 2000. Si lo recibió correctamente, notifíqueme para entregarle el paquete de datos No. 2001 ".

El significado de ACK1001 es el siguiente:
" El paquete de datos con SEQ 1000 que se acaba de transmitir se recibió correctamente. Ahora pase el paquete de datos con SEQ 1001 " .

El mensaje de confirmación (ACK1001) del paquete de datos transmitido por primera vez por el host A y el mensaje de sincronización (SEQ2000) que prepara la transmisión de datos por el host B se envían en un paquete, por lo que este tipo de mensaje también se denomina SYN+. ACK.

Antes de enviar y recibir datos, asigne un número de secuencia al paquete de datos y notifique a la otra parte el número de secuencia, lo cual es una preparación para evitar la pérdida de datos . Al asignar números de secuencia a los paquetes y reconocerlos, los paquetes perdidos se pueden ver y retransmitir inmediatamente en caso de pérdida de datos. Por tanto, TCP puede garantizar una transmisión de datos fiable. Finalmente, observe el mensaje transmitido por el host A al host B:
[ACK]SEQ:1001, ACK:2001

Es necesario asignar números de secuencia al enviar paquetes de datos durante una conexión TCP.
Sume 1 al número de serie anterior 1000, es decir, asigne 1001. En este momento, el paquete de datos entrega el siguiente mensaje:
"El paquete de datos transmitido con SEQ 2000 se recibió correctamente y el paquete de datos con SEQ 2001 ahora se puede transmitir".

Esto transmite el mensaje ACK con ACK2001 agregado. En este punto, el Anfitrión A y el Anfitrión B han confirmado que el otro está listo.

  • Maldita sea, el texto es demasiado complicado, déjame darte una explicación fácil de entender.
    El apretón de manos de tres vías de TCP es como caminar solo en la comunidad en una noche oscura y ventosa. No muy lejos, ves a una hermosa chica de la comunidad que viene hacia ti. Sin embargo, no puedes estar 100% seguro porque las luces de la calle son un Un poco oscuro, por lo que debes usar el saludo para determinar si la otra persona te reconoce.
    Primero saludas a la chica (syn), y después de que la chica te ve saludándola, te saluda con la cabeza y esboza una sonrisa (ack). Después de ver a la niña sonreír, confirmas que la niña te ha identificado exitosamente (ingresando al estado establecido).
    Pero la chica estaba un poco avergonzada y miró a su alrededor para ver si estaba mirando a alguien más, también necesitaba confirmar. La chica también te saludó (syn). Cuando viste a la chica saludándote, supiste que la otra parte estaba buscando tu confirmación, así que asentiste y sonreíste (ack). La chica confirmó después de ver la sonrisa de la otra parte. te estás saludando (entrando en el estado establecido).

Ataque utilizando el protocolo de enlace de tres vías TCP :
el cliente sigue realizando solo el primer protocolo de enlace, por lo que el servidor sigue creando conexiones y asignando números de secuencia en función de este protocolo de enlace, pero estas conexiones creadas requieren un tiempo de espera de aproximadamente dos horas, por lo que ocupa más servidor. recursos y un volumen excesivo de datos bloquea el servidor.

2. Transmisión de datos

  • Durante la transmisión de datos, debemos prestar atención a: retransmisión y deduplicación. El módulo del kernel de red del sistema operativo ya se ha encargado de estas dos tareas por nosotros.
    Insertar descripción de la imagen aquí
  • La transmisión de datos TCP significa que dos personas se comunican por aire, hay una cierta distancia y la otra parte necesita confirmar repetidamente que escuchó lo que dijeron.
    Gritas una frase (seq), y después de que la niña la escuche, te responderá que la escuchó (ack).
    Si gritas y no escuchas respuesta de la chica durante mucho tiempo, te sentirás muy deprimido, al igual que cuando estás enamorado, estás lleno de entusiasmo, pero la chica tiene calor y frío, así que perseveras. Si no funciona una vez, hazlo dos veces. Si no funciona dos veces, hazlo dos veces. Tres veces, esto es retransmisión TCP. Dado que se retransmitirá, es posible que la niña haya escuchado la misma frase dos veces, lo que supone la eliminación de la duplicación.
    El módulo del kernel de red del sistema operativo ya se ha encargado de las dos tareas de retransmisión y deduplicación por nosotros.
    Cuando todo termine, tu hermana y tú os separaréis a regañadientes.
    Entonces tenemos que romper.
    Es decir, hemos llegado a las cuatro ondas de comunicación TCP.

3. Saluda cuatro veces

Insertar descripción de la imagen aquí

5. Programación UDP

UDP es la forma principal de ataque DDOS (ataque de denegación de servicio distribuido en la red) . La dirección del remitente se puede falsificar y los datos no se pueden rechazar. Los paquetes de datos deben recibirse, pero debido a que la dirección del remitente está falsificada, el remitente no puede ser encontrado y la dirección del remitente no se puede rechazar.

1. Principios básicos de UDP

Podemos ilustrar cómo funciona UDP a través de letras.Este es el ejemplo tradicional utilizado para explicar UDP y es totalmente consistente con las características de UDP. Antes de enviar una carta, debe completar las direcciones del remitente y del destinatario en el sobre, luego colocar un sello y colocarlo en el buzón. Por supuesto, la naturaleza de la carta hace imposible confirmar si la otra parte la recibió. Además, las cartas pueden perderse durante el proceso de envío. Dicho esto, las cartas son un método de transmisión poco fiable. De manera similar, UDP también proporciona servicios de transmisión de datos poco confiables.
"En este caso, TCP debería ser un mejor protocolo, ¿verdad?"

Si solo considera la confiabilidad, TCP es mejor que UDP . Pero UDP es estructuralmente más simple que TCP . UDP no envía un mensaje de respuesta como ACK, ni asigna números de secuencia a paquetes de datos como SEQ. Por lo tanto, el rendimiento de UDP es a veces mucho mayor que el de TCP. Implementar UDP en programación también es más sencillo que TCP. Además, aunque UDP no es tan confiable como TCP, la corrupción de datos no ocurre con tanta frecuencia como se imagina. Por lo tanto, UDP es una buena opción cuando el rendimiento es más importante que la confiabilidad.

En este caso, ¿cuál es el papel de UDP? Para proporcionar servicios de transmisión de datos confiables, TCP realiza control de flujo en la capa IP no confiable y UDP carece de este mecanismo de control de flujo.
El control de flujo es el símbolo más importante que distingue a UDP y TCP . Pero si elimina el control de flujo de TCP, solo quedará un puñado de contenido. En otras palabras, la vida de TCP radica en el control de flujo .

Si se compara TCP con una llamada telefónica, UDP se compara con una carta. Pero esto sólo describe cómo funciona el protocolo y no incluye el tipo de cambio de datos. No piense erróneamente que "las llamadas telefónicas son más rápidas que las cartas, por lo que la velocidad de envío y recepción de datos de TCP también es más rápida que la de UDP". En realidad, todo lo contrario. TCP no puede ser más rápido que UDP, pero puede estar cerca de UDP al enviar y recibir ciertos tipos de datos. Por ejemplo, cuanto mayor sea la cantidad de datos intercambiados cada vez, más cercana estará la velocidad de transmisión TCP a la velocidad de transmisión UDP.

TCP se utiliza en combinación con UDP: TCP se utiliza para el control de flujo y UDP se utiliza para la transmisión de datos.

Debido a la naturaleza desordenada de la transmisión de datos de paquetes UDP, debido al cambio de estado del enrutador, es posible que los paquetes se envíen primero y lleguen más tarde.

Insertar descripción de la imagen aquí

  • Como se puede ver en la figura anterior, la función de IP es entregar con precisión el paquete de datos UDP que sale del host B al host A. Pero el proceso de entrega final del paquete UDP a un determinado socket UDP del host A lo completa UDP. La función más importante de UDP es entregar los paquetes de datos transmitidos al host al socket UDP final de acuerdo con el número de puerto . De hecho, en escenarios de aplicaciones reales, UDP también tiene cierto grado de confiabilidad. Las características de transmisión de la red conducen a pérdidas frecuentes de información, pero si desea transmitir archivos comprimidos (al enviar 10,000 paquetes de datos, solo una pérdida causará problemas), debe usar TCP, porque siempre que se pierda una parte del archivo comprimido, es difícil descomprimirlo. Pero la situación es diferente cuando se transmite vídeo o audio en tiempo real a través de una red. Para los datos multimedia, no es un gran problema perder parte de ellos, lo que sólo provocará fluctuaciones de imagen a corto plazo o ruidos sutiles. Sin embargo, debido a que es necesario proporcionar servicios en tiempo real, la velocidad se convierte en un factor muy importante y en este momento es necesario considerar UDP. Pero UDP no siempre es más rápido que TCP. Las razones por las que TCP es más lento que UDP suelen deberse a los dos puntos siguientes.
    • 1 El proceso de configuración y borrado de la conexión antes y después de enviar y recibir datos.
    • 2. Se agregó control de flujo para garantizar la confiabilidad durante el proceso de envío y recepción de datos.
      Especialmente cuando la cantidad de datos enviados y recibidos es pequeña pero se requieren conexiones frecuentes, UDP es más eficiente que TCP.

2. Conector UDP

  • La diferencia entre UDP y TCP:

    • 1. UDP no tiene escuchar y aceptar
    • 2. UDP envía y recibe datos en un socket, que se distingue según la dirección del remitente y la dirección del destinatario de y recvfrom.
  • El servidor y el cliente en UDP no están conectados.
    El servidor/cliente UDP no intercambia datos en el estado conectado como TCP, por lo que, a diferencia de TCP, no es necesario pasar por el proceso de conexión. En otras palabras, no es necesario llamar a la función de escucha y aceptar la función llamada durante el proceso de conexión TCP. Solo existe el proceso de creación de un socket y el proceso de intercambio de datos en UDP.

  • Tanto el servidor UDP como el cliente solo necesitan 1 socket
    en TCP y debe haber una relación uno a uno entre los sockets. Para atender a 10 clientes, necesita 10 sockets del lado del servidor además del socket del servidor del gatekeeper. Pero en UDP, tanto el servidor como el cliente solo necesitan 1 socket . Cuando expliqué el principio de UDP antes, di el ejemplo de una carta: el buzón utilizado para enviar y recibir cartas se puede comparar con un socket UDP. Siempre que haya un buzón cerca, podrá enviar cartas a cualquier dirección a través de él. Asimismo, sólo se necesita 1 socket UDP para transmitir datos a cualquier host

Insertar descripción de la imagen aquí

  • La figura anterior muestra el proceso de intercambio de datos entre un socket UDP y dos hosts diferentes. En otras palabras, solo un socket UDP puede comunicarse con múltiples hosts .
  • Una vez creado el socket TCP, no es necesario agregar información de dirección al transmitir datos. Porque el socket TCP permanecerá conectado al socket de la otra parte. En otras palabras, el socket TCP conoce la información de la dirección de destino. Sin embargo, los sockets UDP no mantienen el estado de la conexión (los sockets UDP solo tienen una función de buzón simple), por lo que se debe agregar información de la dirección de destino cada vez que se transmiten datos. Esto equivale a completar la dirección en la carta antes de enviarla . Las siguientes son: Funciones relacionadas con UDP llamadas al completar la dirección y transmitir datos.

1. función sendto()—remitente

#include<sys/socket.h>
→成功时返回传输的字节数,失败时返回-1。
ssize_t sendto(int sock,void*buff,size_t nbytes,int flags,struct sockaddr *to, socklen_t addrlen);

● sock
es el descriptor de archivo de socket UDP utilizado para transmitir datos.
● buff
guarda el valor de la dirección del búfer de los datos que se van a transmitir.
● nbytes
La longitud de los datos que se transmitirán, en bytes.
● marca
el parámetro opcional; si no está disponible, pase 0.

El valor de dirección de la variable de estructura sockaddr que almacena la información de la dirección de destino.
● addrlen
es la longitud de la variable de estructura del valor de dirección pasada al parámetro.

La mayor diferencia entre la función sendto y la función de salida TCP anterior es que esta función necesita pasarle la información de la dirección de destino . A continuación, presentamos la función para recibir datos UDP. El remitente de datos UDP no es fijo , por lo que esta función se define en un formulario que puede recibir la información del remitente, es decir, la información del remitente en el paquete de datos UDP se devolverá al mismo tiempo .

2. función recvfrom(): destinatario

#include<sys/socket.h>
//→成功时返回接收的字节数,失败时返回-1。
ssize_t recvfrom(int sock,  void *buff,size_t nbytes,  int flags,
struct sockaddr*from,   socklen_t*addrlen);

●sock es el descriptor de archivo de socket UDP utilizado para recibir datos.
●buff guarda el valor de la dirección de caché de los datos recibidos.
●nbytes El número máximo de bytes que se pueden recibir, por lo que no puede exceder el tamaño del búfer señalado por el parámetro buf.
●flags parámetro opcional, si no está disponible, pase 0.
●fromEl valor de dirección de la variable de estructura sockaddr que almacena la información de la dirección del remitente.
●addrlen contiene el valor de la dirección variable de la longitud variable de la estructura del parámetro.
La parte central al escribir un programa UDP radica en las dos funciones anteriores, que también ilustran su estado en la transmisión de datos UDP.

3. Programación UDP: servidor de eco

1. servidor

  • 1. Crear enchufe
  • 2. Configurar la familia de direcciones (ip, puerto)
  • 3. Vincular vincular
  • 4. Recibir datos recibidos desde y enviar datos enviados a
  • 5. Cerrar el enchufe

2. Cliente

  • 1. Crear enchufe
  • 2. Configurar la familia de direcciones (ip, puerto)
  • 3. Enviar datos enviados a y recibir datos recibidos desde
  • 4. Cerrar el enchufe

3. Código fuente principal

//UDP回声服务器
#define BUF_SIZE 30 //buffer字节大小

//需要控制台 输入端口
void udp_server(int argc, char* argv[])
{
    
    
    if (argc != 2) {
    
    //校验端口参数,
        printf("Usage : %s <port>\n", argv[0]);
        error_handling("argement is error: 端口");
    }

    int serv_sock;
    char message[BUF_SIZE];
    socklen_t clnt_adr_sz;
    struct sockaddr_in serv_adr, clnt_adr;
    
    //创建socket
    serv_sock = socket(PF_INET, SOCK_DGRAM, 0);//UDP的socket SOCK_DGRAM: 报文类型
    if (serv_sock == -1)
        error_handling("UDP socket creation error");

    memset(&serv_adr, 0, sizeof(serv_adr));
    //配置socket的地址族
    serv_adr.sin_family = AF_INET;
    serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY->0.0.0.0 htonl:主机字节序 转网络字节序 l:long型 4或8字节
    serv_adr.sin_port = htons((uint16_t)atoi(argv[1]));//需要输入端口 htons:主机字节序 转网络字节序 s:short型,2字节

    //绑定
    if (bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)) == -1)
        error_handling("bind() error");
        
    //收发数据
    ssize_t str_len;
    while (1)
    {
    
    
        clnt_adr_sz = sizeof(clnt_adr);
        str_len = recvfrom(serv_sock, message, BUF_SIZE, 0, (struct sockaddr*)&clnt_adr, &clnt_adr_sz);//利用分配的地址接收数据。不限制数据传输对象
        sendto(serv_sock, message, str_len, 0, (struct sockaddr*)&clnt_adr, clnt_adr_sz);//函数调用同时获取数据传输端的地址。正是利用该地址将接收的数据逆向重传。
    }
    close(serv_sock);
}

void udp_client(int argc, char* argv[])
{
    
    
    if (argc != 3) {
    
    
        printf("Usage : %s <IP> <port>\n", argv[0]);
        error_handling("argement is error: IP");
    }

    int sock;
    char message[BUF_SIZE];
    socklen_t adr_sz;

    struct sockaddr_in serv_adr, from_adr;
    
    //创建socket
    sock = socket(PF_INET, SOCK_DGRAM, 0);
    if (sock == -1)
        error_handling("socket() error");

    memset(&serv_adr, 0, sizeof(serv_adr));//清理,防止默认值

    //配置socket的地址族
    serv_adr.sin_family = AF_INET;
    serv_adr.sin_addr.s_addr = inet_addr(argv[1]);//控制台输入ip地址,eg:127.0.0.1
    serv_adr.sin_port = htons((uint16_t)atoi(argv[2]));//控制台输入端口

    //收发数据
    ssize_t str_len;
    while (1)
    {
    
    
        fputs("Insert message(q to quit): ", stdout);
        fgets(message, sizeof(message), stdin);
        if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))//字符串比较
            break;

        ssize_t len = sendto(sock, message, strlen(message), 0, (struct sockaddr*)&serv_adr, sizeof(serv_adr));
        memset(message, 0, len);
        adr_sz = sizeof(from_adr);
        str_len = recvfrom(sock, message, BUF_SIZE, 0, (struct sockaddr*)&from_adr, &adr_sz);

        printf("Message from server: %s", message);
        message[str_len] = 0;
    }
    close(sock);
}


//主进程开启客户端,再开启服务端,是为了在主进程调试方便
void udp_server_client_connect(int argc, char* argv[])
{
    
    

    char* argv0 = "";
    pid_t pid = fork();
    if (pid < 0)
        std::cout << "fork failed!" << pid << std::endl;

    if (pid == 0)//开启新进程,子进程(服务端进程)
    {
    
    

        int argc = 2;
        char* argv[] = {
    
     argv0, (char*)"9555" };
        udp_server(argc, argv);
        
    }
    else//pid > 0 , 父进程(客户端进程) 
    {
    
    
        int argc = 3;
        char* argv[] = {
    
     argv0, (char*)"127.0.0.1", (char*)"9555" };
        udp_client(argc, argv);

        int status = 0;
        wait(&status);
    }

}

3. Características de transmisión y llamadas UDP.

  • El método de implementación del servidor/cliente UDP se explicó anteriormente. Pero si observa de cerca el cliente UDP, encontrará que carece del proceso de asignación de IP y puertos a los sockets.
  • El cliente TCP llama a la función de conexión para completar automáticamente este proceso, mientras que UDP ni siquiera tiene una declaración de llamada de función que pueda asumir la misma función. ¿Cuándo exactamente se asignan los números de IP y de puerto?
  • En un programa UDP, la asignación de la dirección del socket debe completarse antes de llamar a la función sendto para transmitir datos , por lo que se llama a la función de enlace.
  • Por supuesto, la función de vinculación ha aparecido en programas TCP, pero la función de vinculación no distingue entre TCP y UDP , es decir, también se puede llamar en programas UDP.
  • Además, si se descubre que la información de la dirección no se ha asignado al llamar a la función sendto, la IP y el puerto se asignarán automáticamente al socket correspondiente cuando se llame a la función sendto por primera vez.
  • Además, la dirección asignada en este momento se conserva hasta el final del programa (antes de cerrar el socket) , por lo que también se puede utilizar para intercambiar datos con otros sockets UDP.
  • Por supuesto, utilice la IP del host como IP y seleccione cualquier número de puerto no utilizado como número de puerto.

En resumen, la IP y el número de puerto se asignan automáticamente cuando se llama a la función sendto, por lo que generalmente no hay necesidad de procesos de asignación de direcciones adicionales en el cliente UDP .

6. Varias opciones de enchufes.

1. Tamaño del búfer de E/S

1. Comprensión:

Insertar descripción de la imagen aquí

2, getsockopt y setsockopt

Podemos leer (Obtener) y configurar (Establecer) casi todas las opciones en la tabla anterior (por supuesto, algunas opciones solo pueden realizar una operación). La lectura y configuración de opciones se completan a través de las dos funciones siguientes.

#include<sys/socket.h>
//→成功时返回0,失败时返回-1。
int getsockopt(int sock, int level,int optname, void *optval, socklen_t *optlen);

●sock: se utiliza para ver los descriptores de archivos de socket de opciones.
●nivelLa capa de protocolo opcional que se va a ver.
●optname El nombre de la opción opcional que se va a ver.
●optval guarda el valor de la dirección del búfer del resultado de visualización.
●El tamaño del búfer pasado por optlen al cuarto parámetro optval. Después de llamar a la función, esta variable almacena la cantidad de bytes de información opcional devuelta a través del cuarto parámetro.

#include<sys/socket.h>
//→成功时返回0,失败时返回-1。
int setsockopt(int sock, int level, int optname, const void*optval, socklen_t optlen);

●sock se utiliza para cambiar el descriptor de archivo de socket opcional.
●nivelLa capa de protocolo opcional que se va a cambiar.
●optname El nombre opcional que se va a cambiar.
●optval contiene el valor de la dirección del búfer de la información de la opción que se va a cambiar.
●optlen El número de bytes de información opcional pasados ​​al cuarto parámetro optval.

3, SO_SNDBUF y SO_RCVBUF

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
void error_handling(char *message);

int main(int argc, char *argv[])
{
    
    
	int sock;
	int snd_buf=1024*3, rcv_buf=1024*3;
	int state;
	socklen_t len;
	
	sock=socket(PF_INET, SOCK_STREAM, 0);
	state=setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void*)&rcv_buf, sizeof(rcv_buf));
	if(state)
		error_handling("setsockopt() error!");
	
	state=setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void*)&snd_buf, sizeof(snd_buf));
	if(state)
		error_handling("setsockopt() error!");
	
	len=sizeof(snd_buf);
	state=getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void*)&snd_buf, &len);
	if(state)
		error_handling("getsockopt() error!");
	
	len=sizeof(rcv_buf);
	state=getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void*)&rcv_buf, &len);
	if(state)
		error_handling("getsockopt() error!");
	
	printf("Input buffer size: %d \n", rcv_buf);
	printf("Output buffer size: %d \n", snd_buf);
	return 0;
}

void error_handling(char *message)
{
    
    
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}

2, SO_REUSEADDR

  • Error de asignación de dirección (Error de enlace) Se produjo la reasignación de dirección

El servidor se encuentra en un estado de espera de tiempo debido a un retraso en la obtención de una respuesta.

  • La solución de tiempo de espera es cambiar el estado de SO_REUSEADDR en las opciones del socket. Al ajustar este parámetro adecuadamente, el número de puerto del socket en el estado de tiempo de espera se puede reasignar a un nuevo socket . El valor predeterminado de SO_REUSEADDR es 0 (falso), lo que significa que no se puede asignar un número de puerto de socket en el estado de tiempo de espera. Por lo tanto, debe cambiar este valor a 1 (verdadero).
	//解决Time-wait的问题
    getsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, &addrlen);
    printf("SO_REUSEADDR = %d\n", optval);
    //设置optval
    if (optval == 0)
        optval = 1;

    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, addrlen);
  • proyecto de demostración:
//------------------------ Time-wait--------------------------
#define TRUE 1
#define FALSE 0

void tw_tcp_server()
{
    
    
    int sock, client, optval = 0;
    struct sockaddr_in addr, cliAddr;
    socklen_t addrlen = sizeof(addr);
    char buffer[256] = "";

    sock = socket(PF_INET, SOCK_STREAM, 0);

    //解决Time-wait的问题
    getsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, &addrlen);
    printf("SO_REUSEADDR = %d\n", optval);
    //设置optval
    if (optval == 0)
        optval = 1;

    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, addrlen);
    //查看新的optval
    getsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, &addrlen);
    printf("SO_REUSEADDR = %d\n", optval);

    memset(&addr, 0, addrlen);
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port = htons(9526);

    addrlen = sizeof(addr);
    if (bind(sock, (struct sockaddr*) & addr, addrlen) == -1)
    {
    
    
        error_handling("tw_tcp_server bind failed");
    }

    listen(sock, 3);
    client = accept(sock, (struct sockaddr*)&cliAddr, &addrlen);
    read(client, buffer, sizeof(buffer));
    close(client);
    close(sock);
}

void tw_tcp_client()
{
    
    
    int client = socket(PF_INET, SOCK_STREAM, 0);
    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    servaddr.sin_port = htons(9526);
    int ret = connect(client, (struct sockaddr*)&servaddr, sizeof(servaddr));
    if (ret == -1) {
    
    
        std::cout << "client connect failed!\n";
        close(client);
        return;
    }
    std::cout << "client connect server is success!\n";

    char buffer[256] = "";
    while (ret == 0)
    {
    
    
        fputs("Input message(Q to quit):", stdout);//提示语句,输入Q结束
        fgets(buffer, sizeof(buffer), stdin);//对文件的标准输入流操作 读取buffer的256字节
        if (strcmp(buffer, "q\n") == 0 || (strcmp(buffer, "Q\n") == 0)) {
    
    
            break;
        }

        size_t len = strlen(buffer);
        size_t send_len = 0;

        //当数据量很大时,并不能一次把所有数据全部发送完,因此需要分包发送
        while (send_len < len)
        {
    
    
            ssize_t ret = write(client, buffer + send_len, len - send_len);//send_len 记录分包的标记
            if (ret <= 0) {
    
    //连接出了问题
                fputs("may be connect newwork failed,make client write failed!\n", stdout);
                close(client);
                return;
            }
            send_len += (size_t)ret;

            std::cout << "client write success, msg:" << buffer << std::endl;

        }
        memset(buffer, 0, sizeof(buffer));

        //当数据量很大时,并不能一次把所有数据全部读取完,因此需要分包读取
        size_t read_len = 0;
        while (read_len < len)
        {
    
    
            size_t ret = read(client, buffer + read_len, len - read_len);
            if (ret <= 0) {
    
    //连接出了问题
                fputs("may be connect newwork failed, make client read failed!\n", stdout);
                close(client);
                return;
            }
            read_len += (size_t)ret;
        }
        std::cout << "from server:" << buffer << std::endl;
    };

    close(client);
    std::cout << "client done!" << std::endl;
}

void tw_func(char* option)
{
    
    
    if (strcmp(option, "1") == 0)
    {
    
    
        tw_tcp_server();
        tw_tcp_server();
    }
    else {
    
    
        tw_tcp_client();
    }
}
  • Llamado en función principal:
 tw_func(argv[1]);//Time-wait超时等待
  • resultado de la operación:
    Insertar descripción de la imagen aquí

3,TCP_NODELAY

NODELAY: Sin retraso.

Algoritmo de Nagle

El algoritmo de Nagle, que lleva el nombre de su inventor John Nagle, se utiliza para concatenar automáticamente muchos mensajes pequeños en el buffer ; este proceso (llamado nagling) aumenta la eficiencia de los sistemas de software de red al reducir la cantidad de paquetes que deben enviarse .
Insertar descripción de la imagen aquí
De la figura anterior se pueden extraer las siguientes conclusiones:

  • "El algoritmo Nagle sólo envía los datos siguientes cuando se recibe un mensaje ACK para los datos anteriores." Los
    sockets TCP utilizan el algoritmo Nagle de forma predeterminada para intercambiar datos, por lo que el almacenamiento en búfer se maximiza hasta que se recibe un ACK. Este es exactamente el caso en el lado izquierdo de la imagen de arriba. Para enviar la cadena "Nagle", pásela al búfer de salida. En este momento no hay otros datos antes del carácter de encabezado "N" (no hay ACK para recibir), por lo que se transmite inmediatamente. Luego comienza a esperar el mensaje ACK del carácter "N". Durante el proceso de espera, el "águilo" restante se llena en el búfer de salida. A continuación, después de recibir el mensaje ACK del carácter "N", la salida "agle" almacenada en el buffer se carga en un paquete de datos y se envía. En otras palabras, es necesario pasar un total de 4 paquetes de datos para transmitir 1 cadena.
  • A continuación, analice el proceso de envío de la cadena "Nagle" cuando no se utiliza el algoritmo de Nagle. Supongamos que los caracteres "N" a "e" se pasan al búfer de salida en orden. El proceso de envío en este momento no tiene nada que ver con si se recibe ACK o no, por lo que los datos se enviarán inmediatamente después de llegar al búfer de salida. Como puede ver en el lado derecho de la figura anterior, se necesitan un total de 10 paquetes para enviar la cadena "Nagle". Se puede ver que no utilizar el algoritmo de Nagle tendrá un impacto negativo en el tráfico de la red. Incluso si solo se transmite 1 byte de datos, la información del encabezado puede tener docenas de bytes. Por lo tanto, para mejorar la eficiencia de transmisión de la red, se debe utilizar el algoritmo de Nagle.
  • Cuando la cadena se pasa al búfer de salida del programa, no se pasa palabra por palabra, por lo que la situación real de enviar la cadena "Nagle" no es como se muestra en la figura anterior. Pero si los caracteres que componen la cadena se transfieren al búfer de salida después de un período de tiempo (si existe dicha transferencia de datos), puede ocurrir una situación similar a la imagen de arriba. En la imagen de arriba, los datos a enviar se transfieren al búfer de salida a intervalos.
  • Pero el algoritmo de Nagle no es aplicable todo el tiempo. Según las características de los datos transmitidos, cuando el tráfico de la red no se ve muy afectado, la velocidad de transmisión es más rápida cuando no se utiliza el algoritmo Nagle que cuando se utiliza . El más típico es " transmitir datos de archivos grandes ". Llevar los datos del archivo al búfer de salida no lleva mucho tiempo, por lo que incluso sin utilizar el algoritmo de Nagle, los paquetes se transmitirán cuando el búfer de salida esté lleno. Esto no solo no aumentará la cantidad de paquetes de datos, sino que se transmitirá continuamente sin esperar ACK, por lo que la velocidad de transmisión se puede mejorar considerablemente.
  • En general, no aplicar el algoritmo de Nagle puede mejorar la velocidad de transmisión. Sin embargo, si abandona incondicionalmente el algoritmo de Nagle, aumentará el tráfico excesivo de la red y afectará la transmisión . Por lo tanto, el algoritmo de Nagle no debe desactivarse cuando las características de los datos no se juzgan con precisión .
  • El algoritmo de Nagle debe desactivarse para los "datos de archivos grandes" que acabamos de mencionar. En otras palabras, el algoritmo de Nagle debería desactivarse si es necesario. "Hay poca diferencia en el tráfico de la red, ya sea que se use o no el algoritmo de Nagle. La velocidad de transmisión que usa el algoritmo de Nagle es más lenta". El método de desactivación es muy simple.
  • Como también se puede ver en el siguiente código, simplemente cambie la opción del socket TCP_NODELAY a 1 (verdadero).
int opt_val=1;
setsockopt(sock, IPPROTO_TCP,TCP_NODELAY,(void*)&opt_val, sizeof(opt_val));
  • Puede verificar el estado de configuración del algoritmo de Nagle a través del valor de TCP_NODELAY .
int opt_val;socklen_t opt_len;
opt_len=sizeof(opt_val);
getsockopt(sock,IPPROTO_TCP,TCP_NODELAY,(void*)&opt_val,&opt_len);
  • Si se utiliza el algoritmo de Nagle, se guarda 0 en la variable opt val;
    si el algoritmo de Nagle está deshabilitado, se guarda 1.

Supongo que te gusta

Origin blog.csdn.net/MOON_YZM/article/details/130818508
Recomendado
Clasificación