Java聊天室之实现客户端一对一聊天功能

目录
  • 一、题目描述
  • 二、解题思路
  • 三、代码详解
  • 多学一个知识点

一、题目描述

题目实现:不同的客户端之间需要进行通信,一个客户端与指定的另一客户端进行通信,实现一对一聊天功能。

实现一个客户端与指定的另一客户端进行通信,运行程序,服务器启动后,启动3个客户端程序,分别以小小,虚虚,竹竹,登录 ,然后在左侧的用户列表中选择接收信息用户,输入聊天信息,发送到目标用户。

二、解题思路

创建一个服务类:ClientOneToOneServerFrame,继承JFrame类

定义ServerThread线程类,用于为客户端添加用户列表。有一部分代码用于转发客户端发送的消息。

创建一个客户端类:ClientOneToOneClientFrame,继承JFrame类

定义ClientThread线程类,用于对接收到服务器的信息,进行处理。如果是登录用户,就添加到用户列表中。

如果是消息,就追加到文本域中。

技术重点:

​ 在服务器端通过线程对客户端发送的信息进行监听,并对登录用户和消息分别进行处理。如果是登录用户,就将所有用户添加到客户端的用户列表中;如果是消息,就转发给指定的用户;客户端则通过线程对接收到的信息进行处理,如果是登录用户就添加到用户列表中,如果是消息就追加到文本域中。 ​ (1)在服务器端创建线程类ServerThread,用于对登录用户和消息分别进行处理。如果是登录用户,就将所有用户添加到客户端的用户列表中;如果是消息就转发给指定的用户。

​ (2)在客户端创建线程类ClientThread,用于对接收到的信息进行处理,如果是登录用户就添加到用户列表中,如果是消息就追加到文本域中。

启动多个客户端:

1、把项目打成jar包:利用maven 的clean install

会在target目录下生成jar包

2、进入target目录,使用java -cp的命令运行指定的类

java -cp 命令中 cp 指的就是classpath。使用该命令可以运行jar中的某个指定的类(要包含全路径的包名)

进入cmd命令模式

运行服务端

java -cp basics98-1.0-SNAPSHOT.jar com.xiaoxuzhu.ClientOneToOneServerFrame

运行多个客户端

java -cp basics98-1.0-SNAPSHOT.jar com.xiaoxuzhu.ClientOneToOneClientFrame

三、代码详解

ClientOneToOneServerFrame

package com.xiaoxuzhu;
import java.awt.BorderLayout;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

/**
 * Description:
 *
 * @author xiaoxuzhu
 * @version 1.0
 *
 * <pre>
 * 修改记录:
 * 修改后版本	        修改人		修改日期			修改内容
 * 2022/6/5.1	    xiaoxuzhu		2022/6/5		    Create
 * </pre>
 * @date 2022/6/5
 */
public class ClientOneToOneServerFrame  extends JFrame{
    private JTextArea ta_info;
    private ServerSocket server; // 声明ServerSocket对象
    private Socket socket; // 声明Socket对象socket
    private Hashtable<String, Socket> map = new Hashtable<String, Socket>();// 用于存储连接到服务器的用户和客户端套接字对象

