java socket编程实现群聊(超详细)

Posted ꧁༺空༒白༻꧂

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java socket编程实现群聊(超详细)相关的知识,希望对你有一定的参考价值。

Java socket编程实现群聊

最终效果

文末有完整代码,创作不易,点个赞再走吧~

  • 客户端之间的交流
    在这里插入图片描述
  • 有人退出群聊时,减少在线人数
    在这里插入图片描述

实现流程

1、项目结构即原理分析

  • 功能
  1. 实现多客户之间聊天
  2. 实时统计在线人数
  3. 图形化界面
  4. 创建昵称
  5. 下线通知
  • 项目结构
    在这里插入图片描述
  • 原理
    在这里插入图片描述
    在这里插入图片描述
  1. 服务端:
    创建socket,每次接受一个客户端来连接,就分配一个线程处理和客户端通信,将昵称和服务器分配给其的输出流存储,然后实现群聊
  2. 客户端
    客户端继承JFrame框架,添加组件。创建客户端的socket,输入昵称,创建客户端socket对应的输入流和输出流

2、代码分析

2.1、客户端图形化界面设计

public class ClientFrame {
    public  void frame() {
        JFrame frame = new JFrame();
        frame.setTitle("客户端");
        ClientPanel panel = new ClientPanel();
        panel.panel();
        //panel添加到窗体里面
        frame.add(panel);
        //布局置空,设置绝对定位
        frame.setLayout(null);
        frame.setBounds(100, 100, 900, 500);
        //设置窗口不可变
        frame.setResizable(true);
        //设置窗口可见
        frame.setVisible(true);
        //设置背景颜色
//        frame.getContentPane().setBackground(Color.MAGENTA);
        //关闭窗体
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

2.2、客户端面板模块

public void panel() {
        setBounds(30, 30, 800, 410);
//        setBackground(LIGHT_GRAY);
        setFocusable(true);//设置获取焦点
        setLayout(null);//布局自定义
        /*lab_ip*/
        JLabel lab_ip = new JLabel("IP:");
        lab_ip.setBounds(10, 10, 50, 30);
        lab_ip.setFont(new Font("宋体", Font.BOLD, 20));//字体样式
        lab_ip.setForeground(black);//标签字体前景色
        /*ip框*/
        JTextField ip = new JTextField(20);
        ip.setBounds(60, 10, 180, 30);
        ip.setFont(new Font("微软雅黑", Font.BOLD, 20));
        /*端口*/
        JLabel lab_port = new JLabel("端口:");
        lab_port.setBounds(240, 10, 80, 30);
        lab_port.setFont(new Font("宋体", Font.BOLD, 20));//字体样式
        lab_port.setForeground(black);//标签字体前景色

        //用户名称
        JLabel username = new JLabel("用户名:");
        username.setBounds(450, 10, 80, 30);
        username.setFont(new Font("宋体", Font.BOLD, 20));//字体样式
        username.setForeground(black);//标签字体前景色
        //用户名框
        JTextField usernameport = new JTextField(10);
        usernameport.setBounds(550, 10, 100, 30);
        usernameport.setFont(new Font("微软雅黑", Font.BOLD, 20));
        /*端口框*/
        JTextField port = new JTextField(10);
        port.setBounds(320, 10, 100, 30);
        port.setFont(new Font("微软雅黑", Font.BOLD, 20));
        /*连接按钮*/
        JButton connection = new JButton("连接");
        connection.setBounds(700, 10, 100, 30);
        connection.setBackground(green);
        /*状态标签*/
        JLabel flag = new JLabel("");
        flag.setBounds(230, 30, 100, 35);
        /*接收框*/
        TextArea textRec = new TextArea("");
        textRec.setBounds(90, 70, 600, 180);
        textRec.setFont(new Font("微软雅黑", Font.PLAIN, 20));
        /*输入框*/
        TextArea textSend = new TextArea(1, 10);
        textSend.setColumns(20);
        textSend.setBounds(90, 260, 600, 70);
        textSend.setFont(new Font("微软雅黑", Font.BOLD, 20));
        textSend.setBackground(white);
        /*发送按钮*/
        JButton btn_en = new JButton("发送");
        btn_en.setBounds(440, 350, 100, 40);
        btn_en.setBackground(pink);
        /*在线人数lab*/
        JLabel jLabel = new JLabel("在线人数:");
        jLabel.setBounds(0, 70, 90, 35);
        jLabel.setFont(new Font("微软雅黑", Font.PLAIN, 20));
        /*人数*/
        JLabel online = new JLabel("");
        online.setBounds(30, 120, 100, 18);
        online.setFont(new Font("微软雅黑", Font.PLAIN, 18));
        online.setText("");

        add(lab_ip);
        add(ip);
        add(lab_port);
        add(username);
        add(usernameport);
        add(port);
        add(connection);
        add(flag);
        add(textRec);
        add(textSend);
        add(btn_en);
   }

2.3、服务端的转发信息方法

//接收消息
 public String receiveTest(Socket socket) throws IOException {
     inputStream = socket.getInputStream();
     byte[] bytes = new byte[1024];
     int len = inputStream.read(bytes);
     return new String(bytes, 0, len);
 }

 //转发其他客户端方法
 public void sendOther(Socket socket, String msg) throws IOException {
     for (Socket client : ServerTest.list) {
         if (client == socket) {

             //2.是自己返回带标记的消息
             outputStream = socket.getOutputStream();
             InetAddress localAddress = socket.getLocalAddress();
             //是自己的返回消息类型
             outputStream.write(("len" + msg +"\\r\\n\\n").getBytes());

             //客户端是自己不转发
             continue;
         }
         //输出对应客户端socket
         outputStream = client.getOutputStream();
         SocketAddress socketAddress = client.getLocalSocketAddress();
         //分割字符串,从第一个开始
         outputStream.write(( msg+"\\r\\n\\n").getBytes());
         System.out.println("已转发=>" + msg);

     }

2.4、服务器统计在线人数

//统计在线人数
    public void onLine() {
        int num = ServerTest.list.size();

        List<Socket> sockets = ServerTest.list;
        for (Socket client : sockets) {
            try {
                outputStream = client.getOutputStream();
                outputStream.write(("\\n        【当前在线人数:" + num + "】\\n").getBytes());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

2.5、启动服务器供多客户连接

public class ServerTest {

    //客户端集合
    protected static List<Socket> list = new ArrayList<>();

    public static void main(String[] args) {
        Service service = null;
        try {
            int i = 0;
            ServerSocket serverSocket = new ServerSocket(8848);
            while (true) {
                //一直接受客户端请求,多客户端连接
                Socket socket = serverSocket.accept();
                list.add(socket);

                //服务线程
                service = new Service(socket);

                //开启线程
                new Thread(service).start();

                service.onLine();
                System.out.println("socket=" + socket);
                System.out.println("主线程循环" + i++ + "次");
                System.out.println("客户端在线数:" + list.size());
            }
        } catch (IOException e) {
            e.printStackTrace();
            service.onLine();//出异常再次调用通知人数方法
        }
    }
}

2.6、写一个ip地址和端口号供客户端连接

 public static void main(String[] args) {
        try {
            ClientBase clientBase = new ClientBase("127.0.0.1",8848);
            clientBase.sendTest("hello world","hello world");
            System.out.println(clientBase.receiveTest());
        } catch (IOException e) {
            e.printStackTrace();
        }
  }

2.7、发送以及接收方法

 /**
    * 发送方法
    */
   public void sendTest(String username ,String msg) throws IOException {
       //获取 字节输出流
       if (msg.length() != 0) {
           outputStream = socket.getOutputStream();
           String sum =username+"说: "+msg;
           outputStream.write(sum.getBytes("UTF-8"));
       }
   }

   /**
    * 接收方法
    *
    * @return 接收内容
    */
   public String receiveTest() throws IOException {
       //获取服务器回应消息
       inputStream = socket.getInputStream();
       byte[] bytes = new byte[1024];
       int len = inputStream.read(bytes);
       try {
           string = new String(bytes, 0, len, "UTF-8");
       } catch (IOException e) {
           System.out.println("error");
       }
       return string;
   }

2.8、接收到统计的在线人数

 /*接收在线人数*/
 public String onLine() {
     String str = "";
     InputStream inputStream = null;
     try {
         inputStream = socket.getInputStream();
         byte[] bytes = new byte[1024];
         int len = inputStream.read(bytes);
         str = new String(bytes, 0, len);
     } catch (IOException e) {
         e.printStackTrace();
         try {
             inputStream.close();
         } catch (IOException ioException) {
             ioException.printStackTrace();
         }
     }
     return str;
 }

2.9、回到面板写入监听事件

//连接事件监听
       connection.addActionListener(new ActionListener() {
           @Override
           public void actionPerformed(ActionEvent e) {
               try {
                   clientBase = new ClientBase(ip.getText(), Integer.parseInt(port.getText()));
               } catch (IOException ioException) {
                   flag.setText("未连接");
                   flag.setForeground(red);
               }
               Socket socket = clientBase.getSocket();
               if (flag.getText().equals("已连接") && clientBase != null) {

               } else if (socket != null){
                   try {
                       flag.setText("已连接");
                       flag.setForeground(green);
                       //接收线程
                       new Thread(new Runnable() {
                           private boolean f = true;//线程标志位

                           @Override
                           public void run() {
                               //接收消息
                               while (f) {
                                   try {
                                       String text = clientBase.receiveTest();
                                       //判断转回的消息是不是自己的
                                       String temp = text.substring(0, 3);//截取前三位字符,注意不包括3  [0,3)
                                       //是否以len开始
                                       if (text.startsWith("len")) {
                                          // textRec.setForeground(blue);
                                           //追加收到内容
                                           textRec.append(text.substring(3));//截取第三个到末尾的字符串

                                       } else {
                                           textRec.setForeground(black);
                                           textRec.append(text);
                                       }
                                   } catch (IOException ioException) {
                                       flag.setText以上是关于java socket编程实现群聊(超详细)的主要内容,如果未能解决你的问题,请参考以下文章

C++语言实现网络聊天程序的设计与实现(基于TCP/IP协议的SOCKET编程)超详细(代码+解析)

Java编程实例-tcp聊天室代码实现

Java Socket通信实现私聊群聊

Java基于Socket实现聊天群聊敏感词汇过滤功能

Java基于Socket实现聊天群聊敏感词汇过滤功能

手动搭建I/O网络通信框架2:BIO编程模型实现群聊