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

Java实现--基于服务器的多用户聊天室

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

Java实现--基于服务器的多用户聊天室

多用户聊天室
    • 1.项目简介
    • 2.开发环境
    • 3.项目具体功能
    • 4.运行结果
    • 5.其他功能实现运行结果
    • 6.具体实现代码
      • 登录页面:
      • 客户端:
      • 服务器端:

1.项目简介

基于服务器的多用户聊天室应用软件。聊天室最大用户容量为30,每一个用户都是一个独立的线程。

2.开发环境

NetBeans IDE8.2、JDK1.8

3.项目具体功能

服务器端:
1)服务器端界面窗口
A.服务器端IP地址输入框:服务器地址输入功能
B.端口号输入框:端口号输入功能
C.启动服务器按钮:启动服务器功能
D.聊天室大厅消息显示文本域:聊天消息显示功能
E.聊天室在线用户显示文本域:在线用户显示功能

客户端:
1)登录功能
1.1 聊天室登录界面窗口
A.用户名输入框:用户名称输入功能
B.密码输入框:用户密码输入功能
C.服务器地址输入框:服务器地址输入功能
D.端口号输入框:端口号输入功能
E.进入聊天室按钮:登录进入聊天室功能
F.退出聊天室按钮:退出聊天室功能

2)聊天室功能
2.1 多用户聊天室主界面
A.聊天室大厅消息显示文本域:多用户聊天消息显示
B.聊天室在线用户显示文本域:在线用户情况显示
C.文本信息输入文本框:文本信息输入功能
D.发送功能按钮:文本信息发送功能
E.私聊功能选择框:单人聊天模式转换功能
F.清除聊天记录功能按钮:清除聊天记录功能
G.退出聊天室:退出聊天室功能

2.2 私聊界面
A.聊天信息文本域:显示聊天信息以及对方用户名
B.文本输入框:文本信息输入功能
C.发送按钮:文本信息发送功能


界面是使用NetBeans(软件下载可点)
直接新建一个Jframe项目就可以开始制作你自己的界面风格,对小白来说是一款很友好的集成开发软件。
具体的操作就是拖动你要的组件,如下图,从右边的组件面板中拖到JFrame窗体中,调节好大小,然后修改组件的变量名称,为组件添加监听事件,剩下的就是代码的编写了。

4.运行结果

服务器端界面:

客户端界面:
图 登录界面:

图 聊天室主界面:

图 私聊界面:

图 三用户聊天室效果:

5.其他功能实现运行结果

1)在线用户显示功能:

当有用户退出聊天室时,在线用户总数与在线用户列表都会同步更新,如图:

2)清除聊天记录功能:
图 清除前:

图 清除后:

3)私聊功能:
图 用户哈哈与用户康的聊天窗口:

4)退出聊天室功能:
图 服务器端与客户机端的“退出消息”提示:

5)登录错误提示:
当聊天室达到最大用户容量30时,客户机登录出错,错误提示如下:

当用户登录时,用户名或密码为空时错误提示如下:

在本次项目中定义了DatagramSocket类接收和发送数据报,实现UDP协议服务,DatagramSocket本身不维护连接状态,不能产生I/O流,Java使用DatagramPacket代表数据报文,DatagramSocket接收和发送的数据都是通过DatagramPacket报文对象完成的。如下图所示:

图 基于UDP协议的客户机与服务器通信逻辑

6.具体实现代码 登录页面:

LoginUI.java:

public class LoginUI extends javax.swing.JDialog {

    private static DatagramSocket clientSocket;  //客户机套接字
    private static Message msg;                  //消息对象
    private byte[] data = new byte[8096]; //8KB数组
    
    public LoginUI(java.awt.Frame parent, boolean modal) {
        super(parent, modal);
        initComponents();
    }
   
}

“进入聊天室”按钮的监听事件:

