2006年09月14日
一、移动应用消息的格式:
移动应用SCCP消息是在No.7信令的消息信号单元(MSU)中的信号消息字段(SIF)中传送,采用UDT消息类型,协议类别为0或1类,消息基本格式如图:
 
 
F
CK
SIF
SIO
 
LI
FIB
FSN
BIB
BSN
F
8
16
N*8
8
2
6
1
7
1
7
8
 
 
 
1
路由标记
1
消息类型(UDT)00001001
1
协议类别
1
被叫地址指针
1
主叫地址指针
1
数据部分指针
1
被叫地址长度
1
被叫地址
X
主叫地址长度
1
主叫地址
Y
数据长度
1
数据(TCAP消息)
Z
              移动应用SCCP消息的基本格式
二、MAP使用SCCP和TCAP的说明:
1、SCCP的使用:
·         MAP仅用SCCP的无连接业务协议类别0或1类。
·         子系统号: 00000101     用于整个MAP(保留给将来使用)
              00000110         HLR
              00000111      VLR
              00001000      MSC
              00001001      EIR
              00001010      AUC
2、MAP在网络结构上位于TCAP之上
三、UDT结构举例:
118 >> 30168 UDT       000000d       05FF09 03FF11 38 83 11 FF 03 09 FF 05 0D 09 81 03 0E 18 0B 12 06 00 12 04 68 31 39 31 00 00 0A 12 07 00 12 04 68 31 09 40 67 13 65 1D 49 04 2B 81 11 00 6C 15 A2 13 02 01 00 30 0E 02 01 02 30 47 09 10 13 F8 30 03 0A 01 01
在以上跟踪消息中,结构如下:
1、整个消息都属于MTP层。
2、SCCP层是从09 81 开始,一直到最后都属于SCCP层。
3、TCAP层是从65 1D 开始,一直到最后都属于TCAP层。
4、成份子层是从6C 15开始,一直到最后都属于TCAP层的成份子层,成份中可以封装MAP消息。
l       各层消息分解如下:
1MTP层:
38——表示整个MTP消息的长度为56(38H)个字节,当所有消息字节数大于63个字节时,该字节统一为3F。
83——高4位“8”网络表示语,表示国内主用网;低4位“3”业务表示语,表示后面的是SCCP消息。
11 FF 03——DPC是03 FF 03
09 FF 05——OPC是05 FF 09
0D————SLS信令链路选择码
2SCCP层:
l         UDT消息类型的格式包括消息类型码、协议类别、路由标记(包括三个指针:第一个指针指向被叫用户地址、第二个指针指向主叫用户地址、第三个指针指向数据即TCAP部分)
09——表示消息类型是UDT。
81——高4位是“8”表示QOS(Quality of service)要求出错返回,高4位如果为“0”则表示不要求出错返回;低4位“1” 表示SCCP协议分类是1类有序的无连接类。
03——被叫用户地址指针,“03”表示从“03”后面的第三个字节开始是被叫地址。
0E——主叫用户地址指针,“0E”表示从“0E”后面的第十四个字节开始是主叫地址。
18——数据地址指针,“18”表示从“18”后面的第二十四个字节开始是数据地址,亦即TCAP部分的开始。
0B 12 06 00 12 04 68 31 39 31 00 00 ——被叫GT码地址。
0B——表示被叫GT地址长度为11个字节。
12——该字节表示地址表示语和翻译类型,含义如下:
         Bit8——备用
         Bit7——路由表示语
“0”根据地址中的全局码(GT)选取路由
                     “1”根据MTP路由标记中的DPC和被叫用户地址中的子系统选取路由
         Bit6/5/4/3——全局码表示语
                     “0000”表示0类GT
                     “0001”表示1类GT
                     “0010”表示2类GT
                     “0011”表示3类GT
                     “0100”表示4类GT
         Bit2——子系统表示语
                     “0”未包括子系统号
                     “1”包括子系统号
         Bit1——信令点表示语
                     “0”未包括信令点码
                     “1”包括信令点码
06——子系统号
         0000 0000—未定义的子系统号/没有使用
         0000 0001—SCCP管理(SCMG)
         0000 0010—备用
         0000 0000—ISDN用户部分(ISUP)
         0000 0100—操作维护管理部分(OMAP)
         0000 0101—移动应用部分(MAP)
         0000 0110—归属位置登记器(HLR)
         0000 0111—拜访位置登记器(VLR)
         0000 1000—移动交换中心(MSC)
         0000 1001—设备识别中心(EIR)
         0000 1010—认证中心(AUC)
         0000 1011—备用
         0000 1100—智能网应用部分(INAP)
         0000 1101
                 
                      备用
                 
         1111 1110
         1111 1111—扩充备用
 
00——在4类GT中该字节备用
12——该字节高4位是编号计划,低4位是编码设计。
              编号计划                                                 编码设计
8765                                                                                                   4321
0000              未定义                                0000              未定义
0001              ISDN/电话编号计划            0001              BCD,奇数个数字
0010              备用                                          0010              BCD,偶数个数字
0011              数据编号计划                            0011              备用
0100              Telex编号计划                    0100             
0101              海事移动编号计划                     0101             
0110              陆地移动编号计划                     0110             
0111              ISDN/移动编号计划            0111       
1000                                                        1000             
                             备用                                                        
                                                                                     
1111                                                  1111        备用
04——地址性质表示语编码
              7 6 5 4 3 2 1
              0 0 0 0 0 0 0          空闲
              0 0 0 0 0 0 1          用户号码
              0 0 0 0 0 1 0          国内备用
              0 0 0 0 0 1 1          国内有效号码
              0 0 0 0 1 0 0          国际号码
              0 0 0 0 1 1 0          智能网业务号码
              0 0 0 0 1 0 1          空闲
                    
                    
              1 1 1 1 1 1 1          空闲
68 31 39 31 00 00——MSISDN,86139313000
0A 12 07 00 12 04 68 31 09 40 67——主叫GT码地址。分析方法同被叫GT码地址。
13——SCCP数据部分的长度,即TCAP消息的长度
3TCAP层:
TCAP层也就是SCCP的数据部分,TCAP层的消息是由信息单元组成的,一个信息单元由标签(Tag)、长度(Length)、内容(Contents)组成。划分信息单元是对TC消息进行分析基础。
TC由两个子层组成,即事务处理子层和成份子层,事务处理子层处理两个“TC—用户”之间包含成份的消息交换。成份子层处理成份,即传送远端操作及响应的协议数据单元。从功能上,成份子层可以提供对话处理和成份处理,事务处理子层提供事务处理。
以下即是TCAP消息的内容。
65 1D 49 04 2B 81 11 00 6C 15 A2 13 02 01 00 30 0E 02 01 02 30 47 09 10 13 F8 30 03 0A 01 01
 
具体分析如下:
65——事务处理部分字段编码,即TCAP的消息类型标签(Tag),表示消息类型是上一个消息继续(Continue)。
                                                                                    编 码
              主要消息类型标签(字段名称)           H G F E DC B A
              开始(Begin)                                                0 1 1 0   0 0 1 0
              结束(End)                                                   0 1 1 0   0 1 0 0
              继续(Continue)                                            0 1 1 0   0 1 0 1
              中止(Abort)                                                0 1 1 0   0 1 1 1
1D——根据信息单元组成,消息类型标签(Tag)之后即为信息单元长度(Length),所以“1D”表示该TCAP消息的长度是29(1DH)个字节。
49——表示消息类型中的事务处理ID,以区分不同的事务。“49”表示目的地事务处理ID标签。
            “48”起源事务处理ID标签
            “49”目的地事务处理ID标签
