BIO实现一个聊天室

4 篇文章 0 订阅
这是一个使用BIO( Blocking I/O)实现的简单Java聊天室应用。客户端通过Swing创建图形界面,用户输入消息后,消息会被发送到服务器,服务器再广播给所有在线用户。客户端可以查看在线用户列表并选择私聊。代码包括客户端和服务器端,实现了群聊和私聊功能。
摘要由CSDN通过智能技术生成

BIO实现一个聊天室

需要实现一个客户端的消息可以发送给所有客户端去接受。(群聊的实现)(B站黑马程序员里的代码)

效果:

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

客户端代码

package BIO_instantmassging.client;
import BIO_instantmassging.util.Constants;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataOutputStream;
import java.net.Socket;
import java.sql.Struct;

public class ClientChat implements ActionListener {

    //界面设计
    private JFrame win = new JFrame();
    //消息内容框架
    public JTextArea smsContent = new JTextArea(23,50);
    //发送消息的框
    private JTextArea smsSend = new JTextArea(4,40);
    //在线人数的区域
    //存放人的数据
    //展示在线人数的窗口
    public JList<String> onLineUsers = new JList<>();

    //是否私聊按钮
    private JCheckBox isPrivateBn = new JCheckBox("私聊");
    //消息按钮
    private JButton sendBn = new JButton("发送");

    //登录界面
    private JFrame loginView;
    private  JTextField ipEt, nameEt, idEt;
    private Socket socket;

    public static void main(String[] args) {
        new ClientChat().initView();
    }

    private void initView() {
        //初始化聊天窗口的界面
        win.setSize(650,600);
        //展示登录界面
        displayLoginView();

    }

    private void displayLoginView() {
        /**
         * 先让用户进行登录
         * 服务端ip
         * 用户名
         * id
         */
        //显示一个登录框
        loginView = new JFrame("登录");
        loginView.setLayout(new GridLayout(3,1));
        loginView.setSize(400,230);

        JPanel ip = new JPanel();
        JLabel label = new JLabel("IP:");
        ip.add(label);
        ipEt = new JTextField(20);
        ip.add(ipEt);
        loginView.add(ip);

        JPanel name = new JPanel();
        JLabel label1 = new JLabel("姓名:");
        name.add(label1);
        nameEt = new JTextField(20);
        name.add(nameEt);
        loginView.add(name);

        JPanel btnView = new JPanel();
        JButton login = new JButton("登录");
        btnView.add(login);
        JButton cancle = new JButton("取消");
        btnView.add(cancle);
        loginView.add(btnView);
        //关闭窗口退出当前程序
        loginView.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setWindowCenter(loginView,400,260, true);

        //给登录和取消绑定点击事件
        login.addActionListener(this);
        cancle.addActionListener(this);
    }

    private void setWindowCenter(JFrame frame, int width, int height, boolean flag) {
        //得到所在系统的屏幕的宽高
        Dimension ds =frame.getToolkit().getScreenSize();

        //拿到电脑的宽
        int width1 = ds.width;
        //高
        int height1 = ds.height;

        System.out.println(width1 + "*" + height1);
        //设置窗口的左上角的坐标
        frame.setLocation(width1/2 - width/2,height1/2 - height/2);
        frame.setVisible(flag);

    }

