基于Java ServerSocket类+Telnet实现简单网络聊天室
前言:前一篇博客已经介绍了如何利用Java提供ServerSocket类搭建本地服务器并实现简单的消息首发。现在呢主要来考虑一下登陆注册以及实现私聊这些功能,上一篇博客我们已经实现允许多个人同时登陆服务器来,由于笔者的失误,忘记测试多个人登陆服务器的情况了,不过本篇博客将会体现哦。为了使得我们的服务器更便于管理,共需要五个类:1.ChatServer.java:创建服务器并启动,等待连结,将进入的连结交给处理线程对象去处理;
2.ServerThread.java:处理 Socket 对象的线程类,每进入一个连结,就需要创建一个这个类的对象,在其 run 方法中负责与对应客户央的处理逻辑;从服务器的角度看:一个 ServerThread 对象,就 对应了一个客户端;
3.ChatTools.java:服务器端的的辅助类,负责将某个客户机发来的消息转发给其它的客户机;在 其中就必须保持一个队列对象;队列中存放了服务器生成的每一个 ServerThread 对象;
4.DaoTools.java:数据访问和验证类,负责生成模拟数据,并验证客户端帐号;
5.UserInfo.java:用户数据模型类,每个 UserInfo 类对象保存一个用户的帐号,身份信息;
本篇博客构成:
一、如何实现ChatTools.java(群聊、私聊功能)
二、如何实现DaoTools.java(登陆、注册、账号验证)
三、如何实现UserInfo.java(用户信息)
由于上一篇博客已经介绍了ChatServer和ServerThread(把那两个类换个名字就可以了2333),这里就不再赘述了。
ok进入正题!
一、实现ChatTools
ChatTools 类:可以理解为服务器端的”连结管理类”,服务器每进入一个客户机,就生成一个 Socket 对象,由于从 Socket 对象上读取数据是阻塞式的,所以必须放到一个线程对象中处理; 在本例中,是放到一个 ServerThread 类对象中处理;
ChatTools 类提供了管理连结处理线程对象的方法:向队列中加入一个连结处理线程对象; 将消息通过所管理队列中的连结处理线程对象发送给其所代码的客户端。此类负责保存每一个客户端对应的处理线程对象,即 ServerThread 对象,当某个ServerThread 对象收到消息时,还会调用其中的方法将消息通过其它的 ServerThread 对象发
送给每个客户机。因此
代码如下:
package com.version2.Chet1007;
import java.util.ArrayList;
import java.util.List;
public class ChatTools {
private ChatTools(){};
private static List<ServerThread>sList = new ArrayList();//存储多线程中的每个用户
public static void addClient(ServerThread ct)
{
cstMsg(ct.getOwnUser(),"我上线啦!聊天室目前在线人数:"+sList.size());
sList.add(ct);//新上线的用户加入到list中去,下线则从list删除。
}
public static void cstMsg(UserInfo sender,String msg)
{
msg = sender.getName()+"˵:"+msg;
for(int i=0;i<sList.size();i++)
{
ServerThread st = sList.get(i);
st.sendMsg2Me(msg);//此处是群聊功能的体现,遍历所有的客户端,然后向每一个客户端发送消息
}
}
}
私聊由于客户端没写出来,不太好操作,不过在cmd这个小黑框实现也很简单。首先输出功能列表,让用户选择群聊还是私聊,选择私聊后打印出所有在线用户的账号,让他选择其中的一个进行消息的发送即可,这里就不贴代码咯。
二、DaoTools类:
服务器端必须对用户信息进行验证,或保存用户的信息,将这些方法调用实现在一 个独立的类中;是因为这些方法都是在共同的性质:对用户数据进行增,删,查,改,即 CRUD 操 作;这样的一个类命名时,通常以 Dao 为前缀,即 Data Access Object—数据访问对象类, DaoTools 类中代码如下:
package com.version2.Chet1007;
import java.util.HashMap;
import java.util.Map;
public class Daotools {
public static boolean checkLogin(UserInfo user)
{
if(userDB.containsKey(user.getName()))//如果在数据库中存在该用户
{
return true;
}
//否则验证不通过,返回false
System.out.println("验证失败!"+user.getName());
return false;
}
private static Map<String,UserInfo>userDB = new HashMap();//用哈希表对用户的信息进行存储,使用静态块的原因是程序每次启动,这段代码都会自动执行,而不是运行到该类的时候才执行。
static
{
for(int i=0;i<10;i++)
{
UserInfo user = new UserInfo();//系统默认的用户信息
user.setName("user"+i);
user.setPsw("user"+i);
userDB.put(user.getName(), user);
}
}
}
还是受到黑框的影响,注册功能就暂时不实现了。但是要实现也很简单,登入服务器时首先打印出注册和登录两个功能,用户选择注册后,逐步输出填写账号—>填写密码---->再次确认密码。然后跳转到登陆即可。
三、UserInfo类
这个类就很简单啦!服务器端必须有一个用户类----每个客户机连结上服务器后,输入了服务器需要 的用户名,密码等用户属性,就在服务器端生成一个:用户对象”;最简单的就是 UserInfo 类了, 这个类的每个对象代表一个己登陆进服务器的用户的信息。
代码如下:
package com.version2.Chet1007;
public class UserInfo {
private String name;//用户名
private String psw;//密码
private String logTime;
private String address;//ip地址
public void setName(String name)
{
this.name = name;
}
public void setPsw(String psw)
{
this.psw = psw;
}
public void setLogTimie(String logTime)
{
this.logTime = logTime;
}
public void setAdress(String address)
{
this.address = address;
}
public String getName()
{
return name;
}
public String getPsw()
{
return psw;
}
public String getLogTime()
{
return logTime;
}
public String getAdress()
{
return address;
}
}
由于用户数据是私有性质的,因此通过get和set方法对其进行访问比较好。
最后贴一下ServerThread 和 ChatServer的代码吧,做了一点小改动而已。
package com.version2.Chet1007;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
public class ChatServer {
private ServerSocket serverSocket;
private Socket conn;
List<Socket>list;
public void talk(int port)
{
try {
serverSocket = new ServerSocket(port);
System.out.println("创建服务器成功!端口:"+port);
while(true)
{
Socket conn = serverSocket.accept();//让服务器进入等待状态
ServerThread handler = new ServerThread(conn);
System.out.println("登入一个用户,ip地址为:"+conn.getRemoteSocketAddress().toString());//
handler.start();//启动线程
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] argv)
{
ChatServer cs = new ChatServer();
cs.talk(9090);
}
}
package com.version2.Chet1007;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
public class ServerThread extends Thread{
private Socket client;
private OutputStream ous;
private UserInfo user;
public ServerThread(Socket client)//将socket对象从ChatServer类传递过来
{
this.client = client;
}
public UserInfo getOwnUser()
{
return this.user;
}
public void sendMsg2Me(String msg)//向客户端发送消息的方法。
{
try {
msg+="\r\n";
ous.write(msg.getBytes());//写入消息
ous.flush();
} catch (Exception e) {
// TODO: handle exception
}
}
public void closeMe()
{
try {
client.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public void processSocket()//读取客户端向服务器发送的消息
{
try {
InputStream ins =client.getInputStream();
ous = client.getOutputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(ins));
sendMsg2Me("Welcome to the Net Chet Room,please Enter your username :\r\n");
String userName = reader.readLine();
sendMsg2Me("Please Enter your passw:\r\n");
String pwd = reader.readLine();
user = new UserInfo();
user.setName(userName);//保存当前登入用户的信息
user.setPsw(pwd);
boolean loginState = Daotools.checkLogin(user);
if(!loginState)//如果该用户不存在,则关闭线程
{
this.closeMe();
return ;
}
//认证成功
else
System.out.println("认证成功!开始与朋友们聊天吧!!");
ChatTools.addClient(this);//将该用户加入到服务器队列,该队列保存着当前在线的用户
String input = reader.readLine();//一直保持读取用户输入的信息(按行读取)
while(!"bye".equals(input))//
{
System.out.println("服务器收到的信息:"+input);
//发送给其它的客户端
ChatTools.cstMsg(user, input);
input = reader.readLine();//读取下一条客户端向服务器发送的信息
}
} catch (Exception e) {
// TODO: handle exception
}
ChatTools.cstMsg(user, "用户已下线。");
this.closeMe();
}
@Override
public void run() {
processSocket();
}
}
代码的注释里面都说得很清楚了。另外本人现在已经开始写客户端了。贴几张图给个预告,关注我的博客~手把手教你写Java版QQ!一个人写代码工程量虽然大,但是很享受那个过程。
登陆界面:
注册界面:
主界面现在正在写!!估计两个月左右搞完。加油加油!
有不懂的地方欢迎在我的博客下方留言哦!