udp套接字编程(超详细带你逐步实现)

news2024/9/24 7:33:33

我自己在学习UDP服务器的时候,有着太多的不解,我不明白一个udp服务器是如何设计出来的。我在网上找了很多的资料,不过绝大多数都是把代码往哪里一放,具体的设计流程完全不提,这让我看了之后一头雾水。或许对于刚刚开始学习网络的小伙伴们来说,也面临着同样的问题。于是今天,我以自己的理解详细的给大家介绍一下udp服务器是如何一步一步的写出来的。当然,由于本人也是小白,知识水平有限,若各位大佬发现我的文章有不对的地方,希望能够及时指出我的错误。谢谢

1.udp服务器整体框架

class UdpServer
{
public:
    UdpServer(int port, std::string ip)
        :_port((uint16_t)port)
        ,_ip(ip)
        , _sockfd(-1)
    {}
    ~UdpServer()
    {}
    void Init()
    {}

    void Start()
    {}

private:
    //一个udp服务器至少要有以下这些成员

    //1.套接字,所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。本质上,套接字就是一个文件描述符。
    int _sockfd;
    //2.端口号(port), 标识一台主机的具体某个进行网络通信的进程,是一个16位的无符号整数
    uint16_t _port;
    //3.ip,在公网上唯一标识一台主机
    std::string _ip;
};

int main()
{
    uint16_t port;
    std::string ip;
    UdpServer svr(port, ip);
    svr.Init();
    svr.Start();
    return 0;
}

下面我们来一步步完善。

2.设计服务器

udpServer.cc编译之后会形成可执行程序udpServer,我们想运行这个服务器只需要./udpServer。今天,我们运行服务器的时候还要指明端口号和ip(ip可以省略)。以下面这种方式运行

./udpServer port [ip]

这样我们可以通过命令行参数获取到port和ip。代码就变成了这样

class UdpServer
{
public:
    UdpServer(int port, std::string ip)
        :_port((uint16_t)port)
        ,_ip(ip)
        , _sockfd(-1)
    {}
    ~UdpServer()
    {}
    void Init()
    {}

    void Start()
    {}

private:
    //一个udp服务器至少要有以下这些成员

    //1.套接字,所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。
    //本质上,套接字就是一个文件描述符。
    int _sockfd;
    //2.端口号(port), 标识一台主机的具体某个进行网络通信的进程,是一个16位的无符号整数
    uint16_t _port;
    //3.ip,在公网上唯一标识一台主机
    std::string _ip;
};

void Usage(const std::string proc)
{
    std::cout << "Usage:" << proc << " " << "port" << " "<< "[ip]" << std::endl;
}
int main(int argc, char* argv[])
{
    if(argc != 2 && argc != 3)
    {
        //命令行参数,要么是3要么是2,其他都是错误的
        Usage(argv[0]);//告诉用户使用方式
        exit(1);
    }
    uint16_t port = atoi(argv[1]);//从命令行获取端口号,我们接收到的命令行参数是字符串,注意要将字符串转整形
    std::string ip;
    if(argc == 3)
    {
        ip = argv[2];//如果命令行参数是3个,那么第三个就是ip
    }
    UdpServer svr(port, ip);
    svr.Init();
    svr.Start();
    return 0;
}

这里解释一下为什么ip可以省略呢?首先我使用的腾讯云服务器,端口号我可以自主选择开放哪个端口,而云服务器暴露给用户的ip不能绑定。如果你是自己的虚拟机,那么你填上你自己虚拟机的ip应该就能绑定。ip暂时省略,我们下面再解决。

3.服务器初始化

