Perfil de logLogchanFotosBlogListas Herramientas Ayuda
web statistics

Logchan

Space Or Not Space

log chan

Ocupación
Ubicación
Foto 1 de 16
06/06/2008

利用 Java EE 进行开发已经变得困难

J2SE 推出后,以其跨平台及友好的API迅速获得青睐。J2EE的推出,令Java作为首选的
开发环境迅速占领市场。但好景不长。至今为止,以Java为开发环境的开发越来越变得
复杂艰难。Java EE已经变得臃肿庞大,开发台阶与开发成本提高,开发效率下降,维护
成本提高。

原因很多。

其一,对普通用户来说,Java1.5 与Java1.4相比,并没有实质性的提高。Java1.5
增加了些什莫呢?不论 Auto boxing还是 Generics,对Java本身非但没有实质性的
提高,反而将Java糟蹋得够呛。

其二,Java EE 与 J2EE相比,也没有实质性的提高。EJB3只是将后台的配置转移到了
源代码中,利用Annotation来“修饰”源代码。经过一番“修饰”和相关资源“修饰”
性“注入”后,源代码已经很难看了@@@@

其三,这与Java 社区的演变也有紧密地关系,Java 作为开源平台与开源社区有着千丝
万缕的联系。Spring的出现,将Java开发一劈两半儿。EJB or No EJB 看起来是设计
选择问题,其实是严重影响了Java社区的健康发展:Java社区分裂了。

其四,Framework贻害无穷。Java社区的各种各样的所谓开发框架层出不穷,弊端无数。
主要有四:一是推高了Java开发人员的入门门槛,严重加重了Java从业人员的学习负担;
二是各种框架将基本的开发技能封闭在其所谓的框架之内,一般开发人员没有精力,也没
有兴趣研究透彻,失去了学习理解解决问题的基本开发技能的机会,成为真正的软件蓝领;
三是框架的应用,使整个设计复杂化,开发维护复杂困难,效率低下。四是各种框架的配
置复杂,使得开发工作变了味。这配置成了开发工作的拦路虎,到处是XML文件,也不知
道我们是在用Java编程还是在用XML编程。

其五,普通的Java开发人员需要掌握太多技术。以Web Application为例,前台要用框架
提供的标签,另外要懂html,css,javascript,ajax,jsp/servlet;中间流控制要采用框
架的编程接口,还要明白java,jndi,ejb;后台又要用持久化编程接口,又要搞清楚jdbc,
transaction,databases。还要掌握通讯协议,弄清楚满天乱飞的annotation,xml配置文
件;可以想象,一个普通的Java开发人员需要掌握多少技术,熟悉多少API和所谓的框架们
才能干活儿,更别提那各种各样的花钱与不花钱Application Server了?这还没提开发工
具,单元测试及源代码管理,公司业务等等。开发人员一跳槽,这一切都变了:跳不动啊!
你看看猎头公司列出的对Java开发人员的技术要求条件。我的天,谁能掌握甚至精通那些
技术?James Gosling能掌握得了吗?

Java社区要有改观,这样散乱下去Java就成了怪物。
30/05/2008

Net 远程处理基本知识

一、Net 远程处理基本知识

    三种基本类型的类
          可序列化的类:这些类属性被Serializable标记,用来在程序及系统之间传递。可序列化的类要求每个成员及其应用的对象可序列化。不想序列化的成员可以使用NonSerialized标记。
          可远程调用的类:这些类都是System.MarshalByRefObject的派生类。用公共的属性、方法和成员变量都可被原端调用。
          普通类:辅助完成远程处理,不参与调用与传输,运行于本地计算机。

    NET远程组件宿主
           所有的远程可调用的类,必须宿主在一个用来侦听请求并创建合适对象的服务系统上,才能完成远程调用的过程。这个服务系统成为远程组件宿主。
   一个宿主组件应该完成两个任务:第一创建服务端信道来侦听客户端请求。当收到客户端请求时,NET远程处理框架自动在宿主组件的应用程序域中生成所需的远程对象。
   远程宿主可采用一下几种形式:
       1、专用的WINDOWS服务器程序,服务器打开时自动启动,不需要用户干预。
       2、控制台应用程序,能够在屏幕上显示简单的诊断和日志信息。
       3、完成的WINDOWS应用程序,能够提供更为复杂的接口。

    NET远程处理的决策

     1、激活模式
      单调用SingleCall:这些无状态对象是由调用方法创建的,持续时间也方法相同。客户端能够保存和使用同一引用,可是每次调用都会创建一个新的对象。
      客户端激活对象:这种对象与普通对象类似。它们能够保持状态,每个客户端接受到一个单独的实例。
      单独对象SingleTon:这种类型的对象具有状态并在客户端间共享状态。无论连接多少客户端,都只有一个远程对象实例。
      单调用为无状态连接,消耗资源比较少,很容易装载到群集服务器上;客户端激活具有状态,与普通类相似易于进行编程,但需要耗费的资源比较大。单独对象很难管理,由于大量的客户端同时调用方法,就可能产生并发错误。另外单独对象也是比较耗费服务器资源的。
     2、对象生存周期
      单独对象和客户端激活对象都具有状态,其工作原理是:只允许一个对象存在一定的时间。如果需要存在更长的时间,则要有“人”更新约定时间。这个有“人”可以是特定的约定发起者,客户端组件,或是远程组件本身。为了确保系统的长时间稳定性和可缩放性,一定要使用又较为保守的策略。
     3、服务器和客户端激活
       单调用和单独对象都是服务器激活对象(SAO)。生存期约定既应用于客户端激活对象,也应用于服务端激活对象。客户端激活与服务端激活的最大区别是:客户端初始化时服务器激活对象并没有创建,需要使用时才真正的生成。如果是一个尚不存在的对象,将在第一次调用它的方法时在服务端创建对象。相反的,客户端激活对象是在客户端初始化时被创建。因此,客户端的激活可以使用带参数的构造函数,而服务器端激活对象只能使用没有参数的构造函数初始化。
     4、通信
      实现远程处理最终决定是所使用的通信格式与传输方式。NET框架默认支持两个格式化器和两个信道。
      两个格式化器:
          二进制格式化器。二进制格式化器把数据序列化为压缩的、专用的NET格式。这个格式化器提供了最好的性能,但只能由NET应用程序使用。
          SOAP格式化器。SOAP格式化器把数据序列化为跨平台的、基于XML的、存文本的SOAP消息。因为其它平台上的客户端能够新建和发送SOAP消息,这种格式仪器被用来远程NET组件进行跨平台通信。SOAP格式要求消息占用空间较大,因而总体性能比较低。
      两个信道是:
          TCP信道。这个信道基于TCP协议进行连接。对内部网最为理想。
          HTTP信道。使用HTTP协议,在需要通过INTERNET进行通信时最为理想。

