您的位置:首页 > 其它

用RMI构建聊天应用程序

2004-12-29 14:47 274 查看
用RMI构建聊天应用程序:
其基本思想是:多个客户通过APLLET进行聊天,客户的聊天内容分别显示在各自的 TextArea 内,要做到这些,需要做到:
1、客户首先向服务器注册,告知服务器它在监听某主题;
2、客户注册之后,向服务器发送消息;
3、服务器再把消息发送给所有监听此主题的客户;

需要的文件有:

Chat.java: 客户端远程接口.

ChatImpl.java: 实现聊天远程接口的APPLET.

ChatServer.java: 服务器端远程接口.

ChatServerImpl.java:实现聊天服务器端远程接口的应用程序.

Message.java: 一个消息对象.

ServerTalker.java: 缓冲客户和服务器通信的线程.

Talker.java: 缓冲服务器和客户通信的线程.

NameDialog.java: 输入客户名字的对话框.

文件包结构:
(除NameDialog.java放到$home/java/examples/util里外,其余的都放到$home/java/examples/chat)
· $home/java/examples/chat
· $home/java/examples/util
用RMI构建聊天应用程序实现过程的三个步骤:

· 定义远程接口.
· 服务器类聊天服务器的实现.
· 服务器类客户端的实现.
一、定义远程接口
1、定义聊天服务器远程接口:
package examples.chat;
import java.rmi.*;
public interface ChatServer extends Remote
{
// register chatter with server
public void register(Chat c, String name)
throws RemoteException;

// unregister chatter with server
public void unregister(String name)
throws RemoteException;

// post messages to the server for broadcast
public void postMessage(Message m)
throws RemoteException;

// list chatter names currently logged into server
public String[] listChatters()
throws RemoteException;
}

2
、定义客户端远程接口:
package examples.chat;
import java.rmi.*;
public interface Chat extends Remote
{
public void chatNotify(Message m)
throws RemoteException;
}

二、服务器类聊天服务器的实现:
1、声明实现远程接口
2、定义远程对象的构造函数
3、实现能远程调用的方法
4、创建一个远程对象的实例并注册

package examples.chat;
import java.rmi.*;
import java.rmi.server.*;
import java.rmi.registry.*;
import java.util.*;
public class ChatServerImpl
extends UnicastRemoteObject
implements ChatServer
{
private Vector chatters = new Vector();
public ChatServerImpl() throws RemoteException
{
System.out.println("Initializing Server.");
}
public static void main(String args[])
{
Registry reg;
//Set security manager to allow stub loading over the network
System.setSecurityManager(new RMISecurityManager());
try {
ChatServerImpl cs = new ChatServerImpl();
//create registry running on port 5050
reg = LocateRegistry.createRegistry(5050); // CREATE REGISTRY
//bind cs in registry
reg.bind("ChatServerImpl", cs);
System.out.println("Server Ready.");
} catch (AlreadyBoundException e) {
System.out.println("Name is already bound: " + e);
System.exit(0);
} catch (RemoteException e) {
System.out.println("General Server Error: " + e);
System.exit(0);
}
}
synchronized public void register(Chat c, String name)
{
chatters.addElement(new Talker(c, name));
}
synchronized public void unregister(String name)
{
Talker c;
for (int i = 0; i < chatters.size(); i++)
{
c = (Talker) chatters.elementAt(i);
if (name.equals(c.getChatterName()))
{
chatters.removeElementAt(i);
return;
}
}
}
public String[] listChatters()
{
String list[] = new String[chatters.size()];
Talker c;
for (int i = 0; i < list.length; i++)
{
c = (Talker) chatters.elementAt(i);
list[i] = c.getChatterName();
}
return list;
}
synchronized public void postMessage(Message m)
{
Talker t;
for (int i = 0; i < chatters.size(); i++)
{
t = (Talker) chatters.elementAt(i);
if (!t.addMessage(m))
//remove Talker, if add failed
chatters.removeElementAt(i);
}
}
}

下面是Talker ,是个线程类, ChatServerImpl.java 使用它来实现消息异步通信
package examples.chat;
import java.util.*;
import java.rmi.*;
public class Talker extends Thread
{
private Vector messages = new Vector();
private Chat c;
boolean isActive = true;
private String name;
public Talker(Chat C, String N)
{
c = C;
name = N;
start();
}
public boolean addMessage(Message e)
{
if (!isActive)
return false;
synchronized (messages)
{
messages.addElement(e);
}
resume();
return true;
}
public void run()
{
while (true)
{
try {
if (messages.isEmpty())
suspend();
synchronized (messages)
{
c.chatNotify((Message) messages.elementAt(0));
messages.removeElementAt(0);
}
} catch (RemoteException e) {
//connection down; kill thread
System.out.println("Removing " + name);
isActive = false; // why is this necessary?
stop();
}
yield(); // let other threads compete for resources
}
}
public String getChatterName() { return name; }
}

下面是Message类,必须实现序列化
package examples.chat;
import java.io.*;
public class Message
implements Serializable
{
private String sender;
private String message;
public Message(String sender, String message)
{
this.sender = sender;
this.message = message;
}
public String getSender() { return sender; }
public String getMessage() { return message; }
}

三、服务器类客户端的实现

