网络通讯聊天工具的实现

学习网络与通信,实现聊天界面能够通过服务器进行私聊和群聊的功能。

1.服务器:ServeSocket

客户端先发送消息给服务器,服务器接受消息后再发送给客户端。

利用服务器随时监听。等待客户端的请求,一旦有请求便生产一个socket套接字用于双方之间的数据传输。在此需要指定服务器的端口号。

端口号:区分一个IP地址中套接字。

package ChatV2.Server;
import java.io.IOException;
import java.net.ServerSocket;

public class Server {
    private int port = 50001;
    ServerSocket serverSocket;
    {
        try {
            serverSocket = new ServerSocket(port);
            System.out.println("创建服务端成功");
            ListenerRunnable lr = new ListenerRunnable(serverSocket);
            new Thread(lr).start();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public static void main(String[] args) {
        new Server();
    }
}

同时在服务器中我还创建了两个线程,一个用于不断接受新的客户端的连接,一个用于与各客户端的发送接受消息。

1.1接受客户端连接的线程

这个线程需要接受用户端的连接,那么就需要创建的serverSocket对象,利用ServerSocket.accept();方法接受对象,后接受其创建后返回的socket对象。此方法为阻塞方法,一直等待客户端的接入。

并且将此对象放入创建的Socket套接字的队列中,以便使用。

package ChatV2.Server;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ArrayBlockingQueue;

public class ListenerRunnable implements Runnable{
    private ServerSocket serverSocket;
    private Socket socket;
    private OutputStream os;

    //存放接入的socket
    private static ArrayBlockingQueue<Socket> sockets = new ArrayBlockingQueue<>(10);
    public ListenerRunnable(ServerSocket serverSocket) {
        this.serverSocket = serverSocket;
    }

    public ListenerRunnable() {

    }

    @Override
    public void run() {
        while (true) {
            try {
                socket = serverSocket.accept();//接受,阻塞方法,
                //存入到sockets中
                sockets.offer(socket);
                System.out.println(socket.getPort() + "连接成功");
                //savePort();
                InputStream is = socket.getInputStream();
                //创建接受信息的线程
                InformationRunnable ir = new InformationRunnable(is,socket,sockets);
                //发送给其他所有用户 当前用户的端口号
                String socketMsg = "3#";
                for (int i = 0; i < sockets.size(); i++) {
                    Socket temSocket = sockets.poll();
                    System.out.println("发送端口号");
                    socketMsg += temSocket.getPort() + "#";
                    sockets.offer(temSocket);
                }
                groupOutput(socketMsg);
                new Thread(ir).start();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
    //群发所有端口
    public void groupOutput(String msg) {
        try {
            int size = sockets.size();
            for (int i = 0; i < size; i++) {
                System.out.println("多少个用户端:" + sockets.size());
                Socket temSocket = sockets.poll();
                os = temSocket.getOutputStream();
                output(msg);
                sockets.offer(temSocket);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public void output(String msg) {
        try {
            byte[] msgBytes = msg.getBytes(StandardCharsets.UTF_8);//转化为字节型
            int ml = msgBytes.length;
            os.write(ml);//大小
            for (int i = 0; i < msgBytes.length; i++) {
                os.write(msgBytes[i]);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}

同时在此接受到新客户端的连接后,将此时所有的端口号再转发给所有的客户端用于私聊功能的实现。

输出流的写入实现方法:

利用套接字中OutputStream 与 InputStream 来读取与写入字符

每次写入与读取只能写入/读取一个字节,那么就会存在多个字节如何组成我们想要的字符,以及接受端究竟该接受多少个字符才能写入段的一句完成的对话的问题。

第一个问题的解决:

        只需使用相同的字符编码方式,将字符串类型转化为字节类型中选择UTF-8的字符编码方式,接受端也是用此编码方式 即可保证为相同的字符。

第二个问题的解决:

        我们将字符串转化为字节类型,用数组存储。先将此数组的大小传过去,之后接收端创建相同大小的数组用于接受,接受完后转为字符串即可。

            int ml = is.read();//用于接受字节大小
            byte[] msgBytes = new byte[ml];//发过来的一串信息
            for (int i = 0; i < msgBytes.length; i++) {
                int readByte = is.read();
                msgBytes[i] = (byte) readByte;
            }
            String msg = new String(msgBytes, StandardCharsets.UTF_8);

1.2接受消息与发送消息的线程

这个线程需要判断发过来的信息是群发还是私聊。我的发送过来的消息的格式为“ 字符1# 字符2”如果字符21为0则为群聊,字符二为发送的消息。“字符一#字符二#字符三”如果字符1 为1则为私聊,此时字符二为私聊的端口号,字符三为消息。用‘#’进行分割。

package ChatV2.Server;

import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ArrayBlockingQueue;

public class InformationRunnable implements Runnable {
    private InputStream is;
    private OutputStream os;
    private Socket socket;
    private ArrayBlockingQueue<Socket> sockets;

    public InformationRunnable(InputStream is, Socket socket, ArrayBlockingQueue<Socket> sockets) {
        this.is = is;
        this.socket = socket;
        this.sockets = sockets;
    }

    @Override
    public void run() {
        while (true) {
            getIMG();
        }
    }

    //接受消息
    public void getIMG() {
        try {
            System.out.println("客户端说:");
            int ml = is.read();//用于接受字节大小
            byte[] msgBytes = new byte[ml];//发过来的一串信息
            for (int i = 0; i < msgBytes.length; i++) {
                int readByte = is.read();
                msgBytes[i] = (byte) readByte;
            }
            String msg = new String(msgBytes, StandardCharsets.UTF_8);
            String[] msg1 = msg.split("#");//分开
            if (msg1[0].equals("0")) {//群聊
                System.out.println("群聊消息:");
                groupOutput(msg1[1] + "#");
            } else {//私聊 确认发送对象
                System.out.println("私聊消息:");
                String msg0 = "";//发送给发送信息的人
                int size = sockets.size();
                for (int i = 0; i < size; i++) {
                    System.out.println("查找私聊对象");
                    Socket temSocket = sockets.poll();
                    if (msg1[1].equals(String.valueOf(temSocket.getPort()))) {//给私聊对象发送
                        os = temSocket.getOutputStream();
                        msg1[2] = "私聊:" + msg1[2] +"#";
                        msg0 = "私聊给:" + msg1[1] + msg1[2] +"#";
                        output(msg1[2]);
                    }
                    sockets.offer(temSocket);
                }
                //发送给消息对象
                os = socket.getOutputStream();
                output(msg0);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
    //群发方式
    public void groupOutput(String msg) {
        try {
            int size = sockets.size();
            for (int i = 0; i < size; i++) {
                System.out.println("多少个用户端:" + sockets.size());
                Socket temSocket = sockets.poll();
                os = temSocket.getOutputStream();
                output(msg);
                sockets.offer(temSocket);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //发送消息
    public void output(String msg) {
        try {
            byte[] msgBytes = msg.getBytes(StandardCharsets.UTF_8);//转化为字节型
            int ml = msgBytes.length;
            os.write(ml);//大小
            for (int i = 0; i < msgBytes.length; i++) {
                os.write(msgBytes[i]);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //以端点命名的文件,将端点及聊天内容全部写入文件过来。
    public synchronized void saveInformation(String information) throws IOException {
        String path = "C:\\Users\\15697\\IdeaProjects\\Pro24\\src\\Chat\\UsersInformation\\document";
        FileWriter fw = new FileWriter(path, true);
        BufferedWriter bw = new BufferedWriter(fw);
        bw.write(information);
        bw.write("#");//分割消息
        System.out.println("写入消息:" + information);
        bw.close();
    }
}

将字节数组转化为字符串后,里面split("#");方法将字符串分割为多个字符串数组msg1。判断msg1[0]是否为0来区分群聊或者私聊。

1.21群聊的实现

判断后调用groupOutput()群聊方法。

在这方法会将我们先前存入的socket队列一 一取出并 使用它的outputStream发送消息

    public void groupOutput(String msg) {
        try {
            int size = sockets.size();
            for (int i = 0; i < size; i++) {
                System.out.println("多少个用户端:" + sockets.size());
                Socket temSocket = sockets.poll();
                os = temSocket.getOutputStream();
                output(msg);
                sockets.offer(temSocket);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //发送消息
    public void output(String msg) {
        try {
            byte[] msgBytes = msg.getBytes(StandardCharsets.UTF_8);//转化为字节型
            int ml = msgBytes.length;
            os.write(ml);//大小
            for (int i = 0; i < msgBytes.length; i++) {
                os.write(msgBytes[i]);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

1.22私聊的实现

         与群聊不同的是他需要判断私聊的对象。将各个套接字取出后对比msg1[1]与各个套接字的端口号。找到了则取出它的输出流,设置发送的字符,调用output()方法即可。同时需要发送给消息的发送者,让其显示在界面上。

                System.out.println("私聊消息:");
                String msg0 = "";//发送给发送信息的人
                int size = sockets.size();
                for (int i = 0; i < size; i++) {
                    System.out.println("查找私聊对象");
                    Socket temSocket = sockets.poll();
                    if (msg1[1].equals(String.valueOf(temSocket.getPort()))) {//给私聊对象发送
                        os = temSocket.getOutputStream();
                        msg1[2] = "私聊:" + msg1[2] +"#";
                        msg0 = "私聊给:" + msg1[1] + msg1[2] +"#";
                        output(msg1[2]);
                    }
                    sockets.offer(temSocket);
                }
                //发送给消息对象
                os = socket.getOutputStream();
                output(msg0);

到此服务器的接受与发送功能以实现。

2.客户端:

需要知道服务器的ip以及端口号,设置即可

package ChatV2.Client;

import ChatV2.DATA;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

public class Clint implements DATA {
    private Socket socket;//IP 端点

    public int getPort() {
        return socket.getLocalPort();
    }

    private OutputStream os;

    private InputStream is;

    public InputStream getInputStream() {
        return is;
    }

    {
        try {
            socket = new Socket("127.0.0.1", 50001);
            os = socket.getOutputStream();
            is = socket.getInputStream();
        } catch (IOException e) {
            /* throw new RuntimeException(e);*/
            System.out.println("无法连接服务器");
        }
    }


    public void output(String msg, int index) {
        try {
            //使用#隔开 第一个为类型,第二个为端口号,第三部分为内容 0为私聊
            String s = "";
            if (IMG[1] == 0) {
                s += IMG[1] + "#" + msg;
            } else {
                s += IMG[1] + "#" + PORT.get(index) + "#" + msg;
            }
            System.out.println("待发送消息:" + s);
            byte[] msgBytes = s.getBytes(StandardCharsets.UTF_8);
            int ml = msgBytes.length;
            os.write(ml);
            for (int i = 0; i < ml; i++) {
                os.write(msgBytes[i]);
            }
            
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

同时我在里面写了一个发送给服务器信息的方法output(String msg,int index)msg为发送的信息,格式与服务器接受的格式一致,index则为接受服务端发送端口号后,存入的信息数组的下标,以便确认私聊对象。

其他的交给界面UI来实现。

3.聊天界面:

结构:以及在此调用用户端。

DATA接口中创建了存放界面需要多次使用的数据。用户的端口号,以及生成的单选按钮,选择聊天对象,以及数据。


import javax.swing.*;
import java.util.ArrayList;

public interface DATA {
    //存放用户
    ArrayList<String> PORT = new ArrayList<>();
    //用来存放按钮,表示各用户
    JRadioButton[] JRB = new JRadioButton[10];
    //下标0为记录连接人数以及群聊。1为聊天方式为群聊或私聊
    int[] IMG = new int[2];
}

界面的设置不多说:

package ChatV2;
import ChatV2.Client.Clint;

import javax.swing.*;
import java.awt.*;

public class ChatUI extends JFrame implements DATA{
    Clint clint;

    public ChatUI() {
        setTitle("MyChat");
        setSize(900, 700);
        setLayout(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        //按钮组
        ButtonGroup bg = new ButtonGroup();
        //setLayout(new FlowLayout());
        JLabel jl = new JLabel("群聊");
        //输入框
        JTextArea jta = new JTextArea();
        jta.setBounds(10, 500, 700, 200);
        //JScrollPane scrollPane = new JScrollPane(jta);//翻页的作用
        //scrollPane.set
        JButton jb = new JButton("发送");//需添加监听
        jb.setBounds(710, 550, 75, 50);
        ButtonListener bl = new ButtonListener(jta);
        jb.addActionListener(bl);
        //显示框:
        JTextArea chatArea = new JTextArea( );//几行几列
       // chatArea.setPreferredSize(new Dimension(500,480));
        chatArea.setBounds(10,10,700,480);
        //设置不可编辑
        chatArea.setEditable(false);
        //添加滚动功能
        JScrollPane scrollPane = new JScrollPane(chatArea);
        add(scrollPane,BorderLayout.CENTER);//放置界面的中心
        add(chatArea);
        //刷新列表按钮
        JButton jb1 = new JButton("刷新列表");
        jb1.addActionListener(bl);
        jb1.setBounds(750,30,100,30);
        JRadioButton jrb1 = new JRadioButton("群聊");
        bg.add(jrb1);
        IMG[0] = 1;
        JRB[IMG[0]-1] = jrb1;
        add(jrb1);
        add(jb1);
        add(jb);
        add(jta);
        add(jl);
        setVisible(true);
        clint = new Clint();
        bl.clint = clint;//用户端传过去
        bl.ui = this;
        bl.chatArea = chatArea;
        Thread thread = new Thread(new ChatRunnable(chatArea,clint.getInputStream(),this,bg));
        thread.start();

    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        System.out.println("加载列表");
        System.out.println("人数:"+IMG[0]);
        if(IMG[0] != 0){
            for (int i = 0; i <= IMG[0]; i++) {
                JRB[i].setBounds(750,80+i*40,100,30);
            }
        }
    }

    public static void main(String[] args) {
        new ChatUI();
    }
}

注:重写paint()方法,对单选按钮进行位置及大小的设置。

同时需要将单选按钮加入一个组 ButtonGroup创建的组中才能实现不能多选的功能。

同时需要按钮的监听,以及一个随时接受消息,并显示在聊天区域的线程。线程在打开UI时就启动。

3.1按钮监听的实现内容:

目前有两个按钮,一个为“发送”,一个为“刷新列表”。

“发送”:发送信息给服务器。获取文本后调用clint中output()的方法。实现发送信息

“刷新列表”:调用repaint()进行重绘。当有新的客户端接入的时候则点击“刷新列表”,即可选择新的客户进行私聊。

package ChatV2;

import ChatV2.Client.Clint;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;

public class ButtonListener implements ActionListener, DATA {
    JTextArea jta;
    Clint clint;
    JFrame ui;
    JTextArea chatArea;

    public ButtonListener(JTextArea jta) {
        this.jta = jta;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        String ae = e.getActionCommand();
        if (ae.equals("发送")) {//按一次发送一次文件
            System.out.println("获取文本");
            //获取文本,启动用户端
            String msg = clint.getPort()+":"+jta.getText();
            //获取后清空
            jta.setText(null);
            //发送信息
            int index = groupOrPrivate();
            clint.output(msg,index);
            System.out.println("下标"+ index);
        } else if (ae.equals("刷新列表")) {
            System.out.println("刷新列表");
            ui.repaint();
        }
    }
  
    //监控按钮此时为群聊或者私聊
    public int groupOrPrivate(){

        Boolean[] b = new Boolean[IMG[0]+1];
        for (int i = 0; i < b.length; i++) {
            b[i] = JRB[i].isSelected();
        }
        System.out.println(Arrays.toString(b));
        for (int i = 0; i < b.length; i++) {
            System.out.println("判断私聊or群聊");
            if(b[0]){
                IMG[1] = 0;//群聊
                System.out.println("群聊");
                return -1;
            } else if (b[i]) {
                System.out.println("私聊");
                IMG[1] = 1;//私聊
                //此时的i,端口。
                return i-1;
            }
        }
        System.out.println("判断失败,自动为群聊");
        return -1;
    }
}

实现了一个判断此时选择的是群聊还是私聊,以及选择私聊的对象的下标。

当为群聊返回-1,私聊则返回其存在DATA接口中端口号在PORT数组中的下标。如果没选则默认为群聊,返回-1;

3.2接受消息的线程:

package ChatV2;

import javax.swing.*;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

public class ChatRunnable implements Runnable, DATA {
    InputStream is;
    JTextArea chatArea;
    JFrame ui;
    ButtonGroup bg;

    public ChatRunnable(JTextArea chatArea, InputStream is, JFrame ui, ButtonGroup bg) {
        this.is = is;
        this.chatArea = chatArea;
        this.ui = ui;
        this.bg = bg;
    }

    @Override
    public void run() {
        while (true) {
            //接受消息
            try {
                int ml ;//用于接受字节大小
                ml = is.read();
                byte[] msgBytes = new byte[ml];//发过来的一串信息
                for (int i = 0; i < msgBytes.length; i++) {
                    int readByte = is.read();
                    msgBytes[i] = (byte) readByte;
                }
                String msg = new String(msgBytes, StandardCharsets.UTF_8);
                String[] msg1 = msg.split("#");
                if (msg1[0].equals("3")) {//为端口记录号
                    System.out.println("接受消息为端口号");
                    int size = msg1.length;
                    int count = 0;
                    PORT.clear();
                    for (int i = 1; i < size; i++) {
                        PORT.add(msg1[i]);
                        count++;
                    }
                    IMG[0] = count;
                    addUser();
                } else {
                    sendMsg(msg1[0]);
                    System.out.println("接受到消息:" + msg);
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public void sendMsg(String msg) {
        chatArea.append(msg + "\n");//发送后换行
        chatArea.setCaretPosition(chatArea.getDocument().getLength());//跳转到最新消息
    }

    //创建新的用户,并存入按钮中
    public void addUser() {
        for (int i = 1; i <= IMG[0]; i++) {
            JRadioButton jrb = new JRadioButton(PORT.get(i-1));
            JRB[i] = jrb;
            bg.add(jrb);
            ui.add(jrb);
        }
        System.out.println("已有新用户加入,共" + IMG[0] + "个用户连接,请刷新列表");
    }
}

方法:

1.sendMsg(String msg);将字符串msg添加到界面的聊天显示界面中,同时调转到发送消息的最下面。

2.addUser();将读取的端口号重新设置在新的单选按钮中,并添加好组,以及添加到界面。

线程功能:接受发送过来的字节,并转为字符串。接着将其用“#”进行分割为字符串数组。

msg1[0]为"3"则为发送的端口号,将后面的进行存储。其他则为私聊或者群聊的消息,调用sendMsg();显示在聊天区域即可。

聊天工具的最终实现效果:

一个用户连接:

第二个用户连接后:

第一个界面刷新前:刷新后:

第三个用户的加入:

聊天功能:

群聊:

私聊:未选择客户端接受不到。

不足:

功能不够齐全。加上文件的发送等功能。

代码的优化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/650678.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

51单片机-实机演示(单多个数码管)

仿真链接&#xff1a; http://t.csdnimg.cn/QAPhx 目录 一.引脚位置 二.多个显示 三 扩展 一.引脚位置 注意P00 - >A ; 这个多个的在左边,右边的A到B是控制最右边那个单个的. 接下来上显示单个的代码 #include <reg52.h> #include <intrins.h> #define u…

爬虫利器Frida RPC入门——夜神模拟器环境篇

Frida是一款轻量级HOOK框架&#xff0c;可用于多平台上&#xff0c;例如android、windows、ios等。 frida分为两部分&#xff0c;服务端运行在目标机上&#xff0c;通过注入进程的方式来实现劫持应用函数&#xff0c;另一部分运行在系统机器上。frida上层接口支持js、python、…

前端开发之xlsx的使用和实例,并导出多个sheet

前端开发之xlsx的使用和实例 前言效果图1、安装2、在页面中引用3、封装工具类(excel.js)4、在vue中使用前言 在实现业务功能中导出是必不可少的功能,接下来为大家演示在导出xlsx的时候的操作 效果图 1、安装 npm install xlsx -S npm install file-saver2、在页面中引用 值…

写好的文章怎样联系媒体投稿?

作为单位信息宣传的桥梁,我肩负着将单位的每一次活动、每一项成就转化为社会认可与赞美的重任。初涉此职,我满腔热血,以为凭借扎实的文字功底与不懈的努力,便能在各大媒体平台上为单位赢得一席之地。然而,现实很快就给了我一记响亮的耳光。 我最初采取的是最直接的方式——邮箱…

办公必备!一键拆分文件,效率翻倍的秘密

需求介绍 1、我有一张数据表“测试数据.xlsx” 2、我要根据A1“COUNTY_CODE”分类拆分成几张数据表&#xff08;这里从9657到9658共12类&#xff0c;就是拆分成12张数据表&#xff09; 3、根据12个分类&#xff0c;发送数据邮件给对应的收件人 4、收件人及抄送人、共同抄送人…

执行普罗米修斯插件mysqld_exporter出现闪退问题如何解决?

运行 mysqld_exporter.exe 文件闪退的问题可能是由于配置文件或环境变量设置不正确导致的。 检查配置文件 my.cnf&#xff1a; 打开 my.cnf 文件&#xff0c;确保其中的配置项正确无误&#xff0c;尤其是 MySQL 数据库的连接信息。配置示例&#xff1a;[client] useryour_mysql…

小猪APP分发:一站式免费应用推广的理想平台

在日益拥挤的移动应用市场中&#xff0c;对于独立开发者和新兴应用而言&#xff0c;找到一个高效且成本效益高的分发渠道至关重要。这正是小猪APP分发平台www.appzhu.cn脱颖而出的原因&#xff0c;它不仅提供了一个全面的解决方案&#xff0c;帮助开发者免费推广他们的应用程序…

theharvester一键收集域名信息(KALI工具系列十)

目录 1、KALI LINUX简介 2、theharvester工具简介 3、在KALI中使用theharvester 3.1 用搜索引擎扫描 3.2 扫描并输出结果 3.3 扫描某域名下的所有账号 3.4 使用所有的搜索引擎扫描 4、总结 1、KALI LINUX简介 Kali Linux 是一个功能强大、多才多艺的 Linux 发行版&…

全自动机器学习AutoML高效预测时间序列

大家好&#xff0c;时间序列数据是许多现实世界问题的核心&#xff0c;例如预测能源消耗、气象预报、库存管理。传统上&#xff0c;使用专门的时间序列模型如Prophet来对此类数据进行建模和预测。然而&#xff0c;通过将时间序列数据转换为表格格式&#xff0c;可以利用更广泛的…

长江电力:“你们随意,我躺赢”

“只要长江不断流&#xff0c;我们就躺着挣钱。”这是某股股吧里的股东们喊出的。 今天说的这个公司“没什么意思”&#xff0c;十年来股价一直涨一直涨&#xff0c;涨了5倍&#xff0c; &#xff08;最&#xff09;重要的是&#xff0c;持有体验特别好&#xff0c;几乎没有什…

虚拟化知识学习

虚拟化知识学习 关键概念和术语的简要介绍 虚拟化的基本概念 虚拟机 (VM)&#xff1a;一个虚拟机是一个模拟计算机系统的环境。它运行在物理硬件之上&#xff0c;但与物理硬件隔离&#xff0c;提供类似于物理计算机的功能。 虚拟化技术&#xff1a;这是指使用软件来创建虚拟版…

麒麟系统firewalld限制指定的ip访问指定的端口

先开放所有端口 然后第三个限制会把第四个第五个拦住 so 这个是错误案例 accecpt 接受 reject 拒绝

FreeRtos进阶——关于任务的深入探究

创建任务函数 在我们创建任务中&#xff0c;会有几个比较神奇的参数&#xff0c;例如函数名称&#xff0c;以及栈大小。在我们创建任务时&#xff0c;也相应的要为每一个任务创建栈。这里面的栈除了用于任务数组开辟的空间外&#xff0c;还可以用于保存现场&#xff0c;例如有S…

Android:使用Kotlin搭建MVI架构模式

一、简介MVI架构模式 M&#xff1a;Model 数据层&#xff0c;包含应用数据和业务逻辑V&#xff1a;View 界面层&#xff0c;在屏幕上显示应用数据&#xff0c;包含与界面相关的状态和界面逻辑&#xff0c;根据界面状态对象更新UI&#xff0c;界面状态定义是不可变的。这样的主要…

基于51单片机的智能灯光控制系统

一.硬件方案 智能灯光控制系统由单片机最小系统、人体感应模块、关照强度模块、灯光控制模块、电源模块和灯泡组成。本文以STC89C52单片机为核心&#xff0c;通过利用光照度和红外人体感应相结合主动与被动的探测方法&#xff0c;现了室内无人或者关照充足时灯光自动光灯&…

电脑找不到opencl.dll原因分析及5种详细的解决方法

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“找不到opencl.dll”。这通常意味着计算机中缺少或损坏了与OpenCL&#xff08;开放计算语言&#xff09;相关的动态链接库文件。OpenCL允许应用程序利用图形处理器&#xff08;GPU&#xff…

linux 查看csv文件,按指定列聚合 排序

在Linux中&#xff0c;你可以使用awk工具来查看CSV文件的内容&#xff0c;并按照指定的列进行聚合。awk是一种强大的文本处理工具&#xff0c;它可以处理文本文件中的数据&#xff0c;并根据条件执行相应的操作。 以下是一个示例&#xff0c;假设你有一个名为data.csv的CSV文件…

四川景源畅信:抖音小店新手如何做?

随着短视频平台的兴起&#xff0c;抖音小店成为了许多创业者的新选择。但是&#xff0c;对于新手来说&#xff0c;如何在抖音上开设并经营好自己的小店呢?本文将围绕这一问题展开讨论。 一、明确目标和定位作为抖音小店的新手&#xff0c;首先要明确自己的经营目标和定位。是想…

用队列实现栈 用栈实现队列 设计循环队列

用队列实现栈 思路 栈的特点&#xff1a;后进先出 队列的特点&#xff1a;先进先出 使用两个队列实现栈&#xff1a; 我们可以使用两个队列&#xff0c;一个队列为&#xff1a;空队列&#xff0c;一个队列为&#xff1a;非空队列 当我们要出队列时&#xff1a; 将 size - …

C++: 二叉搜索树及实现

目录 一、二叉搜索树的概念 二、二叉搜索树的操作 2.1插入 2.2删除 1.有左子树&#xff0c;无右子树 2.有右子树&#xff0c;无左子树 3.有左子树和右子树 三、二叉搜索树的实现 要点 前言&#xff1a;为了学习map和set&#xff0c;需要先学二叉搜索树作为铺垫。 一、…