二、Remoting的配置文件
包含用于在远程处理应用程序配置文件中放置自定义设置的标记。
<system.runtime.remoting>  包含有关远程对象和信道的信息。
<application>  包含有关应用程序使用和公开的远程对象的信息。
<lifetime>  包含有关由此应用程序提供服务的所有客户端激活对象生存期的信息。其属性有:leaseTime 租约周期;sponsorshipTimeout 到器等待时间;renewOnCallTime 调用引起的租约延长时间;aseManagerPollTime;超时检测间隔时间
 
<channels>(实例)  包含应用程序用来与远程对象进行通信的信道。
 
<channel>(实例)  配置应用程序用来与远程对象进行通信的信道。属性如下:
      ref:指示客户端要注册以供使用的信道模板的 id。如果指定 ref 属性,则不需要指定 type 属性。NET Framework 远程处理提供三种信道实现:“http”、“tcp”和“ipc”。
      displayName:供 .NET Framework 配置工具用来创建此应用程序已注册的信道列表 
      type:指示信道的完整类型名和信道实现所在的程序集的名称。 
      port:指示一个合适的端口属性
      portName:指定服务器将公开的端口的名称。 
      useIpAddress:个布尔值(true 或 false),它指定是否使用发布 URL 中的 IP 地址,而不使用计算机名称。
      useIpAddress:指定用于当前信道的计算机名称的字符串。如果用于服务器信道,将重写 useIpAddress。
<serverProviders>(实例) 包含信道接收器提供程序,当在配置文件中的其他位置引用此信道模板时,这些提供程序将成为该模板的默认服务器端信道接收器调用链的一部分。
<provider>(实例)  包含要插入信道接收器链的信道接收器的信道接收器提供程序。
<formatter>(实例)  包含要插入信道接收器链的格式化程序接收器的信道接收器提供程序。
<clientProviders>(实例)  包含信道接收器提供程序,当在配置文件中的其他位置引用此信道模板时,这些提供程序将成为该模板的默认客户端信道接收器调用链的一部分。
<client>  包含应用程序使用的对象。
<wellknown>(客户端实例) 包含有关应用程序要使用的服务器激活(已知)对象的信息。
<activated>(客户端实例)  包含有关应用程序要使用的客户端激活对象的信息。
<service>  包含应用程序公开给其他应用程序域或上下文的对象。
<wellknown>(服务实例)  包含有关应用程序要发布的服务器激活(已知)对象的信息。属性如下:
           属性  说明  
           mode  必选的属性。指示对象是一个 Singleton 对象还是一个 SingleCall 对象。必须选择一个选项。有关详细信息,请参见激活。
           objectUrl 必选的属性。指定对象的统一资源标识符 (URI) 的终结点。
            t ype  必选的属性。指定对象的完整类型名和类型实现的程序集名称。如果包含程序集在全局程序集缓存中,则这包括版本、区域性和公钥信息。 
            displayName 可选的属性。由 .NET Framework 配置工具用来创建向此服务器注册要发布的服务器激活对象的列表。.NET Framework 远程处理系统不使用此属性。
<activated>(服务实例)  包含有关应用程序要发布的客户端激活对象的信息。属性如下:
            type 指定对象的完整类型名和包含该类型实现的程序集的名称。如果包含程序集在全局程序集缓存中,则这包括版本、区域性和公钥信息。
<soapInterop>  包含与 SOAP 一起使用的类型映射。
<interopXmlType>  创建公共语言运行库类型与 XML 类型及 XML 命名空间之间的双向映射。
             属性  说明  
             clr  必选的属性。指定要为其创建到 XML 类型及 XML 命名空间的映射的类型的完整类型名和程序集名。如果包含程序集在全局程序集缓存中,则这包括版本、区域性和公钥信息。
             xml  必选的属性。指定要为其创建到类型和程序集的映射的 XML 类型名及 XML 类型命名空间。
<interopXmlElement>  创建公共语言运行库类型与 XML 元素及 XML 命名空间之间的双向映射。
             属性  说明  
             clr  必选的属性。指定要为其创建到 XML 元素及 XML 命名空间的映射的类型的完整类型名和程序集名。如果包含程序集在全局程序集缓存中,则这包括版本、区域性和公钥信息。
             xml  必选的属性。指定要为其创建到类型和程序集的映射的 XML 元素及 XML 命名空间。 
<preLoad>  指定要从扩展 SoapAttribute 的类中加载映射的类型。
             属性  说明  
             type  指定为启用反序列化而预加载的类型。如果包含程序集在全局程序集缓存中,则这包括版本、区域性和公钥信息。
             assembly 预加载指定程序集中的所有类型。
