Отправка SMS-сообщений в формате PDU, теория с примерами на C#, часть 2

Во второй части перейдем от теории, озвученной в части 1, к практике, а именно, приведу примеры основных функций, необходимых для формирования SMS-сообщения в PDU-формате. Функции написаны на языке C#. Все примеры реально работающие – они выдраны из моей программы. Итак приступим.

1. Преобразование номеров

В соответствии с теорией, номер получателя и номер SMS-центра (если он используется) должны быть закодированы извращенским способом. Вот простая функция, которая делает это:

encodephonenumber

Пусть у нас есть пара строковых переменных, содержащих номера в международном формате:

SMSCenterPhoneNumber– содержит номер SMS-центра, например “+79107899999”

RecipientPhoneNumber – содержит номер получателя, например “+79123456789”

Приготовим их к виду, необходимому для PDU-формата, начнем с номера SMS-центра:

sca

Для вычисления длины номера SMS-центра применена ((smscnum.Length / 2) + 1).ToString («X2»), т.к. здесь длина вычисляется по-другому, если вы помните теорию: количество БАЙТ номера, +1 байт – формат номера (“91”). Если брать номер +79107899999, то соответственно, после преобразования, длина его будет 12 символов (9701879999F9), следовательно 6 байт. Для этого мы делим длину строки на 2. И добавляем международный формат, это “91”, но тут 2 символа, а мы считаем в байтах, соответственно делаем +1. Далее добавляется уже непосредственно сам перевернутый номер. Параметр “X2” в методе ToString () – это преобразование в шестнадцатеричный вид, с лидирующем нулем, т.е. длина 7 будет выглядеть как “07”.

Теперь номер получателя. С ним похоже, но попроще:

recipient

Конструкция “RecipientPhoneNumber.Length.ToString («X2»)” возвращает длину номера получателя в шестнадцатеричном виде. Нам нужно именно количество цифр в номере без “+” в начале и “F” в конце. Код международного формата номера (“91”) в длине номера получателя не учитывается.

2. Преобразование текста

Номера подготовили. Теперь надо подготовить сам текст сообщения. А он у нас может быть в двух вариантах: либо в кодировке USC2 (аналоге Unicode, для русского языка), либо в 7-битной ASCII-кодировке (для латиницы). Для этого можно применить следующие функции:

Формат UCS2 (unicode)

stringtoucs2

Тут все просто, вызываем функцию с текстом сообщения, она возвращает нам его в UCS2-формате:

string ucs2Text = StringToUCS2 (messageText);

Формат ASCII (7-битная кодировка)

string7to8

Тут все сложнее, но комментарии, думаю, достаточно ясно объясняют алгоритм. Вызов функции тоже, думаю, не составит труда. Главное, что в результате мы будем иметь перекодированную в 8-битный формат 7-битную ASCII-последовательность.

Всё

Вот, в принципе, и все, что я хотел сказать. Важные моменты вроде бы раскрыл, с остальным справится и новичок. Может, процедуры написаны криво, но это мой второй опыт работы с C#, так что, думаю, простительно. Данные функции должны помочь тому, кто захочет сотворить что-то подобное. Если что-то не ясно, либо чего-то не достаточно, могу пояснить еще.

В общем, комментарии и вопросы приветствуются.

UPD:

Продолжение: Отправка длинных SMS-сообщений в формате PDU

Поделиться с друзьями:
  • Добавить ВКонтакте заметку об этой странице
  • Одноклассники
  • Facebook
  • В закладки Google
  • Мой Мир
  • Twitter
  • LiveJournal
  • Яндекс.Закладки
  • LinkedIn
  • Reddit
  • StumbleUpon
  • БобрДобр
  • Memori.ru
  • МоёМесто.ru

