Netty 创建高性能聊天室 单聊 群聊 websocket

news2024/11/26 4:39:35

目录

一、简单实现Netty发送消息的案例

二、websocket连接注册用户

三、实现单聊

四、群聊功能

五、案例代码


一、简单实现Netty发送消息的案例

案例一的依赖有:若没springboot项目有自动对应版本,其他版本可以使用maven仓库的最新版本。

        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
        </dependency>

新建Netty服务的启动器:配置如下

package com.dragonwu.server;

import com.dragonwu.server.handler.WebSocketHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;

/**
 * @author DragonWu
 * @since 2023-01-05 14:02
 **/
public class IMServer {
    public static void start() {
        //创建线程池
        EventLoopGroup boss = new NioEventLoopGroup();
        //创建工作线程
        EventLoopGroup worker = new NioEventLoopGroup();

        //绑定端口
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(boss, worker) //将线程放入线程池
                .channel(NioServerSocketChannel.class) //选择NIO的channel类型
                .childHandler(new ChannelInitializer<SocketChannel>() { //初始化handler
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        ChannelPipeline pipeline = socketChannel.pipeline();
                        //添加http编码解码器
                        pipeline.addLast(new HttpServerCodec())
                                //支持大数据流
                                .addLast(new ChunkedWriteHandler())
                                //对http消息做聚合操作,会产生FullHttpRequest、FullHttpResponse
                                .addLast(new HttpObjectAggregator(1024 * 64))
                                //websocket支持
                                .addLast(new WebSocketServerProtocolHandler("/")) //websocket的根路径
                                .addLast(new WebSocketHandler());
                    }
                });

        //绑定Netty的启动端口
        ChannelFuture future = bootstrap.bind(8888);
    }
}

创建软件启动类:最简单的一个Netty服务已书写完毕,下面来进行测试

package com.dragonwu;

import com.dragonwu.server.IMServer;

/**
 * @author DragonWu
 * @since 2023-01-05 14:01
 **/
public class NettyIMApplication {
    public static void main(String[] args) {
        IMServer.start();
    }
}

websocket的连接测试:连接服务器

websocket在线测试

 发送消息后,可以看到服务器接收到消息了,这是最简单的实现:

二、websocket连接注册用户

在实现聊天前,先来实现前后端的websocket连接注册步骤:

IMServer修改为:

package com.dragonwu.server;

import com.dragonwu.server.handler.WebSocketHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.util.concurrent.GlobalEventExecutor;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author DragonWu
 * @since 2023-01-05 14:02
 **/
public class IMServer {

    public static final Map<String, Channel> USERS = new ConcurrentHashMap<>(1024);

    public static final ChannelGroup GROUP=new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    public static void start() {
        //创建线程池
        EventLoopGroup boss = new NioEventLoopGroup();
        //创建工作线程
        EventLoopGroup worker = new NioEventLoopGroup();

        //绑定端口
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(boss, worker) //将线程放入线程池
                .channel(NioServerSocketChannel.class) //选择NIO的channel类型
                .childHandler(new ChannelInitializer<SocketChannel>() { //初始化handler
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        ChannelPipeline pipeline = socketChannel.pipeline();
                        //添加http编码解码器
                        pipeline.addLast(new HttpServerCodec())
                                //支持大数据流
                                .addLast(new ChunkedWriteHandler())
                                //对http消息做聚合操作,会产生FullHttpRequest、FullHttpResponse
                                .addLast(new HttpObjectAggregator(1024 * 64))
                                //websocket支持
                                .addLast(new WebSocketServerProtocolHandler("/")) //websocket的根路径
                                .addLast(new WebSocketHandler());
                    }
                });

        //绑定Netty的启动端口
        ChannelFuture future = bootstrap.bind(8888);
    }
}

创建聊天命令枚举类:用于判断发送的数据类型

package com.dragonwu.server.domain.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * @author DragonWu
 * @since 2023-01-05 14:38
 * 聊天的命令枚举
 **/
@Getter
@AllArgsConstructor
public enum CommandType {
    CONNECTION(1001),
    CHAT(1002),
    JOIN_GROUP(1003),
    ERROR(-1);

    private final Integer code;

    public static CommandType match(Integer code) {
        for (CommandType value : CommandType.values()) {
            if (value.getCode().equals(code)) {
                return value;
            }
        }
        return ERROR;
    }
}

创建命令消息接收对象:接收到的json数据将会被转换为该类型,方便后续处理

