Uma das principais séries de tecnologia para desenvolvimento de aplicativos distribuídos - design de mensagem original baseado em TCP/IP

Este artigo é original e publicado pela primeira vez pela equipe técnica da Grape City. Por favor, indique a fonte para reimpressão: Site oficial da Grape City . Grape City fornece aos desenvolvedores ferramentas, soluções e serviços de desenvolvimento profissional para capacitar os desenvolvedores.

Prefácio

O conteúdo deste artigo concentra-se principalmente nas seguintes partes:

  1. Uma breve introdução ao TCP/IP.
  2. Introdução da mensagem.
  3. Formatos de transporte baseados na classificação da mensagem (tipo de fluxo e tipo XML).
  4. A composição do sistema de mensagens.

Uma breve introdução ao TCP/IP

TCP/IP (Transmission Control Protocol/Internet Protocol) é a linguagem ou protocolo básico de comunicação na Internet. Na verdade, é um programa de duas camadas, dividido em alto e baixo nível. O nível mais alto é o Transmission Control Protocol, responsável por agregar informações ou dividir arquivos em pacotes menores. Esses pacotes são transmitidos pela rede para a camada TCP na extremidade receptora, e a camada TCP na extremidade receptora restaura os pacotes aos arquivos originais. A camada inferior é o Protocolo da Internet, que trata da parte do endereço de cada pacote para que os pacotes cheguem ao seu destino corretamente. Os computadores gateway na rede roteiam as informações com base em seu endereço. Mesmo subpacotes do mesmo arquivo podem ser roteados de maneira diferente, mas eventualmente convergem para o destino. O TCP/IP usa um modelo cliente/servidor para comunicação.

Arquitetonicamente, o TCP/IP não está totalmente em conformidade com o modelo de referência de 7 camadas do 0SI. O modelo tradicional de referência de interconexão de sistema aberto é um modelo de referência abstrato de 7 camadas de um protocolo de comunicação, no qual cada camada executa uma tarefa específica. O objetivo deste modelo é permitir que vários hardwares se comuniquem entre si no mesmo nível. Essas 7 camadas são: camada física, camada de enlace de dados, camada de rede, camada de transporte, camada de sessão, camada de apresentação e camada de aplicação. O protocolo de comunicação TCP/IP adota uma estrutura hierárquica de 4 camadas, e cada camada chama a rede fornecida pela próxima camada para atender às suas próprias necessidades. Essas 4 camadas são:

  • Camada de aplicativo: A camada para comunicação entre aplicativos, como Simple Mail Transfer Protocol (SMTP), File Transfer Protocol (FTP), Remote Network Access Protocol (Telnet), etc.
  • Camada de transporte: Nesta camada fornece transmissão de dados entre nós e serviços de comunicação entre aplicações, tendo como principais funções a formatação de dados, confirmação de dados e retransmissão de perdas. Como o Protocolo de Controle de Transmissão (TCP), o Protocolo de Datagrama do Usuário (UDP), etc. TCP e UDP adicionam dados de transmissão ao pacote de dados e os transmitem para a próxima camada. Esta camada é responsável por transmitir os dados e determinar se os dados foram foi entregue e receba.
  • Camada de rede de interconexão: Responsável por fornecer funções básicas de transmissão de pacotes de dados para que cada pacote de dados possa chegar ao host de destino (mas não verifica se foi recebido corretamente), como o Protocolo de Internet (IP).
  • Camada de interface de rede (camada host-rede): recebe datagramas IP e os transmite, recebe quadros físicos da rede, extrai datagramas IP e os encaminha para a próxima camada, gerencia a mídia de rede real e define como usar a rede real ( como Ethernet, linha serial, etc.) para transmitir dados.

Funções comumente usadas em TCP/IP

1. Função de soquete

int socket(int domain,int type,int protocol),

domínio especifica o conjunto de protocolos usado, geralmente PF INET, que representa o conjunto de protocolos da Internet (conjunto de protocolos TCP/IP); o parâmetro type especifica o tipo de soquete; SOCK STREAM para TCP ou SOCK DGRAM para UDP; geralmente é atribuído um valor ao protocolo de [0]. A chamada da função socket retorna um descritor de soquete inteiro, que pode ser chamado posteriormente.

2.função de ligação:

A função bind associa o soquete a uma porta na máquina local e, em seguida, escuta solicitações de serviço nessa porta. O protótipo da função de ligação é:

int bind(int sockfd,struct sockaddr *my addr, int addrlen);

sockfd é o descritor de soquete retornado pela chamada da função socket; meu addr é um ponteiro para o tipo sockaddr contendo informações como o endereço IP local e o número da porta: addrlen geralmente é definido como sizeof (struct sockaddr).

3. função de conexão de conexão:

