网络通讯聊天工具的实现

news2024/9/8 10:33:54

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

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();显示在聊天区域即可。

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

一个用户连接:

第二个用户连接后:

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

第三个用户的加入:

聊天功能:

群聊:

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

不足:

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

代码的优化。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

openEuler系统通过shell脚本安装openGauss 5.0.0企业版

上次提到的开机自启动的配置&#xff0c;获得了LD的称赞&#xff0c;然而LD的要求&#xff0c;都是“既得陇复望蜀”的&#xff0c;他又期望我们能实现openGauss安装的“自动化”&#xff0c;于是尝试了下用shell脚本部署&#xff0c;附件中的脚本实测有效&#xff0c;openEule…

aws lakeformation跨账号共享数据的两种方式和相关配置

lakeformation授权方式分为 基于tag的授权基于命名资源的授权 先决条件 跨账号共享数据的先决条件&#xff08;命名资源和tag授权都需要&#xff09; 分两种情况 如果账户中没有glue data catalog资源策略&#xff0c;则LakeFormation跨账户授予将照常进行 如果存在glue d…

使用高性能NIO框架netty实现IM集群对聊方案

文章目录 前言技术积累什么是nettynetty如何实现IM如何实现IM集群 实战演示基础配置netty搭建IM集群redis发布订阅 实战测试 前言 在前面的博文中我们分享了原生websoket集群搭建&#xff0c;也用redis 发布订阅实现了集群消息正常有序分发。但是有不少同学希望风向一期netty实…

基于生命周期评价法的农田温室气体排放估算;农田CH4和N2O排放模拟;农田碳库模型和土壤呼吸等

目录 专题一 温室气体排放模拟研究 专题二 农田CH4和N2O排放模拟 专题三 农田碳库模型和土壤呼吸 专题四 基于生命周期评价法的农田温室气体排放估算 专题五-六 基于过程模型的温室气体排放模拟 专题七 案例模拟与疑难解答 更多应用 农业是甲烷&#xff08;CH4&#xff…

List基本使用(C++)

目录 1.list的介绍 2.list的使用 list的构造 list的size() 和 max_size() list遍历操作 list元素修改操作 assign()函数 push_front(),push_back 头插&#xff0c;尾插 pop_front() pop_back 头删尾删 insert()函数 swap()函数 resize()函数 clear()函数 list类数…

yolov10 瑞芯微 rknn 部署 C++代码

yolov10 目标检测rknn的C部署来了。 特别说明&#xff1a;如有侵权告知删除&#xff0c;谢谢。 直接上代码和模型&#xff0c;欢迎参考交流 【完整代码和模型】 1、rknn模型准备 pytorch转onnx&#xff0c;onnx再转rknn模型这一步就不再赘述&#xff0c;请参考上一篇 【yolov1…

TPM之VMK密封

本篇文章主要介绍基于TPM的Bitlocker全盘加密时,VMK密钥的密封(Seal)流程,至于TPM、Bitlocker、密钥保护器、VMK密钥等这些东西是什么,这里不做解释,需要自己脑补一下(╮(╯▽╰)╭)。 首先看看一张结构图(来自网络),了解一下TPM加密的基本框架与流程: 同样,基于…

关于读书,你可能没想到的陷阱、问题和思考

最近经常看到有人问&#xff1a;AI 已经这么发达了&#xff0c;如果以后的 AI 会更智能&#xff0c;那我们还有必要读书吗&#xff1f; 我认为&#xff1a;还是十分有必要的。 为什么呢&#xff1f;因为读书其实不仅仅是为了获取知识&#xff0c;它更重要的一个作用&#xff0c…

数据结构-堆排序问题

需要在数组里面进行排序&#xff0c;我们可以采取堆排序对其解决问题 版本1&#xff1a; 创建一个数组等大的堆&#xff0c;把数组里面的数值输入到堆里面进行堆排序&#xff0c;但是这样的弊端就是&#xff0c;不是顺序排序 版本2&#xff1a; 每次我们取堆顶然后打印&#xf…

3440亿!国家大基金三期正式落地,关注半导体与算力芯片!

重磅消息来了&#xff01; 5月24日&#xff0c;注册规模3,440亿元人民币的“国家集成电路产业投资基金三期股份有限公司”正式成立&#xff0c;这也意味着&#xff0c;传闻已久的**“国家大基金三期”正式落地&#xff01;** 企查查股东信息显示&#xff0c;该公司由财政部、国…

