CSDN博客

img zhangleibbq

用RMI编写网络应用程序

发表于2004/1/26 1:33:00  896人阅读

    从Java入门至今已有一年光景.现在谈谈一年心得.首先,Java基础是必须学好的,必须掌握Java的语法规则.接下来

就是awt下的Grahpics和swing包,应用程序编程必学,io下面也是极其重要的内容,再就到了网络编程方面,刚开始接触到

的是Socket编程,初学Java两个月便用Socket编程写"四国大战"(因为我对联众四国大战特有兴趣),写这个游戏整整花了

我一个月时间,每天是上课编程,下课编程,每天晚上忙到12点,甚至做梦也在编程,一个月后,整完了,四国大战也可以正常

开战了,心里乐滋滋的,这就是编程的乐趣.然而初学两个月的Java(Java是我学习的第一门而向对象的编程语言),写出来

的东西结构混乱不堪,用记事本写的几千行的代码搞得我连加功能都不知道去哪儿,于是我决定对其进行改版,这回我选择

JCreator,这是个不错的Java编程工具,找个方法也没那么费劲了,省了我不少时间,建立好整个游戏系统的框架,我准备开

始"动手"实现功能了,于是第一步便遇到自己设计应用程序级协议,这是一个头疼的问题,命令设计就让人头大,还得考虑网

络数据安全性问题,如何通过防火墙,我一筹莫展,整个系统也耽搁了好长时间,因为还有工作(JSP开发企业应用).一次查资

料的偶然,我看到了RMI,带着好奇心,我看了入门,这时我有这么一个感觉,这个东东能不能帮我实现网络对话,而不用自已

写协议?果然,它能做到!我感觉到Java实在是太强大了,使用RMI写网络应用程序,不用自已设计协议,不用自己考虑数据安全,

不用考虑网络防火墙.我的血液沸腾了.用两天看完RMI基础,测试了Hello, world后,我操起了RMI继续我的游戏系统"革命".

在这儿,我不准备详细介绍我的游戏系统,因为我还没有时间去完全实现它的功能.在这里我想通过一个简单的网络应用(聊

天室)来向各位初涉RMI介绍RMI.

    第一步,让我们来看看聊天室能做些什么.我们的聊天室准备实现最简单的功能,大家一起聊天.于是客户端需要一个显示

聊天内容的文本域,一个发送消息的文本框,和一个发送按钮.为支持HTML语法消息,刚才的文本域可以使用JTextPane;我们

的服务器需要知道往哪几个聊天内容显示区域中发送消息,于是,聊天内容显示域必须在服务器上注册.因此它必须是可以序

列化到服务器的.为让服务器可以给它发送消息,它必须有一个可以在远程调用的方法,于是它本身得实现Remote接口,而Remote

接口是空接口,于是,我们得写一个接口继承Remote接口,在这个接口中,我们声明一个可以在服务器端调用的方法:

appendChatContent(String msg).让我们来先"搞定"这个接口ChatViewerInterface:

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface ChatViewerInterface extends Remote
{
    void appendChatContent(String msg) throws RemoteException;
 }

这个接口写完了,如果服务器需要更多的操作,可定义多个方法,这些方法必须抛出RemoteException,然后再实现这个接口.

接下来,我们看看聊天内容显示域类ChatViewer的代码(这儿,我们将它与滚动面板绑在一起,实现聊天内容的滚动):

import java.rmi.*;
import java.rmi.server.*;
import javax.swing.*;
import java.io.*;
import javax.swing.text.*;
import javax.swing.text.html.*;
import javax.swing.event.*;
import java.text.*;

