简单的UDP网络程序

news2025/1/10 3:19:27

目录

准备工作

makefile

udpServer.hpp

udpServer.cc

细节1


服务端部署

创建套接字

接口认识1

socket

协议家族

绑定套接字

认识接口2

bind

sockaddr_in结构体类型

细节2

bzero

inet_addr

服务器启动(初启动)

udpServer.hpp

udpServer.cc

细节3

本地回环通信

认识指令1

netstat

细节4

代码整改

整改后代码

udpServer.hpp

udpServer.cc

认识接口3

recvfrom

参数介绍

inet_ntoa

start启动


客户端部署

认识接口4

sendto

同一台云服务器上

不同的服务器上

通信和业务逻辑解耦


全部代码

udpServer.hpp

udpServer.cc

udpClient.hpp

udpClient.cc

makefile


准备工作

这些先在Xshell上创建,后续直接使用VScode来进行编码

makefile

udpServer.hpp

#pragma once

#include <iostream>
#include <string>

namespace Server
{
    class udpServer
    {
    public:
        udpServer()
        {
        }
        void initServer()   //初始化
        {
        }
        void start()        //启动
        {
        }
        ~udpServer()        //析构
        {
        }
    };

}

udpServer.cc

#include "udpServer.hpp"
#include <memory>


using namespace std;
using namespace Server;

int main()
{
    std::unique_ptr<udpServer> usvr(new udpServer());

    usvr->initServer();
    usvr->start();


    return 0;
}

细节1

服务端部署

创建套接字

接口认识1

socket

参数

返回值

读写网络就像读写文件一样

初始化代码写到构造里面了,后面修改

协议家族

绑定套接字

认识接口2

bind

 这里的填充涉及到内存对齐方面,知道就好

sockaddr_in结构体类型

对类型进行了很多层的封装

细节2

从上面看到,我们可以得到一个问题:为什么库里面使用的是整型的ip地址,而我们是用string的ip地址的呢?

点分十进制和整数风格互转

bzero

往一段空间中填 0 

inet_addr

记得包含头文件

getopt

附:当我们想要做命令行解析的时候是可以用下面这个接口的,这里我们不使用(参数太少了,没必要使用)

 

服务器启动(初启动)

至此服务器已经可以正常启动

 

udpServer.hpp

#pragma once

#include <iostream>
#include <string>
#include <strings.h>
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

namespace Server
{
    using namespace std;
    static const string defaultIp = "0.0.0.0";  //TODO

    enum {USAGE_ERR = 1, SOCKET_ERR, BIND_ERR}; //1.命令行输入错误 2.创建套接字错误 3.绑定端口号错误
    class udpServer
    {
    public:
        udpServer(const uint16_t &port, const string& ip = defaultIp): _port(port),_ip(ip),_sockfd(-1)
        {
            //注意这里是直接写在构造里面的,是写错地方了,虽然运行是没有错的,由于修改图片太麻烦,下面统一进行了修改
            //1.创建套接字
            _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
            if(_sockfd == -1)
            {
                cerr << "socket error: " << errno << " : " << strerror(errno) << endl;
                exit(SOCKET_ERR);   //创建套接字失败直接终止进程
            }
            //2.绑定套接字(port, ip)
            struct sockaddr_in local;   //这里是定义了一个变量,在栈上,而且是用户层,还没有bind之前都是没有产生联系
            bzero(&local, sizeof(local));   //先填 0 再修正
            //注意这下面几个名字是拼接出来的,就是那个##拼接而来的
            local.sin_family = AF_INET; //这里设置与套接字的AF_INET设置意义是不一样的,socket是创建一个网络通信的套接字,在这里是填充一个sockaddr_in的结构体用来网络通信
            local.sin_port = htons(_port);//你如果给别人发信息,你的port和ip要不要发送给对方? 答案是要的 
            local.sin_addr.s_addr = inet_addr(_ip.c_str()); //1. string->unit32_t  2. htonl(); -> inet_addr 
            int n = bind(_sockfd, (struct sockaddr*)&local, sizeof(local));
            if(n == -1)
            {
                cerr << "bin error: " << errno << " : " << strerror(errno) << endl;
                exit(BIND_ERR);
            }
            //UDP Server 的预备工作完成

        }
        void initServer() // 初始化
        {
        }
        void start() // 启动
        {
            //服务器的本质其实就是一个死循环,
            for(;;)
            {
                sleep(1);
            }
        }
        ~udpServer() // 析构
        {
        }

