Java网络编程仿QQ聊天系统

本文介绍了使用Java实现的聊天系统,包括用户在线列表获取、群发消息、私聊、文件传输功能,以及用户登出操作。通过多线程和网络编程展示了如何构建一个简单的前后端交互应用。

项目演示视频

项目下载地址:后端+Java+JAVA聊天系统源码+练习使用-Java文档类资源-优快云文库

也可以关注我之后私信我留个邮箱留言要哪个源码,我会把源码

免费发送到你的邮箱。

简介

这是自学java写的第二个小项目,已经实现了基本聊天需要的功能。程序有一个服务器端,多个客户端。账号是在服务器端的静态代码块里定义的,测试的时候写了3个账号。

    /*
    HashMap没有处理线程安全,因此在多线程情况下是不安全的
    ConcurrentHashMap 处理的线程安全,即线程同步处理。在多线程情况下是安全的
     */
    // 创建一个集合,存放多个用户,如果是这些用户登录,就认为是登录合法的
    private static ConcurrentHashMap<String, User> validUsers = new ConcurrentHashMap<>();

    // 在静态代码块中,初始化validUsers
    static {
        validUsers.put("唐僧", new User("唐僧", "1"));
        validUsers.put("孙悟空", new User("孙悟空", "1"));
        validUsers.put("100", new User("100", "1"));
    }


用到的java技术

  1. 项目框架设计
  2. java面向对象编程
  3. 网络编程
  4. 多线程
  5. I/O流
  6. Mysql/这里是使用集合充当内存数据库


程序功能介绍

1.获取在线用户列表