O programa cliente orientado a conexão usa a função connect para configurar o soquete e estabelecer uma conexão TCP com o servidor remoto. Seu protótipo de função é:

int connect(int sockfd, struct sockaddr *serv addr,int addrlen);

sockfd é o descritor de soquete retornado pela função socket; serv addr é um ponteiro contendo o endereço IP e o número da porta do host remoto; addrlen é o comprimento da estrutura de endereço remoto. A função connect retorna -1 quando ocorre um erro e define errno como o código de erro correspondente. Não há necessidade de chamar bind 0 ao projetar programas clientes, porque neste caso você só precisa saber o endereço IP da máquina de destino, e o cliente não precisa se preocupar com qual porta o cliente usa para estabelecer uma conexão com o servidor. O programa executor de soquete seleciona automaticamente uma porta desocupada e notifica o programa quando os dados chegam à porta.

4. ouvir função de escuta:

A função de escuta de rede (ouvir) coloca o soquete no modo de escuta passiva e estabelece uma fila de dados de entrada para o soquete, salvando as solicitações de serviço que chegam nesta fila até que o programa as processe.

int listen(int sockfd, int backlog);

sockfd é o descritor de soquete retornado pela chamada do sistema Socket; backlog especifica o número máximo de solicitações permitidas na fila de solicitações. As solicitações de conexão recebidas aguardarão na fila pela função de recebimento (aceite 0) (veja abaixo). O backlog limita o número de solicitações aguardando atendimento na fila. Geralmente o valor padrão do sistema é 20. Se uma solicitação de serviço chegar e a fila de entrada estiver cheia, o soquete rejeitará a solicitação de conexão e o cliente receberá uma mensagem de erro.

5. aceitar a função de recebimento:

A função accept0 permite que o servidor aceite a solicitação de conexão do cliente. Após estabelecer a fila de entrada, o servidor chama a função de aceitação, então dorme e aguarda a solicitação de conexão do cliente.

int accept(int sockfd, void *addr, int *addrlen);

sockfd é o descritor de soquete monitorado, addr geralmente é um ponteiro para a variável sockaddr_in, que é usada para armazenar informações sobre o host que faz o serviço de solicitação de conexão (um host envia a solicitação de uma determinada porta); addrlen geralmente é um ponteiro para Uma variável de ponteiro inteiro cujo valor é sizeof (struct sockaddr in). Quando ocorre um erro, a função de aceitação retorna -1 e define o código de erro errno correspondente.

6. função sendto e função recvfrom:

int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to, int tolen):

to representa o endereço IP e as informações do número da porta da máquina de destino, e tolen geralmente recebe o valor de sizeof (struct sockaddr). A função sendto retorna o comprimento real dos bytes de dados enviados ou -1 em caso de erro de envio.

int recyfrom(int sockfd,void *buf,int len,unsigned int flags,structsockaddr *from,int *fromlen);

from é uma variável do tipo struct sockaddr, que salva o endereço IP e o número da porta do host de origem. fromlen é frequentemente definido como sizeof (struct sockaddr).Quando recvfrom() retorna, fromlen contém o número de bytes de dados realmente armazenados em from. A função recvfrom() retorna o número de bytes recebidos ou -1 quando ocorre um erro e define o código de erro errno correspondente.

7. função de desligamento

função de desligamento para fechar o soquete. Esta função permite interromper a transferência de dados em uma direção apenas enquanto a transferência de dados na outra direção continua.

int shutdown(int sockfd,int how);

sockfd é o descritor do soquete que precisa ser fechado. O parâmetro how permite escolher os seguintes métodos para a operação de desligamento:

  • 0-1 Não permite continuar recebendo dados
  • 1--Não permitir continuar enviando dados
  • 2-Não permitir posterior envio e recebimento de dados

shutdown retorna 0 quando a operação é bem-sucedida, retorna -1 quando ocorre um erro e define o código de erro errno correspondente.

8.função fcntl

A função fcntl pode alterar as propriedades de um arquivo aberto.

int fcntl (int fields, int cmd, .../* int arg */) ;

9.funções getsockopt e setsockopt

Essas duas funções podem obter ou definir opções associadas a um soquete. Para manipular as opções da camada de soquete, o valor da camada deve ser especificado como SOL SOCKET. Para operar as opções de controle de outras camadas, o número de protocolo apropriado deve ser fornecido. Por exemplo, para indicar que uma opção deve ser analisada pelo TCP, a camada deve ser definida como o número do protocolo TCP.

int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen);
int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);

10.selecionar função

A função select é uma chamada de sistema ou função usada para multiplexação. Geralmente é usado para lidar com vários fluxos de entrada e saída para implementar operações de E/S assíncronas.

int select(int n, fd set * readfds, fd set * writefds, fd set * exceptfds,struct timeval * timeout);

