简单的 UDP 网络程序

news2024/11/30 12:32:56

文章目录:

  • 简单的UDP网络程序
    • 服务端创建套接字
    • 服务端绑定
    • 启动服务器
    • udp客户端
    • 本地测试
    • INADDR_ANY
  • 地址转换函数
  • 关于 inet_ntoa

简单的UDP网络程序

服务端创建套接字

我们将服务端封装为一个类,当定义一个服务器对象之后,需要立即进行初始化服务器,在 UDP 网络程序中,初始服务器的第一个步骤就是创建套接字。我们使用 socket 函数创建套接字。

socket 函数的定义如下所示:

int socket(int domain, int type, int protocol);

参数说明:

  • domain:整数,指定通信域(Communication Domain)。在同一主机上的进程之间进行通信时,我们使用 POSIX 标准定义的 AF_LOCAL。在不同的主机通过 IPv4 连接的进程之间通信时,我们使用 AF_INET,对于通过 IPv6 连接的进程之间进行通信时,我们使用 AF_INET6。
  • type:通信类型(Communication Type)。SOCK_STREAM:TCP(可靠的、面向连接的);SOCK_DGRAM:UDP(不可靠的、无连接的)。
  • protocol:互联网协议的协议值,通常为0。这与数据包的 IP 头中的协议字段中显示的数字相同。

返回值:套接字创建成功返回一个文件描述符,否则返回-1,同时错误码被设置。

在初始化服务器创建套接字时,需要调用 socket 函数创建套接字,我们需要填入的协议家族是 AF_INET(PF_INET),因为我们需要进行的是网络通信。服务器类型填入 SOCK_DGRAM ,因为编写的 UDP 服务器是面向数据报的。第三个参数设置为0,表示使用默认的传输协议。

下面是使用这些参数创建套接字的示例:

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

class UdpServer
{
public:
    void init()
    {
        // 创建套接字
        sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);
        if (sockfd_ < 0)
        {
            std::cerr << "Failed to create socket." << std::endl;
            exit(1);
        }
        std::cout << "socket create success, sockfd: " << sockfd_ << std::endl;

        // ......
    }
private:
    int sockfd_; // 文件描述符
};

我们对其进行一个测试,查看套接字的创建是否成功。

int main()
{
    UdpServer *svr=new UdpServer();
    svr->init();
    return 0;
}

运行测试结果:

在这里插入图片描述

服务端绑定

在套接字创建成功之后,需要将其与网络相关联,以便进行网络通信。对于一个 UDP 服务器,绑定操作是必要的。通过绑定,将套接字与指定的IP地址和端口号关联起来,以便监听和处理该地址上的网络数据。

使用 bind 函数将套接字与特定的IP地址和端口号进行绑定,bind 函数的定义如下:

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数说明:

  • sockfd:需要绑定的套接字描述符。
  • addr:指向要绑定的地址结构的指针,需要将其转化为 struct sockaddr 类型。
  • addrlen:地址结构长度。

返回值:

  • 绑定成功返回0。失败则返回-1,且错误码被设置。

bind 函数的主要作用就是将套接字与指定的地址进行关联,以便在该地址上进行监听和处理网络数据。调用 bind 之前,需要确保套接字已经创建成功。

在绑定套接字之前,需要定义一个 struct sockaddr_in 结构,并将网络属性信息填充到该结构体中。由于 struct sockaddr_in 结构体中的一些字段是可选的,建议在填充网络信息之前先将结构体变量进行清空,然后填充协议家族、端口号、IP地址等信息。

下列示例展示了如何使用 bind 函数将套接字绑定到指定的IP地址和端口号:

class UdpServer
{
public:
    UdpServer(int port = 8080, 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::cerr << "Failed to create socket." << std::endl;
            exit(1);
        }
        std::cout << "socket create success, sockfd: " << sockfd_ << std::endl;