package com.dragonwu.server.domain.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author DragonWu
 * @since 2023-01-05 14:42
 **/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Command {

    //连接信息编码
    private Integer code;

    //用户昵称
    private String nickname;
}

为了方便前后的数据接收,这里再创建一个案例的Result的vo对象:

package com.dragonwu.server.domain;

import com.alibaba.fastjson2.JSON;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

/**
 * @author DragonWu
 * @since 2023-01-05 15:01
 **/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result {

    private String name;

    private LocalDateTime time;

    private String message;

    public static TextWebSocketFrame fail(String message) {
        return new TextWebSocketFrame(JSON.toJSONString(new Result("系统消息", LocalDateTime.now(), message)));
    }

    public static TextWebSocketFrame success(String message) {
        return new TextWebSocketFrame(JSON.toJSONString(new Result("系统消息", LocalDateTime.now(), message)));
    }

    public static TextWebSocketFrame success(String name, String message) {
        return new TextWebSocketFrame(JSON.toJSONString(new Result(name, LocalDateTime.now(), message)));
    }
}

创建连接处理器:这是WebsocketHandler主处理器的一个调用,处理连接时的操作

package com.dragonwu.server.handler;

import com.alibaba.fastjson2.JSON;
import com.dragonwu.server.IMServer;
import com.dragonwu.server.domain.Result;
import com.dragonwu.server.domain.pojo.Command;
import io.netty.channel.ChannelHandlerContext;

/**
 * @author DragonWu
 * @since 2023-01-05 14:58
 * 连接请求的处理器
 **/
public class ConnectHandler {

    public static void execute(ChannelHandlerContext channelHandlerContext, Command command) {
        //判断用户是否已上线
        if (IMServer.USERS.containsKey(command.getNickname())) {
            channelHandlerContext.channel().writeAndFlush(Result.fail("该用户已上线,请换个昵称再试~"));
            //断开连接
            channelHandlerContext.channel().disconnect();
            return;
        }

        IMServer.USERS.put(command.getNickname(), channelHandlerContext.channel());

        channelHandlerContext.channel().writeAndFlush(Result.success("与服务端建立连接成功"));
        //返回群聊的人
        channelHandlerContext.channel().writeAndFlush(Result.success(JSON.toJSONString(IMServer.USERS.keySet())));
    }
}

接下来试WebsocketHanler的连接处理:调用其他处理器进行不太类型的操作

package com.dragonwu.server.handler;

import com.alibaba.fastjson2.JSON;
import com.dragonwu.server.domain.Result;
import com.dragonwu.server.domain.enums.CommandType;
import com.dragonwu.server.domain.pojo.Command;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;

/**
 * @author DragonWu
 * @since 2023-01-05 14:21
 **/
public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {//通过TextWebSocketFrame作为消息承载体

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {
        try {
            //将json文本解析为指令对象
            Command command = JSON.parseObject(textWebSocketFrame.text(), Command.class);
            switch (CommandType.match(command.getCode())) {
                case CONNECTION:
                    ConnectHandler.execute(channelHandlerContext, command);
                    break;
                default:
                    channelHandlerContext.channel().writeAndFlush(Result.fail("不支持CODE"));
            }
        } catch (Exception e) {
            channelHandlerContext.channel().writeAndFlush(Result.fail("错误消息:" + e.getMessage()));
        }

    }
}

后端到这里,重启后端服务器。

前端如下:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>websocket</title>
</head>
<body>
</body>
<script>

    //创建一个websocket对象
    let ws = new WebSocket("ws://localhost:8888")

    //连接成功时触发
    ws.onopen = function (event) {
        console.log("连接成功");
        ws.send("{\"nickname\":\"" + nickname + "\",\"code\":\"1001\"}");
    }

    //连接失败时触发
    ws.onerror = function (event) {
        console.log("连接失败");
    }

    //接收消息时触发
    ws.onmessage = function (event) {
        console.log(event)
    }

    //连接关闭时触发
    ws.onclose = function (event) {
        console.log("连接关闭")
    }
</script>
</html>

打开前端页面,打开控制台看见:

 此时连接已成功!

三、实现单聊

在连接成功的基础上实现,

实现思路,本次实现通过ConcurrentHashMap来存储聊天用户,线上环境时可以使用redis等来进一步拓展。

创建消息枚举类:用于明确消息的类型

package com.dragonwu.server.domain.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * @author DragonWu
 * @since 2023-01-06 11:50
 **/