    private:
        uint16_t _port;
        string _ip; // TODO
        int _sockfd;
    };
}

udpServer.cc

#include "udpServer.hpp"
#include <memory>

using namespace std;
using namespace Server;

static void Usage(string proc)
{
    cout << "Usage:\n\t" << proc << " local_ip local_port\n\n"; //命令提示符
}

int main(int argc, char *argv[])
{
    if(argc != 3)   //这里我们只想要传递两个参数,所以当argc不是3的时候就直接报错退出就行了,注意文件名运行的那个指令也会算进去所以argc +1
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port = atoi(argv[2]);  //使用atoi强转,因为argv里面放置的都是字符串,类型需要转换
    string ip = argv[1];

    std::unique_ptr<udpServer> usvr(new udpServer(port, ip));

    usvr->initServer();
    usvr->start();


    return 0;
}

认识接口2

细节3

刚开始测试的地址是直接使用这一个

 

本地回环通信

127.0.0.1

 

认识指令1

netstat

查看网络的接口 netstat

 

 

细节4

 

假如绑定其他的公网IP地址需要注意

 

代码整改

所以一个服务器真实情况下是要接受任意ip发过来的通信,因此我们修改ip的,不需要传ip号了

 

整改后代码

udpServer.hpp

#pragma once

#include <iostream>
#include <string>
#include <strings.h>
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

namespace Server
{
    using namespace std;
    static const string defaultIp = "0.0.0.0"; // 直接使用这个缺省值

    enum
    {
        USAGE_ERR = 1,
        SOCKET_ERR,
        BIND_ERR
    }; // 1.命令行输入错误 2.创建套接字错误 3.绑定端口号错误
    class udpServer
    {
    public:
        udpServer(const uint16_t &port, const string &ip = defaultIp) : _port(port), _ip(ip), _sockfd(-1)
        {}
        void initServer() // 初始化
        {
            // 1.创建套接字
            _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
            if (_sockfd == -1)
            {
                cerr << "socket error: " << errno << " : " << strerror(errno) << endl;
                exit(SOCKET_ERR); // 创建套接字失败直接终止进程
            }
            // 2.绑定套接字(port, ip)
            struct sockaddr_in local;     // 这里是定义了一个变量,在栈上,而且是用户层,还没有bind之前都是没有产生联系
            bzero(&local, sizeof(local)); // 先填 0 再修正
            // 注意这下面几个名字是拼接出来的,就是那个##拼接而来的
            local.sin_family = AF_INET;                     // 这里设置与套接字的AF_INET设置意义是不一样的,socket是创建一个网络通信的套接字,在这里是填充一个sockaddr_in的结构体用来网络通信
            local.sin_port = htons(_port);                  // 你如果给别人发信息,你的port和ip要不要发送给对方? 答案是要的
            local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 1. string->unit32_t  2. htonl(); -> inet_addr
            // local.sin_addr.s_addr = htonl(INADDR_ANY);  //可以主机转网络,不够也可以不处理,直接赋值也行
            int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
            if (n == -1)
            {
                cerr << "bin error: " << errno << " : " << strerror(errno) << endl;
                exit(BIND_ERR);
            }
            // UDP Server 的预备工作完成
        }
        void start() // 启动
        {
            // 服务器的本质其实就是一个死循环,
            for (;;)
            {
                sleep(1);
            }
        }
        ~udpServer() // 析构
        {
        }

