创建专属聊天室练习(客户端与服务端通信|客户端界面交互)
生活随笔
收集整理的這篇文章主要介紹了
创建专属聊天室练习(客户端与服务端通信|客户端界面交互)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
一、知識回顧與補充
package frame;import javax.swing.*;//只是發(fā)現(xiàn)以后所有的框都是按照剛才那套流程畫出來的 //只不過具體的組件,位置,布局都不一樣,但是需要做的事情抽象來看都是一樣的 //設(shè)計一個抽象的規(guī)則//模板方法模式 23個設(shè)計模式當(dāng)中的一個//還有一種是:缺省適配器模式 MyFrame(接口 10個) baseFrame(挑一些不重要的實現(xiàn)掉{} 子類去實現(xiàn)一些必要的方法) public abstract class BaseFrame extends JFrame {public BaseFrame(String title){super(title);}protected abstract void setOther();protected abstract void addElements();protected abstract void setSelf(); } package frame;import javax.swing.*; import java.awt.*;//簡單梳理一下關(guān)于Swing的技術(shù) //GUI 圖形用戶接口(規(guī)則) Client/Server //GUI下面有兩個接口:AWT Swing。 可以做但很少用來基于Browser/Server HTML <html></html> //想要利用Swing技術(shù)來實現(xiàn)一個窗口的繪制 //里面有JFrame窗體 可看成類似于<body> //還有JPanel面板 可看成類似于<div><span> 無色透明的容器 做布局管理 //各種組件 按鈕 文本框 文本域 理解為HTMl中那些<input type="button"> //JButton按鈕 JTextField //最終肯定是需要有功能 事件-方法 HTML(布局)+CSS(樣式)+JS(事件) Tomcat //事件背后才是我們真正需要實現(xiàn)的強大邏輯/功能//============================================================ //需要自己描述一個類 //可能需要描述屬性,方法 //類與類的關(guān)系 //繼承 泛化(實現(xiàn)) A is a B // 聚合 組合 A has-a B // 包含 依賴 A use-a B public class QQFrame extends BaseFrame{//是一個窗口 類和類之間的關(guān)系 A is-a B 繼承//1.需要一個窗體/窗口private JFrame frame=new JFrame("聊天框");//GC 邊界管理//2.需要一個無色透明的容器--面板private JPanel panel=new JPanel();// 流式管理 可以有多個//3.需要一些聊天窗口的組件//這是兩個帶滾動條的文本域private JTextArea messArea=new JTextArea();private JScrollPane messPane=new JScrollPane(messArea);//滾動條范圍大,里面是包著文本域的private JTextArea sendArea=new JTextArea();private JScrollPane sendPand=new JScrollPane(sendArea);//兩個按鈕private JButton sendButton=new JButton("發(fā)送");private JButton cancelButton=new JButton("取消");//構(gòu)造方法 里面可以放這三個方法,或者這三個方法可以放在代碼塊,讓它在構(gòu)造方法之前加載public QQFrame(String title){super(title);//2.調(diào)用里面的方法this.setOther();this.addElements();this.setSelf();}//設(shè)計一個方法 做一些設(shè)置 布局 字體。。@Overrideprotected void setOther(){//4.為了讓布局更好的好看一點,我們采用一個自動布局的方式// 自定義布局需要將原有的布局清空panel.setLayout(null);//所有的組件按照自己的設(shè)計位置放置messPane.setBounds(10, 10, 320, 220);sendPand.setBounds(10, 240, 320, 140);sendButton.setBounds(180, 390, 60, 30);cancelButton.setBounds(260, 390, 60, 30);//設(shè)置上面接收框的信息不能更改messArea.setEnabled(false);//設(shè)置文本域中字體的效果messArea.setFont(new Font("宋體", Font.BOLD,18));sendArea.setFont(new Font("宋體", Font.BOLD,18));}//設(shè)計一個方法 做一些組件之間的相互添加@Overrideprotected void addElements(){//5.將這些組件放在panel里,panel放在frame里panel.add(messPane);panel.add(sendPand);panel.add(sendButton);panel.add(cancelButton);this.add(panel);}//設(shè)計一個方法 用來做窗體自己的一些設(shè)置 初始位置 不可拖拽@Overrideprotected void setSelf(){//需要在創(chuàng)建出來的同時,設(shè)置一下窗口的展示this.setBounds(500, 200, 350, 480);//可以設(shè)置窗口不可以拖拽改變大小this.setResizable(false);//設(shè)置點擊右上角關(guān)閉按鈕的同時,讓程序結(jié)束this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// private static final int xxx=3;//其實窗口已經(jīng)創(chuàng)建出來了,默認效果是隱藏的//需要設(shè)置那個窗口的狀態(tài) 隱藏--->顯示//面向?qū)ο? 窗口的狀態(tài)-----對象的屬性// 設(shè)置狀態(tài)----事情----方法 setXXXXthis.setVisible(true);} } package frame;public class Test {public static void main(String[] args) {//1.創(chuàng)建一個QQFrame窗口對象new QQFrame("聊天框");//無參數(shù)構(gòu)造方法} }
二、客戶端與服務(wù)器通信
package client;import java.io.*; import java.net.Socket; import java.util.Scanner;public class TestClient {public static void main(String[] args) {try {System.out.println("我是客戶端");//1.主動訪問服務(wù)端,訪問成功后,即連接成功,獲取一個socket對象Socket socket=new Socket("127.0.0.1",9999);//服務(wù)器地址(IP) 服務(wù)器開放的端口號(port)System.out.println("我成功連接到服務(wù)器啦?");//======================================Scanner input=new Scanner(System.in);//2.讀取服務(wù)器發(fā)送過來的消息 讀消息/輸入流對象//不能自己創(chuàng)建,需要通過socket對象來回去InputStream is = socket.getInputStream();//is其實可以拿去讀取消息,is是一個字節(jié)型輸入流//為了讀取字符更加方便,做一個包裝InputStreamReader isr=new InputStreamReader(is);//字節(jié)--->字符轉(zhuǎn)換流--->最終是個字符//isr其實可以讀取中文啦,但是由于對面發(fā)送過來的是一行為單位的,isr無法讀取一行數(shù)據(jù)//為了讀取一行比較方便,再做一個包裝BufferedReader reader = new BufferedReader(isr);//包裝本身體現(xiàn)出來一種設(shè)計模式——————裝飾者模式 23//3.客戶端回寫點什么 寫消息/輸出流對象OutputStream os = socket.getOutputStream();PrintWriter writer=new PrintWriter(os);while (true){String value = reader.readLine();System.out.println("消息"+value);System.out.println("回寫點什么吧:");String mess=input.nextLine();writer.println(mess);writer.flush();}} catch (IOException e) {e.printStackTrace();}} } package server;import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner;public class TestServer {public static void main(String[] args) {try {System.out.println("===Server start===");//1.開啟一個服務(wù)//調(diào)用構(gòu)造方法,缺少參數(shù)//沒法構(gòu)造對象,接口/抽象類//類中構(gòu)造方法雖然有,但是都是private(私有化),提供的都是類似工廠一樣的方法//構(gòu)建對象的時候,出現(xiàn)了編譯時異常// ArrayIndexOutofBoundsException// NullPointerException// InputMisMatchException// NumberFormatException// ArithmeticException// ClassCastException// StringIndexOutOfBoundsExceptionServerSocket server=new ServerSocket(9999);//0——65535//服務(wù)器開發(fā)的端口號,為了讓客戶的數(shù)據(jù)從這個端口進入到服務(wù)器來的//2.開啟服務(wù)之后,等待某一個客戶端過來訪問我,需要服務(wù)器統(tǒng)一/接收Socket socket = server.accept();System.out.println("有一個客戶端跟我連接成功啦");//================================================================Scanner input=new Scanner(System.in);//3.先讓服務(wù)器跟客戶端說一句話————寫消息/輸出流//不是自己隨意創(chuàng)建的輸出流,得通過socket來獲取OutputStream os = socket.getOutputStream(); //流(讀 I/寫O) 字節(jié)/字符(Reader、Writer)//上述os對象可以寫消息了,但是os是一個字節(jié)流,發(fā)送中文的時候不方便//字節(jié)流進行一個包裝---->字符流PrintWriter writer=new PrintWriter(os);//4.服務(wù)器讀取回寫的消息 讀信息/輸入流對象InputStream is = socket.getInputStream();InputStreamReader isr=new InputStreamReader(is);BufferedReader reader=new BufferedReader(isr);while(true){//4.可以發(fā)送數(shù)據(jù)啦System.out.println("說點什么吧:");String mess = input.nextLine();writer.println(mess);writer.flush();//刷新 清空流管道String value = reader.readLine();System.out.println("消息:"+value);}} catch (IOException e) {e.printStackTrace();}} }三、客戶端發(fā)起聊天
四、完成客戶端界面交互
優(yōu)化代碼
package frame;import javax.swing.*;//只是發(fā)現(xiàn)以后所有的框都是按照剛才那套流程畫出來的 //只不過具體的組件,位置,布局都不一樣,但是需要做的事情抽象來看都是一樣的 //設(shè)計一個抽象的規(guī)則//模板方法模式 23個設(shè)計模式當(dāng)中的一個//還有一種是:缺省適配器模式 MyFrame(接口 10個) baseFrame(挑一些不重要的實現(xiàn)掉{} 子類去實現(xiàn)一些必要的方法) public abstract class BaseFrame extends JFrame {public BaseFrame(String title){super(title);}protected abstract void setOther();protected abstract void addElements();protected abstract void addListener();protected abstract void setSelf(); } package frame;import javax.swing.*; import java.awt.*; import java.io.*; import java.net.Socket; import java.text.DateFormat; import java.text.FieldPosition; import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.Date;//簡單梳理一下關(guān)于Swing的技術(shù) //GUI 圖形用戶接口(規(guī)則) Client/Server //GUI下面有兩個接口:AWT Swing。 可以做但很少用來基于Browser/Server HTML <html></html> //想要利用Swing技術(shù)來實現(xiàn)一個窗口的繪制 //里面有JFrame窗體 可看成類似于<body> //還有JPanel面板 可看成類似于<div><span> 無色透明的容器 做布局管理 //各種組件 按鈕 文本框 文本域 理解為HTMl中那些<input type="button"> //JButton按鈕 JTextField //最終肯定是需要有功能 事件-方法 HTML(布局)+CSS(樣式)+JS(事件) Tomcat //事件背后才是我們真正需要實現(xiàn)的強大邏輯/功能//============================================================ //需要自己描述一個類 //可能需要描述屬性,方法 //類與類的關(guān)系 //繼承 泛化(實現(xiàn)) A is a B // 聚合 組合 A has-a B // 包含 依賴 A use-a B public class QQFrame extends BaseFrame{//是一個窗口 類和類之間的關(guān)系 A is-a B 繼承//構(gòu)造方法 里面可以放這三個方法,或者這三個方法可以放在代碼塊,讓它在構(gòu)造方法之前加載public QQFrame(String uid){super(uid);this.uid=uid;//1.真的跟服務(wù)器產(chǎn)生一個連接this.start();//2.調(diào)用里面的方法this.setOther();this.addElements();this.addListener();this.setSelf();}//1.需要一個窗體/窗口private JFrame frame=new JFrame("聊天框");//GC 邊界管理//2.需要一個無色透明的容器--面板private JPanel panel=new JPanel();// 流式管理 可以有多個//3.需要一些聊天窗口的組件//這是兩個帶滾動條的文本域private JTextArea messArea=new JTextArea();private JScrollPane messPane=new JScrollPane(messArea);//滾動條范圍大,里面是包著文本域的private JTextArea sendArea=new JTextArea();private JScrollPane sendPand=new JScrollPane(sendArea);//兩個按鈕private JButton sendButton=new JButton("發(fā)送");private JButton cancelButton=new JButton("取消");//設(shè)計一個方法 做一些設(shè)置 布局 字體。。@Overrideprotected void setOther(){//4.為了讓布局更好的好看一點,我們采用一個自動布局的方式// 自定義布局需要將原有的布局清空panel.setLayout(null);//所有的組件按照自己的設(shè)計位置放置messPane.setBounds(10, 10, 320, 220);sendPand.setBounds(10, 240, 320, 140);sendButton.setBounds(180, 390, 60, 30);cancelButton.setBounds(260, 390, 60, 30);//設(shè)置上面接收框的信息不能更改messArea.setEnabled(false);//設(shè)置文本域中字體的效果messArea.setFont(new Font("黑體", Font.BOLD,18));sendArea.setFont(new Font("黑體", Font.BOLD,18));}//設(shè)計一個方法 做一些組件之間的相互添加@Overrideprotected void addElements(){//5.將這些組件放在panel里,panel放在frame里panel.add(messPane);panel.add(sendPand);panel.add(sendButton);panel.add(cancelButton);this.add(panel);}//設(shè)計一個方法 給窗口中的某些組件添加事件@Overrideprotected void addListener(){//給取消按鈕綁定一個事件---對象(幫你去做) 觀察者模式Observer 按鈕-目標 做事-觀察者對象cancelButton.addActionListener(e->{sendArea.setText("");});//給發(fā)送按鈕綁定一個事件---sendButton.addActionListener(e -> {try {PrintWriter writer=new PrintWriter(socket.getOutputStream());String message = sendArea.getText();//獲取一個系統(tǒng)當(dāng)前時間Date date=new Date();DateFormat df=new SimpleDateFormat("yyyy-MM-dd kk:mm:ss");String time = df.format(date);writer.println(time+"##@##"+uid+":"+message);//客戶端真正發(fā)送的消息writer.flush();//發(fā)送框文字清空sendArea.setText("");} catch (IOException e1) {e1.printStackTrace();}});}//設(shè)計一個方法 用來做窗體自己的一些設(shè)置 初始位置 不可拖拽@Overrideprotected void setSelf(){//需要在創(chuàng)建出來的同時,設(shè)置一下窗口的展示this.setBounds(500, 200, 350, 480);//可以設(shè)置窗口不可以拖拽改變大小this.setResizable(false);//設(shè)置點擊右上角關(guān)閉按鈕的同時,讓程序結(jié)束this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// private static final int xxx=3;//其實窗口已經(jīng)創(chuàng)建出來了,默認效果是隱藏的//需要設(shè)置那個窗口的狀態(tài) 隱藏--->顯示//面向?qū)ο? 窗口的狀態(tài)-----對象的屬性// 設(shè)置狀態(tài)----事情----方法 setXXXXthis.setVisible(true);}//=============================================================================//因為當(dāng)前的QQFrame就是那個客戶端,// 所以客戶端需要有一個uidprivate String uid;//所以客戶端需要一個socketprivate Socket socket;//====================================================public void start(){try {//1.主動訪問服務(wù)端socket=new Socket("127.0.0.1",9999);//2.客戶端將自己的uid寫給服務(wù)器PrintWriter writer=new PrintWriter(socket.getOutputStream());writer.println(uid);writer.flush();//3.將socket分別交給兩個小弟,讀/寫ClientReader cr=new ClientReader();cr.start();} catch (IOException e) {e.printStackTrace();}}//====================================================//客戶端讀線程(剩下的那個小弟)---只管客戶端自己用//1.不想讓別人看見我客戶端自己不干活,還有一個小弟幫我//2.節(jié)省一個類文件//3.客戶端讀線程,讀取的數(shù)據(jù)不是在控制臺展示的,是需要展示在客戶端的文本域中// 最主要才是這個問題,內(nèi)部類中直接使用外部類成員private class ClientReader extends Thread{//頻繁的追加新的字符串,不能讓聊天框中的文字清空StringBuilder result=new StringBuilder();@Overridepublic void run(){//讀取try {InputStream is=socket.getInputStream();InputStreamReader isr=new InputStreamReader(is);BufferedReader reader=new BufferedReader(isr);//可以讀取一行while (true){String value=reader.readLine();//time##@##uid:messagevalue.replace("##@##", "\r\n");result.append(value);result.append("\n");//找到那個文本域,value放里即可messArea.setText(result.toString());}} catch (IOException e) {//e.printStackTrace();System.out.println("服務(wù)器宕機啦");}}}} package frame;import frame.QQFrame;public class TestClient {public static void main(String[] args) {//1.創(chuàng)建客戶端new QQFrame("zzt");new QQFrame("dmc");new QQFrame("Ella");} // public static void main(String[] args) { // try { // System.out.println("我是客戶端"); // //1.主動訪問服務(wù)端,訪問成功后,即連接成功,獲取一個socket對象 // Socket socket=new Socket("127.0.0.1",9999);//服務(wù)器地址(IP) 服務(wù)器開放的端口號(port) // System.out.println("我成功連接到服務(wù)器啦?"); // //====================================== // Scanner input=new Scanner(System.in); // // //2.讀取服務(wù)器發(fā)送過來的消息 讀消息/輸入流對象 // //不能自己創(chuàng)建,需要通過socket對象來回去 // InputStream is = socket.getInputStream(); // //is其實可以拿去讀取消息,is是一個字節(jié)型輸入流 // //為了讀取字符更加方便,做一個包裝 // InputStreamReader isr=new InputStreamReader(is);//字節(jié)--->字符轉(zhuǎn)換流--->最終是個字符 // //isr其實可以讀取中文啦,但是由于對面發(fā)送過來的是一行為單位的,isr無法讀取一行數(shù)據(jù) // //為了讀取一行比較方便,再做一個包裝 // BufferedReader reader = new BufferedReader(isr);//包裝本身體現(xiàn)出來一種設(shè)計模式——————裝飾者模式 23 // //3.客戶端回寫點什么 寫消息/輸出流對象 // OutputStream os = socket.getOutputStream(); // PrintWriter writer=new PrintWriter(os); // // while (true){ // String value = reader.readLine(); // System.out.println("消息"+value); // // System.out.println("回寫點什么吧:"); // String mess=input.nextLine(); // writer.println(mess); // writer.flush(); // } // } catch (IOException e) { // e.printStackTrace(); // } // // } } package server;import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap;//服務(wù)器類 public class Server {//屬性集合----Map<String,User>static HashMap<String,User> userBox=new HashMap();public void start(int port){try {System.out.println("====server start===");ServerSocket server=new ServerSocket(port);while (true){//服務(wù)器開啟,一直等待客戶端Socket socket = server.accept();//將socket對象存入map集合//讀取客戶端發(fā)來的Uid 服務(wù)端可以獲取socket,但是沒有Uid(客戶端發(fā)送過來)//<Uid,User(Uid,Socket)>InputStream is = socket.getInputStream();InputStreamReader isr=new InputStreamReader(is);BufferedReader reader=new BufferedReader(isr);String uid = reader.readLine();//構(gòu)建一個User對象User user=new User(uid, socket);userBox.put(uid, user);System.out.println(uid+"成功連接到服務(wù)器啦");//將這個socker交給一個小弟來負責(zé)處理ServerThread st=new ServerThread(user);st.start();}} catch (IOException e) {e.printStackTrace();}} } package server;import java.io.*; import java.util.Iterator; import java.util.Map; import java.util.Set;//服務(wù)端線程 public class ServerThread extends Thread{private User user;public ServerThread(User user){this.user=user;}@Overridepublic void run(){try {//1.讀取剛才那個socket客戶端發(fā)送過來的數(shù)據(jù)InputStream is = user.getSocket().getInputStream();InputStreamReader isr=new InputStreamReader(is);BufferedReader reader=new BufferedReader(isr);while (true){//服務(wù)器從客戶端那里讀取過來的真實數(shù)據(jù)/消息String message =reader.readLine();System.out.println("接收到"+user.getUid()+"xxx發(fā)送過來的"+message);//2.將數(shù)據(jù)轉(zhuǎn)發(fā)給其他socket對應(yīng)的客戶端------群聊 map遍歷 User(socket)//想要轉(zhuǎn)發(fā)出去,需要找到那個mapMap<String,User> map=Server.userBox;//<uid,user>//將map集合遍歷,獲取里面全部的user,拿到每一個user里面的socket--->Set keySet = map.keySet();Iterator it = keySet.iterator();while (it.hasNext()){String uid= (String)it.next();User user=map.get(uid);PrintWriter pw=new PrintWriter(user.getSocket().getOutputStream());pw.println(message);pw.flush();}}} catch (IOException e) {//e.printStackTrace();System.out.println(user.getUid()+"下線啦");}} } package server;public class TestServer {public static void main(String[] args) {//1.啟動一個服務(wù)Server server=new Server();//2.啟動即可server.start(9999);} // public static void main(String[] args) { // try { // System.out.println("===Server start==="); // //1.開啟一個服務(wù) // //調(diào)用構(gòu)造方法,缺少參數(shù) // //沒法構(gòu)造對象,接口/抽象類 // //類中構(gòu)造方法雖然有,但是都是private(私有化),提供的都是類似工廠一樣的方法 // //構(gòu)建對象的時候,出現(xiàn)了編譯時異常 // // ArrayIndexOutofBoundsException // // NullPointerException // // InputMisMatchException // // NumberFormatException // // ArithmeticException // // ClassCastException // // StringIndexOutOfBoundsException // ServerSocket server=new ServerSocket(9999);//0——65535//服務(wù)器開發(fā)的端口號,為了讓客戶的數(shù)據(jù)從這個端口進入到服務(wù)器來的 // //2.開啟服務(wù)之后,等待某一個客戶端過來訪問我,需要服務(wù)器統(tǒng)一/接收 // Socket socket = server.accept(); // System.out.println("有一個客戶端跟我連接成功啦"); // //================================================================ // // Scanner input=new Scanner(System.in); // // //3.先讓服務(wù)器跟客戶端說一句話————寫消息/輸出流 // //不是自己隨意創(chuàng)建的輸出流,得通過socket來獲取 // OutputStream os = socket.getOutputStream(); //流(讀 I/寫O) 字節(jié)/字符(Reader、Writer) // //上述os對象可以寫消息了,但是os是一個字節(jié)流,發(fā)送中文的時候不方便 // //字節(jié)流進行一個包裝---->字符流 // PrintWriter writer=new PrintWriter(os); // // // //4.服務(wù)器讀取回寫的消息 讀信息/輸入流對象 // InputStream is = socket.getInputStream(); // InputStreamReader isr=new InputStreamReader(is); // BufferedReader reader=new BufferedReader(isr); // // while(true){ // //4.可以發(fā)送數(shù)據(jù)啦 // System.out.println("說點什么吧:"); // String mess = input.nextLine(); // writer.println(mess); // writer.flush();//刷新 清空流管道 // // String value = reader.readLine(); // System.out.println("消息:"+value); // } // } catch (IOException e) { // e.printStackTrace(); // } // // // // } } package server;import java.net.Socket;//服務(wù)器端自己創(chuàng)建的一個類型 //目的:為了將一個用戶名和這個用戶對應(yīng)的socket對象包括包裝在一起 //每一個User對象,其實就是一個存儲數(shù)據(jù)的容器 public class User {private String uid;private Socket socket;public User(String uid, Socket socket) {this.uid = uid;this.socket = socket;}public String getUid() {return uid;}public Socket getSocket() {return socket;} }總結(jié)
以上是生活随笔為你收集整理的创建专属聊天室练习(客户端与服务端通信|客户端界面交互)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: tableau 倒序都倒了_Tablea
- 下一篇: Linux内核启动去掉企鹅,修改linu