你的位置:首页 > Java教程

[Java教程]Java简单聊天室


实现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 }

客户端代码

(完)