Este artículo es compartido por la comunidad de la nube de Huawei " El servicio SMS de la nube de Huawei le enseña a usar Perl para implementar el protocolo Smgp ", autor: Zhang Jian.
Introducción y descripción general del protocolo
El Protocolo de puerta de enlace de mensajes cortos (SMGP) de China Telecom es un protocolo de comunicación desarrollado por China Netcom para implementar servicios de SMS. Su nombre completo es Protocolo de puerta de enlace de mensajes cortos. Se utiliza entre el Portal de mensajes cortos (SMGW) y el Proveedor de servicios (SP). Comunicación entre la puerta de enlace de mensajes cortos (SMGW) y la puerta de enlace de mensajes cortos (SMGW).
Perl es un antiguo lenguaje de secuencias de comandos que se instala de forma predeterminada en muchos sistemas Linux. Por ejemplo, en la imagen base de la versión 22.04 de Ubuntu ni siquiera hay Python, pero Perl todavía está instalado. La popularidad de Perl es evidente. El módulo IO::Async de Perl proporciona un modelo de programación IO asíncrono conciso.
El protocolo SMGP funciona en un modelo cliente/servidor. El cliente (aplicación de SMS, como teléfono móvil, aplicación, etc.) primero establece una conexión TCP larga con la puerta de enlace SMS (puerta de enlace de mensajes cortos SMGW) y utiliza comandos CNGP para interactuar con SMGW para enviar y recibir mensajes SMS. En el protocolo CNGP, el siguiente comando se puede enviar sin esperar una respuesta de forma sincrónica. Los implementadores pueden implementar dos modos de transmisión de mensajes, sincrónico y asíncrono, de acuerdo con sus propias necesidades para cumplir con los requisitos de rendimiento en diferentes escenarios.
Diagrama de tiempo
Conexión exitosa, enviar mensaje de texto

Conexión exitosa, SMS recibido de SMGW