    private:
        uint16_t _port;
        // 实际上,一款网络服务器,不建议指明一个IP,因为一个服务器可以能有多个ip,万一用户使用其他的ip地址来访问该端口号(这里是8080,就收不到了),这也是我们为什么使用0.0.0.0的IP缺省值
        string _ip;
        int _sockfd;
    };
}

udpServer.cc

#include "udpServer.hpp"
#include <memory>

using namespace std;
using namespace Server;

static void Usage(string proc)
{
    cout << "Usage:\n\t" << proc << " local_port\n\n"; //命令提示符
}

int main(int argc, char *argv[])
{
    if(argc != 2)   //这里我们只想要传递两个参数,所以当argc不是3的时候就直接报错退出就行了,注意文件名运行的那个指令也会算进去所以argc +1
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port = atoi(argv[1]);  //使用atoi强转,因为argv里面放置的都是字符串,类型需要转换
    //string ip = argv[1];

    std::unique_ptr<udpServer> usvr(new udpServer(port));

    usvr->initServer();
    usvr->start();


    return 0;
}

 

        注意这里是服务端是不需要IP,可以接受任意IP地址发来的请求,但是客户端是需要的,这一点后面再细谈,所以并不会造成淘宝的请求跑到京东去了

        这里为什么使用8080的因为服务器可以有很多端口号,当服务器收到了大量的数据,并不是全部都是由一个端口号来进行处理的,也可能是8081之类的端口号,这时候的端口号是没有意义的,后序会详谈,其实不同的端口号是有指定的绑定的不能任意绑定,这是因为只有我自己使用

        且无论是UDP还是TCP都是采取这样的形式,接受任意IP的数据,通过端口号来确定谁是谁处理

认识接口3

recvfrom

读取数据

 

 

参数介绍

socket_t

 

inet_ntoa

        将一个网络字节序的IP地址(也就是结构体in_addr类型变量)转化为点分十进制的IP地址(字符串)

 

 

start启动

void start() // 启动
        {
            // 服务器的本质其实就是一个死循环
            char buffer[gnum]; // 定义一个数组来充当缓冲区
            for (;;)
            {
                // 读取数据
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer); // 必须设置成这个结构体的大小,当作为输入时,告诉recvfrom的长度的多少
                ssize_t s = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
                // 关系两件事情
                // 1.数据是什么? 2. 谁发的?
                if (s > 0)
                {
                    buffer[s] = 0;
                    // 因为是从网络上读取的,所以一定要转,可以使用接口
                    string clientip = inet_ntoa(peer.sin_addr); // 1.网络序列 2.整数 -> 点分十进制的ip
                    uint16_t clientport = ntohs(peer.sin_port);
                    string message = buffer;

                    cout << clientip << "[" << clientport << "]# " << message << endl;
                }
            }
        }

至此服务器端基本完成,停下来处理客户端

客户端部署

认识接口4

sendto

sendto告诉客户端要发给谁

 

0~1023在云服务器上已经被绑定了 

同一台云服务器上

 

不同的服务器上

现在无法跨主机发送消息,权限问题,后续解决

sz:下载到本地

rz:上传到服务器

chmod:修改权限

 

至于如何打开端口后续文章介绍 -- 未完持续

通信和业务逻辑解耦

        我们可以添加function来对业务逻辑进行解耦操作,融入下面代码

        function对server通信和业务逻辑解耦!

全部代码

udpServer.hpp

 

#pragma once

#include <iostream>
#include <string>
#include <strings.h>
#include <cerrno>
#include <functional>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

namespace Server
{
    using namespace std;
    static const string defaultIp = "0.0.0.0"; // 直接使用这个缺省值
    static const int gnum = 1024;
    enum
    {
        USAGE_ERR = 1,
        SOCKET_ERR,
        BIND_ERR
    }; // 1.命令行输入错误 2.创建套接字错误 3.绑定端口号错误

