QQ简易版

package QQ;
/*
* 
* 登录界面
* */
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;


public  class QQLogin extends JFrame {

    public  QQLogin() {
        setTitle("登录");
        setSize(300, 200);
        setLocationRelativeTo(null);//设置居中
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(null);
        JLabel usernameText = new JLabel("账号");
        usernameText.setBounds(30, 30, 60, 30);
        JLabel passwordText = new JLabel("密码");
        passwordText.setBounds(30, 60, 60, 30);
        add(usernameText);
        add(passwordText);
        final JTextField username = new JTextField();
        final JPasswordField password = new JPasswordField();
        username.setBounds(100, 30, 120, 30);
        password.setBounds(100, 60, 120, 30);
        add(username);
        add(password);
        //注册按钮
        JButton reg=new JButton("注册");
        reg.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                new QQReg().setVisible(true);//打开注册页面
                QQLogin.this.dispose();//关闭登录页面
            }
        });
        reg.setBounds(150,110,80,30);
        add(reg);

        //登录
        JButton login = new JButton("登录");
        login.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                //获取账号密码
                Message message = new Message();
                message.setType(Message.LOGIN); //获取注注册类型
                message.setUsername(username.getText());//获取输入框输入名称
                message.setPassword(new String(password.getPassword())); //获取输入框输入密码
                try {
                    Socket socket = new Socket("127.0.0.1", 8888);  //连接服务器
                    ObjectRW.writeObject(message, socket); //调用ObjectRW的静态方法writeObject 并把message, socket的对象传入进去调用方法之后传入到服务器
                    message = (Message) ObjectRW.readObject(socket);  //调用ObjectRW中的readObject方法 获取message信息
                    if (message.getType() == Message.RESULT && message.isSuccess()) {
                        //弹窗
//                        JOptionPane.showConfirmDialog(null, "登录成功", "提示", JOptionPane.YES_OPTION);
                        QQLogin.this.setVisible(false);//隐藏登录界面
                        new Friends(socket,username.getText()).setVisible(true);
//                        QQLogin.this.dispose();

                        //开启friends列表界面 并将登录时的socket和名称传过去
                    } else {
                        JOptionPane.showConfirmDialog(null, "登录失败", "提示", JOptionPane.YES_OPTION);
                    }
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        login.setBounds(50, 110, 80, 30);
        add(login);
    }

}
package QQ;
/*
*
* 注册界面
* */
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPasswordField;
import javax.swing.JTextField;


public class QQReg extends JFrame {

    public QQReg() {
        setTitle("注册");
        setSize(300, 200);
        setLocationRelativeTo(null);//设置居中
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(null);
        JLabel usernameText = new JLabel("账号");
        usernameText.setBounds(30, 30, 60, 30);
        JLabel passwordText = new JLabel("密码");
        passwordText.setBounds(30, 60, 60, 30);
        add(usernameText);
        add(passwordText);
        final JTextField username = new JTextField();
        final JPasswordField password = new JPasswordField();
        username.setBounds(100, 30, 120, 30);
        password.setBounds(100, 60, 120, 30);
        add(username);
        add(password);
        //注册按钮
        JButton reg = new JButton("注册");
        reg.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                //获取账号密码
                Message message = new Message();
                message.setType(Message.REG); //获取注注册类型
                message.setUsername(username.getText());//获取输入框输入名称
                message.setPassword(new String(password.getPassword())); //获取输入框输入密码
                try {
                    Socket socket = new Socket("127.0.0.1", 8888);  //连接服务器
                    ObjectRW.writeObject(message, socket); //调用ObjectRW的静态方法writeObject 并把message, socket的对象传入进去调用方法之后传入到服务器
                    message = (Message) ObjectRW.readObject(socket);  //调用ObjectRW中的readObject方法 获取message信息
                    if (message.getType() == Message.RESULT && message.isSuccess()) {
                        //弹窗
//                        JOptionPane.showConfirmDialog(null, "注册成功", "提示", JOptionPane.OK_OPTION);
                        new QQLogin().setVisible(true);
                        QQReg.this.dispose();

                    } else {
                        JOptionPane.showConfirmDialog(null, "注册失败", "提示", JOptionPane.YES_OPTION);
                    }
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        reg.setBounds(100, 110, 80, 30);
        add(reg);
    }

}
package QQ;

import java.io.Serializable;

public class Message implements Serializable {
    private static final long serialVersionUID = -3021746792251923405L;
    //设为对象流
    private int type = 0;//消息类型

    public static final int RESULT = 2;//完成为2

    public static final int REG = 1;//初始注册

    public static final int LOGIN = 3;//登录

    private boolean isSuccess;//是否注册成功

    public static final int REFRESH_FRIENDS = 4;//刷新好友列表
    public static final int REFRESH_FRIENDS_RESULT = 5;//刷新好友列表结果
    public static final int SINGLE_MESSAGE = 6;//一对一废话聊天
    public static final int GROUP_MESSAGE = 7;//群聊消息
    public static final  int EXIT =8;//退出程序
    private String from;//消息发送者
    private String to;//消息接受者
    private String content;//消息内容
    private String time;//发送时间

    public String getFrom() {
        return from;
    }

    public void setFrom(String from) {
        this.from = from;
    }

    public String getTo() {
        return to;
    }

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

    public String getContent() {
        return content;
    }

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

    public String getTime() {
        return time;
    }

    public void setTime(String time) {
        this.time = time;
    }

    private String[] friends;//所有好友数据

    public String[] getFriends() {
        return friends;
    }

    public void setFriends(String[] friends) {
        this.friends = friends;
    }

    public boolean isSuccess() {
        return isSuccess;
    }

    public void setSuccess(boolean isSuccess) {
        this.isSuccess = isSuccess;
    }

    public int getType() {
        return type;
    }

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

    private String username;

    private String password;

    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;
    }


}
package QQ;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

public class ObjectRW {

    /**
     * 写出对象
     * @param obj
     * @param socket
     */
    public static void writeObject(Object obj,Socket socket){
        //静态  ObjectRW.writeObject(message, socket);传入的是message对象  Object是父类形成一个多态
        try {
            ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
            oos.writeObject(obj);  //获取socket之后 写出writeObject(obj)写出到服务器的是message里面的信息
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 读取对象
     * @param socket
     * @return
     */
    public static Object readObject(Socket socket){
        try {
            ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
            return ois.readObject();   //获取socket之后  从服务器读取所有相关信息
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null; //如果没有就返回为空

    }
}
package QQ;
/*
* 好友列表
* */
import sun.applet.resources.MsgAppletViewer;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

public class Friends extends JFrame {
    private Socket socket;
    private String username;
    private JPanel friends;//一个装列表的容器
    private String[] all;//用来存放好友列表
    private Map<String,Talk> talks = new HashMap<>();//用来存放对话窗口
    // 将用户名 和对应的对话窗口放在一个集合中 方便以后调用该窗口

    public Friends(Socket socket, String username) {
        setTitle(username);
        this.socket = socket; //接受登录时自己的socket
        this.username = username; ////接受登录时自己的username
        friends = new JPanel();
        friends.setLayout(new BorderLayout());
        add(friends); //容器添加到框里去
        JButton group =new JButton("群聊"); //------------群聊按钮
        group.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                    showTalk("群聊",Talk.GROUP);
            }
        });
        add(group,BorderLayout.SOUTH);



//            第一次刷新好友列表
        Message msg = new Message(); //从登陆完成之后从内存中获取相关信息
        msg.setType(Message.REFRESH_FRIENDS);//设置为刷新类型
        ObjectRW.writeObject(msg, socket);//修改之后传入内存中--等待服务器反应
//            msg = (Message)ObjectRW.readObject(socket);//从内存中获取从服务器返回的msg包里的信息
//            if(msg.getType() == Message.REFRESH_FRIENDS_RESULT){ //判读是否获取列表成功
//                refreshFriend(msg); //进行刷新操作
//            }
        ////启动线程随时能够接收服务器发送过来的消息
        FriendUtil friendUtil = new FriendUtil(socket, this); ///--->FriendUtil
        friendUtil.start();

        setSize(150, 500);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);  //退出 告诉服务器
        //添加窗口事件
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                Message msg =new Message();
                msg.setType(Message.EXIT);
                msg.setUsername(username);
                ObjectRW.writeObject(msg,socket);
                System.exit(0);//退出程序
            }
        });


    }

    /**
     * 刷新
     *
     * @param msg
     */
    public void refreshFriend(Message msg) {
        friends.removeAll();//移除容器之前中的组件
        all =msg.getFriends();
        JList list = new JList(all);//将获取到的好友列表信息写入列表中  msg.getFriends()好友列表
        //鼠标事件 对好友列表进行操作
        list.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                //判断是否双击
                if(e.getClickCount()==2){
                    //根据鼠标位置判断点击的是第几个
                    int index = list.locationToIndex(e.getPoint());//返回一个数字
                    //all[index]根据好友列表数组得到接受者
                    //并打开聊天窗口
                    showTalk(all[index],Talk.SINGLE);
                }

            }
        });

        friends.add(list);
        friends.repaint();
        friends.updateUI();//刷新
    }

    /**
     * 显示聊天窗口
     * @param name
     */
    public Talk showTalk(String name,int type){  //聊天窗口类
        Talk talk =talks.get(name); //talks 是聊天窗口的集合 通过名字获得该窗口
        if(talk ==null){ //如果之前没有这个窗口就创建一个新的窗口
            talk = new Talk(name,socket,username,type);// --------->调用talk构造方法   打开对话窗口方法
            talks.put(name,talk); //将接受者的名字和他的窗口放进集合
        }
        if(!talk.isVisible()){  //如果该窗口没有显示才显示窗口
            talk.setVisible(true);
        }
        return talk;
    }
}
package QQ;
/*
*
*
* */
import java.net.Socket;

public class FriendUtil extends Thread {
    private Socket socket;
    private Friends friends;

    public FriendUtil(Socket socket, Friends friends) {
        this.socket = socket;
        this.friends = friends;
    }

    @Override
    public void run() {    //通过friends中线程启动随时能够接收服务器发送过来的消息
        super.run();
        while (true) {
            Message msg = (Message) ObjectRW.readObject(socket);
            if (msg.getType() == Message.REFRESH_FRIENDS_RESULT) {
                friends.refreshFriend(msg); //刷新每一个的列表
            } else if (msg.getType() == Message.SINGLE_MESSAGE) {
                Talk talk = friends.showTalk(msg.getFrom(), Talk.SINGLE);  ///---->>>friends.showTalk   msg.getFrom()发送者的名称
                //开启一个接受者的窗口并将发送者作为你的窗口的接受者
                talk.showMessage(msg.getFrom(), msg.getContent(), msg.getTime());
                //---->talk.showMessage在接受者的窗口显示接受消息
            } else if (msg.getType() == Message.GROUP_MESSAGE) {
                Talk talk = friends.showTalk("群聊", Talk.GROUP);  ///---->>>friends.showTalk   msg.getFrom()发送者的名称
                //开启一个接受者的窗口并将发送者作为你的窗口的接受者
                talk.showMessage(msg.getFrom(), msg.getContent(), msg.getTime());
            }
        }
    }
}

package QQ;
/*
* 聊天
*
* */
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Talk extends JFrame {
    private String to;
    private Socket socket;
    private String from;

    private  int type; //-----------区分群聊
    public static final int SINGLE = 1;//一对一废话聊天
    public static final int GROUP= 2;//群聊消息

    private JTextArea show;//消息接受显示框
    private JTextArea input;//消息发送显示框


    public Talk(String to, Socket socket, String from,int type) {  //从Friends中showTalk方法调用该构造方法   talk = new Talk(name,socket,username);//调用talk
        this.to = to;
        this.socket = socket; //消息内容所需要的
        this.from = from;
        this.type =type;

        setSize(200, 350);
        setLocationRelativeTo(null);
        setTitle(to);
        show = new JTextArea();
        show.setEditable(false);//设置不可编辑
        show.setBorder(BorderFactory.createLineBorder(Color.gray));//设置边框

        input = new JTextArea();
        JButton btn = new JButton("发送");//发送按钮
        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Message msg = new Message();
                if(type ==Talk.SINGLE){
                    msg.setType(Message.SINGLE_MESSAGE);//聊天类型 一对一
                }else {
                    msg.setType(Message.GROUP_MESSAGE);//聊天类型 群聊
                }

                msg.setFrom(from);
                msg.setTo(to);
                msg.setContent(input.getText());//聊天消息内容 content
                //设置发送时间
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                msg.setTime(sdf.format(new Date()));//发送时当前时间
                ObjectRW.writeObject(msg,socket); //写入通道传给服务器 ----->到服务器
                showMessage(from,input.getText(),sdf.format(new Date())); //在发送者界面显示已经发送的消息
                input.setText("");//发送信息后清空发送框

            }
        });
        JScrollPane jsc = new JScrollPane(show);
        jsc.setPreferredSize(new Dimension(0, 200));
        add(jsc,BorderLayout.NORTH);
        add(new JScrollPane(input),BorderLayout.CENTER);
        add(btn,BorderLayout.SOUTH);
        setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
    }
    /*
    * 显示消息
    * */
    public void showMessage(String name,String content,String time){
        //显示消息
        show.append(name);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        show.append("     " + time + "\r\n");
        show.append(content + "\r\n");
    }
}
package QQ;
/*
*
* 服务器端
* */
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

public class QQserver {
    ServerSocket serverSocket;
    private Map<String, Socket> sockets = new HashMap<>();//这里有线程安全的问题

    public QQserver() {
        try {
            serverSocket = new ServerSocket(8888);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /*
     *
     * 开启服务器
     * */
    public void start() {
        while (true) {
            try {
                Socket socket = serverSocket.accept();//开启
                Message msg = (Message) ObjectRW.readObject(socket);
                if (msg.getType() == Message.REG) {
                    msg.setType(Message.RESULT);
                    msg.setSuccess(reg(msg));
                    ObjectRW.writeObject(msg, socket);
                } else if (msg.getType() == Message.LOGIN) {
                    msg.setType(Message.RESULT);
                    boolean flag = login(msg);
                    msg.setSuccess(flag);
                    ObjectRW.writeObject(msg, socket);
                    if(flag){//登录成功之后进行列表信息操作
                        sockets.put(msg.getUsername(), socket);//保存其余所有登录成功时的通道和用户名到集合里面
                        //启动一个专门针对一个用户服务的线程
                        new QQservice(socket,sockets).start();
                    }

                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /*
     * 执行注册
     * */
    public boolean reg(Message msg) {

        return Sqlutil.reg(msg);
    }

    /*
     *
     * 执行登录
     * */
    public boolean login(Message msg) {


        return Sqlutil.login(msg);
    }


}
package QQ;

import java.io.IOException;
import java.net.Socket;
import java.util.Map;
import java.util.Set;

public class QQservice extends Thread {
    private Socket socket;
    private Map<String, Socket> sockets;

    public QQservice(Socket socket, Map<String, Socket> sockets) {
        this.socket = socket; //当前登录并刷新时自己的socket
        this.sockets = sockets;//其他人登录时保存的username和socket
    }

    @Override
    public void run() {
        while (true) { //刷新时需要可复用
            Message msg = (Message) ObjectRW.readObject(socket);//读取写入内存中的信息
            if (msg.getType() == Message.REFRESH_FRIENDS) {//----------判断为获取好友列表操作
                msg.setType(Message.REFRESH_FRIENDS_RESULT);//修改刷新完成类型
                msg.setFriends(getFriends());//获取好友列表并写入msg中
//                ObjectRW.writeObject(msg, socket); 只是刷新给当前登录的那个人
//                刷新给所有人
                Set<String> sets = sockets.keySet();
                for (String name : sets) { //每次循环 给每个登录的人
                    ObjectRW.writeObject(msg, sockets.get(name)); //将修改的msg和socket写回每一个人中的内存中
                }
            } else if (msg.getType() == Message.SINGLE_MESSAGE) {  //>>>>-------判断为一对一聊天操作读取talk发送过来的内容
                Socket to = sockets.get(msg.getTo()); //通过 sockets集合 接受者的sockets (连接方式)
                ObjectRW.writeObject(msg, to);//将信息发送给需要接受的人
            } else if (msg.getType() == Message.GROUP_MESSAGE) {//----------判断为群聊操作
//                ObjectRW.writeObject(msg, socket); 只是刷新给当前登录的那个人
//                刷新给所有人
                Set<String> sets = sockets.keySet();
                for (String name : sets) { //每次循环 给每个登录的人
                    if (!name.equals(msg.getFrom())) { //不需要给自己发送群聊消息
                        ObjectRW.writeObject(msg, sockets.get(name)); //将修改的msg和socket写回每一个人中的内存中
                    }
                }
            } else if (msg.getType() == Message.EXIT) {
                //退出
                //从集合中移除
                sockets.remove(msg.getUsername());
                msg.setType(Message.REFRESH_FRIENDS_RESULT);//修改刷新完成类型
                msg.setFriends(getFriends());//获取好友列表并写入msg中
                Set<String> sets = sockets.keySet();
                for (String name : sets) { //每次循环 给每个登录的人
                    ObjectRW.writeObject(msg, sockets.get(name)); //将修改的msg和socket写回每一个人中的内存中
                }
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                break;
            }
        }
    }

    /**
     * 获取所有的好友数据
     *
     * @return
     */

    public String[] getFriends() {
        Set<String> names = sockets.keySet(); //将好友列表的信息设为set集合
        String friends[] = new String[names.size()];
        names.toArray(friends); //将set中的信息写到集合中去 返回一个好友列表的集合
        return friends;
    }
}
package QQ;


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;


/*
 * 连接数据库并进行添加操作
 *
 *
 * */
public class Sqlutil {

    static {
        try {  //加载驱动  方法块
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() {
        try {  //连接数据库
            return DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false", "root", "123456");
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }


    //关闭连接
    public static void colse(ResultSet rs, PreparedStatement pst, Connection conn) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (pst != null) {
            try {
                pst.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }


    //进行注册功能并写入数据库
    public static boolean reg(Message msg) {
        String sql = "insert into user (user,password)values (?,?)";
        Connection conn = getConnection();//调用getConnection()方法
        if (conn == null) {
            System.out.println("连接问题");
            return false;
        }
        boolean flag = false;
        PreparedStatement pst = null;
        try {
            pst = conn.prepareStatement(sql);
            pst.setString(1, msg.getUsername());
            pst.setString(2, msg.getPassword());
            flag = pst.executeUpdate() > 0;
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            colse(null, pst, conn);
        }
        return flag;
    }

    public static boolean login(Message msg) {
        String sql = "select * from user where user= ? and password= ?";
        Connection conn = getConnection();//调用getConnection()方法
        if (conn == null) {
            System.out.println("连接问题");
            return false;
        }
        PreparedStatement pst = null;
        ResultSet rs = null;
        boolean flag = false;
        try {
            pst = conn.prepareStatement(sql);
            pst.setString(1, msg.getUsername());
            pst.setString(2, msg.getPassword());
            rs = pst.executeQuery();
            flag = rs.next();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            colse(rs, pst, conn);
        }
        return flag;
    }
}
package QQ;

public class main {
    public static void main(String[] args) {
//        new QQReg().setVisible(true);
        new QQLogin().setVisible(true);
    }
}
package QQ;

public class main2 {
    public static void main(String[] args) {
        new QQserver().start();
    }
}

 

posted @ 2019-04-13 21:07  后天2333  阅读(975)  评论(0编辑  收藏  举报