04——再根据信息单元组成,标签(Tag)之后又是信息单元长度(Length),所以“04”表示目的地事务处理ID值的长度(Length)是4个字节
2B 81 11 00——目的地事务处理ID值
4)成份子层:
该层包含有MAP消息,是分析MAP信令的关键。成份子层一般有成份部分和对话部分,在含有MAP消息的UDT中,大多包含有成份部分,但不一定含有对话部分。以下是只含有成份部分的成份子层消息。该层相对于MAP而言是透明的。
6C 15 A2 13 02 01 00 30 0E 02 01 02 30 47 09 10 13 F8 30 03 0A 01 01
以上是该消息的成份子层部分,具体分析如下:
6C——成份部分标签(Tag
              “6C”,表示是成份部分标签。
              “6B”,表示是对话部分标签。
15——根据信息单元组成,标签(Tag)之后又是信息单元长度(Length),所以“15”表示该成份的长度(Length)是21(15H)个字节。
A2——成份部分中部件编码标签(Tag)
              “A1”表示该部件是调用部件
              “A2”表示该部件是返回结果(最终)部件
              “A3”表示该部件是返回差错部件
13——标签(Tag)之后又是信息单元长度(Length),“13”即表示该返回结果部件消息的长度是19(13H)个字节。
02——表示该返回结果部件消息的调用ID标签(Tag),即明确该返回结果部件返回的是针对哪一个调用的结果。
01——调用ID长度(Length)是1个字节。
01——调用ID的值是01
30——表示序列标签(Tag),该项在返回结果成份中是可选部分。有的部件中无此项。
0E——序列长度(Length)
02——本地操作码标签(Tag),表示本次调用所进行的操作。
01——本地操作码长度(Length)是一个字节,即下一个字节即为操作码。
02——操作码,表示本次调用所进行的操作。以下是常用的几种操作:
              操作码                                       操 作
02                                                                                            位置更新(Locating Updating)
03                                                                                            删除位置信息(Cancel Location)
04                                                                                            取漫游号(Sent Roaming Number)
07                                                                                            插入用户数据(Insert Subscriber Data)
09                                             取用户参数(Sent Parament)
16                                                                                            取路由信息(Sent Routing Infomation)
56                                             送鉴权信息(Sent Authentication Infomation)
30 47 09 10 13 F8 30 03 0A 01 01——MAP操作的一些具体内容,采用ANS1的抽象编码,大多MAP层的一些具体参数,在此不必再进行具体分析。
通过以上的分析可以看出,对MAP消息的分析主要在于对成份部分的分析,也即在消息中找到成份部分的标签(Tag)——6C,然后即可对移动用户部分的呼叫接续过程进行分析。


附一、移动用户作为被叫的过程(E164码的作用):
1、有呼叫发往GMSC,号码一般为E164码(即被叫MSISDN135/6/7/8/9XXX),通过从GMSC开始使用MAP(即GT码),GMSC将该GT码发往STP,并通过STP转发到HLR。
2、HLR中存有MSC/VLR的MSCID(即164码138/900XXX),HLR用MAP信令访问VMSC/VLR,VMSC/VLR根据IMSI就为被叫MS分配一个漫游号(Roaming Number,),并将此漫游号(包括MSC/VLR号+漫游号,13900MMMXXX)返回给HLR。然后HLR再将此漫游号转给GMSC。也即是GMSC得到了被叫MS的漫游号。
3、GMSC根据得到的漫游号,通过被叫分析表发IAM给TMSC或VMSC建立话务,并根据漫游号接通被叫MS。
4、如果GMSC从HLR处得到呼叫前转等信息(例如MS转移呼叫到固定电话),则在被叫分析表中将接续转到市话局。
移动用户作为被叫的正常接续过程如下图:
                      取路由信息          取漫游号
GMSC(86139HHHXXXX)——————>HLR—————>VMSC/VLR
                            MAP(135/9HHHXXX)    MAP(138/900MMM) 
 
                             返回漫游号     返回漫游号
GMSC(86139HHHXXXX)<——————HLR<—————VMSC/VLR   
        MAP(138/900MMMXXX)<———MAP(138/900MMMXXX)
 
5、MSC与GMSC的区别:国内一般的MSC都具有GMSC功能,在纯MSC中一般接续发送的消息都只有MSISDN号码,要访问移动用户只能发IAM(包含MSISDN)给GMSC,再由GMSC获得MS的漫游号,然后完成话路接续。


附二、移动用户的位置更新(E214码的作用):
SIM卡中包含的是IMSI(即E212码)号,该号码与E214码对应。
(转化工作在端局MSC完成)
 
    IMSI(E.212)———————————>E.214
460    00    HHH XXXXXXX         86   139    HHH XXXXXXX 
MCC MNC MSIN                          CC NDC   HHH
^             ^             ^                                  ^      ^      ^
^       ^        —————————————————
^       ——————————————————     
 ——————————————————
E.214码用于VLR向HLR发起位置更新时的GT被叫地址格式。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
主要MAP消息流程分析
 
1.位置更新(包括取鉴权参数)
正常流程举例:
MSC/VLR                                                HLR                                                PVLR
       _TC_Begin(MAP_SendParameters_Req)_>
       <_TC_End(MAP_SendParameters_Cnf)___
 
_TC_Begin(MAP_UpdateLocation_Req)_>
       _TC_Begin(MAP_CancelLocation_Req_>
<_TC_Continue(MAP_InsertSD_Req)____
                                                         <_TC_End(MAP_CancelLocation_Cnf)__
__TC_Continue(MAP_InsertSD_Cnf)____>
<_TC_End(MAP_UpdateLocation_Cnf)___
 
1.1 TC_Begin(MAP_SendParameters_Req)
消息举例:
119 >> 30210 UDT       000000b       05FF08 10FF09 3F 83 09 FF 10 08 FF
05 0B 09 00 03 10 1A 0D 12 06 00 71 04 68 31 39 47 09 10 13 08 0A 12 07 00 12
04 68 31 09 40 17 23 62 21 48 04 FA 3A 2E 36 6C 19 A1 17 02 01 01 02 01 09 30
0F 80 08 64 00 30 47 09 10 13 F8 30 03 0A 01 01
在SCCP UDT消息用户数据部分,0×62为TC_BEGIN消息标志;
0×48为源对话ID标志;
0×6C为对话部分标志:
0xA1为调用部件标志:
首先找到调用ID标志(第一个0×02),调用ID为0×01(对话中一方发起的调用,其调用ID不可重复);
       接着找操作码标志(接着的0×02),操作码为0×09(发送用户数据操作);
其后码串为MAP消息编码(注:消息分析只要求根据操作码确定MAP消息类型即可,这里为要求发送鉴权参数)
              注:VLR向HLR取鉴权参数还可以用ObtainAutnenticateSets操作,操作码为0×56
 
1.2 TC_End(MAP_SendParameters_Cnf)
消息举例:
   6 << 30633 UDT       000000c       05FF08 1BFF09 3F 83 09 FF 1B 08 FF
05 0C 09 00 03 0D 18 0A 12 07 00 12 04 68 31 09 90 13 0B 12 06 00 12 04 68 31
49 17 00 00 CE 64 81 CB 49 03 07 02 7B 6C 81 C3 A2 81 C0 02 01 01 30 81 BA 02
01 09 30 81 B4 A1 22 04 10 75 A0 FF 59 C1 A5 5F EB 66 30 72 80 B6 22 CD 75 04
04 AA 04 FD 04 04 08 D8 81 F8 FF 29 FF A0 00 A1 22 04 10 F5 04 61 27 B3 DA 9D
D4 0B C3 7B 2E B4 E8 64 CC 04 04 7F 07 A7 9B 04 08 8B B3 2C A9 DD B0 A4 00 A1
22 04 10 A9 68 64 6A 38 B5 03 46 07 24 09 C6 03 A8 F5 F5 04 04 77 50 86 39 04
08 AF 44 1E BD 79 F4 7C 00 A1 22 04 10 14 6C 4D DE 4B 24 E0 86 7A EE 4F 55 1F
4D 78 DD 04 04 B3 F1 62 A3 04 08 1F CA 98 FF 05 D0 48 00 A1 22 04 10 8D 24 9C
25 AD 75 A8 E9 AA EB EE D8 63 25 29 11 04 04 CB 25 5A 3D 04 08 61 1F 75 E6 75
85 B0 00
在SCCP UDT消息用户数据部分,0×64为TC_END消息标志;
0×49为目的对话ID标志(两个信令实体之间可以并发多个TCAP对话,TCAP根据对话ID区分不同的对话。同一对话中源对话ID和目的对话ID总是成对出现);
0×6C为对话部分标志:
0xA2为结果(最后)部件标志:
首先找到调用ID标志(第一个0×02),调用ID为0×01(TCAP对话中双方实体都可以向对方发起调用,调用ID用于区分同一实体发起的不同调用。结果部件中的调用ID总是与对话中另一实体发起的某一调用部件中的调用ID成对出现。);
接着的操作结果部分为可选(由TCAP用户决定。在操作结果部分没有时需根据调用ID找出该调用结果是对话中另一实体发起的哪一调用的成功返回,如位置更新过程中的插入用户数据响应消息)。举例消息中包含结果部分,标志为0×30:
接着找操作码标志0×02,操作码为0×09(发送用户数据操作);
其后码串为MAP消息编码(注:消息分析只要求根据操作码确定MAP消息类型即可,这里为返回的鉴权参数)
注:若TCAP用户处理出错,返回出错部件(包括错误码和可选的错误参数),
这种情况下为端局应用层处理错误,与HSTP无关,必要时可告知端局错误码及错误参数;
0xA3为出错部件标志:
首先找到调用ID标志(第一个0×02),这时一般需根据调用ID找出该调用结果是对话中另一实体发起的哪一调用的出错返回;
接着找错误码标志0×02;
接着的错误参数部分为可选;
 
1. 3 TC_Begin(MAP_UpdateLocation_Req)
消息举例:
118 >> 30129 UDT       000000d       05FF09 03FF11 3F 83 11 FF 03 09 FF
05 0D 09 81 03 10 1A 0D 12 06 00 71 04 68 31 39 32 09 10 55 04 0A 12 07 00 12
04 68 31 09 40 67 4F 62 4D 48 03 EA 01 85 6B 1E 28 1C 06 07 00 11 86 05 01 01
01 A0 11 60 0F 80 02 07 80 A1 09 06 07 04 00 00 01 00 01 02 6C 80 A1 22 02 01
01 02 01 02 30 1A 04 08 64 00 30 32 09 10 55 F4 81 06 91 68 31 09 40 67 04 06
91 68 31 09 40 67 00 00
在SCCP UDT消息用户数据部分,0×62为TC_BEGIN消息标志;
0×48为源对话ID标志;
0×6B为可选的对话部分标志(可以略过);
0×6C为对话部分标志:
0xA1为调用部件标志:
       首先找到调用ID标志(第一个0×02),调用ID为0×01;
       接着找操作码标志(接着的0×02),操作码为0×02(位置更新操作);
其后码串为MAP消息编码(注:消息分析只要求根据操作码确定MAP消息类型即可)
 
1.4 TC_Continue(MAP_InsertSD_Req)
消息举例:
 58 << 31328 UDT       000000a       09FF01 04FF09 3F 83 09 FF 04 01 FF
09 0A 09 81 03 0D 18 0A 12 07 00 12 04 68 31 09 30 34 0B 12 06 00 12 04 68 31
69 11 00 00 A6 65 81 A3 48 03 D8 02 E5 49 04 2B 81 11 00 6B 2A 28 28 06 07 00
11 86 05 01 01 01 A0 1D 61 1B 80 02 07 80 A1 09 06 07 04 00 00 01 00 01 02 A2
03 02 01 00 A3 05 A1 03 02 01 00 6C 80 A1 66 02 01 02 02 01 07 30 80 81 07 91
68 31 68 21 87 36 82 01 0A 83 01 00 A6 03 04 01 11 A7 80 A1 0D 04 01 92 30 08
30 06 83 01 10 84 01 04 A1 0D 04 01 93 30 08 30 06 83 01 10 84 01 05 A3 09 04
01 11 84 01 05 81 01 01 A3 09 04 01 13 84 01 05 81 01 00 A3 06 04 01 12 84 01
00 A3 06 04 01 14 84 01 00 00 00 00 00 00 00
在SCCP UDT消息用户数据部分,0×65为TC_CONTINUE消息标志;
       0×48为源对话ID标志(在TC_Continue消息中需告知本实体对话ID);
0×49为目的对话ID标志;
0×6B为可选的对话部分标志(可以略过);
0×6C为对话部分标志:
0xA1为调用部件标志:
首先找到调用ID标志(第一个0×02),调用ID为0×02(对话中一方发起的调用,其调用ID不可重复);
       接着找操作码标志(接着的0×02),操作码为0×07(插入用户数据操作);
其后码串为MAP消息编码(注:消息分析只要求根据操作码确定MAP消息类型即可,这里为插入的用户数据)
 
1.5 TC_Continue(MAP_InsertSD_Cnf)
消息举例:
118 >> 30168 UDT       000000d       05FF09 03FF11 38 83 11 FF 03 09 FF
05 0D 09 81 03 0E 18 0B 12 06 00 12 04 68 31 39 31 00 00 0A 12 07 00 12 04 68
31 09 40 67 13 65 11 48 03 EA 01 85 49 03 B2 01 9E 6C 05 A2 03 02 01 03
在SCCP UDT消息用户数据部分,0×65为TC_CONTINUE消息标志;
       0×48为源对话ID标志(在TC_Continue消息中需告知本实体对话ID);
0×49为目的对话ID标志;
0×6C为对话部分标志:
0xA2为结果(最后)部件标志:
首先找到调用ID标志(第一个0×02),调用ID为0×03;
 
1.6 TC_End(MAP_UpdateLocation_Cnf)
消息举例:
 58 << 31365 UDT       000000a       09FF01 04FF09 3F 83 09 FF 04 01 FF
09 0A 09 81 03 0D 18 0A 12 07 00 12 04 68 31 09 30 34 0B 12 06 00 12 04 68 31
69 11 00 00 1F 64 1D 49 04 2B 81 11 00 6C 15 A2 13 02 01 00 30 0E 02 01 02 30
09 04 07 91 68 31 69 11 00 00
在SCCP UDT消息用户数据部分,0×64为TC_END消息标志;
0×49为目的对话ID标志;
0×6C为对话部分标志:
0xA2为结果(最后)部件标志:
首先找到调用ID标志(第一个0×02),调用ID为0×00;
接着的操作结果部分为可选(由TCAP用户决定。在操作结果部分没有时需根据调用ID找出该调用结果是对话中另一实体发起的哪一调用的成功返回,如位置更新过程中的插入用户数据响应消息)。举例消息中包含结果部分,标志为0×30:
接着找操作码标志0×02,操作码为0×02(位置更新操作);
其后码串为MAP消息编码(注:消息分析只要求根据操作码确定MAP消息类型即可,这里为返回的HLR号码)
 
1. 7 TC_Begin(MAP_CancelLocation_Req)
1.8 TC_End(MAP_CancelLocation_Cnf)
在位置更新过程中,HLR可能发起向用户所在的前一VLR(PVLR)的CancelLocation操作,操作码为0×03;
 
2.呼叫
正常流程举例:
GMSC                                        HLR                                       VMSC/VLR
_TC_Begin(MAP_SendRoutingInfo_Req)_>
                                  _TC_Begin(MAP_ProvideRoamingNumber_Req)_>
                                                  <_TC_End(MAP_ ProvideRoamingNumber_Cnf)__
<_TC_End(MAP_SendRoutingInfo_Cnf)___
 
2. 1 TC_Begin(MAP_SendRoutingInfo_Req)
消息举例:
116 >> 28271 UDT       0000008       05FF08 1DFF0A 3F 83 0A FF 1D 08 FF
05 08 09 01 03 0E 18 0B 12 06 00 12 04 68 31 99 05 68 07 0A 12 08 00 12 04 68
31 09 40 17 49 62 47 48 04 FA 39 CE 36 6B 1A 28 18 06 07 00 11 86 05 01 01 01
A0 0D 60 0B A1 09 06 07 04 00 00 01 00 05 02 6C 23 A1 21 02 01 01 02 01 16 30
19 80 07 91 68 31 99 05 68 07 AA 0E 0A 01 04 04 09 04 03 80 90 A3 7D 02 91 81
在SCCP UDT消息用户数据部分,0×62为TC_BEGIN消息标志;
0×48为源对话ID标志;
0×6B为可选的对话部分标志(可以略过);
0×6C为对话部分标志:
0xA1为调用部件标志:
       首先找到调用ID标志(第一个0×02),调用ID为0×01;
       接着找操作码标志(接着的0×02),操作码为0×16(取路由信息操作);
其后码串为MAP消息编码;
 
2.2 TC_Begin(MAP_ProvideRoamingNumber_Req)
消息举例:
116 >> 31343 UDT       0000008       05FF09 1BFF11 3F 83 11 FF 1B 09 FF
05 08 09 80 03 0D 18 0A 12 07 00 12 04 68 31 09 90 63 0B 12 06 00 12 04 68 31
49 57 00 00 47 62 45 48 03 42 01 E3 6B 1E 28 1C 06 07 00 11 86 05 01 01 01 A0
11 60 0F 80 02 07 80 A1 09 06 07 04 00 00 01 00 03 02 6C 80 A1 1A 02 01 01 02
01 04 30 12 80 08 64 00 40 47 79 54 93 F4 81 06 91 68 31 09 90 63 00 00
在SCCP UDT消息用户数据部分,0×62为TC_BEGIN消息标志;
0×48为源对话ID标志;
0×6B为可选的对话部分标志(可以略过);
0×6C为对话部分标志:
0xA1为调用部件标志:
       首先找到调用ID标志(第一个0×02),调用ID为0×01;
       接着找操作码标志(接着的0×02),操作码为0×04(提供漫游号操作);
其后码串为MAP消息编码;
 
2.3 TC_End(MAP_ ProvideRoamingNumber_Cnf)
消息举例:
10 << 30302 UDT       000000e       05FF08 0CFF0A 3F 83 0A FF 0C 08 FF
05 0E 09 00 03 0E 18 0B 12 06 00 12 04 68 31 59 85 00 00 0A 12 07 00 12 04 68
31 09 40 27 46 64 44 49 04 FA 24 F8 DD 6B 26 28 24 06 07 00 11 86 05 01 01 01
A0 19 61 17 A1 09 06 07 04 00 00 01 00 03 02 A2 03 02 01 00 A3 05 A1 03 02 01
00 6C 14 A2 12 02 01 01 30 0D 02 01 04 04 08 91 68 31 09 40 27 88 F3
在SCCP UDT消息用户数据部分,0×64为TC_END消息标志;
0×49为目的对话ID标志;
0×6B为可选的对话部分标志(可以略过);
0×6C为对话部分标志:
0xA2为结果(最后)部件标志:
首先找到调用ID标志(第一个0×02),调用ID为0×01;
接着的操作结果部分为可选(由TCAP用户决定。在操作结果部分没有时需根据调用ID找出该调用结果是对话中另一实体发起的哪一调用的成功返回,如位置更新过程中的插入用户数据响应消息)。举例消息中包含结果部分,标志为0×30:
接着找操作码标志0×02,操作码为0×04(提供漫游号操作);
其后码串为MAP消息编码(注:消息分析只要求根据操作码确定MAP消息类型即可,这里为返回的漫游号)
 
2.4 TC_End(MAP_SendRoutingInfo_Cnf)
消息举例:
7 << 31149 UDT       000000d       05FF08 1CFF08 3F 83 08 FF 1C 08 FF
05 0D 09 00 03 0D 18 0A 12 08 00 12 04 68 31 09 90 17 0B 12 06 00 12 04 68 31
49 17 00 00 52 64 50 49 04 FA 17 17 6D 6B 26 28 24 06 07 00 11 86 05 01 01 01
A0 19 61 17 A1 09 06 07 04 00 00 01 00 05 02 A2 03 02 01 00 A3 05 A1 03 02 01
00 6C 20 A2 1E 02 01 01 30 19 02 01 16 30 14 04 08 64 00 40 17 79 71 65 F7 04
08 91 68 31 09 90 17 20 F8
在SCCP UDT消息用户数据部分,0×64为TC_END消息标志;
0×49为目的对话ID标志;
0×6B为可选的对话部分标志(可以略过);
0×6C为对话部分标志:
0xA2为结果(最后)部件标志:
首先找到调用ID标志(第一个0×02),调用ID为0×01;
接着的操作结果部分为可选(由TCAP用户决定。在操作结果部分没有时需根据调用ID找出该调用结果是对话中另一实体发起的哪一调用的成功返回,如位置更新过程中的插入用户数据响应消息)。举例消息中包含结果部分,标志为0×30:
接着找操作码标志0×02,操作码为0×16(取路由信息操作);
其后码串为MAP消息编码(注:消息分析只要求根据操作码确定MAP消息类型即可,这里为返回的漫游号);
 
注:1由于链路问题或GT翻译数据错误引起消息丢失或无法转发一般会产生相应告警
可根据告警信息做出相应处理;
2 消息分析的主要目的在于判断一个MAP信令过程执行到了哪一步及执行结果
       若无消息过滤功能,查找属于同一个信令过程的消息的主要依据有:
信令流程;
源与目的对话ID(总是成对出现,先找到TC_Begin消息中的源对话ID,那么接着的TC_End或TC_Continue消息中的目的对话ID应与源ID相同,其余类推);
主被叫GT(如发起呼叫时的被叫MSISDN);
注:由于端局静态GT负荷分担或GT翻译数据未指向H1HSTP,可能只跟踪到同一信令过程中的某一方向上的消息,这时主要注意消息中是否有出错部件(部件标志0×03),进而根据相应信令流程和GT信息确定是否端局问题;
2006年09月07日

getconf GNU_LIBPTHREAD_VERSION

2006年01月18日

Java的事件模式是动态响应系统重要的基础,在图形界面领域的事件模式已经有很多文章介绍,但是在服务器端我们会碰到更多的事件模式,这里本人试图总结一下:

  事件直接驱动模式

  事件模式的第一个要求就是性能的要求,需要直接而且快,Command模式是必须经常使用的,主要适合于迅速处理 前台的命令,Command模式往往是系统架构的重要部分,也是流程控制的主要模式。

  Command模式经常Java的Reflect一起使用,因为系统的事件处理系统是处于动态变化的,随着功能要求扩展,就可能有动态变化事件处理响应系统,以Struts中action为例,我们知道,Structs的一个主要配置文件是struts-config.xml 如下:

<struts-config>
  <action-mappings>
    <action path="/login" type="com.javapro.struts.LoginAction"/>
    <action path="/logout" type="com.javapro.struts.LogoutAction"/>
  </action-mappings>
</struts-config>

  它实际是个command和event的映射关系,通过这个配置文件,运行时动态装载相应的Action,完成Command模式, 我们检查LoginAction代码,就可以看出Command模式的基本特征:

public final class LoginAction extends Action {
  public ActionForward execute(ActionMapping mapping,
    ActionForm form, HttpServletRequest request, HttpServletResponse response)
    throws Exception {
        ……………..
  }
}

  很明显,典型的Command模式需要有一个接口.接口中有一个统一的方法,这里统一的方法就是execute;

  比如我们有个实时系统,客户段向服务器发出不同编码代号,意味着不同的请求,不同的请求有不同的Handler进行 处理,Handler接口是:

public class Handler{

  public byte[] handleRequest();

}


  不同性质的处理过程继承这个Handler接口,如负责进入系统的处理过程

public class EnterHandler implements Handler{

  public byte[] handleRequest(){
  //具体业务处理
  ……
  }

}


  调用Handler时是:

//从cache中获取这个requestId对应的Handler
Handler handler = (Handler)cache.get(new Integer(reqId));
//调用handler的统一方法handleRequest()
byte[] outInf = handler.handleRequest();

  以上是常用的一个事件驱动模式。它的特点是靠一个事件直接启动对应的事件处理器。

  Chain of Responsibility职责链模式也应该属于这类,当事件到达后,让这个事件在我们提供的一批处理器中逐个挑选适合的处理器进行处理,这个模式缺点是显然的,性能丧失在逐个挑选 上,一般不推荐使用,这个模式适合在我们无法预知发生的事件内容时使用,因为不知道发生事件的具体情况, 我们就无法在程序运行前事先为其指派相应的处理器,只能靠运行时,事件自己去摸索“撞运气”。

Swing是目前Java中不可缺少的窗口工具组,是用户建立图形化用户界面(GUI)程序的强大工具。Java Swing组件自动产生各种事件来响应用户行为。如当用户点击按钮或选择菜单项目时,Swing组件会产生一个ActionEvent。Swing组件会产生许多事件,如ActionEvents,ChangeEvents,ItemEvents等,来响应用户的鼠标点击行为,列表框中值的改变,计时器的开始计时等行为。在Java Swing编程中,通过注册监听器,我们可以监听事件源产生的事件,从而在事件处理程序中处理我们所需要处理的用户行为。

Java Swing中处理各组件事件的一般步骤是:

1. 新建一个组件(如JButton)。

2. 将该组件添加到相应的面板(如JPanel)。

3. 注册监听器以监听事件源产生的事件(如通过ActionListener来响应用户点击按钮)。

4. 定义处理事件的方法(如在ActionListener中的actionPerformed中定义相应方法)。

以上步骤我们可以用多种方法实现。但人们通常用二种方法。第一种方法是只利用一个监听器以及多个if语句来决定是哪个组件产生的事件;第二种方法是使用多个内部类来响应不同组件产生的各种事件,其具体实现又分两种方式,一种是匿名内部类,一种是一般内部类。

为了说明如何使用上述三种方法实现事件的处理方法,我们建立一个简单的应用程序。该程序界面有两个按钮,当用户点击相应的按钮,就会弹出一个对话框显示相应的内容。通过这个简单程序,你可以实现自己更多、更复杂的用户界面程序。

首先,我们利用单个监听器来实现该程序。我们定义一个名为Simple1的类来包括所有代码。所有的用户行为(如点击按钮)由一个监听器SimpleListenner中的actionPerformed方法来处理。以下是代码:


/*
* Simple1.java – 处理事件的第一种方法
* 在这个例子中,利用一个ActionListener来监听事件源产生的事件
* 用一些if语句来决定是哪个事件源
*/


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Simple1
{
private static JFrame frame; // 定义为静态变量以便main使用
private static JPanel myPanel; // 该面板用来放置按钮组件
private JButton button1; // 这里定义按钮组件
private JButton button2; // 以便让ActionListener使用

public Simple1() // 构造器, 建立图形界面
{
// 新建面板
myPanel = new JPanel();
// 新建按钮
button1 = new JButton("按钮1"); // 新建按钮1
button2 = new JButton("按钮2");

SimpleListener ourListener = new SimpleListener();
// 建立一个actionlistener让两个按钮共享
button1.addActionListener(ourListener);
button2.addActionListener(ourListener);

myPanel.add(button1); // 添加按钮到面板
myPanel.add(button2);
}

private class SimpleListener implements ActionListener
{
/*
* 利用该内部类来监听所有事件源产生的事件
* 便于处理事件代码模块化
*/
public void actionPerformed(ActionEvent e)
{
// 利用getActionCommand获得按钮名称
// 也可以利用getSource()来实现
// if (e.getSource() ==button1)

String buttonName = e.getActionCommand();
if (buttonName.equals("按钮1"))
JOptionPane.showMessageDialog(frame,
"按钮1 被点击");
else if (buttonName.equals("按钮2"))
JOptionPane.showMessageDialog(frame,
"按钮2 被点击");
else
JOptionPane.showMessageDialog(frame,
"Unknown event" ;
}
}

public static void main(String s[])
{
Simple1 gui = new Simple1(); // 新建Simple1组件

frame = new JFrame("Simple1"); // 新建JFrame
// 处理关闭事件的通常方法
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e)
{System.exit(0);} });

frame.getContentPane().add(myPanel);
frame.pack();
frame.setVisible(true);
}
}

