Este artigo é compartilhado pela Huawei Cloud Community "O Huawei Cloud SMS Service ensina você a usar Perl para implementar o protocolo Smgp ", autor: Zhang Jian.
Introdução e visão geral do protocolo
O China Telecom Short Message Gateway Protocol (SMGP) é um protocolo de comunicação desenvolvido pela China Netcom para implementar serviços SMS. Seu nome completo é Short Message Gateway Protocol. É usado entre o Short Message Gateway (SMGW) e o Provedor de Serviços (SP). Comunicação entre Short Message Gateway (SMGW) e Short Message Gateway (SMGW).
Perl é uma linguagem de script antiga instalada por padrão em muitos sistemas Linux. Por exemplo, na imagem base da versão 22.04 do Ubuntu, não há nem Python, mas o Perl ainda está instalado. O módulo IO::Async do Perl fornece um modelo de programação IO assíncrono conciso.
O protocolo SMGP funciona em um modelo cliente/servidor. O cliente (aplicativo SMS, como telefone celular, aplicativo, etc.) primeiro estabelece uma conexão TCP longa com o gateway SMS (gateway de mensagens curtas SMGW) e usa comandos CNGP para interagir com o SMGW para enviar e receber mensagens SMS. No protocolo CNGP, o próximo comando pode ser enviado sem esperar por uma resposta de forma síncrona. Os implementadores podem implementar dois modos de transmissão de mensagens, síncronos e assíncronos, de acordo com suas próprias necessidades para atender aos requisitos de desempenho em diferentes cenários.
Diagrama de tempo
Conexão bem-sucedida, envie mensagem de texto

Conexão bem-sucedida, SMS recebido do SMGW

