- 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;
- 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;
- 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;
- 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;
- 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;
本文简单对SMGP协议进行了介绍,并尝试用perl实现协议栈,但实际商用发送短信往往更加复杂,面临诸如流控、运营商对接、传输层安全等问题,可以选择华为云消息&短信(Message & SMS)服务通过HTTP协议接入,华为云短信服务是华为云携手全球多家优质运营商和渠道,为企业用户提供的通信服务。企业调用API或使用群发助手,即可使用验证码、通知短信服务。