JavaGUI--模拟QQ聊天界面私聊群聊

点击下载源代码

目录

JFrame类的几个重要方法

持久层

服务端创建

用户注册模块

用户登录窗口

好友列表和群聊列表模块

用户私聊逻辑

群聊创建

群聊消息


JFrame类的几个重要方法

  • Frame类的构造方法,设置所画出的窗口的名称:public JFrame(String title)
  • 设置该窗口下的所有窗体(panel):public void setContentPane(Container contentPane)
  • 设置窗口关闭之后的状态:public void setDefaultCloseOperation(int operation)
  • 设置窗口显示位置(传入为null时则默认显示在屏幕正中央):public void setLocationRelativeTo(Component c)
  • 根据布局来确定窗口的最佳大小 public void pack()
  • 设置页面是否显示(默认不显示):public void setVisible(boolean b)

持久层

  • 创建User对象
  • 创建持久层层基础类,封装数据源,获取连接,关闭资源等操作
  • 创建用户持久层模块

用户对象和数据库对应

package com.lin.client.entity;

import java.util.Set;

/**
 * Description:
 * Author:  llf
 * Created in 2019/8/23 16:30
 */
public class User {
    private Integer id;
    private String userName;
    private String password;
    private String brief;
    private Set userNames;
    public User() {}

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getBrief() {
        return brief;
    }

    public void setBrief(String brief) {
        this.brief = brief;
    }

    public Set getUserNames() {
        return userNames;
    }

    public void setUserNames(Set userNames) {
        this.userNames = userNames;
    }
}

dao层基类

package com.lin.client.dao;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.alibaba.druid.pool.DruidPooledConnection;
import com.lin.client.entity.User;
import com.lin.util.CommUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
 * Description:dao层基础类i,封装数据源,获取连接,关闭资源等操作
 * Author:  llf
 * Created in 2019/8/23 16:30
 */
public class BasedDao {
    //获取数据源
    private static DruidDataSource dataSource;
    static{
        Properties properties= CommUtils.loadProperties("db.properties");
        try {
            dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            System.out.println("获取数据源失败");
            e.printStackTrace();
        }
    }

    //获取连接
    protected DruidPooledConnection getConnection() {
        try {
            return (DruidPooledConnection) dataSource.getPooledConnection();
        } catch (SQLException e) {
            System.out.println("数据库连接失败");
            e.printStackTrace();
        }
        return null;
    }