让我们来看看以上代码是如何工作的。在main方法中,我们定义了一个JFrame,然后将面板Jpanel添加到窗体中,该面板包括两个按钮。相应的变量Frame,button1,button2定义在程序的开头部分。

在程序入口main方法中,首先新建Simple1组件,通过构造器建立用户GUI,定义一个面板Jpanle,,增加两个按钮,然后利用JButton.addActionListerner将两个按钮加入到一个活动监听器SimpleLister中,最后,两个按钮添加到面板。当GUI建立后,我们将面板添加到窗体并显示结果。当用户点击按钮时,程序调用actionPerformed方法,通过if语句来判断是哪一个按钮被点击,然后在对话框中显示相应的内容。

利用一个监听器来处理事件的缺点是,当程序比较复杂时,需要一大串的if 语句来实现,程序代码较难阅读与维护。当然,如果处理的事件较少,这种方式比较简单。

通过使用匿名内部类可以解决上述存在的问题。使用简单的匿名内部类作为addActionListener的变量即可。以下是实现代码:


/*
* Simple2.java – 处理事件的第二种方法
* 在这个例子中,利用匿名内部类来监听每一个事件源产生的事件
* 避免使用一些if语句来决定是哪个事件源
*/

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Simple2
{
private static JFrame frame; // 定义为静态变量以便main使用
private static JPanel myPanel; // 该面板用来放置按钮组件
private JButton button1; // 这里定义按钮组件
private JButton button2; // 以便让ActionListener使用

public Simple2() // 构造器, 建立图形界面
{
// 新建面板
myPanel = new JPanel();
// 新建按钮
button1 = new JButton("按钮1"); // 新建按钮1
button2 = new JButton("按钮2");

// 每一个事件源需要一个监听器
// 定义一个匿名内部类来监听事件源产生的事件
button1.addActionListener(
new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
JOptionPane.showMessageDialog(frame,
"按钮1 被点击");
}
}
;

button2.addActionListener(
new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
JOptionPane.showMessageDialog(frame,
"按钮2 被点击");
}
}
;