    typedef function<void(string, uint16_t, string)> func_t;

    class udpServer
    {
    public:
        udpServer(const func_t &cd, const uint16_t &port, const string &ip = defaultIp)
            : _callback(cd), _port(port), _ip(ip), _sockfd(-1)
        {
        }
        void initServer() // 初始化
        {
            // 1.创建套接字
            _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
            if (_sockfd == -1)
            {
                cerr << "socket error: " << errno << " : " << strerror(errno) << endl;
                exit(SOCKET_ERR); // 创建套接字失败直接终止进程
            }
            cout << "udpServer success: "
                 << " : " << _sockfd << endl;

            // 2.绑定套接字(port, ip)
            // 未来服务器要明确的port, 不能随意改变 -- 变了别人就找不到了
            struct sockaddr_in local;     // 这里是定义了一个变量,在栈上,而且是用户层,还没有bind之前都是没有产生联系
            bzero(&local, sizeof(local)); // 先填 0 再修正
            // 注意这下面几个名字是拼接出来的,就是那个##拼接而来的
            local.sin_family = AF_INET;                     // 这里设置与套接字的AF_INET设置意义是不一样的,socket是创建一个网络通信的套接字,在这里是填充一个sockaddr_in的结构体用来网络通信
            local.sin_port = htons(_port);                  // 你如果给别人发信息,你的port和ip要不要发送给对方? 答案是要的
            local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 1. string->unit32_t  2. htonl(); -> inet_addr
            // local.sin_addr.s_addr = htonl(INADDR_ANY);  //可以主机转网络,不够也可以不处理,直接赋值也行
            int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
            if (n == -1)
            {
                cerr << "bin error: " << errno << " : " << strerror(errno) << endl;
                exit(BIND_ERR);
            }
            // UDP Server 的预备工作完成
        }
        void start() // 启动
        {
            // 服务器的本质其实就是一个死循环
            char buffer[gnum]; // 定义一个数组来充当缓冲区
            for (;;)
            {
                // 读取数据
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer); // 必须设置成这个结构体的大小,当作为输入时,告诉recvfrom的长度的多少
                ssize_t s = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
                // 关系两件事情
                // 1.数据是什么? 2. 谁发的?
                if (s > 0)
                {
                    buffer[s] = 0;
                    // 因为是从网络上读取的,所以一定要转,可以使用接口
                    // inet_ntoa 将一个网络字节序的IP地址(也就是结构体in_addr类型变量)转化为点分十进制的IP地址(字符串)
                    string clientip = inet_ntoa(peer.sin_addr); // 1.网络序列 2.整数 -> 点分十进制的ip
                    uint16_t clientport = ntohs(peer.sin_port);
                    string message = buffer;

                    cout << clientip << "[" << clientport << "]# " << message << endl;

                    //我们只把数据读上来就完了吗? 我们要对数据进行处理 -- 所以我们用回调函数的方式来解决
                    _callback(clientip, clientport, message);
                }
            }
        }
        ~udpServer() // 析构
        {
        }

    private:
        uint16_t _port;
        // 实际上,一款网络服务器,不建议指明一个IP,因为一个服务器可以能有多个ip,万一用户使用其他的ip地址来访问该端口号(这里是8080,就收不到了),这也是我们为什么使用0.0.0.0的IP缺省值
        string _ip;
        int _sockfd;
        func_t _callback; // 回调函数,用以处理数据
    };
}

udpServer.cc

#include "udpServer.hpp"
#include <memory>

using namespace std;
using namespace Server;

static void Usage(string proc)
{
    cout << "Usage:\n\t" << proc << " local_port\n\n"; //命令提示符
}


void handlerMessage(string clientip, uint16_t clientport, string message)
{
    //这里就可以对message进行特定的业务处理,而不关心message怎么来的 --- 这就是server通信和业务逻辑解耦!
}

