一、题目描述
题目实现:不同的客户端之间需要进行通信,一个客户端与指定的另一客户端进行通信,实现一对一聊天功能。
实现一个客户端与指定的另一客户端进行通信,运行程序,服务器启动后,启动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俱乐部其它相关文章!