分享
 
 
 

用RMI构建聊天应用程序

王朝java/jsp·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

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

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有