int main(int argc, char *argv[])
{
    if(argc != 2)   //这里我们只想要传递两个参数,所以当argc不是3的时候就直接报错退出就行了,注意文件名运行的那个指令也会算进去所以argc +1
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port = atoi(argv[1]);  //使用atoi强转,因为argv里面放置的都是字符串,类型需要转换
    //string ip = argv[1];

    std::unique_ptr<udpServer> usvr(new udpServer(handlerMessage, port));

    usvr->initServer();
    usvr->start();


    return 0;
}

udpClient.hpp

#pragma once

#include <iostream>
#include <string>
#include <strings.h>
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

namespace Client
{
    using namespace std;
    class udpClient
    {
    public:
        udpClient(const string &serverip, const uint16_t &serverport)
            : _serverip(serverip), _serverprot(serverport), _sockfd(-1), _quit(false)
        {
        }
        void initClient()
        {
            // 1. 创建socket
            _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
            if (_sockfd == -1)
            {
                cerr << "socket error: " << errno << " : " << strerror(errno) << endl;
                exit(1); // 创建套接字失败直接终止进程
            }
            cout << "socket success: " << " : " << _sockfd << endl;   //成功打印出来

            // 2. client要不要bind(必须要!)因为bind就是和系统、网络产生联系. client要不要显示的bind -> 需不需要程序员bind? 名字不重要,重要的是唯一性的,和服务端是不一样的
            // 就像宿舍号是几不重要,有就行了。一个端口号只能被一个客户端绑定,就像是服务端是明星,客户端是民众,民众名字不重要
            // 服务端是具体的一家公司,比如抖音是字节的,就像一个手机有很多app比如抖音,快手这样的客户端,不能让它们固定bind端口号,万一其他公司也用了用一个端口就冲突其他不来了
            // 写服务器的是一家公司,写client是无数家公司 -- 有OS自动形成端口进行bind!不需要自己操作,包括ip地址也不需要,OS自己会处理,当然也可以自己写
            // 那么OS在什么时候,如何bind
        }
        void run()
        {
            struct sockaddr_in server;
            memset(&server, 0, sizeof(server));
            server.sin_family = AF_INET;
            server.sin_addr.s_addr = inet_addr(_serverip.c_str());
            server.sin_port = htons(_serverprot);

            string messages;
            while (!_quit)
            {
                cout << "Please Enter# ";
                cin >> messages;

                sendto(_sockfd, messages.c_str(), sizeof(messages), 0, (struct sockaddr *)&server, sizeof(server));
            }
        }
        ~udpClient()
        {
        }

    private:
        int _sockfd;
        string _serverip;
        uint16_t _serverprot;
        bool _quit;
    };

}

udpClient.cc

#include "udpClient.hpp"
#include <memory>

using namespace Client;

static void Usage(string proc)
{
    cout << "\nUsage:\n\t" << proc << " server_ip server_port\n\n"; //命令提示符
}

// ./udpClient server_ip server_port    第一个是运行方式 要知道要发送的服务端的ip地址 和 端口号
int main(int argc, char *argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    string serverip = argv[1];
    uint16_t serverport = atoi(argv[2]);


    unique_ptr<udpClient> ucli(new udpClient(serverip, serverport));

    ucli->initClient();
    ucli->run();

    return 0;
}

makefile

cc=g++

.PHONY:all
all:udpClient udpServer


udpClient:udpClient.cc
	$(cc) -o $@ $^ -std=c++11 
udpServer:udpServer.cc
	$(cc) -o $@ $^ -std=c++11

.PHONY:clean
clean:
	rm -f udpClient udpServer

至于如何打开端口后续文章介绍 -- 未完持续中……

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

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

相关文章

跑通NeRF-SLAM代码记录

前言 Install 原文章github链接 下载代码 git clone https://github.com/ToniRV/NeRF-SLAM.git --recurse-submodules git submodule update --init --recursive因为有相关依赖&#xff0c;所以尽量使用命令下载代码。 2. 新建nerf-slam环境&#xff0c;github上也没提到p…

