實現Java簡單的聊天室
所用主要知識:多線程+網絡編程
效果如下圖
/**
*
* @author Administrator
*
* 簡單的多人聊天系統——重點:同時性,異步性
* 1、客戶端:發送消息,並且接收消息
* 1.1 消息發送至服務器:服務器每次都將客戶發過來的信息發送到每個客戶端
* 1.2 接收消息:發送的同時也要接收消息,所以必須有兩個線程,一個發送消息,一個接收消息
* 1.3 關於這兩個線程:如果沒有線程,接收和發送就是按順序執行的了,那么是發送后接收還是接收后發送?或者是
* 一邊可以發送一邊又可以接收呢?答案不言而喻
* 1.4 在客戶端,用可以通過界面的點擊按鈕事件發送消息,所以就要開啟一個線程來接收服務器發送給客戶端的消息了
* 1.5 多線程:不需按步驟逐步循環,可以和其他程序同時執行,不相互干擾
* 2、服務器端:接收所有用戶的消息並且借每個用戶的消息發送給連接了服務器的客戶端
* 2.1 服務器既要接收C端信息又要想所有C端發送信息,先收后發。一收就發。
* 2.2 S端還要顯示C端上線提醒
* 2.3 上線提醒和收發信息是不影響的,互不干擾的,但是都要在S端上執行,如果沒有線程,所以的程序都要按順序執行了所以又要 多線程
* 2.4 一個線程負責向所以C端發送接收到的任何一條信息,一個線程負責顯示C端上線的信息
*
* 3、類:S端:Server、ServerFrame、sendThread 三個類
* C端:Client、ClientFrame 兩個類
*
*/