O parâmetro n representa o maior descritor de arquivo mais 1. Os parâmetros readfds, writefds e exceptfds são chamados de grupos de descritores e são usados ​​para retornar o status de leitura, gravação ou exceção do descritor.

11. função de enquete

int poll(struct pollfd fds[], nfds t nfds, int timeout);

Entre eles, fds é um array do tipo struct pollfd, usado para armazenar o descritor de soquete cujo status precisa ser detectado. A definição de struct pollfd é a seguinte:

struct pollfd {
        //descriptor to check
        int fd;
        //events of interest on fd
        short events;
        //events that occurred on fd
        short revents;
}

o que é novidade

Mensagem é a menor unidade no nível de programação durante a comunicação entre duas entidades lógicas na rede no desenvolvimento de aplicativos distribuídos.

Aqui estão algumas explicações para a definição acima:

(1) O conceito de mensagens existe no trabalho de desenvolvimento e está localizado no nível da programação. Enquanto o sistema está em execução, ele fica transparente para os usuários do aplicativo.

(2) Duas entidades lógicas na rede referem-se a dois programas que podem ser executados de forma independente.Eles podem ser implantados em dois dispositivos físicos diferentes na rede ou podem ser implantados no mesmo dispositivo físico, mas geralmente dois Um processo independente sem um relacionamento pai-filho (isto é diferente do conceito de mensagem mais básico na programação IPC).

(3) A mensagem é a menor unidade no nível de programação na comunicação distribuída, ou seja, não importa quantos ou poucos dados estejam envolvidos na comunicação, o código do programa é implementado enviando e recebendo uma ou mais mensagens.

(4) A comunicação entre duas aplicações na rede inclui dois tipos: transmissão de fluxo de dados e chamada de procedimento remoto (função).

(5) As mensagens podem ser usadas para obter comunicação estruturada de dados entre aplicativos distribuídos. Em outras palavras, o que os programadores enfrentam no nível da comunicação não é mais um fluxo de bytes real, mas uma unidade de dados estruturada que pode ser combinada a partir de vários tipos de dados.

Na verdade, esta própria unidade de dados estruturados é uma "mensagem", que pode ser representada externamente como uma estrutura ou classe. Portanto, quando o mecanismo de mensagem baseado na definição acima é estabelecido, os programadores só precisam gerar a mensagem correspondente quando a comunicação distribuída é necessária durante o processo de codificação e, em seguida, chamar as interfaces de envio e recebimento correspondentes para implementá-la convenientemente. para entender o conhecimento de TCP/IP, dominar as habilidades básicas de programação de soquete e não há necessidade de considerar outras questões, como muitas mensagens seriais, muitas mensagens simultâneas, controle de fluxo de rede, etc., para que os aplicativos distribuídos possam realmente ser Os esforços de desenvolvimento estão concentrados na implementação de negócios, o que melhora muito a eficiência e a qualidade do desenvolvimento de sistemas distribuídos, especialmente sistemas distribuídos de grande escala.

Quanto à forma de existência da mensagem, na linguagem C tradicional, ela pode ser uma estrutura struct; em uma linguagem orientada a objetos (C++ ou Java), pode ser uma classe.

Formato de transmissão baseado na classificação da mensagem

Com base nos diferentes formatos de transmissão de mensagens, as mensagens podem ser divididas em mensagens de fluxo e mensagens XML. As mensagens de fluxo são transmitidas com base no formato de streaming de bytes binários e as mensagens XML são transmitidas com base em strings no formato XML.

mensagens de streaming

Mensagens de fluxo referem-se a mensagens que são transmitidas e processadas em fluxo (stream) em um sistema de computador. Uma mensagem de fluxo consiste em uma série de dados contínuos, que são gerados no terminal emissor em uma determinada ordem e transmitidos ao terminal receptor na forma de um fluxo. Durante o processo de transmissão, a extremidade receptora pode ler os dados do fluxo um por um. , para mensagens de fluxo, não importa como o programador expresse a mensagem, a mensagem precisa ser convertida em um formato de fluxo binário antes de ser realmente enviada.Esse processo de conversão é chamado de Streamlização ou Serilização.

Mensagem XML

Mensagens XML referem-se a um método de transmissão de dados que usa Extensible Markup Language (XML) como formato de mensagem. XML é uma linguagem de marcação de texto usada para descrever e armazenar dados. Ela usa tags para definir a estrutura e as propriedades dos dados. No mecanismo de mensagem XML, depois que os programadores expressam o conteúdo da mensagem no formato XML, eles não precisam realizar nenhum trabalho de conversão de formato para envio e transmissão (excluindo o trabalho de criptografia para transmissão segura) e podem enviá-lo diretamente no formato de string XML. . Mensagens XML também são amplamente utilizadas. Por exemplo, o protocolo SOAP em Web Service é projetado e implementado com base em mensagens XML.

