This article is shared from the Huawei Cloud Community " Huawei Cloud SMS Service teaches you to use Perl to implement the Smgp protocol ", author: Zhang Jian.
Introduction & Protocol Overview
China Telecom Short Message Gateway Protocol (SMGP) is a communication protocol developed by China Netcom to implement SMS services. Its full name is Short Message Gateway Protocol. It is used between the Short Message Gateway (SMGW) and the Service Provider (SP). Communication between Short Message Gateway (SMGW) and Short Message Gateway (SMGW).
Perl is an old scripting language that is installed by default on many Linux systems. For example, in the base image of version 22.04 of Ubuntu, there is not even Python, but Perl is still installed. The popularity of Perl is evident. Perl's IO::Async module provides a concise asynchronous IO programming model.
The SMGP protocol works on a client/server model. The client (SMS application, such as mobile phone, application, etc.) first establishes a long TCP connection with the SMS Gateway (SMGW Short Message Gateway), and uses CNGP commands to interact with SMGW to send and receive SMS messages. In the CNGP protocol, the next command can be sent without waiting for a response synchronously. Implementers can implement two message transmission modes, synchronous and asynchronous, according to their own needs to meet performance requirements in different scenarios.
Timing diagram
Connection successful, send text message

Connection successful, SMS received from SMGW

