通信与网络中的三层交换机测试调试及解决办法


-
测试方式 对二层交换机进行VLAN划分,隔离业务,访问控制内容定义到接入层的路由器上;对三层交换机基于端口划分VLAN、定义网关并进行各业务间的访问控制设定。对交换机进行配置的过程略。 在R2611访问控制内容不变的情况下,对S3526AC进行逐条访问控制命令添加,测试各业务间的隔离情况,各机器之间相互ping通试验。 测试内容 首先,在S3526-AC上添加ACL 100后,PCA与PCB、PCC不通, PCB与PCC通;然后,添加ACL 101后,PCA与PCB通, PCB与PCC不通;最后,添加ACL 102后,PCA、PCB、PCC之间全通。试验隔离不成功。经过多次修改
9KB
三层交换机设置(CiscoTP)
2018-09-02本文提供配置和故障排除步骤可适用对第3层接口的创建。VLAN 会对 LAN 环境中的广播域进行划分。当某个 VLAN 中的主机需要与另一个 VLAN 中的主机进行通信时,必须在这两个 VLAN 之间路由数据流。这就是 VLAN 间路由。在Catalyst交换机上它由第3层接口完成(交换机虚拟接口(SVIs)的)创建。
4.87MB
大容量交换机结构设计及线卡实现---华中科技大学
2013-02-21随着Internet和宽带通信技术的飞速发展,Internet的信息流量和设备的数量以惊人的速度增长,同时传输技术的长足发展使得传输容量大幅度提高。交换机和路由器是架构Internet的主要互连设备,因而对交换机和路由器提出了更高的要求。本论文主要研究了大容量交换机的实现架构,以及交换结构和转发处理等关键技术。在经历了单总线、多总线、共享存储等结构的演变后,基于交换结构的多转发引擎结构成为目前高性能路由器中的主要体系结构。在此基础上,本文提出了通道速率为 1.0625Gb/s 的 128×128 大容量交换机的体系结构,并且详细介绍了交换机线卡的实现和交换背板的组成结构。论文主要对Clos结构进行了研究,其中两级结构是通过折叠Clos结构的第三级而构成的。根据设计要求,我们选择两级交换结构,完成了对128×128多级互连网络的设计。光互连技术结合了光的传输能力和电的逻辑处理能力,非常适合于宽带、高速通信网中骨干节点的交叉连接和交换,因此我们将采用光互连的方式设计交换背板。然后探讨了大容量交换机线卡物理平台的构建,详细介绍了物理平台的主要组成部分,物理层芯片、FPGA 及线卡串行收发器的器件特性和相关配置。本文详细讨论了交换机线卡 FPGA 的设计和具体实现,给出了关键模块的 FPGA 实现并进行了时序仿真验证。最后对交换机线卡进行了电路调试和分析以及下一步工作的计划。
802KB
网络互联技术实验指导书
2009-06-17对于网络互联技术很有用!所有的SISCO配置. 实验一:交叉线的制作 3 实验二:子网划分 5 实验三:交换机基本配置 7 实验四:跨交换机VLAN配置 9 实验五:路由器基本配置 12 实验六:静态路由配置 14 实验七:RIP动态路由配置 18 实验八:HDLC配置 23 实验九:PPP之PAP验证 26 实验十:帧中继配置及调试 29 实验十一:标准访问控制列表配置 32 实验十二:静态NAT地址转换配置 35 实验十三:Vlan间通信—单臂路由配置 38 实验十四:三层交换机配置-Vlan间通信 41 附加实验一:OSPF动态路由配置 44 附加实验二:PPP之CHAP验证 47 附加实验三:ISDN之DDR配置实验 50 附加实验四:扩展访问控制列表配置 54 附加实验五:三层交换机基本配置实验 57 附加实验六:VTP配置 59 附加实验七:RIP Version 2配置非连续子网 63
3KB
软考 网络工程师
2009-08-27软考 分享 试题一 阅读以下有关传统局域网络运行和维护的叙述,将应填入 __(n)__ 处的字句写在答题纸的对应栏内。 在对网络运行及维护前首先要了解网络,包括识别网络对象的硬件情况、判别局域网的拓扑结构和信道访问方式、确定网络互联以及用户负载等。常见的三种拓扑结构是星型、 __(1)__ 与 __(2)__ 拓扑结构,而常见的信道访问方式有 __(3)__ 及 __(4)__ 。 网络配置和运行包括一系列保证局域网络运转的工作,主要有:选择网络操作系统和网络连接协议等;配置网络服务器及网络的外围设备,做好网络突发事件预防和处理;网络安全控制,包括网络安全管理、网络用户权限分配以及病毒的预防处理等。配置网络过程中要做好数据备份工作,一般来说,备份的硬件设备包括磁盘、__(5)__ 和 __(6)__ ,而利用磁盘备份的方法常用的是磁盘镜像 __(7)__ 以及磁盘阵列,其中前两者的区别在于 __(8)__ 。 网络维护是保障网络正常运行的重要方面,主要包括故障检测与排除、网络日常检查及网络升级。一定要建立起 __(9)__ 制度,记录网络运行和变更的情况,以保证维护经验的交流与延续。 试题二 阅读以下有关网络规划的叙述,回答问题1、问题2和问题3,把解答填入答题纸的对应栏内。 网络工程是一项复杂的系统工程,一般可分为网络规划、网络设计、工程实施、系统测试验收和运行维护等几个阶段。网络规划是在需求分析的基础上,进行系统可行性分析和论证,以确定网络总体方案。网络规划阶段任务完成之后转入下一阶段,即网络设计阶段。 [问题1] 简述网络规划阶段需求分析的方法和解决的问题。 (控制在100个字以内) [问题2] 在网络规划阶段“系统可行性分析和论证”的主要内容是什么?(控制在100个字以内) [问题3] 在需求分析过程中应对已有网络的现状及运行情况作调研,如果要在已有的网络上作新的网络建设规划,如何保护用户已有投资? (控制在100个字以内) 试题三 阅读以下有关网络设计的叙述,分析网络结构,回答问题1、问题2和问题3,把解答填入答题纸的对应栏内。 某企业从 20 世纪 50 年代中期开始使用 PC机,历经 3+ 网络、 NOVELL网络的应用,后着手组建企业网络。经过需求分析和论证,设计出如下网络方案。 【问题1】 该企业网络的核心层采用了 ATM 技术,由三台 ATM 交换机互联构成。试对 ATM 网络技术的主要特点、协议分层结构和优点作简要叙述。(控制在 100 个字以内) 【问题2】 PC1~PC4 按 100Mbps 的以太网协议运行,PC1 和 PC2 划分在一个虚拟网之中(VLAN1),PC3 和 PC4 划分在另一个虚拟网之中(VLAN2),试述 PC1 和 PC2 之间 IP 包通信的全过程。(控制在100个字以内) 【问题3】 图中用了两台路由器,Router1,和 Router2,简述路由器的技术特点,并说明 Router1 和 Router2 在本网中的作用。(控制在100个字以内) 试题四 阅读以下有关网络设备安装与调试的叙述,分析设备配置文件,回答问题 1、问题 2 和问题 3,把解答填入答题纸的对应栏内。 现以一台远程访问服务器(RAS,Remote Access Server)Cisco 2509、RJ45 为例来说明。 第一步,准备安装与调试所需的设备,主要包括 RAS---Cisco 2509、RJ45 直通线,RJ45 转 9 针串口转换器、计算机。 第二步,硬件连接,RJ45 直通线一头插入 Cisco 2509 的 console 口,另一头接 RJ45 转 9 针串口转换器,•再将转换器接到计算机的串口。 第三步,RAS加电,在计算机上调用 WINDOWS 98 下的超级终端程序,配置设备连接参数,以便进入Cisco 设备的虚拟操作台。 第四步,输入 Cisco 2509 的 IOS 配置命令。 第五步,将调试完毕的设备连人本地网络,通过拨号验证配置是否正确。 【问题1】 在 RAS 上存在着两个 RJ45 的端口,分别为“console”与“AUX”,请问这两个端口的用途是什么? (控制在100个字以内) 【问题2】 在调用超级终端程序进行设备连接时,应该对设备的连接参数进行正确设置,参数主要包括串口数据传输率、数据位数。停止位数以及是否有奇偶校验。清给出正确的连接参数,以便进入 Cisco 设备的虚拟操作台,进行设备调试。 (控制在100个字以内) 【问题3】 在第四步中,进入虚拟操作台后,在 IOS 环境下输入了如下的配置,请解释【1】~【4】处的标有下划线部分配置命令的含义(“◇”后为配置内容,“★”和“//”后为注释内容) ★ 配置服务器信息 ◇ hostname Cisco 2509 //服务器名称 ◇ enable secret****** //特权口令 ◇ ip domain-manl wxx.edu.cn //设置拨号服务器所属域名 ◇ ip-name-server 202.112.77.2 //设置拨号服务器 DNS 【1】 (此处有3条下划线)
404KB
C++ Primer(第4版)习题解答
2008-06-16C++ Primer(第4版)习题解答
11.11MB
用TCP/IP进行网际互联 第二卷:设计、实现与内核(第三版)(ANSI C版)--详细书签版
2012-10-12CruiseYoung提供的带有详细书签的电子书籍目录 http://blog.csdn.net/fksec/article/details/7888251 用TCP/IP进行网际互联第二卷:设计、实现与内核(第三版) 基本信息 原书名:Internetworking With TCP/IP Vol Ⅱ:Design,Implementation,and Internals Third Edition 作者: (美)Douglas E.Comer David L.Stevens 译者: 张娟 王海 黄述真 丛书名: 国外计算机科学教材系列 出版社:电子工业出版社 ISBN:7505366300 上架时间:2001-5-28 出版日期:2001 年4月 页码:518 版次:1-1 所属分类:计算机 > 计算机网络 > 网络协议 > TCP/IP 教材 > 计算机教材 > 本科/研究生 > 计算机专业教材 > 计算机专业课程 > 计算机网络 内容简介 本书是一部有关计算机网络的经典教科书。它是目前美国大多数大学里所开设的计算机网络课程的主要参考书。目前国内外能见到的各种有关TCP/IP的书籍,其主要内容均出自本书。本书的特点是:强调原理、概念准确、深入浅出、内容丰富且新颖。 全书共分为三卷。第一卷介绍了TCP/IP的基本概念,第二卷在第一卷的基础上,进一步详细讨论了TCP/IP的实现过程,这一卷的突出特点是非常注重实际。本书作者利用程序代码实现了TCP/IP的每一个具体细节,并且所有的代码在书中均可找到,这对于读者深入了解并掌握TCP/IP的细节内容大有帮助。各章之后附有很多习题,内容全面且结合实际。全书最后还有三个附录,分别给出了过程调用交叉参考表、程序代码中用到的c数据结构交叉参考表以及xinu函数和常量。本书可供计算机和通信专业的研究生、高年级本科生作为教科书和学习参考书,也可供各种从事科研的人员参考。 作译者 Douglas Comer博士是TCP/IP协议和因特网的国际公认专家。自20世纪70年代末、80年代初形成因特网以来,他就一直致力于因特网的研究工作,他也是负责指导因特网开发的因特网结构委员会(IAB)的成员,该委员会是确定互联网发展标准的权威机构, 还是CSNET技术委员会的主席和CSNET执行委员会的成员。 Comer为一些公司提供网络设计和实现的咨询,还给全世界的技术和非技术人员开TCP/IP和互联网络的专业讲座。他的操作系统Xinu以及TCP/IP协议的实现在他的书中都有介绍,并且应用到了商业产品中. Comer是Purdue大学计算机科学系的教授,他主要教授计算机网络、互联网络和操作系统的课程,并进行相关的研究.除了撰写一系列畅销的技术书籍外,他还是《Software—PracticecandcExperience》杂志的北美地区编辑.Comer是ACM会员(Fellow). 其他的信息可查询以下网址:www.cs.purdue.deu/people/comer 目录 封面 -24 封底 -23 书名 -22 版权 -21 出版说明 -20 前言 -17 序言 -16 目录 -14 第1章 引言与概述 1 1.1 TCP/IP协议 1 1.2 了解细节的必要性 1 1.3 协议间交互作用的复杂性 1 1.4 本书采用的方法 2 1.5 研究代码的重要性 2 1.6 Xinu操作系统 2 1.7 本书其余部分的组织 3 1.8 小结 3 深入研究 4 第2章 操作系统中的TCP/IP软件结构 5 2.1 引言 5 2.2 进程的概念 5 2.3 进程的优先级 6 2.4 进程的同步通信 6 2.5 进程间通信 8 2.5.1 端口 8 2.5.2 消息传送 9 2.6 设备驱动程序和输入、输出程序 9 2.7 网络的输入和中断 10 2.8 向高层协议传递分组 11 2.9 IP协议与传输协议之间的数据报传递 12 2.9.1 将传入的数据报发送给TCP 12 2.9.2 将传入的数据报发送给UDP 12 2.10 向应用程序的传递操作 13 2.11 输出时的信息流 13 2.12 从TCP经过IP到网络输出 14 2.13 UDP输出处理 15 2.14 小结 15 深入研究 16 习题 16 第3章 网络接口层 18 3.1 引言 18 3.2 网络接口抽象模型 18 3.2.1 接口数据结构 18 3.3 以太网的基本定义 21 3.3.1 应用中的统计数据 24 3.4 接口的逻辑状态 24 3.5 本地主机接口 24 3.6 缓冲区管理 25 3.6.1 大缓冲区方案 25 3.6.2 链表方案(mbuf) 26 3.6.3 方案举例 26 3.6.4 有关缓冲区的其他议题 26 3.7 传入分组的多路分解 27 3.8 小结 28 深入研究 29 习题 29 第4章 地址的发现及绑定(ARP) 30 4.1 引言 30 4.2 ARP软件在理论上的结构 30 4.3 ARP设计方案举例 30 4.4 ARP高速缓存的数据结构 31 4.5 ARP输出处理 34 4.5.1 搜索ARP高速缓存 34 4.5.2 ARP请求分组的广播 35 4.5.3 输出过程 36 4.6 ARP输入处理 38 4.6.1 向表中增加已转换的表项 38 4.6.2 发送等待发送的分组 39 4.6.3 ARP输入过程 40 4.7 ARP高速缓存的管理 42 4.7.1 高速缓存表项的分配 42 4.7.2 高速缓存的定期维护管理 43 4.7.3 释放队列中的分组 44 4.8 ARP初始化 45 4.9 ARP参数配置 46 4.10 小结 46 深入研究 47 习题 47 第5章 IP:软件的总体结构 48 5.1 引言 48 5.2 中心环节 48 5.3 IP软件设计思想 48 5.4 IP软件结构和数据报流程 49 5.4.1 选择传入数据报的策略 49 5.4.2 允许IP进程被阻塞 51 5.4.3 IP使用的常量的定义 54 5.4.4 校验和的计算 57 5.4.5 处理定向广播 57 5.4.6 识别一个广播地址 59 5.5 IP首部中的字节顺序 60 5.6 向IP发送数据报 61 5.6.1 发送本地生成的数据报 61 5.6.2 发送传入数据报 63 5.7 表格的维护 63 5.8 小结 65 深入研究 65 习题 65 第6章 IP:选路表和选路算法 67 6.1 引言 67 6.2 路由维护和查找 67 6.3 选路表结构 67 6.4 选路表数据结构 68 6.5 路由的生成源及保持时间 70 6.6 为数据报选择路由 70 6.6.1 实用过程 70 6.6.2 获得一个路由 73 6.6.3 数据结构初始化 74 6.7 选路表的定期维护 75 6.7.1 增加路由 77 6.7.2 删除路由 80 6.8 IP选项处理 82 6.9 小结 83 深入研究 83 习题 84 第7章 IP:分片与重组 85 7.1 引言 85 7.2 数据报的分片 85 7.2.1 为一个数据报片再次分片 85 7.3 分片的实现 85 7.3.1 发送一个数据报片 87 7.3.2 复制数据报首部 89 7.4 数据报的重组 90 7.4.1 数据结构 90 7.4.2 互斥操作 91 7.4.3 在链表中加入一个数据报片 91 7.4.4 溢出时的丢弃 93 7.4.5 测试一个完整的数据报 94 7.4.6 将数据报片组装成完整的数据报 96 7.5 数据报片链表的维护管理 97 7.6 初始化 99 7.7 小结 99 深入研究 100 习题 100 第8章 IP:差错处理(ICMP) 101 8.1 引言 101 8.2 ICMP报文格式 101 8.3 ICMP报文的实现 101 8.4 传入ICMP报文的处理 103 8.5 ICMP重定向报文的处理 105 8.6 设置子网掩码 107 8.7 为一个ICMP分组选择源地址 108 8.8 生成ICMP差错报文 109 8.9 避免出现关于差错报文的差错报文 111 8.10 为ICMP报文分配缓冲区 112 8.11 ICMP报文中的数据部分 114 8.12 ICMP重定向报文的生成 116 8.13 小结 117 深入研究 117 习题 117 第9章 IP:组播处理(IGMP) 119 9.1 引言 119 9.2 维护组播主机群的成员信息 119 9.3 主机群表 119 9.4 查找一个主机群 121 9.5 向主机群表中增加一个表项 122 9.6 为一个组播地址设置网络接口 124 9.7 IP组播地址和硬件组播地址之间的转换 125 9.8 从主机群表中删除一个组播地址 126 9.9 加入一个主机群 127 9.10 维持与一个组播路由器的联系 129 9.11 IGMP成员关系报告的实现 130 9.12 计算随机时延 131 9.13 发送IGMP报告的进程 132 9.14 处理输入的IGMP报文 133 9.15 脱离主机群 134 9.16 IGMP数据结构的初始化 136 9.17 小结 137 深入研究 137 习题 137 第10章 UDP:用户数据报 138 10.1 引言 138 10.2 UDP端口和多路分解处理 138 10.2.1 成对通信使用的端口 138 10.2.2 多对一通信使用的端口 138 10.2.3 操作模式 139 10.2.4 多路分解处理中的细节问题 139 10.3 UDP的输入处理 141 10.3.1 UDP数据结构的说明 141 10.3.2 传入数据报队列的说明 142 10.3.3 UDP端口号与队列的映射 144 10.3.4 分配空闲队列 144 10.3.5 网络字节顺序与本机字节顺序之间的相互转换 145 10.3.6 处理一个已到达的数据报 146 10.3.7 UDP校验和的计算 148 10.4 UDP输出的处理 149 10.4.1 一个UDP数据报的发送 150 10.5 小结 151 深入研究 152 习题 152 第11章 TCP:数据结构和输入处理 153 11.1 引言 153 11.2 TCP软件概览 153 11.3 传输控制块 153 11.4 TCP报文段格式 158 11.5 序列空间中的比较 159 11.6 TCP有限状态机 160 11.7 状态变迁举例 160 11.8 有限状态机的说明 161 11.9 TCB的分配及初始化 163 11.9.1 分配一个TCB 163 11.9.2 释放一个TCB 164 11.10 有限状态机的实现 165 11.11 处理一个输入报文段 165 11.11.1 将TCP首部转换为本地字节顺序 167 11.11.2 计算TCP的校验和 168 11.11.3 为报文段查找对应的TCB 169 11.11.4 检查报文段的有效性 171 11.11.5 为当前状态选择一个过程 172 11.12 小结 173 深入研究 173 习题 173 第12章 TCP:有限状态机的实现 175 12.1 引言 175 12.2 CLOSED状态处理 175 12.3 从容关闭 175 12.4 关闭后的延迟计时 176 12.5 TIME-WAIT状态处理 177 12.6 CLOSING状态处理 178 12.7 FIN-WAIT-2状态处理 179 12.8 FIN-WAIT-1状态处理 180 12.9 CLOSE-WAIT状态处理 181 12.10 LAST-ACK状态处理 182 12.11 ESTABLISHED状态处理 183 12.12 处理报文段中的紧急数据 184 12.13 处理报文段中的其他数据 186 12.14 经常注意已接收的八位组 188 12.15 终止一个TCP连接 190 12.16 建立TCP连接 191 12.17 初始化TCB 191 12.18 SYN-SENT状态处理 193 12.19 SYN-RECEIVED状态处理 194 12.20 LISTEN状态处理 196 12.21 为一个新TCB初始化窗口变量 197 12.22 小结 199 深入研究 199 习题 199 第13章 TCP:输出处理 200 13.1 引言 200 13.2 TCP输出控制的复杂性 200 13.3 TCP输出的四种状态 200 13.4 作为一个进程的TCP输出 201 13.5 TCP输出报文 201 13.6 对输出状态和TCB编号的编码 202 13.7 TCP输出进程的实现 202 13.8 互斥操作 203 13.9 IDLE状态的实现 203 13.10 PERSIST状态的实现 204 13.11 TRANSMIT状态的实现 205 13.12 RETRANSMIT(重发)状态的实现 206 13.13 发送一个报文段 206 13.14 计算TCP数据长度 210 13.15 序号计算 210 13.16 其他TCP过程 211 13.16.1 发送复位 211 13.16.2 转换成网络字节顺序 213 13.16.3 等待输出缓冲空间 213 13.16.4 唤醒等待TCB的进程 214 13.16.5 选择初始序号 216 13.17 小结 217 深入研究 217 习题 217 第14章 定时器管理 218 14.1 引言 218 14.2 定时事件的通用数据结构 218 14.3 TCP事件使用的数据结构 219 14.4 定时器、事件和报文 220 14.5 TCP定时器进程 220 14.6 删除TCP定时器事件 222 14.7 删除一个TCB的所有事件 223 14.8 确定出现一个事件的尚需时间 224 14.9 插入TCP定时器事件 225 14.10 启动无时延的TCP输出 227 14.11 小结 227 深入研究 228 习题 228 第15章 流量控制和自适应重发 229 15.1 引言 229 15.2 自适应重发中的难题 229 15.3 自适应重发的调整 229 15.4 重发定时器和退避 230 15.4.1 Karn算法 230 15.4.2 重发输出状态的处理 230 15.5 基于窗口的流量控制 231 15.5.1 糊涂窗口综合症 232 15.5.2 接收方预防糊涂窗口 232 15.5.3 零窗口之后的性能优化 233 15.5.4 调整发送方的窗口 233 15.6 最大报文段长度的计算 235 15.6.1 发送方的最大报文段长度 235 15.6.2 选项处理 236 15.6.3 通告一个最大输入报文段长度 238 15.7 网络拥塞预防与控制 239 15.7.1 成倍递减法 239 15.8 慢启动和拥塞预防 239 15.8.1 慢启动 239 15.8.2 超过上限后减速递增 240 15.8.3 递增拥塞窗口尺寸的实现 240 15.9 平均往返时间估值及超时 242 15.9.1 一种快速平均值更新算法 242 15.9.2 传入确认的处理 243 15.9.3 为窗口外的数据创建确认报文段 245 15.9.4 接收到一个确认后改变输出状态 246 15.10 技巧和注意事项 247 15.11 小结 247 深入研究 248 习题 248 第16章 紧急数据处理和推功能 250 16.1 引言 250 16.2 带外信令 250 16.3 紧急数据 250 16.4 标准的解释 250 16.4.1 带外数据的解释法 251 16.4.2 数据标记解释法 252 16.5 为Berkeley紧急指针解释法而进行的配置 252 16.6 通知应用程序 253 16.6.1 多个并发应用程序 253 16.7 从TCP中读取数据 254 16.8 发送紧急数据 255 16.9 TCP的推功能 256 16.10 在失序交付时对推数据的解释 257 16.11 输入时推功能的实现 257 16.12 小结 258 深入研究 258 习题 259 第17章 套接层的接口 260 17.1 引言 260 17.2 通过设备形成的接口 260 17.2.1 单字节I/O 261 17.2.2 其他一些非传送的函数 261 17.3 作为设备的TCP连接 262 17.4 TCP客户程序举例 262 17.5 TCP服务器程序举例 263 17.6 TCP主设备的实现 265 17.6.1 TCP主设备打开功能 265 17.6.2 被动TCP连接的形成 266 17.6.3 主动TCP连接的形成 267 17.6.4 分配一个未使用的本地端口 268 17.6.5 主动连接的完成 269 17.6.6 TCP主设备的控制 271 17.7 TCP从设备的实现 271 17.7.1 由TCP从设备输入数据 271 17.7.2 由TCP从设备输入单字节 273 17.7.3 通过TCP从设备的输出 274 17.7.4 TCP连接的关闭 276 17.7.5 TCP从设备的控制操作 278 17.7.6 接受来自被动设备的连接 279 17.7.7 改变LISTEN队列的长度 279 17.7.8 获取从设备中的统计数据 280 17.7.9 设置或清除TCP选项 282 17.8 从设备的初始化 283 17.9 小结 284 深入研究 284 习题 284 第18章 RIP:主动路由传播和被动获取 286 18.1 引言 286 18.2 主动和被动模式的参与者 286 18.3 基本的RIP算法和费用度量 286 18.4 不稳定性及解决方案 287 18.4.1 计数到无穷大 287 18.4.2 网关瘫痪和路由超时 288 18.4.3 水平划分 288 18.4.4 毒性逆转 288 18.4.5 具有毒性逆转的路由超时 289 18.4.6 触发更新 289 18.4.7 随机化以防止广播风暴 289 18.5 报文类型 290 18.6 协议特性 290 18.7 RIP的具体实现 291 18.7.1 实现的两种形式 291 18.7.2 定义 291 18.7.3 输出的理论结构 293 18.8 基本RIP进程 294 18.8.1 “必须为零”的字段内容必须为零 295 18.8.2 处理一个传入响应 296 18.8.3 在更新期间的锁定 298 18.8.4 验证一个地址 298 18.9 对输入请求的响应 299 18.10 生成更新报文 300 18.11 初始化一个更新报文的副本 301 18.11.1 向更新报文的副本中添加路由 302 18.11.2 计算一个待通告的费用值 304 18.11.3 为RIP报文分配数据报 305 18.12 生成定期的RIP输出 306 18.13 RIP的局限性 307 18.14 小结 307 深入研究 307 习题 307 第19章 OSPF:使用SPF算法的路由传播 308 19.1 引言 308 19.2 OSPF配置和选项 308 19.3 OSPF的图论模型 308 19.4 OSPF的说明 311 19.4.1 OSPF分组格式的说明 311 19.4.2 OSPF接口说明 313 19.4.3 全局常量和数据结构的说明 314 19.5 邻接关系和链路状态信息的传播 316 19.6 用Hello发现相邻网关 317 19.7 Hello分组的发送 319 19.7.1 Hello分组的一个模板 320 19.7.2 Hello分组输出进程 321 19.8 指定路由器 323 19.9 选出一个指定路由器 324 19.10 变动之后重建邻接关系 327 19.11 处理到达的Hello分组 329 19.12 在相邻网关表中增加一个网关 331 19.13 相邻网关状态的变迁 332 19.14 OSPF定时器事件和重发 333 19.15 判断是否允许邻接关系 335 19.16 OSPF输入的处理 336 19.17 链路状态处理中的说明和过程 339 19.18 数据库描述分组的生成 341 19.19 创建一个模板 343 19.20 传送数据库描述分组 344 19.21 处理到达的数据库描述分组 345 19.21.1 处理EXSTART状态下的分组 347 19.21.2 处理EXCHNG状态下的分组 348 19.21.3 处理FULL状态下的分组 349 19.22 处理链路状态请求分组 350 19.23 建立链路状态概要信息 352 19.24 OSPF实用过程 354 19.25 小结 356 深入研究 357 习题 357 第20章 SNMP:MIB变量、表示形式和绑定 358 20.1 引言 358 20.2 服务器的组织和名字的映射 358 20.3 MIB变量 359 20.3.1 表格中的字段 359 20.4 MIB变量名 360 20.4.1 变量名的数字表示形式 360 20.5 名字之间的字典顺序 360 20.6 除去前缀 361 20.7 在MIB变量上执行的操作 361 20.8 表格名 362 20.9 名字体系概念上的线索 362 20.10 MIB变量的数据结构 363 20.10.1 使用独立的函数完成操作 365 20.11 用于快速查找的数据结构 365 20.12 散列表的实现 366 20.13 MIB绑定的描述 366 20.14 绑定中使用的内部变量 373 20.15 散列表的查找 374 20.16 SNMP的结构和常量 376 20.17 ASN.1表示形式的处理 382 20.17.1 长度表示法 382 20.17.2 将整数转换为ASN.1格式 384 20.17.3 将对象标识符转换为ASN.1格式 386 20.17.4 用于转换对象值的例程 389 20.18 小结 391 深入研究 391 习题 392 第21章 SNMP:客户与服务器 393 21.1 引言 393 21.2 服务器中数据的表示形式 393 21.3 服务器的实现 393 21.4 对SNMP报文的分析 396 21.5 绑定链表中ASN.1名字的转换 400 21.6 解析一个请求 401 21.7 对get-next操作的解释 404 21.8 操作的间接执行 404 21.9 表格的间接寻址 407 21.10 应答报文的反向生成 408 21.11 将内部格式转换为ASN.1表示形式 410 21.12 服务器使用的实用函数 412 21.13 一个SNMP客户的实现 413 21.14 变量初始化 415 21.15 小结 417 深入研究 417 习题 417 第22章 SNMP:表格访问函数 419 22.1 引言 419 22.2 表格访问 419 22.3 表格的对象标识符 419 22.4 地址人口表函数 420 22.4.1 对地址人口表的get操作 421 22.4.2 对地址人口表的get-first操作 423 22.4.3 对地址人口表的get-next操作 424 22.4.4 地址入口表中的递增搜索 425 22.4.5 对地址人口表的set操作 426 22.5 地址转换表函数 426 22.5.1 对地址转换表的get操作 428 22.5.2 对地址转换表的get-first操作 429 22.5.3 对地址转换表的get_next操作 431 22.5.4 地址转换表中的递增搜索 432 22.5.5 乱中有序 433 22.5.6 对地址转换表的set操作 434 22.6 网络接口表的函数 435 22.6.1 接口表标识符的匹配 435 22.6.2 对网络接口表的get操作 436 22.6.3 对网络接口表的get-first操作 440 22.6.4 对网络接口表的get-next操作 441 22.6.5 对网络接口表的set操作 442 22.7 选路表函数 443 22.7.1 对选路表的get操作 444 22.7.2 对选路表的get-first操作 446 22.7.3 对选路表的get-next操作 447 22.7.4 选路表中的递增搜索 449 22.7.5 对选路表的set操作 450 22.8 TCP连接表函数 452 22.8.1 对TCP连接表的get操作 453 22.8.2 对TCP连接表的get-first操作 455 22.8.3 对TCP连接表的get-next操作 456 22.8.4 TCP连接表中的递增搜索 457 22.8.5 对TCP连接表的set操作 458 22.9 UDP Listener表 460 22.9.1 对UDP Listener表的get操作 461 22.9.2 对UDP Listener表的get-first操作 462 22.9.3 对UDP Listener表的get-next操作 463 22.9.4 UDP Listener表中的递增搜索 464 22.9.5 对UDP Listener表的set操作 465 22.10 IP地址转换的实用例程 466 22.11 小结 467 深入研究 467 习题 468 第23章 实现的回顾 469 23.1 引言 469 23.2 程序代码统计分析 469 23.3 各个协议的程序代码行数 469 23.4 每个协议所需的函数和过程 470 23.5 小结 471 习题 471 附录1 过程调用交叉参考表 473 附录2 程序代码中使用到的C数据结构交叉参考表 493 附录3 程序代码中使用到的Xinu函数和常量 498 参考文献 513 附录页 前言 很荣幸Doug Comer给我这个机会,让我能在他的这本书第三版付印时与诸位探讨一些个人的想法。在过去的十年期间,Internet以惊人的速度发展着。Internet上主机的数目从1989年的100000台发展到1998年的30000 000台。在本书第二版出版的时候,连接到Intemet的网络大约有26000个。到1998年,这个数字大约在200000到350000之间,这还不包括那些利用互联网技术但未连接到公共网络上的专用内联网。 除了规模上的变化,Internet在应用上也发生了惊人的变化。Internet技术与正在蓬勃发展的WWW作为一个完整的、不可缺少的系统已被人们所认可。而WWW在商业、学术和政府等部门都掀起了一场革命。在WWW上每天出现的“网页”可达3.2亿之多,并且还在不断涌现。有些学校大约四分之一的入学申请都是通过电子邮件或其他网页申请形式进行的。Dell公司透露,他们通过网站每天销售价值大约600万美元的PC机。Amazon网上书店以季度盈利达6600万美元而成为历史上发展最快的公司,这也是第一个在一年之内销售额超过2.5亿美元的公司。 目前,至少有两千多家无线电台将其音频服务推向了Internet,许多网站也开始提供音频甚至低质量的视频服务。随着访问速度提高到兆比特的范围以及主干网容量的增加,视频服务质量也将得到提高。 随着1996年网络电视和1997年Nokia推出的支持网络功能的手机的问世,其他支持网络功能的产品也不断地涌现。更多的实用产品实现了相似的网络功能,例如水暖加热器既可以由家用电脑来控制,也可以由电力公司来控制,以满足适度的调峰需求。尽管智能代理还没有成为主流,但是XML近来的发展也表明Internet上有关事务处理的应用正在急剧增多。SML的标准“文档”表示方式以及一致的常用解释格式构成了一种可传输对象,该对象已形成所有商业、金融事务、数据库事务及其他需要标准表示方式和解释格式的交易的基础。 再回头看看电话产品的发展,无论传统的服务商还是现代企业都在将支持网络的电话产品推向市场。“Soft PBX”系统利用LAN和Internet实现传统的专用交换机的功能,基于微处理器的电话机也正在改变远程通信的经济情况和可操作性。具有IP功能的传真机已经出现。网关把原有的模拟世界与现在的Intemet世界联系在一起,起着非常重要的作用。 对Internet服务需求量的增长速度赶上甚至超过了网络本身的增长。主干网络的速度达到622MM/s已是很平常的事。利用硬件IP交换机的新一代路由器可望以10Gb/s(OC192)以上的速度处理Internet分组。下一个挑战是利用单模光纤处理每秒兆兆字节的信息量。 网络的安全性过去总是处于后台操作状态,随着更多领域依赖于Internet,安全性已逐渐走向前台。防火墙技术、端—端加密、密钥管理、证书系统和鉴别系统已成为成功管理Internet的关键因素。 再向未来展望,网络协议和结构已经朝着星际互联的方向发展,也许会形成和互联网一样的形式。域名系统如果还存在,还要考虑将不同的行星计人命名体系。“互联的Internet”协议将能够处理更高的延迟,传统的TCP概念也将被更多的单向过程取代。未来的网际邀游者回看这十年,一定会觉得这是个充满挑战但技术相对落后的时代。在他们看来,这段前言中的观点可能已经过时又难以理解,因为他们所面对的是经历了另外二十年变革的Internet。 Vinton Cerf Camelot Northern Virginia 序言 用TCP/IP进行网际互联第二卷提供了第一卷所没有包含的关于TCP/IP协议的一些细节问题。第二卷如同将TCP/IP置于放大镜下,考察每个协议的具体细节。它讨论了协议的实现方案,并着重于介绍协议软件的内部机制。第三版包含了对某些协议的修改和更新的内容。其中的代码改用C++语言的ANSI标准C子集,包含了函数原型和参数的声明。另外还纠正了一些错误。我们将SNMP更新为SNMPv2,其中包括替换了地址转换表,增加了UDP listener列表。在本书的最后,还增加了附录2,给出了书中代码所用到的主要数据结构的声明及变量的交叉参考表,并扩充了附录1中的过程调用交叉参考的内容。 书中的范例代码用Gnu C++编译器在Intel体系平台编译通过,也在Pentium TM系统上经过测试。所有的代码均可在以下网址得到: ftP://ftp.cs.purdue.edu/pub/comer/TCPIP—vol2.dist.tar.Z 虽然本书受到版权保护,但书中的代码可供读者使用,而且已经在许多商业产品中实现。这些代码使用的惟一限制是不得在公开出版物中出版。 我们鼓励读者利用计算机工具来查看、修改、编译和测试这些代码。事实上,尽管附录1和附录2中提供了定位代码条目的有效途径,但在查看大段代码时,UNIXgrep程序的价值更是不可估量。 对于各种正式的协议规范,以及对协议的实现和使用的讨论,可参见请求评论文档(RFC)。尽管一些RFC文档对初学者来讲难以理解,但这些文档是信息详尽的权威性资源,没有哪个作者能够做到在自己编写的书中包含RFC文档中的所有内容。尽管RFC文档涉及了每一个协议,但有时它们对协议之间的交互问题并未加以说明。例如,选路信息协议(RIP或OSPF)之类的选路协议规定了网关如何将路由置人IP路由表中,以及如何将表中的路由通知其他网关。RIP还规定路由必须设立定时机制,一旦某条路由超时,就将其删除。但是,我们在RFC文档中并不容易看出RIP和其他协议之间是如何交互作用的,随之而来的问题是:“路由超时机制将如何影响路由表中那些由ICMP设置的路由呢?”我们可能还会考虑这样一个问题:“当RIP更新路由时,应不应该推翻那些由管理员直接输入的路由呢?” 为了有助于解释协议之间的交互作用,并确保我们的方案能协调工作,我们设计并构造了一个工作系统,作为全书的中心范例。该系统提供了TCP/IP协议族中的大部分协议,包括:TCP、IP、ICMP、IGMP、UDP、ARP、RIP、SNMPv2以及OSPF的主要部分。另外,该系统还有一个finger服务的客户和服务器范例。由于本书包括了每一个协议的程序代码,读者可以研究其实现方法并了解其内部结构。最重要的是,由于范例系统将所有协议软件集成为一个工作整体,读者可以清楚地了解协议之间的交互作用。 范例的程序代码试图做到一方面遵守协议标准,另一方面引入一些新的思想。例如,我们的TCP程序代码中包含了“糊涂窗口预防”技术、Jacobson—Karels的“慢启动”和“拥塞预防”等优化技术,诸如此类的性能可能在商业实现中被忽略。但同时我们也清楚地认识到商业领域并不总是遵从已经公布的标准,因此我们也努力将系统调整到能够在现实环境中使用。例如,程序代码中包含了一个配置参数,使得它既可以采纳Internet标准,也可以采纳BSD UNIX中“TCP紧急数据指针”的实现方法。 我们并不认为书中所提供的程序代码都是准确无误的,甚至不能断言它肯定比其他实现方法要好。事实上,经过多年使用,我们仍然在不断完善这套软件,同时,也希望读者跟我们一起继续改进它。 本书可以作为网络工程人员的高级教程或者作为研究生教材使用。在作为本科教程使用时,应将重点放在前几章,而忽略有关OSPF、SNMP和RIP这几章内容。研究生可能会在有关TCP的章节中发现一些最为有趣和最难理解的概念。为保证其高性能而采用的自适应重发和相关的试探法尤为重要,应当仔细加以研究。纵观全书,绝大部分习题都向大家提示了其他可选择的实现方案及其大致情况,这些内容并不要求死记硬背,学生们可能需要阅读本书以外的其他资料,才能解答这些习题。 正如任何耗费甚巨的工程一样,本书中包含了许多人的心血,对此我们表示衷心的感谢。作者.之一David Stevens完成了大部分软件的编制工作,其中包括一个完整的TCP版本。Shawn Ostermann为本书付出了许多努力。Shawn将TCPAP代码集成到xinu版本8中,并将其从最初的Sun 3平台移植到DECstion 3100上。在这一版本中,他还对上一版中由Vic Norman编写的SNMP软件进行了大量的修改,以符合第二版的标准并有助于代码的调试。John Lin对书中的技术细节进行了校对,纠正了一些错误。 珀杜(Purdue)大学的网际互联研究小组(Internetworking Research Group)的许多成员对程序代码的早期版本做过很多贡献。Andy Muckelbauer和Steve Chapin建立了一个UNIX兼容库,并与Shawn Ostermann和Scott Mark合作,使用TCP代码运行一个X window服务器。他们对TCP做了大量的测试工作,并指出其性能上存在的几个问题。Scott M.Ballew参与了一些软件的开发工作。 我的妻子Christine对本书手稿进行了编辑,并提出了许多建议。最后,我们感谢珀杜大学的计算机科学系和计算个L1所提供的帮助。 Douglas E.Comer David L.Stevens
178KB
华为编程开发规范与案例
2008-09-04软件编程规范培训实例与练习 软件编程规范培训实例与练习 问题分类 1 逻辑类问题(A类)-指设计、编码中出现的计算正确性和一致性、程序逻辑控制等方面出现的问题,在系统中起关键作用,将导致软件死机、功能正常实现等严重问题; 接口类问题(B类)-指设计、编码中出现的函数和环境、其他函数、全局/局部变量或数据变量之间的数据/控制传输不匹配的问题,在系统中起重要作用,将导致模块间配合失效等严重问题; 维护类问题(C类)-指设计、编码中出现的对软件系统的维护方便程度造成影响的问题,在系统中不起关键作用,但对系统后期维护造成不便或导致维护费用上升; 可测试性问题(D类)-指设计、编码中因考虑不周而导致后期系统可测试性差的问题。 处罚办法 问题发生率: P=D/S D=DA+0.5DB+0.25DC 其中: P -问题发生率 D -1个季度内错误总数 DA -1个季度内A类错误总数 DB -1个季度内B类错误总数 DC -1个季度内C类错误总数 S -1个季度内收到问题报告单总数 1)当D≥3时,如果P≥3%,将进行警告处理,并予以公告; 2)当D≥5时,如果P≥5%,将进行罚款处理,并予以公告。 目 录 一、逻辑类代码问题 第5页 1、变量/指针在使用前就必须初始化 第5页 【案例1.1.1】 第5页 2、防止指针/数组操作越界 第5页 【案例1.2.1】 第5页 【案例1.2.2】 第6页 【案例1.2.3】 第7页 【案例1.2.4】 第8页 3、避免指针的非法引用 第9页 【案例1.3.1】 第9页 4、变量类型定义错误 第10页 【案例1.4.1】 第10页 5、正确使用逻辑与&&、屏蔽&操作符 第17页 【案例1.5.1】 第17页 6、注意数据类型的匹配 第18页 【案例1.6.1】 第18页 【案例1.6.2】 第18页 7、用于控制条件转移的表达式及取值范围是否书写正确 第20页 【案例1.7.1】 第20页 【案例1.7.2】 第21页 【案例1.7.3】 第22页 8、条件分支处理是否有遗漏 第24页 【案例1.8.1】 第24页 9、引用已释放的资源 第26页 【案例1.9.1】 第26页 10、分配资源是否已正确释放 第28页 【案例1.10.1】 第28页 【案例1.10.2】 第29页 【案例1.10.3】 第30页 【案例1.10.4】 第32页 【案例1.10.5】 第33页 【案例1.10.6】 第35页 【案例1.10.7】 第38页 11、防止资源的重复释放 第39页 【案例1.11.1】 第39页 12、公共资源的互斥性和竞用性 第40页 【案例1.12.1】 第40页 【案例1.12.2】 第40页 二、接口类代码问题 第43页 1、对函数参数进行有效性检查 第43页 【案例2.1.1】 第43页 【案例2.1.2】 第43页 【案例2.1.3】 第44页 【案例2.1.4】 第46页 【案例2.1.5】 第47页 【案例2.1.6】 第48页 2、注意多出口函数的处理 第49页 【案例2.2.1】 第49页 三、维护类代码问题 第51页 1、 统一枚举类型的使用 第51页 【案例3.1.1】 第51页 2、 注释量至少占代码总量的20% 第51页 【案例3.2.1】对XXX产品BAM某版本部分代码注释量的统计 第51页 四、产品兼容性问题 第52页 1、系统配置、命令方式 第52页 【案例4.1.1】 第52页 【案例4.1.2】 第53页 2、设备对接 第54页 【案例4.2.1】 第54页 3、其他 第55页 【案例4.3.1】 第55页 五、版本控制问题 第58页 1、新老代码中同一全局变量不一致 第58页 【案例5.1.1】 第58页 六、可测试性代码问题 第59页 1、调试信息/打印信息的正确性 第59页 【案例6.1.1】 第59页 一、逻辑类代码问题 1、变量/指针在使用前就必须初始化 【案例1.1.1】 C语言中最大的特色就是指针。指针的使用具有很强的技巧性和灵活性,但同时也带来了很大的危险性。在XXX的代码中有如下一端对指针的灵活使用: ... ... _UC *puc_card_config_tab; ... ... Get_Config_Table( AMP_CPM_CARD_CONFIG_TABLE, &ul_card_config_num, &puc_card_config_tab, use_which_data_area ); ... ... b_middle_data_ok = generate_trans_middle_data_from_original_data( puc_card_config_tab, Ul_card_config_num) .... ... 其中红色部分巧妙的利用指向指针的指针为指针puc_card_config_tab赋值,而在兰色部分使用该指针。但在Get_Config_Table函数中有可能失败返回而不给该指针赋值。因此,以后使用的可能是一个非法指针。 指针的使用是非常灵活的,同时也存在危险性,必须小心使用。指针使用的危险性举世共知。在新的编程思想中,指针基本上被禁止使用(JAVA中就是这样),至少也是被限制使用。而在我们交换机的程序中大量使用指针,并且有增无减。 2、防止指针/数组操作越界 【案例1.2.1】 在香港项目测试中,发现ISDN话机拨新业务号码时,若一位一位的拨至18位,不会有问题。但若先拨完号码再成组发送,会导致MPU死机。 处理过程: 查错过程很简单,按呼叫处理的过程检查代码,发现某一处的判断有误,本应为小于18的判断,写成了小于等于18。 结 论: 代码编写有误。 思考与启示: 1、极限测试必须注意,测试前应对某项设计的极限做好充分测试规划。 2、测试极限时还要注意多种业务接入点,本例为ISDN。对于交换机来说,任何一种业务都要分别在模拟话机、ISDN话机、V5话机、多种形式的话务台上做测试。对于中继的业务,则要充分考虑各种信令:TUP、ISUP、PRA、NO1、V5等等。 【案例1.2.2】 对某交换类进行计费测试,字冠011对应1号路由、1号子路由,有4个中继群11,12,13,14(都属于1#模块),前后两个群分别构成自环。其中11,13群向为出中继,12,14群向为入中继,对这四个群分别进行计费设置,对出入中继都计费。电话60640001拨打01160010001两次,使四个群都有机会被计费,取话单后浏览话单发现对11群计费计次表话单出中继群号不正确,其它群的计次表中出中继群号正常。 处理过程: 与开发人员在测试组环境多次重复以上步骤,发现11群的计次表话单有时正常,有时其出中继群号就为一个随机值,发生异常的频率比较高。为什么其它群的话单正常,唯独11群不正常呢?11群是四个群中最小的群,其中继计次表位于缓冲区的首位,打完电话后查询内存发现出中继群号在内存中是正确的,取完话单后再查就不正确了。 结 论: 话单池的一个备份指针Pool_head_1和中继计次表的头指针重合,影响到第一个中继计次表的计费。 思考与启示: 随机值的背后往往隐藏着指针问题,两块内存缓冲区的交界处比较容易出现问题,在编程时是应该注意的地方。 【案例1.2.3】 【正 文】 在接入网产品A测试中,在内存数据库正常的情况下的各种数据库方面的操作都是正常的。为了进行数据库异常测试,于是将数据库内容人为地破坏了。发现在对数据库进行比较操作时,出现程序跑死了现象。 经过跟踪调试发现问题出现在如下一段代码中: 1 for(i=0; i<pSysHead->dbf_count; i++) 2 { 3 pDBFat = (_NM_DBFAT_STRUC *)(NVDB_BASE + DBFAT_OFFSET + i*DBFAT_LEN); 4 if(fat_check(pDBFat) != 0) 5 { 6 pSysHead->system_flag = 0; 7 head_sum(); 8 continue; 9 } 10 if(strlen(dbf->dbf_name) != 0 && strncmp(dbf->dbf_name, pDBFat->dbf_name, strlen(dbf->dbf_name)) == 0) 11 { 12 dbf_ptr1 = (_UC *)pDBFat->dbf_head; 13 filesize = pDBFat->dbf_fsize; 14 break; 15 } 16 } 在测试时发现程序死在循环之中,得到的错误记录是"Bus Error"(总线出错),由此可以说明出现了内存操作异常。 经过跟踪变量值发现循环变量i的阀值pSysHead->dbf_count的数值为0xFFFFFFFF,该值是从被破坏的内存数据库中获取的,正常情况下该值小于127。而pDBFat是数据库的起始地址,如果pSysHead->dbf_count值异常过大,将导致pDBFat值超过最大内存地址值,随后进行的内存操作将导致内存操作越界错误,因而在测试过程中数据库破坏后就出现了主机死机的现象。 上面的问题解决起来很容易,只需在第一行代码中增加一个判断条件即可,如下: for(i=0; i<pSysHead->dbf_coun && i < MAX_DB_NUM; i++) // MAX_DB_NUM=127 这样就保证了循环变量i的值在正常范围内,从而避免了对指针pDBFat进行内存越界的操作。 从上面的测试过程中,我们可以看到:如此严重的问题,仅仅是一个简单的错误引起的。实际上,系统的不稳定往往是由这些看似很简单的小错误导致的。这个问题给我们教训的是:在直接对内存地址进行操作时,一定要保证其值的合法性,否则容易引起内存操作越界,给系统的稳定性带来潜在的威胁。 【案例1.2.4】 近日在CDB并行测试中发现一个问题:我们需要的小区负荷话统结果总是为零,开始还以为小区负荷太小,于是加大短消息下发数量,但还为零,于是在程序中加入测试代码,把收到的数据在BAM上打印出来, 结果打印出来的数据正常,不可能为零,仔细查看相关代码,问题只可能在指针移位上有问题,果然在函数中发现一处比较隐蔽的错误。 /* 功能:一个BM模块内所有小区CDB侧广播消息忙闲情况 */ /*************************************************************/ void Cell_CBCH_Load_Static(struct MsgCB FAR *pMsg) { 。。。 memcpy((_UC *)&tmp_msg,pMsg,sizeof(tmp_msg)); pMsg=pMsg+sizeof(tmp_msg);//sizeof(tmp_msg)=10;本意是想移动10个字节,可是实际上指针移动了10*sizeof(struct MsgCB)个字节; CellNum=tmp_msg.usCellNum; 。。。 } 1 所以结构指针传入函数后,如要进行指针移动操作,最好先将其转化为_UC型再说。总之指针操作要小心为上。 3、避免指针的非法引用 【案例1.3.1】 【正 文】 在一次测试中,并没有记得做了什么操作,发现HONET系统的主机复位了,之后,系统又工作正常了。由于没有打开后台的跟踪窗口,当时查了半天没有眉目。过了半天,现象又出现了,而且这次是主机在反复复位,系统根本无法正常工作了。 我凭记忆,判断应该是与当时正在测试的DSL板的端口配置有关。于是将板上所有端口配置为普通2B+D端口,重新加载在主机数据,现象消失。于是初步定位为主机在DSL端口处理过程中有重大错误。 我在新的数据上努力恢复原出问题的现象,却一直没有重现,于是恢复原数据,加载后立即重现。并注意到,当DSL端口激活时,主机复位。仔细比较两种数据的差别,发现出现主机复位问题的数据中DSL板配置了MNT/MLT端口,但是没有做DSL端口之间的半永久数据。 于是在程序中不断加打印语句,通过后台的DBWIN调试程序跟踪,最后终于定位为:每当执行到portdsl.c的DeviceDslMsgProc()函数中处理U口透传的 if ( SPC_STATE_OK == pSpcCB->bySpcState ) 语句时,主机复位。但是该语句似乎并无不妥。 再分析整个函数,pSpcCB在函数前部分已经被赋值, pSpcCB = SpcCB + (PortTable+index)->spcNo; 但由于得到 index 后,没有任何判断,导致若MNT/MLT端口没有做半永久,端口激活后,执行此部分函数,(PortTable+index)->spcNo 有可能为NULL_WORD,于是,运算后,pSpcCB 可能为非法值。此时主机在取进行判断,就不知会导致什么后果了。 其实,改起来很简单,只要在这两句前增加一个判断就行了。于是,修改代码为: if ( (PortTable+index)->spcNo != NULL_WORD) { pSpcCB = SpcCB + (PortTable+index)->spcNo; if ( SPC_STATE_OK == pSpcCB->bySpcState ) {。。。} } 修改后,问题不再重现。 经过分析可以发现,编译环境是有很大的容许空间的,若主机没有做充分的保护,很可能会有极严重的随即故障出现。所以编程时一定要考虑各种可能情况;而测试中遇到此类死机问题,则要耐心的定位到具体是执行哪句代码时出现的,再进行分析。因为问题很隐蔽,直接分析海一样的代码是很难发现的。 4、变量类型定义错误 【案例1.4.1】 【正 文】 在FRI板上建几条FRPVC,其DLCI类型分别为:10Bit/2bytes、10bit/3bytes、16bit/3bytes、17bit/4bytes、23bit/4bytes。相应的DLCI值为:16、234、991、126975、1234567,然后保存,重起MUX,观察PVC的恢复情况,结果DLCI值为16、234和991的PVC正确恢复,而DLCI=126975的PVC恢复的数据错误为61439,而DLCI=1234567的PVC完全没有恢复。 对于17/4类型,DLCI=126975的PVC在恢复时变成61439,根据这条线索,查找原因,发现126975-61439=65535,转化二进制就是10000000000000000,也就是说在数据恢复或保存时把原数据的第一个1给忽略了。此时第一个想法是:在程序处理中,把无符号长整型变量当作短整型变量处理了,为了证实这个判断,针对17bit/4bytes类型又重新设计测试用例:(1) 先建PVC,DLCI=65535,然后保存,重起MUX,观察PVC的恢复情况,发现PVC能够正确恢复; (2)再建PVC,DLCI=65536,然后保存,重起MUX,观察PVC的恢复情况,此时PVC不能正确恢复。 至此基本可以断定原因就是出在这里。带着这个目的查看原代码,发现在以下代码中有问题: int _GetFrDlci( DWORD* dwDlci, char* str, DWORD dwDlciType, DWORD dwPortType, DWORD dwSlotID, DWORD dwPortID) { DWORD tempDlci; char szArg[80]; 1 char szLine[80]; ID LowPVCEP; DWORD dwDlciVal[5][2] = { {16,1007}, {16,1007}, {1024,64511}, {2048,129023}, {131072,4194303} } ; ... } typedef struct tagFrPppIntIWF { ... WORD wHdlcPort; WORD wHdlcDlci; WORD wPeerHdlcDlci; WORD wPeerOldAtmPort; ... } SFrPppIntIWFData; DWORD SaveFrNetIntIWFData ( DWORD *pdwWritePoint ) { BYTE bSlotID, bPeerSlotID; DWORD dwCCID, dwPeerCCID; WORD wHdlcPort, wAtmPort, wIci, wPeerIci, wPeerHdlcPort ; WORD wCount; ... } DWORD SaveFrNetExtIWFData ( DWORD *pdwWritePoint ) { BYTE bSlotID; DWORD dwCCID, dwPeerCCID; WORD wHdlcPort, wAtmPort, wIci ; WORD wCount; ... unSevData.FrNetExtIWF[wCount].bSlotID = bSlotID; unSevData.FrNetExtIWF[wCount].wHdlcPort = wHdlcPort; unSevData.FrNetExtIWF[wCount].wHdlcDlci = gFrPVCEP[bSlotID ][ gFrPVCC[bSlotID][dwCCID].dwLoPVCEP ].dwDLCI; unSevData.FrNetExtIWF[wCount].wOldAtmPort = wAtmPort; unSevData.FrNetExtIWF[wCount].wAtmDlci = gFrPVCEP[ bSlotID ][ gFrPVCC[bSlotID][dwCCID].dwHiPVCEP ].dwDLCI; unSevData.FrNetExtIWF[wCount].dwMapMode = gFrPVCC[bSlotID][dwCCID].dwMapMode; ... } DWORD RestoreFrNetExtIWFData ( WORD wSlotID, BYTE *pReadPoint ) { WORD wCount, wTotalNetIWF; BYTE bSlotID, bHdlcDlciType, bAtmDlciType; WORD wOldAtmPort, wAtmDlci, wHdlcPort, wHdlcDlci; DWORD dwMapMode, dwCIR, dwBe; DWORD dwCCID, dwResult, dwAtmPort; wTotalNetIWF = g_MuxData.SevDataSize.wFrNetExtIWFNum; ... } DWORD RestoreFrHdlcIntIWFData ( WORD wSlotID, BYTE *pReadPoint ) { WORD wCount, wTotalHdlcIWF; DWORD dwCCID, dwPeerCCID, dwAtmPort, dwPeerAtmPort; DWORD dwResult; BYTE bSlotID, bPeerSlotID; WORD wHdlcPort, wOldAtmPort, wCIR; WORD wPeerHdlcPort, wPeerOldAtmPort; ... } 其中涉及DLCI值的变量都为WORD(即无符号短整型)类型,在程序的处理时,出现WORD和DWORD(无符号长整型)类型在一句中同时存在的情况,至此可以判断问题出在这里。由于DLCI值在不同类型时的取值范围不同,前三种类型的取值范围为16~991,第四种取值范围为2048~126975,第五种取值范围为131072~4194303,所以当采用前三种DLCI类型时,采用WORD类型最大值为65535,已经完全够用了;而对于第四种类型时,其取值在超过65535时,获取DLCI值的函数_GetFrDlci()采用DWORD类型,而负责保存和恢复的两个函数SaveFrNetExtIWFData()和RestoreFrNetExtIWFData(),都把DLCI的值当作WORD类型进行处理,因此导致DLCI取值越界,于是程序把原本为长整型的DLCI强制转换成整型,从而导致DLCI值在恢复时,比原数据小65536。而在程序运行过程中,这些数据保存在DRAM中,程序运行直接从DRAM中获取数据,程序不会出错;当FRI板复位或插拔后,需要从FLASH中读取数据,此时恢复函数的错误就表现出来。 另一个问题是为什么23/4类型的DLCI数据不能恢复?这是由于对于23/4类型的PVC,其DLCI的取值范围为:131072~4194303,而程序强制转换并恢复的数据最大只能是65535,所以这条PVC不能恢复。 至此,DLCI数据恢复出错的原因完全找到,解决的方法是将DLCI的类型改为DWORD类型。从这个案例可以看出,在程序开发中一个很低级的错误,将在实际工作中造成很严重的后果。 【案例1.4.2】 【正 文】 在FRI板上建几条FRPVC,其DLCI类型分别为:10Bit/2bytes、10bit/3bytes、16bit/3bytes、17bit/4bytes、23bit/4bytes。相应的DLCI值为:16、234、991、126975、1234567,然后保存,重起MUX,观察PVC的恢复情况,结果DLCI值为16、234和991的PVC正确恢复,而DLCI=126975的PVC恢复的数据错误为61439,而DLCI=1234567的PVC完全没有恢复。 对于17/4类型,DLCI=126975的PVC在恢复时变成61439,根据这条线索,查找原因,发现126975-61439=65535,转化二进制就是10000000000000000,也就是说在数据恢复或保存时把原数据的第一个1给忽略了。此时第一个想法是:在程序处理中,把无符号长整型变量当作短整型变量处理了,为了证实这个判断,针对17bit/4bytes类型又重新设计测试用例:(1) 先建PVC,DLCI=65535,然后保存,重起MUX,观察PVC的恢复情况,发现PVC能够正确恢复; (2)再建PVC,DLCI=65536,然后保存,重起MUX,观察PVC的恢复情况,此时PVC不能正确恢复。 至此基本可以断定原因就是出在这里。带着这个目的查看原代码,发现在以下代码中有问题: int _GetFrDlci( DWORD* dwDlci, char* str, DWORD dwDlciType, DWORD dwPortType, DWORD dwSlotID, DWORD dwPortID) { DWORD tempDlci; char szArg[80]; char szLine[80]; ID LowPVCEP; DWORD dwDlciVal[5][2] = { {16,1007}, {16,1007}, {1024,64511}, {2048,129023}, {131072,4194303} } ; ... } typedef struct tagFrPppIntIWF { ... WORD wHdlcPort; WORD wHdlcDlci; WORD wPeerHdlcDlci; WORD wPeerOldAtmPort; ... } SFrPppIntIWFData; DWORD SaveFrNetIntIWFData ( DWORD *pdwWritePoint ) { BYTE bSlotID, bPeerSlotID; DWORD dwCCID, dwPeerCCID; WORD wHdlcPort, wAtmPort, wIci, wPeerIci, wPeerHdlcPort ; WORD wCount; ... } DWORD SaveFrNetExtIWFData ( DWORD *pdwWritePoint ) { BYTE bSlotID; DWORD dwCCID, dwPeerCCID; WORD wHdlcPort, wAtmPort, wIci ; WORD wCount; ... unSevData.FrNetExtIWF[wCount].bSlotID = bSlotID; unSevData.FrNetExtIWF[wCount].wHdlcPort = wHdlcPort; unSevData.FrNetExtIWF[wCount].wHdlcDlci = gFrPVCEP[bSlotID ][ gFrPVCC[bSlotID][dwCCID].dwLoPVCEP ].dwDLCI; unSevData.FrNetExtIWF[wCount].wOldAtmPort = wAtmPort; unSevData.FrNetExtIWF[wCount].wAtmDlci = gFrPVCEP[ bSlotID ][ gFrPVCC[bSlotID][dwCCID].dwHiPVCEP ].dwDLCI; unSevData.FrNetExtIWF[wCount].dwMapMode = gFrPVCC[bSlotID][dwCCID].dwMapMode; ... } DWORD RestoreFrNetExtIWFData ( WORD wSlotID, BYTE *pReadPoint ) { WORD wCount, wTotalNetIWF; BYTE bSlotID, bHdlcDlciType, bAtmDlciType; WORD wOldAtmPort, wAtmDlci, wHdlcPort, wHdlcDlci; DWORD dwMapMode, dwCIR, dwBe; DWORD dwCCID, dwResult, dwAtmPort; wTotalNetIWF = g_MuxData.SevDataSize.wFrNetExtIWFNum; ... } DWORD RestoreFrHdlcIntIWFData ( WORD wSlotID, BYTE *pReadPoint ) { WORD wCount, wTotalHdlcIWF; DWORD dwCCID, dwPeerCCID, dwAtmPort, dwPeerAtmPort; DWORD dwResult; BYTE bSlotID, bPeerSlotID; WORD wHdlcPort, wOldAtmPort, wCIR; WORD wPeerHdlcPort, wPeerOldAtmPort; ... } 其中涉及DLCI值的变量都为WORD(即无符号短整型)类型,在程序的处理时,出现WORD和DWORD(无符号长整型)类型在一句中同时存在的情况,至此可以判断问题出在这里。由于DLCI值在不同类型时的取值范围不同,前三种类型的取值范围为16~991,第四种取值范围为2048~126975,第五种取值范围为131072~4194303,所以当采用前三种DLCI类型时,采用WORD类型最大值为65535,已经完全够用了;而对于第四种类型时,其取值在超过65535时,获取DLCI值的函数_GetFrDlci()采用DWORD类型,而负责保存和恢复的两个函数SaveFrNetExtIWFData()和RestoreFrNetExtIWFData(),都把DLCI的值当作WORD类型进行处理,因此导致DLCI取值越界,于是程序把原本为长整型的DLCI强制转换成整型,从而导致DLCI值在恢复时,比原数据小65536。而在程序运行过程中,这些数据保存在DRAM中,程序运行直接从DRAM中获取数据,程序不会出错;当FRI板复位或插拔后,需要从FLASH中读取数据,此时恢复函数的错误就表现出来。 另一个问题是为什么23/4类型的DLCI数据不能恢复?这是由于对于23/4类型的PVC,其DLCI的取值范围为:131072~4194303,而程序强制转换并恢复的数据最大只能是65535,所以这条PVC不能恢复。 至此,DLCI数据恢复出错的原因完全找到,解决的方法是将DLCI的类型改为DWORD类型。从这个案例可以看出,在程序开发中一个很低级的错误,将在实际工作中造成很严重的后果。 5、正确使用逻辑与&&、屏蔽&操作符 【案例1.5.1】 【案例描述】:由于C语言中位与比求模效率高,因而系统设计时,对于模128的地方都改为与127,系统定义的宏为#define MOD128 127和#define W_MOD 127(定义的宏的名字易引起误解),但实际程序中还是采取求模,从而引起发送窗口欲重发的和实际重发的不一致,最终导致链路复位此类严重问题,曾在定位此问题时花了不少时间。 【处理过程】:处理过程如下: #define MOD128 127 //队列长128,当队头到128时,上其返回。 #define W_MOD 127 //发送窗口队列,意义同上。 在函数L2_TO_L1()中,有如下语句: linkstate_ptr->SendWin.head = (head + 1) % W_MOD ; 这里当head=126时,SendWin.head = 0,这将造成发送窗口指针和队列窗口指针错位,造成链路复位; 另外,在重发函数void INVOKE_RETRANSMISSION(_US logic_link,_US n_r)中,有如下语句: retran_num = (LinkState[logic_link].Vs + MOD128 - (_UC)n_r) % MOD128 ; w_head = (LinkState[logic_link].SendWin.head + W_MOD - retran_num) % W_MOD ; 第一个语句求欲重发的消息包个数,第二个语句求重发的起始位置,当Vs小于n_r时,将造成实际重发数小于欲重发数,同时造成实际起始重发位置和欲重发起始位置错开,从而引起链路复位。上面三个语句应该做如下改动: linkstate_ptr->SendWin.head = (head + 1) & W_MOD ; retran_num = (LinkState[logic_link].Vs + MOD128 + 1 - (_UC)n_r) & MOD128 ; w_head = (LinkState[logic_link].SendWin.head + W_MOD + 1 - retran_num) & W_MOD ; 【结 论】:由于链路通信对系统效率要求很高,算法采用效率最高的,但位与(&)和求模(%)这小小的区别,造成的竟是链路复位这种严重的错误。 【思考与启示】:对这类问题,大家在阅读代码或代码审查时一定要注意,仔细一点往往能发现问题,但在测试中来定位这种问题,花费的时间往往更长。 6、注意数据类型的匹配 【案例1.6.1】 【案例描述】 下面通过测试中的一个例子来说明这个问题:命令DSP N7C是用来显示NO7电路状态的,其参数设备类型DID支持TUP和ISUP,参数信道号BSN支持多值输入(最多支持32路查询),正常情况下该命令没有问题。但试了非正常情况下,问题就出来了。 1、首先试BSN参数越界情况,即参数BSN超过32路查询,选了几个数据段,问题就出来了。对于0&&300和0&&256,该命令返回结果不一致,对前者认为参数越界,对后者返回执行成功。 2、对于参数DID,选定一种设备类型(TUP或ISUP),让参数BSN所包含的32路电路跨越TUP和ISUP,两次结果是不一致的。 【处理过程】 反馈到开发人员那里,第一个问题是BAM的问题,第二个问题是SM的问题。 【结 论】 1、为数据超出范围溢出造成,int值赋值给BYTE,造成数据丢失。 2、问题的产生是因为查询的第一个信道是TUP电路,但是却按ISUP电路查询。ISUP的维护处理函数判断第一个信道不是ISUP信道,认为整个的PCM不是ISUP类型的PCM,返回全部的电路状态为未安装。消息处理不合理。TUP也会产生如此错误。 【思考与启示】 我们的MML命令并不是无懈可击的,许多表面上的小问题,往往隐藏着代码的缺陷和错误。 【案例1.6.2】 【正 文】 当我们使用PC-LINT检查代码时,会发现大量的数据类型不匹配的告警,大部分情况下,这种代码上存在的问题并不会引起程序功能实现上的错误,但有些情况下,也许会产生严重的问题: 一、不同数据类型变量之间赋值引起的问题,实际上,该类问题也可以分为几种情况: 1、直接赋值,比如,把一个WORD型变量赋给一个INT型变量,如果WORD型变量大于32767,INT型变量得到的就是一个负值了。 【例一】一次测试过程中发现,SDH送的告警在BAM调试窗口打印出红色提示:File(XXX),Line(XXX):Invalid alarm id ,from: 7, AlarmId: 65463 经过检查数据发现,并没有ID为65463的告警,分析上报的数据帧,发现上报的告警ID为B7,原来代码中有一处强制类型转换: sdhAlmStru.AlarmId = (WORD)RecvBuffer[iTmpLen + 5]; char型强制转换成WORD型。B7就变成了FFB7,十进制就是65463。由于char是有符号型,B7的第8位为1,所以转换后为FFB7,而不是代码作者希望的00B7,如果第8位是0,或该变量是BYTE型,转换就不会有问题了。 2、函数形参和实参不一致,实际上和第一种情况本质上是一样的,只是表现的形式不太一样,这种情况也是代码中经常出现的问题,下面例子是测试中曾经发现的一个小问题: 【例二】在file01中的INT DebugMsgProc(char byMsg0, char byMsg1)函数,两个形参都是char型,而实际传入的参数都是BYTE型,结果函数中的如下语句: PrintfE(PID_RED," %d ticks time out!",byMsg1); 在byMsg1大于127时,输出错误的结果。 二、不同数据类型之间的比较操作 在循环终止条件的判断中,不同类型变量的比较操作是容易造成死循环错误的地方,同时也是开发人员容易忽视的地方,值得测试人员多加留意。下面两个例子是该类错误的两种典型情况: 【例三】file02文件中某函数中如下代码,可能造成死循环: ...... int i; WORD *pCheck =(WORD*)p; WORD wCheckSum=*pCheck; pCheck++; for(i=1;i<dwLen/2;i++) { wCheckSum^=(*pCheck); pCheck++; } //binlen had already word alignment return (wCheckSum); ...... 该段代码是在DOS环境下用BC编译的,由于循环变量i是int型(2个字节),而dwLen是DWORD型(4个字节),如果dwLen大于65536,那么该函数就是死循环了。 上面的例子是不同类型变量之间直接比较操作,还有一种情况是函数的返回值与另一不同类型的变量比较,见下面例子: 【例四】file03.c文件中某函数中如下代码, while( ftell(fp)< Part[3]) {..... } ftell返回long型,而Part是DWORD型,有符号变量和无符号变量的比较,可能造成死循环。 类似的例子还有很多,类型不匹配的问题还有许多种情况,都是代码中的隐患,有时会造成严重的后果,需要引起足够的重视。对于该类问题,我们可以利用PC-LINT工具对代码进行细致的检查。 7、用于控制条件转移的表达式及取值范围是否书写正确 【案例1.7.1】 【案例描述】: 在测试主机MPU板倒换功能时,如果MPU备份充分,倒换前后对处于激活状态的电路应无影响,即不影响通话。但近期测试发现,如果两局通过DT板进行一号对接,MPU备份倒换却发生断话。具体现象为:如果DT板的第1个PCM系统电路为故障,则MPU倒换时复位该DT板,如果DT板的第2个PCM系统电路为故障,则MPU倒换时复位下一块DT。 【处理过程】: 据查,MPU倒换时会自动复位处于“故障”态的电路,但由于计算错误(多加了32),错复位了下一个PCM系统32路电路。 【结 论】: 如此严重问题为什么到今天才发现?因为我们在实验室中一般采用同一单板的2个PCM系统自环进行测试,则不会在某单板上有故障和空闲电路共存,自环屏蔽了错误。 【思考与启示】: 自环是在测试环境下常用的一种提高效率的手段,但一旦条件允许,我们的测试工作应尽量模拟网上的实际环境进行。 【案例1.7.2】 平时对计费功能进行测试的时候,浏览详细话单都是比较注意话单本身的正确性,并没有注意该命令对系统的影响。所以当浏览少量话单的时候,并没有发现该命令的异常。但是当时间的跨度较大时,详细话单数量较多,问题就出现了。执行如下命令: LST AMA: TP=NRM, SD=1999&7&1, SA=YES; 当浏览了大约10万张详细话单后,终端与BAM的连接关闭。重建连接后,发现话单台的命令不能执行。观察BAM的性能,发现话单台仍占有CPU50%以上的利用率,说明原来的任务仍在执行。需要关一下话单台才能恢复正常。 重复上述步骤,当终端与BAM的连接尚未关闭时主动断开此次连接,结果同上。 反馈到开发人员那里,发现该现象与设计的初衷是相违背的。本来话单台控制最多输出200张话单,这是为了防止过多话单的输出显示会增加BAM的开销,从而降低BAM的性能。查看一下源代码,问题就发现了。 话单台控制最多输出200张话单 程序如下 while(timeCur <= timeEnd) { timeCur += tsOneDay;//加一天 while(fileBill.Read(&rpt, sizeof(CBillReport)) == sizeof(CBillReport)) { ..................... //只输出满足条件的前200张话单 if (++wBillCount == 200) { break; } }//一个文件查询结束 }//所有文件查询结束 在话单输出200张之后,程序只退出一层循环,仍然会从下一天话单继续输出,导致向MML发帧过多,造成MML和话单台都被堵死。 修改ProcessQueryBill()函数 //只输出满足条件的前200张话单 if (++wBillCount == 200) { timeCur = timeEnd + tsOneDay;//退出第二层循环, while(timeCur <= timeEnd) break; } 作上述修改后问题就不再出现了。 一些MML命令从完成的功能来讲可能是没什么问题的,但其执行对系统性能的影响我们在测试时时往往给忽视了。在我们目前的BAM方案中,存在着多个终端协同工作,如果某个终端发出的命令在BAM中长时间独占着大部分系统资源,造成的后果是严重的。这是在设计时要避免的,在测试中要注意的问题。 【案例1.7.3】 【正 文】 在判断模拟用户端口是否反极性时有这样一段程序: if ( ( bsn >= g_wASL32StartPSN ) && ( ( ( bsn - g_wASL32StartPSN ) % 32 ) == 15 || ( ( bsn - g_wASL32StartPSN ) % 32 == 16 ) ) ) return TRUE; if ( ( bsn % 16 ) == 7 || ( bsn % 16 ) == 8 ) return TRUE; return FALSE; 作者的本意是如果是32路用户板(蓝色字体判断),就看端口号是否是第15和16路,如果是,就是反极性端口,返回TRUE,否则就不是,应该返回FALSE。但代码表达的意思是:如果是32路用户板并且端口号是15或16就返回真值,否则还要执行下边语句。 当端口在32路用户板上,但端口号不是15或16时,不同的32路端口的起始地址g_wASL32StartPSN,会导致不同的非15、16端口被误认为是反极性端口。举个例子,当g_wASL32StartPSN的值为3000时,端口号为3000(第一块板上的第0个端口)就被认为是反极性端口,这与作者的意图完全相悖。 可以将代码修改如下: if ( ( bsn >= g_wASL32StartPSN ) { if ( ( ( bsn - g_wASL32StartPSN ) % 32 ) == 15 || ( ( bsn - g_wASL32StartPSN ) % 32 == 16 ) ) ) return TRUE; } else if ( ( bsn % 16 ) == 7 || ( bsn % 16 ) == 8 ) return TRUE; return FALSE; 通过这个例子,我觉得在代码审查时应该留意在判断条件较多的情况下,每个输入是否都能正确输出,在单元测试、集成测试、系统测试时要针对边界值设计相应的测试用例。 判断条件较多时开发人员也应该适当分开写,既使代码更易读,又不容易出错。 8、条件分支处理是否有遗漏 【案例1.8.1】 【现 象】 在接入网主机程序的代码审查中,发现dbquery.c的DBQ_Init_ANType函数中如下代码段缺少应有的条件分支,在数据异常的情况下,会产生较严重的问题。 【处理过程】 该错误比较隐蔽,现在说明如下: Max2B1QStatTime 最大统计时间 Max2B1QStatPortNum最大统计端口数 MAX_2B1Q_STAT_PSN 最大统计内存分配数量 其中:Max2B1QStatTime(最大统计时间)和Max2B1QStatPortNum (最大统计 端口数)的乘积不能大于MAX_2B1Q_STAT_PSN 程序如下: //查询数据库,获得Max2B1QStatTime的值 directQueryCond.tupleNo = 10; error_code = DB_Query( RID_OTHERS_PARA_INFO, 1, (LPDBCondition)&directQueryCond, (BYTE FAR *)&tempstruct0 ); //查询数据库成功 if( error_code == DB_SUCCESS ) { //tempstruct0.data是数据库中为Max2B1QStatTime配置的值 if ( tempstruct0.data > MAX_2B1Q_STAT_PSN ) Max2B1QStatTime = MAX_2B1Q_STAT_PSN; else if ( tempstruct0.data != 0 ) Max2B1QStatTime = tempstruct0.data; } //查询数据库,获得Max2B1QStatPortNum的值 directQueryCond.tupleNo = 11; error_code = DB_Query( RID_OTHERS_PARA_INFO, 1, (LPDBCondition)&directQueryCond, (BYTE FAR *)&tempstruct0 ); //查询数据库成功 if( error_code == DB_SUCCESS ) { //tempstruct0.data为数据库中为Max2B1QStatPortNum配置的值,如果其缺省值和Max2B1QStatTime乘积值大于MAX_2B1Q_STAT_PSN的话: if ( (tempstruct0.data * Max2B1QStatTime) > MAX_2B1Q_STAT_PSN ) Max2B1QStatPortNum = MAX_2B1Q_STAT_PSN / Max2B1QStatTime; //如果在合理范围内且不为0的话: else if ( tempstruct0.data != 0 ) Max2B1QStatPortNum = tempstruct0.data; } 此处if-else if 分支没有判断 值为0的情况,即数据库为Max2B1QStatPortNum配置的值为0: tempstruct0.data == 0,则Max2B1QStatPortNum就为缺省值32。 【结 论】 由于内存限制,Max2B1QStatTime(最大统计时间)和Max2B1QStatPortNum(最大统计端口数)的乘积不能大于MAX_2B1Q_STAT_PSN, 如果从数据库中得到Max2B1QStatTime为MAX_2B1Q_STAT_PSN,而数据库中最大统计端口数恰好为0,由于上述代码没有对tempstruct0.data == 0的情况进行判断,Max2B1QStatPortNum为缺省值32,这样Max2B1QStatTime和Max2B1QStatPortNum乘积已经是32倍MAX_2B1Q_STAT_PSN了,远远超过了设计内存的限制。 造成这种错误的原因是判断语句对条件判断不完整。 【思考与启示】 在代码审查时,应该十分注意条件判断的的完备性。好多问题就是因为条件判断不完全造成的。 9、引用已释放的资源 【案例1.9.1】 【正 文】 在计费测试的过程中,用呼叫器进行大话务量呼叫测试。30路话路通过TUP自环呼叫另外30路话路,计费数据的设定是这样的:通过计费情况索引对主叫计费,得到详细话单。首先保证计费数据设定的正确性,打了几次自环电话后,查看话单正常,则开始呼叫。 呼叫几万次后停止呼叫,取话单进行观察。发现这30路每次呼叫总会出现一张告警话单,其余话单正常,该告警话单相对于话路来说是随机出现的。 通知开发人员后,首先我们再次对计费数据进行了确认。某个用户在某次呼叫产生了告警话单,其上一次和下一次呼叫的计费情况都正常,两次呼叫之间的时间间隔只有几秒钟,排除了人为修改数据的可能。开发人员认为是CCB的问题,后来一查果然如此。 当中继选线发生了同抢需要重新选线时,CCB的reset_CCB_for_reseatch_called_location()就会把有关的呼叫信息清掉,造成计费情况分析失败,产生计费费用为0的告警话单。 更正reset_CCB_for_reseatch_called_location()中清除被叫信息的代码,重选中继时不清除被叫用户这部分属性。 思考与启示: 1、在计费测试过程中,对话单的观察很重要,不应该放过任何一个细小的疑点; 2、计费测试仅仅打几次电话往往达不到效果,越接近用户实际使用的情况越可能发现问题。 【案例1.9.2】 【案例描述】 在进行128模块V5用户CENTREX新业务测试时,偶然遇到一个怪现象:对群内一个V5ST用户只开放MCT权限,在进行恶意呼叫追查时,有一次报恶意呼叫追查成功音只报了一半,当正要报出恶意呼叫的号码时,业务中断重新回到通话态,随即重新追查一次,报“已申请其它新业务,本次申请不成功”。恶意呼叫追查与任何新业务都不会冲突,而且此用户也只有恶意呼叫追查有权,可以肯定此时程序出问题了。为了重现,再次挂机,重新呼叫,应用此新业务,但这个现象一直没有出现。大约反复操作20遍,又出现了一次这样的情况,显然程序中可能存在某种问题。 【处理过程】 出现这个问题后,及时与开发人员A取得了联系,并一起试图重现这个问题,通过许多次的反复操作,又出现了一次这种情况。确认问题后,A表现出高度的责任心,马上驾调试环境,反复调测,终于在当天就逮住了狐狸尾巴: 1、当用户接听恶意呼叫者的电话, 并启动恶意呼叫追查业务后, 在V5_CR_VOICETONE状态下, 只要听MCT音的用户用脉冲方式拨任意一个数字, 则立即停止送MCT音, 而将用户切换回与恶意呼叫者的通话. 但是程序中没有对拨号类型作判断, 导致用户若用音频拨号也会作同样的处理。 2、除了取消此次MCT服务, 将用户切换回与恶意呼叫者的通话外, 如果不释放MCT_HANDLE, 由于每个模块只有一个这样的资源, 则下一次使用MCT业务的用户不能成功, 因为会在申请MCT_HANDLE时失败, V5模块和ST模块在这个地方处理都有问题, 没有将MCT_HANDLE释放掉, 对于V5用户会听新业务失败音, 对于ST用户会听音乐。 当不停的拨测V5用户的MCT业务时, 有时在听音时, 可能由于网板有杂音等原因(或用户碰了话机的按键), 导致DTR收到一位号, 则会立即停止此次MCT服务, 用户会听到MCT送音突然中断, 然后恢复了与恶意呼叫者的通话. 而下次再用MCT时, 由于上面所述的原因, 会听到新业务失败音, 此次失败后, 无论MCT_HANDLE分配成功与否, 该用户的MCT标志都被置为1, 所以在用户挂机时, 会将该模块唯一的MCT_HANDLE资源释放掉. 则以后该功能又可以正常实现。 在追查这个问题时,开发人员A又发现了一个可能导致死机的严重问题:在用户启动MCT服务, 正在听报追查号码的MCT音时, 若恶意用户此时挂机, CCB的处理中, 只针对ST用户送DISCONNECT, 而对V5ST用户送的是RELEASE消息, 这导致V5X收到此消息后, 将该V5ST用户的cr2清除掉, V5_USER_TALBE[ ]. cr2变为0xFFFF, 这样在V5_CR_VOICETONE超时后, 程序中会检查cr2的状态是否为HOLD, 当取cr2的内容时, 由于cr2已被清除, 会发生指针越界的GP错误。 【结 论】 通过调测发现、定位并解决问题。 【思考与启示】 我们平常一些熟视无睹的业务或按正常流程操作没有问题的业务,不能保证它就一定没有问题,要善于抓住一丝一毫的异常现象。对于很难重现的问题千万不要轻易放过,我们网上设备所出的问题很多都是一些在实验室难以出现或很难重现的一些问题,一些显而易见的问题一般都可消灭在实验室,难就难在消灭一些隐藏很深的问题。说老实话,我们的产品还有许多问题 ,需要我们扎扎实实锲而不舍的工作。 10、分配资源是否已正确释放 【案例1.10.1】 【正 文】 在对接入网A产品的网管软件测试中,发现了一个WINDSOWS资源损耗的的问题:当网管软件运行几天后,WINDOWS总会出现“资源不够”的告警提示。如果网管软件不关掉再重新启动的话,就会出现WINDOWS资源完全耗尽的现象,最终网管系统反应很慢,无法正常工作。 从现象上可以判断出,网管软件存在隐蔽的内存泄露或资源不释放的问题,并且这种资源耗尽是一个缓慢的过程。如何定位这个问题呢? 定位这种问题可以利用WINDOWS中的一个系统资源监视工具。打开Windows的“附件/系统工具/资源状况”,这是一个系统资源、用户资源、和GDI资源的实时监视工具。 工具有了,那么如何发现导致不断消耗资源的特定操作呢? 首先和开发人员共同探讨,列出几个最可能消耗资源的操作和一些操作组合,这样就缩小了监视范围,避免没有范围的碰运气,否则如大海捞针。 监视前,首先重新启动WINDOWS,最好不运行其他的程序,打开“系统状况”这个监视工具,然后运行网管软件,记下此时的资源状况数据。 然后针对一个可疑的操作,快速大量地重复进行。这种重复性的操作可以利用QArun测试工具执行,QArun可以记录操作者的一次操作步骤,然后按照设定的次数重复操作。操作后,观察此时的资源状况,并记下此时的数据,与操作前的数据比较,如果操作前后的数据数据没有变化或变化很小,可排除此项操作,否则就可断定此项操作会引起资源耗尽。 对其它可疑的操作和操作组合重复以上过程。 通过以上的步骤,终于找出引起资源耗尽的罪魁祸首。分析相应部分的代码,发现引起资源耗尽原因有:内存泄露,画笔和画刷资源用完后未释放等。 【案例1.10.2】 【正 文】 某产品后台软件版本,是用C++写的,程序员在写代码时,经常在构造函数中申请一块内存,而不释放,在程序其他代码中也经常只管申请,不管释放。 例如: void WarnSvr::SaveWarnData() { ...... for(int m=0;m<RecordsInBuffer[EVENT_ALARM];m++) { HISTORY_FILTER_INDEX* item= new HISTORY_FILTER_INDEX; item->Csn=Buffer[EVENT_ALARM][m].Csn; item->Position=m +(RecordsInHistoryFile-RecordsInBuffer[EVENT_ALARM]); //If a warn with a certain Csn is not in EventFilterIndex //it is not necessary to be added to HistoryFilterIndex int item_total=EventFilterIndex.GetItemsInContainer(); BOOL find_flag=false; for(int k=0;k<item_total;k++) if(EventFilterIndex[k]->Csn==item->Csn) { find_flag=true; break; } if(find_flag) { HistoryFilterIndex.Add(item); if(HistoryFilterIndex.IsFull()) ClearIndexEntry(); } //建议在此处加上: // else // delete item; }。 有的程序员认为,后台运行的环境有大量内存,几个字节的浪费不会造成死机等重大事故。然而,长时间累计起来,必然会造成资源紧张而出现故障。 实际上,这种思想是造成我们产品不稳定的原因之一。我们的主机在网上能运行几个月几年,大家对内存的分配释放较敏感,而我们的后台产品往往只能正常运行几天。这个地方不注意也是原因之一吧。 【案例1.10.3】 【正 文】 在进行代码审查过程中,造成内存泄漏的代码比较多。下面举几种常见的内存泄漏错误,供测试人员在代码审查中参考: 1. 函数有多个出口时,没有在每个出口处对动态申请的内存进行释放。一般在异常处理时容易出现这种错误。下面的代码段就是这样的例子: ..... pRecord = new char[pTable->GetRecordLength()]; assert(pRecord != NULL); if (pTable->GoTop(FALSE) != DBIERR_NONE) return; // 如果从这里返回,pRecord将得不到释放 ..... pTable->Close(); delete[] pRecord; } 2. 给指针赋值时,没有检查指针是否为空,如果指针不为空,那么指针原来指向的内存将丢失。请看如下代码段: .... struct FileInfo * pdbffile = new struct FileInfo; pdbffile->pfileinfo = new struct ffblk; pdbffile->srcname = srcRootPath; pdbffile->desname = desRootPath; pdbffile->prev = NULL; pfile = pdbffile; //赋值之前没有检查一下pfile是否为空,如果不为空,会造成pfile指向的内存丢失。 dbf_start_needed = FALSE; dbf_Finish = FALSE; flag_begined = TRUE; if(FALSE == Copy(TRUE)) { dbf_start_needed = TRUE; WarnMsgOut("Error occurs while copying files in directory <dbf>,trying again."); } } 3. 连续二次内存动态分配,在第二次分配失败时,忘记释放第一次已经申请到的内存。 .... pMsgDB_DEV = (PDBDevMsg)GetBuff( sizeof( DBDevMsg ), __LINE__); if( pMsgDB_DEV == NULL ) return; pMsgDBApp_To_Logic = (LPDBSelfMsg)GetBuff( sizeof(DBSelfMsg), __LINE__ ); if( pMsgDBApp_To_Logic == NULL ) return;//此处返回造成pMsgDB_DEV指向的内存丢失 .... 4.代码中缺少应有的条件分支处理,导致程序未执行任何操作而退出时,也可能没有释放应释放的内存,这种情况一般是缺少应有的else分支,或switch语句的default分支没有应有的处理。 static void OncePowerCmdHandle( struct HT_Appmsg * msg ) { ... ... pPower_test_answer =(struct _oncepower_test_answer *)GetBuff(sizeof(struct _oncepower_test_answer),__LINE__); if( pPower_test_answer == NULL_PTR ) return; ... ... if (TSS_State[testpsn].state == TEST_DEV_BUSY || TSS_State[testpsn].state == TEST_DEV_ERROR ) {... } else if (TSS_State[testpsn].state == TEST_DEV_IDLE ) {... } // 缺少 else 分支,可能造成 pPower_test_answer 得不到释放 } 造成内存泄漏的情况很多,以上是几种典型的情况。 虽然内存泄露一般出现在异常情况下,毕竟给系统造成很大的隐患,使系统的健壮性降低。测试人员在作代码审查时,对上述几种情况要尤其注意。 【案例1.10.4】 【正 文】在进行SAR的PDU包发收的测试过程中要同时考虑几个边界值,即发送包大小范围[0-Nmax],SAR的PDU包接收的最大值Kmax,MBUF块的大小M.在实测中,将SAR的PDU包接收的最大值设为2000(Kmax=2000B), MBUF的块长设为512(M = 512B),则发送包大小的正确分支的取值为下限0,上限Nmax=2000,然后在0与2000之间随机取若干值,再考虑MBUF的块长,还可增加M倍数的若干选值及其附近值.以上是测试的一般思路,但由于很偶然的机会选择包长2000,及Kmax=2000B,才发现问题.原因如下: MBUF块长512,但块中实际存放数据的只有500(MBUF头上有2个长字,尾部有1个长字共12B只用于块控制),而发送的包长正好是500的整数倍4,由于是整数倍,所以SAR(BT8230)从FREE链上摘成5个MBUF(原因从略),而SAR驱动只知道有4个MBUF,这样到上层用户时,只释放4个MBUF,从而漏掉1个MBUF,经过很短一段时间后,内存即被耗尽.(此问题非常严重,因为在实际运用中,是500的整数倍的PDU包的概率较小,但一旦出现就会发生一次内存泄漏,这样经过若干天或若干月的运行后会使系统崩溃) 以前未发现此问题的原因是因为原来使用的缓冲块长为2048,减去12B的控制信息,实际存放数据的长度为2036.由于只考虑了2048这个值,忽略了2036,所以在选取上下限中的若干值时,选取包的长度是2036的倍数的概率就非常小,因而未发现该问题. 由于测试中一般很难将取值范围中的所有值覆盖全,所以在选取上下限中的若干取值时要格外仔细,考虑的方面尽可能全,因为很有可能其中某些值就是测试边界值.凡是涉及的数字尽量选取,象该例中正确分支的测试边界为0,2000,512及其整数倍,500 及其整数倍,12 及其整数倍等值,它们是必测的边界值,而非可测可不测的随机选取的所谓若干选值. 【案例1.10.5】 【正 文】 ABIS.CPP中的函数rel_ABIS_CCB_conn( )中,在进行消息链表Msg_Queue[ces]的拆链操作时,对于相应的CCB只进行了一次拆链操作,即只拆除了一个节点,如果出现该CCB对应的消息节点不止一个的情况就会出现大量节点不能释放的问题。 if( Msg_Queue[ces].msghead != NULL_PTR )//message buffer notempty { //get first message record pMsgRecord = Msg_Queue[ces].msghead; //release buffer-messages concerning with ccb_no for( index = 0; index < MSGBUFFERNUM; index++ ) { //这里要对pMsgRecord的值进行判断 if( (pMsgRecord != NULL_PTR) && pMsgRecord->CCB_no == ccb_no ) { //free the message buffer if( pMsgRecord == Msg_Queue[ces].msghead )//head Msg_Queue[ces].msghead = pMsgRecord->pnext; else if( pMsgRecord == Msg_Queue[ces].msgtail )//tail { Msg_Queue[ces].msgtail = pPrevMsgRecord; Msg_Queue[ces].msgtail->pnext = NULL_PTR; } else//not head and tail { pPrevMsgRecord->pnext = pMsgRecord->pnext; } //put buffer back to buffer pool if( Msg_Buffer.empty_num == 0 ) { Msg_Buffer.linkhead = Msg_Buffer.linktail = pMsgRecord; pMsgRecord->pnext = NULL_PTR;//这里将 pMsgRecord->pnext置为空 Msg_Buffer.empty_num++; } else { Msg_Buffer.linktail->pnext = pMsgRecord; pMsgRecord->pnext = NULL_PTR;//这里将 pMsgRecord->pnext置为空 Msg_Buffer.linktail = pMsgRecord; Msg_Buffer.empty_num++; } } else if( pMsgRecord == NULL_PTR ) break;//end of if //get next message record pPrevMsgRecord = pMsgRecord; pMsgRecord = pMsgRecord->pnext;//这时pMsgRecord为 NULL_PTR将跳出for循环语句 }//end of for }//end of if 这里在拆除一个节点后导致pMsgRecord为NULL_PTR,再进行判断时将会跳出循环,这样将不能保证所有与同一个CCB有关的节点均被拆除,这时如果与同一个CCB对应的消息节点不止一个则这些消息节点均无法释放,造成可用的节点数不断减少,直接影响系统的建链过程,给系统的稳定带来隐患。 后与开发人员联系,根据这段算法编写小程序验证了该问题,并提出了相应的解决方案,消除了该隐患。 【案例1.10.6】 【正 文】 1、建立一个呼叫,并保持通话。在AM控存监控操作界面中观察通话建立在哪一块FBI板上。 2、将有通话的FBI板拔出,观察通话情况,此时话音中断,但信令仍然保持。观察AM控存监控操作界面和E3M板2K网界面,发现AM侧因为检测到光纤已断,将通话在CTN、E3M板上占用的时隙置为空闲,即在AM控存监控操作界面和E3M板2K网界面观察不到时隙占用情况。 3、分别在30秒、1分钟、3分钟时将拔出的FBI板插回原槽位,发现每次插回FBI板后话音立即恢复。 4、观察BAM上的打印消息,发现打印的各模块占用CTN板大HW上DM时隙的空闲个数比较混乱。打印消息如下图所示: 其中: 1) 由于模块1、2、3、4各占用CTN板上两条大HW,每个DM时隙个数为256(即由两条大HW的两个DM组成,由于与OPT相联的大HW上有两个保留时隙,因此此DM上空闲时隙个数为:254。 2) 由于E3M板只与一条大HW相联,故每个DM上空闲的时隙个数为:128。 本现象对应2个问题:idle_count打印混乱,BM释放故障光路的时隙和对应的CCB、无线信道等资源。 1、idle_count打印混乱是由于函数restore_one_hw中的一些处理不当造成的,以前被当作B型机的历史遗留问题没有重视; 2、B2模块有2条光路,如果断掉其中一条,模块状态不会改变,原B型机程序对此不作任何处理,但应该增加这个功能,以免光路故障导致资源吊死。 解决方法: 问题一: 将函数restore_one_hw中原代码作如下改动: mod_dm[mod][i].tail.tsn = idle_dm_head + 125; ( idle_dm_head == 384 ) ? mod_dm[mod][i].idle_count += TS_PER_DM - 1: mod_dm[mod][i].idle_count += TS_PER_DM - 1; 改为: if ( idle_dm_head != 384 ) { mod_dm[mod][i].tail.tsn = idle_dm_head + 127; mod_dm[mod][i].idle_count += TS_PER_DM; } else { mod_dm[mod][i].tail.tsn = idle_dm_head + 126; mod_dm[mod][i].idle_count += TS_PER_DM - 1; } 问题二分析如下: 目前的模块状态是由IPATH调用DBMS模块的边检查实现的,只要存在一条可用的光路,即认为相邻模块为正常,对于具体的OPT板上的时隙状态的维护没有与呼叫控制的接口。具体的OPT板状态功能的检测是由IPATH完成的,在BM侧没有专门维护OPT和MC2板的模块,将转交OS组处理。 总结: 在拔出FBC板后,通话话音被中断,AM/CM侧已将与被拔出的FBC 板相关的资源全部置为不可用,此时BM侧主机程序也应该与AM/CM侧一致,释放掉所占用的资源,并将原通话的信令连接断开。这可能是由于不同模块的开发人员缺少相互间了解而造成的,即AM/CM侧与BM侧开发人员交流不够。作为测试人员对类似两个或多个模块相关的部分应该充分进行测试,不要想当然,往往是看起来不可能出问题的地方也容易测出问题。 【案例1.10.7】 在进行有关排队指示的系统测试中,先闭塞掉基站的所有业务信道TCH,进行呼叫,再直接挂机或超时释放,发现TC存在中继资源吊死的问题。 由于此问题重现,后经定位分析,发现是ccb超时后收到AIR发来的clear cmd,进入 rel_one_bm_res( )时,由于ccb所登记的CIC还放在pre_occupied_res,并没有放入occuped_res,而rel_one_bm_res()只对存入occuped_res的CIC进行判断,并向AIE发UNBOOKCIC,而没有对存入pre_occupied_res的CIC进行判断,并UNBOOK掉,导致TC的中继资源吊死。应在超时函数或释放函数中对pre_occupied_res的CIC进行处理。 在此过程中,CIC资源还存放在老CCB的pre_occupied_res中,在超时函数或释放函数中均未对pre_occupied_res中的CIC进行处理(即向AIE UNBOOK),导致TC中继资源吊死。 在超时函数RR_time_out()中timer_name为TN_WAIT_ASS_READY时,和释放函数rel_one_bam_res()中增加对CCB的pre_occupied_res中的CIC的判断和释放处理。 在使用资源同时,就要周密地考虑好资源的释放问题,只有这样,才能使我们的系统不断地稳定下来。 资源的释放对于我们的交换机来说是至关重要的,一点点的疏忽都可能最终使我们的交换机因为无资源使用而死掉,要知道,“千里长堤,毁于蚁穴”。 11、防止资源的重复释放 【案例1.11.1】 【正 文】 当进行大话务量呼叫时,在统计代码中出现AIE收到UNBOOK CIC消息时,发现自身电路状态为空闲,出现一个断言。这说明AIE电路电路被误释放了。 这个问题出现的原因有以下几种: 1. RR可能发错了电路号,导致AIE状态错误。 2. AIE可能发起资源核查,失败后将本控制表项释放了。 3. RR可能发起了重复释放操作,导致AIE的某个表项连续收到两个UNBOOK消息。 分析完了可能的情况,就要一一分析定位。 在可能原因一发生的情况下,RR发来的UNBOOK消息所带的AIR连接号和模块号会错误,导致我们会出现断言。而在测试数据结果文件中,没有出现这个断言,因此可能原因一不成立。 在可能原因二发生的情况下,AIE收到资源核查失败消息的数目应该不是零。但是实际情况下统计结果中收到资源核查失败消息的个数为零,说明情况二也不成立。 由上分析,这个问题只可能是由于RR重复释放造成的。但是为何会发生重复释放,这需要进行进一步分析。 从呼叫的正常流程来看,是不会产生重复释放的,因此我们怀疑该问题与异常流程有关。从统计代码中查找异常流程,发现该次统计中BSC内切换流程多次出现问题,具体原因是由于切换过程中在目标小区申请不到信道,产生切换失败造成的。因此集中研究这个流程,发现存在问题如下: 当原小区向目标小区发送内部切换请求消息时,带来了AIR和AIE的各项信息,而目标小区收到这些信息后就将之保存在自身的占用资源中。如果目标侧申请信道失败,就会向源侧发内部切换拒绝消息,而后产生本地释放。由于在释放前目标侧RR没有将占用资源中的AIR和AIE信息清除,因此导致重复释放时对AIR和AIE发起了释放操作。由于AIR释放时有保护机制,所以不会产生问题,而AIE没有保护机制,新CCB就将AIE电路释放掉了。而后当老CCB在通话结束后发起释放时,就产生了重复释放。 从上面分析可以看出,这个问题是由于RR释放流程的错误造成的,因此,我们要对此加以修改,在新CCB释放前将AIR和AIE信息从预占资源中清除。 RR的释放是一个非常复杂的过程,如何正确的整理资源,确保资源的合理释放,这是摆在我们面前的一个艰巨的问题,我们要仔细分析各种可能发生的情况,正确释放各种资源,即不会吊死资源,也不会产生重复释放。 12、公共资源的互斥性和竞用性 【案例1.12.1】 【正 文】 试验环境:CPX8216 CPCI 机架、vxWorks操作系统、Tornado1.0.1调试环境 测试用例:测试板间通信性能。从接口板A向接口板B循环发送消息,通过超级终端观察消息的收发情况。 测试结果:每发送一定数量的消息帧后,会出现发送地址出错现象。 原因分析:接收板回送缓冲区指针给发送板,是采用memcpy单字节拷贝的方式。若发送速度快于接收速度,两板竞用发送板系统总线访问缓冲区指针所在的共享内存,导致数据访问冲突。memcpy过程被打断,即出现发送板读发送地址出错现象。 采用四字节拷贝函数bcopyLongs传送发送缓冲区指针,问题解决。 共享内存的访问设计,除了考虑互斥外,还有总线竞用问题。 【案例1.12.2】 【正 文】 问题描述: 在进行主BCCH载频互助新功能开发的并行联调测试的过程中,发现了以下的问题:在数管台设置“TRX倒换是否允许”为“是”,进行设定整表后,关闭基站其中配有4个TRX的小区的主BCCH所在的TRX电源,发现对应小区重新初始化并成功,也就是载频互助成功。这个时候从后台对该小区所在的站点进行4级复位,同时重新打开之前关闭的该小区的原配主BCCH所在TRX的电源,发现对应小区初始化失败。 问题定位: 在问题定位开始,先是查看了载频互助相关代码在站点初始化流程中的处理。BTSM程序初始化过程中,先是判断这一次初始化之前是否发生过载频互助,若发生过,再判断原配主BCCH(即数据库中实际配置的主BCCH所在的TRX)是否已经恢复(即能正常建立TEI,能正常设置该TRX对应的RC属性,总之能正常开工)。若载频互助发生过,且原配主BCCH所在的TRX(CoTRXGroupForBts[BtsNo].MainTRX)已经恢复,即把之前进行互助的TRX (CoTRXGroupForBts[BtsNo].AidTRX)的数据和原配的主BCCH所在TRX的数据交换回来,并重新进行初始化。表面上看原理应该没有什么逻辑错误,怎么会出现初始化不成功呢? 我们对程序中的每一个可能导致该问题的变量加打印调试程序,然后重现该问题,终于在打印出来的信息中发现在载频互助发生后其互助的主BCCH所在的TRX与实际数据配置主BCCH所在的TRX为同一TRX,这有问题,因为载频互助的实质就是实际数据配置主BCCH所在的TRX不能正常开工而借用其他TRX作为主BCCH。于是我们根据此线索查询了所有BTSM的程序,没有发现问题的根源。于是我们查了最近合进版本的相关模块的程序,终于找出了问题的根源所在。 在载频互助程序中以全局变量ptrBTS_CONFIG_MAP[BtsNo].TRX_no_BCCH_in表示当前实际运行的主BCCH所在的TRX号,是随时变化的;以CoTRXGroupForBts[BtsNo].MainTRX表示原配的主BCCH所在的TRX号,是固定的。两者在系统开工的系统开工的接口函数FetchOneSiteConfig( )中赋了相同的值:该函数的409行有赋值语句CoTRXGroupForBts[BTS_no_temp].MainTRX = ptrBTS_CONFIG_MAP[BTS_no_temp].TRX_no_BCCH_in。以前函数FetchOneSiteConfig()只是在系统开工时才调用过一次,故CoTRXGroupForBts[BTS_no_temp].MainTRX 在系统开工以后是不变的,但是在DBMI同步开发的整改中,作了如下处理:在每一次数据动态设定后,先判断站点下有没有发生过载频互助,若发生过则试图先把目前进行互助的TRX的数据与实际数据配置成主BCCH的TRX的数据倒换回来,然后进行站点初始化。问题就出现在这,在DBMI中认为DB中原配的主BCCH的TRX是ptrBTS_CONFIG_MAP[BTS_no_temp].TRX_no_BCCH_in,而且每次进行站点初始化时都调用函数FetchOneSiteConfig(),这样将导致CoTRXGroupForBts[BTS_no_te
2.92MB
综合布线工程实用技术
2018-04-29附带案例,图文并茂,深入浅出。很好的教学和学习材料。(5分) 综合布线工程实用技术 单元一 认识综合布线 1.1 综合布线系统的基本概念 1.1 综合布线系统的基本概念 1.我们都在使用综合布线系统 综合布线系统就是网络系统的传输通道和基础,因为我们从电脑上获取的各种信息流都是通过综合布线系统传输到我们的电脑中的,因此没有综合布线系统,我们就无法获取各种信息。 2.综合布线系统基本概念 综合布线系统就是用数据和通信电缆、光缆、各种软电缆及有关连接硬件构成的通用布线系统,是能支持语音、数据、影像和其他控制信息技术的标准应用系统。 3.综合布线系统是智能建筑的基础 综合布线系统是智能建筑快速发展的基础和需求,没有综合布线技术的快速发展就没有智能建筑的普及和应用。 综合布线也是物联网、数字化城市的基础,还是建筑物的基础设施。 4. 综合布线的基本形式 1.3 真实案例 湖北交通职业技术图书馆信息大楼综合布线工程概述 湖北交通职业技术图书馆信息大楼,该项目为新建一幢12层高局部五层高的综合楼,设有单层高的地下室,总占地面积为2988平方米,总建筑面积为27339.47平方米,占地面积为2988平方米,包括:图书馆、教室、地下停车场、用房。拟将建成集语音通信、计算机网络、安全防范和智能控制集成的智能图书信息大楼。 湖北交通职业技术图书馆信息大楼综合布线工程需求分析 湖北交通职业技术图书馆信息大楼综合布线工程总共有信息点848个,其中数据点424个,语音点为424个,光纤点18个,配线间与中心机房之间用光纤联接,计算机实训室也才用光纤接入的方式,具体详见综合布线点位表。 经过了解,湖北交通职业技术图书馆信息大楼综合布线工程项目的布线系统具有以下的特点: ⑴应用系统种类非常丰富,部分传输信息要求绝对安全、可靠; ⑵临时变换性强,如遇大型活动,一些场所会对现场通信环境提出很高要求; ⑶信息扩充需求量大,在布线方案中,要有充足的预留考虑,以避免出现部分区域楼层布线资源告急的情况; ⑷办公调整较多,对信息管理要求较高,需及时修改更新; ⑸新技术应用较快,对布线会不断提出新的应用要求。 单元一 认识综合布线 单元一 认识综合布线 设计标准 ISO/IEC 11801:2002——用户建筑-通用布线系统信息技术国际标准 ANSI/TIA/EIA-568C—商业大楼通信布线标准 ANSI/TIA/EIA-569B—商业大楼信道和空间管理标准 ANSI/TIA/EIA-607—商业大楼接地和联接标准 ANSI/TIA/EIA-TSB72 开放办公室布线系统 EMC Standard EN55022——(电磁兼容)标准 EN50173:2001—信息技术-综合布线系统(欧洲标准) GB/T 50311-2007 建筑与建筑群综合布线系统工程设计规范 GB/T 50312-2007 建筑与建筑群综合布线系统工程施工与验收规范 GB/T18233-2008 信息技术-用户建筑群的通用布线 GB/T7427-87 通讯光缆的一般要求 YD/T926.2 通信行业标准 YD/T926 1-2-2001——大楼通信综合布线系统 IEEE 802.3、IEEE802.5以太网 单元一 认识综合布线 网线、网络模块、配线架等产品都必须提供最近三年内“质量认证中心”检测报告。其中网线、模块、面板、配线加、跳线考虑到系统兼容性和以后的售后服务均要求采用同一品牌;跳线采用原厂商成型跳线 国内、外均产品包装要求提供中文厂名,中文厂址、、许可证号、产品标志、生产日期、中文产品说明书等;国外产品必须要提供报关单,原厂商证明原件 综合布线系统是连接本招标书要求的内部及外部数据,图象,显示信号及多媒体信号的传输通道,它不但必须满足当前的业务处理需求,更需要考虑今后的通讯及宽带网络发展需求,提供原厂商20年质量及系统保证书。综合布线系统必须符合国际标准ISO/IEC11801,EIA\TIA-568B2.1,ITU-T国际电联等组织颁布的最新颁布的对五类铜缆布线,IEEE802.3ae万兆以太网及各子系统的标准和规定。 单元一 认识综合布线 根据目前了解的结构分布情况,整个布线系统由工作区子系统、水平子系统、楼层管理区子系统、主干子系统、总设备间子系统五个部分组成。主要考虑正常的语音,数据通信用途,布线系统的定位以目前主流的形式为主,如下: 数据骨干采用光纤,语音主干三类大对数线;语音、数据水平布线采用六类布线. 单元一 认识综合布线 图书馆计算机中心机房位于6层,在每一层设一个楼层配线间,楼层配线间至中心机方的核心交换机采用烽火6芯室内光缆连接。 语音主干采用非屏蔽三类大对数电缆。从中心机
269KB
CISCO 技术大集合
2013-05-22CISCO 技术大集合 {适合你们的技术} 二、命令状态 1. router> 路由器处于用户命令状态,这时用户可以看路由器的连接状态,访问其它网络和主机,但不能看到和更改路由器的设置内容。 2. router# 在router>提示符下键入enable,路由器进入特权命令状态router#,这时不但可以执行所有的用户命令,还可以看到和更改路由器的设置内容。 3. router(config)# 在router#提示符下键入configure terminal,出现提示符router(config)#,此时路由器处于全局设置状态,这时可以设置路由器的全局参数。 4. router(config-if)#; router(config-line)#; router(config-router)#;… 路由器处于局部设置状态,这时可以设置路由器某个局部的参数。 5. > 路由器处于RXBOOT状态,在开机后60秒内按ctrl-break可进入此状态,这时路由器不能完成正常的功能,只能进行软件升级和手工引导。 6. 设置对话状态 这是一台新路由器开机时自动进入的状态,在特权命令状态使用SETUP命令也可进入此状态,这时可通过对话方式对路由器进行设置。 返回目录 三、设置对话过程 1. 显示提示信息 2. 全局参数的设置 3. 接口参数的设置 4. 显示结果 利用设置对话过程可以避免手工输入命令的烦琐,但它还不能完全代替手工设置,一些特殊的设置还必须通过手工输入的方式完成。 进入设置对话过程后,路由器首先会显示一些提示信息: --- System Configuration Dialog --- At any point you may enter a question mark '?' for help. Use ctrl-c to abort configuration dialog at any prompt. Default settings are in square brackets '[]'. 这是告诉你在设置对话过程中的任何地方都可以键入“?”得到系统的帮助,按ctrl-c可以退出设置过程,缺省设置将显示在‘[]’中。然后路由器会问是否进入设置对话: Would you like to enter the initial configuration dialog? [yes]: 如果按y或回车,路由器就会进入设置对话过程。首先你可以看到各端口当前的状况: First, would you like to see the current interface summary? [yes]: Any interface listed with OK? value "NO" does not have a valid configuration Interface IP-Address OK? Method Status Protocol Ethernet0 unassigned NO unset up up Serial0 unassigned NO unset up up ……… ……… … …… … … 然后,路由器就开始全局参数的设置: Configuring global parameters: 1.设置路由器名: Enter host name [Router]: 2.设置进入特权状态的密文(secret),此密文在设置以后不会以明文方式显示: The enable secret is a one-way cryptographic secret used instead of the enable password when it exists. Enter enable secret: cisco 3.设置进入特权状态的密码(password),此密码只在没有密文时起作用,并且在设置以后会以明文方式显示: The enable password is used when there is no enable secret and when using older software and some boot images. Enter enable password: pass 4.设置虚拟终端访问时的密码: Enter virtual terminal password: cisco 5.询问是否要设置路由器支持的各种网络协议: Configure SNMP Network Management? [yes]: Configure DECnet? [no]: Configure AppleTalk? [no]: Co
175KB
IPv6.rar
2012-07-25IPv4 包头为12字段 (点分十进制) IPv6 包头为8字段 (冒号分16进制) 共8个小节,每小节4个16bit IPV6地址=前缀+接口标识 <为何要部署IPV6> ·IPv4的局限性: 1.地址空间的局限性:IP地址空间的危机由来已久,并正是升级到IPv6的主要动力。 2.安全性:IPv4在网络层没有安全性可言,安全性一直被认为是由网络层以上的层负责。 3.自动配置:对于IPv4节点的配置比较复杂,让很多普通用户无所适从。 4.NAT:破坏了Internet端到端的网络模型。 5.由于IPv4地址分配杂乱无章,没有层次性,网络设备需要维护庞大的路由表项。 6.IPv4包头过于复杂,使得网络节点处理的效率不高。 IPV6的好处: 1、超大的地址空间 2、全球可达性,不需要再用NAT 3、全球重新部署,有规划,易于实现聚合 4、能自动配置,实现即插即用 5、方便的进行重编址 6、包头简单,通过扩展包头技术可实现以后的新技术扩展 (基本包头 + n多个扩展包头) ipv4 路由转发的时候,ip包会改变checksum(校验和) 和TTL(每经过一个路由器TTL值减一) ipv6 只变TTL,没有校验和 CPU现在无法实现128位的转发。 最好只是64位的。 ·Theoretical limit: 4.3 billion (十亿) 43亿 Practical limit : 250 million (百万) 2.5亿 Over 420 million Internet in Y2001 (less than 10% of the worldwide population) 没有广播,组播代替广播。所以没有ARP。 IPv4中的广播(broadcast)可以导致网络性能的下降甚至广播风暴(broadcast storm).在IPv6中,就不存在广播这一概念了,取而代之的是组播(multicast)和任意播(anycast),任意播也称为泛播. IPV6在以太网中的协议ID值是0x86DD <IPV6地址的表示方法> ·IPv4 点分十进制 32bit IPv6 冒号分十六进制 128bit 0000:0000:0000:0000:0000:0000:0000:0000=>:: 0000:0000:0000:0000:0000:0000:0000:0001=>0:0:0:0:0:0:0:1=>::1 2001:0000:0000:1234:0000:0000:0567:00ff=>2001::1234:0:0:567:ff 只能有一个:: fe80:0000:0000:0000:0000:0000:0000:0009=>fe80::9 URL的IPV6地址表示 为了区分IPV6地址中的冒号和端口号前的冒号,要把IPV6地址用[]括起来 www.example.net:8080/index.html https:[2001:410:0:1:250:fcee:e450:33ab]:8443/abc.html IPV6中掩码的表示: 在IPV6中掩码只能使用CIDR表示法 2001:410:0:1::45ff/128 2001:410::1/64 注意:在IPV6中没有广播地址和网络号保留地址 ------------------------------------------------------------------------------------------ <IPV6的地址类型> 可分为三大类: 1、单播地址 2、组播地址 3、任意播地址 单播--Unicast : one to one ·单播地址用于一对一的连接 ·IPv6单播地址有以下六种类型: 1-Aggregate Global Unicast Address 2xxx:xxxxx/3 - 3FFF: :FFFF 2001::/16 IPV6因特网地址 2002::/16 6to4过渡地址 2-Link Local Address FE80::/10 (前10位以FE80开头) 3-Site Local Address (Private) FEC0::/10 4-Unspecified Address 0:0:0:0:0:0:0:0/128 => ::/128 5-Loopback Address 0:0:0:0:0:0:0:1/128 => ::1/128 6-IPv4 Compatible Address ::192.168.30.1 => ::C0A8:1E01 以下是一些单播地址的具体说明: 1、可聚合全球单播地址Aggregate global unicast address 由IANA分配的可在全球路由的公网IP地址 目前已分配的前缀:2000::/3 占用了12.5%的IPV6地址空间 2000:0000:0000:0000:0000:0000:0000:0000--3FFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF 这个前缀中总共包含8192个/16的前缀 目前实际用于IPV6因特网运作的前缀:2001::/16 2002::/16 为使用6-TO-4过渡机制的节点保留 3ffe::/16 用于6bone测试目的的前缀 2、本地链路地址:link-local address 当在一个节点启用IPV6,启动时节点的每个接口自动生成一个link-local address 其前缀64位为标准指定的,其后64位按EUI-64格式来构造 注意:在本链路上,路由表中看到的下一跳都是对端的Link Local地址,不是公网IP地址 前缀:FE80::/10 范围:只能在本地链路使用,不能在子网间路由 为何需要link-local:在一个接口可以配置很多IPv6地址,所以学习路由就有可能出现很多下一跳。 所以出现Link Local地址唯一标识一个节点。在本地链路看到下一跳都是对端的Link Local地址。 在网络重新编址过程中,节点和路由器的Link Local地址不会发生变化,可以很容易地做一个修改,不用担心网络不可达。 R1(config-if)#ipv6 address FE80:0:0:0:0123:0456:0789:0abc link-local 手工指定link-local地址 3、本地站点地址:site-local address IPV6的私网地址,就像IPV4中的私网保留地址一样 只占用到整个IPV6地址空间的0.1% 前缀:FEC0::/10 其后的54比特用于子网ID 最后64位用于主机ID 范围:只能在本站点内使用,不能在公网上使用 例如:在本地分配十个子网 1、FEC0:0:0:0001::/64 2、FEC0:0:0:0002::/64 3、FEC0:0:0:0003::/64 10、FEC0:0:0:000A::/64 本地站点地址被设计用于永远不会与全球IPV6因特网进行通信的设备,比如:打印机、内部网服务器、网络交换机等 4、未指定地址Unspecified address 形式:0:0:0:0:0:0:0:0 表示地址未指定,或者在写默认路由时代表所有路由 5、回环地址Loopack address 形式:0:0:0:0:0:0:0:1 同IPV4中127.0.0.1地址的含义一样,表示节点自已 6、内嵌IPV4地址的IPV6地址 IPv4 Compatible Address 1、IPV4兼容的IPV6地址--用于在IPV4网络上建立自动隧道,以传输IPV6数据包。 其中高96bit设为0,后面跟32bit的IPV4地址 0000:0000:0000:0000:0000:0000:206.123.31.2 0000:0000:0000:0000:0000:0000:ce7b:1f01 由于这种机制不太好,现在已经不再使用,转而采用更好的过渡机制 2、映射IPV4的IPV6地址--仅用于拥有IPV4和IPV6双协议栈节点的本地范围 其中高80bit设为0,后16bit设为1,再跟IPV4地址 0000:0000:0000:0000:0000:ffff:206.123.31.2 0000:0000:0000:0000:0000:ffff:ce7b:1f01 EUI-64格式:扩展惟一标识符 在IPV6中,无状态自动配置机制使用EUI-64格式来自动配置IPV6地址 所谓无状态自动配置是指在网络中没有DHCP服务器的情况下,允许节点自行配置IPV6地址的机制。 手工设置LINK--LOCAL地址后,EUI-64地址自动用LINK-LOCAL后64位用到EUI后64BIT上。 EUI-64的构造规则--根据接口的MAC地址再加上固定的前缀来生成一个IPV6的地址 工作原理:自动将48bit的以太网MAC地址扩展成64bit,再挂在一个64bit的前缀后面,组成一个IPV6地址 一、将48位的MAC地址从中间分开,插入一个固定数值FFFE 0050:3EE4:4C00-->0050:3EFF:FEE4:4C00 二、将第7个比特位反转,如果原来是0,就变为1,如果原来是1,就变为0 0050:3EFF:FEE4:4C00-->0250:3EFF:FEE4:4C00 三、加上前缀--FE80::0250:3EFF:FEE4:4C00 这就是一个完整的IPV6地址 反转的原因: 在MAC地址中,第7比特为1表示本地管理,为0表示全球管理 在EUI-64格式中,第7位为1表示全球惟一,为0表示本地惟一 组播地址Multicast 在IPV6中没有广播,用组播来代替 前缀:FF00::/8 占用了0.38%的IPV6地址空间 1111 1111 4bit 4bit |→固定值←||→标志←| |→范围←| 标志位为0000表示是永久保留的组播地址,分配给各种技术使用 标志位为0001表示是用户可使用的临时组播地址 范围段定义了组播地址的范围,其定义如下: 二进制 十六进制 范围类型 0001 1 本地接口范围 0010 2 本地链路范围 0011 3 本地子网范围 0100 4 本地管理范围 0101 5 本地站点范围 类似组播的私网地址 1000 8 组织机构范围 1110 E 全球范围 类似组播的公网地址 下面是一些组播指定地址: FF02::1 all nodes 在本地链路范围的所有节点 FF02::2 all routers 在本地链路范围的所有路由器 FF02::5 all ospf routers FF02::9 all rip routers 所有运行RIP的路由器 FF02::A all eigrp routers 所有运行eigrp的路由器 FF05::2 在一个站点范围内的所有路由器 被请求节点的组播地址solicited-node multicast address--重要的东东 一种特殊的组播地址,对于节点或路由器的接口上配置的每个单播和任意播地址,都会自动生成一个对应的被请求节点组播地址。注意link-local address也会生成一个被请求节点的组播地址。 工作范围:只在本地链路上有效 特点:1、在本地链路上,被请求节点的组播地址组中通常只包含一个用户 2、只要知道一个节点的IPV6地址,就能计算出它的被请求节点的组播地址 作用:1、在IPV6中,没有ARP。ICMP代替了ARP的功能,被请求节点的组播地址被节点用来获得相同本地链路上邻居节点的链路层地址 2、用于重复地址检测DAD,在使用无状态自动配置将某个地址配置为自已的IPV6地址之前,节点利用DAD 验证在其本地链路上该地址是否已经被使用。 前缀:FF02:0000:0000:0000:0000:0001:FFxx:xxxx/104 FF02::1:FFxx:xxxx/104 如何产生:被请求节点组播地址的前104位固定,将IP地址的后24位移下来填充到后面就可以了 例如:IPv6---2001::1234:5678/64 被请求节点组播地址---FF02::1:FF34:5678/104 其中FF02::1:FF为固定部分,共104位 组播IP地址到MAC地址的映射: 映射规则:组播MAC地址的前16位固定为0x3333,将组播IPV6地址的后32位直接映射到组播MAC地址的后32位就可以了。 例:IPV6地址为--FF12::1234:5678/64 对应的组播MAC地址为--3333:1234:5678 0x3333为固定前缀 例:所有节点的组播地址:FF02::0001 对应的组播MAC地址:3333:0000:0001 任意播地址Anycast address 应用在one-to-nearest(一到近)模式 任意播是多个设备共享一个地址.分配IPv6单播(unicast)地址给拥有相同功用的一些设备.发送方发送一个以任意播为目标地址的包,当路由器接受到这个包以后,就转发给具有这个地址的离它最近的设备.单播地址用来分配任意播地址.对于那些没有配备任意播的的地址就是单播地址;但是当一个单播地址分配给不止一个接口的时候,单播地址就成了任意播地址。 例如:Mobile方面的特性,移动设备漫游到其他区域,不必接入原始的接入点,只需要找到最近的即可。 必须的IPV6地址: 一旦节点启用IPV6,那么接口就会自动生成下列地址 1、本地链路地址 2、回环地址 3、所有节点多播地址FF02::1 4、如果是路由器,还会有FF02::2 5、被请求节点的组播地址 如果接口配了一个IPV6的单播地址,还会产生被请求节点的组播地址 《如何在帧中继中写MAP》 注意:必须写两条MAP,一条是所配IPV6地址的MAP,一条是link-local地址的MAP(该加brocast的时候要加) 否则会由于没有MAP映射导致无法PING通。 <IPV6的基本命令> R1(config)#ipv6 unicast-routing 在路由器上开启IPV6路由功能 R1(config-if)#ipv6 enable 在接口下启用IPV6,会自动生成一个link-local地址 R1(config-if)#ipv6 address 2001::1/64 指定一个IP地址,配置后会自动生成一个link-local地址 R1(config-if)#ipv6 address FE80:0:0:0:0123:0456:0789:0abc link-local 手工指定link-local地址(优于自动生成) R1(config-if)#ipv6 address 2001:0410:0:1::/64 eui-64 使用eui-64格式自动生成IPV6地址的低64位 R1(config-if)#ipv6 unnumbered 让本接口使用另一个接口的MAC地址生成源地址 R1(config-if)#ipv6 mtu 1500 配置接口的MTU值 R1(config-if)#ipv6 nd suppress-ra 关闭自动下发前缀 R2(config-rtr)#no split-horizon 关闭水平分割 注意IPV6的水平分割是在进程下关闭,不是在接口下 show ipv6 interface e0 显示IPV6接口的信息,包括IPV6地址,link-local地址,加入的组播地址及被请求节点组播地址 注意:串口和loopback口会借用以太口的MAC地址来生成link-local地址。 ------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------ 《ICMP V6》 ICMP用来向源节点报告关于向目的地传输IP数据包的错误和信息。定义了多种消息类型。 ICMP V6在IPV6中的协议号是58 在IPV6中ICMP的功能得到极大增强,除了原有的功能,还有以下几个主要作用 在IPV6中ICMP的主要功能: 1、路径MTU发现--PMTUD 2、替代地址解析协议--NDP 3、无状态自动配置--NDP 4、重复地址检测--NDP 5、前缀重新编址--NDP 一、PMTUD path MTU discovery 作用:使用ICMP来检测数据包传输路径上最小的MTU值,检测到后使用ICMP 类型2消息(数据包超长)告知源节点,这样分片的工作在源路由器上就可完成,中间路由器不必做这一工作。原理如下: PMTUD使用ICMPV6的类型2消息--数据包超长错误消息 Router#show ipv6 mtu 显示源节点缓存的去每一个目的地的PMTUD值 二、在IPV6中使用NDP 为NDP特有的范畴定义了新的ICMPV6消息: NS (Neighbor Solicitation) 135 邻居请求 NA (Neighbor Advertisement) 136 邻居通告 RS 133 发到FF02::2 路由器请求信息,发给所有路由器 RA 134 发到FF02::1 路由器通告信息,每200S发一次,发向所有节点 类型137 重定向消息 三、替代ARP 使用NS 135和NA 136消息 主机使用邻居请求消息NS135发向邻居的被请求节点组播地址,请求一个MAC地址,邻居回应一个NA136邻居通告消息 show ipv6 neighbors 显示IPV6邻居的地址,生存期、链路层地址、和去往邻居的接口 router#ipv6 neighbor fec0::1:0:0:1:b e0 0080:12ff:6633 静态加入一个邻居项 clear ipv6 neighbors 清除邻居发现表 默认情况下邻居请求消息1000ms发送一次,可用以下命令修改: router(config-if)#ipv6 nd ns-interval 1000 router(config-if)#ipv6 nd reachable-time 1800000 设置邻居的可达时间间隔,默认30分钟,如果30分钟内还没再收到邻居的消息,就会从邻居发现表中删除邻居表项 四、公告前缀--类似于DHCP 路由器周期性地发出RA134公告,每200S一次,主机收到后自动生成IP地址 只要在路由器的接口上配置了一个本地站点或全球可聚合单播地址,就启用了CISCO路由器上的IPV6前缀通告 默认情况下:无状态自动配置公告的前缀长度为64比特 RS 133 发到FF02::2 请求信息,发给所有路由器 RA 134 发到FF02::1 通告信息,每200S发一次,发向所有节点 注意:每一条被通告的前缀都有生存期-- 生存期:每一个被公告前缀都有生存期,可以从0到无穷大,路由器会不断检查这个值,生存期又分两种 有效生存期:主机节点保持有效状态的时间。 首选生存期:必须小于或等于有效生存期,到期后,IPV6地址不能主动去建立新的连接,但可以在有效生存期没过时之前接受别的连接。通常用于前缀重新编址。 在默认情况下,cisco设备中,有效生存期为30天,首选生存期为7天 Autoconfiguration自动配置 来看下IPv6接口的自动配置--当本地链路的路由器发送网络类型信息(前缀)给所有节点的时候.支持IPv6的主机就把它自己64位的链路层地址(即MAC地址)附着在64位的前缀后面按EUI-64格式自动配置成128位长的地址,保证地址的唯一性.自动配置启用即插即用(Plug and Play) show ipv6 interface e0 prefix 显示路由器接口上公告的前缀的参数 R2(config-if)#ipv6 address autoconfig 用路由器模拟主机时,允许这个接口使用无状态自动配置 ,默认情况下路由器不允许使用无状态自动配置 R1(config-if)#ipv6 nd ra-lifetime 1000000 设定路由器公告消息(RA134)的生存期,默认情况下为30min R1(config-if)#ipv6 nd ra-interval 200 设定路由器公告消息的时间间隔,默认是200S R1(config-if)#ipv6 nd prefix 2001:1:1::/64 20000 10000 改写前缀公告的参数,后面分别是有效时间和首选时间 R1(config-if)#no ipv6 nd prefix 2001:1:1::/64 不通告本前缀(nnd,这条命令咋不起作用) R1(config-if)#ipv6 nd suppress-ra 基于接口关闭路由器公告 R1(config-if)#ipv6 nd managed-config-flag 在主机节点上启用有状态自动配置 debug ipv6 nd 调试前缀公告信息 五、重复地址检测<DAD------Duplicate Address Detection> 使用邻居请求消息NS 135 用于确定准备配置的IPV6地址在网络上是否唯一。 六、重编址 路由器发送组播数据包,其中数据包中包含2个前缀,一个是拥有比较短的生存期的旧前缀,还有一个是新的拥有正常时间的前缀.通知网络上的节点用完旧的前缀后换成新的前缀,这样就能进行平滑的前缀过渡 也是使用RS和RA消息 七、路由器重定向 使用重定向消息,类型为137 路由器使用ICMPV6重定向消息通知链路上的节点,在链路上存在一个更好的转发数据包的路由器. 默认是启用的 R1(config-if)#no ipv6 redirects 关闭重定向 八、Ping echo 128 echo reply 129 《静态路由》 建议写法: ipv6 route 2001::/64 e0 fe80::1234:abcd:1234:abcd (下一跳的link local地址) ----------------------------------------------------------------------------------------- <RIP>--ripng 在IPV6中使用UDP521端口,在IPV4中是520端口 使用组播地址:FF02::9 操作半径15跳 R1(config)#ipv6 unicast-routing R1(config)#ipv6 router rip yucedu 必须要有一个进程号 R1(config-if)#ipv6 rip yucedu enable 必须进入接口下开启接口的RIP Show ipv6 route Show ipv6 route rip show ipv6 rip show ipv6 rip database IPV6的metric值出口和入口都加1. IPV6的水平分割是整个路由器开启(在进程中关闭),IPV4是在接口下开关的,在帧中继的HUB-spoke模式中要关闭水平分割 <OSPF> 在IPV6中使用的是OSPFV3版 R1(config)#Ipv6 router ospf 110 R1(config-router)#Router-id 2.2.2.2 注意:必须使用一个类似IPV4地址的标识,必须手工指定,不能自动选 R1(config)#Int s0 R1(config-if)#Ipv6 ospf 110 area 0 也是在接口下宣告 R1(config)#Int lo0 R1(config-if)#Ipv6 ospf 110 area 0 环回口依然是主机路由,128位,可通过改网络类型来改动 Show ipv6 route ospf R1(config)#Int s0 R1(config-if)#Ipv6 ospf neighbor 2001::2 注意OSPF手工指邻居在接口下做,而IPV4是在进程下做 <is-is> R1(config)#Router isis R1(config-router)#Net 49.0001.2222.2222.2222.00 R1(config-router)#Log-adjacency-changes all 当邻居起来时出个提示 R1(config)#Int s0 R1(config-if)#Ipv6 router isis IPV4的ISIS也是在接口下启用 重分布直连: R1(config)#Router isis R1(config-router)#Redistribute connected 在IPV4中直接在ISIS进程中重分布 R1(config)#Router isis R1(config-router)#Address-family ipv6 在IPV6中重分布必须进这一进程 R1(config-router-af)#Redistribute connected 注意重分布命令必须在address-family进程中用 《BGP》 使用TCP179端口,和IPV4中一样 R1(config)#Router bgp 3 R1(config-router)#No autosummary R1(config-router)#bgp router-id 3.3.3.3 (一定要手工指定) R1(config-router)#No synchronization R1(config-router)#Neighbor 2001:13::1 remote-as 1 R1(config-router)#Address-family ipv6 注意进入address-family进程下 R1(config-router-af)#Neighbor 2001:13::1 activate 必须在address-family进程下激活,否则不起效 R3(config-router-af)#network 3::/64 也要在address-family下宣告 注意:IBGP中指下一跳也要在addres-family Show bgp ipv6 neighbor Show bgp ipv6 summary 注意bgp与ipv6反过来了 Show bgp ipv6 查看路由 Clear ip bgp * 清邻居的命令和IPV4中一样 《IPV6 ACL》 在IPV6中,ACL必须命名,写法类似IPV4命名访问列表 1、标准的访问列表可以基于源和目的进行过滤 2、扩展的访问列表可以基于源地址、目的地址、传输层协议、源端口、目的端口及其他特性进行过滤。 事实上IPV6没有标准和扩展之分了。 例一: Ipv6 access-list yucedu Deny ipv6 2001:12::2/128 any Permit ipv6 any any Int s0 Ipv6 traffic-filter yucedu out 在接口下调用 Ipv6的access-list语句的后面默认隐含三条语句: Permit icmp any any nd-ns Permit icmp any any nd-na Deny ipv6 any any 例二: ipv6 access-list yucedu permit tcp any host 2001::1 eq telnet 在接口应用了访问列表后,P包不通会提示AAAAA Prefix-list 例:在R1上,只允许2002:1::/64和2001:12::/64的路由传给R3 ipv6 prefix-list yucedu permit 2001:12::/64 ipv6 prefix-list yucedu permit 2002:1::/64 默认最后有deny any ipv6 router rip xwx distribute-list prefix-list yucedu out Serial1 clear ipv6 rip xwx 在IPV6中,分布列表后只能跟prefix,不能跟ACL ----------------------------------------------------------------------------------------- 《IPV4到IPV6的过渡解决方案》 IPV6 over IP Tunnel方案 Tunnel R2(config)#int tunnel 2 R3(config)#int tunnel 3 tunnel source 2.2.2.2 tunnel source 3.3.3.3 tunnel destination 3.3.3.3 tunnel destination 2.2.2.2 tunnel mode ipv6ip tunnel mode ipv6ip ipv6 enable ipv6 enable show ipv6 interface tunnel 2 注意在采用IPV6IP模式时,会用Tunnel源IP地址来生成本Tunnel的link-local地址,即使你不用ipv6 enable命令,而采用ipv6 unnumbered 命令借用别的地址,结果还是一样。 R2(config)#ipv6 router ospf 110 R2(config-router)#router-id 2.2.2.2 R2(config)#int tunnel 2 R2(config-if)#ipv6 ospf 110 area 0 R2(config)#int e0 R2(config-if)#ipv6 ospf 110 area 0 <6 TO 4>自动Tunnel方案 特点: 1、自动tunnel 2、只要指一条静态路由就OK 前提:首先要注意E0接口的地址必须和源地址配合,且必须用2002这一前缀开头 R2(config)#int tunnel 2 R3(config)#int tunnel 3 tunnel source 2.2.2.2 tunnel source 3.3.3.3 tunnel mode ipv6ip 6to4 tunnel mode ipv6ip 6to4 ipv6 enable ipv6 enable 实际上会按源地址生成link-local地址 R2(config)#ipv6 route 2002::/16 tunnel 2 R3(config)#ipv6 route 2002::/16 tunnel 3 必须写上静态路由
高并发下的Nginx性能优化实战
2019-12-24<p> <b><span style="background-color:#FFE500;">【超实用课程内容】</span></b> </p> <p> <br /> </p> <p> <br /> </p> <p> 本课程内容包含讲解<span>解读Nginx的基础知识,</span><span>解读Nginx的核心知识、带领学员进行</span>高并发环境下的Nginx性能优化实战,让学生能够快速将所学融合到企业应用中。 </p> <p> <br /> </p> <p style="font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;"> <b><br /> </b> </p> <p style="font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;"> <b><span style="background-color:#FFE500;">【课程如何观看?】</span></b> </p> <p style="font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;"> PC端:<a href="https://edu.csdn.net/course/detail/26277"><span id="__kindeditor_bookmark_start_21__"></span></a><a href="https://edu.csdn.net/course/detail/27216">https://edu.csdn.net/course/detail/27216</a> </p> <p style="font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;"> 移动端:CSDN 学院APP(注意不是CSDN APP哦) </p> <p style="font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;"> 本课程为录播课,课程永久有效观看时长,大家可以抓紧时间学习后一起讨论哦~ </p> <p style="font-family:"color:#3A4151;font-size:14px;background-color:#FFFFFF;"> <br /> </p> <p class="ql-long-24357476" style="font-family:"color:#3A4151;font-size:14px;background-color:#FFFFFF;"> <strong><span style="background-color:#FFE500;">【学员专享增值服务】</span></strong> </p> <p class="ql-long-24357476" style="font-family:"color:#3A4151;font-size:14px;background-color:#FFFFFF;"> <b>源码开放</b> </p> <p class="ql-long-24357476" style="font-family:"color:#3A4151;font-size:14px;background-color:#FFFFFF;"> 课件、课程案例代码完全开放给你,你可以根据所学知识,自行修改、优化 </p> <p class="ql-long-24357476" style="font-family:"color:#3A4151;font-size:14px;background-color:#FFFFFF;"> 下载方式:电脑登录<a href="https://edu.csdn.net/course/detail/26277"></a><a href="https://edu.csdn.net/course/detail/27216">https://edu.csdn.net/course/detail/27216</a>,播放页面右侧点击课件进行资料打包下载 </p> <p> <br /> </p> <p> <br /> </p> <p> <br /> </p>
Java8零基础入门视频教程
2016-09-29这门课程基于主流的java8平台,由浅入深的详细讲解了java SE的开发技术,可以使java方向的入门学员,快速扎实的掌握java开发技术!
Java基础与实践
2018-07-31Java语言是目前流行的一门程序设计语言。本课程是一套全面讲解Java语言程序设计的开发类课程,由浅入深地介绍Java基础内容,主要包括基本类型及运算符、控制执行流程、字符串、面向对象、集合与数组、文件及流、异常、多线程等完整的Java知识体系。
手把手教你蓝牙协议栈入门
2020-07-16<p> 本课程定位是:引领想学习蓝牙协议栈的学生或者从事蓝牙,但是对蓝牙没有一个系统概念的工程师快速入门 </p> <p> 课程是多年从事蓝牙经验总结出来的,希望能让你看完有一种醍醐灌顶的感觉。 </p> <p> 不要在摸着石头过河了·学习完这些你肯定还是要继续学习蓝牙协议栈,但是至少懂了蓝牙的一些概念以及适合高效的学习方法 </p> <p> 本课程一共分为4个小节: </p> <p> 1)蓝牙教程计划.mp4 ,主要介绍下我们的视频规划以及后续的蓝牙教程规划 </p> <p> 2)蓝牙的前生后世.mp4 主要介绍下蓝牙的产生背景概念,以及蓝牙从开始产生到现在最新的5.2的发展过程,新赠的功能特性 </p> <p> 3)市面蓝牙架构调查.mp4 主要介绍市面蓝牙产品的架构以及HCI蓝牙芯片的详细架构,让你对蓝牙有一个整体的认识,对于后续做蓝牙产品选型大有帮助 </p> <p> 4)快速学习蓝牙文档介绍_工具介绍.mp4 主要介绍HCI蓝牙芯片的协议栈以及profile获取途径以及学习蓝牙的高效工具,引领你快速找到适合自己的方法来学习蓝牙 </p>
基于SSM技术的在线商城系统[实战视频]
2018-07-04本课程基于【SSM】【Maven】【BootStrap】【MySQL】【BootStrap】技术,使用IntelliJ IDEA开发工具。 主要是锻炼SSM技术的运用,通过项目实战,加强对框架技术的理解和运用,如果你是SSM的初学者,这套视频课程适合你!!
C语言入门--必须基础17讲
2017-07-28适合没有基础的人群学习C语言,简单的入门教程。帮助小白理解什么是开发,什么是编程。做的很简单,很多细节没有详细讲解,不适合用来深入研究。学了这个,你能理解什么是编程,什么是C语言。
SpringBoot实战教程:SpringBoot企业级线上商城项目讲解
2019-09-27<div style="color:rgba(0,0,0,.75);"> <span style="color:#4d4d4d;"> </span> <div style="color:rgba(0,0,0,.75);"> <span style="color:#4d4d4d;"> </span> <div style="color:rgba(0,0,0,.75);"> <div style="color:rgba(0,0,0,.75);"> <span style="color:#4d4d4d;">当前课程中商城项目的实战源码是我发布在 GitHub 上的开源项目 newbee-mall (新蜂商城),目前已有 6300 多个 star,</span><span style="color:#4d4d4d;">本课程是一个 Spring Boot 技术栈的实战类课程,课程共分为 3 大部分,前面两个部分为基础环境准备和相关概念介绍,第三个部分是 Spring Boot 商城项目功能的讲解,让大家实际操作并实践上手一个大型的线上商城项目,并学习到一定的开发经验以及其中的开发技巧。<br /> 商城项目所涉及的功能结构图整理如下:<br /> </span> </div> <div style="color:rgba(0,0,0,.75);"> </div> <div style="color:rgba(0,0,0,.75);"> <p style="color:#4d4d4d;"> <img alt="modules" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9uZXdiZWUtbWFsbC5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vcG9zdGVyL3N0b3JlL25ld2JlZS1tYWxsLXMucG5n?x-oss-process=image/format,png" /> </p> </div> <p style="color:rgba(0,0,0,.75);"> <strong><span style="color:#e53333;">课程特色</span></strong> </p> <p style="color:rgba(0,0,0,.75);"> </p> <div style="color:rgba(0,0,0,.75);"> </div> <div style="color:rgba(0,0,0,.75);"> <ul> <li> 对新手开发者十分友好,无需复杂的操作步骤,仅需 2 秒就可以启动这个完整的商城项目 </li> <li> 最终的实战项目是一个企业级别的 Spring Boot 大型项目,对于各个阶段的 Java 开发者都是极佳的选择 </li> <li> 实践项目页面美观且实用,交互效果完美 </li> <li> 教程详细开发教程详细完整、文档资源齐全 </li> <li> 代码+讲解+演示网站全方位保证,向 Hello World 教程说拜拜 </li> <li> 技术栈新颖且知识点丰富,学习后可以提升大家对于知识的理解和掌握,可以进一步提升你的市场竞争力 </li> </ul> </div> <p style="color:rgba(0,0,0,.75);"> </p> <p style="color:rgba(0,0,0,.75);"> <span style="color:#e53333;">课程预览</span> </p> <p style="color:rgba(0,0,0,.75);"> </p> <div style="color:rgba(0,0,0,.75);"> </div> <div style="color:rgba(0,0,0,.75);"> <p style="color:#4d4d4d;"> 以下为商城项目的页面和功能展示,分别为: </p> </div> <div style="color:rgba(0,0,0,.75);"> <ul> <li> 商城首页 1<br /> <img alt="" src="https://img-bss.csdnimg.cn/202103050347585499.gif" /> </li> <li> 商城首页 2<br /> <img alt="" src="https://img-bss.csdn.net/202005181054413605.png" /> </li> <li> </li> <li> 购物车<br /> <img alt="cart" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9uZXdiZWUtbWFsbC5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vcG9zdGVyL3Byb2R1Y3QvY2FydC5wbmc?x-oss-process=image/format,png" /> </li> <li> 订单结算<br /> <img alt="settle" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9uZXdiZWUtbWFsbC5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vcG9zdGVyL3Byb2R1Y3Qvc2V0dGxlLnBuZw?x-oss-process=image/format,png" /> </li> <li> 订单列表<br /> <img alt="orders" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9uZXdiZWUtbWFsbC5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vcG9zdGVyL3Byb2R1Y3Qvb3JkZXJzLnBuZw?x-oss-process=image/format,png" /> </li> <li> 支付页面<br /> <img alt="" src="https://img-bss.csdn.net/201909280301493716.jpg" /> </li> <li> 后台管理系统登录页<br /> <img alt="login" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9uZXdiZWUtbWFsbC5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vcG9zdGVyL3Byb2R1Y3QvbWFuYWdlLWxvZ2luLnBuZw?x-oss-process=image/format,png" /> </li> <li> 商品管理<br /> <img alt="goods" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9uZXdiZWUtbWFsbC5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vcG9zdGVyL3Byb2R1Y3QvbWFuYWdlLWdvb2RzLnBuZw?x-oss-process=image/format,png" /> </li> <li> 商品编辑<br /> <img alt="" src="https://img-bss.csdnimg.cn/202103050348242799.png" /> </li> </ul> </div> </div> </div> </div>
Python进阶-Pandas数据分析库
2018-12-18<p> <br /> </p> <p style="font-family:"color:#3D3D3D;font-size:16px;background-color:#FFFFFF;"> 您观看课程学习后<br /> 免费入群领取【超全Python资料包+17本学习电子书】 </p> <p style="font-family:"color:#3D3D3D;font-size:16px;background-color:#FFFFFF;"> <img src="https://img-bss.csdn.net/201909261022146699.jpg" alt="" /> </p> <p> <br /> </p> <p> Pandas是python中非常常用的数据分析库,在数据分析,机器学习,深度学习等领域经常被使用。本课程会讲解到pandas中最核心的一些知识点,包括Series以及DataFrame的构建,赋值,操作,选择数据,合并等等,以及使用pandas对文件进行读取和写入,使用pandas绘图等等。 </p>
Java软件开发工程师全套课程(笔记+项目实战案例)
2020-06-08Java软件开发系列课程,一站式学习全套Java技术。 包含三个阶段课程: 第一阶段: Java基础入门——JavaSE核心技术 本阶段为Java基础入门,包含:初识Java、变量、运算符、选择结构、循环结构、方法、数组、面向对象、抽象类和接口、常用类、枚举、泛型、内部类、集合、异常、I/O、设计模式、数据库、JDBC、项目实战 第二阶段: Java进阶开发——Web开发技术 本阶段为JavaWeb开发技术,包含:HTML、CSS、JavaScript、jQuery、Bootstrap、Servlet、JSP、Ajax、MVC等 第三阶段: Java高级开发——JavaEE框架技术 Java框架技术,包含:IDEA、Maven、MyBatis、Spring、SpringMVC、SpringBoot、SpringCloud、Shiro、Redis、ZooKeeper、Dubbo、Kafka、Nginx、Git、Docker、Vue.js、在线商城实战等 教学全程采用笔记+代码案例的形式讲解,由浅入深,每个知识点都有详细的讲解,通俗易懂!
-
下载
搭建Hadoop HA分布式集群.pdf
搭建Hadoop HA分布式集群.pdf
-
下载
WuziqiGame-master.zip
WuziqiGame-master.zip
-
下载
二值灰度图像水印实验
二值灰度图像水印实验
-
下载
化工实验装置实训设备型号价格表.docx
化工实验装置实训设备型号价格表.docx
-
下载
基于Simhash的安全密文排序检索方案.pdf
基于Simhash的安全密文排序检索方案.pdf
-
下载
热工、采暖空调类实验装置系列产品大全.wps
热工、采暖空调类实验装置系列产品大全.wps
-
下载
基于Simhash算法的海量文本相似性检测方法研究.pdf
基于Simhash算法的海量文本相似性检测方法研究.pdf
-
下载
Labview相关工具包与模块安装.pdf
Labview相关工具包与模块安装.pdf
-
下载
winfrom凭证录入及打印
winfrom凭证录入及打印
-
下载
openssl.rar
openssl.rar
