本例开发实现了一个多用户的P2P在线聊天程序,C/S结构,客户端可发送消息,然后其他的用户接收到该消息并将其显示在界面中,服务器对信息进行有关处理并向适当的用户发送,同时在服务器端还将显示所有的在线用户,网络管理员即服务器端管理员可选中某用户并将其踢出该聊天室。
本篇为第二篇,如果您是第一次进来请从第一篇开始,此程序不用修改完全可用,第一篇地址:http://java161.iteye.com/blog/616113
3 编写监听用户信息类(UserThread.java)
在监听用户信息类中,首先需要实例化一个Socket输入流来读取从客户端发送过来的信息,然后根据该信息进行相应的处理:
protected DataInputStream inStream;
inStream=new DataInputStream(connSock.getInputStream());
其中connSock为Socket连接的实例。
同时该类还应该得到该用户所在的IP地址及该用户的登陆用户名等信息,从而使网络管理员可以很方便地对聊天室中的该用户进行管理。得到客户端IP的程序如下所示:
String userIP="";
InetAddress inet=connSock.getInetAddress();
userIP=inet.getHostAddress();
然后该类可以从输入流中读取有关数据:
String str=inStream.readUTF();
得到客户的输入数据后则可以对该数据进行分析,在本次开发中利用“:%:”作为分隔符,对得到的字符串进行分析:
StringTokenizer st=new StringTokenizer(str,":%:");
String key=st.nextToken();
if(key.equals("login"))
{
userName=st.nextToken();
server.list1.addItem(userIP+"---"+userName);
}
else if(key.equals("quit"))
{
server.list1.remove(userIP+"---"+userName);
server.removeServerConn(userIP,userName);
stop();
}
else
server.tellEveryone(str);
对上面的程序进行分析,用户监听线程得到从客户端传送过来的数据,然后利用StringTokenizer类进行分析得到数据的主关键字key为“login”表示该客户为新登陆用户,则继续得到该信息中所包含的用户登录名,同时将该用户的IP地址和其用户名同时显示到主窗口中,其中Server为主窗口对象的实例句柄,list1为主窗口中的一个列表框,它用来记录当前所有的客户用户名和IP地址。
若 key 为“quit”表示客户端用户退出的通知,则需要在主界面中将该用户删除,同时将该用户的实例从矢量表中删除,调用主窗口类中的removeServerConn()方法实现。该方法实现如下所示:
void removeServerConn(String ip,String userName)
{
for(int i=0;i<=users.size();i++)
{
ServerConn conn=(ServerConn)users.elementAt(i);
String ip1=conn.reader.userIP;
String userName1=conn.reader.userName;
if(ip.equals(ip1)&&userName.equals(userName1))
users.removeElement(conn);
}
}
users为在主窗体对象中定义的一个矢量表,用来存放一共有多少个客户实例,它存储的是ServerConn对象。
若key是客户端所发送过来的信息,需要将其发送给该聊天室中的其他所有成员,则需要调用主函数中的tellEveryone()函数:
void tellEveryone(String str)
{
for(int i=0;i<=users.size();i++)
{
ServerConn conn=(ServerConn)users.elementAt(i);
conn.sendString(str);
}
}
tellEveryone()函数将逐项得到当前聊天室中所有的客户实例,然后分别向它们发送指定的信息。
在这用到了一个很重要的类:StringTokenizer 类。
StringTokenizer 类可以将指定的字符串分解成为标识,它依靠一系列的分隔符来识别标识。它的构造函数有以下三种:
l public StringTokenizer(Sring str);
l public StringTokenizer(Sring str,String delim);
l public StringTokenizer(Sring str,String delim,boolean returnTokens);
其中参数:
l Sring str—————————需要分解的字符串;
l String delim———————指定的分隔符字符串;
l boolean returnTokens——指定返回时是否包含指定的分隔符, returnTokens为true,返回字符串时将包含该分隔符,
returnTokens为false,返回字符串时将忽略该分隔符。
下面列出StringTokenizer 类包含的所有方法:
? countTokens()—————————返回还有多少可用的标识
? hasMoreElements()———————测试在使用nextToken()前是否还有标识
? hasMoreTokens()————————同hasMoreElements()
? nextElement()—————————返回字符串中下一个标识
? nextToken()——————————同nextElement()
? nextToken(String newDelim)—返回字符串的下一个标识,但重新指定分隔符
程序清单
package Server;
import java.awt.*;
import java.util.*;
import java.net.*;
import java.io.*;
import java.lang.*;
/**
* <p>Title: </p>
*
* <p>Description: </p>
*
* <p>Copyright: Copyright (c) 2010</p>
*
* <p>Company: </p>
*
* @author www.jianfei5u.com
* @version 1.0
*/
public class UserThread extends Thread {
protected Socket connectionSocket;
protected DataInputStream inStream;
SimpleServer server;
String userIP = "";
String userName = "";
public UserThread(SimpleServer server, Socket connSock) throws IOException {
InetAddress inet = connSock.getInetAddress();
userIP = inet.getHostAddress();
connectionSocket = connSock;
this.server = server;
inStream = new DataInputStream(connSock.getInputStream());
}
protected void closeConnection() {
try {
connectionSocket.close();
} catch (Exception oops) {
}
stop();
}
public void run() {
while (true) {
try {
String str = inStream.readUTF();
StringTokenizer st = new StringTokenizer(str, ":%:");
String key = st.nextToken();
if (key.equals("login")) {
userName = st.nextToken();
server.list1.addItem(userIP + "---" + userName);
} else if (key.equals("quit")) {
server.list1.remove(userIP + "---" + userName);
server.removeServerConn(userIP, userName);
stop();
} else
server.tellEveryone(str);
} catch (Exception oops) {
}//end of try
}//end of while
}//end of public
}//end of class
未完待续。。。下篇
http://java161.iteye.com/blog/616657
Client端 编写客户端Applet类