        // 2.绑定网络信息,指明IP+port
        // 2.1 先填充基本信息到 struct sockaddr_in
        struct sockaddr_in local;      // lock在用户栈上开辟的空间 -> 临时变量 -> 写入内核中
        bzero(&local, sizeof(local));  // bzero
        local.sin_family = AF_INET;    // 填充协议家族,域
        local.sin_port = htons(port_); // 填充服务器对应的端口信息,一定会发给对方,_port一定会到网络中
        // local.sin_addr // 服务器都必须具有IP地址("xx.yy.zz.aaa"字符串风格点分十进制) -> 4字节IP -> uint32_t ip
        local.sin_addr.s_addr = ip_.empty() ? htonl(INADDR_ANY) : inet_addr(ip_.c_str());

        // 2.2 绑定网络信息
        if (bind(sockfd_, (struct sockaddr *)&local, sizeof(local)) == -1)
        {
            std::cerr << "Failed to bind socket." << std::endl;
            exit(2);
        }
        std::cout << "Socket bound successfully." << std::endl;
    }

private:
    uint16_t port_;  // 服务器必须得有端口号信息
    std::string ip_; // 服务器必须得有IP地址
    int sockfd_;     // 文件描述符
};

启动服务器

UDP 服务器的初始化在上面已经完成。服务器初始化完成之后,就可以启动服务器并提供服务了。服务器通常是以循环的方式运行的,以便持续接收和处理客户端请求。UDP 服务器在接收到客户端发送的数据后,可以直接读取这些数据,而无需建立连接。

recvfrom函数:
recvfrom 函数是在网络编程中使用的一个系统调用函数,用于从一个指定的套接字接收数据,并将数据存储到指定的缓冲区中。

recvfrom 函数的定义如下:

#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:指向接收数据的缓冲区。
  • len:期望读取数据的字节数。
  • flags:可选的标志参数,用于控制接收操作的行为。常用的标志有:0、MSG_DONTWAIT。
  • src_addr:指向用于存储发送方地址信息的 struct sockaddr 结构体的指针,包括协议家族、IP地址、端口号等。
  • addrlen:src_addr 结构体的长度,及其可用空间大小,这是一个输出型参数。

当使用 recvfrom 函数接收 UDP 数据时,除了获取数据内容外,还可以获取发送方的网络属性信息,包括IP地址和端口号。调用 recvfrom 函数之前,需要将 addrlen 参数设置为接收方地址结构体的大小,以确保函数可以正确填充发送方的地址信息。

接下来使用下列函数启动服务器:

当使用 recvfrom 函数读取客户端数据后,可以将读取到的数据视为字符串,并将最后一个位置设置为 ‘\0’ ,以便于进行输出。此时,我们也获取到了客户端的IP地址和端口号。recvfrom 函数调用成功之后返回的端口号是网络序列(以大端字节序表示),我们需要调用 ntohs 函数将其转换为主机序列(与本地字节序相匹配)。同样的,获取的IP地址也需要用 inet_ntoa 函数进行转化。

class UdpServer
{
public:
    void start()
    {
        // 服务器设计的时候都是死循环
        char inbuffer[1024];  // 储存读取到的数据
        char outbuffer[1024]; // 储存发送的数据
        while (true)
        {
            struct sockaddr_in peer;      // 输出型参数
            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; // 将其当作字符串
            else if (s == -1)
            {
                std::cerr << "recvfrom:" << strerror(errno) << ":" << sockfd_ << endl;
                continue; // 调用失败,继续进行读取,服务器不能因为一个客户端连接失败就退出
            }
            // 读取成功,除了读取到对方的数据,还要读取到对方的网络地址[ip,port]
            std::string peerIp = inet_ntoa(peer.sin_addr);
            uint32_t peerPort = ntohs(peer.sin_port);
        }
    }
    
private:
    uint16_t port_;  // 服务器必须得有端口号信息
    std::string ip_; // 服务器必须得有IP地址
    int sockfd_;     // 文件描述符
};

在构建服务器时,我们可以引入命令行参数用于指定服务器的IP地址和端口号。使用云服务器,实际上不需要传入IP地址,直接运行程序时指定端口即可,这里可以将IP地址设置为(127.0.0.1),它代表本地主机或本地环回地址,相当于 localhost 。

以下为一个示例代码,演示如何通过命令行参数传递端口号,并将IP地址设置为本地环回地址:

