疯狂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(" ip/" + address + ":" + port + " 上线了");
 96                 
 97                 //开启转发信息的线程
 98                 new sendThread(socket).start();
 99 
100             } catch (IOException e) {
101 //                System.out.println(address + ":" + port + " 退出了服务器");
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()+" ";
173                 message.append(str);
174             }
175         } catch (IOException e) {
176             System.out.println("无法发送,服务器连接异常!");
177         }
178     }
179 
180 }