服务器的初始化分为以下几个步骤。

  1. 创建套接字。其实就是打开一个文件。我们需要使用下面这个函数来创建套接字

     #include <sys/types.h>       
     #include <sys/socket.h>
     int socket(int domain, int type, int protocol);
     //domain(范围):指明是本地通信还是网络通信,我们是网络通信,这里参数传 AF_INET.
     //type:套接字类型。我们udp服务器是面向数据报的,此处参数传SOCK_DGRAM
     //protocol(协议):网络应用,传0就行
     //返回值:文件描述符
    
  2. 绑定 套接字 和 ip+port

    绑定我们需要用到这个函数

    #include <sys/types.h>          
    #include <sys/socket.h>
    
    int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
    //sockfd:我们要绑定的套接字。
    //返回值:成功返回0,失败返回-1
    

    后两个参数里面,一定包含了ip与port信息。

    为了弄清楚后两个参数的意义,我们介绍一下struct sockaddr,struct sockaddr_in

    image-20230224193947000

    这两个结构体大小是一样的,只不过sockaddr_in具体说明了哪几位标识port,哪几位表示ip。我们可以先创建一个sockaddr_in的结构体,把port和ip信息填上去,再将其强转为sockaddr类型做为参数传到bind函数。这样就可以将套接字 和 ip+port绑定了

    先填充基本信息导struct sockaddr_in.

    struct sockaddr_in local;
    local.sin_family = AF_INET;//网络通信,ipv4
    local.sin_port = htons(_port);//port号要发送到网络当中的,hton主机转网络,s表示short,16位
    local.sin_addr.s_addr = _ip.empty() ? htonl(INADDR_ANY) : inet_addr(_ip.c_str());
    

    这里解释一下。sin_family,协议家族,指明是本地通信还是网络通信,我们还是填AF_INET。

    sin_port,端口号,我们的端口号是一个16位的无符号整数,端口号要发送到网络中,必须“主机转网络”。h表示host(主机),n表示网络,而s表示short(16位),因此我们要htons(_port)。

    sin_addr.s_addr填ip信息,我们经常看到的ip比如"223.101.110.33"是点分十进制的字符串风格的ip,这种风格的ip适合给人看,但是一个ipv4的ip地址最大也就是“255.255.255.255”,只需要4字节也就是32位就足够了,因此我们在网络中使用4字节ip。ip也是要发送到网络中的,也要主机转网络。htonl,其中l表示long(32位)。如果我们的_ip原本为空,比如我的云服务器就为空,就填INADDR_ANY。含义是,让服务器端计算机上的全部网卡的IP地址均可以做为服务器IP地址,也即监听外部客户端程序发送到服务器端全部网卡的网络请求,我们绝大多数情况下使用INADDR_ANY。如果不为空,指定填充特定的ip要使用inet_addr()函数。这个函数不仅帮助我们“主机转网络”,同时还做到了字符串风格ip,转为4字节ip。

    这里再讲一下什么是主机转网络,什么意思呢?

    要知道,机器分为大端机(以大端字节序存储数据)和小端机(以小端字节序存储数据),假如大端机上的数据直接发送到小端机,小端机肯定看不懂(好比本应该从左往右读的句子从右往左读)。因此,数据在网络中传输的时候,提出了这样一个规定。就是网络中的数据都是以大端字节序存储,谁要是想上网,那就得按我的规矩来,你原本是大端机就直接把数据传过来,小端机你自己把数据反过来在传过来。htoi就是做这个工作的。当然还有对应的ntoh,也就是把网络中的数据转成主机的数据。

    在我们填好网络信息之后我们调用bind函数绑定即可。至此,初始化函数的部分我们就写好了

        void Init()
        {
            //1.创建套接字
            _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
            if(_sockfd < 0)
            {
                std::cout << "socket fail!" << std::endl;
                exit(2);
            }
            std::cout << "socket success!" << std::endl;
    
            //2.绑定 套接字 和 ip+port
            //2.1填充基本信息到struct sockaddr_in.
            struct sockaddr_in local;
            local.sin_family = AF_INET;//网络通信,ipv4
            local.sin_port = htons(_port);//port号要发送到网络当中的,hton主机转网络,s表示short,16位
            local.sin_addr.s_addr = _ip.empty() ? htonl(INADDR_ANY) : inet_addr(_ip.c_str());
            //2.2bind网络信息
            if(bind(_sockfd, (const sockaddr*)&local, sizeof(local))< 0)
            {
                std::cout << "bind fail!" << std::endl;
                exit(3);
            }
            std::cout << "bind success" << std::endl;
        }
    