Introdução aos quadros de protocolo
Cabeçalho SMGP
O cabeçalho contém os seguintes campos, cujo tamanho e comprimento são todos de 4 bytes
- Comprimento do pacote : O comprimento de toda a PDU, incluindo cabeçalho e corpo.
- Request ID : Usado para identificar o tipo de PDU (por exemplo, Login, Submit, etc.).
- Id de sequência : número de sequência, usado para combinar solicitações e respostas.
Use perl para estabelecer conexões na pilha de protocolos SMGP
├── Makefile.PL ├── exemplos │ └── smgp_client_login_example.pl └──lib └── Smgp ├── BoundAtomic.pm ├── Cliente.pm ├── Constante.pm └── Protocolo.pm
exemplos: armazena código de amostra
- smgp_client_login_example.pl: armazena exemplo de login Smgp
- BoundAtomic.pm: classe de ferramenta incremental usada para gerar SequenceId
- Client.pm: definição do Smgp, responsável pela comunicação com os serviços do Smgp, como estabelecer conexões, enviar mensagens de texto, etc.
- Protocol.pm: armazena PDU, codec, etc.
Implementar incremento de sequence_id
sequencia_id é um valor de 1 a 0x7FFFFFFFF
pacote Smgp::BoundAtomic; use estrito; use avisos FATAL => 'todos'; sub novo { minha ($classe,%args) = @_; meu $self = { min => $args{min}, máximo => $args{máximo}, valor => $args{min}, }; abençoe $self, $class; retornar $self; } subincremento { meu ($self) = @_; if ($self->{valor} >= $self->{max}) { $self->{valor} = $self->{min}; } outro { $self->{valor}++; } retornar $self->{valor}; } sub obter { meu ($self) = @_; retornar $self->{valor}; } 1;
Definir SMGP PDU e funções de codificação e decodificação em Perl
pacote Smgp::Protocolo; use estrito; use avisos FATAL => 'todos'; use Smgp::Constante; sub new_login { minha ($classe,%args) = @_; meu $self = { clienteId => $args{clientId}, authenticatorClient => $args{authenticatorClient}, loginMode => $args{loginMode}, timeStamp => $args{timeStamp}, versão => $args{versão}, }; return abençoe $self, $class; } subencode_login { meu ($self) = @_; return pack("A8A16CNC", @{$self}{qw(clientId authenticatorClient loginMode timeStamp version)}); } subdecode_login_resp { minha ($classe, $buffer) = @_; meu ($status, $authenticatorServer, $versão) = unpack("N4A16C", $buffer); retorne abençoado { status => $ status, authenticatorServer => $authenticatorServer, versão => $versão, }, $classe; } sub novo_cabeçalho { minha ($classe,%args) = @_; meu $self = { comprimento_total => $args{comprimento_total}, request_id => $args{request_id}, status_de_comando => $args{status_de_comando}, sequência_id => $args{sequence_id}, }; return abençoe $self, $class; } subencode_header { meu ($self, $total_length) = @_; return pack("N3", $total_length, @{$self}{qw(request_id sequencia_id)}); } sub new_pdu { minha ($classe,%args) = @_; meu $self = { cabeçalho => $args{cabeçalho}, corpo => $args{corpo}, }; return abençoe $self, $class; } subencode_login_pdu { meu ($self) = @_; meu $encoded_body = $self->{body}->encode_login(); retorne $self->{header}->encode_header(length($encoded_body) + 12) . $encoded_body; } sub decode_pdu { minha ($classe, $buffer) = @_; meu ($request_id, $sequence_id) = unpack("N2", substr($buffer, 0, 8)); meu $body_buffer = substr($buffer, 8); meu $header = $class->new_header( comprimento_total => 0, request_id => $ request_id, sequência_id => $sequence_id, ); meu $corpo; if ($request_id == Smgp::Constant::LOGIN_RESP_ID) { $body = $class->decode_login_resp($body_buffer); } outro { morrer "solicitação_id não suportada: $request_id"; } retorne $class->new_pdu( cabeçalho => $ cabeçalho, corpo => $corpo, ); } 1;
constante.pm armazena requestId relacionado
pacote Smgp::Constante; use estrito; use avisos FATAL => 'todos'; usar constante { LOGIN_ID => 0x00000001, LOGIN_RESP_ID => 0x80000001, ENVIAR_ID => 0x00000002, ENVIAR_RESP_ID => 0x80000002, DELIVER_ID => 0x00000003, DELIVER_RESP_ID => 0x80000003, ACTIVE_TEST_ID => 0x00000004, ACTIVE_TEST_RESP_ID => 0x80000004, FORWARD_ID => 0x00000005, FORWARD_RESP_ID => 0x80000005, EXIT_ID => 0x00000006, EXIT_RESP_ID => 0x80000006, QUERY_ID => 0x00000007, QUERY_RESP_ID => 0x80000007, MT_ROUTE_UPDATE_ID => 0x00000008, MT_ROUTE_UPDATE_RESP_ID => 0x80000008, }; 1;
Implementar métodos de cliente e login
pacote Smgp::Cliente; use estrito; use avisos FATAL => 'todos'; use IO::Socket::INET; use Smgp::Protocolo; use Smgp::Constante; sub novo { minha ($classe,%args) = @_; meu $self = { host => $args{host} // 'localhost', porta => $args{porta} // 9000, soquete => undef, sequência_id => 1, }; abençoe $self, $class; retornar $self; } subconectar { meu ($self) = @_; $self->{socket} = IO::Socket::INET->new( PeerHost => $self->{host}, PeerPort => $self->{porta}, Proto => 'tcp', ) ou morrer "Não é possível conectar-se a $self->{host}:$self->{port} $!"; } sublogin { meu ($self, $corpo) = @_; meu $header = Smgp::Protocol->new_header( request_id => Smgp::Constant::LOGIN_ID, sequência_id => 1, ); meu $pdu = Smgp::Protocol->new_pdu( cabeçalho => $ cabeçalho, corpo => $corpo, ); $self->{socket}->send($pdu->encode_login_pdu()); $self->{socket}->recv(meu $response_length_bytes, 4); meu $total_length = unpack("N", $response_length_bytes); meu $remain_length = $total_length - 4; $self->{socket}->recv(meu $response_data, $remain_length); return Smgp::Protocol->decode_pdu($response_data)->{body}; } subdesconexão { meu ($self) = @_; fechar($self->{socket}) se $self->{socket}; } 1;
Execute o exemplo para verificar se a conexão foi bem-sucedida
pacote smgp_client_login_example; use estrito; use avisos FATAL => 'todos'; use Smgp::Cliente; use Smgp::Protocolo; use Smgp::Constante; sub principal { meu $cliente = Smgp::Cliente->new( host => 'localhost', porta => 9000, ); $cliente->conectar(); meu $login = Smgp::Protocol->new_login( clienteId => '12345678', authenticatorClient => '1234567890123456', loginMode => 1, timeStamp => hora(), versão => 0, ); minha $resposta = $client->login($login); if ($resposta->{status} == 0) { print "Login realizado com sucesso!\n"; } outro { print "Falha no login! Status: ", (definido $response->{status} ? $response->{status} : 'indefinido'), "\n"; } $cliente->desconectar(); } main() a menos que chamador; 1;
Projetos de código aberto relacionados
- netty-codec-sms armazena codecs netty para vários protocolos SMS (como cmpp, sgip, smpp)
- sms-client-java armazena clientes Java de vários protocolos SMS
- sms-server-java armazena servidores Java para vários protocolos SMS
- implementação cmpp-python python do protocolo cmpp
- implementação python do protocolo cngp-zig cmpp
- implementação perl smgp-perl do protocolo smgp
- implementação smpp-rust ferrugem do protocolo smpp
Resumir
Este artigo apresenta brevemente o protocolo SMGP e tenta usar perl para implementar a pilha de protocolos. No entanto, o envio comercial real de SMS costuma ser mais complicado e enfrenta problemas como controle de fluxo, interoperabilidade do operador e segurança da camada de transporte . O serviço & SMS (Mensagem e SMS) é acessado através do protocolo HTTP . O serviço SMS da Huawei Cloud é um serviço de comunicação fornecido pela Huawei Cloud para usuários corporativos em cooperação com muitas operadoras e canais de alta qualidade em todo o mundo. As empresas podem usar código de verificação e serviços de notificação por SMS chamando a API ou usando o assistente de envio de grupo.
Clique para seguir e conhecer as novas tecnologias da Huawei Cloud o mais rápido possível~