Introducción a las tramas de protocolo.
Encabezado SMGP
El encabezado contiene los siguientes campos, cuyo tamaño y longitud son 4 bytes
- Longitud del paquete : la longitud de toda la PDU, incluidos el encabezado y el cuerpo.
- ID de solicitud : se utiliza para identificar el tipo de PDU (por ejemplo, iniciar sesión, enviar, etc.).
- ID de secuencia : número de secuencia, utilizado para hacer coincidir solicitudes y respuestas.
Utilice Perl para establecer conexiones en la pila de protocolos SMGP
├── Makefile.PL ├── ejemplos │ └── smgp_client_login_example.pl └── biblioteca └── Smgp ├── BoundAtomic.pm ├── Cliente.pm ├── Constante.pm └── Protocolo.pm
ejemplos: almacena código de muestra
- smgp_client_login_example.pl: ejemplo de inicio de sesión de Smgp en tiendas
- BoundAtomic.pm: clase de herramienta incremental utilizada para generar SequenceId
- client.pm: Definición de Smgp, responsable de comunicarse con los servicios de Smgp, como establecer conexiones, enviar mensajes de texto, etc.
- Protocol.pm: almacena PDU, códec, etc.
Implementar el incremento de id_secuencia
secuencia_id es un valor de 1 a 0x7FFFFFFFF
paquete Smgp::BoundAtomic; utilizar estricto; utilizar advertencias FATAL => 'todos'; sub nuevo { mi ($clase, %args) = @_; mi $yo = { min => $args{min}, máx => $args{máx}, valor => $args{min}, }; bendito $yo, $clase; devolver $ uno mismo; } subincremento { mi ($yo) = @_; si ($self->{valor} >= $self->{max}) { $self->{valor} = $self->{min}; } demás { $self->{valor}++; } devolver $self->{valor}; } sub obtener { mi ($yo) = @_; devolver $self->{valor}; } 1;
Definir SMGP PDU y funciones de codificación y decodificación en Perl
paquete Smgp::Protocolo; utilizar estricto; utilizar advertencias FATAL => 'todos'; utilizar Smgp::Constante; sub nuevo_iniciar sesión { mi ($clase, %args) = @_; mi $yo = { clienteId => $args{clienteId}, authenticatorClient => $args{authenticatorClient}, modo de inicio de sesión => $args{modo de inicio de sesión}, marca de tiempo => $args{marca de tiempo}, versión => $args{versión}, }; volver bendice $self, $class; } subencode_login { mi ($yo) = @_; return pack("A8A16CNC", @{$self}{qw(clientId authenticatorClient loginMode timeStamp version)}); } sub decode_login_resp { mi ($clase, $buffer) = @_; mi ($estado, $authenticatorServer, $versión) = descomprimir("N4A16C", $buffer); volver bendiga { estado => $estado, servidorautenticador => $servidorautenticador, versión => $versión, }, $clase; } sub nuevo_encabezado { mi ($clase, %args) = @_; mi $yo = { longitud_total => $args{longitud_total}, request_id => $args{request_id}, estado_comando => $args{estado_comando}, id_secuencia => $args{id_secuencia}, }; volver bendice $self, $class; } subencode_header { mi ($self, $total_length) = @_; paquete de retorno ("N3", $longitud_total, @{$self}{qw(request_id secuencia_id)}); } sub nuevo_pdu { mi ($clase, %args) = @_; mi $yo = { encabezado => $args{encabezado}, cuerpo => $args{cuerpo}, }; volver bendice $self, $class; } sub encode_login_pdu { mi ($yo) = @_; mi $encode_body = $self->{body}->encode_login(); devuelve $self->{header}->encode_header(length($encoded_body) + 12). $cuerpo_codificado; } sub decode_pdu { mi ($clase, $buffer) = @_; mi ($request_id, $sequence_id) = desempaquetar("N2", substr($buffer, 0, 8)); mi $body_buffer = substr($buffer, 8); mi $encabezado = $clase->new_header( longitud_total => 0, id_solicitud => $id_solicitud, id_secuencia => $id_secuencia, ); mi $cuerpo; si ($request_id == Smgp::Constante::LOGIN_RESP_ID) { $cuerpo = $clase->decode_login_resp($body_buffer); } demás { morir "Solicitud_id no compatible: $request_id"; } devolver $clase->new_pdu( encabezado => $encabezado, cuerpo => $cuerpo, ); } 1;
Constant.pm almacena el ID de solicitud relacionado
paquete Smgp::Constante; utilizar estricto; utilizar advertencias FATAL => 'todos'; usar constante { LOGIN_ID => 0x00000001, LOGIN_RESP_ID => 0x80000001, SUBMIT_ID => 0x00000002, SUBMIT_RESP_ID => 0x80000002, ENTREGA_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 inicio de sesión
paquete Smgp::Cliente; utilizar estricto; utilizar advertencias FATAL => 'todos'; utilizar IO::Socket::INET; utilizar Smgp::Protocolo; utilizar Smgp::Constante; sub nuevo { mi ($clase, %args) = @_; mi $yo = { anfitrión => $args{host} // 'localhost', puerto => $args{puerto} // 9000, enchufe => indef, secuencia_id => 1, }; bendito $yo, $clase; devolver $ uno mismo; } subconexión { mi ($yo) = @_; $self->{socket} = IO::Socket::INET->nuevo( PeerHost => $self->{host}, PeerPort => $self->{puerto}, Protocolo => 'tcp', ) o morir "¡No se puede conectar a $self->{host}:$self->{port} $!"; } subiniciar sesión { mi ($yo, $cuerpo) = @_; mi $encabezado = Smgp::Protocolo->new_header( request_id => Smgp::Constante::LOGIN_ID, secuencia_id => 1, ); mi $pdu = Smgp::Protocolo->new_pdu( encabezado => $encabezado, cuerpo => $cuerpo, ); $self->{socket}->send($pdu->encode_login_pdu()); $self->{socket}->recv(mi $response_length_bytes, 4); mi $longitud_total = desempaquetar("N", $longitud_respuesta_bytes); mi $remain_length = $total_length - 4; $self->{socket}->recv(mis $response_data, $remain_length); return Smgp::Protocolo->decode_pdu($response_data)->{body}; } subdesconexión { mi ($yo) = @_; close($self->{socket}) si $self->{socket}; } 1;
Ejecute el ejemplo para verificar que la conexión sea exitosa
paquete smgp_client_login_example; utilizar estricto; utilizar advertencias FATAL => 'todos'; utilizar Smgp::Cliente; utilizar Smgp::Protocolo; utilizar Smgp::Constante; sub principal { mi $cliente = Smgp::Cliente->nuevo( anfitrión => 'localhost', puerto => 9000, ); $cliente->conectar(); mi $login = Smgp::Protocolo->new_login( ID de cliente => '12345678', clienteautenticador => '1234567890123456', modo de inicio de sesión => 1, marca de tiempo => hora(), versión => 0, ); mi $respuesta = $cliente->iniciar sesión($iniciar sesión); si ($respuesta->{estado} == 0) { print "¡Inicio de sesión exitoso!\n"; } demás { print "¡Falló el inicio de sesión! Estado: ", (definido $respuesta->{estado} ? $respuesta->{estado} : 'indefinido'), "\n"; } $cliente->desconectar(); } main() a menos que la persona que llama; 1;
Proyectos de código abierto relacionados
- netty-codec-sms almacena códecs netty para varios protocolos de SMS (como cmpp, sgip, smpp)
- sms-client-java almacena clientes Java de varios protocolos SMS
- sms-server-java almacena servidores Java para varios protocolos de SMS
- cmpp-python implementación en Python del protocolo cmpp
- Implementación en Python del protocolo cmpp cngp-zig
- smgp-perl implementación perl del protocolo smgp
- smpp-rust implementación de óxido del protocolo smpp
Resumir
Este artículo presenta brevemente el protocolo SMGP e intenta utilizar Perl para implementar la pila de protocolos. Sin embargo, el envío de SMS comerciales real suele ser más complicado y enfrenta problemas como el control de flujo, la interoperabilidad del operador y la seguridad de la capa de transporte. Puede elegir Huawei Cloud Message. & Se accede al servicio SMS (Mensajes y SMS) a través del protocolo HTTP . El servicio Huawei Cloud SMS es un servicio de comunicación proporcionado por Huawei Cloud a usuarios empresariales en cooperación con muchos operadores y canales de alta calidad en todo el mundo. Las empresas pueden utilizar códigos de verificación y servicios de notificación por SMS llamando a la API o utilizando el asistente de envío grupal.
Haga clic para seguir y conocer las nuevas tecnologías de Huawei Cloud lo antes posible ~