4.服务器启动

    void Start()
    {
        //服务器和客户端进行通信,也就是发送和接收数据,因此,我们给服务器定义一个发送缓冲区和接收缓冲区
        char inBuffer[1024];//将来接收到的数据都放在inBuffer
        char outBuffer[1024];//将来发送的数据都放在outBuffer;
        while(true)//服务器都是死循环一直运行的
        {
            
        }
    }

首先,我们作为服务器,肯定得从对端(客户端)接受数据,我们要用到这个函数接收数据

 #include <sys/types.h>
 #include <sys/socket.h>
 ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
//sockfd:套接字,是和对端主机通信的端点,这里传我们服务器的套接字即可
//buf:接收到的数据放到哪,填inBuffer即可
//len:缓冲区长度,填sizeof(buffer)-1.
//flags:直接填0
//src_addr:我们这个函数是从对端接受消息,肯定得知道从哪里接收,得知道对端的ip,端口信息啊。我们填的前几个参数,都是本主机的信息,而这个参数,就是用于接受对方主机信息的。这是一个输出型参数,我们需要先自己定义一个struct sockaddr_in类型的变量,再将其强转(struct sockaddr*)传入。
//addrlen:这是一个输入输出型参数,填上一个参数的大小。
//返回值:从对端读取到的字节数,读取失败返回-1.

当然,服务器也要像客户端发送数据。这就要用到下面这个函数

 #include <sys/types.h>
 #include <sys/socket.h>

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
//sockfd:套接字,是和对端主机通信的端点,这里传我们服务器的套接字即可
//buf:发什么数据,填outBuffer即可
//len:发多少数据,填strlen(outBuffer)即可
//flags:直接填0
//dest_addr:对端主机的信息。一样注意强转
addrlen:填上一个参数的大小。
//返回值:发送出去的字符数,失败返回-1.

学会这两个函数我们直接看代码

    void Start()
    {
        //服务器都是要处理具体的业务的。今天我们写这样一个简单的业务:
        //将对端发送过来的数据在服务器端打印出来(指明对端ip,port),将数据中的小写字母转为大写字母,再将数据返还客户端


        //服务器和客户端进行通信,也就是发送和接收数据,因此,我们给服务器定义一个发送缓冲区和接收缓冲区
        char inBuffer[1024];//将来接收到的数据都放在inBuffer
        char outBuffer[1024];//将来发送的数据都放在outBuffer;

        struct sockaddr_in peer;//输出型参数,将来对端主机的信息就放在这里
        socklen_t len = sizeof(peer);//输入输出型参数
        while(true)//服务器都是死循环一直运行的
        {
            ssize_t s = recvfrom(_sockfd, inBuffer, sizeof(inBuffer)-1, 0, (struct sockaddr*)&peer, &len);
            if(s > 0)
            {
                //成功读取到数据
                inBuffer[s] = '\0';
            }
            else
            {
                continue;//服务器不能因为一次数据读取失败就退出,这里直接continue
            }
            //读取成功的数据,我们可以提取出对端的ip和端口信息
            std::string peer_ip = inet_ntoa(peer.sin_addr);//这个函数可以将ip网络转主机,并且4字节转字符串风格
            uint16_t peer_port = ntohs(peer.sin_port);
            std::cout << "[" <<peer_ip << ":" << peer_port<<"]# " << inBuffer << std::endl;
            
            //小写字母转大写字母,再发给客户端
            for(int i = 0; i < s; i++)
            {
                outBuffer[i] = inBuffer[i];
                if(islower(outBuffer[i]))
                {
                    toupper(outBuffer[i]);
                }
            }
            sendto(_sockfd, outBuffer, strlen(outBuffer), 0, (const sockaddr*)&peer, len);
        }
    }