myPanel.add(button1); // 添加按钮到面板
myPanel.add(button2);
}

public static void main(String s[])
{
Simple2 gui = new Simple2(); // 新建Simple2组件

frame = new JFrame("Simple2"); // 新建JFrame
// 处理关闭事件的通常方法
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e)
{System.exit(0);} });
frame.getContentPane().add(myPanel);
frame.pack();
frame.setVisible(true);
}
}

使用匿名内部类同样存在许多另外的问题。首先,根据组件在代码中被定义的不同位置,类的定义以及处理事件的代码将分散在程序的各个部分,不是集中在一块,同样不便于阅读与维护。各事件的处理全部由嵌套的程序块组成,视觉上很难定位程序代码。如果事件处理程序比较复杂,内部类中的代码将变得很长,你将找不到相应的组件定义位置。最后,当工具栏、菜单栏目等需要处理同样的用户行为时,该方法将使代码更难维护。

我们使用一般的命名内部类可以解决以上许多问题。所有的事件处理方法都集中在一块,并且都具有有意义的名称,程序非常容易阅读与维护。单个的事件处理程序也可以被工具栏、菜单栏等重复使用,

以下是实现代码:


/*
* Simple3.java – 处理事件的第三种方法
* For this example, we will use inner member classes to
* 在这个例子中,利用一般内部类来监听每个事件源产生的事件
* 该方法避免了第二种方法中由于使用匿名内部类而导致的代码混乱
* 便于集中处理事件代码
* 每一个Hander可以被工具栏或菜单多次使用
*/
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Simple3
{
private static JFrame frame; // 定义为静态变量以便main使用
private static JPanel myPanel; // 该面板用来放置按钮组件
private JButton button1; // 这里定义按钮组件
private JButton button2; // 以便让ActionListener使用

// 利用一般内部类来监听每一个事件源产生的事件如(button1, button2)
private class Button1Handler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
JOptionPane.showMessageDialog(frame,
"按钮1 被点击");
}
}