private void btnLoginActionPerformed(java.awt.event.ActionEvent evt) {                                         
        // TODO add your handling code here:
        try{
            String id = txtUserId.getText();
            String password = String.valueOf(txtPassword.getPassword());
            if(id.equals("") || password.equals("")){
                JOptionPane.showMessageDialog(null, "账号或密码不能为空!",
                    "错误提示",JOptionPane.ERROR_MESSAGE);
                return;
            }
            //获取服务器地址和端口
            String remoteName = txtRemoteName.getText();
            InetAddress remoteAddr = InetAddress.getByName(remoteName);
            int remotePort = Integer.parseInt(txtRemotePort.getText());
            //创建UDP套接字
            DatagramSocket clientSocket = new DatagramSocket();
            clientSocket.setSoTimeout(3000);      //设置超时时间
            //构建用户登录消息
            Message msg = new Message();
            msg.setUserId(id);                           //登录名
            msg.setPassword(password);                   //密码
            msg.setType("M_LOGIN");                      //登录消息类型
            msg.setToAddr(remoteAddr);                   //目标主机地址
            msg.setToPort(remotePort);                   //目标主机端口
            byte[] data = Translate.ObjectToByte(msg);   //消息对象序列化为字节数组
            //定义登录报文
            DatagramPacket packet = new DatagramPacket(data, data.length, remoteAddr, remotePort);
            //发送登录报文
            clientSocket.send(packet);
            //接收服务器回送的报文
            DatagramPacket backPacket = new DatagramPacket(data, data.length);
            clientSocket.receive(backPacket);
            clientSocket.setSoTimeout(0);                          //取消超时时间
            Message backMsg = (Message)Translate.ByteToObject(data);
            //处理登录结果
            if(backMsg.getType().equalsIgnoreCase("M_SUCCESS")){   //登录成功
                this.dispose();                                    //关闭登录对话框
                ClientUI client = new ClientUI(clientSocket,msg);  //创建客户机界面
                client.setTitle(msg.getUserId());                  //设置标题
                client.setVisible(true);                           //显示会话窗体
            }else{                                                 //登录失败
                JOptionPane.showMessageDialog(null, "用户 ID 或密码错误!n",
                    "登录失败",JOptionPane.ERROR_MESSAGE);
            }
        }catch(IOException ex){
            JOptionPane.showMessageDialog(null, ex.getMessage(),
                "登录错误",JOptionPane.ERROR_MESSAGE);
        }//end try
    }

主函数:

public static void main(String args[]) {
        
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                LoginUI dialog = new LoginUI(new javax.swing.JFrame(), true);
                dialog.addWindowListener(new java.awt.event.WindowAdapter() {
                    @Override
                    public void windowClosing(java.awt.event.WindowEvent e) {
                        System.exit(0);
                    }
                });
                dialog.setVisible(true);
            }
        });
    }
客户端:

ClientUI.java:

public class ClientUI extends javax.swing.JFrame {

    private static DatagramSocket clientSocket;  //客户机套接字
    private static Message msg;                  //消息对象
    private byte[] data = new byte[8096];        //8KB数组
    
    
     public ClientUI() {
        initComponents();
    }
     
    public ClientUI(DatagramSocket socket, Message msg) {
        this();                  //调用无参数构造函数,初始化界面
        clientSocket = socket;   //初始化会话套接字
        this.msg = msg;          //登录消息
        //创建客户机消息接收和处理线程
        Thread recvThread = new ReceiveMessage(clientSocket,this);
        recvThread.start();      //启动消息接收线程
    }
        
}

“发送”按钮的监听事件:

private void btnSendActionPerformed(java.awt.event.ActionEvent evt) {                                        
        // TODO add your handling code here:
        try{
            msg.setText(txtInput.getText());     //获取输入的文本
            msg.setType("M_MSG");                //普通会话消息
            data = Translate.ObjectToByte(msg);  //消息对象序列化
            //构建发送报文
            DatagramPacket packet = new DatagramPacket(
                data, data.length, msg.getToAddr(), msg.getToPort());
            clientSocket.send(packet);   //发送
            txtInput.setText("");        //清空输入框
        } catch (IOException ex) {
            JOptionPane.showMessageDialog(null, ex.getMessage(),
                "错误提示",JOptionPane.ERROR_MESSAGE);
        }
    }

实现按下回车键,也能发送消息:

    private void txtInputActionPerformed(java.awt.event.ActionEvent evt) {                                         
        // TODO add your handling code here:
        btnSendActionPerformed(evt);   //直接调用btnSpeak按钮的响应函数即可
    } 

