从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