public class ChatViewer extends JComponent implements ChatViewerInterface,Serializable
{
    JScrollPane scrollpane;
    JTextPane viewer;
    public ChatViewer()
    {
 this.initializedComponent();
     }
    public ChatViewer(String inimsg)
    {
 this.initializedComponent();
 this.viewer.setText(inimsg);
     }
    private void initializedComponent()
    {
 this.viewer = new JTextPane();
 this.viewer.setContentType("text/html;charset=gb2312");
 this.viewer.setEditable(false);
 this.viewer.addHyperlinkListener(new LinkListener()); 
 this.scrollpane = new JScrollPane(this.viewer);
 this.setLayout(new BorderLayout());
 this.add(this.scrollpane,BorderLayout.CENTER);
     }
    public void appendChatContent(String msg)
    {
 HTMLEditorKit kit = (HTMLEditorKit)(this.viewer.getEditorKit());
 Document doc = this.viewer.getDocument();
 StringReader reader = new StringReader(msg);
 try{
  kit.read(reader,doc,doc.getLength());    
  }catch(Exception e){System.out.println("chat content /""+msg+"/" lost..");}
 //实现自动滚动
 if(this.viewer.getSelectedText()==null||this.viewer.getSelectedText().trim().length()==0)
 {   
  this.viewer.select(this.viewer.getText().length(),this.viewer.getText().length());
  }
     }
    public void sendToServer()
    {
 try{
     UnicastRemoteObject.exportObject((ChatViewerInterface)this);
     }catch(Exception e){
           System.out.println("send object to server error: "+e.getMessage());
           }
     }
class LinkListener implements HyperlinkListener
{
    public void hyperlinkUpdate(HyperlinkEvent e)
    {      
      if(e.getEventType() == HyperlinkEvent.EventType.ACTIVATED)
      {         
 if(e instanceof HTMLFrameHyperlinkEvent)
 {
   HTMLFrameHyperlinkEvent evt = (HTMLFrameHyperlinkEvent)e;
   HTMLDocument doc = (HTMLDocument)(viewer.getDocument());
   doc.processHTMLFrameHyperlinkEvent(evt);
 }
 else
 {
    try {
    Runtime.getRuntime().exec("explorer "+e.getURL());             
   }
   catch(Exception ioe)  {
     MessageDialog.showMessage(null,Informations.UNSUPPORTED_BROWSER);
   }
 }
      }
    }
 } //内部类
}

在这儿,我介绍一下sendToServer()这个方法,这个方法用于将自身对象序列化到服务器端,服务器端将这些对象注册.

然后我们写一个窗口,来完成客户端的工作.在这个类中,我们要用一个按钮来向服务器发送消息,服务器也是一个远程

对象,我们把这个类命名为ServerForChat,它也实现一个远程接口ServerForChatInterface.在写完这些代码后,我们来

