IT俱乐部 Java Java聊天室之实现客户端一对一聊天功能

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
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>
<p> * @date 2022/6/5<br>
 */<br>
public class ClientOneToOneServerFrame  extends JFrame{<br>
    private JTextArea ta_info;<br>
    private ServerSocket server; // 声明ServerSocket对象<br>
    private Socket socket; // 声明Socket对象socket<br>
    private Hashtable map = new Hashtable();// 用于存储连接到服务器的用户和客户端套接字对象</p>
<p>    public void createSocket() {<br>
        try {<br>
            server = new ServerSocket(9527);<br>
            while (true) {<br>
                ta_info.append("等待新客户连接......n");<br>
                socket = server.accept();// 创建套接字对象<br>
                ta_info.append("客户端连接成功。" + socket + "n");<br>
                new ServerThread(socket).start();// 创建并启动线程对象<br>
            }<br>
        } catch (IOException e) {<br>
            e.printStackTrace();<br>
        }<br>
    }</p>
<p>    class ServerThread extends Thread {<br>
        Socket socket;</p>
<p>        public ServerThread(Socket socket) {<br>
            this.socket = socket;<br>
        }</p>
<p>        public void run() {<br>
            try {<br>
                BufferedReader in = new BufferedReader(new InputStreamReader(<br>
                        socket.getInputStream()));// 创建输入流对象<br>
                while (true) {<br>
                    String info = in.readLine();// 读取信息<br>
                    String key = "";<br>
                    if (info.startsWith("用户:")) {// 添加登录用户到客户端列表<br>
                        key = info.substring(3, info.length());// 获得用户名并作为键使用<br>
                        map.put(key, socket);// 添加键值对<br>
                        Set set = map.keySet();// 获得集合中所有键的Set视图<br>
                        Iterator keyIt = set.iterator();// 获得所有键的迭代器<br>
                        while (keyIt.hasNext()) {<br>
                            String receiveKey = keyIt.next();// 获得表示接收信息的键<br>
                            Socket s = map.get(receiveKey);// 获得与该键对应的套接字对象<br>
                            PrintWriter out = new PrintWriter(s<br>
                                    .getOutputStream(), true);// 创建输出流对象<br>
                            Iterator keyIt1 = set.iterator();// 获得所有键的迭代器<br>
                            while (keyIt1.hasNext()) {<br>
                                String receiveKey1 = keyIt1.next();// 获得键,用于向客户端添加用户列表<br>
                                out.println(receiveKey1);// 发送信息<br>
                                out.flush();// 刷新输出缓冲区<br>
                            }<br>
                        }</p>
<p>                    } else {// 转发接收的消息<br>
                        key = info.substring(info.indexOf(":发送给:") + 5, info<br>
                                .indexOf(":的信息是:"));// 获得接收方的key值,即接收方的用户名<br>
                        String sendUser = info.substring(0, info<br>
                                .indexOf(":发送给:"));// 获得发送方的key值,即发送方的用户名<br>
                        Set set = map.keySet();// 获得集合中所有键的Set视图<br>
                        Iterator keyIt = set.iterator();// 获得所有键的迭代器<br>
                        while (keyIt.hasNext()) {<br>
                            String receiveKey = keyIt.next();// 获得表示接收信息的键<br>
                            if (key.equals(receiveKey)<br>
                                    && !sendUser.equals(receiveKey)) {// 如果是发送方,但不是用户本身<br>
                                Socket s = map.get(receiveKey);// 获得与该键对应的套接字对象<br>
                                PrintWriter out = new PrintWriter(s<br>
                                        .getOutputStream(), true);// 创建输出流对象</p>
<p>                                out.println("MSG:"+info);// 发送信息<br>
                                out.flush();// 刷新输出缓冲区<br>
                            }<br>
                        }<br>
                    }<br>
                }<br>
            } catch (IOException e) {<br>
                ta_info.append(socket + "已经退出。n");<br>
            }<br>
        }<br>
    }</p>
<p>    /**<br>
     * Launch the application<br>
     *<br>
     * @param args<br>
     */<br>
    public static void main(String args[]) {<br>
        ClientOneToOneServerFrame frame = new ClientOneToOneServerFrame();<br>
        frame.setVisible(true);<br>
        frame.createSocket();<br>
    }</p>
<p>    /**<br>
     * Create the frame<br>
     */<br>
    public ClientOneToOneServerFrame() {<br>
        super();<br>
        setTitle("客户端一对一通信——服务器端程序");<br>
        setBounds(100, 100, 385, 266);<br>
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);</p>
<p>        final JScrollPane scrollPane = new JScrollPane();<br>
        getContentPane().add(scrollPane, BorderLayout.CENTER);</p>
<p>        ta_info = new JTextArea();<br>
        scrollPane.setViewportView(ta_info);<br>
    }<br>
}
</p>

ClientOneToOneClientFrame

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
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>
<p> * @date 2022/6/5<br>
 */<br>
public class ClientOneToOneClientFrame extends JFrame{<br>
    private JTextField tf_newUser;<br>
    private JList user_list;<br>
    private JTextArea ta_info;<br>
    private JTextField tf_send;<br>
    PrintWriter out;// 声明输出流对象<br>
    private boolean loginFlag = false;// 为true时表示已经登录,为false时表示未登录<br>
    private Socket socket;<br>
    /**<br>
     * Launch the application<br>
     *<br>
     * @param args<br>
     */<br>
    public static void main(String args[]) {<br>
        EventQueue.invokeLater(new Runnable() {<br>
            public void run() {<br>
                try {<br>
                    ClientOneToOneClientFrame frame = new ClientOneToOneClientFrame();<br>
                    frame.setVisible(true);<br>
                    frame.createClientSocket();// 调用方法创建套接字对象<br>
                } catch (Exception e) {<br>
                    e.printStackTrace();<br>
                }<br>
            }<br>
        });<br>
    }</p>
<p>    public void createClientSocket() {<br>
        try {<br>
             socket = new Socket("127.0.0.1", 9527);// 创建套接字对象<br>
            out = new PrintWriter(socket.getOutputStream(), true);// 创建输出流对象<br>
            SwingWorker worker=new SwingWorker() {<br>
                @Override<br>
                protected Void doInBackground() throws Exception {<br>
                    try {<br>
                        BufferedReader in = new BufferedReader(new InputStreamReader(<br>
                                socket.getInputStream()));// 创建输入流对象<br>
                        DefaultComboBoxModel model = (DefaultComboBoxModel) user_list<br>
                                .getModel();// 获得列表框的模型<br>
                        while (true) {<br>
                            String info = in.readLine().trim();// 读取信息</p>
<p>                            if (!info.startsWith("MSG:")) {<br>
                                boolean itemFlag = false;// 标记是否为列表框添加列表项,为true不添加,为false添加<br>
                                for (int i = 0; i </p>

服务器启动

客户端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聊天室的资料请关注IT俱乐部其它相关文章!

本文收集自网络,不代表IT俱乐部立场,转载请注明出处。https://www.2it.club/code/java/6734.html
上一篇
下一篇
联系我们

联系我们

在线咨询: QQ交谈

邮箱: 1120393934@qq.com

工作时间:周一至周五,9:00-17:30,节假日休息

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部