1 package com.java.chat; 2 3 import java.awt.event.ActionEvent; 4 import java.awt.event.ActionListener; 5 import java.io.BufferedReader; 6 import java.io.BufferedWriter; 7 import java.io.IOException; 8 import java.io.InputStream; 9 import java.io.InputStreamReader; 10 import java.io.OutputStream; 11 import java.io.OutputStreamWriter; 12 import java.net.ServerSocket; 13 import java.net.Socket; 14 import java.util.ArrayList; 15 import java.util.List; 16 17 import javax.swing.JButton; 18 import javax.swing.JFrame; 19 import javax.swing.JTextArea; 20 21 22 public class Server { 23 //用於保存Socket的集合,也可以說是把個C端與S端的一個連接通道保存起來 24 //作用:服務器將接收到的信息發給集合里的所以socket,也就是發給每個C端 25 public static List<Socket> list = new ArrayList<Socket>(); 26 27 public static void main(String[] args) { 28 new ServerFrame("服務器端"); 29 } 30 31 } 32 33 /* 34 * 繼承Jframe類表示該類可以創建一個窗口程序了, 35 * 實現ActionListener:動作監聽,在S端點擊“啟動服務器”是要執行的代碼 36 * 實現Runnable:實現多線程,該窗口是個客戶端窗口,要開啟一個線程接收顯示服務器發過來的信息 37 */ 38 class ServerFrame extends JFrame implements ActionListener, Runnable { 39 40 private static final long serialVersionUID = 1L; 41 42 private ServerSocket serverSocket = null; 43 44 private Socket socket; 45 46 private JButton start; 47 private JTextArea message; 48 49 public ServerFrame(String name) { 50 super(name); 51 52 start = new JButton("啟動服務器"); 53 start.addActionListener(this); 54 message = new JTextArea(); 55 message.setEditable(false); 56 this.add(start, "North"); 57 this.add(message, "Center"); 58 59 this.setResizable(false); 60 this.setBounds(0, 0, 400, 300); 61 this.setVisible(true); 62 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 63 } 64 65 @Override 66 public void actionPerformed(ActionEvent e) { 67 if (e.getSource() == start) { 68 // 如果點擊的按鈕是開始按鈕。則啟動服務器。 69 try { 70 serverSocket = new ServerSocket(6666); 71 message.setText("服務器已啟動。。。"); 72 73 //單擊“啟動服務器”開啟一個線程用於獲取用戶上線的情況 74 new Thread(this).start(); 75 76 } catch (IOException e1) { 77 e1.printStackTrace(); 78 } 79 } 80 } 81 82 //獲取C端上線的情況 83 @Override 84 public void run() { 85 String address = null; 86 int port = 0; 87 //用一個死循環一直讓S端開啟接收C端的連接,將C端的IP和端口顯示在面板上 88 //如果用循環的話就只能接收一次 89 while (true) { 90 try { 91 socket = serverSocket.accept(); 92 Server.list.add(socket); 93 address = socket.getInetAddress().getHostAddress(); 94 port = socket.getPort(); 95 message.append("\r\nip/" + address + ":" + port + "\t上線了"); 96 97 //開啟轉發信息的線程 98 new sendThread(socket).start(); 99 100 } catch (IOException e) { 101 // System.out.println(address + ":" + port + "\t退出了服務器"); 102 } 103 } 104 } 105 106 } 107 108 /* 109 * 接收每個C端的信息並向每個C端發送接收到的信息 110 */ 111 class sendThread extends Thread { 112 private Socket socket; 113 114 public sendThread(Socket socket) { 115 super(); 116 this.socket = socket; 117 } 118 119 @Override 120 public void run() { 121 InputStream is = null; 122 BufferedReader br = null; 123 String str = null; 124 OutputStream os = null; 125 BufferedWriter bw = null; 126 while (true) { 127 try { 128 is = socket.getInputStream(); 129 br = new BufferedReader(new InputStreamReader(is)); 130 str = br.readLine(); 131 for (Socket s : Server.list) { 132 os = s.getOutputStream(); 133 bw = new BufferedWriter(new OutputStreamWriter(os)); 134 bw.write(str); 135 bw.newLine(); 136 bw.flush(); 137 } 138 } catch (IOException e) { 139 //如果斷開連接則移除對於的socket 140 Server.list.remove(socket); 141 } 142 } 143 } 144 }

1 package com.java.chat; 2 3 import java.awt.BorderLayout; 4 import java.awt.Color; 5 import java.awt.Font; 6 import java.awt.event.ActionEvent; 7 import java.awt.event.ActionListener; 8 import java.io.BufferedReader; 9 import java.io.BufferedWriter; 10 import java.io.IOException; 11 import java.io.InputStream; 12 import java.io.InputStreamReader; 13 import java.io.OutputStream; 14 import java.io.OutputStreamWriter; 15 import java.net.Socket; 16 import java.text.SimpleDateFormat; 17 import java.util.Date; 18 19 import javax.swing.JButton; 20 import javax.swing.JFrame; 21 import javax.swing.JLabel; 22 import javax.swing.JOptionPane; 23 import javax.swing.JPanel; 24 import javax.swing.JScrollPane; 25 import javax.swing.JTextArea; 26 import javax.swing.JTextField; 27 28 /** 29 * 30 * @author Administrator 31 * 32 */ 33 34 public class Client { 35 public static void main(String[] args) { 36 new ClientFrame("客戶端"); 37 } 38 } 39 40 /* 41 * 繼承Jframe類表示該類可以創建一個窗口程序了, 42 * 實現ActionListener:動作監聽,就是一個事件發送后應該執行什么就要重新它的方法 43 * 實現Runnable:實現多線程,該窗口是個客戶端窗口,要開啟一個線程接收顯示服務器發過來的信息 44 */ 45 class ClientFrame extends JFrame implements ActionListener, Runnable { 46 47 private static final long serialVersionUID = 1L; 48 49 private Socket socket; 50 OutputStream os = null; 51 BufferedWriter bw = null; 52 53 JTextArea message; 54 JScrollPane scroll; 55 JTextField input; 56 JButton submit; 57 JPanel panel; 58 59 String uname = ""; 60 // 聊天頁面 61 JPanel chat; 62 63 // 登錄頁面 64 JPanel login; 65 JTextField username; 66 JButton ok; 67 68 public ClientFrame(String name) { 69 super(name); 70 71 message = new JTextArea(); 72 message.setEditable(false); 73 scroll = new JScrollPane(message); 74 input = new JTextField(20); 75 submit = new JButton("發送"); 76 panel = new JPanel(); 77 78 panel.add(input); 79 panel.add(submit); 80 chat = new JPanel(new BorderLayout()); 81 chat.add(scroll, "Center"); 82 chat.add(panel, "South"); 83 84 85 // 在創建客戶端窗體是就要把客戶端與服務端連接 86 try { 87 //127.0.0.1表示本機地址,地址好端口都可以改,這要看服務器那邊是什么地址和端口 88 socket = new Socket("127.0.0.1", 6666); 89 os = socket.getOutputStream(); 90 bw = new BufferedWriter(new OutputStreamWriter(os)); 91 92 } catch (IOException e1) { 93 e1.printStackTrace(); 94 } 95 96 /* 在創建C端窗體的時候也應該開啟一個線程接收顯示來自服務器的信息 97 * 為什么要開啟一個線程呢?因為在這個窗體里既要實現消息的發送, 98 * 也要接收信息,而且兩個不能按順序進行,也互不干擾,所以開啟一個線程時時刻刻等着S端發來的信息 99 */ 100 //接收信息 101 new Thread(this).start(); 102 103 //提交按鈕工作監聽器,點擊提交時觸發 104 submit.addActionListener(this); 105 106 login = new JPanel(); 107 username = new JTextField(20); 108 ok = new JButton("確定"); 109 ok.addActionListener(this); 110 JLabel label = new JLabel("請輸入用戶名"); 111 label.setFont(new Font(Font.DIALOG, Font.BOLD, 28)); 112 label.setForeground(Color.red); 113 login.add(label); 114 login.add(username); 115 login.add(ok); 116 117 this.add(chat); 118 this.add(login); 119 120 this.setResizable(false); 121 this.setBounds(400, 100, 400, 300); 122 this.setVisible(true); 123 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 124 } 125 126 @Override 127 public void actionPerformed(ActionEvent e) { 128 if (e.getSource() == submit) { 129 // 如果點擊提交按鈕,則表示需要將信息發送出去。 130 String str = null; 131 //以下三行是創建發送時間的代碼 132 Date date = new Date(); 133 SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss"); 134 String dateStr = format.format(date); 135 136 str = uname+" "+dateStr+" "+input.getText(); 137 138 try { 139 bw.write(str); 140 bw.newLine(); 141 // System.out.println("客戶端發送了:"+str); 142 bw.flush(); 143 } catch (IOException e1) { 144 System.out.println("無法發送,服務器連接異常!"); 145 } 146 147 // System.out.println(str); 148 149 } else if (e.getSource() == ok) {// 用戶登錄 150 uname = username.getText();// 獲取文本框輸入的文本信息 151 if ("".equals(uname)) { 152 JOptionPane.showMessageDialog(this, "用戶名不能為空!"); 153 } else { 154 login.setVisible(false); 155 chat.setVisible(true); 156 this.setTitle(uname + " 的客戶端"); 157 this.add(chat); 158 } 159 } 160 } 161 162 //run方法里面的是接受S端信息和將信息顯示的代碼 163 @Override 164 public void run() { 165 BufferedReader br = null; 166 InputStream is = null; 167 String str = null; 168 try { 169 is = socket.getInputStream(); 170 br = new BufferedReader(new InputStreamReader(is)); 171 while (true) { 172 str = br.readLine()+"\r\n"; 173 message.append(str); 174 } 175 } catch (IOException e) { 176 System.out.println("無法發送,服務器連接異常!"); 177 } 178 } 179 180 }
(完)