    @Override
    public void actionPerformed(ActionEvent e) {
        //得到点击的时间源
        JButton btn = (JButton) e.getSource();
        switch (btn.getText()){
            case "登录":
                String ip = ipEt.getText().toString();
                String name = nameEt.getText().toString();
                //校验是否为空
                //错误提示
                String msg = "";
                //12.1.2.0
                //\d{1,3}\.\d{1,3}\.\d{1,3}\
                if (ip == null || !ip.matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}")){
                    msg = "请输入合法的服务器ip地址!";
                } else if(name == null || !name.matches("\\S{1,}")){
                    msg = "姓名必须1个字符以上!";
                }

                if (!msg.equals("")){
                    //msg有内容说明参数不为空
                    //参数一:弹出放到哪个窗口里面
                    JOptionPane.showMessageDialog(loginView,msg);
                }else {
                    try{
                        //参数都合法
                        //当前登录的用户,去服务端登录
                        //先把当前用户的名称展示到界面
                        win.setTitle(name);
                        //去服务端登录连接一个socket通道
                        socket = new Socket(ip, Constants.PORT);

                        //为客户端的socket分配一个线程专门负责收消息
                        new ClientReader(this,socket).start();

                        //禁止用户信息过去
                        DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
                        dos.writeInt(1);//登录信息
                        dos.writeUTF(name.trim());
                        dos.flush();

                        //关掉当前窗口 弹出聊天界面
                        loginView.dispose();//登录窗口销毁
                        displayChatView();//展示聊天窗口
                    }catch (Exception e1){
                        e1.printStackTrace();
                    }
                }
                break;
            case "取消":
                //退出系统
                System.exit(0);
                break;
            case "发送":
                //得到发送消息的内容
                String msgSend = smsSend.getText().toString();
                if (!msgSend.trim().equals("")){
                    //发消息给服务端
                    try{
                        //判断是否对谁发送
                        String selectName = onLineUsers.getSelectedValue();
                        int flag = 2;//群发消息
                        if (selectName != null && !selectName.equals("")){
                            msgSend = "@" + selectName + "," + msgSend;
                            //判断是否选中了私发
                            if (isPrivateBn.isSelected()){
                                //私发
                                flag = 3;//私发消息
                            }
                        }

                        DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
                        dos.writeInt(flag);
                        dos.writeUTF(msgSend);
                        if (flag == 3){
                            //告诉服务器我对谁私发
                            dos.writeUTF(selectName.trim());
                        }
                        dos.flush();
                    }catch (Exception e1){
                        e1.printStackTrace();
                    }
                }
                smsSend.setText(null);
                break;
        }
    }

    private void displayChatView() {
        JPanel bottomPanel = new JPanel(new BorderLayout());
        //-------------------------------------------------------
        //将消息框和按钮添加到窗口的地段
        win.add(bottomPanel, BorderLayout.SOUTH);
        bottomPanel.add(smsSend);
        JPanel btns = new JPanel(new FlowLayout(FlowLayout.LEFT));
        btns.add(sendBn);
        btns.add(isPrivateBn);
        bottomPanel.add(btns,BorderLayout.EAST);
        //-------------------------------------------------------
        //给发送消息按钮绑定点击事件监听器
        //将展示消息区centerPanel添加到窗口的中间
        smsContent.setBackground(new Color(0xdd,0xdd,0xdd));
        //让展示消息区可以滚动
        win.add(new JScrollPane(smsContent),BorderLayout.CENTER);
        smsContent.setEditable(false);
        //-----------------------------------------------------------
        //用户列表和是否私聊放到窗口的最右边
        Box rightBox = new Box(BoxLayout.Y_AXIS);
        onLineUsers.setFixedCellWidth(120);
        onLineUsers.setVisibleRowCount(13);
        rightBox.add(new JScrollPane(onLineUsers));
        win.add(rightBox,BorderLayout.EAST);
        //---------------------------------------------------------
        //关闭窗口退出当前程序
        win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        win.pack();//swing加上这句 就可以拥有关闭窗口的功能
        //设置窗口居中,显示出来
        setWindowCenter(win,650,600,true);
        //发送按钮绑定点击事件
        sendBn.addActionListener(this);
    }
}
package BIO_instantmassging.client;


import BIO_instantmassging.util.Constants;

import java.io.DataInputStream;
import java.net.Socket;

public class ClientReader extends Thread{
    private Socket socket;
    private  ClientChat clientChat;

    public ClientReader(ClientChat clientChat, Socket socket){
        this.clientChat = clientChat;
        this.socket = socket;
    }