private class Button2Handler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
JOptionPane.showMessageDialog(frame,
"按钮2 被点击");
}
}

public Simple3() // // 构造器, 建立图形界面
{
// 新建面板
myPanel = new JPanel();
// 新建按钮
button1 = new JButton("按钮1"); // 新建按钮1
button2 = new JButton("按钮2");

// 对每一个组件注册监听内部类
button1.addActionListener(new Button1Handler());
button2.addActionListener(new Button2Handler());

myPanel.add(button1); // 添加按钮到面板
myPanel.add(button2);
}

public static void main(String s[])
{
Simple3 gui = new Simple3(); // 新建Simple3组件

frame = new JFrame("Simple3"); // 新建JFrame
// 处理关闭事件的通常方法
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e)
{System.exit(0);} });

frame.getContentPane().add(myPanel);
frame.pack();
frame.setVisible(true);
}
}

以上分析了在Java Swing中三种事件的处理方式,其中利用一般内部类来实现的方法,从代码书写、阅读、维护以及程序的可扩展性角度来看,最为值得推荐供大家使用。

在JAVA程序设计中,事件的处理是非常重要的,尤其是在需要自定义事件和设计JavaBean时.对事件的处理过程有一个完整的认识对于编程是很有帮助的。

下面用一个演示性的例子来说明事件及其处理过程

一.事件的组成
如果想要自定义一个事件,则必须提供一个事件的监听接口以及一个事件类。在JAVA中监听接口继承java.util.EventListener,事件类继承java.util.EventObject.很多基本的事件在编程环境中都已经提供可以很方便使用,但是在自定义事件中必须要要了解这些。

下面是一个事件类的代码,事件类可以向用户处理程序提供被监听类的信息
import java.util.*;
public class PropertyEvent extends EventObject {
public PropertyEvent(){}
}

下面是监听接口的代码
import java.util.*;

public interface PropertyListener extends EventListener {
public void propertyChanged(PropertyEvent propertyEvent);
}

二.事件的处理机制
下面是一段简要的被监听类代码,通过代码分析事件处理过程
import java.util.*;

public class Exam {
private int property;
//listeners用来存放已注册的监听对象
private Set listeners= new HashSet();
…..
public void addListener(PropertyListener propertyListener){
//listeners必须保证只能被一个线程访问
synchronized(listeners){
listeners.add(propertyListener);
}
}
public void firePropertyChange(){
Iterator iterator;
synchronized(listeners){
//将listeners中的类名放到iterator
iterator = new HashSet(listeners).iterator();
}

//创建事件类
PropertyEvent propertyEvent = new PropertyEvent();
while(iterator.hasNext()){
PropertyListener propertyListener = (propertyListener) iterator.next();
//调用用户的事件处理程序
propertyListener.propertyChanged(propertyEvent);
}
}
}

当属性值发生变化时,首先进行内部处理调用firePropertyChange方法,产生一个事件对象然后用事件对象为参数来调用用户的事件处理程序。

三.事件处理的使用
1.基本用法
public Exam exam;
exam.addListener(this);
public void propertyChange(PropertyEvent event){…}