移动云:开发者手中的未来钥匙

《移动云&#xff1a;开发者手中的未来钥匙》 引言一、无缝集成&#xff0c;加速开发进程二、数据智能&#xff0c;洞悉用户心声三、安全合规&#xff0c;护航创新之旅四、成本优化&#xff0c;助力轻装前行总结 引言 在科技日新月异的今天&#xff0c;移动云已成为推动行业变革…

文件夹类型异常成文件:原因解析与恢复策略

在数字时代&#xff0c;数据的安全与完整性对于个人和企业都至关重要。然而&#xff0c;有时我们可能会遇到一种令人困惑的情况&#xff1a;原本应该是文件夹的图标&#xff0c;却突然变成了文件的图标&#xff0c;这就是所谓的“文件夹类型成文件”问题。本文将深入探讨这一现…

(原创)从右到左排列RecycleView的数据

问题的提出 当我们写一个Recycleview时&#xff0c;默认的效果大概是这样的&#xff1a; 当然&#xff0c;我们也可以用表格布局管理器GridLayoutManager做成这样&#xff1a; 可以看到&#xff0c;默认的绘制方向是&#xff1a; 从左到右&#xff0c;从上到下 那么问题来了…

香橙派 AIpro综合体验及AI样例运行

香橙派 AIpro综合体验及AI样例运行 环境&#xff1a; 香橙派版本&#xff1a; AIpro(8TOPSINT8) OS : Ubuntu 22.04.3 LTS(GNU/Linux 5.10.0 aarch64) (2024-03-18) 远程服务端1&#xff1a;OpenSSH 8.9p1 远程服务端2&#xff1a;TightVNC Server 1.3.10 远程客户端&#xf…

使用numpy手写一个神经网络

本文主要包含以下内容&#xff1a; 推导神经网络的误差反向传播过程使用numpy编写简单的神经网络&#xff0c;并使用iris数据集和california_housing数据集分别进行分类和回归任务&#xff0c;最终将训练过程可视化。 1. BP算法的推导过程 1.1 导入 前向传播和反向传播的总体…

基于EBAZ4205矿板的图像处理:10gamma变换

基于EBAZ4205矿板的图像处理&#xff1a;10gamma变换 项目全部文件 会上传项目全部文件&#xff0c;如果没传&#xff0c;可以私信催我一下&#xff0c;最近太忙了 先看效果 我的项目中的gamma的变换系数为2.2&#xff0c;是会让图像整体变暗的&#xff0c;看右图说明我的ga…

哪款洗地机好用?洗地机十大排行榜

在智能家电飞速发展的今天&#xff0c;洗地机因其吸拖洗一体化的技术优势&#xff0c;成为越来越多家庭的清洁利器。它不仅能快速清理各种地面污渍&#xff0c;还能轻松处理干湿垃圾&#xff0c;大大提升了日常清洁的效率。可是面对市场上琳琅满目的洗地机品牌和型号&#xff0…

数据持久化第六课-ASP.NET运行机制

数据持久化第六课-ASP.NET运行机制 一.预习笔记 1.动态网页的工作机制通常分为以下几个阶段&#xff1a; 1&#xff09;使用动态Web开发技术编写Web应用程序&#xff0c;并部署到Web服务器。 2&#xff09;客户端通过在浏览器中输入地址&#xff0c;请求动态页面。 3&#…

Swift 初学者交心:在 Array 和 Set 之间我们该如何抉择?

概述 初学 Swift 且头发茂密的小码农们在日常开发中必定会在数组&#xff08;Array&#xff09;和集合&#xff08;Set&#xff09;两种类型之间的选择中“摇摆不定”&#xff0c;这也是人之常情。 Array 和 Set 在某些方面“亲如兄弟”&#xff0c;但实际上它们之间却有着“云…

关于DDos防御...别在听别人瞎扯了.....

前言 无意间刷文章的时候看到一篇文章&#xff0c;写的是遇到ddos&#xff0c;怎么用iptables封IP....... 然后我就百度搜了一下&#xff0c;好多都是这么说的&#xff0c;但是我发现&#xff0c;大多数人只要遭受过长期Ddos的&#xff0c;就不会再信网上的文章 文笔不太好&…