一、控制台版本的群聊天室
其原理就是:通过死循环进行保存连接服务器的客户端,并创建一个容器来保存所有连接服务器的客户端
服务器:
package com.lixiangning.chat; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.Vector; /** * qq群的服务器 * * @author Administrator */ public class ServerChat { public static void main(String[] args) throws Exception { System.out.println("---------------------服务器--------------------"); // 创建一个服务器 ServerSocket ss = new ServerSocket(8989); // 无线去接收客户端的连接(需要使用一个容器去保存所有连接服务器的客户端) System.out.println("服务器已启动,端口号为8989"); // 通过死循环进行保存连接服务器的客户端 // 创建一个容器来保存所有连接服务器的客户端 Vector<Socket> vc = new Vector<Socket>(); Socket sk = null; while (true) { // 接收客户端 sk = ss.accept();// 来一个 System.out.println("某客户端上线了..."); // 加一个到Vc中 vc.add(sk); System.out.println("当前QQ群已加入新成员,目前有: " + vc.size()); // 实例化无线读取全部客户端发送过来的消息 new Thread(new ServerReadTread(vc, sk)).start(); } // 关闭资源 } } // 自定义线程类 无线接收已成功连接服务器的客户端发送的消息 class ServerReadTread implements Runnable { private Socket sk = null; private Vector<Socket> vc = null; public ServerReadTread(Vector<Socket> vc, Socket sk) { this.vc = vc; this.sk = sk; } @Override public void run() { // 无线接收客户端的消息 while (true) { // 输入流 try { if (sk != null) { InputStream is = sk.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String content = br.readLine(); System.out.println("客户端说: " + content); // 问题:如何将content内容转发给QQ群中其他人。 System.out.println("vc: " + vc.size()); for (Socket socket : vc) { if (socket != null) {// 判断非空 // 排除自己 if (socket != sk) { // 输出流 OutputStream os = socket.getOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(os); BufferedWriter bw = new BufferedWriter(osw); bw.write(content); bw.newLine(); bw.flush(); } } } } } catch (Exception e) { e.printStackTrace(); } } } }
客户端:
package com.lixiangning.chat; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.net.Socket; import java.util.Scanner; /** * 客户端的角色 * * @author Administrator * */ public class ClientChat { public static void main(String[] args) throws Exception { System.out.println("---------------------客户端--------------------"); // 连接服务器 Socket sk = new Socket("127.0.0.1", 8989); System.out.println("连接成功"); // 获取输出流 编写信息到网络中 让服务器去读取 new Thread(new ClientChatThreadWriter(sk)).start(); new Thread(new ClientChatThreadReader(sk)).start(); } } //无线写入 class ClientChatThreadWriter implements Runnable{ Socket sk = null; public ClientChatThreadWriter(Socket sk) { this.sk = sk; } @Override public void run() { Scanner sc = new Scanner(System.in); while(true) { try { OutputStream os = sk.getOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(os); BufferedWriter bw = new BufferedWriter(osw); System.out.println("请输入发送的内容"); String content = sc.next(); bw.write(content); bw.newLine(); bw.flush(); if("bye".equalsIgnoreCase(content)) { break; } } catch (Exception e) { // TODO: handle exception } } } } //无限读取 class ClientChatThreadReader implements Runnable{ Socket sk = null; public ClientChatThreadReader(Socket sk) { this.sk = sk; } @Override public void run() { while(true) { try { InputStream is = sk.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String content = br.readLine(); System.out.println("某客户端说: "+content); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
二、窗体版本的群聊天室
其原理就是:有一个客户发送消息,然后通过服务器保存消息发送给其他客户,然后其他的客户接收消息
服务器:
package com.lixiangning.files_09; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.Date; import java.util.Scanner; import java.util.Vector; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.UIManager; public class MyServer extends JFrame { private ServerSocket ss = null; private Socket sk = null; private OutputStream os = null; private InputStream is = null; private InputStreamReader isr = null; private BufferedReader br = null; private OutputStreamWriter osw = null; private BufferedWriter bw = null; private Vector<Socket> vc = null; private JPanel jpa = new JPanel(null); private JLabel jla = new JLabel("服务器"); private JLabel jlb = new JLabel("端 口"); private JTextField jtfa = new JTextField(15); private JButton jba = new JButton("发送连接"); private JTextArea jta = new JTextArea(20, 30); private JScrollPane jsp = new JScrollPane(jta); private JTextField jtfb = new JTextField(15); private JButton jbb = new JButton("发送"); // private JButton jbc = new JButton("接收"); public MyServer() { this.setTitle(""); this.setSize(620, 620); this.setDefaultCloseOperation(3); this.setLocationRelativeTo(null); jla.setBounds(240, 0, 200, 100); jla.setFont(new Font("华文楷体", Font.BOLD, 45)); jpa.add(jla); jlb.setBounds(50, 100, 60, 30); jlb.setFont(new Font("华文楷体", Font.BOLD, 20)); jpa.add(jlb); jtfa.setBounds(130, 100, 250, 30); jtfa.setFont(new Font("华文楷体", Font.BOLD, 20)); jpa.add(jtfa); jtfa.setText("9090"); jba.setBounds(400, 100, 150, 30); jba.setFont(new Font("华文楷体", Font.BOLD, 20)); jpa.add(jba); jta.setFont(new Font("华文隶书", Font.BOLD, 30)); jsp.setBounds(10, 150, 590, 310); jsp.setFont(new Font("华文楷体", Font.BOLD, 30)); jpa.add(jsp); jta.setEditable(false); jtfb.setBounds(70, 500, 250, 30); jtfb.setFont(new Font("华文楷体", Font.BOLD, 20)); jpa.add(jtfb); jbb.setBounds(360, 500, 130, 30); jbb.setFont(new Font("华文楷体", Font.BOLD, 20)); jpa.add(jbb); this.getContentPane().add(jpa); jba.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { int port = Integer.parseInt(jtfb.getText()); jta.append("服务器正在打开!\n"); // 端口号不能为1024之内(系统端口号或是保留端口号)也不能为1433(sqlserver端口号) if (port > 1024 && port != 1433) { try { // 创建服务器,将文本框中的内容获取并启动 ss = new ServerSocket(port); // 创建一个集合拿到数据 Vector<Socket> vc = new Vector<Socket>(); System.out.println(vc); new Thread() { public void run() { while (true) { try { sk = ss.accept(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } vc.add(sk); jta.append("与客户端连接成功!\n"); jtfb.setEnabled(false); jta.append("有客户端连接:" + sk.getInetAddress().getHostName() + "\n"); new ServerThread0(vc,sk).start(); } }; }.start(); } catch (Exception e2) { // e2.printStackTrace(); } } else { JOptionPane.showMessageDialog(null, "端口号不能为1024之内(系统端口号或是保留端口号)也不能为1433(sqlserver端口号)"); } } }); jbb.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { try { OutputStream os = sk.getOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(os); BufferedWriter bw = new BufferedWriter(osw); bw.write(jtfb.getText()); bw.newLine(); bw.flush(); } catch (Exception e1) { e1.printStackTrace(); } jta.append("自己\t"+jtfb.getText()+"\n"); jtfb.setText(""); } }); class ServerReadTread implements Runnable { private Socket sk = null; private Vector<Socket> vc = null; public ServerReadTread(Vector<Socket> vc, Socket sk) { this.vc = vc; this.sk = sk; } @Override public void run() { // 无线接收客户端的消息 while (true) { // 输入流 try { String text = jtfb.getText(); if (sk != null) { is = sk.getInputStream(); isr = new InputStreamReader(is); br = new BufferedReader(isr); for (Socket socket : vc) { if (socket != null) {// 判断非空 // 排除自己 if (socket != sk) { // 输出流 OutputStream os = socket.getOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(os); BufferedWriter bw = new BufferedWriter(osw); bw.write(text); bw.newLine(); bw.flush(); jta.append("我:" + text + "\n"); } } } } } catch (Exception e) { e.printStackTrace(); } } } } this.setVisible(true); } public static void main(String[] args) { try { UIManager.setLookAndFeel("com.jtattoo.plaf.mcwin.McWinLookAndFeel"); } catch (Exception e) { e.printStackTrace(); } new MyServer(); } } class ServerThread0 extends Thread{ //套接字 private Socket sk =null; //数组 private Vector<Socket> vc =null; /** * 构造方法进行赋值 * @param sk 套接字 * @param vc 数组 */ public ServerThread0(Vector<Socket> vc,Socket sk) { this.vc = vc; this.sk = sk; } @Override public void run() { while(true) { try { //获取消息 InputStream is = sk.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); //读取一整行 String r=br.readLine()+"/"+sk.getInetAddress().getHostName();//把当前电脑名字用/分割开发送到网络 for (Socket s : vc) { //不能没有对象发送 if(s!=null) { //不能发送给自己 if(s!=sk) { //发送消息 OutputStream os = s.getOutputStream(); OutputStreamWriter osw =new OutputStreamWriter(os); BufferedWriter bw =new BufferedWriter(osw); //把服务器获取到的客户端内容写入返回给另一个客户端 bw.write(r); bw.newLine(); bw.flush(); } } } // //通过条件进行自我关闭 // if("bye".equals(r)) { // //清除数据 // vc.remove(0); // //进行结束 // break; // } } catch (Exception e) { //系统处理异常 // e.printStackTrace(); } } } }
客户端:
package com.lixiangning.files_09; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.net.Socket; import java.util.Date; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.UIManager; public class MyClinet extends JFrame { private Socket sk = null; private OutputStream os = null; private InputStream is = null; private InputStreamReader isr = null; private BufferedReader br = null; private OutputStreamWriter osw = null; private BufferedWriter bw = null; private JPanel jpa = new JPanel(null); private JLabel jla = new JLabel("客户端"); private JLabel jlb = new JLabel("IP:"); private JTextField jtfa = new JTextField(15); private JLabel jlc = new JLabel("端口:"); private JTextField jtfb = new JTextField(15); private JButton jba = new JButton("连接"); private JTextArea jta = new JTextArea(20, 30); private JScrollPane jsp = new JScrollPane(jta); private JTextField jtfc = new JTextField(15); private JButton jbb = new JButton("发送"); public MyClinet() { this.setTitle(""); this.setSize(620, 620); this.setDefaultCloseOperation(3); this.setLocationRelativeTo(null); jla.setBounds(240, 0, 200, 100); jla.setFont(new Font("华文楷体", Font.BOLD, 45)); jpa.add(jla); jlb.setBounds(50, 100, 100, 30); jlb.setFont(new Font("华文楷体", Font.BOLD, 15)); jpa.add(jlb); jtfa.setBounds(90, 100, 150, 30); jtfa.setFont(new Font("华文楷体", Font.BOLD, 20)); jpa.add(jtfa); jtfa.setText("127.0.0.1"); jlc.setBounds(260, 100, 60, 30); jlc.setFont(new Font("华文楷体", Font.BOLD, 15)); jpa.add(jlc); jtfb.setBounds(310, 100, 150, 30); jtfb.setFont(new Font("华文楷体", Font.BOLD, 20)); jpa.add(jtfb); jba.setBounds(480, 100, 100, 30); jba.setFont(new Font("华文楷体", Font.BOLD, 20)); jpa.add(jba); jta.setFont(new Font("华文隶书", Font.BOLD, 30)); jsp.setBounds(10, 150, 590, 310); jsp.setFont(new Font("华文楷体", Font.BOLD, 30)); jpa.add(jsp); jta.setEditable(false); jtfc.setBounds(70, 500, 250, 30); jtfc.setFont(new Font("华文楷体", Font.BOLD, 20)); jpa.add(jtfc); jbb.setBounds(360, 500, 130, 30); jbb.setFont(new Font("华文楷体", Font.BOLD, 20)); jpa.add(jbb); this.getContentPane().add(jpa); jba.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { jta.append("客户端正在连接...\n"); String address = jtfa.getText(); int port = Integer.parseInt(jtfb.getText()); if(port>1024 && port!=1433) { try { sk = new Socket(address, port); jtfa.setEnabled(false); jtfb.setEnabled(false); } catch (Exception e2) { } if(sk!=null) { jba.setEnabled(false); }else { JOptionPane.showMessageDialog(null, "未连接上!"); } }else { JOptionPane.showMessageDialog(null, "端口号不能为1024之内(系统端口号或是保留端口号)也不能为1433(sqlserver端口号)"); } //创建一个人来无限等待消息 new Thread() { public void run() { while(true) { try { InputStream is = sk.getInputStream(); InputStreamReader isr =new InputStreamReader(is); BufferedReader br =new BufferedReader(isr); String s =br.readLine();//读取网络发送的消息 String name =s.substring(s.lastIndexOf("/")+1); String read =s.substring(0,s.lastIndexOf("/")); jta.append("\t\t"+name+"\t\t"+read+"\n"); } catch (Exception e) { e.printStackTrace(); } } }; }.start(); } }); jbb.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String text = jtfc.getText(); if (sk != null) { try { // 获取界面连接 os = sk.getOutputStream(); // 字节流 osw = new OutputStreamWriter(os); // 字符流FileWriter 缓冲流 bw = new BufferedWriter(osw); bw.write(text); bw.newLine(); bw.flush(); jta.append("我:" + text + "\n"); } catch (Exception e2) { //e2.printStackTrace(); } } else { JOptionPane.showMessageDialog(null, "请先获取连接...."); } } }); this.setVisible(true); } public static void main(String[] args) { try { UIManager.setLookAndFeel("com.jtattoo.plaf.mcwin.McWinLookAndFeel"); } catch (Exception e) { //e.printStackTrace(); } new MyClinet(); } }
个人总结:
群聊天室简单来说就是:一个服务器多个客户端,服务器负责接收客户端发送的消息,再将接收到的消息发送给除发送消息以外的所有客户端。