@Getter
@AllArgsConstructor
public enum MessageType {
    //私聊
    PRIVATE(1),
    //群聊
    GROUP(2),
    //不支持的类型
    ERROR(-1);

    private final Integer type;

    public static MessageType match(Integer type) {
        for (MessageType value : MessageType.values()) {
            if (value.getType().equals(type)) {
                return value;
            }
        }
        return ERROR;
    }
}

创建消息实体类:接收到的消息为json字符串,转换为该实体类方便后续操作

package com.dragonwu.server.domain.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author DragonWu
 * @since 2023-01-06 11:48
 **/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ChatMessage extends Command {

    //消息类型
    private Integer type;

    //目标接收对象
    private String target;

    //消息内容
    private String content;
}

将之前的WebsocketHandler主要处理器再添加一个分支,用于处理私聊

package com.dragonwu.server.handler;

import com.alibaba.fastjson2.JSON;
import com.dragonwu.server.domain.Result;
import com.dragonwu.server.domain.enums.CommandType;
import com.dragonwu.server.domain.pojo.Command;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;

/**
 * @author DragonWu
 * @since 2023-01-05 14:21
 **/
public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {//通过TextWebSocketFrame作为消息承载体

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {
        try {
            //将json文本解析为指令对象
            Command command = JSON.parseObject(textWebSocketFrame.text(), Command.class);
            switch (CommandType.match(command.getCode())) {
                case CONNECTION:
                    ConnectHandler.execute(channelHandlerContext, command);
                    break;
                case CHAT:
                    ChatHandler.execute(channelHandlerContext, textWebSocketFrame);
                    break;
                default:
                    channelHandlerContext.channel().writeAndFlush(Result.fail("不支持CODE"));
            }
        } catch (Exception e) {
            channelHandlerContext.channel().writeAndFlush(Result.fail("错误消息:" + e.getMessage()));
        }

    }
}

前端修改为如下:前端通过ws.send()发送消息给Netty服务器,再由服务器转发给对应客户端。

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>websocket</title>
</head>
<body>
<div>
    <div>
        <input id="send-msg-box" type="text">
        <input id="who" type="text" placeholder="who">
        <button id="send-button" onclick="sendMsg()">发送</button>
    </div>
</div>
</body>
<script>
    function getSearchString(key, Url) {
        var str = Url;
        str = str.substring(1, str.length); // 获取URL中?之后的字符(去掉第一位的问号)
        // 以&分隔字符串,获得类似name=xiaoli这样的元素数组
        var arr = str.split("&");
        var obj = new Object();

        // 将每一个数组元素以=分隔并赋给obj对象
        for (var i = 0; i < arr.length; i++) {
            var tmp_arr = arr[i].split("=");
            obj[decodeURIComponent(tmp_arr[0])] = decodeURIComponent(tmp_arr[1]);
        }
        return obj[key];
    }

    //创建一个websocket对象
    let ws = new WebSocket("ws://localhost:8888")
    let search = window.location.search
    let nickname = getSearchString("user", search)

    //连接成功时触发
    ws.onopen = function (event) {
        console.log("连接成功");
        ws.send("{\"nickname\":\"" + nickname + "\",\"code\":\"1001\"}");
    }

    //连接失败时触发
    ws.onerror = function (event) {
        console.log("连接失败");
    }

    //接收消息时触发
    ws.onmessage = function (event) {
        console.log(event)
    }

    //连接关闭时触发
    ws.onclose = function (event) {
        console.log("连接关闭")
    }

    function sendMsg() {
        let msg = document.getElementById("send-msg-box").value;
        let target = document.getElementById("who").value;
        document.getElementById("send-msg-box").value = "";
        document.getElementById("who").value = "";
        ws.send('{"nickname":"' + nickname + '","code":"1002","type":"1","target":"' + target + '","content":"' + msg + '"}')
    }
</script>
</html>

打开一个前端页面:记得写上user是谁,实际业务中user应该为登录的用户,

本次模拟两个用户间的聊天:

先上线John:

 再上线张三:

可以看到服务器转发的在线用户列表,该消息是这个代码段转发的:

 

 下面是聊天测试:John发送消息

 张三控制台接收到消息:

如果张三指定的人是不存在的:John发送

 

 服务器会转发出错误提示:

这部的实现代码是这里:

 私聊功能基本结构就是这样实现,可结合业务就行拓展与修改。

四、群聊功能

 群聊功能在私聊的基础上进行实现:

添加一个JoinGroup功能,让对应的用户模拟加入群聊:

package com.dragonwu.server.handler;