从sftp下载大文件到浏览器

从sftp下载大文件到浏览器 问题方案相关依赖包相关代码片段&#xff08;后端&#xff09;文件信息缓存工具类-FileChunkCache文件信息对象-FileDetailsftp传输进度监控-FileProgressMonitor切片工具类-ChunkService文件下载服务-AsyncDownloadService 问题 近期遇到直接使用sf…

Dubbo与SpringBoot整合

1.注意starter版本适配 2.服务提供者 创建Maven项目 boot-user-service-provider 服务提供者 2.1.通用模块依旧照用 2.2.POM <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</a…

如何在华为OD机试中获得满分?Java实现【IPv4地址转换成整数】一文详解!

✅创作者:陈书予 🎉个人主页:陈书予的个人主页 🍁陈书予的个人社区,欢迎你的加入: 陈书予的社区 🌟专栏地址: Java华为OD机试真题(2022&2023) 文章目录 1. 题目描述2. 输入描述3. 输出描述4. Java算法源码5. 测试6.解题思路1. 题目描述 存在一种虚拟 IPv4<

本地创建的项目托管到git

本地创建的项目托管到git 这里的情况是本地先通过命令在电脑上指定文件夹创建好项目后&#xff0c;需要托管到git上&#xff0c;这里以gitee为例 打开gitee&#xff0c;登录滑动到右上方&#xff0b;&#xff0c;点击新建仓库&#xff0c;跳转到新建仓库页面 填写仓库信息&am…

Ubuntu18.04+RTX3060+TensorFlow2.12.0(GPU版)+Cuda11.1+CuDNN8.6.0安装

前情提要 可以跳过 我在Ubuntu18.04上安装了pytorch的相关环境&#xff0c;配置如图。 Ubuntu18.04RTX3060显卡配置pytorch、cuda、cudnn和miniconda_Toblerone_Wind的博客-CSDN博客之前已经安装成功了&#xff0c;也发了篇博客梳理了整套流程如下。ubuntu18.04安装pytorch、c…

回归预测 | MATLAB实现实现FOA-BP果蝇算法优化BP神经网络多变量输入回归预测模型

回归预测 | MATLAB实现实现FOA-BP果蝇算法优化BP神经网络多变量输入回归预测模型 目录 回归预测 | MATLAB实现实现FOA-BP果蝇算法优化BP神经网络多变量输入回归预测模型效果一览基本介绍程序设计参考资料 效果一览 基本介绍 果蝇算法(FOA)优化BP神经网络回归预测,FOA-BP回归预测…

springboot3.0集成nacos2.2.1(一)

本章节内容是没有开启nacos校验方式进行接入 集成环境&#xff1a; java版本&#xff1a;JDK17 springboot版本&#xff1a;3.0.2 创建spring项目&#xff0c;我这里用到的是spring-cloud全家桶 首先是jar包依赖&#xff1a; <properties><maven.compiler.so…

HTB-Forest(PowerView.ps1使用、嵌套组解析、了解帐户操作员组)

目录 扫描 枚举特定于域控制器的服务 AS-REP烘焙服务帐户svc-alfresco 使用Hashcat破解AS-REP哈希 作为svc-alfresco获得立足点 攻击后的枚举和权限提升 查找指向“Account Operators”组的嵌套组 使用PowerView.ps1枚举组 了解帐户操作员组 寻找有价值的ACE 在Exc…

IDE装上ChatGPT,这款编辑器真的做到可以自动写代码了!

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 我新建了人工智能中文站https://ai.weoknow.com 每天给大家更新可用的国内可用chatGPT资源 Cursor 是集成了 GPT-4 的 IDE 工具&#xff0c;目前免费并且无需 API Key&#xff0c;支持 Win、Mac、Linux 平台&#xff0c;…

