Chinaunix首页 | 论坛 | 博客
  • 博客访问: 318739
  • 博文数量: 145
  • 博客积分: 2500
  • 博客等级: 少校
  • 技术积分: 1510
  • 用 户 组: 普通用户
  • 注册时间: 2006-03-19 11:12
文章分类

全部博文(145)

文章存档

2007年(75)

2006年(70)

我的朋友

分类:

2007-01-23 15:41:43

  Applet与Servlet通讯的四种方法及其比较    
     
     
  Applet与Servlet之间的联系一般是间接的,即页面请求由浏览器发送给Servlet,作为回答,Servlet将结果生成HTML文档发送给浏览器。很多时候在Servlet与Applet之间建立直接的联系是没有必要的,但如果要显示一些实时的动态数据,如聊天室窗口、新闻显示、股市行情等,建立两者之间的直接联系就很有用了。特别地,在较为复杂的分布式Java应用中,这种通讯更是必不可少。在客户端Java程序与服务器端Java程序之间建立这种复杂的交互方式仅仅使用Servlet与Applet   API是不够的,本文为你介绍和比较四种通讯方式:通过HTML页面传递参数,用java.net包的网络功能建立直接网络连接,远程方法调用(RMI)与CORBA。       
       一、通过HTML页面传递Applet参数       
    通过HTML页面传递参数是在Servlet与Applet之间建立联系最为常用的方法。Servlet只要把传递给Applet的参数写入HTML页面就可以了。这种通讯是单向的,用于实现Servlet对Applet的控制。其实现方法类如:       
  //   doGet()方法生成一个包含Applet的HTML页面,       
  //   该Applet需要一个Data参数       
  public   void   doGet(HttpServletRequest   request,HttpServletResponse   response)       
  throws   ServletException,   IOException   {       
  response.setContentType("text/html");       
  PrintWriter   out   =   new   PrintWriter   (   response.getOutputStream());       
  out.write("<   HTML><   HEAD><   TITLE>   HTML   Example<   /TITLE>   <   /HEAD><   BODY>");       
  out.write("Servlet通过PARAM标记传递参数给Applet.   ");       
  out.write("<   APPLET   code=\"SimpleApplet\"   width=\"225\"   height=\"149\">");       
  //   写入PARAM标记       
  out.write("<   param   name=\"Data\"   value=\"");    
  out.write(java.text.DateFormat.getDateInstance().format(new   java.util.Date()));    
  out.write("\"><   /APPLET><   /BODY><   /HTML>");       
  out.close();       
  }       
    这种方法很简单,但它有两个缺点。首先,所有参数都是静态的,即一旦HTML页面发送完成,要更新其中部分数据就很困难了。虽然可以使用HTTP的刷新机制,但这种刷新需要重新下载整个页面,效率是很低的。第二,如果需要传递给Applet的数据量很大或数据结构很复杂,就会使得HTML页面过于庞大或复杂了。       
       二、用java.net包建立双向通讯       
    第二种方法是利用java.net包提供的网络能力。以有连接流通讯方式为例,在服务器端的操作为一般为:       
       ⑴   创建一个ServerSocket对象,在指定端口监听客户端发来的请求。       
       ⑵   在接收到请求时accept()方法将返回一个Socket对象。       
       ⑶   用上述Socket对象创建输入、输出流对象。       
       ⑷   通过输入、输出流与客户交互。       
       ⑸   交互完毕,关闭输入、输出流与Socket。       
       ⑹   服务程序运行结束,关闭ServerSocket。       
       实现代码代码类如:       
  try{       
  boolean   flag=true;       
  Socket   clientSocket=null;       
  ServerSocket   serverSocket   =   new   ServerSocket(0);       
  System.out.println("Server   listen   on:   "   +serverSocket.getLocalPort());       
  while(flag){       
  clientSocket=serverSocket.accept();       
  DataInputStream   is=new   DataInputStream(   new   bufferedInputStream(clientSocket.getInputStream()));       
  PrintStream   os=new   PrintStream(   new   bufferedOutputStream(clientSocket.getOutputStream()));       
  //   处理Applet请求       
  os.close();       
  is.close();       
  clientSocket.close();       
  }       
  serverSocket.close();       
  }catch(   IOException   e){       
  System.err.println("   Exception:   "+e);       
  }       
       在客户端的操作为:       
       ⑴   创建Socket对象建立与服务器的连接。       
       ⑵   用该Socket对象创建输入、输出流。       
       ⑶   与服务器交互。       
       ⑷   交互完毕,关闭输入、输出流与Socket。       
       实现代码类如:       
  try   {       
  Socket   clientSocket   =new   Socket("serverName",7);       
  OutputStream   os=clientSocket.getOutputStream();       
  DataInputStream   is=new   DataInputStream(   clientSocket.getInputStream());       
  //   其它操作.       
  os.close();       
  is.close();       
  clientSocket.close();       
  }catch(Exception   e){       
  System.err.println("Exception:"+e);       
  }       
    这种方法只依赖于标准的Java网络支持,不需要用到附加的软件包或工具,因此显得相当简洁和灵活,易于实现某些特殊的需要。但在另一方面,服务器端对进入请求以及客户端对返回结果的分析工作量较大。如果服务器端要执行大量的操作,可以考虑使用RMI。       
       三、远程方法调用RMI       
       Java的RMI技术显著地改善了客户程序操作复杂的服务器端对象的能力。对Applet来说,服务器端对象所提供的远程方法可以象普通的客户端句柄一样调用。使用RMI时应先定义远程接口,即一个继承自java.rmi.Remote的公用接口。在该接口中定义要求在服务器上运行的方法,所有这些方法的throws子句至少应该声明java.rmi.RemoteException异常。       
       接下来是实现远程接口,远程接口的实现类应该:       
       ⑴   指定所实现的一个或多个接口。       
       ⑵   定义远程对象的构造方法。       
       ⑶   提供所有可供远程调用的方法的实现。       
       ⑷   创建和安装安全管理器。       
       ⑸   创建一个或多个远程对象实例。       
       ⑹   注册至少一个远程对象。       
    下面的代码实现一个返回日期字符串的getData()远程方法,该方法由接口RMIServ2App定义:       
  //   本类继承自UnicastRemoteObject,实现接口serv2app       
  package   com.servletsolutions.serv2app.rmi;       
  import   java.rmi.*;       
  import   java.rmi.server.*;       
  public   class   RMIImplementation   extends   UnicastRemoteObject       
  implements   RMIServ2App{       
  public   RMIImplementation()   throws   RemoteException{}       
  public   String   getData()   throws   RemoteException{       
  return   java.text.DateFormat.getDateInstance().format(       
  new   java.util.Date());       
  }       
  public   static   void   main(String[]   args){       
  //   安全管理器       
  System.setSecurityManager(new   RMISecurityManager());       
  try{       
  //   远程对象实例       
  RMIImplementation   bootStrap   =   new   RMIImplementation();       
  //   注册       
  Naming.bind(args[0],   bootStrap);       
  System.out.println(args[0]   +   "   注册成功");       
  }catch(Exception   e){       
  System.err.println("注册失败.");       
  System.err.println(e);       
  }       
  }       
  }       
    在客户端,Applet对接口RMIServ2App的引用通过调用Naming.lookup(String)获得,如下所示:       
       RMIServ2App   obj   =   (RMIServ2App)Naming.lookup("//"   +   getCodeBase().getHost()   +   "/ServerName");       
    除了编译Java代码之外,还要用rmic编译器生成远程对象的存根和骨架。存根(Stub)是远程对象在客户端的代理,它将RMI调用传递给服务器端的骨架(Skeleton),后者负责将该调用传递给实际的远程方法。例如执行命令rmic   com.servletsolutions.RMIImplementation,则生成的两个类文件分别为RMIImplementation_Stub.class   和RMIImplementation_Stub.class,前者封装入Applet且运行于客户端,后者封装入Servlet且运行与服务器端。       
    当大量的数据是由服务器端的对象动态提供时使用RMI是非常合适的。虽然RMI支持面向对象的分布式编程,但它要求客户端和服务器端都是Java程序。因此,对于使用混合语言开发的应用来说,CORBA比RMI更为适用。       
       四、使用CORBA       
       CORBA即公用对象请求代理体系(Common   Object   Request   Broker   Architecture),它的使用方法类似于RMI:定义接口,使用某个工具创建Stub和Skeleton,实现接口,在服务器上注册后客户就可以像本地对象一样访问远程对象了。两者主要的不同之处在于,CORBA使用一种语言中立的接口定义语言(IDL)来定义接口,IDL是一种可用于混合语言环境的更为广泛的协议集。CORBA最大的缺点在于客户本身必须具备调用ORB的能力,这往往意味着增加一个体积较大的JAR文件。       
    下面的CORBAServ2App.idl是上述getData()接口的IDL描述,它定义了一组与Java包名字对应的嵌套模块:       
  module   com   {       
  module   servletsolutions   {       
  module   serv2app   {       
  module   corba   {       
  interface   CORBAServ2App{       
  string   getData();       
  };       
  };       
  };       
  };       
  };       
    可以用idl2java从IDL文件生成对应的java文件。其中_CORBAServ2AppImplBase.java包含了描述该接口的java抽象类,下面的CORBAImplementation类继承自抽象类_CORBAServ2AppImplBase并具体实现getData()方法:       
  package   com.servletsolutions.serv2app.corba;       
  public   class   CORBAImplementation   extends   _CORBAServ2AppImplBase   {       
  CORBAImplementation(String   s){   super(s);   }       
  public   String   getData(){       
  return   java.text.DateFormat.getDateInstance().format(new   java.util.Date());       
  }       
  }       
    为了让该类可以被客户访问,Servlet中还应该创建其实例。代码如下所示:       
  //   在Servlet的init()方法中初始化CORBAImplementation对象       
  //   其中BOA(Basic   Object   Adapter)的作用类似于RMI中的注册服务       
  public   class   CORBAServ   
阅读(438) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~