用RMI构建聊天应用程序

类别:Java 点击:0 评论:0 推荐:

用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!输入您的名字,就可以聊侃了,祝贺你!

 

本文地址:http://com.8s8s.com/it/it12180.htm