C# | 凸包算法之Graham,快速找到一组点最外侧的凸多边形

C#实现凸包算法之Graham 文章目录 C#实现凸包算法之Graham前言示例代码实现思路测试结果结束语 前言 这篇关于凸包算法的文章&#xff0c;本文使用C#和Graham算法来实现凸包算法。 首先消除两个最基本的问题&#xff1a; 什么是凸包呢&#xff1f; 凸包是一个包围一组点的凸多…

IIC协议

1.认识IIC 1、IIC协议概述&#xff1a; IIC&#xff08;Inter-Integrated Circuit&#xff0c;集成电路总线&#xff09;是一种串行通信协议&#xff0c;也被称为I2C协议。它是由荷兰的PHILIPS公司&#xff08;现在philips公司将其半导体部门拆分出来并更名为NXP半导体公司&a…

KVM虚拟化技术学习-KVM管理

二&#xff0c;KVM管理 1.升级配置 1.创建一个空磁盘卷 [rootlocalhost ~]# qemu-img create -f qcow2 /kvm/images/disk2.qcow2 5G Formatting disk2.qcow2, fmtqcow2 size5368709120 encryptionoff cluster_size65536 lazy_refcountsoff 2.修改配置文件 <disk typefi…

SpringCloudAlibaba整合分布式事务Seata

文章目录 1 整合分布式事务Seata1.1 环境搭建1.1.1 Nacos搭建1.1.2 Seata搭建 1.2 项目搭建1.2.1 项目示意1.2.2 pom.xml1.2.2.1 alibaba-demo模块1.2.2.2 call模块1.2.2.3 order模块1.2.2.4 common模块 1.2.3 配置文件1.2.3.1 order模块1.2.3.2 call模块 1.2.4 OpenFeign调用1…

想要成为一个性能测试工程师需要掌握哪些知识?

如果想要成为一个性能测试工程师需要掌握哪些知识&#xff1f; 可以看看下方教程&#xff01; 2023年最新版Jmeter性能测试项目实战讲解&#xff0c;从入门到精通价值8888的实战教程_哔哩哔哩_bilibili2023年最新版Jmeter性能测试项目实战讲解&#xff0c;从入门到精通价值888…

idea不识别yml文件了

添加上这两个就好了

recurdyn实用操作

目录 1.剖视图查看 2.自动重复操作 3.多个面生成FaceSurface 4.查看质心&#xff0c;质量坐标工具Mass 5.履带仿真建立其他特征路面 6.joint单位 7.创建样条插值函数AKISPL 8.导出结果曲线数据 9.后处理各名称含义 1.剖视图查看 取消剖视图需要重新进入&#xff0c;取…

Redis的ZipList和QuickList和SkipList和RedisObject(未完成)

ZipList:压缩列表&#xff0c;为了节省内存而设计的一种数据结构 ZipList是一种特殊的双端链表&#xff0c;是由一系列的特殊编码的连续内存块组成&#xff0c;不需要通过指针来进行寻址来找到各个节点&#xff0c;可以在任意一端进行压入或者是弹出操作&#xff0c;并且该操作…

C# | 凸包算法之Andrew‘s,获取围绕一组点的凸多边形的轮廓点

C#实现凸包算法之Andrew’s 文章目录 C#实现凸包算法之Andrews前言示例代码实现思路测试结果结束语 前言 这篇关于凸包算法的文章&#xff0c;本文使用C#和Andrew’s算法来实现凸包算法。 首先消除两个最基本的问题&#xff1a; 什么是凸包呢&#xff1f; 凸包是一个包围一组…

上海城市开发者社区小聚有感

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是Rockey&#xff0c;不知名企业的不知名Java开发工程师 &#x1f525;如果感觉博主的文章还不错的话&#xff0c;请&#x1f44d;三连支持&#x1f44d;一下博主哦 &#x1f4dd;联系方式&#xff1a;he18339193956&…