1、项目基本描述:
1.1项目名称:
Java聊天工具
1.2开发工具:
相关技术:MySQL + Swing + Socket 开发工具:Eclipse
1.3项目描述:
该软件能快速的注册一个账号,简单几步就可以查找好友并添加好友,以及系统处理结果的合理的提示,发送软件自带的一套兔斯基的gif图片,震屏效果,和传送任何类型的文件,基本上符合了现在人们对于即时聊天的需求。
1.4项目技术要点:
Java IO的操作 Java基于TCP的网络编程 MySQL在项目中的使用 Java Swing界面的设计
2、项目功能说明:
3、项目工程
(1) 客户端程序文件说明:
类名 功能 FriendData 保存每个好友的数据类 FriendList 保存好友列表类 FriendMap 保存好友图类 PersonalData 保存自己的数据类 UserManager 管理数据库处理相关函数的类 ChatMainMap 管理主界面的图类 ChatWinMap 管理聊天窗口的图类 ClientConnServerThread 客户端连接到服务器的线程类 ClientThreadMap 管理客户端的线程类 FileClient 接收文件的客户端类 FileServer 发送文件的服务器类 LoginCheck 客户端登录检验类 ConnMySQL 建立连接数据库类 ChatLogin 登录界面类 ChatMain_center 主界面中间部分面板类 ChatMain 主界面类 ChatWindow_right 聊天窗口右侧面板类 ChatWindow 聊天窗口类 ConfirmRequestMessage 确认消息框 MessageBox 自定义普通消息框 Onli① 用户表ne_message 上线消息框 ReceiverFileTransMessage 接收文件确认消息框 ReceiverMessage 消息管理器类 ReceiverRequestMessage 好友请求显示框 Register 注册面板 TransportFile 发送文件的文件选择器 FileTranMes 发送文件携带信息类 Message 发送信息类 MessageType 信息类型 User 用户 ColorTools 自定义颜色工具 FontTools 自定义字体工具 ImagePanel 图片面板
(2) 服务器端程序文件说明:
类名 功能 ChatServer 服务器处理类 ServerConnClientThread 服务器接收线程类 ThreadMap 管理客户端连接到服务器的线程类 ServerWindow 服务器窗口类 Message 发送信息类 MessageType 信息类型 User 用户
(3) 数据库文件结构
① 用户表
+----------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | ±---------±------------±-----±----±--------±------+ | userId | int(11) | NO | PRI | NULL | | | username | varchar(50) | YES | | NULL | | | password | varchar(20) | YES | | NULL | | | picNo | varchar(10) | YES | | NULL | | ±---------±------------±-----±----±--------±------+
② 心情表
+-------------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | ±------------±-------------±-----±----±--------±------+ | moodId | int(11) | NO | PRI | NULL | | | moodcontent | varchar(280) | YES | | NULL | | | pid | int(11) | YES | | NULL | | | moodtime | varchar(20) | YES | | NULL | | ±------------±-------------±-----±----±--------±------+
③ 好友关系表
+------------+---------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | ±-----------±--------±-----±----±--------±------+ | relationid | int(11) | NO | PRI | NULL | | | pid | int(11) | YES | | NULL | | | friendid | int(11) | YES | | NULL | | ±-----------±--------±-----±----±--------±------+
④ 主键维护表
+------------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | ±-----------±------------±-----±----±--------±------+ | gen_name | varchar(20) | YES | | NULL | | | gen_number | int(11) | YES | | NULL | | ±-----------±------------±-----±----±--------±------+
4、项目运行截图:
5、关键代码:
(1) 数据库处理源代码:
/**
-
在数据库中查询用户的资料,保存在本地的PersonalData中
-
@return
*/
public void checkPersonalData(String id){
PersonalData pd = PersonalData.getInstance();
Connection conn = null;
ResultSet rs = null;
PreparedStatement pstmt = null;
String sql = “select * from user where userid=?”;
try {
conn = ConnMySQL.getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, id);
rs = pstmt.executeQuery();
if (rs.next()) {
pd.setUserId(rs.getString(“userId”));
pd.setUserName(rs.getString(“username”));
pd.setPicNo(rs.getString(“picNo”));} }catch(SQLException e) { e.printStackTrace(); }finally { ConnMySQL.close(rs); ConnMySQL.close(pstmt); ConnMySQL.close(conn); }
}
(2) 其他数据处理关键语句:
/**
-
在数据库中查询用户的资料,保存在本地的PersonalData中
-
@return
*/
String sql = “select * from user where userid=?”;/**
-
在数据库中查询最新的心情
-
@return
*/
String sql = “select moodcontent from moodlist where moodid = (select max(moodid)from moodlist where pid=?)”;/**
* 查找用户的所有好友,保存在本地程序中 -
@param id
-
@return
*/
String sql = “select * from user where userId in (select friendid from relationlist where pid=?)”;/**
* 修改用户的昵称 -
@return
*/
String sql = “update user set username=? where userId=?”;/**
-
修改用户的密码
* @return
*/
String sql = “update user set password=? where userId=?”;/**
-
获取自动编号ID
-
@param gen_name
-
@return
*/
String sql = “select gen_number from generator where gen_name =?”;/**
-
更新Id维护列表,此方法给此类别的函数调用
-
@param gen_name
* @param current_id -
@param conn
*/
String sql = “update generator set gen_number =? where gen_name = ?”;/**
* 添加心情 -
@param userId
-
@param mood
-
@return
*/
String sql = “insert into moodlist values(?,?,?,?);”;String sql = “select * from moodlist order by moodId desc limit 20”;
/**
-
加好友
-
@param args
*/
String sql = “insert into relationlist values(?,?,?);”;String sql = “insert into user values(?,?,?,?);”;
(3) 客户端接收线程的run()函数:
public void run() {
while(run1){
try {
ObjectInputStream ois = new ObjectInputStream(sk.getInputStream());
Message mes = (Message)ois.readObject();
String messageType = mes.getMessageType();
String sender = mes.getSender();
String receiver = mes.getReceiver();
if(messageType.equals(MessageType.CONTENT) || messageType.equals(MessageType.EXPRESSION)) {
// System.out.println(receiver);
ChatWindow cw = ChatWinMap.getChatWindow(sender);
// System.out.println(“来自用户”+cw);
if(null == cw){
if(null != FriendMap.getFriendData(mes.getSender())) {
ReceiverMessage rm = new ReceiverMessage(mes);
Thread th = new Thread(rm);
th.start();
}
} else {
cw.showMessage(mes);
}
} else if(messageType.equals(MessageType.ON_LINE)) {
System.out.println("更新在线好友");
String []all_online_ip = mes.getAll_online_id().split(",");
ChatMainMap.getChatMain(mes.getReceiver()).getJp_center().changeOnlineState(all_online_ip);
Online_message on_mes = new Online_message(mes.getSender());
Thread th = new Thread(on_mes);
th.start();
} else if(messageType.equals(MessageType.SHAKE_WIN)) {
ChatWindow cw = ChatWinMap.getChatWindow(sender);
Thread th = new Thread(cw);
th.start();
cw.showMessage(mes);
} else if(messageType.equals(MessageType.REQUEST)){
//把该好友的资料丛数据库中取出并保存在客户端程序中
ReceiverRequestMessage rsm = new ReceiverRequestMessage(mes);
Thread th = new Thread(rsm);
th.start();
} else if(messageType.equals(MessageType.REQUEST_CONFIRM)) {
//把双边的关系加入数据库
UserManager.getInstance().addFriend(mes.getSender(), mes.getReceiver());
System.out.println("保存好友:"+mes.getSender());
System.out.println(mes.getReceiver());
UserManager.getInstance().addFriend(mes.getReceiver(),mes.getSender());
//把该好友资料从数据库中取得并保存在客户端程序中
String []fds = UserManager.getInstance().checkUser(mes.getSender());
FriendData fd = new FriendData();
fd.setFriendId(fds[0]);
fd.setFriendName(fds[1]);
fd.setFriendPic(fds[2]);
FriendList.getFriendList().add(fd);
FriendMap.addFriendData(fds[0], fd);
//更新好友列表
String all_ip = mes.getAll_online_id();
ChatMainMap.getChatMain(PersonalData.getUserId()).updataCenter(all_ip);
ConfirmRequestMessage rm = new ConfirmRequestMessage(mes);
Thread th = new Thread(rm);
th.start();
} else if(messageType.equals(MessageType.REQUEST_REFUSE)){
ConfirmRequestMessage rm = new ConfirmRequestMessage(mes);
Thread th = new Thread(rm);
th.start();
} else if(messageType.equals(MessageType.NOT_ONLINE)) {
MessageBox msb = new MessageBox("对方不在线");
Thread th = new Thread(msb);
th.start();
} else if(messageType.equals(MessageType.OFF_LINE)) {
System.out.println("有人下线了");
//更新好友列表
if(sender.equals(PersonalData.getUserId())) {
break;
}
String all_ip = mes.getAll_online_id();
System.out.println("当前在线好友:"+mes.getAll_online_id());
ChatMainMap.getChatMain(PersonalData.getUserId()).updataCenter(all_ip);
} else if(messageType.equals(MessageType.FILE_TRAN)) {
ReceiverFileTransMessage rftm = new ReceiverFileTransMessage(mes);
Thread th = new Thread(rftm);
th.start();
} else if(messageType.equals(MessageType.FILE_TRAN_CONFIRM)) {
ConfirmRequestMessage rm = new ConfirmRequestMessage(mes);
Thread th = new Thread(rm);
th.start();
} else if(messageType.equals(MessageType.FILE_TRAN_REFUSE)) {
ConfirmRequestMessage rm = new ConfirmRequestMessage(mes);
Thread th = new Thread(rm);
th.start();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
(4) 接收文件的客户端run()函数
public void run() {
Socket server;
try {
server = new Socket(msg.getFileTranMes().getServerIp(), Integer.parseInt(msg.getFileTranMes().getServerPort()));
//创建网络接受流接受服务器文件数据
InputStream netIn = server.getInputStream();
InputStream in = new DataInputStream(new BufferedInputStream(netIn));
//创建缓冲区缓冲网络数据
// System.out.println(“传输文件的长度:”+in.available());
byte[] buf = new byte[2048];
int num = in.read(buf);
while (num != (-1)) {
//是否读完所有数据
raf.write(buf, 0, num);//将数据写往文件
raf.skipBytes(num);//顺序写文件字节
num = in.read(buf);//继续从网络中读取文件
width = width+1;
width_1 = width/size * 180;
jl2.setBounds(10,40,(int)(width_1),20);
jl2.setText(" “+(int)(width/size*100)+”%");
}
jl2.setText(“传送完成”);
in.close();
raf.close();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
(5) 发送文件的服务器run()函数
public void run() {
//创建网络服务器接受客户请求
ServerSocket ss;
Socket client = null;
try {
ss = new ServerSocket(5678);
client = ss.accept();
} catch (IOException e) {
// TODO Auto-generated catch block
// e.printStackTrace();
}
//创建网络输出流并提供数据包装器
OutputStream netOut;
try {
netOut = client.getOutputStream();
OutputStream doc = new DataOutputStream(
new BufferedOutputStream(netOut));
//创建文件读取缓冲区
byte[] buf = new byte[2048];
int num = fos.read(buf);
while (num != (-1)) {//是否读完文件
doc.write(buf, 0, num);//把文件数据写出网络缓冲区
doc.flush();//刷新缓冲区把数据写往客户端
num = fos.read(buf);//继续从文件中读取数据
width = width+1;
width_1 = width/size * 180;
jl2.setBounds(10,40,(int)(width_1),20);
jl2.setText(" "+(int)(width/size*100)+"%");
}
jl2.setText("传送完成");
fos.close();
doc.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
(6) 客户端登录验证的函数
public boolean CheckUser (Message msg) {
boolean check = false;
try {
sk = new Socket(ip,port);
System.out.println(“获取socket”);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
ObjectOutputStream oos = new ObjectOutputStream(sk.getOutputStream());
oos.writeObject(msg);
ObjectInputStream ois=new ObjectInputStream(sk.getInputStream());
Message mss=(Message)ois.readObject();
if(mss.getMessageType().equals(MessageType.ON_LINE)){
ClientConnServerThread ccst = new ClientConnServerThread(sk);
System.out.println("赋给ClietThreadMap的id:" \+ mss.getUser().getUserId());
ClientThreadMap.setClientThreadHm(mss.getUser().getUserId(), ccst);
ccst.start();
User u = mss.getUser();
//在这里查询数据库中用户的信息,并保存在客户端程序中
UserManager.getInstance().checkPersonalData(u.getUserId());
PersonalData.setMood(UserManager.getInstance().checkMood(u.getUserId()));
UserManager.getInstance().checkAllFriend(u.getUserId());
ChatMain cm = new ChatMain(u,mss);
ChatMainMap.addChatMain(u.getUserId(), cm);
check = true;
} else if(mss.getMessageType().equals(MessageType.ON_LINE_FAIL)) {
// JOptionPane.showMessageDialog(null, “用户名或密码错误”);
MessageBox msgb = new MessageBox(“用户名或密码错误!”);
Thread th = new Thread(msgb);
th.start();
ois.close();
oos.close();
sk.close();
}
} catch (Exception e) {
e.printStackTrace();
}
return check;
}
(7) 连接数据库的函数
public static Connection getConnection() {
Connection conn = null;
try {
Class.forName(“com.mysql.jdbc.Driver”);
conn = DriverManager.getConnection(“jdbc:mysql://localhost/chatdata?user=user1&password=m123”);
} catch (Exception ex) {
ex.printStackTrace();
}
return conn;
}
(8) 发送消息的响应事件:
if(e.getSource() == jb_send) {
if(cardNo == 0) {
Date time = new Date();
SimpleDateFormat sdf = new SimpleDateFormat(“hh:mm:ss”);
String sendtime = sdf.format(time);
Document doc = jta.getDocument();
try {
doc.insertString(jta.getDocument().getLength(),PersonalData.getUserName()+“(”+this.userId+“) “+sendtime+”\n”, null);
doc.insertString(jta.getDocument().getLength()," “+jta_send.getText()+”\n", null);
} catch (BadLocationException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
String sendMes = jta_send.getText();
jta_send.setText("");
Message mes = new Message();
mes.setSender(this.userId);
mes.setReceiver(this.receiveId);
// System.out.println(“发送前receiverId的值:”+receiveId);
mes.setContent(sendMes);
mes.setSendtime(sendtime);
mes.setPic_no(PersonalData.getPicNo());
mes.setMessageType(MessageType.CONTENT);
try {
ObjectOutputStream oos = new ObjectOutputStream(ClientThreadMap.getClientThreadHm(this.userId).getSocket().getOutputStream());
oos.writeObject(mes);
} catch (Exception e1) {
e1.printStackTrace();
}
} else {
Date time = new Date();
SimpleDateFormat sdf = new SimpleDateFormat(“MM-DD hh:mm:ss”);
String sendtime = sdf.format(time);
Document doc = jta.getDocument();
try {
doc.insertString(jta.getDocument().getLength(),PersonalData.getUserName()+“(”+this.userId+“) “+sendtime+”\n”, null);
doc.insertString(jta.getDocument().getLength(), " “, null);
jta.setCaretPosition(doc.getLength());
jta.insertIcon(new ImageIcon(“images/”+faceNo+”.gif"));
doc.insertString(jta.getDocument().getLength(), “\n”, null);
} catch (BadLocationException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
System.out.println("send picture:"+faceNo);
String sendMes = faceNo;
Message mes = new Message();
mes.setSender(this.userId);
mes.setReceiver(this.receiveId);
mes.setContent(sendMes);
mes.setSendtime(sendtime);
mes.setMessageType(MessageType.EXPRESSION);
try {
// System.out.println(“准备发送消息==”);
ObjectOutputStream oos = new ObjectOutputStream(ClientThreadMap.getClientThreadHm(this.userId).getSocket().getOutputStream());
// System.out.println(oos+“:”+mes.getMessageType());
oos.writeObject(mes);
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
(9) 显示消息的函数:
public void showMessage(Message ms) {
try {
Document doc = chat_window.jta.getDocument();
doc.insertString(chat_window.jta.getDocument().getLength(),FriendMap.getFriendData(this.receiveId).getFriendName()+“(”+ms.getSender()+“) “+ms.getSendtime()+”\n”, null);
if(ms.getMessageType().equals(MessageType.CONTENT)){
doc.insertString(chat_window.jta.getDocument().getLength()," “+ms.getContent()+”\n", null);
} else if(ms.getMessageType().equals(MessageType.EXPRESSION)){
//插入图片
doc.insertString(chat_window.jta.getDocument().getLength(), " “, null);
chat_window.jta.setCaretPosition(doc.getLength());
chat_window.jta.insertIcon(new ImageIcon(“images/”+ms.getContent()+”.gif"));
doc.insertString(chat_window.jta.getDocument().getLength(), “\n”, null);
} else if(ms.getMessageType().equals(MessageType.SHAKE_WIN)){
doc.insertString(chat_window.jta.getDocument().getLength()," “+ms.getContent()+”\n", null);
}
} catch (BadLocationException e) {
e.printStackTrace();
}
}
(10) 服务器端等待和验证客户端信息函数
public void run() {
while (true) {
try {
System.out.println(“服务器正在等待客户端的连接”);
Socket sk = ssk.accept();
ObjectInputStream ois=new ObjectInputStream(sk.getInputStream());
Message ms = (Message)ois.readObject();
User u = ms.getUser();
String password = UserManager.getInstance().checkPersonalData(u.getUserId());
if(null != password){
if(u.getPassword().equals(password)) {
System.out.println(“验证密码”);
ServerConnClientThread sct = new ServerConnClientThread(sk);
sct.start();
ThreadMap.addThread(u.getUserId(), sct);
/*验证成功后,把所有在线用户返回给所有的用户*/
Iterator it = ThreadMap.hm.entrySet().iterator();
String all_id = "";
while(it.hasNext()){
Entry entry = (Entry) it.next();
all_id+=entry.getKey();
if(it.hasNext()){
all_id+=",";
}
// System.out.println(all_id);
}
ms.setAll_online_id(all_id);
Iterator it2 = ThreadMap.hm.entrySet().iterator();
while(it2.hasNext()) {
Entry entry = (Entry) it2.next();
ServerConnClientThread st = (ServerConnClientThread) entry.getValue();
ObjectOutputStream oos = new ObjectOutputStream(st.sk.getOutputStream());
ms.setReceiver(entry.getKey()+“”);
oos.writeObject(ms);
}
} else {
//密码错误
Message mss = new Message();
mss.setMessageType(MessageType.ON_LINE_FAIL);
mss.setContent(“用户密码错误!”);
ObjectOutputStream oos=new ObjectOutputStream(sk.getOutputStream());
oos.writeObject(mss);
sk.close();
}
} else {
//用户不存在
Message mss = new Message();
mss.setMessageType(MessageType.ON_LINE_FAIL);
mss.setContent(“该用户不存在”);
ObjectOutputStream oos=new ObjectOutputStream(sk.getOutputStream());
oos.writeObject(mss);
sk.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
(11) 服务器转发消息函数
public void run() {
boolean run = true;
Message m = null;
while(true){
try {
ObjectInputStream ois = new ObjectInputStream(sk.getInputStream());
m=(Message)ois.readObject();
System.out.println(“开始接收数据”);
String messageType = m.getMessageType();
String sender = m.getSender();
String receiver = m.getReceiver();
if(m.getMessageType().equals(MessageType.OFF_LINE)) {
//重新获取在线ID
Iterator it = ThreadMap.hm.entrySet().iterator();
String all_id = "";
while(it.hasNext()){
Entry entry = (Entry) it.next();
if(!(entry.getKey().toString().equals(sender))){
all_id+=entry.getKey();
if(it.hasNext()){
all_id+=",";
}
}
}
String all_id2 = all_id.substring(0, all_id.length());
System.out.println(all_id2);
//把消息传给每个人
Iterator it2 = ThreadMap.hm.entrySet().iterator();
ObjectOutputStream oos = null;
ServerConnClientThread scct = null;
while(it2.hasNext()){
Entry entry = (Entry) it2.next();
scct = (ServerConnClientThread) entry.getValue();
oos = new ObjectOutputStream(scct.sk.getOutputStream());
m.setAll_online_id(all_id2);
oos.writeObject(m);
}
ThreadMap.hm.remove(sender);
break;
} else {
if(messageType.equals(MessageType.REQUEST)||messageType.equals(MessageType.REQUEST_CONFIRM)) {
Iterator it = ThreadMap.hm.entrySet().iterator();
String all_id = "";
while(it.hasNext()){
Entry entry = (Entry) it.next();
all_id+=entry.getKey();
if(it.hasNext()){
all_id+=",";
}
// System.out.println(all_id);
}
m.setAll_online_id(all_id);
}
ObjectOutputStream oos = new ObjectOutputStream(ThreadMap.getThread(receiver).sk.getOutputStream());
System.out.println(“开始转发数据”);
oos.writeObject(m);
}
}catch(Exception ex){
ex.printStackTrace();
}
}
}