import com.dragonwu.server.IMServer;
import com.dragonwu.server.domain.Result;
import io.netty.channel.ChannelHandlerContext;

/**
 * @author DragonWu
 * @since 2023-01-06 13:24
 **/
public class JoinGroupHandler {
    public static void execute(ChannelHandlerContext channelHandlerContext){
        //将Channel添加到ChannelGroup
        IMServer.GROUP.add(channelHandlerContext.channel());
        channelHandlerContext.channel().writeAndFlush(Result.success("加入系统默认群聊成功~"));
    }
}

主处理器添加分支:

package com.dragonwu.server.handler;

import com.alibaba.fastjson2.JSON;
import com.dragonwu.server.domain.Result;
import com.dragonwu.server.domain.enums.CommandType;
import com.dragonwu.server.domain.pojo.Command;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;

/**
 * @author DragonWu
 * @since 2023-01-05 14:21
 **/
public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {//通过TextWebSocketFrame作为消息承载体

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {
        try {
            //将json文本解析为指令对象
            Command command = JSON.parseObject(textWebSocketFrame.text(), Command.class);
            switch (CommandType.match(command.getCode())) {
                case CONNECTION:
                    ConnectHandler.execute(channelHandlerContext, command);
                    break;
                case CHAT:
                    ChatHandler.execute(channelHandlerContext, textWebSocketFrame);
                    break;
                case JOIN_GROUP:
                    JoinGroupHandler.execute(channelHandlerContext);
                    break;
                default:
                    channelHandlerContext.channel().writeAndFlush(Result.fail("不支持CODE"));
            }
        } catch (Exception e) {
            channelHandlerContext.channel().writeAndFlush(Result.fail("错误消息:" + e.getMessage()));
        }

    }
}

聊天处理器添加分支:

package com.dragonwu.server.handler;

import com.alibaba.fastjson2.JSON;
import com.dragonwu.server.IMServer;
import com.dragonwu.server.domain.Result;
import com.dragonwu.server.domain.enums.MessageType;
import com.dragonwu.server.domain.pojo.ChatMessage;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.internal.StringUtil;

/**
 * @author DragonWu
 * @since 2023-01-06 11:52
 **/
public class ChatHandler {
    public static void execute(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) {
        try {
            ChatMessage chat = JSON.parseObject(textWebSocketFrame.text(), ChatMessage.class);
            switch (MessageType.match(chat.getType())) {
                case PRIVATE:
                    if (StringUtil.isNullOrEmpty(chat.getTarget())) {
                        channelHandlerContext.channel().writeAndFlush(Result.fail("消息发送失败,消息发送前请指定接收对象"));
                        return;
                    }
                    Channel channel = IMServer.USERS.get(chat.getTarget());
                    if (null == channel || !channel.isActive()) {
                        channelHandlerContext.channel().writeAndFlush(Result.fail("消息发送失败,对方" + chat.getTarget() + "不在线"));
                    } else {
                        channel.writeAndFlush(Result.success("私聊消息(" + chat.getNickname() + ")", chat.getContent()));
                    }
                    break;
                case GROUP:
                    IMServer.GROUP.writeAndFlush(Result.success("群聊消息(" + chat.getNickname() + ")", chat.getContent()));
                    break;
                default:
                    channelHandlerContext.channel().writeAndFlush(Result.fail("不支持消息类型"));
            }
        } catch (Exception e) {

        }
    }
}

前端修改如下:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>websocket</title>
</head>
<body>
<div>
    <div>
        <input id="send-msg-box" type="text">
        <input id="who" type="text" placeholder="who">
        <button id="send-button" onclick="sendMsg()">发送</button>
    </div>
