php - 如何构建串联的短信 pdu?获取垃圾字符

标签 php sms telnet pdu

我正在尝试构建一些 PHP 代码以通过 telnet 将 SMS 发送到 SIM 服务器,但我在发送串联消息时遇到了问题。

我读过一些关于使用填充位将编码的消息七位字节变成八位字节的内容,但我不完全理解它是如何工作的。

我有一个类接收电话号码、消息(已拆分为最多 153 个字符)、短信总数和文本当前部分的顺序号。

只要我在$hexmessage 之前添加'20',它就可以工作。但是我在第一部分的开头得到了一个垃圾字符(在我的消息的第一个字母之前),并且同样的垃圾字符替换了第二部分的第一个字母! (使用'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);
}

}
?>

最佳答案

如您所知,创建串联的 SMS 消息需要您在文本消息之前添加 UDH。 UDH 成为您的有效负载的一部分,从而减少了每个段可以发送的字符数。

由于它已成为您的有效负载的一部分,因此需要确认您的有效负载数据要求 - 这是 7 位。然而,UDH 是 8 位的,这显然使事情变得复杂。

考虑以下的 UDH:

050003000302
  • 05是UDH的长度
  • 00 是 IEI
  • 03 是 IEDL(另外 3 个八位字节)
  • 00 是一个引用(这个数字在您的每个串联消息 UDH 中必须相同)
  • 03是消息的最大数量
  • 02为当前留言号。

总共有 6 个八位字节 - 等于 48 位。这一切都很好,但由于 UDH 实际上是您的 SMS 消息的一部分,您需要做的是添加更多位,以便实际消息从七位字节边界开始。 septet 边界是每 7 位,所以在这种情况下,我们必须再添加 1 位数据以使 UDH 成为 49 位,然后我们可以添加我们的标准 GSM-7 编码字符。

您可以从 Here 阅读更多相关信息

关于php - 如何构建串联的短信 pdu?获取垃圾字符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12298558/

相关文章:

php - 在 Symfony 的控制台命令中使用事件监听器

php - 使用 php 和 mysql 将多个复选框值存储到数据库

sms - 如何在 SMPP 中正确表示消息类别

java - 如何通过 onreceive() 函数发送位置(经度和纬度)?

ios - 不能 telnet gateway.sandbox.push.apple.com 2195?

vba - 从 VBA 运行 Telnet session

php - 尝试在两个不同数据库中的两个表之间找到至少一个相似性

php - 如何使用来自 php 的一些参数运行 perl 脚本

java - 无法处理收到的短信

.net - 在 C# 中使用 TcpClient 的 HTTP 客户端真的很奇怪