<channels>(模板)  包含应用程序用来与远程对象进行通信的信道模板。
<channel>(模板)  包含某信道模板,应用程序可指定并配置该信道模板,以便与远程对象进行通信或侦听对远程对象的请求。
             属性   说明  
            customChannelProperty 可选的属性。指示支持的自定义信道属性。可以指定信道能支持的任意数量的信道属性。应使用属性/值对指定自定义信道属性。
            delayLoadAsClientChannel 可选的属性。指示如果客户端没有为应用程序注册信道,是否应加载此信道。此值为布尔型,并且只影响客户端行为。值为 true 指示 .NET Framework 远程处理应在运行时测试此信道,以查看它是否支持使用在远程激活 URL 中指定的特定协议方案建立客户端连接。如果此值不存在,则默认值为 false。
            displayName  可选的属性。由 .NET Framework 配置工具用来创建此应用程序使用的信道列表。.NET Framework 远程处理系统不使用此属性。
            id   必选的属性。提供应用程序用来在注册时标识信道的字符串。若要引用此信道模板,请将此属性指定为实例信道元素中 ref 属性的值。
<channelSinkProviders> 包含客户端和服务器信道接收器提供程序的模板。在可能注册信道接收器提供程序的任何地方都可以引用此元素下指定的任何信道接收器提供程序。
<serverProviders>(模板)  包含可插入服务器信道调用链中的信道接收器模板。
<provider>(模板)  包含信道接收器的信道接收器提供程序模板,该信道接收器将插入服务器或客户端信道接收器链中。 
<formatter>(模板)  包含格式化程序接收器的信道接收器提供程序,该格式化程序接收器将插入客户端或服务器信道接收器链中。
  <formatter 
    id="FormatterID"
    type="FormatterSinkProviderType, FormatterAssembly"
    customFormatterProperty="customProperty"
    includeVersions="true"
    strictBinding="false"
 ...typeFilterLevel="Low | Full"
 />
            属性  说明  
           customFormatterProperty  指示支持的自定义信道接收器属性。可以指定自定义信道接收器提供程序及其接收器能支持的任意数量的提供程序和信道接收器属性。应使用属性/值对指定自定义信道接收器属性。
includeVersions 指示在序列化类型信息时,发送格式化程序包含完整的类型和程序集版本信息。有关重要的详细信息,请参见下面的备注部分。系统提供的两个格式化程序的默认值都是 true。
           strictBinding 指示在仅使用类型名称和程序集名称(无版本信息)之前,接收格式化程序首先试着使用完整的版本信息(如果有的话)来标识类型。有关重要的详细信息,请参见下面的备注部分。系统提供的两个格式化程序的默认值都是 false。
           typeFilterLevel 一个字符串值,它指定服务器通道接收器链中的格式化程序尝试的自动反序列化的级别。支持的值为 Low(默认值)和 Full。限 .NET Framework 1.1 版
           id  指定标识此格式化程序接收器提供程序的字符串。其他人可以在自己的 ref 属性中使用此字符串,以绑定到此格式化程序接收器提供程序。
           type  指定格式化程序接收器提供程序的完整类型名以及包含该提供程序实现的程序集的名称。如果包含程序集在全局程序集缓存中,则这包括版本、区域性和公钥信息。
<clientProviders>(模板)  包含可插入客户端信道调用链中的信道接收器模板。
<debug>  指定当应用程序启动时是否加载配置文件中的类型。
            属性  说明  
            loadTypes 必选的属性。指定当应用程序启动时是否加载在此配置文件中指定的所有类型,以便在配置文件中存在错误时可以得到警告。这有助于防止简单的键入错误占用太多的调试时间。 
<customErrors>  指示此应用程序域中的服务器通道是否将经筛选的或完整的异常信息返回给本地或远程调用方。
 <customErrors  mode="Off|On|RemoteOnly"/>

示例代码

客户的配置文件

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  
<appSettings>
    
<add key="CallType" value="1"/>
    
<add key="CallURL" value="http://127.0.0.1:8080/" />
    
<add key="DllName" value="DAL.DLL"/>
  
</appSettings>
  
<system.runtime.remoting>
    
<application>

      
<client url="tcp://127.0.0.1:8081/">
       
<!-- <wellknown type="DAL.DOrder,DAL"   url="http://127.0.0.1:8080/DOrder"/>
        <wellknown type="DAL.DShopCart,DAL" displayName="DShopCart2" url="http://127.0.0.1:8080/DShopCart" />
-->
        
        
<activated type="DAL.DShopCart,DAL" />
      
</client>
      
<channels>
          
<serverProviders>
            
<provider ref="wsdl" />
            
<formatter ref="soap" typeFilterLevel="Full" />
            
<formatter ref="binary" typeFilterLevel="Full" />
          
</serverProviders>
        
<channel ref="http" port="8083"/>
        
<channel ref="tcp" port="8082"/>
      
</channels>
    
</application>
  
</system.runtime.remoting>
</configuration>


服务端的配置文件

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  
<system.runtime.remoting>
    
<application name="服务终端">
      
<lifetime leaseTime="120S" sponsorshipTimeout="5S" renewOnCallTime="50S" leaseManagerPollTime="2S" />
      
<service>
        
<activated type="DAL.DShopCart,DAL"/>
        
<wellknown type="DAL.DOrder,DAL" objectUri="DAL.DOrder" mode="SingleCall" />
        
<wellknown type="DAL.DShopCart,DAL" objectUri="DShopCart" mode="SingleCall" />
        
<wellknown type="DAL.DShopCart,DAL" objectUri="DShopCart2" mode="Singleton" />
        
      
</service>
      
<channels>
        
<channel port="8080" ref="http"/>
        
<channel port="8081" ref="TCP"/>
        
<serverProviders>
          
<provider ref="wsdl" />
          
<formatter ref="soap" />
          
<formatter ref="binary"/>
        
</serverProviders>
      
</channels>
    
</application>
    
<debug loadTypes="true"/>
  
</system.runtime.remoting>
</configuration>

 