单击窗体“关闭”按钮,关闭窗体之前发送下线消息:

    private void formWindowClosing(java.awt.event.WindowEvent evt) {                                   
        // TODO add your handling code here:
         try{
            msg.setType("M_QUIT");               //消息类型
            msg.setText(null);
            data = Translate.ObjectToByte(msg);  //消息对象序列化
            //构建发送
            DatagramPacket packet = new DatagramPacket(
                    data, data.length, msg.getToAddr(), msg.getToPort());
            clientSocket.send(packet);          //发送
         }catch (IOException ex){}
        clientSocket.close();                   //关闭套接字
    }

JList用户列表“双击”事件,进入私聊界面:

    private void userListMouseClicked(java.awt.event.MouseEvent evt) {                                      
        // TODO add your handling code here:
        if(evt.getClickCount() == 2){
            //txtArea.append("确定要进行私聊吗?n");
                //this.dispose();                                    //关闭登录对话框
                PrivateChatUI privateChat = new PrivateChatUI();  //创建客户机界面
                privateChat.setTitle(msg.getUserId() + "与" + userList.getSelectedValue() + "的聊天窗口");                  //设置标题
                privateChat.setVisible(true);                           //显示会话窗体
            }
        
    }

主函数:

public static void main(String args[]) {
        
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new ClientUI(clientSocket, msg).setVisible(true);
            }
        });
    }
服务器端:

ServerUI.java:

public class ServerUI extends javax.swing.JFrame {

    private ServerSocketChannel listenChannel = null;  //侦听通道
    private Selector selector;                         //选择器
    
    
    public ServerUI() {
        initComponents();
    }
    
}

启动服务器过程:

    private void btnStartActionPerformed(java.awt.event.ActionEvent evt) {                                         
        // TODO add your handling code here:
        //启动服务器
        btnStart.setEnabled(false);
        try{
            //构建工作地址
            String hostName = txtHostName.getText();
            int hostPort = Integer.parseInt(txtHostPort.getText());
            //构建套接字格式的地址
            SocketAddress serverAddr = new InetSocketAddress(
                            InetAddress.getByName(hostName),hostPort);
            selector = Selector.open();                 //创建选择器
            listenChannel = ServerSocketChannel.open(); //创建侦听通道
            listenChannel.socket().bind(serverAddr);    //侦听通道绑定工作地址
            listenChannel.configureBlocking(false);     //侦听通道工作于非阻塞模式
            //侦听通道注册到选择器,设置OP_ACCEPT标志位
            listenChannel.register(selector, SelectionKey.OP_ACCEPT);
            txtArea.append("服务器开始侦听客户机连接...n");
        } catch (IOException ex) {}
        
        //服务器轮询线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    while(true){  //轮询各通道状态,处理连接和会话
                        int nKeys = selector.select();  //查询令牌集合
                        if(nKeys == 0) continue;        //没有就绪令牌,越过下面步骤,开始新一轮查询
                        Set  readyKeys = selector.selectedKeys();//返回就绪令牌集合
                        Iterator  it = readyKeys.iterator();     //就绪令牌集合迭代器
                        while(it.hasNext()){                  //遍历就绪令牌集合
                            SelectionKey key = it.next();     //取出下一个令牌
                            if(key.isAcceptable()){           //如果是连接事件
                                doAccept(key);                //简历连接,创建新会话通道
                            }else if(key.isReadable()){       //如果是读数据事件
                                doRead(key);                  //接收数据
                            }
                            it.remove();                      //从就绪集合中删除处理过的令牌
                        }//end while
                    }//end while
                }catch(IOException ex){}//end try catch
            }//end run()
        }).start();
    } 

主函数:

public static void main(String args[]) {
        
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new ServerUI().setVisible(true);
            }
        });
    }

关于项目中的一些具体细节,可以看看代码中的注释。
写了比较久,此多用户聊天室应用软件程序还有很多不完善的地方,后面会再增添一些功能,例如:文件传输,保存聊天记录,一对一聊天,服务器端关闭服务器并通知客户端退出,采用数据库检验用户账号和密码的正确性。
不足之处,欢迎各位大佬指正。

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

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

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