static void Usage(const std::string proc)
{
    cout << "Usage:\n\t" << proc << " port [ip]" << endl;
}

// ./udpServer port [ip]
int main(int argc,char *argv[])
{
    if(argc!=2&&argc!=3)
    {
        Usage(argv[0]);
        return 1;
    }
    std::string ip="127.0.0.1";
    uint16_t port=atoi(argv[1]);
    UdpServer svr(port,ip);
    svr.init();
    svr.start();
}

如下所示,运行程序并加上端口号就可以创建 udp 服务器成功了:

在这里插入图片描述

使用 netstat 命令来查看当前网络的状态,下图标出的就是当前运行的 udpServer 程序:

在这里插入图片描述

netstat 是一个用于显示网络连接、路由表和网络接口等相关信息的命令。以下是 netstat 常用的选项:

  • -a (all):显示所有的sockets(包括监听和非监听的)。
  • -t (tcp):显示 TCP 协议相关的连接信息。
  • -u (udp):显示 UDP 协议相关的连接信息。
  • -n (numeric):以数字形式显示IP地址和端口号,不进行主机名和服务名的解析。
  • -p (program):显示与每个连接关联的进程/程序的 PID 和名称。
  • -l (listening):显示每个处于监听状态的 sockets。

注意:不同操作系统上的 netstat 命令可能会不同,包括选项的名称和支持的功能。因此,在使用 netstat 命令时可以通过 netstat --help 来查看系统文档使用说明。

udp客户端

udp客户端的实现步骤如下所示,用于与服务器进行 UDP 通信:

  • 参数检查:通过检查命令行参数,来确保命令行输入的参数个数是否正确,这是为了确保程序能够正确获取服务器的IP地址和端口号,避免后续出现错误。
  • 获取服务端的IP地址和端口号:根据命令行参数获取服务器的IP地址和端口号,并将它们存储在变量 server_ip 和 server_port 中。这样客户端就获取到了需要连接服务器的地址信息。
  • 创建 UDP 套接字:使用 socket 函数创建一个 UDP 套接字,即创建一个用于网络通信的套接字。AF_INET 参数表示使用 IPv4 地址,SOCK_DGRAM 参数表示使用数据报(UDP) 套接字类型。创建套接字成功之后,会返回一个文件描述符,用于后续该套接字的操作。
  • 设置服务器地址信息:UDP 通信中,需要指定连接的服务器地址信息。这里通过填充 server 结构体来实现。bzero 函数将 server 结构体清零。sin_family 表示地址族为 IPv4 ,sin_port 表示服务器的端口号,sin_addr.s_addr 表示服务器的IP地址。
// ./udpServer server_ip server_port
// 客户端要连接服务端,需要知道server对应的IP和port
class UdpClient
{
public:
    UdpClient(std::string ip = "", uint16_t port = 8080)
        : ip_(ip), port_(port), sockfd_(-1)
    {
    }

    void init()
    {
        // 创建套接字
        sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);
        if (sockfd_ < 0)
        {
            std::cerr << "socket create error" << std::endl;
            exit(1);
        }
    }

    void start()
    {
        // 填写服务器对应信息
        struct sockaddr_in peer;
        bzero(&peer, sizeof peer);
        peer.sin_family = AF_INET;
        peer.sin_port = htons(port_);
        peer.sin_addr.s_addr = inet_addr(ip_.c_str());

        std::string buffer;
        // 启动客户端
        while (true)
        {
            std::cout << "Please Enter# ";
            getline(cin, buffer);

            // 发送消息给server
            sendto(sockfd_, buffer.c_str(), buffer.size(), 0, (const struct sockaddr *)&peer, sizeof(peer));
        }
    }

    ~UdpClient()
    {
        if (sockfd_ >= 0)
            close(sockfd_);
    }

private:
    std::string ip_;
    uint16_t port_;
    int sockfd_;
};

static void Usage(const std::string name)
{
    cout << "Usage:\n\t" << name << " server_ip server_port" << endl;
}

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(2);
    }

    // 根据命令行,设置需要访问的服务器IP
    std::string server_ip = argv[1];
    uint16_t server_port = atoi(argv[2]);
    UdpClient client(server_ip, server_port);
    client.init();
    client.start();
    return 0;
}

