zoukankan      html  css  js  c++  java
  • 亲身体验CORBA: 使用java和C++混合编程

    级别: 初级

    王辉 (mailto:ddxxkk@21cn.com?subject=使用java和C++混合编程&cc=ddxxkk@21cn.com),

    2002 年 3 月 03 日

    本文作者通过一次使用SUN的 tnameserv命名服务程序,服务器用JAVA编写,客户机分别用JAVA和C++(VC6+omniORB)编写的试验,希望通过一次编程的具体操作实例来体验或明了CORBA思想。

    前言

    现在很多人在对CORBA进行学习,大家都已经了解到CORBA是一个完全中间性的语言,可以使用接口定义语言(IDL)定义开发时使用接口的 Client 和实现接口的 Server 所需要的信息。Client 和 Server 的具体实现代码并不在IDL定义中编写,而是使用某种目标语言的IDL 编译器生成所需的代码存根及helper类,Client 和 Server再使用真正的编程语言来进行具体实现。

    为了保证在不同的 CORBA 产品基础之上构建的分布式对象可以相互通信,Client和Server通过ORB(对象请求代理)进行通信。一般的运行流程是Client把请求发送给ORB,ORB再把请求发送给Server,Server把返回结果发送ORB,ORB再把返回结果发送给Client。ORB可以说Client和Server之间的翻译者。即使Client和Server使用不同的编程语言编写,只要是符合相同的IDL定义,ORB也可以完成相互的通信。

    所有的文档在强调服务器及客户机可以是Java也可以是C++或其他语言(如:Delphi)进行编写,但在网站或书本是没有详细说如何应对多语言客户机的例子。《JAVA2核心技术》上面有些说明,但也只是介绍性的文章,故自己下载了omniORB304,进行了一次使用SUN的 tnameserv命名服务程序,服务器用JAVA编写,客户机分别用JAVA和C++(VC6+omniORB)编写的试验,希望通过一次编程的具体操作实例来体验或明了CORBA思想。

    总体的编写过程如下:

    1. 用IDL定义一个接口文件,描绘要实现的功能,也可以说是定义一个要实现功能的一个模版(SysProp.idl)
    2. 使用"IDL to Java"编译器(这里是IDLJ)将IDL文件转化为Java编程语言中编写的接口定义,生成所需的代码存根及helper类
    3. 使用Java语言编写客户机和服务器的实现程序。
    4. 使用"IDL to C++"编译器(这里是omniidl)将IDL文件转化为C++编程语言中编写的接口定义,生成所需的代码存根及helper类
    5. 使用C++语言编写客户机实现程序(当然也可编写服务器程序,但本次试验没有进行)
    6. 起动命名服务tnameserv
    7. 起动Java编写的服务程序
    8. 用Java和C++编写的客户机分别调用相应的服务




    回页首


    运行环境的设定:

    总体环境由jdk1.3+omniORB3.0(www.uk.research.att.com\omniORB\doc\3.0) +vc6 组成,下面说明具体的安装。

    2.1. 安装JDK1.3

    从SUN公司DOWN JDK1.3或者通过其他方式得到jdk1.3进行安装,再设定相应的环境变量,在本文测试用的电脑上是如下所示:

    CLASSPATH=.;
                JAVA_HOME=D:\jdk130
                

    修改原来的PATH变量,添加"%JAVA_HOME%\bin;",如下

    PTAH=%JAVA_HOME%\bin;原变量
                

    注意:我在第一次使用jbuilder的jdk1.3时,服务器不能正常工作,我只是发觉这么一回事,具体原因与本文无关而没有进行了解,请谅。

    2.2. 安装VC6

    VC6按常规方式安装,注意的是:在本文测试用的电脑上安装在如下位置

    C:\Program Files\Microsoft Visual Studio

    2.3. 安装omniORB

    从 www.uk.research.att.com\omniORB\doc\3.0 下载omniORB3.0 ( 本文测试所下载的文件是 omniORB_304_x86_win32.zip )。

    用WINZIP进行解压omniORB_304_x86_win32.zip到omniORB_304_x86_win32目录,目录内存在omni目录,复制omni目录内的文件到你把想存放的位置。

    测试电脑安装在 C:\omni

    根据C:\omni\README.win32 文档进行设定,由于运行程序及命令行在控制台进行,所以本次测试并不根据文档要求去设定环境变量,而是编写了一个omni.bat,在使用控制台时,首先运行。

    本测试电脑omni.bat内容如下

    set TOP=c:\omni
                set path=%TOP%\bin\x86_win32;%path%
                set LIB=%TOP%\bin\x86_win32;%LIB%
                set INCLUDE=%TOP%\include;%INCLUDE%
                set VcOsDir=
                set VSCommonDir=
                

    如果你的电脑VC的环境变量已经设定在你的环境变量中,那么C:\Program Files\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT 就可以不运行。否则运行omni.bat前要首先运行VCVARS32.BAT。





    回页首


    实践过程

    约定所有编写的文件保存在D:\mywork\t1中,omni.bat也在这个目录内

    3.1. 编写SysProp.idl,功能是返回系统属性

    interface SysProp
                {  string getProperty(in string name);
                };
                

    3.2. 编写JAVA的服务器

    3.2.1. 把IDL文件转化为JAVA编程语言代码存根类及helper类。

    执行如下命令

     idlj -fall SysProp.idl
                

    在正常的情况下D:\mywork\t1 目录内将生成以下文件,否则请检查你的执行程序及文件

    SysProp.java
    SysPropHelper.java
    SysPropHolder.java
    SysPropOperations.java
    _SysPropImplBase.java
    _SysPropStub.java

    3.2.2. 编写 SysPropServer.java

    import org.omg.CosNaming.*;
                import org.omg.CORBA.*;
                

    //编写相对应的服务,一定要从 _类名ImplBase继承,并实现相应的方法 class SysPropS extends _SysPropImplBase //具体的服务实现

    {  public String getProperty(String key)
                {
                System.out.println("调用"+key);
                String S;
                S=System.getProperty(key);
                if (S==null)   { S="null"; }
                System.out.println(key+"="+S);
                return S;
                }
                }
                public class SysPropServer          //起动服务的程序
                {  public static void main(String args[])
                {  try
                {  System.out.println("创建和初始化 ORB ");
                ORB orb = ORB.init(args, null);
                System.out.println("创建服务对象并将其向 ORB 注册 ");
                SysPropS impl = new SysPropS();
                orb.connect(impl);
                //打印IOR字符串
                System.out.println(orb.object_to_string(impl));
                org.omg.CORBA.Object namingContextObj =orb.resolve_initial_references("NameService");
                         NamingContext namingContext= NamingContextHelper.narrow(namingContextObj);
                NameComponent[] path = {new NameComponent("SysProp", "")};
                System.out.println("绑定服务...SysPropS");
                namingContext.rebind(path, impl);
                System.out.println("等待调用...SysPropS");
                java.lang.Object sync = new java.lang.Object();
                synchronized (sync)
                {  sync.wait();
                }
                }
                catch (Exception e)
                {   System.err.println("Error: " + e);
                e.printStackTrace(System.out);
                }
                }
                }
                

    3.3. 编写JAVA的客户机

    3.3.1. 编写 SysPropClient.java 使用IOR字符串的方式

    注意在代码内有一段注解掉的代码,用"//使用ORB的方法的开始"开始,用"//使用ORB的方法的结束"结束。这段代码是使用ORB方法的代码,如果在代码中"//使用IOR的方法开始"前一行添加"/*",在"//使用IOR的方法结束"后一行添加"*/",而把"//使用ORB的方法的开始"前面的"/*"去掉,把"//使用ORB的方法的结束"后面的"*/"去掉,就是使用ORB方法的代码,程序运行时就是" SysPropClient [环境变量] "的方式。以下是具体代码:

    import org.omg.CosNaming.*;
                import org.omg.CORBA.*;
                public class SysPropClient
                {
                public static void main(String args[])
                {
                try{
                String SetInfo,ReturnInfo,ref;
                org.omg.CORBA.Object objRef;
                SysProp syspropRef;
                ORB orb = ORB.init(args, null);
                //使用IOR的方法开始
                if (args.length>=1)
                {
                ref=args[0];
                }
                else
                {
                System.out.println("SysPropClient <IOR字符串>  [环境变量]");
                return;
                }
                objRef = orb.string_to_object(ref);
                syspropRef = SysPropHelper.narrow(objRef);
                //使用IOR的方法结束
                /*
                //使用ORB的方法的开始
                objRef = orb.resolve_initial_references("NameService");
                NamingContext ncRef = NamingContextHelper.narrow(objRef);
                // 进行服务定位
                NameComponent nc = new NameComponent("SysProp", "");
                NameComponent path[] = {nc};
                syspropRef = SysPropHelper.narrow(ncRef.resolve(path));
                //使用ORB的方法的开始结束
                */
                if (args.length>1)
                {
                SetInfo=args[1];
                }
                else
                {
                SetInfo="java.home";
                }
                System.out.println("开始调用");
                ReturnInfo = syspropRef.getProperty(SetInfo);
                System.out.println(SetInfo+"="+ReturnInfo);
                } catch (Exception e) {
                System.out.println("ERROR : " + e) ;
                }
                }
                }
                

    3.3.2. 编译程序,在文件目录内执行如下命令

    jAVAC *.JAVA

    3.4. 进行测试

    第1控制台,执行
    tnameserv
    测试时如下所示

    D:\mywork\t1>java tnameserv
                Initial Naming Context:
                IOR:000000000000002849444c3a6f6d672e6f72672f436f734e616d696e672f4e616d696e67436f
                6e746578743a312e3000000000010000000000000054000101000000000c3139322e3136382e302e
                31000ca6000000000018afabcafe00000002a999c474000000080000000000000000000000010000
                0001000000140000000000010020000000000001010000000000
                TransientNameServer: setting port for initial object references to: 900
                Ready.
                

    第2控制台,执行
    java SysPropServer
    测试时如下所示

    D:\mywork\t1>java SysPropServer
                创建和初始化 ORB
                创建服务对象并将其向 ORB 注册
                IOR:000000000000001049444c3a53797350726f703a312e30000000000100000000000000540001
                01000000000c3139322e3136382e302e31000ca7000000000018afabcafe00000002a999dbeb0000
                00080000000000000000000000010000000100000014000000000001002000000000000101000000
                0000
                绑定服务...SysPropS
                等待调用...SysPropS
                

    第3控制台,执行
    java SysPropClient IOR:XXX JAVA.HOME
    测试时如下所示

    D:\mywork\t1>java SysPropClient IOR:000000000000001049444c3a53797350726f703a312e
                3000000000010000000000000054000101000000000c3139322e3136382e302e31000ca700000000
                0018afabcafe00000002a999dbeb0000000800000000000000000000000100000001000000140000
                000000010020000000000001010000000000 java.home
                开始调用
                java.home=D:\bea\jdk130\jre
                

    3.5. 编写C++的IOR客户机

    从实践来讲编写C++的客户机程序同JAVA没有多大的区别,只不过JAVA是用idlj生成代码存根类及helper类,而omni是用omniidl来生成代码存根类及helper类,而编程思想及编码过程非常相似。

    由于C++的程序要调用omni及VC6的相关文件,所以进入控制台后,如果VC没有进行环境变量设定,那么要先运行 C:\Program Files\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT,再运行omni.bat,否则直接运行omni.bat后再编译程序及运行程序。

    3.5.1. 把IDL文件转化为C++编程语言代码存根类及helper类。

    执行如下命令
    omniidl -bcxx SysProp.idl
    在正常的情况下D:\mywork\t1 目录内将生成C++编程语言的代码存根类及helper类SysProp.hh和SysPropSK.cc。否则请检查你的执行程序及文件。

    3.5.2. 编写SysPropC.cc

    #include <iostream.h>
                #include <SysProp.hh>
                int main(int argc, char** argv)
                {
                try {
                CORBA::ORB_var orb = CORBA::ORB_init(argc, argv, "omniORB3");
                if( argc < 2 || argc > 3 ) {
                cout << "usage:  SysPropC <IOR字符串> [环境变量名]" << endl;
                return 1;
                }
                CORBA::Object_var obj = orb->string_to_object(argv[1]);
                SysProp_ptr echoref = SysProp::_narrow(obj);
                if( CORBA::is_nil(echoref) ) {
                cerr << "没有对象" << endl;
                return 1;
                }
                const char* message;
                if (argc==3)
                {
                message=argv[2];
                }
                else
                {
                message="java.home";
                }
                CORBA::String_var dest = echoref->getProperty(message);
                cout <<  (char*)message << "=" <<(char*)dest << endl;
                orb->destroy();
                }
                catch(...) {
                cerr << "Caught unknown exception." << endl;
                }
                return 0;
                }
                

    3.5.3. 编写dirc.mak,如下所示

    TOP = C:\omni
                OMNI_DYNAMIC_LIB = msvcstub.lib -NODEFAULTLIB:libcmt.lib -NODEFAULTLIB:libcmtd.lib
                CORBA_CPPFLAGS = -D__WIN32__ -D__x86__ -D__NT__ -D__OSVERSION__=4
                CORBA_LIB      = omniORB304_rt.lib omnithread2_rt.lib \
                $(OMNI_DYNAMIC_LIB) \
                wsock32.lib advapi32.lib \
                -libpath:$(TOP)\lib\x86_win32
                CXXFLAGS       = -O2 -MD -GX $(CORBA_CPPFLAGS) $(DIR_CPPFLAGS)
                CXXLINKOPTIONS =
                .SUFFIXES: .cc
                .cc.obj:
                cl /nologo /c $(CXXFLAGS) /Tp$<
                all:: SysPropC.exe
                SysPropC.exe: SysPropSK.obj SysPropC.obj
                link -nologo $(CXXLINKOPTIONS) -out:$@ $** $(CORBA_LIB)
                clean::
                -del *.obj
                -del *.exe
                veryclean::
                -del *.obj
                -del echoSK.* echo.hh
                -del *.exe
                SysProp.hh SysPropSK.cc: SysProp.idl
                $(TOP)\bin\x86_win32\omniidl -T -bcxx -Wbh=.hh -Wbs=SK.cc -Wbtp SysProp.idl
                

    3.5.4. 编译程序,执行如下命令

    nmake -f dirc.mak

    3.5.5. 测试 

    在第4控制台
    SysPropC IOR:XXX JAVA.HOME
    本测试如下所示

    D:\mywork\t1>syspropc IOR:000000000000001049444c3a53797350726f703a312e3000000000
                010000000000000054000101000000000c3139322e3136382e302e31000ca7000000000018afabca
                fe00000002a999dbeb00000008000000000000000000000001000000010000001400000000000100
                20000000000001010000000000 os.name
                os.name=Windows 2000
                

    3.6. 编写C++的NAME方式客户机

    为了使用NANE方式,必须为OMNI软件设置注册表信息,要在注册表中建立如下数据项(regedit)HKEY_LOCAL_MACHINE\SOFTWARE\ORL\omniORB\2.0\NAMESERVICE(字符串)。

    NAMESERVICE的值为tnameserv(jdk1.3\bin内的程序)启动的IOR值(第一次设置时自行添加)。

    注意为了使用这种方式每次起动tnameserv后要用新IOR值换去旧的IOR值,我测试过用omini的omniNames.exe程序做服务器,IOR值是不变的,但服务器用JVAV编写就会出错。如果起动tnameserv,用 c编写的服务器及客户机就可以在上面运行。本例子如下所示

    Initial Naming Context:
                IOR:000000000000002849444c3a6f6d672e6f72672f436f734e616d696e672f4e616d696e67436f
                6e746578743a312e3000000000010000000000000054000101000000000c3139322e3136382e302e
                31000ca6000000000018afabcafe00000002a999c474000000080000000000000000000000010000
                0001000000140000000000010020000000000001010000000000
                TransientNameServer: setting port for initial object references to: 900
                Ready.
                

    那么就要把从 IOR:开始(含IOR:)后面的字符串放进注册表。

    3.6.1. 编写SysPropCC.cc

    //使用NAME方式的客户机
                #include <iostream.h>
                #include "SysProp.hh"
                static CORBA::Object_ptr getObjectReference(CORBA::ORB_ptr orb);
                int main (int argc, char **argv)
                {
                if(  argc != 2 ) {
                cout << "使用方法: SysPropCC  <环境变量名>" << endl;
                return 1;
                }
                try {
                CORBA::ORB_var orb = CORBA::ORB_init(argc, argv, "omniORB3");
                CORBA::Object_var obj = getObjectReference(orb);
                SysProp_ptr echoref = SysProp::_narrow(obj);
                const char* message;
                if (argc==2){message=argv[1];}
                else {message="java.home"; }
                CORBA::String_var dest = echoref->getProperty(message);
                cout <<  (char*)message << "=" <<(char*)dest << endl;
                orb->destroy();
                }
                catch(...) { cerr << "Caught unknown exception." << endl; }
                return 0;
                }
                //////////////////////////////////////////////////////////////////////
                static CORBA::Object_ptr getObjectReference(CORBA::ORB_ptr orb)
                {
                CosNaming::NamingContext_var rootContext;
                try {
                // 定位服务器:
                CORBA::Object_var obj;
                obj = orb->resolve_initial_references("NameService");
                // Narrow the reference returned.
                rootContext = CosNaming::NamingContext::_narrow(obj);
                if( CORBA::is_nil(rootContext) ) {
                cerr << " 初始化不成功." << endl;
                return CORBA::Object::_nil();
                }
                }
                catch(CORBA::ORB::InvalidName& ex) {
                cerr << " 没有找到服务" << endl;
                return CORBA::Object::_nil();
                }
                CosNaming::Name name;
                name.length(1);
                name[0].id   = (const char*) "SysProp";
                name[0].kind = (const char*) "";
                try {
                // Resolve the name to an object reference.
                return rootContext->resolve(name);
                }
                catch(...) {  cerr << "定位不成功." << endl;}
                return CORBA::Object::_nil();
                }
                

    3.6.2. 编写dircc.mak

    TOP = C:\omni
                DIR_CPPFLAGS   = -I. -I$(TOP)\include
                OMNI_DYNAMIC_LIB = msvcstub.lib -NODEFAULTLIB:libcmt.lib -NODEFAULTLIB:libcmtd.lib
                CORBA_CPPFLAGS = -D__WIN32__ -D__x86__ -D__NT__ -D__OSVERSION__=4
                CORBA_LIB      = omniORB304_rt.lib omnithread2_rt.lib \
                $(OMNI_DYNAMIC_LIB) \
                wsock32.lib advapi32.lib \
                -libpath:$(TOP)\lib\x86_win32
                CXXFLAGS       = -O2 -MD -GX $(CORBA_CPPFLAGS) $(DIR_CPPFLAGS)
                CXXLINKOPTIONS =
                .SUFFIXES: .cc
                .cc.obj:
                cl /nologo /c $(CXXFLAGS) /Tp$<
                all:: SysPropCc.exe
                SysPropCc.exe: SysPropSK.obj SysPropCc.obj
                link -nologo $(CXXLINKOPTIONS) -out:$@ $** $(CORBA_LIB)
                clean::
                -del *.obj
                -del *.exe
                veryclean::
                -del *.obj
                -del echoSK.* echo.hh
                -del *.exe
                SysProp.hh SysPropSK.cc: SysProp.idl
                $(TOP)\bin\x86_win32\omniidl -T -bcxx -Wbh=.hh -Wbs=SK.cc -Wbtp SysProp.idl
                

    3.6.3. 编译文件

    nmake -f dircc.mak

    3.6.4. 测试

    在第5控制台
    SysPropCC JAVA.HOME
    测试结果如下所示

    D:\mywork\t1>syspropcc java.home
                java.home=D:\bea\jdk130\jre
                





    回页首


    小结

    另还使用了j2sdkee1.2.1进行测试,由于j2sdkee1.2.1起动时的nameserver的PORT是1050 所以启动服务要加参数,对于本文中的程序运行时要如下所示:

    java SysPropServer -ORBInitialPort 1050

    如果用IOR的方式,也可以用omni的omniNames.exe程序做命名服务器,用C++编写服务器,客户机使用Java和C++编写,c++编写服务器的例子可见omni的文档。

    本次只是简单进行了测试,可以使大家了解一下,CORBA的这种特性,多种不同程序语言进行协作编程的具体运作过程,希望可以抛砖引玉。



    参考资料

    《JAVA2核心技术 卷II:高级特性》(对于omniORB是2.0的例子)

    《The omniORB version 3.0 User's Guid》(omniORB软件内自带)



    关于作者

    王辉,具有八年的编程及系统管理经验,所使用的语言为C和Java 编程语言。目前在深圳一家公司做分析员,使用C和JAVA为DB2数据库编程。可通过 mailto:ddxxkk@21cn.com?cc=ddxxkk@21cn.com联系。

  • 相关阅读:
    gain 基尼系数
    luogu P5826 【模板】子序列自动机 主席树 vector 二分
    牛客挑战赛39 树与异或 离线 树上莫队 树状数组 约数
    4.22 省选模拟赛 三元组 manacher 回文自动机
    4.22 省选模拟赛 最优价值 网络流 最大权闭合子图
    4.18 省选模拟赛 消息传递 树剖 倍增 线段树维护等比数列
    luogu P4008 [NOI2003]文本编辑器 splay 块状链表
    牛客挑战赛39 密码系统 后缀数组
    luogu P1526 [NOI2003]智破连环阵 搜索+最大匹配+剪枝
    luogu P4095 [HEOI2013]Eden 的新背包问题 多重背包 背包的合并
  • 原文地址:https://www.cnblogs.com/luluping/p/1666188.html
Copyright © 2011-2022 走看看