Comments

  1. StarXXX, прога замечательная, я довольно долго (2 нед.) пишу аналогичную. НО у меня проблема, я надеюсь Вы поможете ее решить: Так же как и Вы использую io.ports определяю COM порт, но на этапе посылки ЛЮБОЙ команды на порт компилятор кричит типо «Время ожидания истекло». Вот кусок кода:

    SerialPort port = new SerialPort ();

    port.PortName = «COM5»;

    … прописываю настройки

    port.Open ();

    //ну для примера пытаюсь позвонить

    port.WriteLine ("ATD+70000000000;”)// Время ожидания истекло

    Помогите пожалуйста не могу разобраться. Информация по инициализации com порта брал от сюда www.akhmed.ru/post/2007/1...rtWithMobil.aspx

  2. Я сам пользовался той статьей при инициализации порта, у меня все нормально работает... Особенно рекомендую обратить внимание на комментарии в той статье.

    Порт указан правильно? (У меня на windows 7 если модем воткнуть в другой USB-порт, то номер com-порта тоже меняется...)

    А такие строки пробовали добавлять:

    port.WriteTimeout = 500;

    port.ReadTimeout = 500;

    ?

  3. Конечно, перепробовал ВСЕ возможные параметры и несколько телефонов, порт правильно определяю, так как предварительно смотрю в диспетчере устройств. Я был бы очень признателен Вам, если бы скинули мне часть кода своей программы, а точнее то место где происходит выбор модема и установка параметров.

    Мой контакт svonidze@mail.ru

  4. Решил проблему, т.е. решил методом перебора, в Диспечере устройств мой Нокиа подключен через COM3, но если это писать в имени порта port.PortName = «COM3»; то дальше вываливает исключение, а если я поставлю port.PortName = «COM4»; то все норм!!! Неведомая вещь)) но я все равно очень бы хотел взгянуть на Ваш код

    1. Обычно сотовый телефон при подключении занимает 2 а то и 3 com-порта. Видимо модемный осел у Ввас на com4. В своей проге я получал список всех модемов через WMI.

      А код коннекта у меня почти такой же как и у Вас. На всякий случай кинул его Вам почтой.

      1. Список портов я получил SerialPort.GetPortNames (); но там ясно что храниться COM1, COM2... Хотелось бы узнать как у Вас получилось узнать действительно имя модема(в моем случае «Nokia 7500...»), и все остальные свойста.

        Можно ли от Вас получил код где получен список всех модемов через WMI, эта область для меня еще не исследована, ели да то вышлите на тот же адресс, буду очень признателен

        p.s. курсач на этом завязан

  5. Неплохо было бы предусмотреть возможность прямого указания COM-порта, а то у меня телефон Siemens M65 в списке этой программки не вывелся, хотя в диспетчере устройств он виден как модем и я точно знаю на какаом порте он сидит.

    Сейчас как раз пишу dll-ку для MsterSCADA, тоже на С#.

    Очень полезная статья по PDU-режим, теперь все понятно стало, спасибо!!!

    Действительно, какие только шизоиды придумали все это!

  6. Вопрос совсем не в тему, но как отправить личное сообщение так и не нашёл ...

    Я пытаюсь отладить работу протокола SAP (SIM Acess Profile) на телефоне при подключении через Bluetooth, а именно передачу SMS с SIM карты по этому протоколу. Проблемма заключается в том что для отладки мне нужена программа-клиент, которая бы с компа через bluetooth dongle подключилась к телефону по этому протоколу и могла бы вытащить контакты и SMS-ки по SAP протоколу (что-то типа эмулятора carkit). Если автор может что-нибудь посоветовать по этому поводу, буду весьма благодарен.

    1. К сожалению, здесь я помочь ничем не смогу, ибо про протокол SAP впервые услышал от Вас. Однако, на сколько я помню то, что читал, AT-командами можно получить доступ к записной книжке без проблем

  7. Огромное спасибо за столь подробное описание PDU!

    У меня остался один вопрос:

    Как кодировать символы переноса строки?

    Еще у меня в телефоне есть какой-то символ, похожий на знак абзаца, что-то вроде «мягкого переноса», чтоли. Его тоже как-то можно закодировать?

    1. Что имеется ввиду под «переносом» строки? Стандартная последовательность перевода строки — это комбинация символов «возврат каретки» и «Перевод строки». Их коды соответственно 0Dh и 0Ah, тоесть 13 и 10 в десятичном счислении. Если именно это имеется ввиду — попробуйте либо эту комбинацию, либо отдельно каждый из них. Но обычно это именно «0D0A»...

      Возможно ваш «мягкий» перенос — это отдельно «0A» или «0D», попробуйте.

      1. Т.е. Вы хотите сказать, что если в исходном однобайтовом ANSI-послании будет идти последовательность OD0A, то в конечном послании в формате UCS2 это будет последовательность 000D000A ? Или в Unicode эти сиволы кодируются как-то иначе? (в этом именно и была суть моего вопроса).

        1. Да, так и есть. Анализ Unicode-файлов показывает, что 0D и 0A дополняются нулем. Как и любые другие символы с кодом меньше 127. Тоесть правильная комбинация будет 000D000A

  8. Ваша функция преобразует текст будущего смс в 7-битную строку (на входе — текст, на выходе — 7-битная ASCII-последовательность), а поставленная мне задача предполагает обратное преобразование(на входе — 7-битная ASCII-последовательность, на выходе — текст)

    1. Ну эта задача еще проще, чем кодирование... Бери каждые 7 бит — вот тебе и символ... Что тут писать? У меня свободного времени не хватает на такое, но уж если совсем не получится — помогу...

      1. Здравствуйте! Сейчас занимаюсь разработкой похожей програмки, только на JAVA.Модем тот же самый.

        Хотел пару вопросов спросить:-как вы передавали АТ-команды на COM-порт, через какой-нибудь bat-файл или как-то еще?

        -сможете скинуть исходный код проги на почту studentsapra@yandex.ru ?

        1. Причем тут bat-файл? Открываешь com-порт на котором сидит модем и шлешь ему последовательность символов «AT»... и так далее. Судя по всему, Вы понятия не имеете, как работать с модемом? Тут я Вам тоже не помощник, ибо на Java не пишу. Ищите, как на java открыть com-порт и слать в него данные, тут мои исходники Вам вряд ли чем-нибудь помогут, тем более, что примеры выше я выдирал именно из них, а все, что осталось — как раз и есть отсылка данных в порт.

        2. для java я начинал отсюда

          disserman.blogspot.com/2007/11/sms.html

          исходник там, вообщем у меня сразу не пошел.

          необходимы библиотеки я брал RXTX.

          раскурил пока до 70 символов на кирилице, модем mc35

          отправляет с севера все ok пока!

          Сл. задача — длинные тексты.

  9. Создал отправку смс больше 70 символов использ. pdu и UCS2 на java. 200 — 300 символов в сообщении смс, работало на ура, поэтому загнал в модем строку около 900 может больше символов в кирилице и отправил на тестовый телефон, появилась проблема. Телефон перестал получать длинные смс вообще. Короткие до 70 символов получает, а длинные нет. Если отправить длинную смс до 350 симв. на другой телефон не тестовый, то она приходит, а на тестовый не желает, хотя программа рапортует везде OK. Как ето понять и второе существует ли ограничение на длину длинных смс ведь по протоколу более 6000 симв. выходит так ли это?

    Помогите, плз, откройте глаза слепому!

    1. Видимо, Ваш тестовый телефон придется вернуть к заводским настройкакм или перепрошить — крыша видать у него от такого сообщения поехала.

      Вообще, производители иногда протоколы не полностью поддерживают, посему, на 6000 символов я бы не рассчитывал

  10. Привет!

    Спасибо огромное за интересную статью!

    В битах-байтах не сильна, поэтому запуталась, пытаясь написать функцию, обратную String7To8. Если есть возможность, помогите. Заранее спасибо!

    1. Ну это самое простое.

      Загоняете всё в двоичную строку и режете её по 7 символов. Ез получившихся наборов получаете код символа и формируете текстовую строку.

      Вот пара функций на VB

      Function Dec2Bin ( _

      ByVal DecimalIn As Variant _

      , Optional NumberOfBits As Variant _

      ) As String

      'Decimal To Binary

      ' www.devhut.net/2010/06/22...imal-and-binary/

      ' =================

      ' Source: groups.google.ca/group/co...979c5e918fad7e63

      ' Author: Randy Birch (MVP Visual Basic)

      ' NOTE: You can limit the size of the returned answer by specifying the number of bits

      Dec2Bin = ""

      DecimalIn = Int (CDec (DecimalIn))

      Do While DecimalIn 0

      Dec2Bin = Format$(DecimalIn — 2 * Int (DecimalIn / 2)) & Dec2Bin

      DecimalIn = Int (DecimalIn / 2)

      Loop

      If Not IsMissing (NumberOfBits) Then

      If Len (Dec2Bin) > NumberOfBits Then

      Dec2Bin = «Error — Number exceeds specified bit size»

      Else

      Dec2Bin = Right$(String$(NumberOfBits, _

      «0») & Dec2Bin, NumberOfBits)

      End If

      End If

      End Function

      Function Bin2Dec ( _

      BinaryString As String _

      ) As Variant

      'Binary To Decimal

      ' www.devhut.net/2010/06/22...imal-and-binary/

      ' =================

      Dim X As Integer

      For X = 0 To Len (BinaryString) — 1

      Bin2Dec = CDec (Bin2Dec) + Val (Mid (BinaryString, _

      Len (BinaryString) — X, 1)) * 2 ^ X

      Next

      End Function

  11. Автор не только хороший программист, но и отлично подаёт материал — всё ясно, логично и понятно. Читать и понимать одно удовольствие. И за программку спасибо — прекрасное наглядное пособие, как надо делать просто и эффективно.

    Желаю удачи!

  12. Автору респект. Материал отличный! Многое почерпнул отсюда.

    Вот только есть косяк в функции String7To8 в условии последнего цикла. Функция работает не корректно если длина исходной строки

    (n*8-1), т.е. 7,15,23... , при такой длине последний байт результирующей строки будет нулевым, и отбрасывать его никак нельзя.

    Поэтому условие нужно исправить на:

    (i < arr.Length — arr.Length / 8)

    или сразу так:

    (i < arr.Length * 0.875).

    Обнаружил когда пытался отправить на модем *100*1#

    Функция выдавала AA180CA68A8D, вместо AA180CA68A8D00.

  13. уточнение: «при такой длине последний байт результирующей строки будет нулевым»

    читай «при такой длине последний байт результирующей строки может быть нулевым»

  14. Сергей, проверял на своих модемах. Отлично отправляются и 7 и 15 и 23 символа и именно с моим вариантом функции. Никаких ошибок, СМС приходят без проблем. *100*1# так же нормально отправилось и пришло.

    Честно говоря, не вижу смысла отправлять лишние нули. Хотя, конечно, Ваша версия имеет право на жизнь.

    не могли бы Вы сказать, что за модем Вы использовали?

  15. Доброго времени суток !

    Скажите, можно ли раскодировать сообщение в формате PDU состоящее из трех сообщений и на кириллице ?

    1. Ну естественно можно, раскодирование — процесс обратный кодированию, сложного вроде ничего нету... А если серьезно — то PDU — это не шифрование, процесс кодирования описан, значит можно поступить наоборот, чтобы его раскодировать...

  16. При использовании программы возникла проблема, помогите, пожалуйста. Первые штук 10 смс отправились хорошо. Потом был около 20 часов прога ничего не отправляла, но продолжала оставаться запущенной. И вот после этого при попытке отправить СМС, произошла ошибка:

    Command: AT+CMGF=1

    Error: True

    Modem reply:

    ----------

    Произошла ошибка при приеме ответа на команду AT+CMGF=1

    До этого с отправкой текстовых сообщений не было проблем. Такое впечатление, что то ли порт, то ли модем отключились... Можно ли это как-то проверить и переподключиться?

    1. Программа подключается к модему только в момент отправки сообщения. Все остальное время порт свободен и модем не занят.

      Я встречал такое на некоторых модемах — они со временем просто отваливались от системы. Непонятно почему. Выдернешь его, вставишь назад — все снова работает. Не знаю, в чем причина, мне кажется — в модеме. Потому что на других моделях модемов программа работала месяцами без проблем.

      Сейчас я рассматриваю вариант переделать ее, чтобы подключение к порту было постоянным, а не только во время отправки. Может это поможет решить проблему ...

  17. Интересно будет посмотреть на этот вариант. Но я вспомнил, что как раз модифицировал вашу программу для экспериментов, и порт у меня открывается 1 раз. Может быть, выход как раз в том, чтобы всякий раз открывать порт заново? Но судя по вашему постингу дело не в открытии порта, а в самом модеме... В принципе, есть вариант делать модему Disable/Enable программно, как бы эмулируя «выткнул/воткнул»...

    1. Есть еще один вариант, для вашей переделки (при постоянно открытом порте): например с определенным интервалом посылать модему простую команду «АТ» и принимать от него «ОК», чтобы не дать ему заснуть/отвалиться.

      Попробуйте так же вернуть мой вариант, т.е. открывать порт непосредственно перед передачей и закрывать его восле. Может это тоже сработает.

  18. При формировании TR DA по-моему в коде ошибка в строке

    string recipient = RecipientPhoneNumber.Length.ToString («X2») + «91» + EncodePhoneNumber (RecipientPhoneNumber);

    Для «+79123456789» выражение RecipientPhoneNumber.Length.ToString («X2») не дает 0B, оно дает 0C.Тут RecipientPhoneNumber есть строка с содержаниием «+79123456789», то есть она содержит 12 знаков. То есть «+» перед употреблением этого фрагмента надо отбросить насильно. Или я что-то неправильно понял?

  19. Тогда значит код должен быть типа таким:

    string RecipientPhoneNumber = «+79123456789»;

    string RecipientPhoneNumber2 = RecipientPhoneNumber.Replace («+», "");

    string recipient = RecipientPhoneNumber2.Length.ToString («X2») + «91» + EncodePhoneNumber (RecipientPhoneNumber);

    ПРавильно?

    Есть еше один вопрос. При формировании SMS = SCA + TPDU все составляющие накапливаются в переменной типа string. В разобранном примере получаем строку «0001000B919721436587F9000812041F04400438043204350442002100210021». Про нее мы говорим модему, что отправляем 31 байт, а не 32. Но реально ы этой строке 64 байта, по 2 на знак.Модем не должен нас понять, а понимает. Почему? Что делается с лишними байтами?

    1. Модем нас прекрасно понимает. Он не дурак и тоже прекрасно знает что один байт кодируется двумя ASCII-символами, так что все в порядке.

  20. Попробовал на Nokia, использовал 2 модели телефонов — отлично все работает. На LG — нет. А все тоже самое в программе, ничего не менял, только телефон переключил. LG ставит два модема и использует несколько портов(3 я так понял). Попробовал все, неудача везде. На одном правда модем оттранслировал обратно сообщение с добавлением Error, на другом просто Error, а третий не ответил. Не подскажите в чем может быть дело?

    1. У меня на технику LG идиосинкразия, так что про лыжи ничего сказать не могу — никогда не пользовался... Но с другими марками телефонов это работало... Возможно это какая-то индивидуальная особенность данного аппарата или марки... В конце концов не все производители придерживаются стандартов...

  21. Купил твои исходники. Сейчас пытаюсь перебить на VB_6, чтоб прикрутить к MSAccess как класс.

    Не понял эту строку с коде, поскольку в C# не силён:

    // Готовим UDH

    Random rnd = new Random ();

    string UDH = textIsUnicode ? «050003» + rnd.Next (256).ToString («X2») : «060804» + rnd.Next (65536).ToString («X4»);

    Если точнее, то меня интересует эта часть «rnd.Next (256)» и это «rnd.Next (65536)». Что здесь происходит по сути. Если правильно понял, то строкой выше ты запустил рандомайзер, а здесь получаешь случайное число, но зачем? И если да, то в каких пределах оно должно быть?

    1. Ты, видимо, не читал мою статью про отправку длинных СМС.

      В данном случае, мы добавляем поле пользовательских данных UDH, которое содержит информацию о частях сообщения.

      Это случайное число — номер-ссылка (reference number) нужен для того, чтобы телефон при приеме наших нескольких коротких сообщений, на которые разбивается длинное, знал, что они принадлежат к одному и тому же большому. Для всех частей одного большого сообщения это число должно быть одинаковым! Например, вы посылаете на телефон длинное сообщение разбитое на 3 коротких, а в это же время кто-то еще посылает на этот же телефон длинное сообщение. Короткие сообщения приходят в телефон вперемешку, и для того, чтобы телефон в них не запутался как раз и применяется это поле. Оно будет разное у двух длинных сообщений, но одинаковое в пределах частей каждого из них. Таким образом телефон не перепутает кусочки. Число может быть абсолютно любым. Я применяю для его формирования датчик случайных чисел.

      Еще один момент, связанный с полем – это его размерность. Она может быть либо 1 либо 2 байта. А зависит это в моем случа от типа кодировки, если текст сообщения — юникод, то размерность 1 байт, т.е. число от 0 до 255. Если нет, то размерность 2 байта, т.е. от 0 до 65535.

      Рекомендую изучить мою статью «Отправка длинных SMS-сообщений в формате PDU» : hardisoft.ru/soft/otpravk...j-v-formate-pdu/

  22. С кодировкой вроде разобрался. Но всё равно не отправляет.

    После команды AT+CMGS=31 на приглашение «>» отправляю сформированную смс — получаю в ответ только «OK».

    И всё...

    1. Гм... Раз модем отвечает ОК, значит, синтаксических ошибок нет, т.е. все правильно. Может, номер не тот 🙂 ?

      1. Номер тот.

        Если бы она ушла, пусть и на другой номер, то баланс должен поменяться, а так ничего не происходит. И потом, слишком быстро он даёт ответ — мгновенно, и в ответе только «OK», а должно быть вроде ещё что-то типа «+CMGS: 51».

  23. Формирую сообщение «Hello!!!» на номер «+375296888651». Получаю

    0001000C91732569886815000008C8329BFD0E8542

    Это отправляю в модем.

    Попробовал проверить отправляемое сообщение online-декодером: www.diafaan.com/sms-tutor...sms-pdu-decoder/

    Перевелось всё правильно. Правда, там ещё много каких-то полей с нулевыми значениями.

    1. Попробуй все это отправить, подключившись к модему по этому же порту через гипертерминал или Putty. Модемы создают несколько COM-портов, может это не тот порт?

      1. Порт тот. По крайней мере в переделанных ваших исходниках то кнопке «тест порта» выдаёт что всё ok. Да и как будет порт не тот, если предыдущие команды обрабатывает правильно.

        1. Ну, сложно сказать, что у тебя не так, без возможности поковыряться самому... А моя программа с этим портом правильно работает?

          1. Спасибо, разобрался сам.

            Поскольку работаю в VBA, то пришлось отправлять команды на модем через mscomm32.ocx

            Там есть такая фишка «очистка буфера» — задаётся отправкой 27-го символа. Так я умудрился его посылать в модем перед каждой командой (скопипастил, так сказать, не удачно). Естественно у меня ничего не получалось.

  24. Доброго дня. У меня аналогичная с Aeliot ситуация, только на С#. Модем команды принимает, строка в PDU формате формируется правильно (проверил по www.diafaan.com/sms-tutor...sms-pdu-decoder/ ), но смска не приходит и модем выдает сообщение только «OK», хотя должен еще что то выдать.

    СМС-центр указан правильно. № абонента тоже правильно. В текстовом режиме можем СМС-ки отправляет.

    Ваши исходники не качал. Пытаюсь сам разобраться. Помогите, плизззз...

    P.S. Модем Siemens MC35i

  25. Доброго дня!

    Спасибо за статьи! Очень понятно, доходчиво расписано. У меня ситуация, похожая на ту, что описал Игорь 15.10.2012, только не ОК отвечает модем, а ошибку выдаёт 304. Хотя тоже проверил, то, что отправляется в модем на указанном сайте — всё расшифровалось. В исходниках ничего нет (видимо, уже давно слишком это было). Интересно, на сайте вообще ещё жизнь есть?

  26. День добрый!

    Имеем 7 бит кодированную строку Test.

    Ее HEX представление D4F29C0E

    Соответственно я хочу перегнать обратно из 7 в 8...что мы получаем в итоге если делать все через двоичную последовательность??

    Получаем выходит неверные данные. Начнем

    D4 11010100

    F2 11110010

    9C 10011100

    0E 00001110

    Двоичная последовательность получается

    11010100 11110010 10011100 00001110

    Выдергиваем по 7 бит и имеем

    1101010 0111100 1010011 1000000 1110

    Каким образом тут выявить те самые Test при 8 битном кодировании, где HEX представление имеет вид

    54657374

    54 (T) 01010100

    65 (e) 01100101

    73 (s) 01110011

    74 (t) 01110100

    Причем это только для случая где количество символов < 8

Добавить комментарий

Ваш e-mail не будет опубликован.