//向服务器端请求在线用户列表
    public void onlineFriendList() {
        //发送一个Message  类型MESSAGE_GET_ONLINE_FRIEND
        Message message = new Message();
        message.setMesType(MessageType.MESSAGE_GET_ONLINE_FRIEND);
        message.setSender(user.getUserId());
        //发送给服务器端

        try {
            //从管理线程的集合中,通过userId, 得到这个线程对象
            ClientConnectServerThread ccst =
                    ManageClientConnectServerThread.getClientConnectServerThread(user.getUserId());
            Socket socket = ccst.getSocket();
            ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
            oos.writeObject(message);//发送一个Message对象,向服务端要求在线用户列表
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

客户端主要代码

// 判断客户端发来的是什么类型的消息,然后进行一系列的操作
if (message.getMesType().equals(MessageType.MESSAGE_GET_ONLINE_FRIEND)) {
                    System.out.println(message.getSender() + "要在线用户列表");
                    String onlineUser = ManageClientThreads.getOnlineUser();
                    //返回message
                    //构建一个Message对象,返回给客户端
                    Message message2 = new Message();
                    message2.setMesType(MessageType.MESSAGE_RET_ONLINE_FRIEND);
                    message2.setContent(onlineUser);
                    message2.setSender(message.getSender());
                    //返回给客户端
                    ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                    oos.writeObject(message2);
                } 

服务器端主要代码

简单来说就是客户端封装一个消息类发送给服务端,然后服务端进行遍历在线用户集合返回给客户端的操作。

2.群发消息

public void sendMessageToAll(String content, String senderId) {
        //构建message
        Message message = new Message();
        //群发聊天类型
        message.setMesType(MessageType.MESSAGE_TO_ALL_MES);
        message.setSender(senderId);//设置发送者id
        message.setContent(content);//设置发送内容
        //发送时间设置到message对象
        message.setSendTime(new Date().toString());
        System.out.println("\n" + senderId + "对大家说:" + content);
        try {
            //发送给服务端
            ObjectOutputStream oos = new ObjectOutputStream(ManageClientConnectServerThread
                    .getClientConnectServerThread(senderId)
                    .getSocket().getOutputStream());
            oos.writeObject(message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

客户端主要代码

if (message.getMesType().equals(MessageType.MESSAGE_TO_ALL_MES)) {
                    //需要遍历 管理线程的集合 把所有的线程的socket得到,然后把message进行转发即可
                    HashMap<String, ServerConnectClientThread> hm = ManageClientThreads.getHm();
                    Iterator<String> iterator = hm.keySet().iterator();
                    while (iterator.hasNext()) {
                        //取出在线用户id
                        String onLineUserId = iterator.next().toString();
                        if (!onLineUserId.equals(message.getSender())) {
                            //进行转发message
                            ObjectOutputStream oos = new ObjectOutputStream
                                    (hm.get(onLineUserId).getSocket().getOutputStream());
                            oos.writeObject(message);
                         }
                    }
                }

服务端主要代码

服务器接收到发来的消息类型,进行判断,之后遍历管理线程的集合,把所有的线程的socket得到,然后把message进行转发即可

3.私聊消息

/**
     * @param content  内容
     * @param senderId 发送用户id
     * @param getterId 接收用户id
     */
    public void sendMessageToOne(String content, String senderId, String getterId) {
        //构建message
        Message message = new Message();
        //普通聊天类型
        message.setMesType(MessageType.MESSAGE_COMM_MES);
        message.setSender(senderId);
        message.setGetter(getterId);
        message.setContent(content);
        //发送时间设置到message对象
        message.setSendTime(new Date().toString());
        System.out.println(senderId + "对" + getterId + "说" + content);
        try {
            //发送给服务端
            ObjectOutputStream oos = new ObjectOutputStream(ManageClientConnectServerThread
                    .getClientConnectServerThread(senderId)
                    .getSocket().getOutputStream());
            oos.writeObject(message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

客户端主要代码

if (message.getMesType().equals(MessageType.MESSAGE_COMM_MES)) {
                    //根据messgae获取getter id,让后在得到对应的线程
                    ServerConnectClientThread scct =
                            ManageClientThreads.getServerConnectClientThread(message.getGetter());
                    //得到对应socket的对象输出流。将message对象转发给指定的客户端
                    ObjectOutputStream oos = new ObjectOutputStream(scct.getSocket().getOutputStream());
                    //TODO 如果客户不在线可以保存到数据库,这样就可以实现离线留言
                    oos.writeObject(message);

服务器端主要代码

服务端获取到要私聊的对象线程,返回给客户端。

4.发送文件

/**
     * @param src      文件源路径
     * @param dest     文件传输目标路径
     * @param senderId 文件发送者
     * @param getterId 文件接收者
     */
    public void sendFileToOne(String src, String dest, String senderId, String getterId) {
        // 读取src文件 -->message
        Message message = new Message();
        message.setMesType(MessageType.MESSAGE_FILE_MES);
        message.setSender(senderId);
        message.setGetter(getterId);
        message.setSrc(src);
        message.setDest(dest);
        // 需要将文件读取
        FileInputStream fileInputStream = null;
        byte[] fileBytes = new byte[(int) new File(src).length()];

        try {
            fileInputStream = new FileInputStream(src);
            //将src文件读入到程序的字节数组
            fileInputStream.read(fileBytes);
            //将文件对应的字节数组设置message
            message.setFileBytes(fileBytes);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //发送
            try {
                ObjectOutputStream oos = new ObjectOutputStream(ManageClientConnectServerThread
                        .getClientConnectServerThread(senderId).getSocket().getOutputStream());
                oos.writeObject(message);

            } catch (IOException e) {
                e.printStackTrace();
            }
            //提示信息
            System.out.println("\n" + senderId + "给" + getterId +
                    "发送文件:" + src + "到对方的电脑的目录" + dest);
        }
    }

客户端主要代码

if (message.getMesType().equals(MessageType.MESSAGE_FILE_MES)) {
                    // 根据getter id获取到对应的线程,将message对象转发
                    ObjectOutputStream oos = new ObjectOutputStream(ManageClientThreads
                            .getServerConnectClientThread(message.getGetter())
                            .getSocket().getOutputStream());
                    // 转发
                    oos.writeObject(message);
                }

服务端主要代码

客户端将要发送的文件保存到一个·字节数组里,然后发送给服务端。

5.登出

public void logout() {
        Message message = new Message();
        message.setMesType(MessageType.MESSAGE_CLIENT_EXIT);
        message.setSender(user.getUserId());

        try {
            ObjectOutputStream oos = new ObjectOutputStream(ManageClientConnectServerThread
                    .getClientConnectServerThread(user.getUserId())
                    .getSocket().getOutputStream());
            oos.writeObject(message);
            System.out.println(user.getUserId() + "退出系统");
            //结束进程
            System.exit(0);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

客户端主要代码

if (message.getMesType().equals(MessageType.MESSAGE_CLIENT_EXIT)) {
                    System.out.println(message.getSender() + "退出");
                    // 将这个客户端对应线程,从集合中删除
                    ManageClientThreads.removeServerConnectClientThread(message.getSender());
                    socket.close();
                    //退出循环,关闭线程
                    break;
                }

服务端主要代码

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

冰忆往昔

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

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

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

打赏作者

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

抵扣说明:

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

余额充值