您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“怎么使用Java編寫網(wǎng)絡(luò)聊天程序”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
實(shí)驗(yàn)?zāi)康模?/strong>
使用客戶機(jī)/服務(wù)器模式、基于TCP協(xié)議編寫一對(duì)多“群聊”程序。其中客戶機(jī)端單擊“連接服務(wù)器”或“斷開連接”按鈕,均能即時(shí)更新服務(wù)器和所有客戶機(jī)的在線人數(shù)和客戶名。
實(shí)驗(yàn)要求:
設(shè)計(jì)一對(duì)多的網(wǎng)絡(luò)聊天程序,要求:
1、基于TCP/IP設(shè)計(jì)聊天程序
2、采用圖形界面設(shè)計(jì)
3、能夠進(jìn)行一對(duì)多聊天
項(xiàng)目截圖
服務(wù)器端代碼:
import javax.swing.*; import javax.swing.border.TitledBorder; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.Vector; public class Server extends JFrame { // TODO 該圖形界面擁有三塊區(qū)域,分別位于上、中、下 (up、middle、down)。 private JPanel panUp = new JPanel(); private JPanel panMid = new JPanel(); private JPanel panDown = new JPanel(); // panUp 區(qū)域的子節(jié)點(diǎn)定義,標(biāo)簽、輸入框、按鈕 private JLabel lblLocalPort = new JLabel("本機(jī)服務(wù)器監(jiān)聽端口:"); protected JButton butStart = new JButton("啟動(dòng)服務(wù)器"); protected JTextField tfLocalPort = new JTextField(25); // panMid 區(qū)域的子節(jié)點(diǎn)定義,顯示框 以及 滾動(dòng)條 protected JTextArea taMsg = new JTextArea(25, 25); JScrollPane scroll = new JScrollPane(taMsg); // panDown 區(qū)域的子節(jié)點(diǎn)定義,lstUsers在線用戶界面 JList lstUsers = new JList(); // TODO 以下是存放數(shù)據(jù)的變量 public static int localPort = 8000; // 默認(rèn)端口 8000 static int SerialNum = 0; // 用戶連接數(shù)量 ServerSocket serverSocket; // 服務(wù)器端 Socket ArrayList<AcceptRunnable.Client> clients = new ArrayList<>(); // 用戶連接對(duì)象數(shù)組 Vector<String> clientNames = new Vector<>(); // lstUsers 中存放的數(shù)據(jù) // TODO 構(gòu)造方法 public Server() { init(); } // TODO 初始化方法:初始化圖形界面布局 private void init() { // panUp 區(qū)域初始化:流式區(qū)域 panUp.setLayout(new FlowLayout()); panUp.add(lblLocalPort); panUp.add(tfLocalPort); panUp.add(butStart); tfLocalPort.setText(String.valueOf(localPort)); butStart.addActionListener(new startServerHandler()); // 注冊(cè) "啟動(dòng)服務(wù)器" 按鈕點(diǎn)擊事件 // panMid 區(qū)域初始化 panMid.setBorder(new TitledBorder("監(jiān)聽消息")); taMsg.setEditable(false); panMid.add(scroll); // panDown 區(qū)域初始化 panDown.setBorder(new TitledBorder("在線用戶")); panDown.add(lstUsers); lstUsers.setVisibleRowCount(10); // 圖形界面的總體初始化 + 啟動(dòng)圖形界面 this.setTitle("服務(wù)器端"); this.add(panUp, BorderLayout.NORTH); this.add(panMid, BorderLayout.CENTER); this.add(panDown, BorderLayout.SOUTH); this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); this.setPreferredSize(new Dimension(600, 400)); this.pack(); this.setVisible(true); } // TODO “啟動(dòng)服務(wù)器”按鈕的動(dòng)作事件監(jiān)聽處理類 private class startServerHandler implements ActionListener { @Override public void actionPerformed(ActionEvent e) { try { // 當(dāng)點(diǎn)擊按鈕時(shí),獲取端口設(shè)置并啟動(dòng)新進(jìn)程、監(jiān)聽端口 localPort = Integer.parseInt(tfLocalPort.getText()); serverSocket = new ServerSocket(localPort); Thread acptThrd = new Thread(new AcceptRunnable()); acptThrd.start(); taMsg.append("**** 服務(wù)器(端口" + localPort + ")已啟動(dòng) ****\n"); } catch (Exception ex) { System.out.println(ex); } } } // TODO 接受用戶連接請(qǐng)求的線程關(guān)聯(lián)類 private class AcceptRunnable implements Runnable { public void run() { // 持續(xù)監(jiān)聽端口,當(dāng)有新用戶連接時(shí) 再開啟新進(jìn)程 while (true) { try { Socket socket = serverSocket.accept(); // 新的用戶已連接,創(chuàng)建 Client 對(duì)象 Client client = new Client(socket); taMsg.append("——客戶【" + client.nickname + "】加入\n"); Thread clientThread = new Thread(client); clientThread.start(); clients.add(client); } catch (Exception ex) { System.out.println(ex); } } } // TODO 服務(wù)器存放用戶對(duì)象的客戶類(主要編程)。每當(dāng)有新的用戶連接時(shí),該類都會(huì)被調(diào)用 // TODO 該類繼承自 Runnable,內(nèi)部含有 run()方法 private class Client implements Runnable { private Socket socket; // 用來保存用戶的連接對(duì)象 private BufferedReader in; // IO 流 private PrintStream out; private String nickname; // 保存用戶昵稱 // Client類的構(gòu)建方法。當(dāng)有 新用戶 連接時(shí)會(huì)被調(diào)用 public Client(Socket socket) throws Exception { this.socket = socket; InputStream is = socket.getInputStream(); in = new BufferedReader(new InputStreamReader(is)); OutputStream os = socket.getOutputStream(); out = new PrintStream(os); nickname = in.readLine(); // 獲取用戶昵稱 for (Client c : clients) { // 將新用戶的登錄消息發(fā)給所有用戶 c.out.println("——客戶【" + nickname + "】加入\n"); } } //客戶類線程運(yùn)行方法 public void run() { try { while (true) { String usermsg = in.readLine(); //讀用戶發(fā)來消息 String secondMsg = usermsg.substring(usermsg.lastIndexOf(":") + 1); // 字符串輔助對(duì)象 // 如果用戶發(fā)過來的消息不為空 if (usermsg != null && usermsg.length() > 0) { // 如果消息是 bye,則斷開與此用戶的連接 并 告知所有用戶當(dāng)前信息,跳出循環(huán)終止當(dāng)前進(jìn)程 if (secondMsg.equals("bye")) { clients.remove(this); for (Client c : clients) { c.out.println(usermsg); } taMsg.append("——客戶離開:" + nickname + "\n"); // 更新在線用戶數(shù)量 lstUsers的界面信息 updateUsers(); break; } /** * 每當(dāng)有新用戶連接時(shí),服務(wù)器就會(huì)接收到 USERS 請(qǐng)求 * 當(dāng)服務(wù)器接收到此請(qǐng)求時(shí),就會(huì)要求現(xiàn)在所有用戶更新 在線用戶數(shù)量 的列表 * */ if (usermsg.equals("USERS")) { updateUsers(); continue; } // 當(dāng)用戶發(fā)出的消息都不是以上兩者時(shí),消息才會(huì)被正常發(fā)送 for (Client c : clients) { c.out.println(usermsg); } } } socket.close(); } catch (Exception ex) { System.out.println(ex); } } // TODO 更新在線用戶數(shù)量 lstUsers 信息,并要求所有的用戶端同步更新 public void updateUsers() { // clientNames 是 Vector<String>對(duì)象,用來存放所有用戶的名字 clientNames.removeAllElements(); StringBuffer allname = new StringBuffer(); for (AcceptRunnable.Client client : clients) { clientNames.add(0, client.nickname); allname.insert(0, "|" + client.nickname); } panDown.setBorder(new TitledBorder("在線用戶(" +clientNames.size() + "個(gè))")); // 要求所有的用戶端同步更新 for (Client c : clients) { c.out.println(clientNames); } lstUsers.setListData(clientNames); } } } // TODO 主方法 public static void main(String[] args) { new Server(); } }
客戶端代碼:
import javax.swing.*; import javax.swing.border.TitledBorder; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.Socket; import java.util.Vector; public class Client extends JFrame { //客戶機(jī)窗體類 // TODO 該圖形界面擁有四塊區(qū)域,分別位于上、左、中、下 (up、Left、middle、down)。 private JPanel panUp = new JPanel(); private JPanel panLeft = new JPanel(); private JPanel panMid = new JPanel(); private JPanel panDown = new JPanel(); // panUp 區(qū)域的子節(jié)點(diǎn)定義,3個(gè)標(biāo)簽、3個(gè)輸入框、2個(gè)按鈕 private JLabel lblLocalPort1 = new JLabel("服務(wù)器IP: "); private JLabel lblLocalPort2 = new JLabel("端口: "); private JLabel lblLocalPort3 = new JLabel("本人昵稱: "); protected JTextField tfLocalPort1 = new JTextField(15); protected JTextField tfLocalPort2 = new JTextField(5); protected JTextField tfLocalPort3 = new JTextField(5); protected JButton butStart = new JButton("連接服務(wù)器"); protected JButton butStop = new JButton("斷開服務(wù)器"); // TODO // panLeft 區(qū)域的子節(jié)點(diǎn)定義,顯示框、滾動(dòng)條 protected JTextArea taMsg = new JTextArea(25, 25); JScrollPane scroll = new JScrollPane(taMsg); // panMid 區(qū)域的子節(jié)點(diǎn)定義,lstUsers在線用戶界面 JList lstUsers = new JList(); // panDown 區(qū)域的子節(jié)點(diǎn)定義,標(biāo)簽,輸入框 private JLabel lblLocalPort4 = new JLabel("消息(按回車發(fā)送): "); protected JTextField tfLocalPort4 = new JTextField(20); /** * ===== 變量分割 ===== * 上面是圖形界面變量,下面是存放數(shù)據(jù)的變量 */ BufferedReader in; PrintStream out; public static int localPort = 8000; // 默認(rèn)端口 public static String localIP = "127.0.0.1"; // 默認(rèn)服務(wù)器IP地址 public static String nickname = "Cat"; // 默認(rèn)用戶名 public Socket socket; public static String msg; // 存放本次發(fā)送的消息 Vector<String> clientNames = new Vector<>(); // TODO 構(gòu)造方法 public Client() { init(); } // TODO 初始化方法:初始化圖形界面 private void init() { // panUp 區(qū)域初始化:流式面板,3個(gè)標(biāo)簽、3個(gè)輸入框,2個(gè)按鈕 panUp.setLayout(new FlowLayout()); panUp.add(lblLocalPort1); panUp.add(tfLocalPort1); panUp.add(lblLocalPort2); panUp.add(tfLocalPort2); panUp.add(lblLocalPort3); panUp.add(tfLocalPort3); tfLocalPort1.setText(localIP); tfLocalPort2.setText(String.valueOf(localPort)); tfLocalPort3.setText(nickname); panUp.add(butStart); panUp.add(butStop); butStart.addActionListener(new linkServerHandlerStart()); butStop.addActionListener(new linkServerHandlerStop()); butStop.setEnabled(false); // 斷開服務(wù)器按鈕的初始狀態(tài)應(yīng)該為 不可點(diǎn)擊,只有連接服務(wù)器之后才能點(diǎn)擊 // 添加 Left taMsg.setEditable(false); panLeft.add(scroll); panLeft.setBorder(new TitledBorder("聊天——消息區(qū)")); scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); // 添加 Middle panMid.setBorder(new TitledBorder("在線用戶")); panMid.add(lstUsers); lstUsers.setVisibleRowCount(20); // 添加 Down // TODO 此處注意:JTextField輸入框 的回車事件默認(rèn)存在,無需添加 panDown.setLayout(new FlowLayout()); panDown.add(lblLocalPort4); panDown.add(tfLocalPort4); tfLocalPort4.addActionListener(new Client.SendHandler()); // 圖形界面的總體初始化 + 啟動(dòng)圖形界面 this.setTitle("客戶端"); this.add(panUp, BorderLayout.NORTH); this.add(panLeft, BorderLayout.WEST); this.add(panMid, BorderLayout.CENTER); this.add(panDown, BorderLayout.SOUTH); this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); this.addWindowListener(new WindowHandler()); this.setPreferredSize(new Dimension(800, 600)); this.pack(); this.setVisible(true); } // TODO “連接服務(wù)器”按鈕的動(dòng)作事件監(jiān)聽處理類: private class linkServerHandlerStart implements ActionListener { @Override public void actionPerformed(ActionEvent e) { // 當(dāng)點(diǎn)擊"連接服務(wù)器"按鈕之后,該按鈕被禁用(不可重復(fù)點(diǎn)擊)。同時(shí)"斷開服務(wù)器按鈕"被恢復(fù)使用 butStart.setEnabled(false); butStop.setEnabled(true); localIP = tfLocalPort1.getText(); localPort = Integer.parseInt(tfLocalPort2.getText()); nickname = tfLocalPort3.getText(); linkServer(); // 連接服務(wù)器 Thread acceptThread = new Thread(new Client.ReceiveRunnable()); acceptThread.start(); } } // TODO “斷開服務(wù)器”按鈕的動(dòng)作事件監(jiān)聽處理類 private class linkServerHandlerStop implements ActionListener { /** * 當(dāng)點(diǎn)擊該按鈕之后,斷開服務(wù)器連接、清空?qǐng)D形界面所有數(shù)據(jù) */ @Override public void actionPerformed(ActionEvent e) { taMsg.setText(""); clientNames = new Vector<>(); updateUsers(); out.println("——客戶【" + nickname + "】離開:bye\n"); butStart.setEnabled(true); butStop.setEnabled(false); } } // TODO 連接服務(wù)器的方法 public void linkServer() { try { socket = new Socket(localIP, localPort); } catch (Exception ex) { taMsg.append("==== 連接服務(wù)器失敗~ ===="); } } // TODO 接收服務(wù)器消息的線程關(guān)聯(lián)類 private class ReceiveRunnable implements Runnable { public void run() { try { in = new BufferedReader(new InputStreamReader(socket.getInputStream())); out = new PrintStream(socket.getOutputStream()); out.println(nickname); // 當(dāng)用戶首次連接服務(wù)器時(shí),應(yīng)該向服務(wù)器發(fā)送自己的用戶名、方便服務(wù)器區(qū)分 taMsg.append("——本人【" + nickname + "】成功連接到服務(wù)器......\n"); out.println("USERS"); // 向服務(wù)器發(fā)送"神秘代碼",請(qǐng)求 當(dāng)前在線用戶 列表 while (true) { msg = in.readLine(); // 讀取服務(wù)器端的發(fā)送的數(shù)據(jù) // 此 if 語句的作用是:過濾服務(wù)器發(fā)送過來的 更新當(dāng)前在線用戶列表 請(qǐng)求 if (msg.matches(".*\\[.*\\].*")) { clientNames.removeAllElements(); String[] split = msg.split(","); for (String single : split) { clientNames.add(single); } updateUsers(); continue; } // 更新 "聊天——消息區(qū)" 信息 taMsg.append(msg + "\n"); // 此 if 語句作用:與服務(wù)器進(jìn)行握手確認(rèn)消息。 // 當(dāng)接收到服務(wù)器端發(fā)送的確認(rèn)離開請(qǐng)求bye 的時(shí)候,用戶真正離線 msg = msg.substring(msg.lastIndexOf(":") + 1); if (msg.equals(nickname)) { socket.close(); clientNames.remove(nickname); updateUsers(); break; // 終止線程 } } } catch (Exception e) { } } } // TODO "發(fā)送消息文本框" 的動(dòng)作事件監(jiān)聽處理類 private class SendHandler implements ActionListener { @Override public void actionPerformed(ActionEvent e) { out.println("【" + nickname + "】:" + tfLocalPort4.getText()); tfLocalPort4.setText(""); // 當(dāng)按下回車發(fā)送消息之后,輸入框應(yīng)該被清空 } } // TODO 窗口關(guān)閉的動(dòng)作事件監(jiān)聽處理類 // 當(dāng)用戶點(diǎn)擊 "x" 離開窗口時(shí),也會(huì)向服務(wù)器發(fā)送 bye 請(qǐng)求,目的是為了同步更新數(shù)據(jù)。 private class WindowHandler extends WindowAdapter { @Override public void windowClosing(WindowEvent e) { cutServer(); } } private void cutServer() { out.println("——客戶【" + nickname + "】離開:bye"); } // TODO 更新 "在線用戶列表" 的方法 public void updateUsers() { panMid.setBorder(new TitledBorder("在線用戶(" + clientNames.size() + "個(gè))")); lstUsers.setListData(clientNames); } // TODO 主方法 public static void main(String[] args) { new Client(); } }
如何同時(shí)開啟兩個(gè)客戶端進(jìn)行聊天?
將上述的 Client 類復(fù)制一份,改名為 Client2 ,然后同時(shí)啟動(dòng) Client 和 Client2 程序。
“怎么使用Java編寫網(wǎng)絡(luò)聊天程序”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。