sendto函数

sendto 函数用于在 UDP 通信中发送数据,它的函数定义如下:

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); 

参数说明:

  • sockfd:使用 socket 函数创建的套接字描述符。

  • buf:指向要发送的数据的缓冲区的指针。数据可以是任意类型的指针,通常是一个 char 数组或字符串。

  • len:要发送的数据的长度,单位是字节。

  • flags:指定发送操作的可选标志。一般使用0或与 MSG_DONTROUTE 等特殊标志进行按位或运算。有以下常用标志:
    MSG_DONTROUTE:不使用路由表发送数据,直接发送到目的地址。
    MSG_OOB:发送外带数据。
    MSG_NOSIGNAL:在发送数据时忽略 SIGPIPE 信号,避免导致进程终止。

  • dest_addr:对端网络相关的属性信息。在 UDP 通信中,该参数指向 sockaddr 结构体的指针,其中包含了目标服务器的IP地址和端口号。

  • addrlen:传入 dest_addr 结构体的长度,通常是 sizeof(struct sockaddr_in)。

本地测试

udp 的服务端和客户端的代码都已经编写完成。因此,我们可以先在本地进行测试,使用本地环回地址(127.0.0.1)作为服务器的IP地址,客户端可以连接到该地址来与服务器进行通信。同时,我们需要确保服务器绑定的端口号与客户端连接时指定的端口号一致。

在这里插入图片描述
当客户端和服务端成功建立连接并进行通信时,可以通过使用 netstat 命令来看网络信息来确认连接的建立。

在这里插入图片描述

INADDR_ANY

INADDR_ANY 是一个IP地址,当我们不想将套接字绑定到任何特定的IP时使用。在实现通信时,我们需要将套接字绑定到IP地址。当我们不知道自己及其的IP地址或者其它情况时,可以使用特殊的IP地址 INADDR_ANY。它允许我们的服务器接收任何接口作为目标的数据包。

在进行网络测试时,你需要确保服务器能够通过公网IP地址进行访问。然而,直接将服务器绑定到公网IP可能会导致绑定失败的问题。

在这里插入图片描述

在云服务器中,你所获得的公网IP地址并不一定是真正的公网IP,而是有云服务器厂商提供的内部IP地址,这些内部IP地址无法直接在服务器代码中进行绑定。

为了让服务器能够通过公网IP进行访问,可以使用一个特殊的绑定值,即 INADDR_ANY 。这个值是系统提供的宏,对应的数值为0。通过将服务器绑定到 INADDR_ANY ,就可以让服务器接收来自任意IP地址的连接请求。

若想要我们写的服务端能够被外部网络进行访问,我们可以这样做:

  1. 在服务器代码中,将与IP地址相关的代码去掉。这样可以使服务器动态绑定到可用的IP地址。
  2. 填充 struct sockaddr_in 结构体时,将IP地址设置为 INADDR_ANY 。这样服务器就可以接收到来自任意IP地址的连接请求了。
struct sockaddr_in peer;
bzero(&peer, sizeof peer);
peer.sin_family = AF_INET;
peer.sin_port = htons(port_);
peer.sin_addr.s_addr = INADDR_ANY;

注意:INADDR_ANY 的值是0,它不需要进行网络字节序的转化。因此,在设置时无需进行大小端的处理。

在这里插入图片描述

编译并运行修改后的代码时,再次使用 netstat 命令查看网络连接情况时,该服务器的IP地址变成了 0.0.0.0 ,意味着该服务器可以接收任意主机发起的连接请求:

在这里插入图片描述

地址转换函数

这里主要介绍的是 IPv4 的 socket 网络编程。sockaddr_in 中的成员 struct in_addr sin_addr 表示32位的IP地址。但是我们通常使用点分十进制的字符串表示IP地址,以下函数可以在字符串表示和 in_addr 表示之间进行转换:

字符串转 in_addr 的函数:

#include <arpa/inet.h>