5.服务器的整体代码

至此,我们服务器的代码写的就差不多了,我把整体代码放在下面

#include <iostream>
#include <string>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>

class UdpServer
{
public:
    UdpServer(int port, std::string ip)
        :_port((uint16_t)port)
        ,_ip(ip)
        , _sockfd(-1)
    {}
    ~UdpServer()
    {}
    void Init()
    {
        //1.创建套接字
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if(_sockfd < 0)
        {
            std::cout << "socket fail!" << std::endl;
            exit(2);
        }
        std::cout << "socket success!" << std::endl;

        //2.绑定 套接字 和 ip+port
        //2.1填充基本信息到struct sockaddr_in.
        struct sockaddr_in local;
        local.sin_family = AF_INET;//网络通信,ipv4
        local.sin_port = htons(_port);//port号要发送到网络当中的,hton主机转网络,s表示short,16位
        local.sin_addr.s_addr = _ip.empty() ? htonl(INADDR_ANY) : inet_addr(_ip.c_str());
        //2.2bind网络信息
        if(bind(_sockfd, (const sockaddr*)&local, sizeof(local))< 0)
        {
            std::cout << "bind fail!" << std::endl;
            exit(3);
        }
        std::cout << "bind success" << std::endl;
    }

    void Start()
    {
        //服务器都是要处理具体的业务的。今天我们写这样一个简单的业务:
        //将对端发送过来的数据在服务器端打印出来(指明对端ip,port),将数据中的小写字母转为大写字母,再将数据返还客户端


        //服务器和客户端进行通信,也就是发送和接收数据,因此,我们给服务器定义一个发送缓冲区和接收缓冲区
        char inBuffer[1024];//将来接收到的数据都放在inBuffer
        char outBuffer[1024];//将来发送的数据都放在outBuffer;

        struct sockaddr_in peer;//输出型参数,将来对端主机的信息就放在这里
        socklen_t len = sizeof(peer);//输入输出型参数
        while(true)//服务器都是死循环一直运行的
        {
            ssize_t s = recvfrom(_sockfd, inBuffer, sizeof(inBuffer)-1, 0, (struct sockaddr*)&peer, &len);
            if(s > 0)
            {
                //成功读取到数据
                inBuffer[s] = '\0';
            }
            else
            {
                continue;//服务器不能因为一次数据读取失败就退出,这里直接continue
            }
            //读取成功的数据,我们可以提取出对端的ip和端口信息
            std::string peer_ip = inet_ntoa(peer.sin_addr);//这个函数可以将ip网络转主机,并且4字节转字符串风格
            uint16_t peer_port = ntohs(peer.sin_port);
            std::cout << "[" <<peer_ip << ":" << peer_port<<"]# " << inBuffer << std::endl;
            
            //小写字母转大写字母,再发给客户端
            for(int i = 0; i < s; i++)
            {
                outBuffer[i] = inBuffer[i];
                if(islower(outBuffer[i]))
                {
                    toupper(outBuffer[i]);
                }
            }
            sendto(_sockfd, outBuffer, strlen(outBuffer), 0, (const sockaddr*)&peer, len);
        }
    }

private:
    //一个udp服务器至少要有以下这些成员

    //1.套接字,所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。
    //本质上,套接字就是一个文件描述符。
    int _sockfd;
    //2.端口号(port), 标识一台主机的具体某个进行网络通信的进程,是一个16位的无符号整数
    uint16_t _port;
    //3.ip,在公网上唯一标识一台主机
    std::string _ip;
    

};

void Usage(const std::string proc)
{
    std::cout << "Usage:" << proc << " " << "port" << " "<< "[ip]" << std::endl;
}
int main(int argc, char* argv[])
{
    if(argc != 2 && argc != 3)
    {
        //命令行参数,要么是3要么是2,其他都是错误的
        Usage(argv[0]);//告诉用户使用方式
        exit(1);
    }
    uint16_t port = atoi(argv[1]);//从命令行获取端口号,我们接收到的命令行参数是字符串,注意要将字符串转整形
    std::string ip;
    if(argc == 3)
    {
        ip = argv[2];//如果命令行参数是3个,那么第三个就是ip
    }
    UdpServer svr(port, ip);
    svr.Init();
    svr.Start();
    return 0;
}

