[Rede de computadores] Protocolo UDP, programação UDP, UDP conectado

Conhecimento básico

O UDP exporta a camada de transporte no protocolo TCP / IP em camadas.
O UDP é um protocolo de comunicação não confiável, sem retransmissão e reconhecimento, sem controle de pedidos e sem controle de congestionamento.
O UDP não garante a entrega efetiva de pacotes, nem a ordem dos pacotes, ou seja, ao usar o UDP, precisamos fazer um bom trabalho de perda de pacotes, retransmissão e montagem de mensagens.
O UDP é relativamente simples e existem muitos cenários adequados: nossos serviços DNS comuns e serviços SNMP são baseados no protocolo UDP, que não são particularmente sensíveis ao atraso e à perda de pacotes. Além disso, cenários de comunicação para várias pessoas, como salas de bate-papo, jogos com vários jogadores etc. também usarão o protocolo UDP.
OSI e TCP / IP

Formato da mensagem

O cabeçalho UDP tem 8 bytes, que são a porta de origem, a porta de destino, o comprimento do pacote UDP e a soma de verificação .
Como o tamanho de uma mensagem UDP é registrado apenas com 2 bytes, o tamanho máximo de uma mensagem, incluindo o comprimento do cabeçalho, é 65535 bytes.

Ao enviar com o protocolo UDP, o comprimento máximo dos dados que podem ser enviados com a função sendto é: cabeçalho 65535-IP (20) - cabeçalhoUDP (8) = 65507 bytes. Ao enviar dados com a função sendto, se o comprimento dos dados enviados for maior que esse valor, a função retornará um erro.
Como o IP tem o MTU máximo,
o tamanho do pacote UDP deve ser o cabeçalho 1500-IP (20) -UDP header (8) = 1472 (bytes)

Mensagem UDP

Programação UDP

Servidor:
1. Crie um soquete.
2. Ligue o ip e a porta a serem monitorados.
3. Loop:
3.1 Ligue para recvfrom para ler a mensagem recebida e bloqueie-a se não houver mensagem.
3.2 Depois de receber a mensagem, ligue para sendto e envie para o cliente de acordo.

Cliente:
1. Crie um soquete.
2. Loop:
2.1 Ligue para enviar para enviar a solicitação.
2.2 Chame recvfrom para receber o correspondente.

#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); 

Tempo de programação UDP
O código refere-se ao meu artigo anterior: Programa de eco UDP https://blog.csdn.net/Hanoi_ahoj/article/details/105358383

O recurso "sem conexão" dos pacotes UDP permite continuar enviando pacotes após a reinicialização do servidor UDP. Esta é a melhor explicação para o "sem contexto" dos pacotes UDP.

UDP conectado

Através do exposto acima, no UDP, não é necessário estabelecer uma conexão semelhante à conexão na programação TCP.
De fato, o UDP também pode ser "conectado".

Vamos testá-lo através de um programa:
Cliente: Note -se que após a criação do soquete, ele é conectado e vinculado ao ip e à porta do 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: é um servidor normal e a solicitação é retornada inalterada.

#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

Teste:
1. Não execute o servidor, apenas execute o cliente.
Como você pode ver, ele foi enviado quando o sendto foi chamado, mas houve um erro que o Connection recusou ao caminhar para recvfrom.

cliente
2. Execute servidor e cliente.
Receba e processe solicitações normalmente.
servidor && cliente

O papel da conexão UDP

Se você não se conectar, não inicie o servidor, execute o cliente, o programa será bloqueado novamente. Até o servidor reiniciar ou atingir o tempo limite.
Geralmente, o servidor UDP não precisa se conectar, porque o servidor só pode se conectar ao cliente após a conexão.

A função do connect é permitir que o programa receba a mensagem de erro o mais rápido possível e retorne:

Ao executar uma operação de conexão em um soquete UDP, é estabelecido um "contexto" para o soquete UDP, que possui uma conexão com o endereço e a porta do lado do servidor.É esse relacionamento de ligação que fornece ao kernel do sistema operacional as informações necessárias , Pode associar as informações recebidas pelo kernel do sistema operacional ao soquete correspondente.

Quando a função de operação sendto ou send é chamada, a mensagem do aplicativo é enviada, nosso aplicativo retorna, o kernel do sistema operacional assume a mensagem e, em seguida, o sistema operacional começa a tentar enviar para o endereço e porta correspondentes, porque o endereço e a porta correspondentes não são Uma vez alcançada, uma mensagem ICMP será retornada ao kernel do sistema operacional.A mensagem ICMP contém informações como o endereço de destino e a porta.

A operação de conexão foi realizada para ajudar o kernel do sistema operacional a estabelecer o relacionamento de mapeamento entre (endereço de destino do soquete UDP + porta) Quando uma mensagem inacessível do ICMP é recebida, o kernel do sistema operacional pode encontrá-lo na tabela de mapeamento Em qual soquete UDP tem o endereço e a porta de destino, não se esqueça que o soquete é globalmente exclusivo dentro do sistema operacional. Quando chamamos o método recvfrom ou recv no soquete novamente, podemos receber a operação A mensagem "Conexão recusada" retornada pelo kernel do sistema.

Após a conexão com o UDP, muitos livros sobre o uso das funções de envio e recebimento são recomendados: função de
envio ou gravação a ser enviada, se você usar o envio, precisará definir zero as informações relevantes para endereçar;
use a função recv ou leitura para receber, se O uso de recvfrom precisa definir as informações correspondentes do endereço como zero.
De fato, diferentes implementações do UNIX se comportam de maneira diferente.

Fator de eficiência:

Como se você não usar o método de conexão, sempre que enviar uma mensagem, será necessário este processo:
conectar soquete → enviar mensagem → desconectar soquete → conectar soquete → enviar mensagem → desconectar soquete → ...... ...

Se você usar o método de conexão, ele se tornará o seguinte:
conectar soquete → enviar mensagem → enviar mensagem → ... → finalmente desconectar o soquete

Sabemos que conectar soquetes requer uma certa quantidade de sobrecarga, como a necessidade de procurar informações da tabela de roteamento. Portanto, o programa cliente UDP pode obter uma certa melhoria de desempenho através da conexão.


Referência: Programação de Rede Prática em Tempo de Geek ( https://time.geekbang.org/column/article/129807 )

EOF

98 artigos originais foram publicados · 91 elogios · mais de 40.000 visualizações

Acho que você gosta

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