计算器客户和服务器

本例子带给你对如何制作一个实际的可工作的服务器的进一步理解,使用了来自 ORBit 的支持。

系统不能做很多。服务器只提供两个功能,一个是两个数的加法一个是两个数的减法。第一件必须做的事是为服务器写 IDL 文件。在我们的例子中是非常简单的。

Example 5-3. 计算器 IDL 文件

interface Calculator
{
      double add(in double number1, in double number2);
      double sub(in double number1, in double number2);
};

接着你必须生成框架(skeleton)和存根(stub)文件。除这两个文件之外 ORBit IDL 还生成一个公用文件和一个头文件。公用文件实现内存管理功能和其他事情,对客户和服务器都有用。生成 C 源文件的命令序列非常简单。$ orbit-idl --skeleton-impl calculator.idl 生成我们例子要用的所有文件。

简单的计算器客户

下一件你必须做的事是写服务器和客户程序。我们从客户开始,因为它容易些。

一个简单的客户实现可能是这样的:

Example 5-4. 计算器客户 C 源文件

#include "orb/orbit.h"
#include "calculator.h"
#include <stdio.h>

int
main(int argc, char* argv[])
{
        CORBA_Environment ev;
	CORBA_ORB         orb;
	CORBA_Object      server;
        CORBA_double      res;
        gchar*            dummy_argv[2];
        gint              dummy_argc;

        dummy_argc = 1;
        dummy_argv[0] = argv[0];
        dummy_argv[1] = 0;

	CORBA_exception_init(&ev);
	orb = CORBA_ORB_init(&dummy_argc, dummy_argv, "orbit-local-orb", &ev);
	server = CORBA_ORB_string_to_object(orb, argv[1], &ev);
        res = Calculator_add(server,1.0, 2.0, &ev);
	fprintf(stdout,"1.0 + 2.0 = %2.0f\n", res);
	CORBA_Object_release(server,&ev);
	exit(0);
}

  

非常简单,但全是未加解释的材料。我们先看一下定义的变量。

env
这个变量用于保持在函数调用期间可能发生的例外的信息。在后面的例子中将解释如何使用这个例子来侦测函数中的错误。
orb
这是 ORB 自身。
server
这是对服务器的对象引用。

上面的例子是全功能的客户。这个例子的秘诀是把 argv[1] 参数传递给 CORBA_ORB_string_to_object 函数。对此的解释为假设程序是以表示计算器服务器的字符串为第一个参数而被调用的。如何获得这个字符串,将在下一个例子中描述。

简单的计算器服务器

对于实现服务器,IDL 编译器为你做了大量的工作。它能为服务器实现散发对设置数据结构和函数调用必须的材料。所有你必须写的是在你的主函数中的设置这些材料,以及真正的服务器功能实现。首先我将提供 IDL 编译器生成的函数和数据结构,接着我将展示为使这些函数正确的工作,必须设置的环境是那些。

计算器实现框架

为了减轻实现计算器的任务, ORBit IDL 编译器输出一个服务器的实现框架。是通过给 IDL 编译器一个 --skeleton-impl 开关(选项)来允许生成的。 orbit-idl --skeleton-impl calculator.idl 的输出是下面这个样子(生成的源文件的缺省的名字是 calculator-skelimpl.c ,但在这个例子中被重命名为 calculator-impl.c ):

Example 5-5. 计算器实现框架

#include "calculator.h"

/***
    App-specific servant structures
    应用程序指定的仆从结构 
 ***/
 
typedef struct {
   POA_Calculator servant;
   PortableServer_POA poa;

} impl_POA_Calculator;

/*** 
    Implementation stub prototypes 
    实现存根原型
 ***/

static void impl_Calculator__destroy(impl_POA_Calculator * servant,
				     CORBA_Environment * ev);

CORBA_double impl_Calculator_add(impl_POA_Calculator * servant,
		    CORBA_double number1,
		    CORBA_double number2,
		    CORBA_Environment * ev);