Introduction to protocol frames
SMGP Header
Header contains the following fields, the size and length of which are all 4 bytes
- Packet Length : The length of the entire PDU, including Header and Body.
- Request ID : Used to identify the type of PDU (for example, Login, Submit, etc.).
- Sequence Id : Sequence number, used to match requests and responses.
Use perl to establish connections in the SMGP protocol stack
├── Makefile.PL ├── examples │ └── smgp_client_login_example.pl └── lib └── Smgp ├── BoundAtomic.pm ├── Client.pm ├── Constant.pm └── Protocol.pm
examples: stores sample code
- smgp_client_login_example.pl: stores Smgp login example
- BoundAtomic.pm: incremental tool class used to generate SequenceId
- Client.pm: Smgp definition, responsible for communicating with Smgp services, such as establishing connections, sending text messages, etc.
- Protocol.pm: stores PDU, codec, etc.
Implement sequence_id increment
sequence_id is a value from 1 to 0x7FFFFFFFF
package Smgp::BoundAtomic; use strict; use warnings FATAL => 'all'; sub new { my ($class, %args) = @_; my $self = { min => $args{min}, max => $args{max}, value => $args{min}, }; bless $self, $class; return $self; } sub increment { my ($self) = @_; if ($self->{value} >= $self->{max}) { $self->{value} = $self->{min}; } else { $self->{value}++; } return $self->{value}; } sub get { my ($self) = @_; return $self->{value}; } 1;
Define SMGP PDU and encoding and decoding functions in Perl
package Smgp::Protocol; use strict; use warnings FATAL => 'all'; use Smgp::Constant; sub new_login { my ($class, %args) = @_; my $self = { clientId => $args{clientId}, authenticatorClient => $args{authenticatorClient}, loginMode => $args{loginMode}, timeStamp => $args{timeStamp}, version => $args{version}, }; return bless $self, $class; } sub encode_login { my ($self) = @_; return pack("A8A16CNC", @{$self}{qw(clientId authenticatorClient loginMode timeStamp version)}); } sub decode_login_resp { my ($class, $buffer) = @_; my ($status, $authenticatorServer, $version) = unpack("N4A16C", $buffer); return bless { status => $status, authenticatorServer => $authenticatorServer, version => $version, }, $class; } sub new_header { my ($class, %args) = @_; my $self = { total_length => $args{total_length}, request_id => $args{request_id}, command_status => $args{command_status}, sequence_id => $args{sequence_id}, }; return bless $self, $class; } sub encode_header { my ($self, $total_length) = @_; return pack("N3", $total_length, @{$self}{qw(request_id sequence_id)}); } sub new_pdu { my ($class, %args) = @_; my $self = { header => $args{header}, body => $args{body}, }; return bless $self, $class; } sub encode_login_pdu { my ($self) = @_; my $encoded_body = $self->{body}->encode_login(); return $self->{header}->encode_header(length($encoded_body) + 12) . $encoded_body; } sub decode_pdu { my ($class, $buffer) = @_; my ($request_id, $sequence_id) = unpack("N2", substr($buffer, 0, 8)); my $body_buffer = substr($buffer, 8); my $header = $class->new_header( total_length => 0, request_id => $request_id, sequence_id => $sequence_id, ); my $body; if ($request_id == Smgp::Constant::LOGIN_RESP_ID) { $body = $class->decode_login_resp($body_buffer); } else { die "Unsupported request_id: $request_id"; } return $class->new_pdu( header => $header, body => $body, ); } 1;
constant.pm stores related requestId
package Smgp::Constant; use strict; use warnings FATAL => 'all'; use constant { LOGIN_ID => 0x00000001, LOGIN_RESP_ID => 0x80000001, SUBMIT_ID => 0x00000002, SUBMIT_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;
Implement client and login methods
package Smgp::Client; use strict; use warnings FATAL => 'all'; use IO::Socket::INET; use Smgp::Protocol; use Smgp::Constant; sub new { my ($class, %args) = @_; my $self = { host => $args{host} // 'localhost', port => $args{port} // 9000, socket => undef, sequence_id => 1, }; bless $self, $class; return $self; } sub connect { my ($self) = @_; $self->{socket} = IO::Socket::INET->new( PeerHost => $self->{host}, PeerPort => $self->{port}, Proto => 'tcp', ) or die "Cannot connect to $self->{host}:$self->{port} $!"; } sub login { my ($self, $body) = @_; my $header = Smgp::Protocol->new_header( request_id => Smgp::Constant::LOGIN_ID, sequence_id => 1, ); my $pdu = Smgp::Protocol->new_pdu( header => $header, body => $body, ); $self->{socket}->send($pdu->encode_login_pdu()); $self->{socket}->recv(my $response_length_bytes, 4); my $total_length = unpack("N", $response_length_bytes); my $remain_length = $total_length - 4; $self->{socket}->recv(my $response_data, $remain_length); return Smgp::Protocol->decode_pdu($response_data)->{body}; } sub disconnect { my ($self) = @_; close($self->{socket}) if $self->{socket}; } 1;
Run example to verify the connection is successful
package smgp_client_login_example; use strict; use warnings FATAL => 'all'; use Smgp::Client; use Smgp::Protocol; use Smgp::Constant; sub main { my $client = Smgp::Client->new( host => 'localhost', port => 9000, ); $client->connect(); my $login = Smgp::Protocol->new_login( clientId => '12345678', authenticatorClient => '1234567890123456', loginMode => 1, timeStamp => time(), version => 0, ); my $response = $client->login($login); if ($response->{status} == 0) { print "Login successful!\n"; } else { print "Login failed! Status: ", (defined $response->{status} ? $response->{status} : 'undefined'), "\n"; } $client->disconnect(); } main() unless caller; 1;
Related open source projects
- netty-codec-sms stores netty codecs for various SMS protocols (such as cmpp, sgip, smpp)
- sms-client-java stores Java clients of various SMS protocols
- sms-server-java stores Java servers for various SMS protocols
- cmpp-python python implementation of cmpp protocol
- python implementation of cngp-zig cmpp protocol
- smgp-perl perl implementation of smgp protocol
- smpp-rust rust implementation of smpp protocol
Summarize
This article briefly introduces the SMGP protocol and attempts to use perl to implement the protocol stack. However, actual commercial SMS sending is often more complicated and faces issues such as flow control, operator interoperability, and transport layer security. You can choose Huawei Cloud Message & SMS (Message & SMS) service is accessed through HTTP protocol . Huawei Cloud SMS service is a communication service provided by Huawei Cloud to enterprise users in cooperation with many high-quality operators and channels around the world. Enterprises can use verification code and notification SMS services by calling API or using group sending assistant.
Click to follow and learn about Huawei Cloud’s new technologies as soon as possible~