Demo如下:
客戶端:
/* * 一個簡單的QQ * 2013-8-1 * @李志傑 */ package SimpleQQ_Client; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.io.*; import java.net.*; public class DemoQQ_Client { public static void main(String[] args) { // TODO Auto-generated method stub DemoQQ_Client demoQQ_Server_1 = new DemoQQ_Client(); } public DemoQQ_Client() { //初始化界面,開始界面的多線程 MyPanel mp; mp = new MyPanel(); Thread t1 = new Thread(mp); t1.start(); //服務器getter MyClient mServer = new MyClient(); Thread t2 = new Thread(mServer); t2.start(); //服務器sender MyClient_writer myClient_writer = new MyClient_writer(); Thread t3 = new Thread(myClient_writer); t3.start(); /* //測試線程 Mytest mytest = new Mytest(); Thread t4 = new Thread(mytest); t4.start(); */ } } //共享參數類 class MyBaseClient { //定義參數,務必讓發送信息和接收信息的參數共享 static Socket s; static String send_ino,get_ino; } class MyClient_writer extends MyBaseClient implements Runnable //QQ寫者 { @Override public void run() { // TODO Auto-generated method stub while(true) { try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } sendIno(); send_ino = null; } } //發送信息 public void sendIno() { if(send_ino == null) { return; } try { //發送數據 PrintWriter pw = new PrintWriter(s.getOutputStream(),true); //獲取發送內容 pw.println(send_ino); send_ino = "Client :" + send_ino +"\n"; } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } class MyClient extends MyBaseClient implements Runnable { public MyClient() { //下面是主要功能 try { s = new Socket("127.0.0.1", 9999); get_ino = "歡迎使用星塵QQ簡易版\n"; } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //傳遞返回信息 public void getIno() { try { //讀數據 InputStreamReader inr = new InputStreamReader(s.getInputStream()); BufferedReader bf = new BufferedReader(inr); get_ino = "Server :"+bf.readLine()+"\n"; } catch (IOException e) { System.out.println("Error!"); return; } } @Override public void run() { // TODO Auto-generated method stub while(true) { try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } getIno(); } } } //專門的界面類,以后要用這種方式去畫界面,不要用視頻教程里面的 class MyPanel extends JFrame implements Runnable,ActionListener { //定義參數 String sender,getter; MyBaseClient mbc = new MyBaseClient(); //定義組件 JPanel jp; JTextArea jta; JButton jb; JTextField jt; JScrollPane jsp; public MyPanel() { //創建組件 jp = new JPanel(); jta = new JTextArea(); jb = new JButton("發送"); jt = new JTextField(); jsp = new JScrollPane(jta); jt.setCaretColor(Color.GREEN);//jt綠色光標 jt.setForeground(Color.GREEN);//jt綠色字體 //信息區只能讀 jta.setEditable(false); //添加監聽 jb.setActionCommand("send"); //注冊監聽 jb.addActionListener(this); //添加組件 jp.setLayout(new GridLayout(2,1)); jp.add(jt); jp.add(jb); this.setLayout(new GridLayout(2,1)); this.add(jsp); this.add(jp); this.setSize(400,400); this.setVisible(true); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setTitle("QQ_Client"); } public void paint(Graphics g) { super.paint(g); } @Override public void run() { while(true) { try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } sender = this.jt.getText(); getter = mbc.get_ino; //更新面板 if(getter == null) //信息為空,跳過更新 { continue; } if(mbc.get_ino != null) { String s_board = this.jta.getText() + getter; //通過面板來獲取記錄 this.jta.setText(s_board); mbc.get_ino = null; } this.repaint(); } } @Override public void actionPerformed(ActionEvent e) { if(e.getActionCommand() == "send") { //發送信息 mbc.send_ino = sender; //更新消息緩存 String s_board = this.jta.getText() + "Client :" +sender +"\n"; //通過面板來獲取記錄 this.jta.setText(s_board); jt.setText(null);//清空內容 this.repaint(); } } } //測試類 class Mytest extends MyBaseClient implements Runnable { int times = 0; public void run() { // TODO Auto-generated method stub try { while(true) { times++; Thread.sleep(100); System.out.println("Time :"+ times + " " + get_ino); } } catch (Exception e) { // TODO: handle exception } } }
服務器端的:
/* * 一個簡單的QQ * 2013-8-1 * @李志傑 */ package SimpleQQ_Server; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.io.*; import java.net.*; public class DemoQQ_Server { public static void main(String[] args) { // TODO Auto-generated method stub DemoQQ_Server demoQQ_Server = new DemoQQ_Server(); } public DemoQQ_Server() { //初始化界面,開始界面的多線程 MyPanel mp; mp = new MyPanel(); Thread t1 = new Thread(mp); t1.start(); //服務器getter MyServer mServer = new MyServer(); Thread t2 = new Thread(mServer); t2.start(); //服務器sender MyServer_writer myClient_writer = new MyServer_writer(); Thread t3 = new Thread(myClient_writer); t3.start(); } } //共享參數類 class MyBaseServer { //定義參數,務必讓發送信息和接收信息的參數共享 static Socket s; static ServerSocket ss; static String send_ino,get_ino; } class MyServer_writer extends MyBaseServer implements Runnable //QQ寫者 { @Override public void run() { // TODO Auto-generated method stub while(true) { try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } sendIno(); send_ino = null; //get_ino = null; } } //發送信息 public void sendIno() { if(send_ino == null) { return; } try { //發送數據 PrintWriter pw = new PrintWriter(s.getOutputStream(),true); //獲取發送內容 pw.println(send_ino); send_ino = "Client :" + send_ino +"\n"; } catch (IOException e) { e.printStackTrace(); } } } class MyServer extends MyBaseServer implements Runnable { public MyServer() { //下面是主要功能 try { get_ino = "歡迎使用星塵QQ簡易版\n"; ss = new ServerSocket(9999); s = ss.accept(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //傳遞返回信息 public void getIno() { //要擁有一個判斷去跳出這個讀取,否則一旦進入try塊,如果沒有信息進來,讀者會一直占用cpu而不會進入到寫者,除非寫者也開多線程 /* if(get_ino == null) { return; } */ try { //讀數據 InputStreamReader inr = new InputStreamReader(s.getInputStream()); BufferedReader bf = new BufferedReader(inr); get_ino = "Server :"+bf.readLine()+"\n"; } catch (IOException e) { return; } } @Override public void run() { // TODO Auto-generated method stub while(true) { try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } getIno(); //sendIno(); // send_ino = null; //get_ino = null; } } } //專門的界面類,以后要用這種方式去畫界面,不要用視頻教程里面的 class MyPanel extends JFrame implements Runnable,ActionListener { //定義參數 String sender,getter; MyBaseServer mbs = new MyBaseServer(); //定義組件 JPanel jp; JTextArea jta; JButton jb; JTextField jt; JScrollPane jsp; public MyPanel() { //創建組件 jp = new JPanel(); jta = new JTextArea(); jb = new JButton("發送"); jt = new JTextField(); jsp = new JScrollPane(jta); jt.setCaretColor(Color.RED);//jt紅色光標 jt.setForeground(Color.RED);//jt紅色字體 //信息區只能讀 jta.setEditable(false); //添加監聽 jb.setActionCommand("send"); //注冊監聽 jb.addActionListener(this); //添加組件 jp.setLayout(new GridLayout(2,1)); jp.add(jt); jp.add(jb); this.setLayout(new GridLayout(2,1)); this.add(jsp); this.add(jp); this.setSize(400,400); this.setVisible(true); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setTitle("QQ_Server"); } public void paint(Graphics g) { super.paint(g); } @Override public void run() { while(true) { try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } sender = this.jt.getText(); getter = mbs.get_ino; //更新面板 //更新面板 if(getter == null) //信息為空,跳過更新 { continue; } if(mbs.get_ino != null) { String s_board = this.jta.getText() + getter; //通過面板來獲取記錄 this.jta.setText(s_board); mbs.get_ino = null; } this.repaint(); } } @Override public void actionPerformed(ActionEvent e) { if(e.getActionCommand() == "send") { //發送信息 mbs.send_ino = sender; //更新消息緩存 String s_board = this.jta.getText() + "Client :" +sender +"\n"; //通過面板來獲取記錄 this.jta.setText(s_board); jt.setText(null);//清空內容 this.repaint(); } } }
總結:
先說一下這個簡單項目的設計思路:
1、本着模塊獨立化的思路,將整個項目的兩個模塊(界面+通訊)獨立成三個類,其中界面類為MyPanel,通訊(這里以客戶端為例)為MyClient(接收消息)和MyClient_writer(發送消息)。
2、除了以上三個類外,還設計了一個共享信息的類,即MyBaseClient,通訊的兩個類直接繼承這個類,界面類通過將這個類實例化來共享這個類的變量信息;
3、此項目還有一個特色的地方是,使用了三個線程,一個是界面線程用以定時繪圖和監聽按鈕,另一個是接收消息的類,用以定時接收消息。除了這兩個外,還有一個用以發送消息。之所以要這樣設計,首先是要讓界面與后台邏輯分離,即界面與通訊分離,其次是要讓后台中的讀寫分離,實現同步和實時。
這是我今天的技術收獲,記此。