登录。好友上线,下线均提示。
点击好友列表所有人,发送消息,既为群聊
点击好友列表,好友名字,既为选中此好友进行私聊
ChatRoomServer
package sram.server;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import sram.thread.ChatRoomServerReaderThread;
import sram.user.User;
public class ChatRoomServer {
private ServerSocket ss;
public static HashMap online = new HashMap();
public ChatRoomServer(){
try {
ss = new ServerSocket(6666);
System.out.println("服务器已经开启,等待客户端...");
while(true){
Socket s = ss.accept();
new Thread(new ChatRoomServerReaderThread(s)).start();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
new ChatRoomServer();
}
}
ChatRoomServerReaderThread
package sram.thread;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import sram.server.ChatRoomServer;
import sram.user.Message;
import sram.user.User;
import sram.util.SendMessageUtil;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
public class ChatRoomServerReaderThread implements Runnable {
private Socket s;
private BufferedReader br;
private PrintWriter pw;
private Message message;
public ChatRoomServerReaderThread(Socket s) throws IOException {
this.s = s;
br = new BufferedReader(new InputStreamReader(s.getInputStream()));
pw = new PrintWriter(s.getOutputStream(),true);
}
@Override
public void run() {
String str = null;
try {
while((str = br.readLine())!=null){
message = new Message(); //返回的消息。
System.out.println("发过来的消息是:"+str);
Message msg =
(Message) JSONObject.toBean
(JSONObject.fromObject(str), Message.class);
switch (msg.getType()) {
case 1: //处理登录消息。
User loginUser = msg.getFromUser();
if(ChatRoomServer.online.containsKey(loginUser)){
//用户名已经存在,不能登录
message.setType(11);
pw.println(JSONObject.fromObject(message).toString());
}else{
//用户名未存在,可以登录,返回可以登录的消息
//获取所有用户
List users = new ArrayList(ChatRoomServer.online.keySet());
//List转化为JSON字符串
message.setMessage(JSONArray.fromObject(users).toString());
message.setType(10);
ChatRoomServer.online.put(loginUser, s);
pw.println(JSONObject.fromObject(message).toString());
//用户登录成功后,系统发送系统消息给每个用户,告诉所有用户 XXX上线了。
message.setType(4);
message.setFromUser(loginUser);
//调用群发方法,群发消息给所有用户。
SendMessageUtil.sendMessage(ChatRoomServer.online, message);
}
break;
case 3:
User out = msg.getFromUser();
ChatRoomServer.online.remove(out);
message.setType(5);
message.setFromUser(out);
SendMessageUtil.sendMessage(ChatRoomServer.online, message);
break;
case 20: //处理私聊消息
User toUser = msg.getToUser();
Socket s = ChatRoomServer.online.get(toUser);
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
msg.setType(22);
pw.println(JSONObject.fromObject(msg).toString());
break;
case 21: //处理群聊消息
msg.setType(23);
SendMessageUtil.sendMessage(ChatRoomServer.online,msg);
break;
}
if(msg.getType()==3){
break;
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Message
package sram.user;
//用来描述发送消息的类。 1登陆 2聊天 3退出 4系统消息等
public class Message {
private String message; //发送消息内容
private int type; //发送消息类型
private User fromUser; //来源自谁的消息
private User toUser; //发送给谁
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public User getFromUser() {
return fromUser;
}
public void setFromUser(User fromUser) {
this.fromUser = fromUser;
}
public User getToUser() {
return toUser;
}
public void setToUser(User toUser) {
this.toUser = toUser;
}
}
User
package sram.user;
//用户实体类。登陆成功后,可以被保存在服务器端。
public class User {
private String username; //用户名,唯一的标识
private String nickname;//用户自定义的网络名称
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public User(String username, String nickname) {
this.username = username;
this.nickname = nickname;
}
public User() {
}
@Override
public int hashCode() {
// TODO Auto-generated method stub
return 0;
}
@Override
public boolean equals(Object obj) {
User user = (User)obj;
return this.username.equals(user.username);
}
}
SendMessageUtil
package sram.util;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Map;
import java.util.Set;
import net.sf.json.JSONObject;
import sram.user.Message;
import sram.user.User;
public class SendMessageUtil {
//群发的方法
public static void sendMessage(Map online,Message message) throws IOException{
Set users = online.keySet();
for(User user :users){
Socket s = online.get(user);
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
pw.println(JSONObject.fromObject(message).toString());
}
}
}
ClientFrame
package sram.frame;
import java.awt.BorderLayout;
import java.awt.ScrollPane;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import net.sf.json.JSONObject;
import sram.user.Message;
import sram.user.User;
public class ClientFrame {
private JFrame frame; //主窗口
private JTextArea area; //显示消息的文本域
private JTextField text; //输入消息的文本框
private JButton btnSend; //发送消息的按钮
private JPanel p1,p2,p3; //布局使用的面板
private TitledBorder b1,b2,b3;
//好友列表使用JTree显示
private JTree tree;
private DefaultMutableTreeNode root;// 根节点
private DefaultTreeModel model; //树模型
private User user;
private Socket socket;
private PrintWriter pw;
private User toUser = new User("All","所有人");
private List friends;
public DefaultMutableTreeNode getRoot() {
return root;
}
public void setRoot(DefaultMutableTreeNode root) {
this.root = root;
}
public DefaultTreeModel getModel() {
return model;
}
public void setModel(DefaultTreeModel model) {
this.model = model;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public JTextArea getArea() {
return area;
}
public void setArea(JTextArea area) {
this.area = area;
}
public ClientFrame(User user,Socket socket,List friends){
this.user = user;//******************************************************
this.socket = socket;//**************************************************
this.friends = friends;//************************************************
//调用init()方法初始化界面
init();
listener();
}
//初始化方法显示界面
public void init(){
//初始化对象**********************************************
try {
pw = new PrintWriter(socket.getOutputStream(),true);
} catch (IOException e) {
e.printStackTrace();
}
frame = new JFrame("当前用户:["+user.getNickname()+"("+user.getUsername()+")"+"]");
area = new JTextArea();
area.setEditable(false);
area.setLineWrap(true);
text = new JTextField(20);
root = new DefaultMutableTreeNode("我的好友");
model = new DefaultTreeModel(root);
model.insertNodeInto(new DefaultMutableTreeNode(new User("All", "所有人")), root,root.getChildCount());
//************************************************************************************
if(friends!=null && friends.size()>0){
for(User userf : friends){
model.insertNodeInto(new DefaultMutableTreeNode(userf), root,root.getChildCount());
}
}
//************************************************************************************
tree = new JTree(model);
//启用折叠显示
tree.setShowsRootHandles(true);
btnSend = new JButton("发送");
b1 = new TitledBorder("好友列表");
b2 = new TitledBorder("消息显示");
b3 = new TitledBorder("发送消息");
p1 = new JPanel(); //右边的面板,用来显示好友列表
p2 = new JPanel(); //中间的面板,用来显示JTextArea
p3 = new JPanel(); //下边的面板,用来显示文本输入框和发送按钮
p1.setLayout(new BorderLayout());
p2.setLayout(new BorderLayout());
p1.setBorder(b1);
p2.setBorder(b2);
p3.setBorder(b3);
p1.add(new JScrollPane(tree));
p2.add(new JScrollPane(area,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_NEVER),BorderLayout.CENTER);
p3.add(text);
p3.add(btnSend);
p2.add(p3,BorderLayout.SOUTH);
frame.add(p1,BorderLayout.WEST);
frame.add(p2,BorderLayout.CENTER);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.setSize(500, 450);
frame.setVisible(true);
}
public void listener(){
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
int i = JOptionPane.showConfirmDialog(null, "您确定要退出聊天室?");
if(i == JOptionPane.OK_OPTION){
Message message = new Message();
message.setType(3); //退出消息
message.setFromUser(user);
pw.println(JSONObject.fromObject(message).toString());
try {
Thread.sleep(100);
System.exit(0);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
});
tree.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if(tree.getSelectionPath()!=null && tree.getSelectionPath().getParentPath()!=null){
//获取点击节点的行数//*********************************************
int i = tree.getLeadSelectionRow();
DefaultMutableTreeNode node
= (DefaultMutableTreeNode) root.getChildAt(i-1);
toUser = (User) node.getUserObject();
frame.setTitle("当前用户:["+user.getNickname()
+"("+user.getUsername()+")"+"] 和 ["+toUser.getNickname()
+"("+toUser.getUsername()+")"+"]的聊天");
}
}
});
btnSend.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//私聊 群聊
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String msg = text.getText();
if(msg!=null && !msg.equals("")){
Message message = new Message();
message.setMessage(msg);
message.setFromUser(user);
text.setText("");
if(toUser!=null){
if(toUser.getUsername().equals("All")){
message.setType(21);
area.append("【群聊】"+user.getNickname()
+"("+user.getUsername()+") "+sdf.format(date)+" 说:");
area.append(msg+"\r\n");
}else{
message.setType(20);
area.append("【私聊】"+user.getNickname()
+"("+user.getUsername()+") "+sdf.format(date)+" 说:");
area.append(msg+"\r\n");
}
}
message.setToUser(toUser);
pw.println(JSONObject.fromObject(message).toString());
}
}
});
}
public static void main(String[] args) {
//new ClientFrame(new User(),new Socket());
}
}
LoginFrame
package sram.frame;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import sram.thread.ChatRoomClientReaderThread;
import sram.user.Message;
import sram.user.User;
public class LoginFrame {
private JFrame frame;
private JLabel usernameLabel;
private JLabel nicknameLabel;
private JTextField usernameText;
private JTextField nicknameText;
private JButton btnRest;
private JButton btnLogin;
private JPanel p1,p2,p3;
public LoginFrame(){
init();
listener();
}
public void init(){
frame = new JFrame();
usernameLabel = new JLabel("用户名");
nicknameLabel = new JLabel("昵 称");
usernameText = new JTextField(10);
nicknameText = new JTextField(10);
btnRest = new JButton("重置");
btnLogin = new JButton("登录");
p1 = new JPanel();
p2 = new JPanel();
p3 = new JPanel();
frame.setLayout(new GridLayout(3, 1));
p1.add(usernameLabel);
p1.add(usernameText);
p2.add(nicknameLabel);
p2.add(nicknameText);
p3.add(btnRest);
p3.add(btnLogin);
frame.add(p1);
frame.add(p2);
frame.add(p3);
frame.setResizable(false);
frame.pack();
Toolkit t = Toolkit.getDefaultToolkit(); //获取工具包
Dimension d = t.getScreenSize(); //获取屏幕尺寸
frame.setLocation(d.width/2-175,d.height/2-40);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public void listener(){
btnLogin.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String username = usernameText.getText();
String nickname = nicknameText.getText();
User user = new User(username,nickname);
Message msg = new Message();
msg.setType(1);
msg.setFromUser(user);
try {
Socket s = new Socket("127.0.0.1",6666);
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
pw.println(JSONObject.fromObject(msg).toString());
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
String message = br.readLine();
System.out.println("客户端接受的消息是:"+message);
Message msgReturn =
(Message) JSONObject.toBean
(JSONObject.fromObject(message), Message.class);
switch (msgReturn.getType()) {
case 10:
System.out.println("开启聊天窗口");
String usersStr = msgReturn.getMessage();
List list = JSONArray.toList(JSONArray.fromObject(usersStr),User.class);
ClientFrame f = new ClientFrame(user,s,list);
new Thread(new ChatRoomClientReaderThread(s, f)).start();
frame.setVisible(false);
break;
case 11:
s.close();
JOptionPane.showMessageDialog(null, "用户名已经存在,请重新输入后登录");
break;
default:
break;
}
} catch (UnknownHostException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
}
public static void main(String[] args) {
new LoginFrame();
}
}
ChatRoomClientReaderThread
package sram.thread;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import net.sf.json.JSONObject;
import sram.frame.ClientFrame;
import sram.user.Message;
import sram.user.User;
import sram.util.FriendsTreeFlushUtil;
public class ChatRoomClientReaderThread implements Runnable {
private Socket socket;
private BufferedReader br ;
private PrintWriter pw;
private ClientFrame frame;
public ChatRoomClientReaderThread(Socket socket,ClientFrame frame) throws IOException {
this.socket = socket;
this.frame = frame;
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
pw = new PrintWriter(socket.getOutputStream(),true);
}
@Override
public void run() {
String str = null;
try {
while((str = br.readLine())!=null){
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("客户端接收的消息是:"+str);
Message msg =
(Message) JSONObject.toBean
(JSONObject.fromObject(str), Message.class);
switch (msg.getType()) {
case 4: //系统登录消息。
User userLogin = msg.getFromUser();
if(userLogin.equals(frame.getUser())){
frame.getArea().append("[系统消息]:您成功上线了!\r\n");
}else{
frame.getArea().append("[系统消息]:您的好友"+userLogin.getNickname()+"("+userLogin.getUsername()+")上线了!\r\n");
//好友列表增加用户
FriendsTreeFlushUtil.insert(frame, userLogin);
}
break;
case 5: //系统退出消息
User out = msg.getFromUser();
frame.getArea().append("[系统消息]:您的好友"+out.getNickname()+"("+out.getUsername()+")下线了!\r\n");
//在好友列表中移除退出的用户
FriendsTreeFlushUtil.remove(frame, out);
break;
case 22: //客户端接收的私聊消息
User fromUser = msg.getFromUser();
String msgStr = msg.getMessage();
frame.getArea().append("【私聊】"+fromUser.getNickname()
+"("+fromUser.getUsername()+") "+sdf.format(date)+" 说:");
frame.getArea().append(msgStr+"\r\n");
break;
case 23: //客户端接收的群聊消息
User fromU = msg.getFromUser();
String msgS = msg.getMessage();
if(!frame.getUser().equals(fromU)){
frame.getArea().append("【群聊】"+fromU.getNickname()
+"("+fromU.getUsername()+") "+sdf.format(date)+" 说:");
frame.getArea().append(msgS+"\r\n");
}
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Message
package sram.user;
//用来描述发送消息的类。 1登陆(10 成功 11 不成功) 2 (服务器(20 私聊 21 群聊)客户端( 22 私聊,23群聊 )聊天 3退出 4系统登录消息 5 系统退出消息等
public class Message {
private String message; //发送消息内容
private int type; //发送消息类型
private User fromUser; //来源自谁的消息
private User toUser; //发送给谁
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public User getFromUser() {
return fromUser;
}
public void setFromUser(User fromUser) {
this.fromUser = fromUser;
}
public User getToUser() {
return toUser;
}
public void setToUser(User toUser) {
this.toUser = toUser;
}
}
User
package sram.user;
//用户实体类。登陆成功后,可以被保存在服务器端。
public class User {
private String username; //用户名,唯一的标识
private String nickname;//用户自定义的网络名称
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public User(String username, String nickname) {
this.username = username;
this.nickname = nickname;
}
public User() {
}
@Override
public boolean equals(Object obj) {
User user = (User)obj;
return this.username.equals(user.username);
}
@Override
public String toString() {
return nickname+"("+username+")";
}
}
FriendsTreeFlushUtil
package sram.util;
import java.util.Enumeration;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import sram.frame.ClientFrame;
import sram.user.User;
public class FriendsTreeFlushUtil {
public static void insert(ClientFrame frame,User u){
DefaultMutableTreeNode root = frame.getRoot();
DefaultTreeModel model = frame.getModel();
model.insertNodeInto(new DefaultMutableTreeNode(u), root, root.getChildCount());
}
public static void remove(ClientFrame frame,User u){
DefaultMutableTreeNode root = frame.getRoot();
DefaultTreeModel model = frame.getModel();
//获取所有的子节点
Enumeration children = root.children();
while(children.hasMoreElements()){
DefaultMutableTreeNode node = children.nextElement();
User user = (User) node.getUserObject();
if(user.equals(u)){
model.removeNodeFromParent(node);
}
}
}
}