本帖最后由 ApfelZaft 于 2012-8-23 01:38 编辑
RMI全称是Remote Method Invocation-远程方法调用,Java RMI在JDK1.1中实现的,其威力就体现在它强大的开发分布式网络应用的能力上,是纯Java的网络分布式应用系统的核心解决方案之一。其实它可以被看作是RPC的Java版本。但是传统RPC并不能很好地应用于分布式对象系统。而Java RMI 则支持存储于不同地址空间的程序级对象之间彼此进行通信,实现远程对象之间的无缝远程调用。
RMI目前使用Java远程消息交换协议JRMP(Java Remote Messaging Protocol)进行通信。由于JRMP是专为Java对象制定的,Java RMI具有Java的"Write Once,Run Anywhere"的优点,是分布式应用系统的百分之百纯Java解决方案。用Java RMI开发的应用系统可以部署在任何支持JRE(Java Run Environment Java,运行环境)的平台上。但由于JRMP是专为Java对象制定的,因此,RMI对于用非Java语言开发的应用系统的支持不足。不能与用非Java语言书写的对象进行通信。
RMI可利用标准Java本机方法接口JNI与现有的和原有的系统相连接。RMI还可利用标准JDBC包与现有的关系数据库连接。RMI/JNI和RMI/JDBC相结合,可帮助您利用RMI与目前使用非Java语言的现有服务器进行通信,而且在您需要时可扩展Java在这些服务器上的使用。RMI可帮助您在扩展使用时充分利用Java的强大功能。
总之RMI很好的解决的纯JAVA环境中对象远程调用的问题~下面我们就利用RMI的思想来实现一个最最简单的聊天室功能…其实将服务端配置到一个网站服务器中之后完全可以用作小范围的聊天室~
我们开始吧~
首先,我们来定义一个远程接口。这里说点题外话…本来聊天室什么的没啥必要用RMI去实现…TCP就可以了…这里只是RMI一个比较全面应用的例子。
由于自己程序结构的问题,我们需要定义两个接口。先是定义服务端对象的接口,我们把它保存为IChatServer.java:
[mw_shl_code=java,true]import java.rmi.*;
public interface IChatServer extends Remote {
public void addClient(IChatClient client,String msg) throws RemoteException;
public void removeClient(IChatClient client,String msg) throws RemoteException;
public void sendMessage(IChatClient client,String msg) throws RemoteException;
}[/mw_shl_code]
接下来我们实现这个服务端的远程接口里的方法,我们保存为ChatServerImpl.java:
[mw_shl_code=java,true]import java.rmi.*;
import java.rmi.server.*;
import java.util.*;
public class ChatServerImpl extends UnicastRemoteObject implements IChatServer{
ArrayList list;
public ChatServerImpl() throws RemoteException{
super();
list=new ArrayList();
}
public void addClient(IChatClient client,String msg) throws RemoteException{
if(!list.contains(client)){
for(int i=0;i<list.size();i++){
sendMessage((IChatClient)list.get(i),msg);
}
list.add(client);
}
}
public void removeClient(IChatClient client,String msg) throws RemoteException{
if (list.contains(client)){
for(int i=0;i<list.size();i++){
sendMessage((IChatClient)list.get(i),msg);
}
list.remove(client);
}
}
public void sendMessage(IChatClient client,String msg) throws RemoteException{
if(!list.contains(client))
return;
for(int i=0;i<list.size();i++){
String user=client.getName();
//String message=msg;
if(user==null ||user==""){
user="Anonymous";
}
((IChatClient)list.get(i)).sendMessage(user+": "+msg);
}
}
}[/mw_shl_code]
接下来的任务是写一个注册服务端程序,可以命名为ChatServer.java:
[mw_shl_code=java,true]import java.rmi.*;
import java.rmi.registry.*;
public class ChatServer{
public static void main(String[] args){
try{
ChatServerImpl server=new ChatServerImpl();
Naming.bind("IChatServer",server);
System.out.println("Server Start!!");
}
catch(Exception e){
System.out.println("IChatServer: "+e.getMessage());
e.printStackTrace();
}
}
}[/mw_shl_code]
OK,到此我们服务端就算是完成了,下面进行客户端的编写。
由于我们要用到用户界面,聊天室也需要一个面向所有用户的广播,
我们还需要一个客户端的对象接口,命名为IChatClient.java:
[mw_shl_code=java,true]import java.rmi.*;
public interface IChatClient extends Remote{
public String getName() throws RemoteException;
public void sendMessage(String msg) throws RemoteException;
}[/mw_shl_code]
最后一步就是编写聊天室的客户端,别看代码很长,大部分代码都是用来编写聊天室的界面了=。=
其实在聊天室实际运用中,界面设计一般都是交由其它软件完成的,我们只需要把核心的方法调用掌握一般就可以了。
下面是客户端的实现代码 SwingChatClient.java:
[mw_shl_code=java,true]import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.rmi.*;
import java.rmi.server.*;
import java.rmi.registry.*;
import java.util.*;
public class SwingChatClient extends UnicastRemoteObject implements IChatClient,ActionListener{
IChatServer server=null;
boolean connected=false;
String title="ChatClient";
JFrame f;
String username="";
JTextArea area;
JTextField inName;
JButton connect;
JButton disconnect;
JButton send;
JTextField contentIn;
public SwingChatClient() throws RemoteException{
super();
//System.setSecurityManager(new RMISecurityManager());
}
public void init(){
//setLayout(new BorderLayout());
f=new JFrame(title);
f.setBounds(250,250,500,500);
f.setVisible(true);
JPanel pNorth,pSouth,pText;
pNorth=new JPanel();
pSouth=new JPanel();
pText=new JPanel();
inName=new JTextField(6);
contentIn=new JTextField(22);
area=new JTextArea(15,35);
connect=new JButton("connet");
disconnect=new JButton("Disconnect");
send=new JButton("send");
/////////////////////////////
pNorth.add(new JLabel("Enter your Name: "));
pNorth.add(inName);
pNorth.add(connect);
pNorth.add(disconnect);
pSouth.add(new JLabel("Enter Message: "));
pSouth.add(contentIn);
pSouth.add(send);
pText.add(new JScrollPane(area));
connect.addActionListener(this);
disconnect.addActionListener(this);
send.addActionListener(this);
contentIn.addActionListener(this);
f.add(pNorth,BorderLayout.NORTH);
f.add(pSouth,BorderLayout.SOUTH);
f.add(pText,BorderLayout.CENTER);
f.validate();
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
/*try{
UnicastRemoteObject.export(this);
}
catch(Exception e){
}*/
}
public static void main(String args[])throws RemoteException{
SwingChatClient client=new SwingChatClient();
client.init();
}
public void connect(){
try{
server=(IChatServer)Naming.lookup("rmi://localhost/IChatServer");
//此处localhost可改为服务端所在主机IP
System.out.println("Client Start!");
connected=true;
setStatus();
username=inName.getText().trim();
server.addClient(this,username+" Entered!");
}
catch(Exception e){
area.append("Connection fault!");
connected=false;
setStatus();
server=null;
System.out.println(e);
}
}
public void disconnect(){
try{
if(server==null)
return;
server.removeClient(this,username+" Has Quited!");
server=null;
}
catch(Exception e){
area.append("Connection Error!"+"/n");
int length = area.getText().length();
area.setCaretPosition(length);
}
finally{
connected=false;
setStatus();
}
}
public void actionPerformed(ActionEvent e){
if(e.getSource()==connect){
connect();
}
else if(e.getSource()==disconnect){
disconnect();
}
else if(e.getSource()==contentIn){
if(server==null)
return;
try{
server.sendMessage(this,contentIn.getText());
contentIn.setText("");
}
catch(Exception ee){
area.append("Connection Error!"+"\n");
int length = area.getText().length();
area.setCaretPosition(length);
}
}
else{
if (server==null)
return;
try{
server.sendMessage(this,contentIn.getText());
contentIn.setText("");
}
catch(Exception ex){
area.append("Connection Error!"+"\n");
int length = area.getText().length();
area.setCaretPosition(length);
}
}
}
public void setStatus(){
if(connected)
f.setTitle(title+"connected");
else
f.setTitle(title+"disconnected");
}
public String getName() throws RemoteException{
return username;
}
public void sendMessage(String msg) throws RemoteException{
area.append(msg+"\n");
int length = area.getText().length();
area.setCaretPosition(length);
}
}[/mw_shl_code]
看着挺恶心吧~这里面包含了客户端对于远程对象的调用,还有接口方法的实现和我们的SWING界面。
之后我们编译运行就大功告成了!
首先用javac命令编译刚才所有的 .java文件;
(这里建议大家编号程序之后的编译运行调试命令最好都写成一个个batch文件,对于之后debug比较方便…根据个人喜好不同,方法也不同=。=)
之后用rmic生成stub和skeleton文件;这步才是RMI的核心啊!~
rmic ChatServerImpl SwingChatClient
成功执行之后会发现有一些ChatServerImpl_Stub.class 类似这样有stub字样的class文件,如果服务端程序与客户端程序在同一台机器上并在同一目录中,则可以省略掉接口实现类生成的桩和框架文件,但这就失去了使用RMI的意义,而如果要在不同的JVM上运行时,客户端程序就必须得依靠服务端运程方法实现的桩和框架文件以及接口类。
接下来运行注册程序RMIRegistry,必须在包含刚写的类的目录下运行这个注册程序。
命令是start rmiregistry 同样推荐用bat文件执行比较方便。注册程序开始运行了,
不要关闭那个窗口哟,不然注册程序就被关闭了。
之后我们运行服务端!!
java -Djava.rmi.server.codebase=file:/你文件的绝对路径,注意用“/”(中间空格) ChatServer
Pause
最后我们运行编译好的服务端!
java -Djava.rmi.codebase=file:/你文件的绝对路径,注意用“/”(中间空格) SwingChatClient
Pause
到此,要是你的人品没什么问题应该已经看到这类似这样的情境:
要是throw了exception~不要紧~慢慢debug就好~~
做一个project的时候,刚编译好一个程序一遍运行通过可是很不容易的~
小东西自己做着玩玩还是很有趣的。
RMI配合好的服务器的话完全可以提供很多远程服务和不错的方法调用,非常灵活。