    //关闭资源
    protected void closeResources(Connection connection,
                                  Statement statement) {
        try {
        if (connection != null) connection.close();
        if (statement != null) statement.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    protected void closeResources(Connection connection,
                                  Statement statement,
                                  ResultSet resultSet) {
        closeResources(connection,statement);
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

}

用户查询和插入DAO模块

package com.lin.client.dao;


import com.alibaba.druid.pool.DruidPooledConnection;
import com.lin.client.entity.User;
import org.apache.commons.codec.digest.DigestUtils;

import java.sql.*;


/**
 * Description:
 * Author:  llf
 * Created in 2019/8/23 16:30
 */
public class AccountDao extends BasedDao{
    public User userLogin(String userName, String password) {
        Connection connection=null;
        PreparedStatement statement=null;
        ResultSet resultSet=null;
        try{
            connection=getConnection();
            String sql="select * from user where username=? and password=?";
            statement = connection.prepareStatement(sql);
            statement.setString(1,userName);
            //******解密   ********************
            statement.setString(2, DigestUtils.md5Hex(password));
            resultSet=statement.executeQuery();
            if (resultSet.next()) {
                User user=getUser(resultSet);
                return user;
            }
        }catch (SQLException e) {
            System.out.println("用户登陆失败");
            e.printStackTrace();
        }finally {
            closeResources(connection,statement,resultSet);
        }
        return null;
    }

    private User getUser(ResultSet resultSet) throws SQLException {
        User user=new User();
        user.setId(resultSet.getInt("id"));
        user.setUserName(resultSet.getString("username"));
        user.setPassword(resultSet.getString("password"));
        user.setBrief(resultSet.getString("brief"));
        return user;
    }

    public boolean userReg(User user) {
        Connection connection=null;
        PreparedStatement statement=null;
        try{
            connection = getConnection();
            String str="insert into user(username,password,brief) values (?,?,?)";
            //接收执行sql语句影响的行数,
            statement=connection.prepareStatement(str, Statement.RETURN_GENERATED_KEYS);
            statement.setString(1,user.getUserName());

            //加密   *************
            statement.setString(2,DigestUtils.md5Hex(user.getPassword()));
            statement.setString(3,user.getBrief());
            int rows = statement.executeUpdate();
            if (rows==1) {
                return true;
            }
        }catch (SQLException ex) {
            System.out.println("用户注册失败");
            ex.printStackTrace();
        }finally {
            closeResources(connection,statement);
        }
        return false;
    }
}

服务端创建

  • 设置工具类,读取配置文件中的IP和PORT
  • 设置Json格式和Object类之间的相互转换
  • 设置客户端和服务端之间通信的VO对象
public class CommUtils {
    //这里我们使用的是谷歌推出的GSON类,创建对象(建造者模式)   
    private static final Gson GSON=new GsonBuilder().create();
    //加载配置文件
    public static Properties loadProperties(String fileName) {
        Properties properties=new Properties();
        InputStream in=CommUtils.class.getClassLoader().getResourceAsStream(fileName);
        try {
            properties.load(in);
        } catch (IOException e) {
            return null;
        }
        return properties;
    }

    //将实例化对象转换为JSON格式
    public static String object2Json(Object obj) {
        return GSON.toJson(obj);
    }
    //将JSON格式转换为实例化对象
    public static Object json2Object(String jsonStr, Class objClass) {
        return GSON.fromJson(jsonStr,objClass);

    }
}

创建客户端和服务端之间通信的VO对象

public class MessageVO {
    /**
     * 客户端和服务器之间的协议
     */
    private String type;
    /**
     * 客户端与服务端之间发送数据的内容
     */
    private String content;
    /**
     * 客户端告诉服务器要将信息发送给何处,在群聊中也可封装群聊用户集合等数据
     */
    private String to;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getTo() {
        return to;
    }

    public void setTo(String to) {
        this.to = to;
    }
}

创建服务端

public class MultiThreadServer {
    private static final String IP;
    private static final int PORT;
    static {
        //读取socket配置文件
        Properties pros= CommUtils.loadProperties("socket.properties");
        IP=pros.getProperty("address");
        PORT=Integer.parseInt(pros.getProperty("port"));
    }

    
    public static void main(String[] args) throws IOException {
        //创建服务端
        ServerSocket serverSocket=new ServerSocket(PORT);
        //创建线程池设置连接服务端的人数
        ExecutorService executors = Executors.newFixedThreadPool(50);
        for (int i = 0; i < 50; i++) {
            System.out.println("等待客户端连接...");
            Socket client = serverSocket.accept();
            System.out.println("有新的客户端连接,端口号为"+client.getPort());
            //有客户端连接就创建线程不断监听该客户端发来的信息
            executors.submit(new ExecuteClient(client));
        }
    }
}

封装客户端与服务端的连接

public class Connect2Server {
    private static final String IP;
    private static final int PORT;
    static {
        Properties properties = CommUtils.loadProperties("socket.properties");
        IP=properties.getProperty("address");
        PORT= Integer.parseInt(properties.getProperty("port"));
    }
    private Socket client;
    private InputStream in;
    private OutputStream out;

    public Connect2Server() {
        try {
            //连接服务器的端口,所以此处与服务器端读取同一个配置文件
            client=new Socket(IP,PORT);
            //获取连接的输入流
            in=client.getInputStream();
            //获取连接的输出流
            out=client.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public InputStream getIn() {
        return in;
    }

    public OutputStream getOut() {
        return out;
    }
}

用户注册模块

创建GUI Form画布(窗口):设置相应的窗体名称,Idea软件会为我们自动生成窗口的对应的属性,

JavaGUI--模拟QQ聊天界面私聊群聊_第1张图片

分析

当点击注册按钮之后获取窗体数据,封装成User对象,调用数据库进行创建,

        创建成功,显示创建成功,创建用户窗口关闭

        创建失败,显示失败信息

public class UserReg {
    private JTextField userNameText;
    private JPasswordField passwordText;
    private JTextField brifeText;
    private JButton regBtn;
    private JPanel userRegPanel;

    private AccountDao accountDao=new AccountDao();

    public UserReg() {
        JFrame frame = new JFrame("用户注册");
        //包含这个窗口下的所有窗体
        frame.setContentPane(userRegPanel);
        //设置关闭即该窗口永久消失
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //使窗口显示在正中央
        frame.setLocationRelativeTo(null);
        //根据布局来确定窗口的最佳大小
        frame.pack();
        //窗口图形界面可视,     默认不可视
        frame.setVisible(true);

        //点击注册按钮,将信息持久化到db中,成功弹出提示框
        regBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //获取表单参数
                String userName = userNameText.getText();
                //获取加密的密码
                String password = String.valueOf(passwordText.getPassword());
                String brief=brifeText.getText();
                //封装User保存到数据库中
                User user=new User();
                user.setUserName(userName);
                user.setPassword(password);
                user.setBrief(brief);
                if (accountDao.userReg(user)) {
                    //注册成功
                    JOptionPane.showMessageDialog(frame,"注册成功!","提示信息",JOptionPane.INFORMATION_MESSAGE);
                    frame.setVisible(false);
                }else {
                    //注册失败
                    JOptionPane.showMessageDialog(frame,"注册失败!","错误信息",JOptionPane.ERROR_MESSAGE);
                }
            }
        });
    }
}

用户登录窗口

用画布画出该窗口

JavaGUI--模拟QQ聊天界面私聊群聊_第2张图片

分析

用户在登陆界面当点击注册按钮时,创建注册按钮,跳转到注册页面逻辑

用户在登陆页面点击登陆按钮时,获取窗体数据,调用数据库根据密码和用户名查询是否存在该用户

         若存在:提示登陆成功信息,

                        点击确定,创建连接,将自己的信息以协议“1”发送到服务端,标识为登陆逻辑,方便服务端转向,

                                                                                                                  * 此时 type = 1     content  = userName

                        登陆页面关闭,创建用户列表界面

        若不存在

                        提示错误信息即可

package com.lin.client.service;

import com.lin.client.dao.AccountDao;
import com.lin.client.entity.User;
import com.lin.util.CommUtils;
import com.lin.vo.MessageVO;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.Scanner;
import java.util.Set;

/**
 * Description:登陆成功,将当前用户所有的在线好友发送到服务器
 * Author:  llf
 * Created in 2019/8/23 19:04
 */
public class UserLogin {
    private JButton regButton;
    private JButton loginButton;
    private JTextField userNameText;
    private JPasswordField passwordText;
    private JPanel userPanel;
    private JPanel UserLoginPanel;
    private  JFrame frame;

    private AccountDao accountDao=new AccountDao();

    public UserLogin() {
        frame = new JFrame("用户登陆");
        frame.setContentPane(UserLoginPanel);
        //退出即关闭该对象
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //将窗口置于屏幕中央
        frame.setLocationRelativeTo(null);
        //根据页面布局设置窗口的大小
        frame.pack();
        //图形界面可视
        frame.setVisible(true);
        //创建监听事件,注册按钮,当点击该按钮,创建出一个注册页面
        regButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                new UserReg();
            }
        });
        //创建监听事件,登陆按钮
        loginButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //获取表单参数   校验用户信息
                String userName = userNameText.getText();
                String password = String.valueOf(passwordText.getPassword());
                User user = accountDao.userLogin(userName,password);
                if (user!=null) {
                    //显示成功信息**************
                    JOptionPane.showMessageDialog(frame,"登陆成功","提示信息",JOptionPane.INFORMATION_MESSAGE);
                    //登陆页面关闭
                    frame.setVisible(false);
                    /**
                     * 与服务器建立连接
                     * 读取服务端所有好友信息
                     * 新建一个后台进程不断读取服务器发来的信息
                     * type   1
                     * content  userName
                     * to   私聊  群聊
                     */
                    //以协议1的方式向服务器发送数据
                    Connect2Server connect2Server=new Connect2Server();
                    MessageVO msg2Server=new MessageVO();
                    msg2Server.setType("1");
                    msg2Server.setContent(userName);
                    String json2Server=CommUtils.object2Json(msg2Server);
                    try {
                        PrintStream out=new PrintStream(connect2Server.getOut(),true,"UTF-8");
                        //发送数据到服务器
                        out.println(json2Server);
                        //接收服务器发来的消息,显示所有在线好友,加载用户列表
                        /**
                         *                       type   1
                         *                       content 在线用户信息
                         *                       to
                         */
                        Scanner in=new Scanner(connect2Server.getIn());
                        if (in.hasNextLine()) {
                            String msgFromServerStr = in.nextLine();
                            MessageVO msgFromServer=(MessageVO) CommUtils.json2Object(msgFromServerStr,MessageVO.class);
                            //将服务器发送过来的信息封装成一个储存user对象的Set集合,传入好友列表进行显示,
                            Set users = (Set) CommUtils.json2Object(msgFromServer.getContent(), Set.class);
                            System.out.println("所有在线用户为:" + users);
                            //加载用户列表界面,将当前用户名,所有在线好友,与服务器建立连接,传递到好友列表界面***********
                            new FriendsList(userName,users,connect2Server);
                        }
                    } catch (UnsupportedEncodingException ex) {
                        ex.printStackTrace();
                    }

                }else {
                    //登陆失败,留在当前登陆页面,显示错误信息
                    JOptionPane.showMessageDialog(frame,"登陆失败!","错误信息",JOptionPane.ERROR_MESSAGE);
                }
            }
        });
    }

    public static void main(String[] args) {
        UserLogin userLogin=new UserLogin();
    }
}