Por exemplo: método de design e implementação baseado em streaming de mensagens

O editor abaixo apresentará brevemente como enviar e receber informações de uma pessoa (incluindo altura, nome e idade) nos dois aplicativos.

(1) Defina uma classe para armazenar informações das pessoas:

struct Person {
        char name[20] ;
        float height;
        int age;
}
struct Person p;
strcpy(p.name ,"Michael Zhang");
height = 170.00;
age = 30;

(2) Estruture a sequência de informações

char sendStream[1024] = {0};
sprintf(sendStream,"|%s|%f"%d",p.name, p.height, p.age);

(3) O remetente envia um fluxo de bytes:

/*注: 这里省略建立/管理/关闭 TCP 连接的代码*/
char datalen[4+1] = (0);
sprintf(datalen,"04d" , strlen (sendStream) );
if(SendBytes ( socket, datalen, 4) == -1) {
        return -l;
}
if(SendBytes(socket, sendStream, strlen(sendStream)) == -1) {
        return -1
}

Observe que a função SendBytes no código acima garante que todos os fluxos de bytes de um determinado comprimento sejam enviados com sucesso antes de retornar. Isto ocorre principalmente porque chamar a função de envio ou gravação no soquete não pode garantir que o fluxo de bytes de um determinado comprimento possa ser enviado completamente de uma só vez. A ideia básica do SendBytes é enviar em um loop até que todos os bytes sejam enviados com sucesso. O código de implementação é o seguinte:

int SendBytes (int sd, const void *buffer, unsigned len) {
        int rez = 0;
        int leftlen = len;
        int readlen = 0:
}
while(true) {
        rez = write (socket, (char *)buffer+readlen, len-readlen);
        if(rez < 0) {
                if (errno != EWOULDBLOCK && errno != EINTR) {
                        ErrorMsg("Error is serious );
                        DisConnect(socket);
        }
    return -l:
    }
    readlen += rez;
    leftlen -= rez;
    if(leftlen <= 0){
    break;
    }
   }
return len:
}

(4) O receptor recebe o fluxo de bytes:

char datalen[4+1] = {0};
char receiveStream[1024] = {0};
sprintf(datalen,"%04d", strlen(sendStream)) ;
if(ReceiveBytes(socket, datalen, 4) == -1 {
        return -l;
}
int packet len = atoi(datalen) :
if(ReceiveBytes (socket, receiveStream, packet len) == -1) {
        return -l;
}

A função ReceiveBytes pode referir-se à terceira etapa em que o remetente envia o fluxo de bytes.

(5) O fluxo de bytes é desserializado para obter a estrutura:

struct Person p;
sscanf(receiveStream,"%[`|]|%f|%d", p.name, &p.height, &p.age) ;

Resumir

Este artigo apresenta brevemente o protocolo TCP/IP e suas funções de interface comumente usadas, depois apresenta o formato de classificação e transmissão de mensagens no protocolo TCP/IP e, finalmente, termina com um exemplo simples de envio de mensagens. Se você tiver algum comentário ou sugestão sobre o conteúdo, fique à vontade para deixar mensagens e discutir na área de comentários.

Livro de referência: "Design e Desenvolvimento de Mensagens - Tecnologia Central de Desenvolvimento de Aplicativos Distribuídos" He Xiaochao

Link de extensão:

Do orientado ao formulário ao orientado ao modelo, interprete a tendência de desenvolvimento de plataformas de desenvolvimento de baixo código

O que é uma plataforma de desenvolvimento low-code?

O gerenciamento de versões baseado em filiais ajuda o low-code a passar da entrega do projeto para o desenvolvimento de produtos personalizados

Lei Jun: A versão oficial do novo sistema operacional da Xiaomi, ThePaper OS, foi empacotada. A janela pop-up na página de loteria do Gome App insulta seu fundador. O Ubuntu 23.10 foi lançado oficialmente. Você também pode aproveitar a sexta-feira para atualizar! Episódio de lançamento do Ubuntu 23.10: A imagem ISO foi "recuperada" com urgência devido a conter discurso de ódio. Um estudante de doutorado de 23 anos corrigiu o "bug fantasma" de 22 anos no Firefox. O desktop remoto RustDesk 1.2.3 foi lançado, Wayland aprimorado para suportar a versão TiDB 7.4: Oficial compatível com MySQL 8.0. Depois de desconectar o receptor USB da Logitech, o kernel do Linux travou. O mestre usou o Scratch para esfregar o simulador RISC-V e executou com sucesso o kernel do Linux. JetBrains lançou o Writerside, uma ferramenta para criação de documentos técnicos.
{{o.nome}}
{{m.nome}}

Acho que você gosta

Origin my.oschina.net/powertoolsteam/blog/10120032
Recomendado
Clasificación