一起看看它们是怎么工作的.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ClientForm extends JFrame
{
    private ChatViewer chat;
    private JTextField megeditor;
    private JButton msgsender;
    private Container contentpane;
    private JPanel panel;
    private ServerForChatInterface server;

    public ClientForm(String serveraddress,int port)
    {
 super("简易聊天室"); 
 try{
      this.server = Naming.lookup("rmi://"+serveraddress+":"+port+"/ChatServer");
     }catch(Exception e){
    System.out.println("不能连接到服务器.");
    System.exit(0);
    }
 this.initializedComponent();
 this.fireEvent();
 this.registChatViewer();
     }
    private void initializedComponent()
    {
 this.chat = new ChatViewer("<font color='blue'>欢迎进入聊天室</font>");
 this.msgeditor = new JTextField();
 this.msgsender = new JButton("发送");
 this.panel = new JPanel();
 this.panel.setLayout(new BorderLayout());
 this.panel.add(this.msgeditor,BorderLayout.CENTER);
 this.panel.add(this.msgsender,BorderLayout.EAST);
 this.contentpane = this.getContentPane();
 this.contentpane.setLayout(new BorderLayout());
 this.contentpane.add(this.chat,BorderLayout.CENTER);
 this.contentpane.add(this.panel,BorderLayout.SOUTH);
     }
    private void fireEvent()
    {
        MessageSender ms = new MessageSender();
 this.msgeditor.addActionListener(ms);
 this.msgsender.addActionListener(ms);
 this.msgeditor.grabFocus();
     }
    private void chat()
    {
 String msg = this.msgeditor.getText();
 this.msgeditor.setText("");
 try{
     this.server.chat(msg);
     }catch(Exception e){
    this.chat.appendChatContent(<font color='red'><b>发送消息失败,请检查网络.</b></font>);
    }
     }
    private void registChatViewer()
    {
 try{
     UnicastRemoteObject.exportObject((ChatViewerInterface)(this.chat));
     this.server.regist((ChatViewerInterface)(this.chat));
     }catch(Exception e){
    this.chat.appendChatContent(<font color='red'><b>连接服务器失败,请检查网络.</b></font>);
    }
     }
    class MessageSender implements ActionListener
    {
 public void actionPerformed(ActionEvent e)
 {
     chat();
  }
     }
    public static void main(String args[])
    {
       String serveraddress = "127.0.0.1";
       int port = 1099;
       if(args!=null&&args.length==2)
       {
          serveraddress = args[0];
   try{
        port = Integer.parseInt(args[1]);
       }catch(Exception e){}
        }
       ClientForm client = new ClientForm(serveraddress,port);
     }
 }

 客户端的工作完成了,它需要与服务器交互的地方:1.到服务器注册自身聊天内容显示域的对象,通过调用服务器的regist(ChatViewerInterface client)方法.

 2.它向服务器发送聊天消息,请求服务器发送消息给其他在线用户,通过调用服务器的chat方法.所以,我们的服务器至少

 包括两个远程方法:regist和chat,我们开始编写服务器远程接口ServerForChatInterface.

 import java.rmi.Remote;
 import java.rmi.RemoteException;

 public interface ServerForChatInterface extends Remote
 {
    void regist(ChatViewerInterface client) throws RemoteException;
    void chat(String msg) throws RemoteException;
  }

 服务器远程接口定义完成,我们开始编写服务器实现代码.

 import java.rmi.*;
 import java.rmi.server.*;
 import java.util.Vector;
 import java.rmi.registry.*;

 public class ServerForChat extends UnicastRemoteObject implements ServerForChatInterface
 {
    private static Vector clients = new Vector();
    private int port = 1099;
    public ServerForChat() throws RemoteException
    {
 super();
     }
    public ServerForChat(int port) throws RemoteException
    {
 super(port);
 this.port = port;
     }
    public void regist(ChatViewerInterface client)
    {
 if(clients.contains(client)==false)
 {
    clients.add(client);
  }
     }
    public void chat(String msg)
    {
 for(int i = 0;i<clients.size();i++)
 {
    try{
  ((ChatViewerInterface)(clients.elementAt(i))).appendChatContent(msg);
        }catch(Exception e){
        clients.remove(i);
        continue;
       }
  }
     }
    public static void main(String args[])
    {
 int port = 1099;
 if(args!=null&&args.length==1)
 {
    try{
  port = Integer.parseInt(args[0]);
        }catch(Exception e){}
  }
 try{
      LocateRegistry.createRegistry(port);
      ServerForChat server = new ServerForChat(port);
      Naming.rebind("rmi://localhost:"+port+"/ChatServer",server);
     }catch(Exception e){
    System.out.println("start sever fail...");
    System.exit(0);
    }
 System.out.println("server started on port: "+port);
     }
  }
整个代码完成了,你是不是感到兴奋,在你熟悉了RMI的机制后,你认为你需要超过半个小时来写一个简单的网络应用

吗?

编译上述代码:

 javac (path)/*.java
 rmic -v1.2 ChatViewer
 rmic -v1.2 ServerForChat

接着我们启动服务器:
 
 java ServerForChat [port]

JVM将启动RMI注册表,在注册表中将保存可供远程调用的对象,远程JVM通过查找网络上的RMI注册表Naming.lookup(String url),

找到位于远程计算机JVM上的对象,你便可以像使用本地JVM对象一样使用远程对象了.而通过UnicastRemoteObject.export(Remote)

则可以直接将本地对象序列化到远程JVM,远程JVM接收到此对象,也可以像使用本地对象一样使用该对象.具体的RMI实现

及残根码,框架码等等概念可参考RMI入门相关书籍.另外值得一提的是Java提供的RMI安全机制,以及HTTP隧道的概念,

感兴趣的读者可详细阅读RMI相关书籍.欢迎与我联系,联系信箱:zlbbq47054370@sina.com

0 0

相关博文

我的热门文章

img
取 消
img