08/05/2008

CSharp 开源项目大全

AOP框架

博客(Blog)

系统构建

图表制作

聊天系统

论坛系统

开发工具(IDE)

IoC容器

日志工具

安装制作

网络客户端

网络服务器

工作流(workflow)

代码覆盖(Code Coverage)

内容管理系统(CMS)

Bug追踪

PDF类库

持久层框架

门户系统

多媒体工具

项目管理

RSS和RDF工具

规则引擎

搜索引擎

剖析工具(Profilers)

版本控制(CVS)

SQL客户端

模板引擎

测试工具

Web框架

Email客户端

Web测试

Wiki引擎

XML解析

C#游戏

Ajax框架

文档工具

脚本语言

文本编辑

地理信息系统(GIS)

其它C#开源项目

03/05/2008

C# 开发终端式短信的原理和方法

 导读:
  简介
  没发过短信的年轻人肯定是属于那种受保护的稀有动物,通讯发达的今天短信已经成为人们交流的重要手段,其中也蕴含着巨大的市场和经济利益,掌握短信技术的人才也受到各大公司的追捧是目前职场上耀眼的明星。本文介绍了短信的原理和实现方法,重点说明了短信的编码方式、AT指令以及用C#实现串口通讯的方法。
  前言
  目前,主要有三种发送短信的方式:
  1、 网关方式:就是向当地的电信部门申请,不需要额外的设备,适用于大型的通信公司,像华为、傲天、中兴、亚信等。
  2、 终端方式:就是借助像GSM MODEM之类的设置(支持AT指令的手机也行),通过数据线连接电脑,来发送短信,用这种方法比较适用于小型及个人。要实现这种方式必须理解串口通信、AT指令、短信编码、解码,这也是本文讨论的重点。
  3、 利用一些网站来实现,方式简单,不过对网站依赖性太高,对网络的要求也比较高,非常不适于进行项目开发
  阅读导航
  终端短信连接示意图
  原理篇 短信编码
  AT指令
  串口通讯
  实践篇
  常见问题
  下载 本文源代码
  编码测试文件
  zmbbs=1;
  
  终端短信连接示意图
  
  
  zmbbs=1; 原理篇
  短信编码
  在收发短信方面,按时间产生先后,共产生了三种模式:Block Mode、基于AT指令的Text Mode、基于AT指令的PDU Modem, Text Mode比较简单,多款诺基亚手机均支持该模式。西门子的手机大多只支持PDU模式,PDU模式是发送或接收手机SMS信息的一种方法,短信息正文经过十六进制编码后被传送。目前,PDU已取代Block Mode,因我们主要探讨PDU模式的发送。以西门子3508手机为例。
  SMS是由 Etsi所制定的一个规范(GSM 03.40 和 GSM 03.38)。当使用7-bits编码时,它可以发送最多160个字符;但用8-bit编码,最多可以发送140个字符,通常无法直接通过手机显示;还有用16-bit编码时,最多70个字符,被用来显示Unicode(UCS2)文本信息,可以被大多数的手机所显示。我们今天讨论的是UCS2编码,也就是说,最多只能发送70个字符,不管英文还是中文。
  现例如我们现在要发送如下信息,向我的手机13715342642发送"你好, Hello!"。在没有发送之前,你要清楚,手机SIM卡所在地的短信中心号,并不是你现在所在地方的短信中心号,像我在深圳,深圳的短信中心号是: 8613800755000,即使我现在到外地,短信中心号仍是深圳。从上面我们得到了下面的信息:
  接收的手机号:13715342642
  短信中心号:8613800755000
  短信内容:你好,Hello!
  在实际使用中,上面这些信息并不为手机所执行,要进行编码手机才会执行,先不管,看看编码后的信息:
  0891683108705500F011000D91683117352446F2000800124F60597DFF0C00480065006C006C006F0021
  看不懂吧,我来解释一下:
  08 - 指的是短信中心号的长度,也就是指(91)+( 683108705500F0)的长度
  91 - 指的是短信息中心号码类型。91是TON/NPI遵守International/E.164标准,指在号码前需加''+''号;此外还有其它数值,但91最常用。
  683108705500F0 - 短信息中心号码。由于位置上略有处理,实际号码应为:8613800731500(字母F是指长度减1)。这需要根据不同的地域作相应的修改。前面的 (08)+(91)+( 683108705500F0)实际上就构成了整个短信的一部份,通称短消息中心地址(Address of the SMSC)。
  11 - 文件头字节
  00 - 信息类型(TP-Message-Reference)
  0D - 被叫号码长度
  91 - 被叫号码类型
  其实在实际处理中,我们通常把11000D91写死在程序中,因为在国内,这些数据都是不会改变的。
  683117352446F2 -被叫号码,经过了位移处理,实际号码为"8613715342642"。上面的(00)+(0D)+(91)+( 683117352446F2),构成了整个短信的第二部份目的地址(TP-Destination-Address)。
  00 - 协议标识TP-PID,这里一般为00
  08 - 数据编码方案TP-DCS(TP-Data-Coding-Scheme),采用前面说的USC2(16bit)数据编码
  00 - 有效期TP-VP(TP-Valid-Period)
  12-长度TP-UDL(TP-User-Data-Length),也就是4F60597DFF0C00480065006C006C的长度 36 / 2 = 18 的十六进 12
  4F60597DFF0C00480065006C006C 006F0021- 这里就是短信内容了,实际内容为:"你好,Hello!"程序实现,请参考本文章所带源程序的PDUdecoding.cs。
  zmbbs=1; AT指令
  说到AT指令可多了,有厚厚的一本书,不属于我们今天讨论的范围,在这里我仅讨论在发送短信中必须要用的几个AT指令。
  与SMS有关的GSM AT指令(from GSM07.05)如表1所示:
  AT 指令 功 能
  AT+CMGC Send an SMS command(发出一条短消息命令)
  AT+CMGD Delete SMS message(删除SIM卡内存的短消息)
  AT+CMGF Select SMS message formate(选择短消息信息格式:0-PDU;1-文本)
  AT+CMGL List SMS message from preferred store(列出SIM卡中的短消息PDU/text: 0/"REC UNREAD"-未读,1/"REC READ"-已读,2/"STO UNSENT"-待发,3/"STO SENT"-已发,4/"ALL"-全部的)
  AT+CMGR Read SMS message(读短消息)
  AT+CMGS Send SMS message(发送短消息)
  AT+CMGW Write SMS message to memory(向SIM内存中写入待发的短消息)
  AT+CMSS Send SMS message from storage(从SIN|M内存中发送短消息)
  AT+CNMI New SMS message indications(显示新收到的短消息)
  AT+CPMS Preferred SMS message storage(选择短消息内存)
  AT+CSCA SMS service center address(短消息中心地址)
  AT+CSCB Select cell broadcast messages(选择蜂窝广播消息)
  AT+CSMP Set SMS text mode parameters(设置短消息文本模式参数)
  AT+CSMS Select Message Service(选择短消息服务)
  表一:相关的GSM AT指令
  我现在以实例来说明这些指令的使用方法:
  先用手机数据线将手机连接到电脑串口,并将串口的波特率设置为19200,可以开始了。
  1、首先测试你的连接及手机是否支持AT指令,请在你的串口调试程序中输入:
  AT<回车>
  屏幕上返回"OK"表明计算机与手机连接正常,那样我们就可以进行其它的AT指令测试了
  2、设置短信发送格式
  AT+CMGF=1<回车>
  屏幕上返回"OK"表明现在短信的发送方式为PDU方式,如果是设置为TEXT方式,则,AT+CMGF=0<回车>
  3、 发送短信
  发送内容及手要号仍旧同上面在编码中的一样,编码后,得到要发送的数据如下
  0891683108705505F011000D91683117352446F2000800124F60597D002C00480065006C006C006F0021
  我们用如下指令来发送
  AT+CMGS=33<回车>
  如果返回">",就把上面编码数据输入,并以CTRL+Z结尾,稍等一下,你就可以看到返回OK啦。
  说明一下,为什么AT+CMGS=33呢,是这样得来的:
  11000D91683117352446F2000800124F60597D002C00480065006C006C006F0021
  这一段字符串的长度除以2得到的结果,上面的字符串,短信中心号加上短信内容得到的,怎么得到的,请回顾一下解码部份
  在我们前面的讨论中,一条完整的短信发送,只要执行三条AT指令,AT、AT+CMGS=?、AT+CMGS=?就可以了。由于篇幅,我只能在这里提到这么多,大家要是想了解更多,可以向各手机厂商索取AT指令白皮书,里面很详细的。
  上面讲到的,只能为我们实际中作准备,我们还必须要一个发送途径,根据我们的需要,我们选择投资最少,实现比较方便的串口通信。注意,串口通过数据线跟手机相连,用AT指令来实现发送短信,在我们选择数据线时,建议购买原厂所配,非原厂所配,在使用过程中,经常出现一些莫明其妙的问题,比如,手机屏幕黑了,手机老是提示电池电量不足之类的。
  zmbbs=1; 串口通信
  在C#中要实现串口通信,很多人都不知所措,在论坛上经常可以看到"怎么用MSCOMM实现串口通信"、"怎样能过串口与设备相连"诸如此类的问题。其实国外的网友早就把这些列入FAQ中了。
  通常,在C#中实现串口通信,我们有四种方法:
  第一:通过MSCOMM控件这是最简单的,最方便的方法。可功能上很难做到控制自如,同时这个控件并不是系统本身所带,所以还得注册,不在本文讨论范围。可以访问http://www.devhood.com/tutorials/tutorial_details.aspx?tutorial_id =320 ,一个国外网友的写的教程,作者很热心,我曾有发邮件给他,很快就回复了。
  第二:微软在.NET新推出了一个串口控件,基于. NET的P/Invoke调用方法实现,详细的大家可以访问微软网站http: //msdn.microsoft.com/msdnmag/issues/02/10/NETSerialComm/default.aspx,方便得到更多资料。
  第三:就是用第三方控件啦,可一般都要付费的,不太合实际,不作考虑
  第四:自己用API写串口通信,这样难度高点,但对于我们来说,可以方便实现自己想要的各种功能
  在本文,我们采用第四种方法来实现串口通信,不过不是自己写,用一个国外网友现成的已经封装好的类库,不过功能简单点,相对我们来说已经够用了。
  在整个终端短信的操作过程中,与串口的通信,只用到了四个功能,打开、写、读、关闭串口。下面是类库对这四个功能的定义:
  打开串口:
  函数原型:public void Open()
  说明:打开事先设置好的端口
  示例:
  using JustinIO;
  static JustinIO.CommPort ss_port = new JustinIO.CommPort();
  ss_port.PortNum = COM1; //端口号
  ss_port.BaudRate = 19200; //串口通信波特率
  ss_port.ByteSize = 8; //数据位
  ss_port.Parity = 0; //奇偶校验
  ss_port.StopBits = 1;//停止位
  ss_port.ReadTimeout = 1000; //读超时
  try
  {
  if (ss_port.Opened)
  {
  ss_port.Close();
  ss_port.Open(); //打开串口
  }
  else
  {
  ss_port.Open();//打开串口
  }
  return true;
  }
  catch(Exception e)
  {
  MessageBox.Show("错误:" + e.Message);
  return false;
  }
  写串口:
  函数原型:public void Write(byte[] WriteBytes)
  WriteBytes 就是你的写入的字节,注意,字符串要转换成字节数组才能进行通信
  示例:
  ss_port.Write(Encoding.ASCII.GetBytes("AT+CGMI\r")); //获取手机品牌
  读串口:
  函数原型:public byte[] Read(int NumBytes)
  NumBytes 读入缓存数,注意读取来的是字节数组,要实际应用中要进行字符转换
  示例:
  string response = Encoding.ASCII.GetString(ss_port.Read(128)); //读取128个字节缓存
  关闭串口:
  函数原型:ss_port.Close()
  示例:
  ss_port.Close();
  由于篇幅,以及串口通信涉及内容广泛,我在这里只讲这些。
  在上面我们已经把终端短信所需的各种原始技术有所了解,是可以小试牛刀的时候了。
  zmbbs=1; 实践篇
  在整个开始的时候,你要准备以下软硬件:
  硬件:西门子3508或C35系列手机一个
  西门子手机通信数据线一条
  软件:VS.NET(C#)
  短信编码类库(PDUdecoding.cs)
  串口通信类库(JustinIO.cs)
  当所要求的软硬件都准备好后,我们就可以正式开始了。下面以我自己的测试用例为大家详细介绍。
  做什么事情都应该有计划,虽然我们的测试用例很简单,但还是画个简单的流程图:
  
  
  有了流程图,还只是明白了程序怎么运行,再看看界面,会让你更心动的了。
  
  
  图二、短信终端C#版界面图
  再不开始,就有人骂我了。下在我讲的开发环境是在VS.NET(C#)中。COME GO,GO…
  步骤一、打开VS.NET,新建项目->Visual C#项目->Windows应用程序,名称中输入你的工程名就行啦,我的是smsForCsharp
  步骤二、参照上面的界面图,设计你的程序界面,下面是我程序中各控件的主要属性
  控件名称 控件Name属性 说明
  TextBox targetNumber 接收手机号码
  TextBox CenterNumber 短信中心号
  TextBox smsState 发送短信后,返回的信息。注意设置控件为多行
  TextBox smsContent 短信内容,同样,注意设置为多行
  ComboBox ConnectPort 连接手机的端口,例:COM1\COM2
  ComboBox ConnectBaudRate 串口连接的波特率,在串口通信中很重要的
  Button btnSend 发送按钮
  Button btnConnect 连接按钮,主要用于程序的初始化
  Button btnExit 退出按钮
  步骤三、将PDUdecoding.cs与JustinIO.cs拷入刚刚新建工程目录,并打开解决方案资源管理器,右键添加现有项,选中两个文件就行了,这里再打开类视图,里面是不是多了两个类,JustinIO与SMS类啊,如图三,要是没有,那你再试。
  
  
  图三,添加类后的类视图
  步骤四、引用命名空间,用代码查看方式打开Form1.cs(这里以我电脑为准,如果你自己更改过,请以你电脑为准),在代码前面加上
  using JustinIO;
  using SMS;
  using System.IO;
  using System.Text;
  步骤五、在smsFormCsharp类中,添加两个字段ss_port、sms,分别为JustinIO及SMS的对象,如下
  
  
  步骤六、添加串口初始化代码,如下:
  /// <summary>
  /// 初始化串口
  /// </summary>
  public bool InitCom(string m_port, int m_baudrate)
  {
  ss_port.PortNum = m_port;//串口号
  ss_port.BaudRate = m_baudrate;//波特率
  ss_port.ByteSize = 8;//数据位
  ss_port.Parity = 0;//
  ss_port.StopBits = 1;//停止位
  ss_port.ReadTimeout = 1000;//读超时
  try
  {
  if (ss_port.Opened)
  {
  ss_port.Close();
  ss_port.Open();
  }
  else
  {
  ss_port.Open();//打开串口
  }
  return true;
  }
  catch(Exception e)
  {
  MessageBox.Show("错误:" + e.Message);
  return false;
  }
  }
  将上述代码直接拷入你的程序中,并确保添加在Main主函数的后面,按F5,调试应该没什么问题,不过上面还没有实际任何看得见的功能,仅仅是打开了串口而以。
  步骤七、打开串口后,我们就应该初始化程序,取得手机的名牌,型号,以及短信中心号,双击连接按钮,并把下面代码拷入程序中:
  /// <summary>
  /// 初始化代码,并获取手机相关信息
  /// </summary>
  /// <param name="sender"></param>
  /// <param name="e"></param>
  private void btnConnect_Click(object sender, System.EventArgs e)
  {
  bool opened = InitCom(ConnectPort.SelectedItem.ToString(),Convert.ToInt32(ConnectBaudRate.SelectedItem.ToString ()));//打开并初始化串口
  bool Connected = false;
  if (opened)
  {
  ss_port.Write(Encoding.ASCII.GetBytes("AT+CGMI\r")); //获取手机品牌
  string response = Encoding.ASCII.GetString(ss_port.Read(128));
  if (response.Length > 0)
  {
  ConnectState.Text = response.Substring(10,7);
  Connected = true;
  }
  else
  {
  ConnectState.Text = "与手机连接不成功";
  Connected = false;
  }
  ss_port.Write(Encoding.ASCII.GetBytes("AT+CGMM\r"));//获取手机型号
  response = Encoding.ASCII.GetString(ss_port.Read(128));
  if(response.Length > 0)
  {
  ConnectState.Text =ConnectState.Text+ ""+ response.Substring(10,5) + "连接中......";
  Connected = true;
  }
  else
  {
  ConnectState.Text = "与手机连接不成功";
  Connected = false;
  }
  ss_port.Write(Encoding.ASCII.GetBytes("AT+CSCA?\r"));//获取手机短信中心号
  response = Encoding.ASCII.GetString(ss_port.Read(128));
  if(response.Length > 0)
  {
  CenterNumber.Text = response.Substring(20,13);
  Connected = true;
  }
  else
  {
  Connected = false;
  }
  if (Connected == true)
  {
  btnConnect.Enabled = false;
  btnSend.Enabled = true;
  }
  else
  {
  btnConnect.Enabled = true;
  btnSend.Enabled = false;
  }
  }
  }
  到这里,你可以按F5,编译调试,通过,在确保你的手机与电脑连接正常下,点击连接按钮看看,是不是像我的一样,手机型号及短信中心号者正常显示出来了。
  
  
  图四、连接后程序界面
  步骤八、看到上在的结果,是不是感觉到离成功发送短信很近啦,看这么长的文章,费了大家不少时间,再不亮出发短信部份,对不起大家了。
  双击发送按钮,将下面代码拷入程序中。
  /// <summary>
  /// 发送短信
  /// </summary>
  /// <param name="sender"></param>
  /// <param name="e"></param>
  private void btnSend_Click(object sender, System.EventArgs e)
  {
  string decodedSMS = sms.smsDecodedsms(CenterNumber.Text,targetNumber.Text,smsContent.Text);
  byte[] buf =Encoding.ASCII.GetBytes(String.Format("AT+CMGS={0}\r",sms.nLength));
  ss_port.Write(buf);
  string response = Encoding.ASCII.GetString(ss_port.Read(128));
  string SendState = "";
  if( response.Length > 0 &&response.EndsWith("> "))
  {
  ss_port.Write(Encoding.ASCII.GetBytes(String.Format("{0}\x01a",decodedSMS)));
  SendState = "发送成功!";
  }
  else
  {
  SendState = "发送失败";
  }
  string Result = String.Format("{0},{1},{2},\n\r",targetNumber.Text,smsContent.Text,SendState);
  smsState.Text += Result;
  }
  快按F5吧!神啊,快通过吧!不用求神了,已经通过了,现在你就可以发短信了,请确保手机可以正常连接电脑。按连接,然后填入你要的发送的目标手机号,并在内容中添入你要发送的内容,发送吧!成功了!成功了是这样子的!看你的跟我的一样吗?
  
  
  图五、发送成功
  还有一些事 不要忘了,记得添加退出代码。双击退出,添加下面代码:
  /// <summary>
  /// 关闭串口,退出程序
  /// </summary>
  /// <param name="sender"></param>
  /// <param name="e"></param>
  private void btnExit_Click(object sender, System.EventArgs e)
  {
  ss_port.Close();
  Application.Exit();
  }
  到这里都告一个段落了,所有的功能都完成了!不过由于这仅仅是一个演示用例,还有很多没有考虑,像串口通信中的,在实际操作不可这样操作的,应该用多线程来处理,一个专门用来读串口,一个专门用来写串口。还有程序中很多防出错代码没有添加进去,希望有心有朋友添加,并公布出来,这也是我写这篇文章希望看到的结果。请勿将本程序直接用于实际中,真诚提醒你!
  终于写完了,我也放松了许多,本来很早就应该完成了,因为一些个人原因,没有及时写完,向那些曾经问过我相关问题,没有及时回复的朋友,抱歉一声,希望你们继续支持我!
  调试环境
  Windows 2000 Professional、Visual Studio.NET、西门子3508手机、西门子专用数据线。
  zmbbs=1; 常见问题
  第一, 手机品牌,因为不同产商的手机,对AT指令的支持不同,所以请选择适合你手机AT指令,像NOKIA的就只能用TEXT模式的AT指令。
  第二, 数据线,问题出得最多的地方也就是数据,如果接上数据线后,你的手机显示为黑屏,建议你换数据线。
  第三, 手机SIM卡上的短信中心号设置,请确保在你的手机上可以发送短信。
  第四, 请你先用串口调试工具调试手机与电脑的连接,这样对你整个工作都是一个保证。
29/04/2008

IMPORT --- Activex、OLE、COM、OCX、DLL之间有什么区别?

熟悉面向对象编程和网络编程的人一定对ActiveX、OLE和COM/DCOM这些概念不会陌生,但是它们之间究竟是什么样的关系,对许多们还是比较模糊的。在具体介绍它们的关系之间,我们还是先明确组件(Component)和对象(Object)之间的区别。组件是一个可重用的模块,它是由一组处理过程、数据封装和用户接口组成的业务对象(Rules Object)。组件看起来像对象,但不符合对象的学术定义。它们的主要区别是: 1)组件可以在另一个称为容器(有时也称为承载者或宿主)的应用程序中使用,也可以作为独立过程使用; 2)组件可以由一个类构成,也可以由多个类组成,或者是一个完整的应用程序; 3)组件为模块重用,而对象为代码重用。现在,比较流行的组件模型有COM(Component Objiect Module,对象组件模型)/DCOM( Distributed COM,分布式对象组件模型)和CORBA(Common Object Request Broker Architecture,公共对象请求代理体系结构)。到这里,已经出现了与本文相关的主题COM,而CORBA与本文无关,就不作介绍。之所以从组件与对象的区别说起,是想让大家明确COM和 CORBA是处在整个体系结构的最底层,如果暂时对此还不能理解,不妨继续往下看,最后在回过头看一看就自然明白了。现在开始阐述ActiveX、OLE 和COM的关系。首先,让大家有一个总体的概念,从时间的角度讲,OLE是最早出现的,然后是COM和ActiveX;从体系结构角度讲,OLE和 ActiveX是建立在 COM之上的,所以COM是基础;单从名称角度讲,OLE、ActiveX是两个商标名称,而COM则是一个纯技术名词,这也是大家更多的听说 ActiveX和OLE的原因。既然OLE是最早出现的,那么就从OLE说起,自从Windows操作系统流行以来,“剪贴板”( Clipboard)首先解决了不同程序间的通信问题(由剪贴板作为数据交换中心,进行复制、粘贴的操作),但是剪贴板传递的都是“死”数据,应用程序开发者得自行编写、解析数据格式的代码,于是动态数据交换(Dynamic Data Exchange,DDE)的通信协定应运而生,它可以让应用程序之间自动获取彼此的最新数据,但是,解决彼此之间的“数据格式”转换仍然是程序员沉重的负担。对象的链接与嵌入(Object Linking and Embedded,OLE)的诞生把原来应用程序的数据交换提高到“对象交换”,这样程序间不但获得数据也同样获得彼此的应用程序对象,并且可以直接使用彼此的数据内容,其实OLE是Microsoft的复合文档技术,它的最初版本只是瞄准复合文档,但在后续版本OLE2中,导入了COM。由此可见, COM是应OLE的需求而诞生的,所以虽然COM是OLE的基础,但OLE的产生却在COM之前。 COM的基本出发点是,让某个软件通过一个通用的机构为另一个软件提供服务。COM是应OLE 的需求而诞生,但它的第一个使用者却是OLE2,所以COM与复合文档间并没有多大的关系,实际上,后来COM就作为与复合文档完全无关的技术,开始被广泛应用。这样一来, Microsoft就开始“染指”通用平台技术。但是COM并不是产品,它需要一个商标名称。而那时Microsoft的市场专家们已经选用了OLE作为商标名称,所以使用COM技术的都开始贴上了 OLE的标签。虽然这些技术中的绝大多数与复合文档没有关系。Microsoft的这一做法让人产生这样一个误解OLE是仅指复合文档呢?还是不单单指复合文档?其实OLE是COM的商标名称,自然不仅仅指复合文档。但Microsoft自己恐怕无法解释清楚,这要花费相当的精力和时间。于是,随着Internet的发展,在1996年春,Microsoft改变了主意,选择ActiveX作为新的商标名称。ActiveX是指宽松定义的、基于COM的技术集合,而OLE仍然仅指复合文档。当然, ActiveX最核心的技术还是COM。ActiveX和OLE的最大不同在于,OLE针对的是桌面上应用软件和文件之间的集成,而ActiveX则以提供进一步的网络应用与用户交互为主。到这里,大家应该对ActiveX、OLE和COM三者的关系有了一个比较明确的认识,COM才是最根本的核心技术,所以下面的重点COM。让对象模型完全独立于编程语言,这是一个非常新奇的思想。这一点从C++和Java的对象概念上,我们就能有所了解。但所谓COM 对象究竟是什么呢?为了便于理解,可以把COM看作是某种(软件)打包技术,即把它看作是软件的不同部分,按照一定的面向对象的形式,组合成可以交互的过程和以组支持库。COM对象可以用C++、Java和VB等任意一种语言编写,并可以用DLL或作为不同过程工作的执行文件的形式来实现。使用COM对象的浏览器,无需关心对象是用什么语言写的,也无须关心它是以DLL还是以另外的过程来执行的。从浏览器端看,无任何区别。这样一个通用的处理技巧非常有用。例如,由用户协调运行的两个应用,可以将它们的共同作业部分作为COM对象间的交互来实现(当然,现在的OLE复合文档也能做到)。为在浏览器中执行从Web服务器下载的代码,浏览器可把它看作是COM对象,也就是说,COM技术也是一种打包可下载代码的标准方法(ActiveX控件就是执行这种功能的)。甚至连应用与本机OS进行交互的方法也可以用COM来指定,例如在Windows和Windows NT中用的是新API,多数是作为COM对象来定义的。可见,COM虽然起源于复合文档,但却可有效地适用于许多软件问题,它毕竟是处在底层的基础技术。用一句话来说,COM是独立于语言的组件体系结构,可以让组件间相互通信。随着计算机网络的发展,COM进一步发展为分布式组件对象模型,这就是 DCOM,它类似于CORBA的ORB,本文对此将不再做进一步的阐述。通过上面的讲述相信大家一定对ActiveX、OLE和COM/DCOM的关系有了一个清楚的了解。

        使用Windows的人对于ActiveX控制一定不会陌生,它提供了一种类似于DLL动态链接库的调用,不过它与DLL的唯一区别就是ActiveX不注册不能被系统识别并使用。那么,当我们得到一个ActiveX没有被正确安装且不能使用的消息后,又要安装ActiveX怎么办呢?1.Regsvr32程序法在Windows的System文件夹下有一个regsvr32.exe的程序,它就是Windows自己带的ActiveX注册和反注册工具。利用它也能够非常方便地注册AcitveX控件,它的用法为:regsvr32/u/s/n/i dllname,dllname其中dllname为ActiveX控件文件名,建议在安装前拷贝到System文件夹下参数有如下意义:/u - 反注册控件/s -不管注册成功与否,均不显示提示框/c - 控制台输出/i - 跳过控件的选项进行安装 (与注册不同)/n - 不注册控件,此选项必须与/i选项一起使用例如笔者要注册一amovie.ocx控件,则打入 regsvr32 amovie.ocx即可,要反注册它时只需使用regsvr32 /uamovie.ocx就行了。2.注册表法所谓注册AcitveX,无非是将一些信息记录在Windows的注册表中,如ShockwaveFlashObject控件,我们可以运行Regedit.exe注册表编辑程序,利用关键字进行搜索,然后把搜索得到后的注册表导出为一REG注册表文件,再将其相应的ActiveX文件拷贝到Windows的System文件夹(一般ActiveX的文件名为OCX,安装在Windows的System文件夹内)下,最后在要安装ActiveX的机器上双击导入刚才导出的注册表文件即可完成安装。

       Activex,OLE,COM都是微软的一些技术标准。Ole比较老后来发展成Activex,再后来发展成为COM OCX,DLL是扩展名。 Activex有两种扩展名OCX和DLL。实际上你可以把它们的扩暂名字调换。 COM作为ActiveX的更新技术,扩展名也有可能是DLL DLL文件还有可能是动态链接库。主要是装载一些函数,可以动态加载。