栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

java多线程聊天室(有图形界面)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

java多线程聊天室(有图形界面)

聊天室分客户端和服务端,可实现的功能:
多人聊天、退出聊天室、发送系统消息

直接上代码吧。
(我用的是idea)

服务端:

package chat;

import javax.swing.*;
import javax.swing.JTextField;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.event.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

class server extends Jframe implements Runnable, ListSelectionListener, ActionListener {
    private ServerSocket ss;//建立socket
    private ArrayList users = new ArrayList<>(); //容量能够动态增长的数组
    DefaultListModel dl = new DefaultListModel<>();
    private JList userList = new JList<>(dl);//显示对象列表并且允许用户选择一个或多个项的组件。单独的模型 ListModel 维护列表的内容。

    //消息输入栏
    JTextField jtf = new JTextField();

    public server() throws Exception{
        this.setTitle("服务器端");//窗口名称
        this.add(userList, "North");
        JPanel jpl = new JPanel();
        this.add(jpl, "South");

        //将消息输入栏设为一栏
        jtf.setColumns(1);
        jpl.setLayout(new BorderLayout());
        jpl.add(jtf, BorderLayout.NORTH);

        JButton jbt1 = new JButton("发送消息");
        jpl.add(jbt1, BorderLayout.WEST);//发送消息

        //发送消息
        jbt1.addActionListener(this);


        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        this.setLocation(400,100);
        this.setSize(500, 400);
        this.setVisible(true);
        this.setAlwaysOnTop(true);

        ss = new ServerSocket(80);//绑定80端口
        new Thread(this).start();//监听用户端的加入


    }
    @Override
    public void run() {
        while(true){
            try{
                Socket s = ss.accept();//等待客户端连接
                ChatThread ct = new ChatThread(s); //为该客户开一个线程
                users.add(ct); //将每个线程加入到users
                ct.start();

            }catch (Exception ex){
                ex.printStackTrace();
                javax.swing.JOptionPane.showMessageDialog(this,"服务器异常!");
                System.exit(0);
            }
        }
    }

    //发送消息按钮点击事件监听
    @Override
    public void actionPerformed(ActionEvent e) {
        String label = e.getActionCommand();
        if(label.equals("发送消息")){
            handleAll();
        }
    }

    public void handleAll(){
        if(!jtf.getText().equals("")){
            sendMessage("ALL#" + jtf.getText());
            //发送完后,输入框中内容为空
            jtf.setText("");
        }
    }//发送消息


    public class ChatThread extends Thread{
        Socket s;
        private BufferedReader br;
        private PrintStream ps;
        public boolean canRun = true;
        String nickName = null;
        public ChatThread(Socket s) throws Exception{
            this.s = s;
            br = new BufferedReader(new InputStreamReader(s.getInputStream()));//读取服客户端数据
            ps = new PrintStream(s.getOutputStream());//向客户端发送数据
        }
        public void run(){
            while(canRun){
                try{
                    String msg = br.readLine();//接收客户端发来的消息
                    String[] strs = msg.split("#");
                    switch (strs[0]) {
                        case "LOGIN" -> { //收到来自客户端的登录消息
                            nickName = strs[1];
                            dl.addElement(nickName);
                            userList.repaint();
                            sendMessage(msg);
                        }
                        case "MSG", "FSMSG" -> sendMessage(msg);
                        case "OFFLINE" -> { //收到来自客户端的退出消息
                            sendMessage(msg);
                            dl.removeElement(strs[1]);
                            // 更新当前在聊天室内的用户List列表
                            userList.repaint();
                        }

                    }
                }catch (Exception ignored){
                    System.out.println("出现异常");
                }
            }

        }

    }

    public void sendMessage(String msg){  //服务器端发送给所有用户
        for(ChatThread ct : users){
            ct.ps.println(msg);
        }
    }

    @Override
    public void valueChanged(ListSelectionEvent e) {
        // TODO 自动生成的方法存根

    }

    public static void main(String[] args) throws Exception{
        new server();
    }
}


客户端:

package chat;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;

public class client1 extends Jframe implements Runnable,ActionListener {
    //north
    private JMenuBar bar = new JMenuBar();

    private JMenu menu = new JMenu("关于");
    private JMenuItem about = new JMenuItem("聊天室信息");

    private JMenuItem exit = new JMenuItem("退出聊天室");
    JPanel north = new JPanel();
    DefaultListModel dl = new DefaultListModel();//用来修改JList
    private JList userList = new JList(dl);//用来展示和选择

    //center面板
    JPanel center = new JPanel();
    Jtextarea jta = new Jtextarea(10,20);
    JScrollPane js = new JScrollPane(jta);
    JPanel operPane = new JPanel();//发送消息的操作面板
    JLabel input = new JLabel("请输入:");
    JTextField jtf = new JTextField(24);

    private JButton jbt = new JButton("发送消息");

    private BufferedReader br = null;
    private PrintStream ps = null;
    private String nickName = null;


