借助Socket、多线程,快速写出聊天室(含源码)


不说废话,先放一波效果图,展示部分给功能效果,想了解所有代码功能可以自己运行代码体验。
在这里插入图片描述 在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

感觉不错,就看下去吧!

一、前置知识

  1. Socket是什么呢?
    Socket俗称套接字,是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。Scoket根据IP+端口确定连接。

  2. Socket怎么使用呢?
    在这里插入图片描述

  3. 为什么要是使用多线程?

    ​ 有时候我们就是需要去同时做一件事情,所以为了达到这个目的,多线程也就应运而生了。就本项目而言当服务器调用accept()方法后线程就会阻塞等待连接,但我们的程序不能卡住不动,所以我们就需要使用到多线程。

二、设计实现

项目结构

在这里插入图片描述

项目精华——工具类

文件工具类

public class MyFileUtils {
    /**
     * 导出文件
     * @param msg 导出内容
     * @param file 目标文件
     */
    public static void writeFile(String msg, File file){
        if(!file.exists()){
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        try {
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            BufferedOutputStream outputStream = new BufferedOutputStream(fileOutputStream);
            byte[] bytes = msg.getBytes(StandardCharsets.UTF_8);
            outputStream.write(bytes,0,bytes.length);
            outputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

面板信息展示工具类

public class MsgShowUtils {
    /**
     *
     * @param jTextPane 显示信息的板面
     * @param msg 需要显示的信息
     * @param color 字体颜色
     * @param size 字体大小
     * @param alignment 对齐方式 0:左对齐;  1:居中;  2:右对齐
     * @throws BadLocationException
     */
    public static void setMsgStyle(JTextPane jTextPane, String msg, Color color, Integer size, Integer alignment)
            throws BadLocationException {
        StyledDocument doc = jTextPane.getStyledDocument();
        SimpleAttributeSet set = new SimpleAttributeSet();
        // 设置文本颜色
        if(color==null){
            StyleConstants.setForeground(set, Color.BLACK);
        }else {
            StyleConstants.setForeground(set, color);
        }
        // 设置文本大小
        if(size==null){
            StyleConstants.setFontSize(set, 14);
        }else {
            StyleConstants.setFontSize(set, size);
        }
        // 设置文本对齐方式
        if(alignment==null){
            StyleConstants.setAlignment(set, 0);
        }else {
            StyleConstants.setAlignment(set, alignment);
        }
        doc.setParagraphAttributes(jTextPane.getText().length(), doc.getLength() - jTextPane.getText().length(), set,
                false);
        doc.insertString(doc.getLength(), msg, set);
    }
}

项目灵魂——实现流程

为了更好的实现功能的实现,可以将消息提进行封装,根据消息属性进行不同的处理操作

public class MsgData implements Serializable {

    private static final long serialVersionUID = 1L;
    /**
     * 消息类型
     */
    private String msgType;
    /**
     * 发送者
     */
    private String sender;
    /**
     * 接收者
     */
    private String recipient;
    /**
     * 内容
     */
    private String content;
    /**
     * 消息发送时间
     */
    private String sendTime;
}

登录

根据用户名、密码,判断用户类型进行页面跳转

  1. 用户名密码相同则视为正确
  2. 用户名密码皆为cnlbc,则默认为管理员跳转至服务器页面

关键代码实现

private void toLogin() {
    String name = username.getText().trim();
    String pass = new String(password.getPassword());
    // 判空,空信息不能提交
    if(Objects.equals("",name) || Objects.equals("",pass.trim())){
        tip.setText("请填写信息后登录");
        return;
    }
    // 设计简单的登录验证规则,用户名密码相同则视为正确
    if (Objects.equals(name, pass.trim())) {
        if(Objects.equals("cnlbc", name)){
            new ServerView().setVisible(true);
        }else {
            new ClientView(name).setVisible(true);
        }
        this.setVisible(false);
    } else {
        tip.setText("用户名或密码错误");
    }
}

服务端

根据接受到的消息类型采取不同的处理方式

if (msgData != null) {
    if (Objects.equals(msgData.getMsgType(), StringConstant.LOGIN_TYPE)) {
        //登录处理
        ServerView.CLIENT_MAP.put(msgData.getSender(), client);
        fillList();
        StringBuffer userList = new StringBuffer();
        for (Map.Entry<String, Socket> entry : ServerView.CLIENT_MAP.entrySet()) {
            if (!Objects.equals(entry.getKey(), msgData.getSender())) {
                userList.append(entry.getKey()+",");
            }
        }
        msgData.setContent(userList.toString());
        objectOutputStream = new ObjectOutputStream(client.getOutputStream());
        objectOutputStream.writeObject(msgData);
        objectOutputStream.flush();

        msgData.setMsgType(StringConstant.SYSTEM_UP);
        msgData.setContent(msgData.getSender() + "用户上线了");
        msgData.setSendTime(MyDateUtils.getDate());
        userSendPublicMsg(msgData);
    } else if (Objects.equals(msgData.getMsgType(), StringConstant.PUBLIC_TYPE)) {
        //群发消息处理
        userSendPublicMsg(msgData);
        String s = msgData.getSender() + "对所有人说:" + msgData.getContent() + " " + msgData.getSendTime()
            + StringConstant.END_LINE;
        MsgShowUtils.setMsgStyle(msgPane, s, null, null, null);
    } else if (Objects.equals(msgData.getMsgType(), StringConstant.PRIVATE_TYPE)) {
        //私聊处理
        String s = msgData.getSender() + "对" + msgData.getRecipient() + "说:" + msgData.getContent()
            + " " + msgData.getSendTime() + StringConstant.END_LINE;
        MsgShowUtils.setMsgStyle(msgPane, s, Color.BLUE, null, null);
        userSendPrivateMsg(msgData);
    } else if (Objects.equals(msgData.getMsgType(), StringConstant.SYSTEM_DOWN)) {
        //退出消息处理
        msgData.setMsgType(StringConstant.SYSTEM_DOWN);
        msgData.setContent("用户:" + msgData.getSender() + "退出聊天室" + StringConstant.END_LINE);
        MsgShowUtils.setMsgStyle(msgPane, msgData.getContent(), Color.RED, null, null);
        userQuit(msgData);
    }
}

客户端

根据接收到的消息类型进行不同展示

if (Objects.nonNull(msgData)) {
    if (Objects.equals(msgData.getMsgType(), StringConstant.LOGIN_TYPE)) {
        // 登录处理
        JOptionPane.showMessageDialog(null, "加入成功", "提示", -1);
    } else if (Objects.equals(msgData.getMsgType(), StringConstant.SYSTEM_UP)) {
        String s = "系统提示:" + msgData.getSender() + "用户上线了 " + msgData.getSendTime()
            + StringConstant.END_LINE;
        MsgShowUtils.setMsgStyle(msgPane, s, Color.RED, 15, 1);
    } else if (Objects.equals(msgData.getMsgType(), StringConstant.SYSTEM_DOWN)) {
        // 下线提示消息
        String s = "";
        if(name.equals(msgData.getRecipient())){
            s = "系统提示:" + "您已被强制下线 " + msgData.getSendTime()
                + StringConstant.END_LINE;
            close();
        }else {
            s = "系统提示:" + msgData.getSender() + "用户下线了 " + msgData.getSendTime()
                + StringConstant.END_LINE;
        }
        MsgShowUtils.setMsgStyle(msgPane, s, Color.RED, 15, 1);
    } else if (Objects.equals(msgData.getMsgType(), StringConstant.SYSTEM_TYPE)) {
        // 系统消息
        String s = "";
        System.out.println(msgData.getRecipient());
        System.out.println(name);
        if(name.equals(msgData.getRecipient())){
            s = "系统悄悄告诉你:" + msgData.getContent() + " " + msgData.getSendTime() + StringConstant.END_LINE;
        }else {
            s = "系统提示:" + msgData.getContent() + " " + msgData.getSendTime() + StringConstant.END_LINE;
        }
        MsgShowUtils.setMsgStyle(msgPane, s, Color.RED, 15, 1);
    } else if (Objects.equals(msgData.getMsgType(), StringConstant.QUIT_TYPE)) {
        // 系统关闭消息
        String s = "系统提示:服务器停止服务 " + msgData.getSendTime() + StringConstant.END_LINE;
        MsgShowUtils.setMsgStyle(msgPane, s, Color.RED, 18, 1);
    } else {
        // 其他消息(群聊、私聊)
        MsgShowUtils.setMsgStyle(msgPane, msgData.getSendTime() + StringConstant.END_LINE, null, null,
                                 null);
        MsgShowUtils.setMsgStyle(msgPane,
                                 msgData.getSender() + ": " + msgData.getContent() + StringConstant.END_LINE, null, null,
                                 null);
    }
}

建议:虽然该项目代码能够完整运行,但不建议大家拿去直接用,可以理理流程,加些功能点,如:文件传输、客户端之间的私聊等。本项目中有封装好的工具类、消息实体类,方便大家根据自己的需求进行整改。希望每个小项目都能有收获。

三、完整源码获取方式

关注微信公众号 菜鸟乐编程,然后发送:源码04Java聊天室

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值