注:exam是被监听对象,this为监听对象,是已经实现了接口方法的当前类,addListener
将当前类注册到listeners.

2.一个被监听对象可以有多个监听对象

exam.addListener(listener1);
exam.addListener(listener2);
这样当exam的property发生变化时,actionListener1和actionListener2的处理程序都会
被调用。当然listener1和listener2必须都是已实现接口方法的类。

3.被监听的对象也可以是实现了方法的接口
exam.addListener(
new PropertyListener(){
//用户定义事件处理过程
public void propertyChange(PropertyEvent event){

}
); 

2005年10月28日

在unix系统中实现自动备份和自动清楚日志文件
————————————————————–
执行备份和清空当前使用的日志文件,clear_countlog.sh
zcatlinux, zcatlinux@yahoo.com.cn , 2004-06-28
—————————————————————

#!/bin/bash
LOGFILE=/var/count.log
DATE=`date +%Y%m%d`

if [ -f $LOGFILE ]
then
cp $LOGFILE $LOGFILE-$DATE
find /var/ -ctime 3 -name count\* -exec rm {} \;
echo "" >$LOGFILE
fi

————————————————-
在crontab中实现每天执行以上脚本一次!
————————————————-

10 3 × × × root /home/clear_countlog.sh


在以上脚本中主要实现的就是循环备份后台清空当前使用的日志,并且保留保留3天的日志

2005年09月07日
作者:isaacxu | 2005年五月03日, 03:03 | 链接 | 评论 (0) | 引用 (0) | 老贴新发

笔者这两年陆陆续续的看了几位专家有关Java开发人员需要阅读的好书籍介绍,现在又看到XP专家Roy Miller有关Java开发人员必备书籍的介绍,觉得有必要将几位专家的意见综合一下,选出几本公认的书来,供各位朋友们参考。专家的意见,见下表:


书名
Kevin Taylor Brian Marick Harshad Oak Roy Miller
1
Thinking in Java, 3rd edition y


y
2
Java In Nutshell y



3
Effective Java: Programming Language Guide y


y
4
Java Examples in a Nutshell y



5
Swing, Second Edition y



6
Java Servlet Programming, 2nd Edition y



7
Enterprise JavaBeans y



8
Expert One-on-One J2EE Development without EJB y



9
Concurrency: State Models & Java Programs y



10
Concurrent Programming in Java: Design Principles and Patterns y


y
11
Refactoring: Improving the Design of Existing Code y
y

y
12
Design Patterns Explained y



13
UML Distilled: A Brief Guide to the Standard Object Modeling Language y


y
14
The Pragmatic Programmer: from Journeyman to Master y
y
y
y
15
Test-Driven Development: By Example y


y
16
Design Patterns: Elements of Reusable Object Oriented Software
y

y
17
Extreme Programming Explained: Embrace Change
y
y

18
Agile Software Development
y


19
Lessons Learned in Software Testing
y


20
Programming Perl
y


21
Working Effectively With Legacy Code
y


22
Structure and Interpretation of Computer Programs
y


23
Ivor Horton’s Beginning Java 2

y

24
A Programmer’s Guide to Java Certification

y

25
Mastering Enterprise JavaBeans

y

26
Head First EJB: Passing the Sun Certified Business Component Developer Exam

y

27
The Career Programmer: Guerilla Tactics for an Imperfect World

y

28
Google Hacks

y

29
Joel on Software

y

30
The Java Programming Language


y
31
Expert One-On-One J2EE Design and Development


y
32
Patterns of Enterprise Application Architecture


y
33
Peopleware: Productive Projects and Teams


y

表1,4位专家推荐的书籍,y代表推荐的书。
从专家推荐的33本书中我们选出至少有两位专家推荐的书,见下表:


书名
Kevin Taylor Brian Marick Harshad Oak Roy Miller
1
Thinking in Java, 3rd edition y


y
2
Effective Java: Programming Language Guide y


y
3
Concurrent Programming in Java: Design Principles and Patterns y


y
4
Refactoring: Improving the Design of Existing Code y
y

y
5
UML Distilled: A Brief Guide to the Standard Object Modeling Language y


y
6
The Pragmatic Programmer: from Journeyman to Master y
y
y
y
7
Test-Driven Development: By Example y


y
8
Design Patterns: Elements of Reusable Object Oriented Software
y

y
9
Extreme Programming Explained: Embrace Change
y
y

表2,至少有两位专家推荐的书
下面我们按推荐度排列一下,再加上Jolt奖这一项,因为Jolt奖也是由几十位专家级的人物评出的,见下表:


书名
Jolt奖
Kevin Taylor Brian Marick Harshad Oak Roy Miller
1
The Pragmatic Programmer: from Journeyman to Master
y
y
y
y
2
Refactoring: Improving the Design of Existing Code
y
y

y
3
Thinking in Java 1998/2002
y


y
4
Effective Java: Programming Language Guide 2001
y


y
5
Concurrent Programming in Java: Design Principles and Patterns
y


y
6
UML Distilled: A Brief Guide to the Standard Object Modeling Language 1997
y


y
7
Test-Driven Development: By Example 2002
y


y
8
Design Patterns: Elements of Reusable Object Oriented Software 1994

y

y
9
Extreme Programming Explained: Embrace Change 1999

y
y

表3,按推荐度大小排列的书
看了上述的排列有何感想,最为专家推崇的是未获的Jolt奖的,1999年出版的,The Pragmatic Programmer: from Journeyman to Master,是不是有点意外?笔者总结前可是真没想到,看起来Jolt奖也不是百分百正确无误的,当然The Pragmatic Programmer的Pragmatic Starter Kit Series还是获得了14届Jolt奖也算是对The Pragmatic Programmer: from Journeyman to Master这本经过时间考验的好书的补偿吧!好了不多讲了,上述9本书可以作为Java开发人员书房必备的经典图书予以推荐。另外,再补充3本得 Jolt奖的Java图书作为参考“:

  • Better,Faster, Lighter Java by Bruce A. Tate andJustin Gehtland(O’Reilly)
  • Java Developer’s Guide to Eclipse, Second Edition by Jim D’Anjou, Scott Fairbrother, Dan Kehn, John Kellerman, Pat McCarthy (Addison-Wesley Professional)
  • Core Java 2, Vol. 1: Fundamentals (6th edition),Cay Horstmann and Gary Cornell

参考资料:
The essential Java language library
Top 10 Must-Read Java Programming Books
Top 5 Must-Read Software Development Books
Ten most influential computer books of the past ten years
The Pragmatic Programmer

2005年09月02日

Eclipse除了可以開發Java之外,還支援了許多語言,現在先介紹
C、C++的開發環境設定,以後有機會再介紹其它的。Enjoy it!

OS:Windows XP Professional SP1
使用版本:Eclipse 2.1.2

一.首先要下載CDT,Eclipse 2.1.2使用者,請下載這項:
CDT 1.2 Full for Windows R2.1.1 1.2.0 GA – Full – Windows。
Eclipse 2.1.3使用者請下載:CDT 1.2.1。
Eclipse 3.0 M7使用者請下載:CDT 2.0 M7。
Eclipse 3.0 M8使用者請下載:CDT 2.0 M8。
Eclipse 3.0 M9使用者請下載:CDT 2.0 M9。
下載網址:http://www.eclipse.org/cdt/

安裝:將解壓縮後的features、plugins整個資料夾複製到Eclipse安裝資料
裡,重新開啟Eclipse即可。

二.下載可在Windows上使用的GNU C、C++編譯器,這裡要下載的是:MinGW。
Download頁面很長的一串,請選擇這個版本:
MinGW bin MinGW-3.1.0-1.exe 14863 kb Sep 15, 2003 11:14
下載網址:http://www.mingw.org/download.shtml

安裝:安裝目錄選C槽,然後狂點下一步(Next)就行了。安裝完後路徑是這
樣->C:\MinGW。

三.先在Command Line模式下測試編譯與執行。先將C:\MinGW\bin底下的
mingw32-make.exe更名為make.exe,因為待會在Eclipse使用時它預設
會抓系統裡make這個檔名而不是mingw32-make。

(註:如果不更名或是還有其他make程式時,也可以在稍後的Eclipse設定
中,在make targets view的地方,新增一個task時,build command 取消
use default , 使用 mingw32-make,或在project properties->make project ->
將make 改為 mingw32-make )
– 由 snpshu 補充。

在環境變數裡加入下列設定:
PATH : C:\MinGW\bin; (如果系統已經有裝其它C/C++編譯器,請把C:\MinGW\bin加在最前面。)
LIBRARY_PATH :C:\MinGW\lib
C_INCLUDE_PATH :C:\MinGW\include
CPLUS_INCLUDE_PATH :C:\MinGW\include\c++\3.2.3;C:\MinGW\include\c++\3.2.3\mingw32;
C:\MinGW\include\c++\3.2.3\backward;C:\MinGW\include

先使用文字編輯器編寫測試用的原始檔,檔名:main.cpp。

1
2
3
4
5
6
7
8
#include <iostream>
using namespace std;
 
int main(void) {
    cout << "Can You Feel My World?" ;
 
    return 0;
}


在Command Line下編譯指令:

1
C:\g++ main.cpp -O3 -o hello


(O3的O是英文大寫"歐")
編譯成功後:便會產生hello.exe的執行檔。
執行畫面如下:

1
2
3
4
5
6
7
8
9
10
Microsoft Windows XP [版本 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.
 
C:\Documents and Settings\Sungo>cd\
 
C:\>g++ main.cpp -O3 -o hello
 
C:\>hello
Can You Feel My World?
C:\>


註:-O3 旗標表示採最高級編譯最佳化,編譯速度最慢,但產生的執行檔
檔案會最小,執行速度會最快;-o 旗標表示將編譯完的*.exe重新更名。

◎步驟一.開啟Eclipse後,首先先開啟C/C++專用視景。
Windows->Open Perspective->C/C++ Development

◎步驟二.建立一個C++用的專案。
File-New->Project->C++->Standard Make C++ Project
(接下來的步驟跟建立一般的Java專案一樣,皆採預設即可)

◎步驟三.把我們剛剛寫的main.cpp import進來,加到專案裡。
File->Import->File System->瀏覽C:\main.cpp

◎步驟四.建立一個makefile。
File->New->File,檔案名稱填:makefile。(不需打副檔名)

makefile內容如下:

1
2
all:
    g++  main.cpp -g -o run


注意:makefile縮排要以Tab鍵作縮排,不能以空格4作縮排,
否則Build會有問題。


◎步驟五.設定Make Targets。
Windows-Show View->Make Targets
在Make Targets視窗裡按滑鼠右鍵,Add Build Target
,name打:編譯。Build Target打:all。

◎步驟六.編譯。
在剛剛建立的Make Targets "編譯" 上點滑鼠2下,即會開始編譯,
此時我們可以發現hello.exe已經產生在我們專案下了。可在底下
C-Build視窗看到以下輸出結果:

1
2
make -k all
g++  main.cpp -g -o run



◎步驟七. *.exe執行前設定。因為在Windows下Run,所以要先作個設定
,請開啟Project->Properties->C/C++ Make Project->Binary Parser頁面。
Binary Parser下拉式選單,將ELF Parser改成PE Windows Parser。

◎步驟八.執行。
Run->Run as->C Local Application。
在底下Consloe視窗看到hello.exe的執行結果。

註:當原始檔有修改,要重新編譯時,只要滑鼠雙擊我們在步驟五
所建立的Make Targets "編譯",即可Rebuilding。

由於CDT的編輯器並無內建Formatter,如果要自動排版C/C++ Source
Code 必須藉由其它排版工具來輔助。這裡我們要使用的是Artistic Style。
它是個短小簡便而且免費的Formatter,除了可以排版C/C++ Source Code
外,你也可以用來排版Java || C#。下載網址:
http://sourceforge.net/projects/astyle

在Eclipse我們必須藉由External Tools來啟動它。

Run->External Tools->External Tools,作以下設定:
name:
C_C++ Formatter

location:
C:\Astyle\astyle.exe (這裡設astyle.exe所在的完整路徑)

Working Directory:
${workspace_loc:/Hello} (請由Browse Button點選)

Arguments:
–style=kr ${resource_name}

做好設定後,當focus在*.cpp檔案上時,點選ToolBar上
的外部工具啟動鈕,就可以排版了。

註:按下排版鈕後,Eclipse會顯示MessageBox提示檔案已變更
,是否要載入,這時請選"Yes"。而Artistic Style有許多的細部
參數可以設定排版效果,可自行參考裡面的說明文件。


2005年09月01日

摘自《程序员》2004年第3期

URL 简介
http://www.linux.org Linux官方新闻和信息网站。
http://freesoft.cei.gov.cn 中国软件行业协会国际自由软件应用研究发展分会的自由软件库,提供各种Linux软件供下载,并有Linux讨论组
http://www.oreilly.com.cn O’Reilly公司中文网站,有许多Linux书籍介绍。
http://www.tldp.org Linux文档仓库,收集的文档包括单独的软件、HOWTO文档和FAQ。
http://www.chinaunix.net Linux在中国最大的论坛,其上有许多的Linux高手。
http://www.china-pub.com 互动出版网,有与Linux相关的书籍600多种。
http://www.xfocus.org 安全焦点,实时Linux安全信息发布。
http://www.embed.com.cn 嵌入开发网,关注Linux嵌入式开发。
http://www.distrowatch.com Linux发行版本站点,上有各种Linux发行版本介绍。
http://www.linuxaid.com.cn Linux社区的专业技术支持网站
http://freshmeat.net Linux软件主要下载站点
http://sourceforge.net Linux软件主要下载站点
http://linuxdrivers.foundries.sourceforge.net Linux硬件驱动程序下载站点
http://www.mydrivers.com Linux硬件驱动程序中文下载站点
http://www.linuxant.com/drivers/ Linux软件调制解调器驱动下载站点
http://www.linuxeden.com Linux最新软件的介绍、下载、新闻、论坛。
http://www.lpi.org LPI认证官方网站
http://www.redhat.com/training/rhce/courses/ RHCE认证官方网站
 
王纲明 软件工程研究所, 北京航空航天大学
2005 年 1 月
在对一个J2EE项目的重构、增加新功能的过程中,对客户端GUI程序,我们使用了State模式。结果显示,该模式的使用,不但减少了客户端GUI程序的程序规模(LOC),而且,该部分的开发及单元测试时间大大减少,同时,在集成测试中发现的缺陷数量比使用该模式前平均减少了3倍。本文就该项目中使用State模式的方式进行介绍。
引言
在分层软件体系结构中,服务端程序关注于实现业务逻辑,客户端程序则包含用户界面。服务端程序由客户端程序调用,其请求、响应模式在设计时已经确定,运行时出现问题的概率较小。相反,客户端程序与用户直接交互,虽然有正确规定的操作顺序或模式,但是用户的操作是不可预知的,程序必须处理各种操作错误、加上数据输入有效验证等要求,使得客户端程序的开发成本上升。
因而,一旦有经过充分测试的、甚至是通过验收的用户交互程序GUI,应该尽可能的重用该GUI,以提高软件的可靠性、可维护性。
在对一个J2EE项目的重构、增加新功能的过程中,对客户端GUI程序,我们使用了State模式。结果显示,该模式的使用,不但减少了客户端GUI程序的程序规模(LOC),而且,该部分的开发及单元测试时间大大减少,同时,在集成测试中发现的缺陷数量比使用该模式前平均减少了3倍。本文就该项目中使用State模式的方式进行介绍。
1. State模式
首先,先简单介绍一下State模式。
该模式是指在对象的内部状态改变时改变对象的行为【1】。其结构如图1所示。
图1 State模式结构

模式中各个参与者职责简介如下:
  • Context:用户对象,拥有一个State类型的成员,以标识对象的当前状态;
  • State:接口或基类,封装与Context的特定状态相关的行为;
  • ConcreteState:接口实现类或子类,实现了一个与Context某个状态相关的行为。
运行时,Context将与状态相关的请求委托给当前的ConcreteState对象处理。关于State模式更详尽的介绍,请参阅参考文献1。
2. 客户端应用
本模式的目标是分离客户端软件中的变化部分与不变部分,以使得变化的部分可独立于不变的部分,有利于扩充新的功能,也有利于维护。
在项目中,对于客户端GUI的重用有两种方式。
  • 方式1适用于:相同数据集合,不同操作模式;此时,在GUI中定义客户端数据处理验证逻辑,不同的状态对象封装了不同的操作模式;
  • 方式2适用于:不同数据集合,相同操作模式;此时,在状态对象中定义客户端数据处理验证逻辑,不同的状态对象封装了不同的数据集合操作。
2.1 类型1: Read-Only & Normal
2.1.1 动机
客户端GUI接受用户输入,经过数据有效性验证,然后将数据传输到服务端,服务端检查业务逻辑有效性,保存数据;但是在特定情况下(比如数据已经存在、且只能经历一次输入),依据客户端GUI所操作数据的状态,业务逻辑要求数据为只读,即客户端只具有显示数据的功能,并不能改变服务器上的数据。
一般地,编程实现时,会在GUI程序中加入判断数据是否应该为只读的逻辑判断语句。如果针对相同的数据集合(Model),有多个客户端GUI(View),就需要在每个程序中加入该判断。如此降低了程序的可维护性,决定数据是否为只读的这样一个业务逻辑将分散在程序中多处,不易维护,在业务逻辑发生改变时,易造成不一致。
我们可以将变化部分(在Normal和Read-Only状态下不同的部分)从GUI中抽取出来,分别用不同的类来表示,这样,当GUI的状态发生改变时,只需要改变其对状态对象的引用即可,状态相关操作委托给状态对象去完成。
2.1.2 适用性
本类型适用环境:相同数据集合,不同操作模式。即特定的GUI根据操作上下文环境,改变其响应模式,根据数据是否可编辑,分为两种状态Read-Only State、 Normal State。
2.1.3 结构(图2)
图2 相同数据集合,不同操作模式
2.1.4 参与者
  • ClientStateChangeable:客户端GUI提供给State对象的操作接口,使得State对象可以访问GUI的成员,以完成该状态相关操作;
    • getChangeableComponents():返回在状态转换为Read-Only时,不可编辑的控件Collection;
    • saveChangeToServer():在可编辑状态时,将客户端数据保存到服务端。
  • AClientGUI:完成图1中的Context功能,即其状态要进行改变的客户端GUI;维护ClientState的一个实例(state);
    • init():将this引用作为参数,调用state.setComponents(this) 以设置可变控件的初始状态;
    • okButtonActionPerformed(e:ActionEvent):用户完成操作后,本方法可作为 "OK"按钮控件的ActionListener方法,调用state.action(this) 以完成本操作。
  • ClientState:State接口,封装与客户端GUI某个特定状态相关的行为;
    • setComponents(gui:ClientStateChangeable):设置参数gui指定的客户端GUI的控件状态;
    • action(gui:ClientStateChangeable):完成数据保存功能。
  • ClientNormalState:可编辑状态子类,实现ClientState接口;
    • setComponents(gui:ClientStateChangeable):调用参数gui指定的客户端GUI的getChangeableComponents()获取控件Collection,然后遍历设置各控件状态为可编辑;
    • action(gui:ClientStateChangeable):调用参数gui指定的客户端GUI的saveChangeToServer()完成数据保存功能。
  • ClientReadOnlyState:只读状态子类,实现ClientState接口;
    • setComponents(gui:ClientStateChangeable):调用参数gui指定的客户端GUI的getChangeableComponents()获取控件Collection,然后遍历设置各控件状态为ReadOnly;
    • action(gui:ClientStateChangeable):空方法,本状态下无数据变化,无须保存。
2.1.5 代码示例
ClientStateChangeable接口:
 
 
public interface ClientStateChangeable {     
        /**
         * To get all changeable components in the GUI object.
         * @return Collection contains Component objects.
         */
        Collection getChageableComponents();
       
        /**
         * To save data to server.
         */
        void saveChangeToServer();
}
AClientGUI类:
 
 
public class AClientGUI extends JPanel implements ClientStateChangeable{
        //…   
        private ClientState state = null;
 
public DesignStep1View(ClientState state){
//…                  
this.state = state;
this.state.setComponents(this);
        }
       
        private void okButton_actionPerformed(ActionEvent e){
               state.action(this);    //save data
        }
 
        public Collection getChageableComponents() {
               Collection dataComponents = new ArrayList();         
               dataComponents.add(jComboBoxESEPattern);
               //…                  
               return dataComponents;
        }
 
        public void saveChangeToServer() {
               //…   
        }
}
ClientState接口:
public interface ClientState { /** * To set components’ state in the GUI. * @param gui a GUI object which implements StateChangeable interface. */ void setComponents(ClientStateChangeable gui); /** * when user click OK-Button in a GUI, the GUI will call this method. * @param gui a GUI object which implements StateChangeable interface. */ void action(ClientStateChangeable gui); }
ClientNormalState类:
 
 
public class ClientNormalState implements ClientState {
        /**
         * 正常状态下, 各个控件默认为可编辑的, 所以不用做任何更改
         */
        public void setComponents(ClientStateChangeable gui) {}
 
        /**
         * 正常状态下, 需要将用户所作修改保存到服务端
         */
        public void action(ClientStateChangeable gui) {
               gui.saveChangeToServer();
        }
}
ClientReadOnlyState类:
 
 
public class ClientReadOnlyState implements ClientState {
        /**
         * 设置GUI的数据控件为Read-Only
         */
public void setComponents(ClientStateChangeable gui) {
               Collection components = gui.getChageableComponents();
               Iterator iter = components.iterator();
               while(iter.hasNext()){
                       JComponent jc = (JComponent)iter.next();
                       jc.setEnabled(false);                
                       String toolTip = jc.getToolTipText();
                       String addedTip = "只读状态";
                       if(toolTip == null)toolTip = addedTip;
                       else toolTip += ". " + addedTip;
                       jc.setToolTipText(toolTip);
               }
        }
 
        /**
         * GUI处于Read-Only状态, 无需将数据保存到server端 
         */
        public void action(ClientStateChangeable gui) {}
}
2.2 类型2:(Reuse GUI)
2.2.1 动机
当多个客户端GUI布局、控件类型很相似,所完成的任务也相似时,只需要经过精心设计,将这些GUI的展示形式统一起来,同一个GUI可以用到多个场景中,达到重用的目的。此时,这些不同任务需要操作不同的数据集合。
可以在GUI类中实现这些不同数据集合的操作,但是这会给程序维护带来麻烦。首先,属于不同逻辑的数据操作出现在同一类文件中,造成逻辑混乱、程序规模增大,不易于调试;其次,要将GUI用于新的数据集合时,只能在相同文件中增加新的代码,此时,该程序的可维护性降低,尤其是新的工作由其他程序员完成时,要理解原有代码是很费力的。
和2.1.1节中提到的解决方法类似:将变化的部分和不变部分分离开来,使得变化的部分可以独立修改、扩充。具体地,则是将数据集合相关操作从GUI程序中抽取出来,定义一个所有数据集合操作的接口(即:状态接口),不同地数据集合操作作为该接口的一个实现类存在。这样,每个数据集合都独立的封装于一个状态对象内;而且,要对新的数据使用该GUI,只需要定义新的状态接口实现类即可,无须修改已有类,甚至不关心已有的状态。
2.2.2 适用性
本类型适用环境:不同的数据集合,相同的操作模式。即不变化的客户端GUI,将不同的数据集合操作委托给变化的状态对象去完成。
2.2.3 结构(图3)
图3 不同数据集合,相同操作模式
2.2.4 参与者
  • InvariableGUI:本身不发生变化的客户端GUI类,维护一个对VariableDataState的引用state,将数据相关操作委托给该引用对象 ;
    • saveChangeToServer():调用state.processData(this)完成数据相关操作;
  • VariableDataState:State接口,封装与数据相关操作的行为;
    • ProcessData(gui:InvariableGUI):调用参数gui的成员获取数据并完成处理;
  • DataState1、DataState2、 … … DataStateN:状态子类,实现VariableDataState接口,封装了特定某一类数据集合的操作,可以根据不同的数据集合定义多个VariableDataState接口的状态类,从而实现了对InvariableGUI的重用。
本类型的实现代码在这里就不列出了,参照2.1.5节中的代码,很容易的就可实现本类型的结构。
2.3 综合以上两种类型
可以将以上两种类型结合起来使用,即实现了客户端软件的数据集合方面对GUI的重用,也实现了操作模式方面对GUI的重用。
程序实现时,可以由GUI类分别维护一个ClientState的引用和一个VariableDataState的引用:
  • 初始化GUI类时,GUI的构造器调用ClientState对象的setComponents()方法设置控件的状态;
  • 用户提交操作时,GUI调用ClientState对象的action()方法,如图2所示,该方法使用传递的gui参数回调GUI的saveChangeToServer()方法,而saveChangeToServer()方法则按照图3所示,调用VariableDataState引用的状态对象的processData()方法完成数据操作。
3 总结
本文介绍的State模式应用于多类型数据、多操作模式的客户端软件,可以取得明显的效果;但如果客户端类和状态都很少时,使用本模式,反而增加了客户端类数量,增加了体系结构的复杂性,此时,可以使用继承方式的类体系来实现重用,无须使用State状态对象的委托操作和回调操作。
参考资料
  1. Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides "Design Patterns: Elements of Reusable Object-Oriented Software", Addison-Wesley, 1995
  2. Bruce Echel, "Thinking in Java", Third Edition, President, MindView, Inc. 2002