查看完整版本: 分析cmpp2.0submit消息16进制数据文件,按XML格式定义的消息格式解析的小程序

xukong 2007-11-21 02:06

分析cmpp2.0submit消息16进制数据文件,按XML格式定义的消息格式解析的小程序

因为工作需要,总是和短信网关服务器和客户端打交道,早就想写个分析的程序了,正好用来熟悉RUBY

parsecmpp2submit.bat
ruby parsecmpp2submitfromhex.rb cmpp2struct.xml cmpp2info.txt

parsecmpp2submitfromhex.rb

require 'rexml/document'

orders_xml = %{
        <orders>
          <order>
            <number>105</number>
            <date>02/10/2006</date>
            <customer>Corner Store</customer>
            <items>
              <item upc="404100" desc="Red Roses" qty="240" />
              <item upc="412002" desc="Candy Hearts" qty="160" />
            </items>
          </order>
        </orders>
        }
#orders = REXML::Document.new(orders_xml)

##
#说明
#本程序是为了将保存下来的16进制的cmpp_submit数据包
#按CMPP2.0协议的SUBMIT消息定义将每一个字段解析出来,
#可以看出是传入的哪个参数不对,在初次使用网关的开发包调试时比较有帮助
##
orders = REXML::Document.new(open(ARGV[0]))#第1个参数是消息结构的定义文件

#这段代码是有用的,但是现在暂时用不着了
#orders.root.each_element do |order|    # each <order> in <orders>
#          order.each_element do |node|         # <customer>, <items>, etc. in <order>
#            if node.has_elements?
#              node.each_element do |child|     # each <item> in <items>
#                puts "子节点名称:#{child.name} 子节点值:#{child.text} 子节点属性:#{child.attributes['desc']}"
#              end
#            else
#              # the contents of <number>, <date>, etc.
#              puts "节点名:#{node.name} 节点值:#{node.text}"
#            end
#          end
#end
#这段代码是有用的,但是现在暂时用不着了

#将包含16进制数据的消息去掉所有空格和换行信息后合并成一行
alldatas = ""
#第2个参数是包含16进制数据的信息文件
open(ARGV[1]).each { |x|
alldatas = alldatas + (x.strip).gsub(" ", "")
#puts "值起始#{x.strip}值结束"
}
#puts alldatas
#puts "\n\n"

#cmpp2.0的SUBMIT消息有22个字段
cmpp2fieldscount = 22
#每个字段在合并成一行的16进制的起始和结束位置
currentindex = 0
currentindexend = 0

#保存没个字段实际值的数组
arrfieldvalue = []

for value in 1..cmpp2fieldscount
        #取出每一个消息字段的定义
        my_order = orders.root.elements[value.to_i]

        temp_node = my_order.elements['index']
        s_index = temp_node.text
        #puts "\n节点名:#{temp_node.name} 节点值:#{temp_node.text}"

        temp_node = my_order.elements['name']
        s_name = temp_node.text
        #puts "\n节点名:#{temp_node.name} 节点值:#{temp_node.text}"

        temp_node = my_order.elements['desc']
        s_desc = temp_node.text
        #puts "\n节点名:#{temp_node.name} 节点值:#{temp_node.text}"

        temp_node = my_order.elements['type']
        s_type = temp_node.text
        #puts "\n节点名:#{temp_node.name} 节点值:#{temp_node.text}"

        temp_node = my_order.elements['length']
        fieldlength = temp_node.text.to_i
        s_length = fieldlength
        #puts "\n节点名:#{temp_node.name} 节点值:#{temp_node.text}"
        #下面这段代码是这样的: 因为每条消息内容的长度是不定的,所以在截取消息内容字段时
        #需要根据实际的消息长度值来截取
        #那在配置文件里面就要设置需要被获取的实际值所在的索引
        #例如消息内容字段的索引是21
        #而消息内容字段的实际长度是包含在索引为20的消息字段实际值中的
        #
        temp_node = my_order.elements['lengthneedrefrenceindex']
        if ((temp_node != nil) && (temp_node.text != nil))
                needrefrenceindex = (temp_node.text).to_i
                if (needrefrenceindex >= 0)

                        s_length = arrfieldvalue[needrefrenceindex].to_i(16)#因为都是16进制表示的,所以需要转换
                        #puts "需要参考节点索引为 #{needrefrenceindex} 的 属性为 #{temp_node.text} 的实际值 #{s_length}"

                end
        end
       
        #因为是16进制的数据,所以1个实际值要取2个字符,又因为是从0开始,所以要减1
        currentindexend = currentindex + s_length*2 - 1

        datafieldvalue = alldatas[currentindex..currentindexend]

        #同理,起始的实际值要加1了
        currentindex = currentindexend + 1

        #puts datafieldvalue
        #将字段的实际值保存在上面声明的数组中,为需要取得被参考的字段时使用,比如取的实际的消息长度
        arrfieldvalue[value] = datafieldvalue
       
        temp_node = my_order.elements['needconvert']
        i_needconvert = temp_node.text.to_i
        end_value = ""
        if (i_needconvert == 1)#将16进制的整型值转化为10进制的形式,方便输出查看
                if (s_type == "int")
                        end_value = datafieldvalue.to_i(16)
                end
        end
       
        #呵呵,因为前面都去掉空格了,但是在输出的时候还是需要加上空格,这样方便查看
        datafieldvaluewithspace = ""
        tmpindex1 = 0
        for tmpv1 in 1..s_length
                #puts tmpindex1
                #puts tmpv1*2 - 1
                tv = datafieldvalue[tmpindex1..tmpv1*2 - 1]
                if (tv != nil)
                        datafieldvaluewithspace = datafieldvaluewithspace + tv + " "
                end
                tmpindex1 = tmpv1*2
                #puts tmpindex1

        if (i_needconvert == 1)
                if (s_type == "string")#如果是字符串,正好将取得的每1个从16进制转换
                        end_value = end_value + (tv.to_i(16)).chr
                end
        end

        end
        #puts "#{s_index}.名称: #{s_name} 长度: #{s_length} 描述: #{s_desc }\n#{datafieldvaluewithspace}\n"
        #输出最终的字段转换结果
        puts "#{s_index}.名称: #{s_name} 长度: #{s_length} 描述: \n#{datafieldvaluewithspace}\n转换后#{end_value}\n\n"