CORBA_double impl_Calculator_sub(impl_POA_Calculator * servant,
		    CORBA_double number1,
		    CORBA_double number2,
		    CORBA_Environment * ev);

/***
    epv structures
    epv 结构
 ***/
 
static PortableServer_ServantBase__epv impl_Calculator_base_epv =
{
   NULL,			/* _private data */
   (gpointer) & impl_Calculator__destroy,	/* finalize routine */
   NULL,			/* default_POA routine */
};
static POA_Calculator__epv impl_Calculator_epv =
{
   NULL,			/* _private */
   (gpointer) & impl_Calculator_add,

   (gpointer) & impl_Calculator_sub,

};

/***
    vepv structures
    vepv 结构
 ***/
static POA_Calculator__vepv impl_Calculator_vepv =
{
   &impl_Calculator_base_epv,
   &impl_Calculator_epv,
};

/***
    Stub implementations
    存根实现
 ***/
 
static Calculator 
impl_Calculator__create(PortableServer_POA poa, CORBA_Environment * ev)
{
   Calculator retval;
   impl_POA_Calculator *newservant;
   PortableServer_ObjectId *objid;

   newservant = g_new0(impl_POA_Calculator, 1);
   newservant->servant.vepv = &impl_Calculator_vepv;
   newservant->poa = poa;
   POA_Calculator__init((PortableServer_Servant) newservant, ev);
   objid = PortableServer_POA_activate_object(poa, newservant, ev);
   CORBA_free(objid);
   retval = PortableServer_POA_servant_to_reference(poa, newservant, ev);

   return retval;
}

/*
 * You shouldn't call this routine directly without first deactivating the servant... 
 * 不终止仆从就不能直接调用这个例程
 */
 
static void
impl_Calculator__destroy(impl_POA_Calculator * servant, CORBA_Environment * ev)
{

   POA_Calculator__fini((PortableServer_Servant) servant, ev);
   g_free(servant);
}

CORBA_double
impl_Calculator_add(impl_POA_Calculator * servant,
		    CORBA_double number1,
		    CORBA_double number2,
		    CORBA_Environment * ev)
{
   CORBA_double retval;

   return retval;
}

CORBA_double
impl_Calculator_sub(impl_POA_Calculator * servant,
		    CORBA_double number1,
		    CORBA_double number2,
		    CORBA_Environment * ev)
{
   CORBA_double retval;

   return retval;
}

这个源文件提供给你一个服务器的最神秘之处。注意我们生成的这个文件(用了 --skeleton-impl 开关)只是一次,接着 makefile 不加任何开关的调用 orbit-idl。如果你在 makefile 中调用 orbit-idl --skeleton-impl,以前的文件将被重写并且你的实现代码将丢失。一旦写了实现代码,只需要在 calculator-server.c 文件的开头包含这个源文件。

在这个例子中,我不想解释生成的这个文件的所有部分和所有位。这将在以后去做。我们将集中于服务器的运行。

下面是两个函数

CORBA_doubleimpl_Calculator_add(impl_POA_Calculator*servant, CORBA_doublenumber1, CORBA_doublenumber2, CORBA_Environment*ev);

CORBA_doubleimpl_Calculator_sub(impl_POA_Calculator*servant, CORBA_doublenumber1, CORBA_doublenumber2, CORBA_Environment*ev);

这两个函数是在 IDL 文件中定义的函数的实现。因为 IDL 编译器不提供真实的实现(它不能知道函数是干什么用的),你必须自己扩展这个框架。

对于 impl_Calculator_add() 应增加两个参数并返回结果所以这个函数应被变更为:

CORBA_double
impl_Calculator_add(impl_POA_Calculator * servant,
		    CORBA_double number1,
		    CORBA_double number2,
		    CORBA_Environment * ev)
{
   CORBA_double retval;

   retval = number1 + number2;
   return retval;
}

计算器服务器实现