    public void createSocket() {
        try {
            server = new ServerSocket(9527);
            while (true) {
                ta_info.append("等待新客户连接......\n");
                socket = server.accept();// 创建套接字对象
                ta_info.append("客户端连接成功。" + socket + "\n");
                new ServerThread(socket).start();// 创建并启动线程对象
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    class ServerThread extends Thread {
        Socket socket;

        public ServerThread(Socket socket) {
            this.socket = socket;
        }

        public void run() {
            try {
                BufferedReader in = new BufferedReader(new InputStreamReader(
                        socket.getInputStream()));// 创建输入流对象
                while (true) {
                    String info = in.readLine();// 读取信息
                    String key = "";
                    if (info.startsWith("用户:")) {// 添加登录用户到客户端列表
                        key = info.substring(3, info.length());// 获得用户名并作为键使用
                        map.put(key, socket);// 添加键值对
                        Set<String> set = map.keySet();// 获得集合中所有键的Set视图
                        Iterator<String> keyIt = set.iterator();// 获得所有键的迭代器
                        while (keyIt.hasNext()) {
                            String receiveKey = keyIt.next();// 获得表示接收信息的键
                            Socket s = map.get(receiveKey);// 获得与该键对应的套接字对象
                            PrintWriter out = new PrintWriter(s
                                    .getOutputStream(), true);// 创建输出流对象
                            Iterator<String> keyIt1 = set.iterator();// 获得所有键的迭代器
                            while (keyIt1.hasNext()) {
                                String receiveKey1 = keyIt1.next();// 获得键,用于向客户端添加用户列表
                                out.println(receiveKey1);// 发送信息
                                out.flush();// 刷新输出缓冲区
                            }
                        }

                    } else {// 转发接收的消息
                        key = info.substring(info.indexOf(":发送给:") + 5, info
                                .indexOf(":的信息是:"));// 获得接收方的key值,即接收方的用户名
                        String sendUser = info.substring(0, info
                                .indexOf(":发送给:"));// 获得发送方的key值,即发送方的用户名
                        Set<String> set = map.keySet();// 获得集合中所有键的Set视图
                        Iterator<String> keyIt = set.iterator();// 获得所有键的迭代器
                        while (keyIt.hasNext()) {
                            String receiveKey = keyIt.next();// 获得表示接收信息的键
                            if (key.equals(receiveKey)
                                    && !sendUser.equals(receiveKey)) {// 如果是发送方,但不是用户本身
                                Socket s = map.get(receiveKey);// 获得与该键对应的套接字对象
                                PrintWriter out = new PrintWriter(s
                                        .getOutputStream(), true);// 创建输出流对象

                                out.println("MSG:"+info);// 发送信息
                                out.flush();// 刷新输出缓冲区
                            }
                        }
                    }
                }
            } catch (IOException e) {
                ta_info.append(socket + "已经退出。\n");
            }
        }
    }

    /**
     * Launch the application
     *
     * @param args
     */
    public static void main(String args[]) {
        ClientOneToOneServerFrame frame = new ClientOneToOneServerFrame();
        frame.setVisible(true);
        frame.createSocket();
    }

    /**
     * Create the frame
     */
    public ClientOneToOneServerFrame() {
        super();
        setTitle("客户端一对一通信——服务器端程序");
        setBounds(100, 100, 385, 266);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        final JScrollPane scrollPane = new JScrollPane();
        getContentPane().add(scrollPane, BorderLayout.CENTER);

        ta_info = new JTextArea();
        scrollPane.setViewportView(ta_info);
    }
}

ClientOneToOneClientFrame

package com.xiaoxuzhu;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.swing.*;

/**
 * Description:
 *
 * @author xiaoxuzhu
 * @version 1.0
 *
 * <pre>
 * 修改记录:
 * 修改后版本	        修改人		修改日期			修改内容
 * 2022/6/5.1	    xiaoxuzhu		2022/6/5		    Create
 * </pre>
 * @date 2022/6/5
 */
public class ClientOneToOneClientFrame extends JFrame{
    private JTextField tf_newUser;
    private JList user_list;
    private JTextArea ta_info;
    private JTextField tf_send;
    PrintWriter out;// 声明输出流对象
    private boolean loginFlag = false;// 为true时表示已经登录,为false时表示未登录
    private Socket socket;
    /**
     * Launch the application
     *
     * @param args
     */
    public static void main(String args[]) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    ClientOneToOneClientFrame frame = new ClientOneToOneClientFrame();
                    frame.setVisible(true);
                    frame.createClientSocket();// 调用方法创建套接字对象
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public void createClientSocket() {
        try {
             socket = new Socket("127.0.0.1", 9527);// 创建套接字对象
            out = new PrintWriter(socket.getOutputStream(), true);// 创建输出流对象
            SwingWorker<Void,Void> worker=new SwingWorker<Void, Void>() {
                @Override
                protected Void doInBackground() throws Exception {
                    try {
                        BufferedReader in = new BufferedReader(new InputStreamReader(
                                socket.getInputStream()));// 创建输入流对象
                        DefaultComboBoxModel model = (DefaultComboBoxModel) user_list
                                .getModel();// 获得列表框的模型
                        while (true) {
                            String info = in.readLine().trim();// 读取信息

                            if (!info.startsWith("MSG:")) {
                                boolean itemFlag = false;// 标记是否为列表框添加列表项,为true不添加,为false添加
                                for (int i = 0; i < model.getSize(); i++) {
                                    if (info.equals((String) model.getElementAt(i))) {
                                        itemFlag = true;
                                    }
                                }
                                if (!itemFlag) {
                                    model.addElement(info);// 添加列表项
                                } else {
                                    itemFlag = false;
                                }
                            } else {
                                ta_info.append(info + "\n");// 在文本域中显示信息
                                if (info.equals("88")) {
                                    break;// 结束线程
                                }
                            }
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    return null;
                }
            };
            worker.execute();

        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void send() {
        if (!loginFlag) {
            JOptionPane.showMessageDialog(null, "请先登录。");
            return;
        }
        String sendUserName = tf_newUser.getText().trim();
        String info = tf_send.getText();// 获得输入的信息
        if (info.equals("")) {
            return;// 如果没输入信息则返回,即不发送
        }
        String receiveUserName = (String) user_list.getSelectedValue();// 获得接收信息的用户
        String msg = sendUserName + ":发送给:" + receiveUserName + ":的信息是: "
                + info;// 定义发送的信息
        if (info.equals("88")) {
            System.exit(0);// 如果没输入信息是88,则退出
        }
        out.println(msg);// 发送信息
        out.flush();// 刷新输出缓冲区
        tf_send.setText(null);// 清空文本框
    }

    /**
     * Create the frame
     */
    public ClientOneToOneClientFrame() {
        super();
        setTitle("客户端一对一通信——客户端程序");
        setBounds(100, 100, 385, 288);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        final JPanel panel = new JPanel();
        getContentPane().add(panel, BorderLayout.SOUTH);

        final JLabel label = new JLabel();
        label.setText("输入聊天内容:");
        panel.add(label);

        tf_send = new JTextField();
        tf_send.addActionListener(new ActionListener() {
            public void actionPerformed(final ActionEvent e) {
                send();// 调用方法发送信息
            }
        });
        tf_send.setPreferredSize(new Dimension(180, 25));
        panel.add(tf_send);

        final JButton button = new JButton();
        button.addActionListener(new ActionListener() {
            public void actionPerformed(final ActionEvent e) {
                send();// 调用方法发送信息
            }
        });
        button.setText("发  送");
        panel.add(button);

        final JSplitPane splitPane = new JSplitPane();
        splitPane.setDividerLocation(100);
        getContentPane().add(splitPane, BorderLayout.CENTER);

        final JScrollPane scrollPane = new JScrollPane();
        splitPane.setRightComponent(scrollPane);

        ta_info = new JTextArea();
        scrollPane.setViewportView(ta_info);

        final JScrollPane scrollPane_1 = new JScrollPane();
        splitPane.setLeftComponent(scrollPane_1);

        user_list = new JList();
        user_list.setModel(new DefaultComboBoxModel(new String[] { "" }));
        scrollPane_1.setViewportView(user_list);

        final JPanel panel_1 = new JPanel();
        getContentPane().add(panel_1, BorderLayout.NORTH);

        final JLabel label_1 = new JLabel();
        label_1.setText("输入用户名称:");
        panel_1.add(label_1);

        tf_newUser = new JTextField();
        tf_newUser.setPreferredSize(new Dimension(180, 25));
        panel_1.add(tf_newUser);

        final JButton button_1 = new JButton();
        button_1.addActionListener(new ActionListener() {
            public void actionPerformed(final ActionEvent e) {
                if (loginFlag) {
                    JOptionPane.showMessageDialog(null, "在同一窗口只能登录一次。");
                    return;
                }
                String userName = tf_newUser.getText().trim();// 获得登录用户名
                out.println("用户:" + userName);// 发送登录用户的名称
                out.flush();// 刷新输出缓冲区
                tf_newUser.setEnabled(false);
                loginFlag = true;
            }
        });
        button_1.setText("登  录");
        panel_1.add(button_1);
    }
}

服务器启动

客户端1和客户端2登录

客户端小小向客户端虚虚发送消息

客户端虚虚向客户端小小发送消息

注:小小发给虚虚时,小小自己的界面不显示自己发出的内容。本示例主要是为了演示客户端向指定客户端发送消息。

多学一个知识点

swing的开发过程,要了解3种线程的概念:

1、初始化线程 :此类线程将执行初始化应用代码。

2、事件调度线程 :所有的事件处理代码在这里执行。大多数与Swing框架 交互的代码也必须执行这个线程。

事件调度线程是单线程的:因为 Swing里面的各种组件类,比如JTextField,JButton 都不是线程安全的,这就意味着,如果有多个线程,那么同一个JTextField的setText方法,可能会被多个线程同时调用,这会导致同步问题以及错误数据的发生

3、工作线程 :也称作background threads(后台线程),此类线程将执行所有消耗时间的任务。

比如的事件监听——在actionPerformed 里放一个长耗时任务,如:数据库访问连接 建立网络连接 文件复制等等 就会自动进入事件调度线程。 而事件调度线程又是单线程模式,其结果就会是在执行这些长耗时任务的时候,界面就无响应了。

为了解决这个问题,Swing提供了一个SwingWorker类来解决。 SwingWorker是一个抽象类,为了使用,必须实现方法 doInBackground,在doInBackground中,就可以编写我们的任务,然后执行SwingWorker的execute方法,放在专门的工作线程中去运行。

上面题目里,ClientOneToOneClientFrame类中的createClientSocket()里就用到了SwingWorker

以上就是Java聊天室之实现客户端一对一聊天功能的详细内容,更多关于Java聊天室的资料请关注我们其它相关文章!

(0)

相关推荐

  • Java聊天室之使用Socket实现传递对象

    目录 一.题目描述 二.解题思路 三.代码详解 一.题目描述 题目实现:使用网络编程时,需要通过Socket传递对象. 二.解题思路 创建一个类:Student,实现序列化 ​ Student类包含两个属性及对应的get()和set()方法 创建一个服务器类:ServerSocketFrame,继承JFrame类 写一个getserver() 方法,实例化Socket对象,启用9527当服务的端口. 创建输入流对象,用来接收客户端信息. 再定义一个getClientInfo()方法,用于接收客户

  • Java聊天室之实现使用Socket传递音频

    目录 一.题目描述 二.解题思路 三.代码详解 多学一个知识点 一.题目描述 题目实现:使用网络编程时,需要通过Socket传递音频文件. 二.解题思路 创建一个服务器类:ServerSocketFrame,继承JFrame类 写一个getserver() 方法,实例化Socket对象,启用9527当服务的端口. 创建输入流对象,用来接收客户端信息. 再定义一个getClientInfo()方法,用于接收客户端发送的音频文件. 创建一个客户端类:ClientSocketFrame,继承JFram

  • Java聊天室之使用Socket实现传递图片

    目录 一.题目描述 二.解题思路 三.代码详解 一.题目描述 题目实现:使用网络编程时,需要通过Socket传递图片. 二.解题思路 创建一个服务器类:ServerSocketFrame,继承JFrame类 写一个getserver() 方法,实例化Socket对象,启用9527当服务的端口. 创建输入流对象,用来接收客户端信息. 再定义一个getClientInfo()方法,用于接收客户端发送的信息. 对文本框添加一个事件:实现向客户端发磅信息. 创建一个客户端类:ClientSocketFr

  • Java聊天室之实现运行服务器与等待客户端连接

    目录 一.题目描述 二.解题思路 三.代码详解 一.题目描述 题目实现:运行服务器程序,等待客户端程序连接. 二.解题思路 首先需要在服务器中建立服务器套接字,并等待客户程序的连接. 创建一个类:ServerSocketFrame,继承JFrame 通过ServerSocket类创建绑定到指定端口的服务器套接字对象.然后调用ServerSocket类的accept()方法监听客户端的连接. 实例化Socket对象 server = new ServerSocket(9527); // 可指定端口

  • Java聊天室之实现客户端一对一聊天功能

    目录 一.题目描述 二.解题思路 三.代码详解 多学一个知识点 一.题目描述 题目实现:不同的客户端之间需要进行通信,一个客户端与指定的另一客户端进行通信,实现一对一聊天功能. 实现一个客户端与指定的另一客户端进行通信,运行程序,服务器启动后,启动3个客户端程序,分别以小小,虚虚,竹竹,登录 ,然后在左侧的用户列表中选择接收信息用户,输入聊天信息,发送到目标用户. 二.解题思路 创建一个服务类:ClientOneToOneServerFrame,继承JFrame类 定义ServerThread线

  • Java聊天室之使用Socket实现通信功能

    目录 一.题目描述 二.解题思路 三.代码详解 一.题目描述 题目实现:使用套接字实现了服务器端与客户端的通信. 运行程序,在服务器端的文本框中输入信息,然后按回车键,客户端就会收到服务器端发送的信息; 在客户端的文本框中输入信息,然后按回车键,服务器端就会收到客户端发送的信息,发送信息后的效果. 二.解题思路 创建一个服务器类:ServerSocketFrame,继承JFrame类 写一个getserver() 方法,实例化Socket对象,启用9527当服务的端口. 创建输入流对象,用来接收

  • Websocket直播间聊天室教程  GoEasy快速实现聊天室

    最近两年直播那个火啊,真的是无法形容!经常有朋友问起,我想实现一个直播间聊天或者我想开发一个聊天室, 要如何开始呢? 今天小编就手把手的教你用GoEasy做一个聊天室,当然也可以用于直播间内的互动.全套源码已经开源,git地址: https://gitee.com/goeasy-io/GoEasyDemo-Live-Chatroom.git 本教程主要目的是为大家介绍实现思路,为了确保本教程能帮助到使用不同前端技术的朋友,采用了HTML + JQuery的方式,后续还会推出Uniapp(vue/

  • 基于java编写局域网多人聊天室

    由于需要制作网络计算机网络课程设计,并且不想搞网络布线或者局域网路由器配置等等这种完全搞不懂的东西,最后决定使用socket基于java编写一个局域网聊天室: 关于socket以及网络编程的相关知识详见我另一篇文章:Java基于socket编程 程序基于C/S结构,即客户端服务器模式. 服务器: 默认ip为本机ip 需要双方确定一个端口号 可设置最大连接人数 可启动与关闭 界面显示在线用户人以及姓名(本机不在此显示) 客户端: 需要手动设置服务器ip地址(局域网) 手动设置端口号 输入姓名 可连

  • Java基于UDP协议实现简单的聊天室程序

    最近比较闲,一直在抽空回顾一些Java方面的技术应用. 今天没什么事做,基于UDP协议,写了一个非常简单的聊天室程序. 现在的工作,很少用到socket,也算是对Java网络编程方面的一个简单回忆. 先看一下效果: 实现的效果可以说是非常非常简单,但还是可以简单的看到一个实现原理.  "聊天室001"的用户,小红和小绿相互聊了两句,"聊天室002"的小黑无人理会,在一旁寂寞着. 看一下代码实现: 1.首先是消息服务器的实现,功能很简单: •将客户端的信息(进入了哪一

  • java socket实现聊天室 java实现多人聊天功能

    用java socket做一个聊天室,实现多人聊天的功能.看了极客学院的视频后跟着敲的.(1DAY) 服务端: 1. 先写服务端的类MyServerSocket,里面放一个监听线程,一启动就好 2. 实现服务端监听类ServerListener.java,用accept来监听,一旦有客户端连上,生成新的socket,就新建个线程实例ChatSocket.启动线程后就把线程交给ChatManager管理 3. 在ChatSocket中实现从客户端读取内容,把读取到的内容发给集合内所有的客户端 4.

  • java控制台输出版多人聊天室

    本文实例为大家分享了java控制台输出版多人聊天室的具体代码,供大家参考,具体内容如下 多人聊天室就要用到网络以及多线程以及输入输出流的知识 多人聊天室就是多个人同时聊天,一个发一条信息所有人都是可以接收得到. 接下来我们捋清楚一下思路 在这里面服务器充当了一个转发的作用,服务器要做的事情是,得到客户写进缓冲流里面的信息,再把得到的信息再写进其他客户端的缓冲流里面,得到其他用户的缓冲流我们可以通过集合把用户的全部Socket对象写进集合里面,客户端把客户想要发送的信息写进缓冲流里,等待客户端发来

  • Java基于UDP协议的聊天室功能

    UDP简述 UDP(User Datagram Protocol)协议是Internet 协议集支持的一个无连接的传输协议,中文名为用户数据报协议.它为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据报的方法. Internet 的传输层有两个主要协议,互为补充.无连接的是 UDP,它除了给应用程序发送数据包功能并允许它们在所需的层次上架构自己的协议之外,几乎没有做什么特别的事情.面向连接的是TCP,该协议几乎做了所有的事情.“when in trouble , use TCP”. 那

  • 使用java基于pushlet和bootstrap实现的简单聊天室

    这是一个简单的不能再简单的聊天室,本代码包含以下功能 1.用户注册. 2.用户登录. 3.当然还可以聊天. DBUtil.java 复制代码 代码如下: package com.hongyuan.core;   import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statemen

  • Android中基于XMPP协议实现IM聊天程序与多人聊天室

    简单的IM聊天程序 由于项目需要做一个基于XMPP协议的Android通讯软件.故开始研究XMPP. XMPP协议采用的是客户端-服务器架构,所有从一个客户端发到另一个客户端的消息和数据都必须经过XMPP服务器转发,而且支持服务器间DNS的路由,也就是说可以构建服务器集群,使不同的 服务器下的客户端也可以通信,XMPP的前身是一个开源组织制定的网络通信协议--Jabber,XMPP的核心是在网络上分片段发送XML流的协议,这个协议是XMPP的即时通讯指令的传递手段.       为了防止服务器间

随机推荐