6.UDP客户端整体框架

写完服务器,我们也要实现对应的客户端与之通信。有了上面的基础,现在写客服端非常简单!

class UdpClient
{
public:
    UdpClient()
        :_sockfd(-1)
    {}
    ~UdpClient()
    {}

    void Init()
    {}
    void start()
    {}

private:
    int _sockfd;
};


int main()
{
    UdpClient client;
    client.Init();
    client.start();
    return 0;
}

7.设计客户端

udpClient.cc编译之后会形成可执行程序udpClient,我们想运行这个客户端只需要./udpClient。今天,我们运行服务器的时候还要指明访问的ip和端口号以下面这种方式运行

./udpClient ip port

这样我们可以通过命令行参数获取到port和ip。代码就变成了这样

class UdpClient
{
public:
    UdpClient()
        :_sockfd(-1)
    {}
    ~UdpClient()
    {}

    void Init()
    {}
    void start()
    {}

private:
    int _sockfd;
};

void Usage(char* proc)
{
    std::cout << "Usage: " << proc << " ip port" << std::endl;
}

// ./udpClient ip port
int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    //从命令行获取服务器的ip和端口号
    std::string server_ip = argv[1];
    uint16_t server_port = atoi(argv[2]);

    UdpClient client;
    client.Init();
    client.start();
    return 0;
}

8.客户端初始化

    void Init()
    {
        //1.创建socket套接字
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if(_sockfd < 0)
        {
            std::cout << "socket fail!" << std::endl;
            exit(2);
        }
        std::cout << "socket success!" << std::endl;
        
        //2.bind 
        //客户端需不需要bind?
        //需要,但是不需要用户手动bind,而是OS自动给你bind,如果你手动bind指定的port号,可能会失败
        //因为你的主机上有很多的client进程在运行,你指定的port可能被别的客户端bind了。
        //如果你非要手动bind,也不是不可以,但是,这种做法及其不推荐
        //那服务器为什么要bind呢?
        //要知道一个服务器要给许许多多的客户端提供服务的,服务器的端口号,必须是固定的。

    }

9.客户端启动

   void start(std::string& server_ip, uint16_t server_port)
    {
        //3这里就是通信过程了。
        //同样的,我们也定义一个inBuffer和outBuffer
        char inBuffer[1024];
        std::string outBuffer;
        while(true)
        {
            std::cout << "Please Enter# ";
            //3.1 先把数据写到outBuffer
            std::getline(std::cin, outBuffer);
            //3.2把数据发给服务器,我们同样需要用到sendto函数
            //当然啦,我们首先得把对端的网络信息填好才行
            struct sockaddr_in peer;
            peer.sin_family = AF_INET;
            peer.sin_port = htons(server_port);
            peer.sin_addr.s_addr = inet_addr(server_ip.c_str());  

            //OS自动绑定ip+port就发生在第一次sendto的时候          
            sendto(_sockfd, outBuffer.c_str(), outBuffer.size(), 0, (const sockaddr*)&peer, sizeof(peer));
            
            //3.3接下来要从服务器端接受消息,还是使用recvfrom
            socklen_t len = sizeof(peer);
            ssize_t s = recvfrom(_sockfd, inBuffer, sizeof(inBuffer)-1, 0, (struct sockaddr*)&peer, &len);
            if(s > 0)
            {
                //成功接收
                inBuffer[s] = 0;
                std::cout << "server echo# " << inBuffer << std::endl;
            }
            else
            {
                //服务器无回应
                std::cout << "recvfrom server nothing!" << std::endl;
            }
        }
    }

10.客户端整体代码

#include <iostream>
#include <string>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
class UdpClient
{
public:
    UdpClient()
        :_sockfd(-1)
    {}
    ~UdpClient()
    {}

