实现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 @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 //如果断开连接则移除对于的socket140 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 @Override127 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 @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("无法发送,服务器连接异常!");177 }178 }179 180 }
(完)