    @Override
    public void run() {
        try{
            DataInputStream dis = new DataInputStream(socket.getInputStream());
            //循环一直等待客户端消息
            while(true){
                //读取当前的消息类型:登录,群发,私聊@消息
                int flag = dis.readInt();
                if (flag == 1){
                    //在线人数消息到达
                    String nameDatas = dis.readUTF();
                    //展示到在线人数的界面
                    String[] names = nameDatas.split(Constants.SPILIT);

                    clientChat.onLineUsers.setListData(names);
                } else if (flag == 2){
                    //群发,私聊,@消息都是直接显示的
                    String msg = dis.readUTF();
                    clientChat.smsContent.append(msg);
                    //让消息界面滚动到底部
                    clientChat.smsContent.setCaretPosition(clientChat.smsContent.getText().length());
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

package BIO_instantmassging.util;


public class Constants {

    public static final int PORT = 9999;

    public static final String SPILIT = "x";
}

服务端:

package BIO_instantmassging.server;


import BIO_instantmassging.util.Constants;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

public class ServerChat {

    /**
     * 定义一个集合存放所有在线的socket
     * 在线集合只需要一个:存储客户端docker的同时还需要知道这个Socket客户端的名称
     */
    public static Map<Socket, String> onLineSockets = new HashMap<>();

    public static void main(String[] args) {
        try {

            ServerSocket serverSocket = new ServerSocket(Constants.PORT);

            while (true){
                Socket socket = serverSocket.accept();
                new ServerReader(socket).start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

package BIO_instantmassging.server;


import BIO_instantmassging.util.Constants;
import com.allwinter.six.Server;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Set;

public class ServerReader extends Thread{
    private Socket socket;
    public ServerReader(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        DataInputStream dis = null;
        try{
            dis = new DataInputStream(socket.getInputStream());
            while (true){
                /**读取当前的消息类型:登录,群发,私聊,@消息*/
                int flag = dis.readInt();
                if (flag == 1){
                    /**先将当前登录的客户端socket存到在线人数的socket集合中*/
                    String name = dis.readUTF();
                    System.out.println(name + "---->" + socket.getRemoteSocketAddress());
                    ServerChat.onLineSockets.put(socket,name);
                }
                writeMsg(flag,dis);
            }
        }catch (Exception e){
            System.out.println("有人下线了!");
            ServerChat.onLineSockets.remove(socket);
            try{
                writeMsg(1,dis);
            }catch (Exception e1){
                e1.printStackTrace();
            }
        }
    }

    private void writeMsg(int flag, DataInputStream dis) throws IOException {
        String msg = null;
        if (flag == 1){
            StringBuilder rs = new StringBuilder();
            Collection<String> onlineNames = ServerChat.onLineSockets.values();
            //判断是否存在在线人数
            if (onlineNames != null && onlineNames.size() > 0){
                for (String name : onlineNames){
                    rs.append(name + Constants.SPILIT);
                }
                //去掉最后的一个分隔符
                msg = rs.substring(0,rs.lastIndexOf(Constants.SPILIT));

                //将消息发送给所有的客户端
                sendMsgToAll(flag,msg);
            }
        } else if (flag == 2 || flag == 3){
            //读到消息 群发或者 @消息
            String newMsg = dis.readUTF();
            //得到发送人
            String sender = ServerChat.onLineSockets.get(socket);

            //发送内容
            StringBuilder msgFinal = new StringBuilder();
            //时间
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss EEE");
            if (flag == 2){
                msgFinal.append(sender).append("     ").append(sdf.format(System.currentTimeMillis()*2)).append("\r\n");
                msgFinal.append("     ").append(newMsg).append("\r\n");
                sendMsgToAll(flag,msgFinal.toString());
            }
            if (flag == 3){
                msgFinal.append(sender).append("   ").append(sdf.format(System.currentTimeMillis()*2)).append("对您私发\r\n");
                msgFinal.append("     ").append(newMsg).append("\r\n");
                //私发
                //得到给谁私发
                String destName = dis.readUTF();
                sendMsgToOne(destName,msgFinal.toString());
            }
        }
    }

    private void sendMsgToOne(String destName, String msg) throws IOException {
        Set<Socket> allOnLineSockets = ServerChat.onLineSockets.keySet();
        for (Socket allOnLineSocket : allOnLineSockets) {
            //得到当前要私发的socket
            if (ServerChat.onLineSockets.get(allOnLineSocket).trim().equals(destName)){
                DataOutputStream dos = new DataOutputStream(allOnLineSocket.getOutputStream());
                dos.writeInt(2);
                dos.writeUTF(msg);
                dos.flush();
            }
        }
    }

    private void sendMsgToAll(int flag, String msg) throws IOException {
        Set<Socket> allOnLineSockets = ServerChat.onLineSockets.keySet();
        for (Socket sk : allOnLineSockets) {
            DataOutputStream dos = new DataOutputStream(sk.getOutputStream());
            dos.writeInt(flag);
            dos.writeUTF(msg);
            dos.flush();
        }
    }
}

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值