    public client1() throws Exception{
        //north 菜单栏
        bar.add(menu);
        menu.add(about);
        menu.add(exit);
        about.addActionListener(this);
        exit.addActionListener(this);
        BorderLayout bl = new BorderLayout();
        north.setLayout(bl);
        north.add(bar,BorderLayout.NORTH);
        add(north,BorderLayout.NORTH);


        //center 聊天消息框  发送消息操作面板
        jta.setEditable(false);//消息显示框是不能编辑的


        BorderLayout bl3 = new BorderLayout();
        center.setLayout(bl3);
        FlowLayout fl = new FlowLayout(FlowLayout.LEFT);
        operPane.setLayout(fl);
        operPane.add(input);
        operPane.add(jtf);
        operPane.add(jbt);
        center.add(js,BorderLayout.CENTER);//js消息展示框JScrollPane
        center.add(operPane,BorderLayout.SOUTH);
        add(center,BorderLayout.CENTER);

        js.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);//需要时才显示滚动条

        //鼠标事件,点击
        jbt.addActionListener(this);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);

        nickName = JOptionPane.showInputDialog("用户名:");
        this.setTitle("聊天室-"+nickName);
        this.setSize(500,400);
        this.setVisible(true);

        Socket s = new Socket("10.100.35.39", 80);//连接到指定主机(即本机)的指定端口号80
        br = new BufferedReader(new InputStreamReader(s.getInputStream()));//读取服务器端数据
        ps = new PrintStream(s.getOutputStream());//向服务器端发送数据
        new Thread(this).start();
        ps.println("LOGIN#" + nickName);//发送登录信息,消息格式:LOGIN#nickName

        jtf.setFocusable(true);//设置焦点

        //键盘事件,实现当输完要发送的内容后,直接按回车键,实现发送
        //监听键盘相应的控件必须是获得焦点的情况下才能起作用
        jtf.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if(e.getKeyCode() == KeyEvent.VK_ENTER) {
                    ps.println("MSG#" + nickName + "#" +  jtf.getText());//发送消息的格式:MSG#nickName#message
                    //发送完后,是输入框中内容为空
                    jtf.setText("");
                }
            }
        });


        //监听系统关闭事件,退出时给服务器端发出指定消息
        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                ps.println("OFFLINE#" + nickName);//发送下线信息,消息格式:OFFLINE#nickName
            }
        });


    }

    public void run(){//客户端与服务器端发消息的线程
        while (true){
            try{
                String msg = br.readLine();//读取服务器是否发送了消息给该客户端
                String[] strs = msg.split("#");
                //判断是否为服务器发来的登陆信息
                if(strs[0].equals("LOGIN")){
                    if(!strs[1].equals(nickName)){//不是本人的上线消息就显示,本人的不显示
                        jta.append(strs[1] + "加入聊天室n");
                        dl.addElement(strs[1]);//DefaultListModel来更改JList的内容
                        userList.repaint();
                    }
                }else if(strs[0].equals("MSG")){//接到服务器发送消息的信息
                    if(!strs[1].equals(nickName)){//别人发送的消息
                        jta.append(strs[1] + ":" + strs[2] + "n");
                    }else{
                        jta.append("我:" + strs[2] + "n");
                    }
                }
                 else if(strs[0].equals("ALL")){

                    jta.append("系统消息:" + strs[1] + "n");
                }

            }catch (Exception ex){//如果服务器端出现问题,则客户端强制下线
                JOptionPane.showMessageDialog(this, "聊天室已关闭。");
                System.exit(0);
            }
        }
    }


    @Override
    public void actionPerformed(ActionEvent e) {//鼠标点击事件
        String label = e.getActionCommand();
        if(label.equals("发送消息")){
            handleSend();
        }

        else if(label.equals("聊天室信息")){
            JOptionPane.showMessageDialog(this, "该聊天室由xxx制作n感谢使用");//这里可以改成别的话,随你心意
        }else if(label.equals("退出聊天室")){
            JOptionPane.showMessageDialog(this, "您已成功退出!");
            ps.println("OFFLINE#" + nickName);
            System.exit(0);
        } else{
            System.out.println("不识别的事件");
        }
    }


    public void handleSend(){//群发消息
        //发送信息时标识一下来源
        ps.println("MSG#" + nickName + "#" +  jtf.getText());
        //发送完后,是输入框中内容为空
        jtf.setText("");
    }


    public static void main(String[] args)throws Exception{
        new client1();
    }
}


不是原创,源代码来自这里,删掉了很多,改了一点点。感谢这位博主的分享。

要实现多人聊天就多复制几个客户端(记得改名)再一起运行就好了。

如果要分别在两台机子上运行,实行不同机器之间的多人聊天,就把电脑的防火墙关掉,然后代码里的主机号改为服务器端所在的电脑的主机号,再ping一下看看是否连接成功就行了。
两台电脑连接成功之后非服务器端所在电脑只需要运行客户端程序,同时服务器端可以同时运行服务器端和客户端,这个问题不大。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/644058.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号