这是客户端类,同时也是SERVER类
package examples.chat;
import java.rmi.*;
import java.rmi.server.*;
import java.net.*;
import java.awt.*;
import java.util.*;
import java.applet.*;
import java.awt.event.*; // ActionListener interface
import examples.util.*; // For NameDialog class
public class ChatImpl
extends Applet
implements Chat, ActionListener
{
private TextArea ta; //the main text window
private TextField tf; //message input area
private ChatServer cs; //reference to Chat method server
private String name; //User's name
private NameDialog nd; //pop-up to request user name
private ServerTalker st; //Thread for handling message sending

public ChatImpl() throws RemoteException { System.out.println("Starting up Chatter."); }
public void init()
{
//set up applet's layout manager
this.setLayout(new BorderLayout());

// create Panel for Buttons
Panel p = new Panel();
p.setLayout(new FlowLayout()); // set its layout manager

//add buttons to panel
Button dc = new Button("Disconnect"), //disconnect from server
lt = new Button("List"), //list users
ct = new Button("ClearText"); //clear TextField
dc.setBackground(Color.pink); //for dramatic effect
p.add(lt);
p.add(ct);
p.add(dc);

//create text widgets & drawing window
ta = new TextArea(4, 40); //message window
ta.setEditable(false); //read-only window
tf = new TextField(40); // text entry field

//add widgets to applet
add(ta, "Center");
add(tf, "South");
add(p, "North"); //add button panel
//register applet as listener for widget actions
lt.addActionListener(this);
ct.addActionListener(this);
dc.addActionListener(this);
tf.addActionListener(this);

//create dialog box for user name
nd = new NameDialog( new Frame("Enter Name"),
"Enter your name", false);
registerChatter(); //register the applet with server
}
public void registerChatter()
{
name = nd.getName(); //get name from NameDialog
nd.setVisible(false); //get rid of NameDialog
nd = null;
try { //export our remote methods UnicastRemoteObject.exportObject(this);
//lookup the server's remote object cs = (ChatServer) Naming.lookup( "rmi://lysander.cs.ucsb.edu:5050/ChatServerImpl" ); //register applet with server cs.register(this, name); //start a communication thread st = new ServerTalker(cs, name); } catch (RemoteException e) { System.out.println("Couldn't locate registry."); System.exit(0); } catch (MalformedURLException e) { System.out.println("Bad binding URL: " + e); System.exit(0); } catch (NotBoundException e) { System.out.println("Service not bound."); System.exit(0); } }
public void actionPerformed(ActionEvent e)
{
String s = tf.getText().trim();
if (!s.equals("")) //message entered?
{
if (!st.addMessage(new Message(name, s))) //failed?
ta.append("***Server Error***/n");

tf.setText("");
}
else if (e.getActionCommand().equals("ClearText"))
ta.setText("");
else if (e.getActionCommand().equals("Disconnect"))
{
st.addMessage(new
Message("*** " + name, "Logged off. Bye."));
try {
cs.unregister(name);
} catch (RemoteException x) {
System.out.println(name +
"'s unregister failed:" + x);
System.exit(0);
}
cs = null;
System.exit(0);
}
else if (e.getActionCommand().equals("List"))
getUserList();
}
public void getUserList()
{
String users[] = null;

try {
users = cs.listChatters();
} catch (RemoteException e) {
System.out.println(e);
users = new String[1];
users[0] = "***Error";
}
//add user names to TextArea
for (int i = 0; i < users.length; i++)
ta.append("***" + users[i] + "/n");
}
public synchronized void chatNotify(Message m) throws RemoteException { ta.append(m.getSender() + ": " + m.getMessage() + "/n"); }
}

这是ServerTalker 线程类,负责把消息(messages)发送到聊天服务器:
package examples.chat;
import java.util.*;
import java.rmi.*;

class ServerTalker
extends Thread
{
private Vector messages = new Vector();
private ChatServer cs;

public ServerTalker(ChatServer cs, String name)
{
this.cs = cs;
//Send a welcome message
messages.addElement(new Message("SYSTEM", "Connected " + name));
this.start();
}
public boolean addMessage(Message e)
{
if (cs == null)
{
System.out.println("Server reference is null.");
return false;
}
resume(); // resume thread, if suspended
messages.addElement(e);
return true;
}
public void run()
{
while (true)
{
try {
if (messages.isEmpty())
suspend();
cs.postMessage((Message) messages.elementAt(0));
messages.removeElementAt(0);
} catch (RemoteException e) {
System.out.println("Error: Server down? " + e);
cs = null;
this.stop();
}
yield();
}
}
}

最后,NameDialog工具类,处理事件,这是个比较过时的APPLET,有兴趣的朋友可以改写一下:
package examples.util;
import java.awt.*;

public class NameDialog
extends Dialog
{
private TextField tf = new TextField(20);
private String value;

public NameDialog(Frame p, String t, boolean modal)
{
super(p, t, modal);
setLayout(new FlowLayout());
this.add(new Label("Enter your name:"));
this.add(tf);
this.add(new Button("OK"));
this.pack();
this.show();
}
public boolean handleEvent(Event e)
{
if (e.target instanceof Button)
{
if (tf.getText().length() > 1)
value = tf.getText().trim();
return true;
}
return false;
}
public String getName()
{
while (value == null)
try {
Thread.sleep(1);
} catch (InterruptedException exception)
{
System.err.println("Exception: " + exception.toString());
}
return value;
}
}

四、编译和部署:
1、
用javac编译源文件,注意你必须在类路径下编译:
javac -d $HOME/class *.java
2
用 rmic 产生skeletons and stubs,skeletons and stubs封装了客户和服务器的通讯细节:

rmic -d $HOME/class examples.chat.ChatImpl examples.chat.ChatServerImpl

3、启动服务器和客户端:
·创建HTML文件:

<html>
<applet code="examples.chat.ChatImpl.class" width=800 height=400 >
</applet>
</html>
·启动服务器
windows下: java examples.chat.ChatServerImpl
·运行客户端
一旦启动了服务器,就可以运行客户端,在 $HOME/class/examples/chat:

appletiewer ChatApplet.html
·OK!输入您的名字,就可以聊侃了,祝贺你!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: