项目实战系列一:【多用户及时通信系统】


在这里插入图片描述

1. QQ用户登录

在这里插入图片描述

1.1 用户登录1

  • qqcommon包下
    User类序列化
    在这里插入图片描述
    Message消息类序列化
    在这里插入图片描述
    MessType接口
    在这里插入图片描述
  • qqclient.utils包下
    Utility.java工具类
  • qqclient.view包下
public class QQView {
    private User u = new User();
    private String key = "";//接受用户键盘输入
    private boolean loop = true;//控制循环

    public static void main(String[] args) {
        new QQView().mainMenu();
        System.out.println("客户端退出");
    }

    public void mainMenu() {
        while (loop) {
            System.out.println("==============欢迎登陆网络通信系统==============");
            System.out.println("\t\t1 登录系统");
            System.out.println("\t\t9 退出系统");
            System.out.print("请输入你的选择: ");
            key = Utility.readString(1);
            switch (key) {
                case "1":
                    System.out.print("请输入你的用户名: ");
                    String userId = Utility.readString(50);
                    System.out.print("请输入你的密码: ");
                    String password = Utility.readString(50);
                    //设计一个类,验证用户名密码
                    if (false) {
                        System.out.println("==============欢迎(用户 " + userId + " 登录成功)==============");
                        //进入二级菜单
                        while (loop) {
                            System.out.println("\n==============网络通信系统二级菜单(用户 " + userId + " )==============");
                            System.out.println("\t\t1 显示在线用户列表");
                            System.out.println("\t\t2 群发消息");
                            System.out.println("\t\t3 私聊消息");
                            System.out.println("\t\t4 发送文件");
                            System.out.println("\t\t9 退出系统");
                            System.out.print("请输入你的选择: ");
                            key = Utility.readString(1);
                            switch (key) {
                                case "1":
                                    System.out.println("显示在线用户列表");
                                    break;
                                case "2":
                                    System.out.println("群发消息");
                                    break;
                                case "3":
                                    System.out.println("私聊消息");
                                    break;
                                case "4":
                                    System.out.println("发送文件");
                                    break;
                                case "9":
                                    System.out.println("退出系统");
                                    loop = false;
                                    break;
                            }
                        }
                    } else {
                        System.out.println("登录失败");
                    }
                    break;
                case "9":
                    System.out.println("退出系统");
                    loop = false;
                    break;
            }
        }
    }
}

1.2 用户登录2

客户端UserClientServer类
该类用于完成用户登录验证和用户注册等功能;

public class UserClientServer {

    private User u = new User();
    private Socket socket;