</div>
</body>
<script>
    function getSearchString(key, Url) {
        var str = Url;
        str = str.substring(1, str.length); // 获取URL中?之后的字符(去掉第一位的问号)
        // 以&分隔字符串,获得类似name=xiaoli这样的元素数组
        var arr = str.split("&");
        var obj = new Object();

        // 将每一个数组元素以=分隔并赋给obj对象
        for (var i = 0; i < arr.length; i++) {
            var tmp_arr = arr[i].split("=");
            obj[decodeURIComponent(tmp_arr[0])] = decodeURIComponent(tmp_arr[1]);
        }
        return obj[key];
    }

    //创建一个websocket对象
    let ws = new WebSocket("ws://localhost:8888")
    let search = window.location.search
    let nickname = getSearchString("user", search)

    //连接成功时触发
    ws.onopen = function (event) {
        console.log("连接成功");
        ws.send("{\"nickname\":\"" + nickname + "\",\"code\":\"1001\"}");
        joinGroup();
    }

    //连接失败时触发
    ws.onerror = function (event) {
        console.log("连接失败");
    }

    //接收消息时触发
    ws.onmessage = function (event) {
        console.log(event)
    }

    //连接关闭时触发
    ws.onclose = function (event) {
        console.log("连接关闭")
    }

    function sendMsg() {
        let msg = document.getElementById("send-msg-box").value;
        let target = document.getElementById("who").value;
        document.getElementById("send-msg-box").value = "";
        document.getElementById("who").value = "";
        if(target==""){
            ws.send('{"nickname":"' + nickname + '","code":"1002","type":"2","content":"' + msg + '"}')
        }else{
            ws.send('{"nickname":"' + nickname + '","code":"1002","type":"1","target":"' + target + '","content":"' + msg + '"}')
        }
    }

    function joinGroup() {
        ws.send('{"nickname":"' + nickname + '","code":"1003"}')
    }
</script>
</html>

当who为空时,直接群发:

John发送群消息后可以看到:

 群里的三个人都收到了消息!

五、案例代码

Websocket案例代码: Websocket案例代码 - Gitee.com

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

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

相关文章

img的应用

我的目的是&#xff0c;因为图片足够的大&#xff0c;我想让它在一个小盒子里居中显示&#xff0c;所以我这样做了&#xff1a;<style>.text{width: 375px;height: 100px;} </style> <body><div class"text"><img src"./img/5.png&q…

企业为什么要利用数据中台进行数字化转型?_光点科技

近两年“数字化”已经悄悄的替代了“信息化”。那么什么是“企业的数字化转型”&#xff1f;数字化转型是企业战略层面的概念&#xff0c;它并不是追求眼前效益的机灵战术&#xff0c;其本质&#xff0c;是用数字化技术对业务的重构、流程的重构和组织的重构&#xff0c;目的是…

云呐|什么是固定资产?什么是流动资产

什么是固定资产&#xff1f;什么是流动资产&#xff0c;  1、固定资产  属于产品生产过程中用来改变或者影响劳动对象的劳动资料&#xff0c;是固定资本的实物形态固定资产在生产过程中可以长期发挥作用&#xff0c;长期保持原有的实物形态&#xff0c;但其价值则随着企业生…

自己centos7系统制作iso镜像,并新建虚拟机

一、自己centos7系统制作iso镜像 1. 前置工作 将系统安全配置 SELINUX 改为 disabled&#xff0c;否则制作好的镜像无法登陆&#xff01;&#xff01;&#xff01; vim /etc/selinux/config # 将其从 enforcing 改为 disabled SELINUXdisabled2.安装 mondo rescue cd /etc…

正则表达式的使用

什么是正则表达式 正则表达式&#xff0c;又称规则表达式,&#xff08;Regular Expression&#xff0c;在代码中常简写为regex、regexp或RE&#xff09;&#xff0c;是一种文本模式&#xff0c;包括普通字符&#xff08;例如&#xff0c;a 到 z 之间的字母&#xff09;和特殊字…

使用VC时一些容易犯的错误

本文迁移自本人网易博客&#xff0c;写于2011年1月13日&#xff0c;使用VC时一些容易犯的错误 - lysygyy的日志 - 网易博客 (163.com)1、在调用其他类中的函数时&#xff0c;需要在当前类中声明一个类对象&#xff0c;但是调用的时候&#xff0c;编译会出错。出现很多符号&…

2023年SQL大厂高频实战面试题(详细解析)

大家好&#xff0c;我是宁一。 已经连续四个周没有休息了&#xff0c;最近主业、副业都是忙碌的巅峰期&#xff0c;晚上11点下班回家&#xff0c;再写课写到凌晨两点。 连续一个多月连轴转&#xff0c;每天最大的愿望&#xff0c;就是睡足觉。 这一阶段终于忙完了~继续来更新SQ…

LongAdder源码【原创+图解+视频讲解】

目录 AtomicLong用法 源码分析 问题 解决 LongAdder用法 高并发下效率测试 原理 源码 add(long x) Striped64的longAccumulate 伪共享 总结 视频讲解&#xff1a; AtomicLong用法 public static void main(String[] args) {AtomicLong i new AtomicLong(0); ​S…

SQL UPDATE 语句