服务器端程序

 //type:“1” 

 //content 所有在线好友     

    //缓存当前服务器所有在线的客户信息
    private static Map clients=new ConcurrentHashMap<>();
  
    private static class ExecuteClient implements Runnable{
        private Socket client;
        private Scanner in;
        private PrintStream out;
        public ExecuteClient(Socket client) {
            this.client=client;
            try {
                this.in=new Scanner(client.getInputStream());
                this.out=new PrintStream(client.getOutputStream(),true,"UTF-8");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void run() {
            while (true) {
                if (in.hasNextLine()) {
                    String jsonStrFromClient = in.nextLine();
                    MessageVO msgFromClient = (MessageVO) CommUtils.json2Object(jsonStrFromClient, MessageVO.class);
                    if (msgFromClient.getType().equals("1")) {
                        //获取客户发来的用户名
                        String userName=msgFromClient.getContent();
                        /**
                         * 将当前所有在线用户发送给新用户
                         * 将新用户上限信息发送给其他用户(上线提醒)
                         * 保存新用户上线信息
                         */
                        //将当前在线的所有用户发回客户端
                        MessageVO msg2Client=new MessageVO();
                        msg2Client.setType("1");
                        msg2Client.setContent(CommUtils.object2Json(clients.keySet()));
                        out.println(CommUtils.object2Json(msg2Client));
                        //将新上线的用户信息发回给当前以在线的所有用户
                        sendUserLogin("newLogin:"+userName);
                        //将当前用户注册到服务端缓存
                        clients.put(userName,client);
                        System.out.println(userName+"上线了!");
                        System.out.println("当前聊天室共有"+clients.size()+"人");                    
                    }
                }
            }
        }

        private void sendUserLogin(String msg) {
            for(Map.Entry entry:clients.entrySet()) {
                Socket socket=entry.getValue();
                try {
                    PrintStream out=new PrintStream(socket.getOutputStream(),true,"UTF-8");
                    out.println(msg);
                }catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }



好友列表和群聊列表模块

用画布画出好友列表和群聊列表窗口的静态部分,动态部分由代码实现

JavaGUI--模拟QQ聊天界面私聊群聊_第3张图片

分析

登陆成功后程序逻辑
服务端
1.保存新用户上线信息  request
2.将当前所有在线用户发回给新用户 response
3.将新用户上线信息发给所有其他用户(上线提醒)
客户端
1.与服务器建立连接,将自己的用户名与Socket保存到服务端缓存
2.读取服务端的所有在线好友信息
3.新建一个后台线程不断读取服务器发来的信息

 

 

public class FriendsList {
    private JButton createGroupBtn;
    private JScrollPane friendsList;
    private JScrollPane groupListPanel;
    private JPanel friendsPanel;

    private JFrame frame;
    private String userName;
    private Set users;
    private Connect2Server connect2Server;

    public FriendsList(String userName, Set users, Connect2Server connect2Server) {
        this.userName = userName;
        this.users = users;
        this.connect2Server = connect2Server;
        frame = new JFrame(userName);
        frame.setContentPane(friendsPanel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(400, 300);
        //使窗口显示在正中央
        frame.setLocationRelativeTo(null);
        //图形界面可视,     默认不可视
        frame.setVisible(true);
        //加载所有在线用户信息
        loadUsers();
        //启动后台线程不断监听服务器发来的消息*************************************
        Thread daemonThread = new Thread(new DaemonTask());
        //设置为守护线程
        daemonThread.setDaemon(true);
        daemonThread.start();
    }

    //好友列表后台任务,不断监听服务器发来的信息,好友上限提醒,用户私聊,群聊
    private class DaemonTask implements Runnable {
        private Scanner in = new Scanner(connect2Server.getIn());
        @Override
        public void run() {
            while (true) {
                if (in.hasNextLine()) {
                    //接收服务到发来的信息
                    String strFromServer = in.nextLine();
                    //此时服务器端发来一个Json字串
                    if (strFromServer.startsWith("{")) {
                       
                    } else {
                        if (strFromServer.startsWith("newLogin:")) {               
                            //好友上线逻辑
                            String newFriendName = strFromServer.split(":")[1];
                            users.add(newFriendName);
                            JOptionPane.showMessageDialog(frame,
                                    newFriendName + "上线了!", "上线提醒",                     
                                    JOptionPane.INFORMATION_MESSAGE);
                            //刷新好友列表
                            loadUsers();
                        }
                    }
                }
            }
        }
    }

    private void loadUsers() {
        //创建好友列表的盘子
        JLabel[] userLables = new JLabel[users.size()];
        JPanel friends = new JPanel();
        //垂直布局
        friends.setLayout(new BoxLayout(friends, BoxLayout.Y_AXIS));

        Iterator iterator = users.iterator();
        int i = 0;
        while (iterator.hasNext()) {
            String userName = iterator.next();
            userLables[i] = new JLabel(userName);       
            //将好友列表加到盘子中
            friends.add(userLables[i]);
            i++;
        }
        //将所有的好友的数组盘子加到friendsList的大盘子
        friendsList.setViewportView(friends);
        //设置滚动条垂直滚动        friendsList.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        //刷新
        friends.revalidate();
        friendsList.revalidate();
    }
}

用户私聊逻辑

用画布画出该窗口的静态部分,显示聊天信息部分由代码实现

JavaGUI--模拟QQ聊天界面私聊群聊_第4张图片
 

JavaGUI--模拟QQ聊天界面私聊群聊_第5张图片

//缓存所有私聊界面
    private Map privateChatGUIList = new ConcurrentHashMap<>();

//私聊点击事件
    private class PrivateLabelAction implements MouseListener {
        private String labelName;

        //根据标签名称跳转私聊界面
        public PrivateLabelAction(String labelName) {
            this.labelName = labelName;
        }

        //鼠标点击事件
        @Override
        public void mouseClicked(MouseEvent e) {
            //判断好友列表是否缓存私聊标签,
            if (privateChatGUIList.containsKey(labelName)) {
                //找到该标签使其显示
                PrivateChatGUI privateChatGUI = privateChatGUIList.get(labelName);
                privateChatGUI.getFreame().setVisible(true);
            } else {
                //第一次点击,创建私聊界面
                PrivateChatGUI privateChatGUI = new PrivateChatGUI(
                        labelName, userName, connect2Server);
                privateChatGUIList.put(labelName, privateChatGUI);
            }
        }

        //鼠标一直按下事件
        @Override
        public void mousePressed(MouseEvent e) {

        }

        //鼠标按下释放事件
        @Override
        public void mouseReleased(MouseEvent e) {

        }

        //鼠标移入执行事件
        @Override
        public void mouseEntered(MouseEvent e) {

        }

        //鼠标移出事件
        @Override
        public void mouseExited(MouseEvent e) {

        }
    }

分析

客户端:

主动发送方

1.点击要私聊的用户标签,弹出私聊界面 (缓存私聊界面对象) 

2.按照指定的协议向服务器发送私聊信息

       // type:2

      // content:sender-msg

      // to:目标客户端的用户名

package com.lin.client.service;

import com.lin.util.CommUtils;
import com.lin.vo.MessageVO;

import javax.swing.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;

/**
 * Description:
 * Author:  llf
 * Created in 2019/8/24 18:55
 */
public class PrivateChatGUI {
    private JTextArea readFromServer;
    private JPanel privateChatPanel;
    private JTextField send2Server;

    private String friendName;
    private String myName;
    private Connect2Server connect2Server;

    private JFrame frame;
    private PrintStream out;

    public PrivateChatGUI(String friendName,
                          String myName,
                          Connect2Server connect2Server) {
        this.friendName=friendName;
        this.connect2Server=connect2Server;
        this.myName=myName;
        try {
            this.out=new PrintStream(connect2Server.getOut(),true,"UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        frame = new JFrame("与"+friendName+"私聊中...");
        frame.setContentPane(privateChatPanel);
        //设置窗口关闭操作,将其设置为隐藏
        frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
        frame.setSize(400,400);
        frame.setVisible(true);
        //捕捉输入框的键盘输入
        send2Server.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                StringBuilder sb=new StringBuilder();
                sb.append(send2Server.getText());
                //当捕捉到按下enter
                if(e.getKeyCode()==KeyEvent.VK_ENTER) {
                    String msg=sb.toString();
                    MessageVO messageVO=new MessageVO();
                    messageVO.setType("2");
                    messageVO.setContent(myName+"-"+msg);
                    messageVO.setTo(friendName);
                    PrivateChatGUI.this.out.println(CommUtils.object2Json(messageVO));
                    //将自己发送的信息展示到当前私聊界面*******
                    readFromServer(myName+"说:"+ msg);
                    send2Server.setText("");
                }
            }
        });
    }

    //给信息接收者
    public void readFromServer(String s) {
        readFromServer.append(s+"\n");
    }

    public JFrame getFreame() {
        return this.frame;
    }
}

 

服务端:

1.收到客户端发来的私聊信息,取出目标端Socket,做信息转发 

     // type:2

     // content:sender-msg

     // to

    private static class ExecuteClient implements Runnable{
        private Socket client;
        private Scanner in;
        private PrintStream out;
        public ExecuteClient(Socket client) {
            this.client=client;
            try {
                this.in=new Scanner(client.getInputStream());
                this.out=new PrintStream(client.getOutputStream(),true,"UTF-8");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void run() {
            while (true) {
                if (in.hasNextLine()) {
                    String jsonStrFromClient = in.nextLine();
                    MessageVO msgFromClient = (MessageVO) CommUtils.json2Object(jsonStrFromClient, MessageVO.class);
                    if (msgFromClient.getType().equals("1")) {
                        //用户登录逻辑
                    }else if (msgFromClient.getType().equals("2")) {
                        //用户私聊   Type2
                        //Content:myName-msg
                        //to:friendName
                        String friendName=msgFromClient.getTo();
                        Socket clientSocket=clients.get(friendName);
                        try{
                            PrintStream out=new PrintStream(clientSocket.getOutputStream(),true,"UTF-8");
                            MessageVO msg2Client=new MessageVO();
                            msg2Client.setType("2");
                            msg2Client.setContent(msgFromClient.getContent());
                            System.out.println("收到私聊消息,内容为:"+msgFromClient.getContent());
                            out.println(CommUtils.object2Json(msg2Client));
                        }catch (IOException ex){
                            ex.printStackTrace();
                        }                  
                    }
                }
            }
        }

信息接收方:

1.判断服务端发来的消息是否是私聊信息,若是,判断缓存中是否有该界面 

2.按照指定协议来读取内容,发送消息

//缓存所有私聊界面
    private Map privateChatGUIList = new ConcurrentHashMap<>();

private class DaemonTask implements Runnable {
        private Scanner in = new Scanner(connect2Server.getIn());

        @Override
        public void run() {
            while (true) {
                if (in.hasNextLine()) {
                    //接收服务到发来的信息
                    String strFromServer = in.nextLine();
                    //此时服务器端发来一个Json字串
                    if (strFromServer.startsWith("{")) {
                        MessageVO messageVO = (MessageVO) CommUtils.json2Object(strFromServer, MessageVO.class);
                        if (messageVO.getType().equals("2")) {
                            String friendName = messageVO.getContent().split("-")[0];
                            String msg = messageVO.getContent().split("-")[1];
                            //判断此私聊是不是第一次创建
                            if (privateChatGUIList.containsKey(friendName)) {
                                PrivateChatGUI privateChatGUI = privateChatGUIList.get(friendName);
                                privateChatGUI.getFreame().setVisible(true);
                                privateChatGUI.readFromServer(friendName + "说:" + msg);
                            } else {
                                PrivateChatGUI privateChatGUI = new PrivateChatGUI(friendName, userName, connect2Server);
                                privateChatGUIList.put(friendName, privateChatGUI);
                                privateChatGUI.readFromServer(friendName + "说:" + msg);
                            }
                        }
                    } else {
                        if (strFromServer.startsWith("newLogin:")) {               //**********************   好友上线逻辑
                            String newFriendName = strFromServer.split(":")[1];
                            users.add(newFriendName);
                            JOptionPane.showMessageDialog(frame,
                                    newFriendName + "上线了!", "上线提醒", JOptionPane.INFORMATION_MESSAGE);
                            //刷新好友列表
                            loadUsers();
                        }

                    }
                }
            }
        }

    }

群聊创建

用画布画出该窗口的静态部分,显示好友列表的复选框部分由代码实现

JavaGUI--模拟QQ聊天界面私聊群聊_第6张图片

分析:点击提交信息,获取表单参数,发送到服务端

客户端

     // type:3

     // content:groupName

     // to:selectedFriends

package com.lin.client.service;

import com.lin.util.CommUtils;
import com.lin.vo.MessageVO;
import javafx.scene.effect.SepiaTone;
import sun.plugin2.message.Message;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * Description:
 * Author:  llf
 * Created in 2019/8/29 18:43
 */
public class CreateGroupGUI {
    private JPanel createGroupPanel;
    private JTextField groupNameText;
    private JButton conformBtn;
    private JPanel friendLabelPanel;

    private String myName;
    private Set friends;
    private Connect2Server connect2Server;
    private FriendsList friendsList;

    public CreateGroupGUI(String myName,
                          Set friends,
                          Connect2Server connect2Server,
                          FriendsList friendsList){
        this.myName=myName;
        this.friends=friends;
        this.connect2Server=connect2Server;
        this.friendsList=friendsList;
        JFrame frame = new JFrame("创建群聊");
        frame.setContentPane(createGroupPanel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(400,300);
        //居中
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        //将在线好友以checkBox展示到界面中
        friendLabelPanel.setLayout(new BoxLayout(friendLabelPanel,BoxLayout.Y_AXIS));
        Iterator iterator=friends.iterator();
        while (iterator.hasNext()) {
            String lableName=iterator.next();
            JCheckBox checkBox=new JCheckBox(lableName);
            friendLabelPanel.add(checkBox);
        }
        friendLabelPanel.revalidate();

        conformBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Set selectedFriends=new HashSet<>();
                Component[] comps=friendLabelPanel.getComponents();
                for (Component comp : comps) {
                    //向下转型
                    JCheckBox checkBox= (JCheckBox) comp;
                    if (checkBox.isSelected()) {
                        String labelName= checkBox.getText();
                        selectedFriends.add(labelName);
                    }
                }
                //创建记得加入自己的名字
                selectedFriends.add(myName);
                //获取群名
                String groupName=groupNameText.getText();
                //发送给服务器
                MessageVO messageVO=new MessageVO();
                /**
                 * 协议3
                 */
                messageVO.setType("3");
                messageVO.setContent(groupName);
                messageVO.setTo(CommUtils.object2Json(selectedFriends));
                try {
                    PrintStream out=new PrintStream(connect2Server.getOut(),true,"UTF-8");
                    out.println(CommUtils.object2Json(messageVO));
                } catch (UnsupportedEncodingException e1) {
                    e1.printStackTrace();
                }
                //将当前群聊界面隐藏
                frame.setVisible(false);
                friendsList.addGroup(groupName,selectedFriends);
                friendsList.loadGroupList();
            }
        });
    }

}

服务端

    //缓存当前服务器所有的群名以及群好友
    private static Map> groups=new ConcurrentHashMap<>(); 
      private static class ExecuteClient implements Runnable{
        private Socket client;
        private Scanner in;
        private PrintStream out;
        public ExecuteClient(Socket client) {
            this.client=client;
            try {
                this.in=new Scanner(client.getInputStream());
                this.out=new PrintStream(client.getOutputStream(),true,"UTF-8");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void run() {
            while (true) {
                if (in.hasNextLine()) {
                    String jsonStrFromClient = in.nextLine();
                    MessageVO msgFromClient = (MessageVO) CommUtils.json2Object(jsonStrFromClient, MessageVO.class);
                    if (msgFromClient.getType().equals("1")) {
                       //用户登陆逻辑
                    }else if (msgFromClient.getType().equals("2")) {
                       //用户私聊逻辑
                    }else if (msgFromClient.getType().equals("3")) {
                        String groupName=msgFromClient.getContent();
                        Set friends= (Set) CommUtils.json2Object(msgFromClient.getTo(), Set.class);
                        groups.put(groupName,friends);
                        System.out.println("有新的群注册成功,群名为:"+groupName
                                +",一共有"+groups.size()+"个群");
                    }                        
                }
            }
        }

好友列表窗口

  //存储所有的群名称和群好友
    private Map> groupMap = new ConcurrentHashMap<>(); 
public void addGroup(String groupName, Set friends) {
        groupMap.put(groupName, friends);
    }

    public void loadGroupList() {
        //存储所有群名称的标签Jpanel
        JPanel groupNamePanel = new JPanel();
        groupNamePanel.setLayout(new BoxLayout(groupNamePanel, BoxLayout.Y_AXIS));
        JLabel[] labels = new JLabel[groupMap.size()];
        //Map遍历
        Set entries = groupMap.keySet();
        Iterator iterator = entries.iterator();
        int i = 0;
        while (iterator.hasNext()) {
            String groupName = iterator.next();
            labels[i] = new JLabel(groupName);
            //添加到自定义的盘子
            groupNamePanel.add(labels[i]);
            i++;
        }
        //将自定义盘子加到滚动条中
        groupListPanel.setViewportView(groupNamePanel);
        //设置垂直滚动
        groupListPanel.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        //刷新
        groupListPanel.revalidate();
    }

群聊消息

用画布画出该窗口的静态部分,显示好友列表和群聊消息的部分由代码实现

JavaGUI--模拟QQ聊天界面私聊群聊_第7张图片


JavaGUI--模拟QQ聊天界面私聊群聊_第8张图片

 

 //缓存所有的群聊页面   群聊名  和群聊界面
    private Map groupChatGUIMap=new ConcurrentHashMap<>();

 //群聊点击事件
    private class GroupLabelAction implements MouseListener {
        private String groupName;

        public GroupLabelAction(String groupName) {
            this.groupName=groupName;
        }
        @Override
        public void mouseClicked(MouseEvent e) {
            if (groupChatGUIMap.containsKey(groupName)) {
                GroupChatGUI groupChatGUI=groupChatGUIMap.get(groupName);
                groupChatGUI.getFrame().setVisible(true);
            }else {
                //  获得所有的群聊成员
                Set names=groupMap.get(groupName);
                GroupChatGUI groupChatGUI=new GroupChatGUI(groupName,names,userName,connect2Server);
                groupChatGUIMap.put(groupName,groupChatGUI);
            }
        }

        @Override
        public void mousePressed(MouseEvent e) {

        }

        @Override
        public void mouseReleased(MouseEvent e) {

        }

        @Override
        public void mouseEntered(MouseEvent e) {

        }

        @Override
        public void mouseExited(MouseEvent e) {

        }
    }

分析
客户端
群聊发起者 
1.点击群名称标签,弹出群聊界面
2.将群聊内容发送到服务端
// type:4
// content:senderName-msg
// to:groupName

package com.lin.client.service;

import com.lin.util.CommUtils;
import com.lin.vo.MessageVO;

import javax.swing.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import java.util.Set;

/**
 * Description:
 * Author:  llf
 * Created in 2019/8/29 20:30
 */
public class GroupChatGUI {
    private JPanel groupPanel;
    private JTextArea readFromServer;
    private JTextField send2Server;
    private JPanel friendsPanel;

    private String groupName;
    private Set friends;
    private String myName;
    private Connect2Server connect2Server;

    private JFrame frame;

    public GroupChatGUI(String groupName,
                        Set friends,
                        String myName,
                        Connect2Server connect2Server) {
        frame= new JFrame("GroupChatGUI");
        frame.setContentPane(groupPanel);
        frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setSize(400,400);
        frame.setVisible(true);

        friendsPanel.setLayout(new BoxLayout(friendsPanel,
                BoxLayout.Y_AXIS));
        Iterator iterator=friends.iterator();
        while (iterator.hasNext()) {
            String lableName=iterator.next();
            JLabel jLabel=new JLabel(lableName);
            friendsPanel.add(jLabel);
        }
        send2Server.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                StringBuilder sb=new StringBuilder();
                sb.append(send2Server.getText());
                //捕捉回车按键
                if (e.getKeyCode()==KeyEvent.VK_ENTER) {
                    String str2Server=sb.toString();
                    /**
                     * type:4
                     * content:myName-msg
                     * to:groupName
                     */
                    MessageVO messageVO=new MessageVO();
                    messageVO.setType("4");
                    messageVO.setContent(myName+"-"+str2Server);
                    messageVO.setTo(groupName);
                    try {
                        PrintStream out=new PrintStream(connect2Server.getOut(),true,"UTF-8");
                        out.println(CommUtils.object2Json(messageVO));
                        send2Server.setText("");
                    } catch (UnsupportedEncodingException e1) {
                        e1.printStackTrace();
                    }

                }

            }
        });
    }

    public void readFromServer(String msg) {
        readFromServer.append(msg+"\n");
    }

    public JFrame getFrame() {
        return frame;
    }
}

服务端 
1.收到群聊信息,解出群名
2.根据群名称找到该群的所有用户的用户名
3.再根据用户名找到该用户的Socket,发送群聊信息

// type:4
// content:senderName-msg
// to:groupName-[该群的所有群成员]

 

  @Override
        public void run() {
            while (true) {
                if (in.hasNextLine()) {
                    String jsonStrFromClient = in.nextLine();
                    MessageVO msgFromClient = (MessageVO) CommUtils.json2Object(jsonStrFromClient, MessageVO.class);
                    if (msgFromClient.getType().equals("1")) {
                        //登陆成功逻辑
                    }else if (msgFromClient.getType().equals("2")) {
                        //用户私聊 
                    }else if (msgFromClient.getType().equals("3")) {
                        //创建群组
                    }else if (msgFromClient.getType().equals("4")) {
                        // type:4
                        // content:myName-msg
                        // to:groupName
                        String groupName=msgFromClient.getTo();
                        Set names=groups.get(groupName);
                        Iterator iterator=names.iterator();
                        while (iterator.hasNext()) {
                            String socketName=iterator.next();
                            Socket client=clients.get(socketName);
                            try {
                                PrintStream out=new PrintStream(client.getOutputStream(),true,"UTF-8");
                                /**
                                 * typr=4
                                 * content直接转发
                                 * 组名-所有的群成员
                                 */
                                MessageVO messageVO=new MessageVO();
                                messageVO.setType("4");
                                messageVO.setContent(msgFromClient.getContent());
                                messageVO.setTo(groupName+"-"+CommUtils.object2Json(names));
                                out.println(CommUtils.object2Json(messageVO));
                                System.out.println("服务器发送的群聊消息为:"+messageVO);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }

群聊接收者
1.当第一次收到群信息,先刷新本客户端的群聊列表信息
2.弹出群聊界面

 private class DaemonTask implements Runnable {
        private Scanner in = new Scanner(connect2Server.getIn());

        @Override
        public void run() {
            while (true) {
                if (in.hasNextLine()) {
                    //接收服务到发来的信息
                    String strFromServer = in.nextLine();
                    //此时服务器端发来一个Json字串
                    if (strFromServer.startsWith("{")) {
                        MessageVO messageVO = (MessageVO) CommUtils.json2Object(strFromServer, MessageVO.class);
                        if (messageVO.getType().equals("2")) {
                            //私聊客户端逻辑
                        }else if (messageVO.getType().equals("4")) {
                            /**
                             * 收到服务器发来的群聊信息
                             * type
                             * content:sender-msg
                             * to:groupName-[]
                             */
                            String groupName = messageVO.getTo().split("-")[0];
                            String senderName = messageVO.getContent().split("-")[0];
                            String groupMsg = messageVO.getContent().split("-")[1];
                            //若此名称在群聊列表
                            if (groupMap.containsKey(groupName)) {
                                if (groupChatGUIMap.containsKey(groupName)) {
                                    //缓存中有群聊界面  弹出
                                    GroupChatGUI groupChatGUI = groupChatGUIMap.get(groupName);
                                    groupChatGUI.getFrame().setVisible(true);
                                    groupChatGUI.readFromServer(senderName+"说:"+groupMsg);
                                }else {
                                    //缓存中没有
                                    Set names=groupMap.get(groupName);
                                    GroupChatGUI groupChatGUI=new GroupChatGUI(groupName,names,userName,connect2Server);
                                    groupChatGUIMap.put(groupName,groupChatGUI);
                                    groupChatGUI.readFromServer(senderName+"说:"+groupMsg);
                                }
                            }else {
                                /**
                                 * 用户第一次收到群聊消息
                                 */
                                Set friends= (Set) CommUtils.json2Object(messageVO.getTo().split("-")[1],
                                        Set.class);
                                groupMap.put(groupName,friends);
                                loadGroupList();
                                //弹出群聊界面
                                GroupChatGUI groupChatGUI = new GroupChatGUI(groupName,
                                        friends,userName,connect2Server);
                                groupChatGUIMap.put(groupName,groupChatGUI);
                                groupChatGUI.readFromServer(senderName+"说:"+groupMsg);
                            }
                        }
                    } else {
                        if (strFromServer.startsWith("newLogin:")) {               //**********************   好友上线逻辑
                            String newFriendName = strFromServer.split(":")[1];
                            users.add(newFriendName);
                            JOptionPane.showMessageDialog(frame,
                                    newFriendName + "上线了!", "上线提醒", JOptionPane.INFORMATION_MESSAGE);
                            //刷新好友列表
                            loadUsers();
                        }

                    }
                }
            }
        }

    }

 

 

你可能感兴趣的:(JavaGUI--模拟QQ聊天界面私聊群聊)