この記事は、Huawei クラウド コミュニティ「Huawei Cloud SMS Service で Perl を使用して Smgp プロトコルを実装する方法を教えます」(著者: Zhang Jian) から共有されたものです。
概要とプロトコルの概要
China Telecom Short Message Gateway Protocol (SMGP) は、SMS サービスを実装するために China Netcom によって開発された通信プロトコルで、ショート メッセージ ゲートウェイ (SMGW) とサービス プロバイダー (SP) の間で使用されます。ショート メッセージ ゲートウェイ (SMGW) とショート メッセージ ゲートウェイ (SMGW) の間の通信。
Perl は多くの Linux システムにデフォルトでインストールされている古いスクリプト言語です。たとえば、Ubuntu のバージョン 22.04 のベース イメージには Python さえありませんが、Perl の人気は明らかです。 Perl の IO::Async モジュールは、簡潔な非同期 IO プログラミング モデルを提供します。
SMGP プロトコルはクライアント/サーバー モデルで動作します。クライアント (携帯電話、アプリケーションなどの SMS アプリケーション) は、まず SMS ゲートウェイ (SMGW ショート メッセージ ゲートウェイ) との長い TCP 接続を確立し、CNGP コマンドを使用して SMGW と対話し、SMS メッセージを送受信します。 CNGP プロトコルでは、実装者は、さまざまなシナリオのパフォーマンス要件を満たすために、独自のニーズに応じて、応答を待たずに次のコマンドを同期的に送信できます。
タイミング図
接続に成功しました。テキスト メッセージを送信します
接続に成功しました。SMGW から SMS を受信しました
プロトコルフレームの概要
SMGPヘッダー
ヘッダーには次のフィールドが含まれており、そのサイズと長さはすべて 4 バイトです
- パケット長: ヘッダーとボディを含む PDU 全体の長さ。
- リクエスト ID : PDU のタイプを識別するために使用されます (ログイン、送信など)。
- Sequence Id : リクエストとレスポンスを照合するために使用されるシーケンス番号。
Perl を使用して SMGP プロトコル スタックで接続を確立する
§── Makefile.PL §── 例 │ └─ smgp_client_login_example.pl ━── ライブラリ └── SMGP §── BoundAtomic.pm §── Client.pm §── 定数.pm └── プロトコル.pm
例: サンプルコードを保存します
- smgp_client_login_example.pl: Smgp ログイン例を保存します
- BoundAtomic.pm: SequenceId の生成に使用される増分ツール クラス
- Client.pm: Smgp 定義。接続の確立、テキスト メッセージの送信など、Smgp サービスとの通信を担当します。
- Protocol.pm: PDU、コーデックなどを格納します。
sequence_id のインクリメントを実装する
sequence_id は 1 ~ 0x7FFFFFFFF の値です。
パッケージ Smgp::BoundAtomic; 厳密を使用します。 警告を使用します。 FATAL => 'all'; サブ新しい{ 私の ($class, %args) = @_; 私の $self = { 分 => $args{分}、 max => $args{max}, 値 => $args{min}、 }; $self と $class を祝福してください。 $self を返します。 } サブ増分 { 私 ($self) = @_; if ($self->{value} >= $self->{max}) { $self->{値} = $self->{min}; } それ以外 { $self->{値}++; } $self->{値}を返します; } サブ取得 { 私 ($self) = @_; $self->{値}を返します; } 1;
Perl で SMGP PDU とエンコードおよびデコード関数を定義する
パッケージ Smgp::プロトコル; 厳密を使用します。 警告を使用します。 FATAL => 'all'; Smgp::Constant を使用します。 サブ new_login { 私の ($class, %args) = @_; 私の $self = { clientId => $args{clientId}, AuthenticatorClient => $args{authenticatorClient}, ログインモード => $args{ログインモード}, タイムスタンプ => $args{タイムスタンプ}, バージョン => $args{バージョン}、 }; $self、$class を祝福して返します。 } サブエンコード_ログイン { 私 ($self) = @_; return Pack("A8A16CNC", @{$self}{qw(clientIdauthenticatorClientloginModetimeStampversion)}); } サブデコード_ログイン_レスプ { 私の ($class, $buffer) = @_; my ($status, $authenticatorServer, $version) = unpack("N4A16C", $buffer); 祝福を返してください { ステータス => $ステータス、 AuthenticatorServer => $authenticatorServer, バージョン => $version、 }、$class; } サブ new_header { 私の ($class, %args) = @_; 私の $self = { total_length => $args{total_length}, request_id => $args{request_id}, command_status => $args{command_status}, sequence_id => $args{sequence_id}, }; $self、$class を祝福して返します。 } サブエンコードヘッダー { 私の ($self, $total_length) = @_; return Pack("N3", $total_length, @{$self}{qw(request_id sequence_id)}); } サブ new_pdu { 私の ($class, %args) = @_; 私の $self = { ヘッダー => $args{ヘッダー}、 body => $args{body}, }; $self、$class を祝福して返します。 } サブエンコード_ログイン_pdu { 私 ($self) = @_; 私の $encoded_body = $self->{body}->encode_login(); $self->{header}->encode_header(length($encoded_body) + 12) を返します。 $encoded_body; } サブデコード_pdu { 私の ($class, $buffer) = @_; my ($request_id, $sequence_id) = unpack("N2", substr($buffer, 0, 8)); 私の $body_buffer = substr($buffer, 8); 私の $header = $class->new_header( total_length => 0、 request_id => $request_id, シーケンス ID => $シーケンス ID、 ); 私の$body; if ($request_id == Smgp::Constant::LOGIN_RESP_ID) { $body = $class->decode_login_resp($body_buffer); } それ以外 { die "サポートされていない request_id: $request_id"; } return $class->new_pdu( ヘッダー => $ヘッダー、 本体 => $本体、 ); } 1;
constant.pm には関連する requestId が格納されます
パッケージ Smgp::Constant; 厳密を使用します。 警告を使用します。 FATAL => 'all'; 定数 { を使用します 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、 クエリ ID => 0x00000007、 QUERY_RESP_ID => 0x80000007、 MT_ROUTE_UPDATE_ID => 0x00000008、 MT_ROUTE_UPDATE_RESP_ID => 0x80000008、 }; 1;
クライアントメソッドとログインメソッドを実装する
パッケージ Smgp::Client; 厳密を使用します。 警告を使用します。 FATAL => 'all'; IO::Socket::INET を使用します。 Smgp::プロトコルを使用します。 Smgp::Constant を使用します。 サブ新しい{ 私の ($class, %args) = @_; 私の $self = { host => $args{host} // 'localhost', port => $args{port} // 9000, ソケット => undef、 シーケンス ID => 1、 }; $self と $class を祝福してください。 $self を返します。 } サブ接続 { 私 ($self) = @_; $self->{ソケット} = IO::Socket::INET->new( PeerHost => $self->{host}、 PeerPort => $self->{port}、 プロト => 'tcp'、 ) または、「$self->{host}:$self->{port} $!」に接続できません。 } サブログイン { 私の ($self, $body) = @_; 私の $header = Smgp::Protocol->new_header( request_id => Smgp::Constant::LOGIN_ID, シーケンス ID => 1、 ); 私の $pdu = Smgp::Protocol->new_pdu( ヘッダー => $ヘッダー、 本体 => $本体、 ); $self->{ソケット}->send($pdu->encode_login_pdu()); $self->{socket}->recv(my $response_length_bytes, 4); 私の $total_length = unpack("N", $response_length_bytes); 私の$remain_length = $total_length - 4; $self->{socket}->recv(my $response_data, $remain_length); return Smgp::Protocol->decode_pdu($response_data)->{body}; } サブ切断 { 私 ($self) = @_; close($self->{socket}) if $self->{socket}; } 1;
例を実行して接続が成功したことを確認する
パッケージ smgp_client_login_example; 厳密を使用します。 警告を使用します。 FATAL => 'all'; Smgp::Client を使用します。 Smgp::プロトコルを使用します。 Smgp::Constant を使用します。 サブメイン{ 私の $client = Smgp::Client->new( ホスト => 'ローカルホスト'、 ポート => 9000、 ); $client->connect(); my $login = Smgp::Protocol->new_login( clientId => '12345678', 認証者クライアント => '1234567890123456', ログインモード => 1、 タイムスタンプ => time()、 バージョン => 0、 ); 私の $response = $client->login($login); if ($response->{ステータス} == 0) { print "ログイン成功!\n"; } それ以外 { print "ログインに失敗しました! ステータス: ", (定義 $response->{status} ? $response->{status} : '未定義'), "\n"; } $client->disconnect(); } main() 呼び出し元の場合を除きます。 1;
関連するオープンソース プロジェクト
- netty-codec-sms は、さまざまな SMS プロトコル (cmpp、sgip、smpp など) の netty コーデックを保存します。
- sms-client-java は、 さまざまな SMS プロトコルの Java クライアントを格納します
- sms-server-java は、 さまざまな SMS プロトコル用の Java サーバーを格納します
- cmpp-python cmppプロトコルのPython実装
- cngp-zig cmppプロトコルのPython実装
- smgp-perl smgp プロトコルの perl 実装
- smpp-rust smpp プロトコルの Rust 実装
要約する
この記事では SMGP プロトコルを簡単に紹介し、perl を使用してプロトコル スタックを実装しようとしていますが、実際の商用 SMS 送信は多くの場合より複雑で、フロー制御、オペレータの相互運用性、トランスポート層のセキュリティなどの問題に直面しています。 & SMS (メッセージ & SMS) サービスは、HTTP プロトコルを通じてアクセスされます. Huawei Cloud SMS サービスは、世界中の多くの高品質な通信事業者およびチャネルと協力して、Huawei Cloud が企業ユーザーに提供する通信サービスです。企業は、API を呼び出すかグループ送信アシスタントを使用することで、確認コードと通知 SMS サービスを利用できます。