在你的已经最小化的主函数中你要使程序工作必须做的事是用下列方式实现的。

Example 5-6. 计算器服务器实现

#include "calculator-impl.c"
#include <stdio.h>

int
main(int argc, char* argv[])
{
  CORBA_ORB                 orb;
  CORBA_Environment*        ev;
  PortableServer_ObjectId*  oid;
  Calculator                calculator;
  PortableServer_POA        root_poa;
  PortableServer_POAManager pm;
  CORBA_char*               objref;
  
  ev = g_new0(CORBA_Environment,1);

  CORBA_exception_init(ev);

  orb = CORBA_ORB_init(&argc, argv, "orbit-local-orb", ev);

  /* 
   * Handle exception somehow 
   * 处理某种原因下的例外
   */
   
  root_poa = (PortableServer_POA)CORBA_ORB_resolve_initial_references(orb, "RootPOA", ev);
 
  /* 
   * 处理某种原因下的例外
   */
   
  calculator = impl_Calculator__create(root_poa, ev);
 
  /* 
   * 处理某种原因下的例外
   */
   
  objref = CORBA_ORB_object_to_string(orb, calculator, ev);
 
  /* 
   * 处理某种原因下的例外
   */
   
  fprintf(stderr, "%s\n", objref);

  pm = PortableServer_POA__get_the_POAManager(root_poa, ev);
 
  /* 
   * 处理某种原因下的例外
   */
   
  PortableServer_POAManager_activate(pm, ev);
 
  /* 
   * 处理某种原因下的例外
   */
   
  CORBA_ORB_run(orb, ev);
  return 0;
}

我不打算解释这个例子的每一行,我们要马上让服务器运行并做我们对它的第一次调用。但 fprintf() 调用的那一行值得解释。这个调用的目的是输出表示对象引用的字符串。这个字符串是以"IOR:"序列开头的,是客户程序的参数。它标识一个特定对象。包括:服务器进程所在的主机,服务器的位置,在这个特定的服务器里的对象标识,这样详细的原因是很可能在一个服务器主机上有多个对象。我们以后解释如何得到这个字符串或对象引用,而不需要剪切一个(服务器)程序的输出接着再粘贴在另一个(客户)程序的命令行上。

编译并运行服务器和客户

下面的 makefile 可用于编译客户和服务器两者。要知道 ORBit 的位置:在我的系统上被安装在 /usr 下面,如果你从源文件编译则它可能在 /usr/local 下,所以 ORBIT 变量的路径可能是多样的。在 orbit-docs.tar.gz 中的例子的配置脚本将找到正确的路径并写出 makefile 文件。

CC = gcc
ORBIT_IDL = /usr/bin/orbit-idl
ORBIT_CFLAGS = -I/usr/lib/glib/include -I/usr/include
ORBIT_LIBS = -L/usr/lib -lORBit -lIIOP -lORBitutil -lglib -lm
CFLAGS = $(ORBIT_CFLAGS)
LFLAGS = $(ORBIT_LIBS)

all : idltargets calculator-client calculator-server

calculator-client : calculator-client.o calculator-common.o calculator-stubs.o
    $(CC) -o calculator-client calculator-client.o calculator-stubs.o
calculator-common.o  -lIIOP -lORBit -lORBitutil $(LFLAGS)

calculator-server : calculator-server.o calculator-skels.o calculator-common.o
    $(CC) -o calculator-server calculator-server.o calculator-skels.o
calculator-common.o  -lIIOP -lORBit -lORBitutil $(LFLAGS)

在调用了 make all 之后你要打开两个窗口。在第一个窗口中我们用命令:calculator-server 启动服务器。服务器将输出一个非常长的以四个字符的 IOR: 序列开头的字符串,在第二个窗口中用命令 calculator-client IOR-string 启动客户程序。你最好不要试图键入这个字符串,而是在你的 xterm 或其他你用那个类似的东西中使用剪切和粘贴功能。

如果一切正常工作,你将看到输出:3.