// 将字符串cp表示的IP地址转换为in_addr结构体,并将结果存储在inp中
int inet_aton(const char *cp, struct in_addr *inp);

// 将字符串cp表示的IP地址转换为in_addr_t类型的值
in_addr_t inet_addr(const char *cp);

// 将字符串从src表示的IP地址转换为指定地址族af的二进制表示,并将转换后的结果存储在dst中
int inet_pton(int af, const char *src, void *dst);

in_addr转字符串的函数:

// 将in_addr结构体中的IP地址转换为字符串形式
char *inet_ntoa(struct in_addr in);

// 将指定地址族af的二进制表示src转化为字符串形式,并将结果存储在dst中
const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);

其中 inet_pton 和 inet_ntop 不仅可以转换 IPv4 的 in_addr ,还可以转换 IPv6 的 in6_addr 因此函数接口是 void *addrptr 。

关于 inet_ntoa

inet_ntoa 这个函数的返回值类型是 char*,很显然这个函数在自己内部申请了一块内存来报错IP结果,那么是否需要调用者手动释放呢?

在这里插入图片描述
man 手册上说,inet_ntoa 函数,是把这个结果放在了静态存储区。这时不需要我们进行手动释放。那么,若多次调用该函数,会有什么样的效果呢?

请看代码:

#include <iostream>
#include <netinet/in.h>
#include <arpa/inet.h>
using namespace std;

int main()
{
    struct sockaddr_in addr1;
    struct sockaddr_in addr2;
    addr1.sin_addr.s_addr = 0;
    addr2.sin_addr.s_addr = 0xffffffff;
    char *ptr1 = inet_ntoa(addr1.sin_addr);
    char *ptr2 = inet_ntoa(addr2.sin_addr);
    cout << "ptr1:" << ptr1 << "  ptr2:" << ptr2 << endl;
    return 0;
}

运行结果如下所示:

在这里插入图片描述

由于 inet_ntoa 函数使用了静态缓冲区,每次调用该函数时,返回的指针都会指向同一个缓冲区。这意味着如果在多个地方同时使用了 inet_ntoa 返回值,并且后续的调用覆盖了之前的结果,那么之前获取的字符串指针将变为无效。

思考:如果有多个线程调用 inet_ntoa,是否会出现异常情况呢?

  • 在 APUE 中,明确提出了 inet_ntoa 不是线程安全的函数。因为它使用了一个全局共享的缓冲区来保存转化后的字符串。所以多线程环境下,该函数可能会导致竞争条件,从而出错。
  • 但是在 centos7 上测试,并没有出现问题,这可能是在内部加了互斥锁来保证线程安全性。
  • 在多线程环境下,推荐使用 inet_ntop 函数,它是线程安全的。inet_ntop 函数要求调用者提供一个缓冲区来存储转换后的字符串,避免了静态缓冲区的共享和竞争条件,可以规避线程安全的问题。

多线程调用 inet_ntoa 代码示例如下:

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>

void *Func1(void *p)
{
    struct sockaddr_in *addr = (struct sockaddr_in *)p;
    while (1)
    {
        char *ptr = inet_ntoa(addr->sin_addr);
        printf("addr1: %s\n", ptr);
    }
    return NULL;
}
void *Func2(void *p)
{
    struct sockaddr_in *addr = (struct sockaddr_in *)p;
    while (1)
    {
        char *ptr = inet_ntoa(addr->sin_addr);
        printf("addr2: %s\n", ptr);
    }
    return NULL;
}

int main()
{
    pthread_t tid1 = 0;
    struct sockaddr_in addr1;
    struct sockaddr_in addr2;
    addr1.sin_addr.s_addr = 0;
    addr2.sin_addr.s_addr = 0xffffffff;
    pthread_create(&tid1, NULL, Func1, &addr1);
    pthread_t tid2 = 0;
    pthread_create(&tid2, NULL, Func2, &addr2);
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    return 0;
}

在该程序测试中并没有出现问题,但这并不意味着 inet_ntoa 函数在多线程环境中是安全的。为了确保线程安全,仍然建议使用线程安全的函数 inet_ntop 来替代 inet_ntoa 。

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

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

