實(shí)現(xiàn)Java簡(jiǎn)單的聊天室
所用主要知識(shí):多線程+網(wǎng)絡(luò)編程
效果如下圖

/** * * @author Administrator * *簡(jiǎn)單的多人聊天系統(tǒng)——重點(diǎn):同時(shí)性,異步性 *1、客戶端:發(fā)送消息,并且接收消息 *1.1 消息發(fā)送至服務(wù)器:服務(wù)器每次都將客戶發(fā)過(guò)來(lái)的信息發(fā)送到每個(gè)客戶端 *1.2 接收消息:發(fā)送的同時(shí)也要接收消息,所以必須有兩個(gè)線程,一個(gè)發(fā)送消息,一個(gè)接收消息 *1.3 關(guān)于這兩個(gè)線程:如果沒(méi)有線程,接收和發(fā)送就是按順序執(zhí)行的了,那么是發(fā)送后接收還是接收后發(fā)送?或者是 *一邊可以發(fā)送一邊又可以接收呢?答案不言而喻 *1.4 在客戶端,用可以通過(guò)界面的點(diǎn)擊按鈕事件發(fā)送消息,所以就要開啟一個(gè)線程來(lái)接收服務(wù)器發(fā)送給客戶端的消息了 *1.5 多線程:不需按步驟逐步循環(huán),可以和其他程序同時(shí)執(zhí)行,不相互干擾 *2、服務(wù)器端:接收所有用戶的消息并且借每個(gè)用戶的消息發(fā)送給連接了服務(wù)器的客戶端 *2.1服務(wù)器既要接收C端信息又要想所有C端發(fā)送信息,先收后發(fā)。一收就發(fā)。 *2.2S端還要顯示C端上線提醒 *2.3上線提醒和收發(fā)信息是不影響的,互不干擾的,但是都要在S端上執(zhí)行,如果沒(méi)有線程,所以的程序都要按順序執(zhí)行了所以又要 多線程 *2.4一個(gè)線程負(fù)責(zé)向所以C端發(fā)送接收到的任何一條信息,一個(gè)線程負(fù)責(zé)顯示C端上線的信息 * *3、類:S端:Server、ServerFrame、sendThread 三個(gè)類 * C端:Client、ClientFrame 兩個(gè)類 * */

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的集合,也可以說(shuō)是把個(gè)C端與S端的一個(gè)連接通道保存起來(lái) 24 //作用:服務(wù)器將接收到的信息發(fā)給集合里的所以socket,也就是發(fā)給每個(gè)C端 25 public static List<Socket> list = new ArrayList<Socket>(); 26 27 public static void main(String[] args) { 28 new ServerFrame("服務(wù)器端"); 29 } 30 31 } 32 33 /* 34 * 繼承Jframe類表示該類可以創(chuàng)建一個(gè)窗口程序了, 35 * 實(shí)現(xiàn)ActionListener:動(dòng)作監(jiān)聽,在S端點(diǎn)擊“啟動(dòng)服務(wù)器”是要執(zhí)行的代碼 36 * 實(shí)現(xiàn)Runnable:實(shí)現(xiàn)多線程,該窗口是個(gè)客戶端窗口,要開啟一個(gè)線程接收顯示服務(wù)器發(fā)過(guò)來(lái)的信息 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("啟動(dòng)服務(wù)器"); 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 // 如果點(diǎn)擊的按鈕是開始按鈕。則啟動(dòng)服務(wù)器。 69 try { 70 serverSocket = new ServerSocket(6666); 71 message.setText("服務(wù)器已啟動(dòng)。。。"); 72 73 //單擊“啟動(dòng)服務(wù)器”開啟一個(gè)線程用于獲取用戶上線的情況 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 //用一個(gè)死循環(huán)一直讓S端開啟接收C端的連接,將C端的ip和端口顯示在面板上 88 //如果用循環(huán)的話就只能接收一次 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 //開啟轉(zhuǎn)發(fā)信息的線程 98 new sendThread(socket).start(); 99 100 } catch (IOException e) {101 // System.out.println(address + ":" + port + "/t退出了服務(wù)器");102 }103 }104 }105 106 }107 108 /*109 * 接收每個(gè)C端的信息并向每個(gè)C端發(fā)送接收到的信息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 @Override120 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 //如果斷開連接則移除對(duì)于的socket140 Server.list.remove(socket);141 }142 }143 }144 }服務(wù)器端代碼
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類表示該類可以創(chuàng)建一個(gè)窗口程序了, 42 * 實(shí)現(xiàn)ActionListener:動(dòng)作監(jiān)聽,就是一個(gè)事件發(fā)送后應(yīng)該執(zhí)行什么就要重新它的方法 43 * 實(shí)現(xiàn)Runnable:實(shí)現(xiàn)多線程,該窗口是個(gè)客戶端窗口,要開啟一個(gè)線程接收顯示服務(wù)器發(fā)過(guò)來(lái)的信息 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 // 聊天頁(yè)面 61 JPanel chat; 62 63 // 登錄頁(yè)面 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("發(fā)送"); 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 // 在創(chuàng)建客戶端窗體是就要把客戶端與服務(wù)端連接 86 try { 87 //127.0.0.1表示本機(jī)地址,地址好端口都可以改,這要看服務(wù)器那邊是什么地址和端口 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 /* 在創(chuàng)建C端窗體的時(shí)候也應(yīng)該開啟一個(gè)線程接收顯示來(lái)自服務(wù)器的信息 97 * 為什么要開啟一個(gè)線程呢?因?yàn)樵谶@個(gè)窗體里既要實(shí)現(xiàn)消息的發(fā)送, 98 * 也要接收信息,而且兩個(gè)不能按順序進(jìn)行,也互不干擾,所以開啟一個(gè)線程時(shí)時(shí)刻刻等著S端發(fā)來(lái)的信息 99 */100 //接收信息101 new Thread(this).start();102 103 //提交按鈕工作監(jiān)聽器,點(diǎn)擊提交時(shí)觸發(fā)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("請(qǐng)輸入用戶名");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 @Override127 public void actionPerformed(ActionEvent e) {128 if (e.getSource() == submit) {129 // 如果點(diǎn)擊提交按鈕,則表示需要將信息發(fā)送出去。130 String str = null;131 //以下三行是創(chuàng)建發(fā)送時(shí)間的代碼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("客戶端發(fā)送了:"+str);142 bw.flush();143 } catch (IOException e1) {144 System.out.println("無(wú)法發(fā)送,服務(wù)器連接異常!");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 @Override164 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("無(wú)法發(fā)送,服務(wù)器連接異常!");177 }178 }179 180 }客戶端代碼(完)
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注