    void Init()
    {
        //1.创建socket套接字
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if(_sockfd < 0)
        {
            std::cout << "socket fail!" << std::endl;
            exit(2);
        }
        std::cout << "socket success!" << std::endl;
        
        //2.bind 
        //客户端需不需要bind?
        //需要,但是不需要用户手动bind,而是OS自动给你bind,如果你手动bind指定的port号,可能会失败
        //因为你的主机上有很多的client进程在运行,你指定的port可能被别的客户端bind了。
        //如果你非要手动bind,也不是不可以,但是,这种做法及其不推荐
        //那服务器为什么要bind呢?
        //要知道一个服务器要给许许多多的客户端提供服务的,服务器的端口号,必须是固定的。

    }
    void start(std::string& server_ip, uint16_t server_port)
    {
        //3这里就是通信过程了。
        //同样的,我们也定义一个inBuffer和outBuffer
        char inBuffer[1024];
        std::string outBuffer;
        while(true)
        {
            std::cout << "Please Enter# ";
            //3.1 先把数据写到outBuffer
            std::getline(std::cin, outBuffer);
            //3.2把数据发给服务器,我们同样需要用到sendto函数
            //当然啦,我们首先得把对端的网络信息填好才行
            struct sockaddr_in peer;
            peer.sin_family = AF_INET;
            peer.sin_port = htons(server_port);
            peer.sin_addr.s_addr = inet_addr(server_ip.c_str());  

            //OS自动绑定ip+port就发生在第一次sendto的时候          
            sendto(_sockfd, outBuffer.c_str(), outBuffer.size(), 0, (const sockaddr*)&peer, sizeof(peer));
            
            //3.3接下来要从服务器端接受消息,还是使用recvfrom
            socklen_t len = sizeof(peer);
            ssize_t s = recvfrom(_sockfd, inBuffer, sizeof(inBuffer)-1, 0, (struct sockaddr*)&peer, &len);
            if(s > 0)
            {
                //成功接收
                inBuffer[s] = 0;
                std::cout << "server echo# " << inBuffer << std::endl;
            }
            else
            {
                //服务器无回应
                std::cout << "recvfrom server nothing!" << std::endl;
            }
        }
    }

private:
    int _sockfd;
};

void Usage(char* proc)
{
    std::cout << "Usage: " << proc << " ip port" << std::endl;
}

// ./udpClient ip port
int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    //从命令行获取服务器的ip和端口号
    std::string server_ip = argv[1];
    uint16_t server_port = atoi(argv[2]);

    UdpClient client;
    client.Init();
    client.start(server_ip, server_port);
    return 0;
}

11.效果测试

至此我们代码终于写完了,让我们测试一下看看效果吧

注意:由于我只有一台云服务器,我测试的时候使用的是“127.0.0.1”本地环回。

简单说就是数据在同一个协议栈自上而下,再自下而上传递。

