public void broadcast(String s) throws IOException //广播过程
{
Enumeration e=chatusers.keys();
while(e.hasMoreElements()) {
String key=(String)e.nextElement();
Connection c=(Connection)chatusers.get(key);
c.out.writeUTF(s);
c.out.flush();
}
}
}
四、客户端的Java Applet
当含有Applet的页面传送到客户的浏览器时,Applet被解释执行。Applet有一个参数(PARAM)为username,代表聊天用户的代号。当Applet初始化同服务器进行连接时,发送一条形式如“$username…”的消息,告诉服务器聊天用户的名字。聊天用户可以输入list命令来列出当前聊天室的用户(参见前面的Connection线程的代码)。客户端Applet启动了一个StreamListener的线程专门用于从服务器获取消息并显示在Applet的发言区。
虽然我们可以正确的传输与显示中文,但在Java Applet的Text类组件中不能输入中文的现象仍然存在,尤其是在IE浏览器上。这个问题产生的根源不是Java语言本身或者我们所编写的代码不正确,而完全是Microsoft VM for Java的Bug。对于使用IE的用户来说,在Web上的Java聊天室输入中文是麻烦的,需要通过拷贝/粘贴方式把汉字贴入Applet的TextField。对于这个问题,我们可以使用一个基本的技巧来解决。那就是不使用Java的TextField或TextArea类进行输入,而是依靠HTML页面中的表格(Form)的Text元素进行输入,然后把输入结果传递给Applet。传递的方法是在HTML页面里加入JavaScript程序,与该页面的Applet进行通讯。
对于客户端Applet来说,进行必要的资源清理工作尤为重要。在HTML页面的生存期内,Applet执行的顺序一般是:init()start()stop()destroy()。所以我们必须在Applet的stop()方法中进行资源清理工作,此外,当与聊天服务器连接中断(如服务器down机或因超时被服务器踢出时),也要进行相应的清理工作。这样,客户端的系统资源不会丢失(如内存泄漏leak),而且服务器端也能够迅速掌握客户端的状态。
客户端的主要代码段及解释如下:
⑴登录页面default.asp
该页面将登录用户的用户名动态的传递给Applet,这是通过插入ASP脚本[3]实现的。如果不用ASP,也可以使用JavaScript编写一个弹出的输入对话框让用户输入名字。
<HTML><HEAD><TITLE>聊天室</TITLE>
<script language="javascript">
function SendIt() //将Form的输入传递给Applet
{
document.AppletClient.SendToServer(document.CHATFORM.textField.value);
document.CHATFORM.textField.value="";
}
function ClearDisplay() //调用Applet的Clear过程清除发言显示区
{ document.AppletClient.Clear(); }
</script></HEAD><BODY>
<%
If trim(request("username"))="" then
'显示用户登录Form
%>
<form name="LOGINFORM" action="default.asp">
<font style="font-size:14">输入你的代号:</font>
<input type="text" name="username" size=20 style="font-size:14">
<input type="submit" value="进入聊天室" style="font-size:14">
</form>
<%
else '否则显示聊天Form
%>
<APPLET CODE="ChatClient.class" name=AppletClient WIDTH=500 HEIGHT=300>
<PARAM name="username" value="<%=request("username")%>">
</APPLET>
<form name="CHATFORM" onSubmit="SendIt();return false;">
<input type="text" name="textField" size=40" style="font-size:14">
<input type="button" value="发送" onClick="SendIt()" style="font-size:14">
<input type="button" value="清除显示区" onClick="ClearDisplay()" style="font-size:14">
</form>
<% end if%></BODY></HTML>
⑵Applet的定义及初始化
public class ChatClient extends Applet{
public final static int PORT=6543;
public Socket s=null;
protected DataInputStream in=null; // 读缓冲
protected DataOutputStream out=null; // 写缓冲
protected TextArea outputarea; // 显示发言的区域
protected StreamListener listener=null; //监听线程用于显示服务器发回的消息
protected String username; //聊天用户名
public void init() { //Applet的启动过程
super.init();
username=getParameter("username"); //获取用户名参数
outputarea=new TextArea();
outputarea.setEditable(false); //仅用于显示
this.setLayout(new BorderLayout());
this.add("Center",outputarea);
try{ //进行连接
//由于Applet的安全限制,只能同下载服务器进行连接
s=new Socket(this.getCodeBase().getHost(),PORT);
in=new DataInputStream(s.getInputStream());
out=new DataOutputStream(s.getOutputStream());
listener=new StreamListener(in,outputarea,this);
outputarea.appendText("连接到服务器
"+s.getInetAddress().getHostName()
+":"+s.getPort()+"\n");
outputarea.appendText("使用list命令列出当前聊天室的用户\n");
out.writeUTF("$username"+username); //登录用户名
}
catch(IOException e){
outputarea.appendText("错误:不能与聊天服务器进行连接\n");
}
}
⑵提供给JavaScript调用的过程
public void SendToServer(String s) {
//此过程供网页内的JavaScript程序调用,将发言s发送到服务器
try{
Date nowTime=new Date();
//将当前时间插入发言,时:分:秒
String chattime=nowTime.getHours()+":"+nowTime.getMinutes()+":"+nowTime.getSeconds();
out.writeUTF(s+" ("+chattime+")");
}
catch(IOException e){ outputarea.appendText("错误:不能发送消息到聊天服务器\n");}
}
public void Clear() //此过程供网页内的JavaScript程序调用,清除显示区
{ outputarea.setText(""); }
⑶线程中止Stop()过程
public void stop() {
//释放相关资源
if(listener!=null) { //中止输入流监听线程
listener.stop();
listener=null; }
try{ if(s!=null) s.close(); } //关闭Socket
catch(IOException e)
{ outputarea.appendText("错误:无法关闭Socket\n"); }
s=null;
outputarea.appendText("释放资源\n");
super.stop();
}}
⑷输入流监听线程StreamListener
class StreamListener extends Thread{
……
public void run(){
String line;
try
{
for(;;){
line=in.readUTF(); //读输入
if(line==null) break;
//将服务器发回信息添加到textarea显示区
output.appendText(line+"\n");
}
}
catch(IOException e){}
finally{ //清理工作
try{
if(app.s!=null) app.s.close(); //关闭socket
}catch(IOException e2)
{ output.appendText("错误:无法关闭Socket\n"); }
app.s=null;
output.appendText("与服务器连接断开,释放资源\n");
}}
}
五、试验结果及结论
整个Java聊天室系统的程序在Visual J++ 1.1下编译运行通过,服务器端安装NT Server 4.0和IIS 4.0,运行聊天服务器。客户端使用Netscape或IE浏览器进行连接。试验证明,该聊天室系统解决了在Java语言中的中文传输、显示及输入问题。该聊天室系统具有高效性、健壮性和灵活性,达到了预期的设计目标。读者可以在此基础上进一步增加功能,如建立私有聊天房间、用户间互送消息甚至将聊天室该为虚拟图形版等等,Java语言将会带给程序员更多的想像与发挥余地。
参考文献:
1 Introduction ,JDK 1.1.6 Online Document,Sun,1997
2 Java Language Specification,Visual J++ Online Help,Microsoft,1997 Active Server Pages帮助,Microsoft Developer Network,Microsoft,1998
|