Как собрать составные смс пду? Получение мусорных символов
Я пытаюсь создать PHP-код для отправки SMS через telnet на SIM-сервер, но у меня возникают проблемы с отправкой составных сообщений.
Я читал кое-что об использовании битов заполнения, чтобы превратить кодированные септеты сообщений в октеты, но я не до конца понимаю, как это работает.
У меня есть класс, который получает номер телефона, сообщение (уже разделенное максимум на 153 символа), общее количество SMS-сообщений и порядковый номер текущей части текста.
Это работает до тех пор, пока у меня добавлено "20" до $hexmessage. Но я получаю мусорный символ в начале первой части (до 1-й буквы моего сообщения), и тот же мусорный символ заменяет первую букву второй части! (используя "20", чтобы он показывал пустое пространство, но отображал треугольник)
Я не могу понять, почему или что я должен был изменить, чтобы это работало должным образом.
Я надеюсь, что кто-то может помочь мне понять, что я делаю неправильно.
Вот что у меня так далеко:
<?php
// Generate PDU string
public function generatePDUm($receiverNumber,$message, $sms_count, $msg_nr) {
//Append filler digit if number is national
if( strlen($receiverNumber)==9){
$nacional=1;
$receiverNumber = $receiverNumber."F";
$network = substr($receiverNumber, 0, 2); //NETWORK code, used to decide the SIM Card to be used
//Check for international flags and set the number type accordingly
}else{
$nacional=0;
if(substr($receiverNumber, 0, 1)=='+'){
$network = substr($receiverNumber, 4, 2); //NETWORK code, used to decide the SIM Card to be used
$receiverNumber = substr($receiverNumber, 1, 12); //remove international indicator
}
else if(substr($receiverNumber, 0, 2)== '00'){
$network = substr($receiverNumber, 5, 2); //NETWORK code, used to decide the SIM Card to be used
$receiverNumber = substr($receiverNumber, 2, 12); //remove international indicator
}
}
/* Flag the network to be used */
switch ($network){
case "92":
$network="TMN";
break;
case "96":
$network="TMN";
break;
case "91":
$network="VODAFONE";
break;
case "93":
$network="OPTIMUS";
break;
}
// Receiver number must be 10 characters long ('national nr' + filler digit) or less than 13 ('351'+'national nr'). (Portugal)
if( strlen($receiverNumber) < 10 || strlen($receiverNumber) > 12) {
// Error, not 10 or over 12 numbers long (Code 1)
$this->setErrorCode(1);
return false;
}
// Message must be 2 characters long at least
if( strlen($message) < 2 ) {
// Error, message too short (Code 2)
$this->setErrorCode(2);
return false;
}
// Message can't be longer than 153 characters. 3SMS.
if( strlen($message) > 153 ) {
// Error, message too long (Code 3)
$this->setErrorCode(3);
return false;
}
// Length of servicecenter number (00 = automatically fixed by phone)
$serviceCenterNumberLength = '00';
// SMS-? : 04=sms-deliver(recieve), 11=sms-submit, 01 = dont know but it works, 41 = SMS-SUBMIT + UDH bit (for extended/concatenated SMS)
// You can try to change this if your phone does not work with 01 command try to use 11 command
$smsType = '41';
// TP Message Reference: (placeholder), let the phone set the message reference number itself
$messageRef = '00';
// Number length. If national -> 9, if international -> 12
if($nacional==1){
$numberLength = '09';
}else{
$numberLength = '0C';
}
// Type of phone adress: (81=Unknown=10dec, 91=InternationalFormat, 92=National?)
if($nacional==1){
$numberType = '81';
}else{
$numberType = '91';
}
// Get the PDU version of the number
$number = $this->getNumberAsPDU( $receiverNumber );
// TP-PID (Protocol Identifier)
$protocolId = '00';
// TP-DCS (Data coding scheme)
$dataCodingScheme = '00';
// TP-Validity-Period (timestamp), AA=4days expiry, disabled for SonyEricsson support.
// $validityPeriod = 'A0';
// $validityPeriod = 'AA'; // Add this if the PDU command fails
/*user data header information (05 - User Data header info length
* 00 - Information element identifier for a concatenated short message
* 03 - Information element data length
* 00 - Reference number, auto
* 0.$sms_count - total SMS nr
* 0.$msg_nr - current SMS order */
$udhi = '050003000'.$sms_count.'0'.$msg_nr;
// echo 'UDHinfo: '.$udhi."\n";
// Data length of message (in hex format)
$dataLength = $this->strToHexLen($message);
// echo 'DATA LENGHT: '.$dataLength."\n\n";
// Convert message, string > 7bits > 8bits > hex
$hexMessage = $this->bit7tohex( $this->strto7bit( $message ) );
// Create the complete PDU string
$pdu = $serviceCenterNumberLength . $smsType . $messageRef . $numberLength .
$numberType . $number . $protocolId . $dataCodingScheme . $dataLength .
$udhi . '20' .$hexMessage;
/*
* Generate the length of var $pdu (pdu/2 minus 1) as pdu format requests
* The -1 is because we don't count the first two characters '00', needed for this command: 'cmgs=24'
*/
$cmgslen = strlen($pdu)/2-1;
// Build data array to return with required information
$data = array();
$data['pdu'] = $pdu;
$data['cmgslen'] = $cmgslen;
$data['rede'] = $network;
// Return the data array with PDU information
return $data;
}
// Generate PDU formatted cellphone number
private function getNumberAsPDU($number) {
// Length of number divided by 2 handle two characters each time
$length = strlen( $number )/2;
// Set counter to 1 for strlen
$i = 1;
$pduNumber = '';
// Loop to handle every 2 characters of the phone number. 06 12 34 56 78
while ($i <= $length) {
// Get 2 characters of the complete string depending on the number of the current loop.
// Then reverse these 2 characters and put them in var $pduNumber (06 = 60)
$pduNumber .= strrev( substr( $number,$i*2-2,2) );
// Counter + 1
$i++;
}
// Return the generated number
return $pduNumber;
}
/* Function to convert ascii character to 8 bits
* Much more efficient than holding a complete ASCII table
* Thanks to Mattijs F.
*/
private function asc2bin($input, $length=8) {
$bin_out = '';
// Loop through every character in the string
for($charCount=0; $charCount < strlen($input); $charCount++) {
$charAscii = ord($input{$charCount}); // ascii value of character
$charBinary = decbin($charAscii); // decimal to binary
$charBinary = str_pad($charBinary, $length, '0', STR_PAD_LEFT);
$bin_out .= $charBinary;
}
// Return complete generated string
return $bin_out;
}
// String to 7 bits array
private function strto7bit($message) {
$message = trim($message);
$length = strlen( $message );
$i = 1;
$bitArray = array();
// Loop through every character in the string
while ($i <= $length) {
// Convert this character to a 7 bits value and insert it into the array
$bitArray[] = $this->asc2bin( substr( $message ,$i-1,1) ,7);
$i++;
}
// Return array containing 7 bits values
return $bitArray;
}
// Convert 8 bits binary string to hex values (like F2)
private function bit8tohex($bin, $padding=false, $uppercase=true) {
$hex = '';
// Last item for counter (for-loop)
$last = strlen($bin)-1;
// Loop for every item
for($i=0; $i<=$last; $i++) {
$hex += $bin[$last-$i] * pow(2,$i);
}
// Convert from decimal to hexadecimal
$hex = dechex($hex);
// Add a 0 (zero) if there is only 1 value returned, like 'F'
if($padding && strlen($hex) < 2 ) {
$hex = '0'.$hex;
}
// If we want the output returned as UPPERCASE do this
if($uppercase) {
$hex = strtoupper($hex);
}
// Return the hexadecimal value
return $hex;
}
// Convert 7 bits binary to hex, 7 bits > 8 bits > hex
private function bit7tohex($bits) {
$i = 0;
$hexOutput = '';
$running = true;
// For every 7 bits character array item
while($running) {
if(count($bits)==$i+1) {
$running = false;
}
$value = $bits[$i];
if($value=='') {
$i++;
continue;
}
// Convert the 7 bits value to the 8 bits value
// Merge a part of the next array element and a part of the current one
// Default: new value is current value
$new = $value;
if(key_exists(($i+1), $bits)) {
// There is a next array item so make it 8 bits
$neededChar = 8 - strlen($value);
// Get the char;s from the next array item
$charFromNext = substr($bits[$i+1], -$neededChar);
// Remove used bit's from next array item
$bits[$i+1] = substr($bits[$i+1], 0, strlen($bits[$i+1])-$neededChar );
// New value is characters from next value and current value
$new = $charFromNext.$value;
}
if($new!='') {
// Always make 8 bits
$new = str_pad($new, 8, '0', STR_PAD_LEFT);
// The 8 bits to hex conversion
$hexOutput .= $this->bit8tohex($new, true);
}
$i++;
}
// Return the 7bits->8bits->hexadecimal generated value
return $hexOutput;
}
// String to length in Hex, String > StringLength > Hex
private function strToHexLen($message) {
// Length of the string (message)
$length = strlen( $message )+7; //+7 for UDH. the UDH is a total of (number of octets x bit size of octets) 6 x 8 = 48 bits long. Therefore a single bit of padding has to be prepended to the message. The UDH is therefore (bits for UDH / bits per septet) = (48 + 1)/7 = 7 septets in length.
// Hex value of this string length
$hex = dechex($length);
// Length of the hex value
$hexLength = strlen($hex);
// If the hex strng length is lower dan 2
if($hexLength < 2) {
// Add a 0 (zero) before it
$hex = '0'.$hex;
}
// Return the hex value in UPPERCASE characters
return strtoupper($hex);
}
}
?>
2 ответа
Как вы уже знаете, создание составных SMS-сообщений требует добавления UDH перед вашим текстовым сообщением. UDH становится частью вашей полезной нагрузки, тем самым сокращая количество символов, которое вы можете отправить на сегмент.
Поскольку это стало частью вашей полезной нагрузки, оно должно быть подтверждено вашими требованиями к данным полезной нагрузки - которые являются 7-битными. UDH, однако, является 8-битным, что явно усложняет ситуацию.
Рассмотрим UDH следующего:
050003000302
- 05 длина UDH
- 00 - это ИЭИ
- 03 - это IEDL (еще 3 октета)
- 00 является ссылкой (этот номер должен быть одинаковым в каждом из ваших составных сообщений UDH)
- 03 максимальное количество сообщений
- 02 номер текущего сообщения.
Это всего 6 октетов, что соответствует 48 битам. Это все и хорошо, но поскольку UDH на самом деле является частью вашего SMS-сообщения, вам нужно добавить больше битов, чтобы фактическое сообщение начиналось с границы септета. Граница септета - каждые 7 битов, поэтому в этом случае нам потребуется добавить еще 1 бит данных, чтобы получить 49 битов UDH, а затем мы можем добавить наши стандартные символы в кодировке GSM-7.
Вы можете прочитать больше об этом здесь
Эти вопросы повсюду, и, кажется, никто не может ответить на них так, чтобы это имело смысл. Нулевое дополнение обычно только усугубляет ситуацию. Я думаю, что самый простой способ обойти этот недостаток дизайна в стандарте GMS - это использовать 8-битную кодировку или 16-битную UCS2, даже если она означает меньше символов. Таким образом, вам не нужно заботиться о разнице в байтовых границах, поэтому создание сложных СМС так сложно.