当然了,如果你想在你的windows下访问服务器,就需要你在windows环境下自己写一个客户端了。写起来也是大同小异的,感兴趣的同学请自行搜索一下。有了我这篇文章做铺垫,相信你能够在windows下轻松实现。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-im2mEc11-1677480151646)(https://lin-typora.oss-cn-beijing.aliyuncs.com/image-20230227143057868.png)]

感谢各位大佬的观看。期待您的支持!

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

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

相关文章

scikit-learn实现近邻算法分类的示例

scikit-learn库 scikit-learn已经封装好很多数据挖掘的算法 现介绍数据挖掘框架的搭建方法 转换器&#xff08;Transformer&#xff09;用于数据预处理&#xff0c;数据转换流水线&#xff08;Pipeline&#xff09;组合数据挖掘流程&#xff0c;方便再次使用&#xff08;封装…

SVN配置使用(钩子配置、updata忽略指定文件)

参考链接&#xff1a; svn(svnsync)实时同步备份及问题解答 SVN常用命令之update SVN钩子就是一个脚本&#xff0c;在SVN更新前、后、或者变化前后等等状态&#xff0c;触发的脚本。据此可以有多种用途&#xff0c;如&#xff1a;1、在版本提交前要求必须对更新进行说明&#…

20分钟6个示例4个动图教你学会Async Hooks

序幕 async_hooks模块提供了一个全新的功能世界,但作为 Node.js 爱好者,我最感兴趣的是,它可以让您轻松了解我们在应用程序中经常执行的一些任务的幕后情况。 在本文中,我将尝试借助async_hooks模块来演示和解释一个典型的异步资源的生命周期。 Async Hooks API 简介 as…

【源码解析】SpringBoot自动装配的实现原理

什么是SpringBoot的自动装配 SpringBoot在启动的时候会扫描外部jar包中的META-INF/spring.factories文件&#xff0c;将文件中配置的类信息按照条件装配到Spring容器中。 实现原理 核心注解SpringBootApplication Target({ElementType.TYPE}) Retention(RetentionPolicy.R…

时间序列分析 | BiLSTM双向长短期记忆神经网络时间序列预测(Matlab完整程序)

时间序列分析 | BiLSTM双向长短期记忆神经网络时间序列预测(Matlab完整程序) 目录 时间序列分析 | BiLSTM双向长短期记忆神经网络时间序列预测(Matlab完整程序)预测结果评价指标基本介绍完整程序参考资料预测结果 评价指标 训练集数据的R2为:0.99302 测试集数据的R2为&…

企业数字化(技术中台、数据中台、工业互联网平台)建设方案

【版权声明】本资料来源网络&#xff0c;知识分享&#xff0c;仅供个人学习&#xff0c;请勿商用。【侵删致歉】如有侵权请联系小编&#xff0c;将在收到信息后第一时间删除&#xff01;完整资料领取见文末&#xff0c;部分资料内容&#xff1a; 1.1 技术架构 1.1.1 技术架构总…

pandas提取excel数据形成三元组,采用neo4j数据库构建小型知识图谱

前言 代码来自github项目 neo4j-python-pandas-py2neo-v3&#xff0c;项目作者为Skyelbin。我记录一下运行该项目的一些过程文字以及遇到的问题和解决办法。 一、提取excel中的数据转换为DataFrame三元组格式 from dataToNeo4jClass.DataToNeo4jClass import DataToNeo4j imp…

Unity WebGL打包后运行

打包后出现以下&#xff1a;工程中没有StreamingAssets文件夹的&#xff0c;则打包后不会有下图StreamingAssets的文件夹 这3个文件夹都是项目资源&#xff0c;只有index.html才是打开Web运行的页面 序列 主流浏览器 WebGL支持 1 Microsoft Edge Edge16或更高版本 2 火狐…

基于超像素的多视觉特征图像分割算法研究

0.引言 背景&#xff1a; 经典聚类算法&#xff1a;Kmeans、FCM 现有问题&#xff1a; 1&#xff09;现有算法大都是基于单一的视觉特征而设计的&#xff0c;eg&#xff1a;基于颜色特征的分割。 2&#xff09;没有考虑像素周围的空间信息&#xff1b;分割结果&#xff1a;多噪…

Spring-boot @ConfigurationProperties(prefix = “/xx“) 实际作用以及实现思路

ConfigurationProperties 可以通过配置该注解到我们自己创建的类上达成取代 Value 的作用 最终目的都是读取到配置文件中的值而已。 ConfigurationProperties(prefix “/xx”) 其中 prefix 指定了配置文件中的配置值得开头值 示例 配置文件中自定义值 application.yml 中…

3-10 图文并茂解释TCP/IP 3次握手4次断开

文章目录前言TCP报文首部TCP数据传输3次握手4次断开前言 TCP/IP传输协议&#xff0c;即传输控制/网络协议&#xff0c;也叫作网络通讯协议。它是在网络的使用中的最基本的通信协议。TCP/IP传输协议对互联网中各部分进行通信的标准和方法进行了规定。并且&#xff0c;TCP/IP传输…

Vue基本指令:v-on,v-if,v-mode

目录 Vue基本指令 1、v-text指令&#xff1a;设置标签的文本值 2、v-html指令&#xff1a;设置标签的innerHTML 3、v-on指令&#xff1a;为元素绑定事件 4、v-on:传参 5、v-show指令&#xff1a;根据真假切换元素的显示状态 6、v-if指令&#xff1a;根据表达式的真假切换…

前端学习---

1.简单且必须掌握的 1.MVVM是什么 将MVC中的V变为了MVVM&#xff0c;实现了双向绑定。其中VM就是vue的作用&#xff0c;这样页面的动态化可以通过vue来操作&#xff0c;而不是页面直接与后端操作&#xff0c;实现了前后端的分离 2.为什么vue采用异步渲染 &#xff1f; 调…

【云原生】k8s集群资源监控平台搭建—20230227

1. 监控指标 集群监控 ①节点资源利用率 ②节点数 ③运行pods pod监控 ①容器指标 ②应用程序 2. 监控平台 1. Prometheus 开源的&#xff1b;集成了很多功能&#xff0c;比如监控、报警、数据库等&#xff1b;以HTTP协议周期性的抓取被监控组件的状态以及相应的数据&…

HarmonyOS Connect “Device Partner”专场FAQ来啦

Device Partner平台是面向AIoT产业链伙伴的一站式服务平台&#xff0c;伙伴可以通过平台获取最新的产品、服务与解决方案&#xff0c;实现智能硬件产品的开发、认证、量产和推广等全生命周期的管理&#xff0c;加入HarmonyOS Connect生态&#xff0c;共同提升消费者的智慧生活体…

就业天花板?33% 程序员月薪达到 5 万元以上

2023年&#xff0c;随着互联网产业的蓬勃发展&#xff0c;程序员作为一个自带“高薪多金”标签的热门群体&#xff0c;被越来越多的人所关注。 在过去充满未知的一年中&#xff0c;他们的职场现状发生了一定的改变。那么&#xff0c;程序员岗位的整体薪资水平、婚恋现状、职业…

主流消息队列RocketMq,RabbitMq比对使用

首先整理这个文章是因为我正好有机会实战了一下rocketmq&#xff0c;阿里巴巴的一个开源消息中间件。所以就与以往中rabbitmq进行小小的比较一下。这里主线的根据常见面试问题进行整理。1.消息队列常用的场景1.1.削峰例如我们做得考试系统中&#xff0c;用户通过人脸识别登录系…

Java查漏补缺(16)网络编程概述、网络通信要素、谈传输层协议:TCP与UDP协议、网络编程API、TCP网络编程、UDP网络编程、URL编程

Java查漏补缺&#xff08;16&#xff09;网络编程概述、网络通信要素、谈传输层协议&#xff1a;TCP与UDP协议、网络编程API、TCP网络编程、UDP网络编程、URL编程本章专题与脉络1. 网络编程概述1.1 软件架构1.2 网络基础2. 网络通信要素2.1 如何实现网络中的主机互相通信2.2 通…

【全网最细PAT题解】【PAT乙】1024 科学计数法

题目链接 1024 科学计数法 题目描述 科学计数法是科学家用来表示很大或很小的数字的一种方便的方法&#xff0c;其满足正则表达式 [-][1-9].[0-9]E[-][0-9]&#xff0c;即数字的整数部分只有 1 位&#xff0c;小数部分至少有 1 位&#xff0c;该数字及其指数部分的正负号即使对…

Linux下安装Redis(云服务器)

一、选择自己需要下载的版本后&#xff0c;右击选择复制链接&#xff0c;然后利用命令进行下载&#xff0c;进入Xshell控制台&#xff0c;输入wget将复制的链接粘帖上 命令如下&#xff1a; wget https://download.redis.io/releases/redis-6.0.5.tar.gz 解压安装Redis解压 将下…