end

cmpp2info.txt

94 0d ab 00 00 00 02 00 01 01 00 00 53 45 52 56
49 43 45 49 44 41 00 31 33 39 38 35 30 35 39 39
32 39 00 00 00 00 00 00 00 00 00 00 00 00 08 39
32 34 30 32 37 30 31 30 30 30 30 30 30 30 37 31
31 32 30 31 34 34 35 35 36 30 33 32 2b 00 30 37
31 31 32 30 31 33 34 35 35 36 30 33 32 2b 00 31
30 36 35 38 32 30 30 00 00 00 00 00 00 00 00 00
00 00 00 00 01 31 33 39 38 35 30 35 39 39 32 39
00 00 00 00 00 00 00 00 00 00 18 4e 2d 65 87 8d
35 5d de 77 ed 6d 88 60 6f 7f 51 51 73 6d 4b 8b
d5 00 31 00 00 00 00 00 00 00 00

cmpp2struct.xml

        <cmpp2structs>
          <field>
            <index>1</index>
            <name>Msg_Id</name>
            <desc>信息标识,由SP侧短信网关本身产生,本处填空。</desc>
            <type>int</type>
            <length>8</length>
            <items>
              <item upc="404100" desc="Red Roses" qty="240">item1</item>
              <item upc="412002" desc="Candy Hearts" qty="160" />
            </items>
          <needconvert>1</needconvert></field>

          <field>
            <index>2</index>
            <name>Pk_total</name>
            <desc>相同Msg_Id的信息总条数,从1开始</desc>
            <type>int</type>
            <length>1</length>
            <items>
              <item upc="404100" desc="Red Roses" qty="240">item1</item>
            </items>
          <needconvert>1</needconvert></field>

...
          <field>
            <index>20</index>
            <name>Msg_Length</name>
            <desc>消息长度 </desc>
            <type>int</type>
            <length>1</length>
            <items>
              <item upc="404100" desc="Red Roses" qty="240">item1</item>
            </items>
          <needconvert>1</needconvert></field>

          <field>
            <index>21</index>
            <name>Msg_Content</name>
            <desc>消息内容 </desc>
            <type>string</type>
            <length>1</length>
            <items>
              <item upc="404100" desc="Red Roses" qty="240">item1</item>
            </items>
          <needconvert>1</needconvert>
          <lengthneedrefrenceindex>20</lengthneedrefrenceindex>
          <lengthneedrefrencename>length</lengthneedrefrencename>
          </field>

          <field>
            <index>22</index>
            <name>Reserve</name>
            <desc>保留 </desc>
            <type>string</type>
            <length>8</length>
            <items>
              <item upc="404100" desc="Red Roses" qty="240">item1</item>
            </items>
          <needconvert>1</needconvert></field>

        </cmpp2structs>

maninred 2007-11-22 17:39

用yaml代替xml来描述数据会不会好一点?

lgn21st 2007-11-22 17:48

因为工作需要,总是和信用卡支付网关以及ERP服务器打交道,SOAP协议,纯手写的xml解析
代码我就不贴了
XML是异构平台通讯的事实标准,但是ruby处理XML就是超级烦
一开始用hpricot,后来因为需要用到XPath,试用过libxml2,bug,问题一大堆
还尝试过xml跟ruby对象map
现在我只用rexml,虽然速度慢,但是能用
谁有完整的解析xml schema中名称空间并验证xml模式的方案?
:D

[[i] 本帖最后由 lgn21st 于 2007-11-22 17:49 编辑 [/i]]
页: [1]
查看完整版本: 分析cmpp2.0submit消息16进制数据文件,按XML格式定义的消息格式解析的小程序