    public boolean checkUser(String userId, String password) {
        //创建一个user对象
        u.setUserId(userId);
        u.setPassword(password);
        boolean b = false;

        try {
            //连接到服务端
            socket = new Socket(InetAddress.getLocalHost(), 9999);
            //获取socket关联的对象输出流
            ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
            oos.writeObject(u);

            ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
            Message message = (Message) ois.readObject();
            if (message.getMessType().equals(MessageType.MESSAGE_LOGIN_SUCCEED)) {
                //启动线程,维护socket
                ClientConnectServerThread clientConnectServerThread = new ClientConnectServerThread(socket);
                clientConnectServerThread.start();
                //集合,管理线程
                ManageClientConnectServerThread.addClientConnectServerThread(userId, clientConnectServerThread);
                b = true;
            } else {
                //如果连接失败,关闭已开启的socket
                socket.close();
                System.out.println();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return b;
    }
}

客户端线程类

public class ClientConnectServerThread extends Thread {
    //socket属性
    private Socket socket;
    //构造器socket赋值
    public ClientConnectServerThread(Socket socket) {
        this.socket = socket;
    }
    //得到socket
    public Socket getSocket() {
        return socket;
    }

    @Override
    public void run() {
        while (true) {

            try {
                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                Message message = (Message) ois.readObject();
                //做其它的处理

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

客户端管理线程的类
在这里插入图片描述

1.3 用户登录3

  • qqserver.server包下
    服务端,QQServer类
public class QQServer {
    private ServerSocket ss = null;
    public QQServer() {
        //端口可以写在配置文件
        try {
            System.out.println("服务端在9999端口监听");
            ss = new ServerSocket(9999);
            while (true) {
                Socket socket = ss.accept();
                //读取User对象
                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                //输出Message对象
                ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                User u = (User) ois.readObject();
                Message message = new Message();
                if ("100".equals(u.getUserId()) && "123456".equals(u.getPassword())) {
                    message.setMessType(MessageType.MESSAGE_LOGIN_SUCCEED);
                    oos.writeObject(message);
                    //登陆成功后,创建线程和客户端保持通信
                    ServerConnectClientThread serverConnectClientThread = new ServerConnectClientThread(socket, u.getUserId());
                    serverConnectClientThread.start();
                    //把该线程对象放入集合中管理
                    ManageClientThreads.addClientThread(u.getUserId(), serverConnectClientThread);
                } else {//登录失败
                    message.setMessType(MessageType.MESSAGE_LOGIN_FAIL);
                    oos.writeObject(message);
                    socket.close();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                ss.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

服务端线程类
在这里插入图片描述
ManageClientThreads管理线程类
在这里插入图片描述

  • qqframe包下
    QQFrame类中启动程序

让多个用户登陆服务端,不再是"100","123456"这一个用户
在这里插入图片描述
紧接着专门写一个方法验证用户名和密码
在这里插入图片描述
单点登录
在这里插入图片描述
在这里插入图片描述

2. 拉取在线用户

补充MessageType
在这里插入图片描述
在客户端UserClientServer类中定义请求在线用户列表方法,并对服务端发出请求
在这里插入图片描述
在这里插入图片描述
在客户端的线程类中等待服务端发送数据
在这里插入图片描述
服务端代码
获取在线用户列表,即要遍历ManageClientThreads类中的hm集合,将这段遍历集合的代码封装进ManageClientThreads类中的getOnlineUser()方法
在这里插入图片描述
在服务端的线程类中接收请求用户列表的消息请求,调用getOnlineUser()方法得到在线用户列表,通过socket将数据返回给客户端
在这里插入图片描述

3. 无异常退出

在这里插入图片描述
在UserClientServer类中编写一个方法,客户端退出系统,并给服务器发送一个退出系统的message
在这里插入图片描述
在服务端响应客户端的请求,接收客户端发送的message,进行处理
在这里插入图片描述

4. 私聊系统

在这里插入图片描述

  • 客户端server包下新建MessageClientServer类
    该类用于提供和消息相关的服务方法
    在MessageClientServer类中编写一个方法并调用
    在这里插入图片描述
    在这里插入图片描述

在服务端的通信线程中接收这个message并进行转发
在这里插入图片描述
在客户端把从服务端转发过来的消息显示
在这里插入图片描述

5. 群聊

在客户端MessageClientServer类中编写一个方法,并调用;向服务端发送一个请求
在这里插入图片描述
在这里插入图片描述
在服务端的通信线程中接收这个message并进行转发(群发)
在这里插入图片描述
在客户端把从服务端转发过来的消息显示
在这里插入图片描述

3. 发送文件

在这里插入图片描述
补充MessageType
在这里插入图片描述
Message类扩充
在这里插入图片描述

  • server包下新建FileClientServer类
    该类/对象 完成文件传输服务
public class FileClientServer {
    /**
     * @param src 源文件
     * @param dest 把该文件传输到对方的哪个目录
     * @param sender 发送用户id
     * @param getter 接受用户id
     */
    public void sendFileToOne(String src, String dest, String sender, String getter) {
        //读取src文件 封装到message对象
        Message message = new Message();
        message.setMessType(MessageType.MESSAGE_FILE_MES);
        message.setSender(sender);
        message.setGetter(getter);
        message.setSrcPath(src);
        message.setDestPath(dest);
        //需要将文件读取
        BufferedInputStream bis = null;
        byte[] fileBytes = new byte[(int) new File(src).length()];//获取文件的大小(Long 字节)

        try {
            bis = new BufferedInputStream(new FileInputStream(src));
            bis.read(fileBytes);
            //将文件对应的字节数组设置到message对象
            message.setFileBytes(fileBytes);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        //提示信息
        System.out.println("\n" + sender + "给 " + getter + " 发送文件:" +
                src + " 到对方的电脑目录" + dest);

        try {
            //发送
            ClientConnectServerThread clientConnectServerThread
                    = ManageClientConnectServerThread.getClientConnectServerThread(sender);
            Socket socket = clientConnectServerThread.getSocket();
            ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
            oos.writeObject(message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

服务端接收这个消息并转发
在这里插入图片描述
客户端接收到转发消息,输出到控制台
在这里插入图片描述
客户端调用
在这里插入图片描述

3.1 服务端推送新闻

  • qqserver包下新建SendNewsToAllServer类
public class SendNewsToAllServer implements Runnable {
    @Override
    public void run() {
        while (true) {
            System.out.print("请输入服务器要推送的新闻[exit退出推送新闻的线程]: ");
            String news = Utility.readString(1000);
            //构建一个消息
            Message message = new Message();
            message.setSender("服务器");
            message.setContent(news);
            message.setMessType(MessageType.MESSAGE_TO_ALL_MES);
            message.setSendTime(new Date().toString());
            System.out.println("服务器推送消息给所有人 说: " + news);

            //遍历当前所有的通信线程,得到Socket,并发送message
            Map<String, ServerConnectClientThread> hm = ManageClientThreads.getHm();

            Collection<ServerConnectClientThread> serverConnectClientThreads
                    = hm.values();
            Iterator<ServerConnectClientThread> iterator =
                    serverConnectClientThreads.iterator();
            while (iterator.hasNext()) {
                ServerConnectClientThread serverConnectClientThread = iterator.next();
                Socket socket = serverConnectClientThread.getSocket();
                try {
                    ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                    oos.writeObject(message);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

在QQServer类启动线程
在这里插入图片描述

3.2 离线留言和离线发文件

  1. 在ManageOffLIneUserMessage类中把添加离线消息这一功能进行封装
    在这里插入图片描述
    群聊消息
    当用户向所有的用户发送群聊消息的时候,给离线状态的用户设置留言
    在这里插入图片描述
    私聊消息
    当用户向某一离线用户发送私聊消息的时候,给这个离线用户设置留言
    在这里插入图片描述
    离线文件
    当用户向某一离线用户发送文件的时候,将文件暂存在服务端中,当用户上线时,得到message
    在这里插入图片描述
  2. 当离线用户登录的时候,登录成功后,服务器统一将离线消息(群聊和私聊)和离线文件发送给用户,将这一功能封装进ManageOffLIneUserMessage类
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~ 小团子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值