相关文章

态路小课堂丨800G QSFP-DD OSFP VR8光模块简介

TARLUZ态路 近年来&#xff0c;随着IDC数据中心的不断升级&#xff0c;我们可以看到大多数企业已经在不断推出 800G系列光模块。其中&#xff0c;800G QSFP-DD/OSFP VR8&#xff08;以下简称800G VR8&#xff09;采用8通道全双工收发模块&#xff0c;能够满足短距离数据中心和云…

腾讯云服务器可用区是什么意思?可用区选择方法

腾讯云服务器可用区是什么意思&#xff1f;云服务器可用区如何选择&#xff1f;可用区是指在同一个地域内电力和网络相互独立的区域&#xff0c;可用区可以做到故障隔离&#xff0c;所以可用区存在的意义在于构建高可用、高容灾应用&#xff0c;将应用部署在不同可用区内&#…

VB.net WebBrowser网页元素抓取分析方法

在用WebBrowser编程实现网页操作自动化时&#xff0c;常要分析网页Html&#xff0c;例如网页在加载数据时&#xff0c;常会显示“系统处理中&#xff0c;请稍候..”&#xff0c;我们需要在数据加载完成后才能继续下一步操作&#xff0c;如何抓取这个信息的网页html元素变化&…

SpringJDBC模板类JdbcTemplate

Spring JdbcTemplate使用JdbcTemplate完成增删改查环境准备新增修改删除查询一个对象批量添加批量修改和批量删除使用德鲁伊连接池&#xff08;之前数据源是用我们自己写的&#xff09; JdbcTemplate JdbcTemplate是Spring提供的一个JDBC模板类&#xff0c;是对JDBC的封装&…

c语言-浅谈指针(2)

文章目录 1.数组名的理解2.使用指针访问数组3.一维数组传参的本质4.二级指针5.指针数组 本篇文章是关于数组与指针的&#xff0c;在上一篇指针内容的基础上进一步了解指针 1.数组名的理解 我们先来了解一个知识点&#xff1a;在给指针变量赋值时&#xff0c;数组用数组名赋给指…

光伏仪器-1763卫星帆板电源阵列模拟器

01 1763卫星帆板电源阵列模拟器 产品综述&#xff1a; 1763卫星帆板电源阵列模拟器用于解决卫星电源系统研制等帆板电源阵列及二次供电设备的测量和分析&#xff0c;解决电源分系统功能验证、现场试验等无法使用真实的帆板电源等带来的难题&#xff0c;用于卫星或卫星测试系…

顺序理清linux下的环境变量

文章目录 关于环境变量概念&#xff08;了解&#xff09;例引 环境变量 查看环境变量的方法echo && printenv 命令 环境变量的配置.bash_profile 常见 环境变量通过程序获取环境变量本地变量环境变量表 关于环境变量 概念&#xff08;了解&#xff09; 环境变量的概念…

有什么进销存软件,比较适合零售行业日常开单要求及库存记录?

本文将为大家总结一下对于进销存软件要求&#xff1a; 基础功能&#xff1a;可以日常开单、退换货处理、出入库进阶功能&#xff1a;电脑、手机数据同步&#xff0c;保障数据安全&#xff0c;可进行数据分析 其实无论是小型创业公司&#xff0c;还是一家大型企业&#xff0c;…

为什么SSL证书会有序列号

SSL证书中的序列号是为了唯一标识该证书。他是用于识别和跟踪证书的唯一标识符。以确保每个证书都具有唯一的值。 有序列号的原因如下&#xff1a; 唯一性&#xff1a;通过序列号&#xff0c;可以确保每个ssl证书都有一个独特的标识符。这对于区分不同的证书非常重要。 跟踪和…

tsconfig.json无法写入文件“XXXX“因为它会覆盖输入文件

在开发ts项目的时候&#xff0c;包错提示无法写入文件&#xff1a; tsconfig.json无法写入文件"XXXX"因为它会覆盖输入文件 这是tsconfig.json文件配置问题&#xff0c;需要加入下面的配置就好了&#xff1a; {"compilerOptions": {"outDir": …

