###前言:
基于JAVA语言开发的一款网络聊天工具,通过Socket实现TCP编程,使用多线程实现了多客户端的连接。模仿腾讯QQ的界面,功能较为简单,但是使用了最基本的网络编程技术,如socket、tcp、I/O阻塞、多线程、MySQL数据库等。
###功能介绍:
用户注册:系统随机生成账号,用户按照要求填入相应的信息。
用户登录:用户根据自己的账号和密码进行登录。
好友聊天:登录成功后,点击好友列表中的好友进行聊天。
###注册模块
该界面主要给用户提供注册新账号的功能。注册账号时除了自动生成的账号和输入的密码外,还需要进行身份的验证,其中包括真实姓名、性别 、手机号。
客户端获取用户输入的信息后,将用户信息封装为User对象,将请求封装为CommandTranser对象,发送给服务器。代码如下:
User user = new User(username, password,realname,sex,phone);
CommandTranser msg = new CommandTranser();
msg.setCmd("checkregist");
msg.setData(user);
msg.setReceiver(username);
msg.setSender(username);
//实例化客户端 并且发送数据 这个client客户端 直到进程死亡 否则一直存在
Client client = new Client();
client.sendData(msg);
msg = client.getData();
在客户端对输入的信息进行了校验,不允许输入的信息为空,并给出相应的系统提示信息,通过客户端向服务器发送的注册请求,服务器进行数据库的校验,同样返回系统提示信息。
//注册时,查询是否已经注册该账号
public boolean checkregistUser(User user) {
PreparedStatement stmt = null;
Connection conn = null;
ResultSet rs = null;
conn = DBHelper.getConnection();
String sql = "select * from user where username=?";
try {
stmt = conn.prepareStatement(sql);
stmt.setString(1, user.getUsername());
rs = stmt.executeQuery();
if (rs.next()) {
return true;
}
}
return false;
}
###登录模块
点击了登录后,首先判断账号和密码是否为空,然后封装为CommandTranser对象,向服务器发送数据,服务器通过与数据库进行比对来验证账号密码。
客户端获取用户输入的信息以及发送请求的代码如下:
if (e.getSource() == login) {
String username = text_name.getText().trim();
String password = new String(text_pwd.getPassword()).trim();
if ("".equals(username) || username == null) {
JOptionPane.showMessageDialog(null, "请输入帐号!!");
return;
} if ("".equals(password) || password == null) {
JOptionPane.showMessageDialog(null, "请输入密码!!");
return;
}
User user = new User(username, password);
CommandTranser msg = new CommandTranser();//封装msg对象发送给服务器
msg.setCmd("login");msg.setData(user);
msg.setReceiver(username);msg.setSender(username);
// 实例化客户端,并且发送数据,这个client客户端直到进程死亡,否则一直存在
Client client = new Client();
client.sendData(msg);
msg = client.getData();
if (msg != null) {
if (msg.isFlag()) {
JOptionPane.showMessageDialog(null, "登陆成功!");
new FriendsUI(username, client); // 显示好友列表界面
} else {
JOptionPane.showMessageDialog(this, msg.getResult());
}
}
}
服务器Userservice类操作数据库对登录进行验证,如果账号和密码正确,则返回true,否则返回false。实现代码如下:
public boolean checkUser(User user) {
PreparedStatement stmt = null;
Connection conn = null;
ResultSet rs = null;
conn = DBHelper.getConnection();
String sql = "select * from user where username=? and password =?";
try {
stmt = conn.prepareStatement(sql);
stmt.setString(1, user.getUsername());
stmt.setString(2, user.getPassword());
rs = stmt.executeQuery();
if (rs.next()) {
return true;
}
}
return false;
}
如果登录成功,则将生成一个SocketThread对象,包括一个socket,一个用户账号,将该SocketThread加入到HashMap<String,Socket>
集合中,并且开启此用户的接收线程。代码如下:
private CommandTranser execute(CommandTranser msg) {
if ("login".equals(msg.getCmd())) { //登录请求
UserService userService = new UserService();
User user = (User) msg.getData();
msg.setFlag(userService.checkUser(user));
/*
* 如果登陆成功,将该客户端加入已经连接成功的map集合里面 并且开启此用户的接受线程
*/
if (msg.isFlag()) {
// 将该线程加入连接成功的map集合
SocketThread socketThread = new SocketThread();
socketThread.setName(msg.getSender());
socketThread.setSocket(socket);
SocketList.addSocket(socketThread);
msg.setResult("登陆成功");
} else {
msg.setResult("账号密码输入错误!");
}
}
HashMap<String,Socket>
用于记录所有已经成功登录的客户端:
public class SocketList {
private static HashMap<String, Socket> map=new HashMap<String, Socket>();
//将SocketThread入集合
public static void addSocket(SocketThread socketThread){
map.put(socketThread.getName(), socketThread.getSocket());
}
public static Socket getSocket(String name){ //通过昵称返回socket
return map.get(name);
}
}
一旦聊天功能启动,为每一个用户开启一个线程,通过I/O阻塞,client.getData()接收服务器发送的数据,如果服务器处理数据成功,接收消息,否则弹出该好友不在线的系统提示信息。
客户端获取消息内容、发送方和接收方,通过CommandTranser向服务器发送数据,代码如下:
public void actionPerformed(ActionEvent e)
if (e.getSource() == send_btn) {// 如果点击了发送按钮
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss a");
String message = "你说:" + message_txt.getText() + "\t"
+ sdf.format(date) + "\n";
chat_txt.append(message);// 在本地文本区追加发送的信息
CommandTranser msg = new CommandTranser();//msg为客户端向服务器发送的数据
msg.setCmd("message");
msg.setSender(owner);
msg.setReceiver(friend);
msg.setData(message_txt.getText());
client.sendData(msg);
message_txt.setText(null);// 发送信息完毕 写信息的文本框设空
}
}
服务器时刻监听客户端发送的消息,并调用CommandTranser execute(CommandTranser msg)方法传入message进行处理,如果处理成功,则可以向该好友发送消息,如果服务器处理信息失败,信息发送给自己,代码如下:
public void run() {
ObjectInputStream ois = null;
ObjectOutputStream oos = null;
while (socket != null) {// 时刻监听 客户端发送来的数据
try {
ois = new ObjectInputStream(socket.getInputStream());
CommandTranser msg = (CommandTranser) ois.readObject();
msg = execute(msg);
if ("message".equals(msg.getCmd())) {//服务器处理消息
if (msg.isFlag()) {
oos = new ObjectOutputStream(SocketList.getSocket(
msg.getReceiver()).getOutputStream());
} else {
oos = new ObjectOutputStream(socket.getOutputStream());
}
}
oos.writeObject(msg);
}
}
服务器对发送的消息进行处理,判断好友是否在线,然后将msg返回:
// 处理客户端发送的信息
private CommandTranser execute(CommandTranser msg) {
//如果是发送消息的指令,判断当前用户是否在线
if ("message".equals(msg.getCmd())) {
// 如果要发送的用户在线 发送信息
if (SocketList.getSocket(msg.getReceiver()) != null) {
msg.setFlag(true);
} else {
msg.setFlag(false);
msg.setResult("当前用户不在线");
}
}
return msg;
}
服务器将所有的操作做完后,客户端再通过getData()接收从服务器返回的数据,并在聊天界面中进行展示,代码如下:
public class ClientThread extends Thread {
private Client client;//客户端对象
public void run() {
while (isOnline) {
//I/O阻塞,接收服务端发送的数据
CommandTranser msg = client.getData();
if (msg != null) {
if (msg.isFlag()) {
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat(
"hh:mm:ss a");
String message = msg.getSender() + "说:"+ (String) msg.getData() + "\t" + sdf.format(date)+ "\n";
chat_txt.append(message);
} else {
JOptionPane.showMessageDialog(chat_txt, msg.getResult());
}
}
}
}
}
####注: 由于水平有限,做的比较low,项目全部代码以及文档请查看我的Github,如果喜欢,请给我点个Star。(❤ ω ❤)
项目全部代码以及文档介绍