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

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协议的客户机与服务器通信逻辑
图 基于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数组
    /**
     * Creates new form LoginUI
     */
    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[]) {
        /* Create and display the dialog */
        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数组
    
    /**
     * Creates new form ChatClient
     */
     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按钮的响应函数即可
    } 

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

/**
    * @param evt 窗体事件
    */
    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[]) {
        /* Create and display the form */
        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;                         //选择器
    
    /**
     * Creates new form ServerUI
     */
    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 <SelectionKey> readyKeys = selector.selectedKeys();//返回就绪令牌集合
                        Iterator <SelectionKey> 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[]) {
        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new ServerUI().setVisible(true);
            }
        });
    }

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

Java聊天室程序 需求分析 2.1 业务需求 1. 与聊天室成员一起聊天。 2. 可以与聊天室成员私聊。 3. 可以改变聊天内容风格。 4. 用户注册(含头像)、登录。 5. 服务器监控聊天内容。 6. 服务器过滤非法内容。 7. 服务器发送通知。 8. 服务器踢人。 9. 保存服务器日志。 10.保存用户聊天信息。 2.2 系统功能模块 2.2.1 服务器端 1.处理用户注册 2.处理用户登录 3.处理用户发送信息 4.处理用户得到信息 5.处理用户退出 2.2.2 客户端 1.用户注册界面及结果 2.用户登录界面及结果 3.用户发送信息界面及结果 4.用户得到信息界面及结果 5.用户退出界面及结果 2.3 性能需求 运行环境:Windows 9x、2000、xp、2003,Linux 必要环境:JDK 1.5 以上 硬件环境:CPU 400MHz以上,内存64MB以上 3.1.2 客户端结构 ChatClient.java 为客户端程序启动类,负责客户端的启动和退出。 Login.java 为客户端程序登录界面,负责用户帐号信息的验证与反馈。 Register.java 为客户端程序注册界面,负责用户帐号信息的注册验证与反馈。 ChatRoom.java 为客户端程序聊天室界面,负责接收、发送聊天内容与服务器端的Connection.java 亲密合作。 Windowclose 为ChatRoom.java的内部类,负责监听聊天室界面的操作,当用户退出时返回给服务器信息。 Clock.java 为客户端程序一个程序实现一个石英钟功能。 3. 2 系统实现原理 当用户聊天时,将当前用户名、聊天对象、聊天内容、聊天语气和是否私聊进行封装,然后与服务器建立Socket连接,再用对象输出流包装Socket的输出流将聊天信息对象发送给服务器端 当用户发送聊天信息时,服务端将会收到客户端用Socket传输过来的聊天信息对象,然后将其强制转换为Chat对象,并将本次用户的聊天信息对象添加到聊天对象集Message,以供所有聊天用户访问。 接收用户的聊天信息是由多线程技术实现的,因为客户端必须时时关注更新服务器上是否有最新消息,在本程序设定的是3秒刷新服务器一次,如果间隔时间太短将会增加客户端与服务器端的通信负担,而间隔时间长就会让人感觉没有时效性,所以经过权衡后认为3秒最佳,因为每个用户都不可能在3秒内连续发送信息。 当每次用户接收到聊天信息后将会开始分析聊天信息然后将适合自己的信息人性化地显示在聊天信息界面上。 4.1.1 问题陈述 1.接受用户注册信息并保存在一个基于文件的对象型数据库。 2.能够允许注册过的用户登陆聊天界面并可以聊天。 3.能够接受私聊信息并发送给特定的用户。 4.服务器运行在自定义的端口上#1001。 5.服务器监控用户列表和用户聊天信息(私聊除外)。 6.服务器踢人,发送通知。 7.服务器保存日志。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

yscc-16

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

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

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

打赏作者

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

抵扣说明:

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

余额充值