教育案例分享 | 安全狗云安全体系为高校提升立体化纵深防御能力

一、客户情况 某高校有服务器500台&#xff0c;对外站点200个&#xff0c;核心交换流量20G。 二、客户痛点 校园网系统分类较多&#xff0c;并且每类网站中安全级重要程度又各不相同&#xff0c;同时有多个网络出口(如&#xff1a;教育网、电信网、移动网等)&#xff0c;二级学…

常见JMeter面试题

1、什么是JMeter&#xff1f; JMeter是一种开源的性能测试工具&#xff0c;可以用于测试静态和动态资源&#xff0c;如Web应用程序、数据库、FTP服务器等。 2、JMeter可以测试哪些类型的应用&#xff1f; JMeter可以测试各种类型的应用程序&#xff0c;包括Web应用程序、数…

【知网会议征稿】第三届社会科学与人文艺术国际学术会议 (SSHA 2024)

第三届社会科学与人文艺术国际学术会议 (SSHA 2024) 2024 3rd International Conference on Social Sciences and Humanities and Arts 第三届社会科学与人文艺术国际学术会议 (SSHA 2024)于2024年3月1-3日在中国福州举行。会议旨在为从事“社会科学”与“人文艺术”研究的专…

【C++&数据结构】二叉树(结合C++)的经典oj例题 [ 盘点&全面解析 ](24)

前言 大家好吖&#xff0c;欢迎来到 YY 滴数据结构系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴 数据结构 专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; 目录 一.二叉树创建字符串1…

.NET 7 创建Android项目 (拥有原生的界面设计能力,比MAUI更好的性能)

vs2022默认移动开发使用的是maui项目模板&#xff0c;maui确实有很多亮点&#xff0c;就是对比android原生项目性能还需要优化&#xff0c;特别是启动app时无法达到秒开。后来发现vs2022中依然可以直接创建android项目&#xff0c;性能和原生Android基本一致。 1、搜索模板 dot…

办理的流量卡怎么判断是否激活成功?看实名认证还是看充值?

很多朋友可能都会有一个疑问&#xff0c;在网上购买的流量卡怎么才算是激活成功呢&#xff1f;是实名之后就算激活成功&#xff0c;还是需要充值使用才算激活成功呢&#xff1f;这篇文章带大家一块儿来了解一下。 ​  可能很多朋友都会有一个疑问&#xff0c;当你收到流量卡&…

Python开源项目GPEN——人脸重建(Face Restoration),模糊清晰、划痕修复及黑白上色的实践

无论是自己、家人或是朋友、客户的照片&#xff0c;免不了有些是黑白的、被污损的、模糊的&#xff0c;总想着修复一下。作为一个程序员 或者 程序员的家属&#xff0c;当然都有责任满足他们的需求、实现他们的想法。除了这个&#xff0c;学习了本文的成果&#xff0c;或许你还…

Leetcode刷题详解——被围绕的区域

1. 题目链接&#xff1a;130. 被围绕的区域 2. 题目描述&#xff1a; 给你一个 m x n 的矩阵 board &#xff0c;由若干字符 X 和 O &#xff0c;找到所有被 X 围绕的区域&#xff0c;并将这些区域里所有的 O 用 X 填充。 示例 1&#xff1a; 输入&#xff1a;board [["…

漏洞分析 | 漏洞调试的捷径:精简代码加速分析与利用

0x01前言 近期&#xff0c;Microsoft威胁情报团队曝光了DEV-0950&#xff08;Lace Tempest&#xff09;组织利用SysAid的事件。随后&#xff0c;SysAid安全团队迅速启动了应急响应&#xff0c;以应对该组织的攻击手法。然而&#xff0c;在对漏洞的分析和复现过程中&#xff0c…

rabbitmq 集群搭建

RabbitMQ集群介绍 RabbitMQ集群是一组RabbitMQ节点&#xff08;broker&#xff09;的集合&#xff0c;它们一起工作以提供高可用性和可伸缩性服务。 RabbitMQ集群中的节点可以在同一物理服务器或不同的物理服务器上运行。 RabbitMQ集群的工作原理是&#xff0c;每个节点在一个…