UPDATE 语句用于更新表中的记录。 SQL UPDATE 语句 UPDATE 语句用于更新表中已存在的记录。 SQL UPDATE 语法 UPDATE table_name SET column1 value1, column2 value2, ... WHERE condition; 参数说明&#xff1a; table_name&#xff1a;要修改的表名称。column1, colu…

C++:std::thread:线程用法

1&#xff1a;std::thread的基本用法 最简单的 std::thread用法如下&#xff0c;调用 thread将立即同时开始执行这个新建立的线程&#xff0c;新线程的任务执行完毕之后&#xff0c; main()的主线程也会继续执行。 #include<iostream> #include<thread> #include&l…

一致性hash算法和hash算法的区别和使用场景

1、hash算法使用场景 一般情况下hash算法主要用于&#xff1a;负载均衡&#xff08;nginx 请求转发&#xff0c;scg路由等&#xff09;&#xff0c;分布式缓存分区&#xff0c;数据库分库分表&#xff08;mycat&#xff0c;shardingSphere&#xff09;等。 2、hash算法大致实…

网络编程套接字——udp网络编程

目录 一、预备知识 1.端口 2.TCP协议和UDP协议 3.socket编程接口 ①socket 常见API ②sockaddr结构 二、网络编程 1.UDP网络程序 1.1服务器 ①打印 ②socket​编辑 ③bind ④recvfrom ​编辑 1.2客户端 ①sendto 1.3提升通信的花样性 ①将字符串返还 …

Individual Tree Segmentation Method Based on Mobile Backpack LiDAR Point Clouds

Abstract 单棵树 (IT) 分割对于森林管理、支持森林清查、生物量监测或树木竞争分析至关重要。光探测和测距 (LiDAR) 是这方面的一项突出技术&#xff0c;优于竞争技术。航空激光扫描 (ALS) 经常用于森林记录&#xff0c;在树顶表面显示良好的点密度。尽管使用多回波 ALS 可以收…

【虹科云展厅专题】虹科赋能汽车智能化云展厅——车载以太网/TSN专题

虹科2023年开年福利来了&#xff01; 聚焦前沿技术&#xff0c;【虹科赋能汽车智能化云展厅】正式上线&#xff0c;本次云展厅围绕“汽车以太网/TSN、汽车总线、智能网联、电子测试与验证、自动驾驶”等核心话题&#xff0c;为您带来如临展会现场般的讲演与介绍&#xff0c;更…

Unity入门基础语法

物体的坐标 transform.position 世界坐标 transform.localPosition 相对坐标 设置物体的坐标&#xff1a; this.transform.localPosition new Vector3(1.5f, 0, 2.0f); 帧更新 Update()&#xff0c;称为帧更新 此方法会被游戏引擎定时调用&#xff0c;已更新游戏的状态 …

基于Java+SpringBoot+vue+element实现物流管理系统

基于JavaSpringBootvueelement实现物流管理系统 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系…

SQL Studio:一款纯Web化SQL开发工具,关键是免安装还免费!

经常使用SQL工具的开发者对Navicat一定都不陌生。这款软件作为一款全球化的多数据库管理工具&#xff0c;这些年逐步得到全国各地SQLer&#xff08;SQL开发者&#xff09;的关注。 与其他很多外来的软件产品一样&#xff0c;由于价格原因&#xff0c;很多SQLer感觉不太适合适应…

聊聊微服务架构中的用户认证方案

传统的用户认证方案 我们直奔主题&#xff0c;什么是用户认证呢&#xff1f;对于大多数与用户相关的操作&#xff0c;软件系统首先要确认用户的身份&#xff0c;因此会提供一个用户登录功能。用户输入用户名、密码等信息&#xff0c;后台系统对其进行校验的操作就是用户认证。…

S7-1200与三菱FX5U系列PLC通过简单CPU通信功能实现以太网通信的具体方法

S7-1200与三菱FX5U系列PLC通过简单CPU通信功能实现以太网通信的具体方法 前提条件: 西门子S7-1200一侧需要在防护安全中选择连接机制,选择连接机制后在将这里面的“ 允许来自远程对象的PUT/GET通讯访问”这个选项勾选即可。 另外要注意,被访问的DB块要设置为非优化的块访问…

Go第 9 章:map

Go第 9 章&#xff1a;map 9.1 map 的基本介绍 map 是 key-value 数据结构&#xff0c;又称为字段或者关联数组。类似其它编程语言的集合&#xff0c; 在编程中是经常使用到 9.2 map 的声明 9.2.1基本语法 var map 变量名 map[keytype]valuetype 9.2.2map 声明的举例 m…