【网络】socket和udp协议

socket

  • 一、六个背景知识
    • 1、Q1:在进行网络通信时,是不是两台机器在进行通信?
    • 2、端口号
    • 3、端口号vs进程PID
    • 4、目的端口怎么跟客户端绑定的呢?也就是怎么通过目的端口去找到对应的进程的呢?
    • 5、我们的客户端,怎么知道服务器的端口号的呢?
    • 6、一个进程可以绑定多个端口号吗?一个端口号可以被多个进程绑定吗?
  • 二、两个协议
    • 1、TCP协议(传输控制协议)
    • 2、UDP协议(用户数据报协议)
  • 三、网络字节序列
  • 四、socket编程接口
    • 1、socket 常见API
    • 2、sockaddr结构
  • 五、udp_socket_server代码编写
    • 1、socket套接字的介绍
    • 2、Log.hpp(为了打印看结果)
    • 3、文件描述符socket是3(套接字创建)
    • 4、bind socket
    • 5、recvfrom和sendto
    • 6、测试一下
    • 7、两个问题(port和ip)
    • 8、argc 和 argv的联合使用
    • 9、总体代码
  • 六、udp_socket_client代码编写
    • 1、客户端要绑定吗?
    • 2、代码部分
    • 3、C/S逻辑
    • 4、效果展示
  • 七、改进代码(解耦合)
    • 1、使用functional改进
    • 2、改进使用linux命令(popen)
    • 3、windows与linux进行数据收发
    • 4、udp简易聊天室
  • 八、地址转换函数
    • 1、介绍
    • 2、关于inet_ntoa


一、六个背景知识

1、Q1:在进行网络通信时,是不是两台机器在进行通信?

答:不是的,是应用层在通信。
解析:网络协议中的下三层,主要解决的是数据安全可靠的送到远端机器。用户使用应用层软件完成数据发送和接收的。
而在使用软件的时候,必须得先启动软件,例如我们想刷抖音就需要先把抖音这个软件启动,运行以后就是进程。所以我们网络在进行通信和收发消息的时候,就是进程间通信啊!只不过是进程之间遵守了网络协议栈,用的是网络协议的系统调用接口罢了,其本质还是进程间通信。手段是两台主机通信,而目的和本质是进程之间的通信,是凌驾于应用层上的进程之间的通信。通过网络协议栈读取网络资源(共享内存资源)来让两台主机读取/存放信息。可以用读者写者这个问题来进行理解,我们的网络资源就是缓冲区的概念,读者写者在缓冲区(网络资源)中读取/存放资源。

2、端口号

在这里插入图片描述

端口号是一个2字节16位的整数;
端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理;
IP地址 + 端口号能够标识网络上的某一台主机的某一个进程;
一个端口号只能被一个进程占用。

注意概念:
在公网上,ip地址能标识唯一的一台主机,端口号port能标识该主机上的唯一一个进程,故:ip:port能标识全网唯一的一个进程。

socket:客户端和服务器在进行通信的时候,客户端进程有唯一的ip地址+端口号,服务端进程也有唯一的ip地址+端口号,两者只需要进行源和目的的唯一标识即发生通信,这就是socket的概念基石。

3、端口号vs进程PID

pid已经是能够标识一台主机上的进程的唯一性的,为什么还要搞个端口号这么复杂的概念?
引入端口号是为了实现系统跟网络功能解耦合,不会引起牵一发而动全身,例如我们的学号和身份证件号的关系。

4、目的端口怎么跟客户端绑定的呢?也就是怎么通过目的端口去找到对应的进程的呢?

在这里插入图片描述

5、我们的客户端,怎么知道服务器的端口号的呢?

每一个服务器的端口号必须是众所周知的精心设计的,要被客户端熟知的,我抖音自己的开发商在开发的时候,客户端和服务端的端口号都是内置的,都是被自己熟知的,所以我们使用者感觉不到这个,但是作为开发人员是要熟知服务器的端口号的。

6、一个进程可以绑定多个端口号吗?一个端口号可以被多个进程绑定吗?

显而易见:一个进程可以绑定多个端口号,但一个端口号不能被多个进程绑定。

二、两个协议

1、TCP协议(传输控制协议)

传输层协议
有连接
可靠传输(前提网络要联通,复杂,维护性要更强)
面向字节流

2、UDP协议(用户数据报协议)

传输层协议
无连接
不可靠传输(简单,但丢包问题解决不了)
面向数据报

三、网络字节序列

我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?

发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;
接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;
因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.
TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节.
不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据;
如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可;

为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。

在这里插入图片描述

这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数。
例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;
如果主机是大端字节序,这些 函数不做转换,将参数原封不动地返回。

四、socket编程接口

1、socket 常见API

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器) 
int bind(int socket, const struct sockaddr *address,
 socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,
 socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,
 socklen_t addrlen);

2、sockaddr结构

在这里插入图片描述

问题:为什么struct sockaddr* 不能写成void*呢?
因为刚开始网络出现的时候c语言还没有void*这个用法。

五、udp_socket_server代码编写

1、socket套接字的介绍

在这里插入图片描述

udp用的是SOCK_DGRAM,那么就是无连接不可靠的协议。

2、Log.hpp(为了打印看结果)

#pragma once

#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

#define SIZE 1024

#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4

#define Screen 1
#define Onefile 2
#define Classfile 3

#define LogFile "log.txt"

class Log
{
public:
    Log()
    {
        printMethod = Screen;
        path = "./log/";
    }
    void Enable(int method)
    {
        printMethod = method;
    }
    std::string levelToString(int level)
    {
        switch (level)
        {
        case Info:
            return "Info";
        case Debug:
            return "Debug";
        case Warning:
            return "Warning";
        case Error:
            return "Error";
        case Fatal:
            return "Fatal";
        default:
            return "None";
        }
    }

    // void logmessage(int level, const char *format, ...)
    // {
    //     time_t t = time(nullptr);
    //     struct tm *ctime = localtime(&t);
    //     char leftbuffer[SIZE];
    //     snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),
    //              ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,
    //              ctime->tm_hour, ctime->tm_min, ctime->tm_sec);

    //     // va_list s;
    //     // va_start(s, format);
    //     char rightbuffer[SIZE];
    //     vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);
    //     // va_end(s);

    //     // 格式:默认部分+自定义部分
    //     char logtxt[SIZE * 2];
    //     snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer);

    //     // printf("%s", logtxt); // 暂时打印
    //     printLog(level, logtxt);
    // }
    void printLog(int level, const std::string &logtxt)
    {
        switch (printMethod)
        {
        case Screen:
            std::cout << logtxt << std::endl;
            break;
        case Onefile:
            printOneFile(LogFile, logtxt);
            break;
        case Classfile:
            printClassFile(level, logtxt);
            break;
        default:
            break;
        }
    }
    void printOneFile(const std::string &logname, const std::string &logtxt)
    {
        std::string _logname = path + logname;
        int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); // "log.txt"
        if (fd < 0)
            return;
        write(fd, logtxt.c_str(), logtxt.size());
        close(fd);
    }
    void printClassFile(int level, const std::string &logtxt)
    {
        std::string filename = LogFile;
        filename += ".";
        filename += levelToString(level); // "log.txt.Debug/Warning/Fatal"
        printOneFile(filename, logtxt);
    }

    ~Log()
    {
    }
    void operator()(int level, const char *format, ...)
    {
        time_t t = time(nullptr);
        struct tm *ctime = localtime(&t);
        char leftbuffer[SIZE];
        snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),
                 ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,
                 ctime->tm_hour, ctime->tm_min, ctime->tm_sec);

        va_list s;
        va_start(s, format);
        char rightbuffer[SIZE];
        vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);
        va_end(s);

        // 格式:默认部分+自定义部分
        char logtxt[SIZE * 2];
        snprintf(logtxt, sizeof(logtxt), "%s %s", leftbuffer, rightbuffer);

        // printf("%s", logtxt); // 暂时打印
        printLog(level, logtxt);
    }

private:
    int printMethod;
    std::string path;
};

// int sum(int n, ...)
// {
//     va_list s; // char*
//     va_start(s, n);

//     int sum = 0;
//     while(n)
//     {
//         sum += va_arg(s, int); // printf("hello %d, hello %s, hello %c, hello %d,", 1, "hello", 'c', 123);
//         n--;
//     }

//     va_end(s); //s = NULL
//     return sum;
// }

3、文件描述符socket是3(套接字创建)

在这里插入图片描述
在这里插入图片描述

4、bind socket

在这里插入图片描述

// 2.绑定端口号bind socket
        struct sockaddr_in local; // 网络套接字结构体
        bzero(&local, sizeof(local)); // 将该套接字结构体对象全部清零
        local.sin_family = AF_INET; // 类型:ipv4
        local.sin_port = htons(_port); // 端口号:是在网络中来回发送的,我发过去要让对面知道我发的端口号是什么,所以必须是网络字节序列
        local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 1.string->unit_32 2.来回通信对方要知道发送的ip,所以ip的unit_32必须是网络序列的
        int n = bind(_socketfd, (const struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            log(Fatal, "bind error, erron:%d, errno string:%s", errno, strerror(errno));
            exit(BIND_ERR);
        }
        log(Info, "bind sucess");

在这里插入图片描述

5、recvfrom和sendto

在这里插入图片描述

struct sockaddr_in client;
            socklen_t len = sizeof(client);
            ssize_t n = recvfrom(_socketfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&client, &len);
            if (n < 0)
            {
                log(Warning, "recvfrom error");
                continue;
            }
            // 简单的数据处理一下
            inbuffer[n] = 0;
            std::string info = inbuffer;
            std::string echo_string = "server_echo#" + info;

            sendto(_socketfd, echo_string.c_str(), echo_string.size(), 0, (const struct sockaddr*)&client, len);

6、测试一下

在这里插入图片描述

7、两个问题(port和ip)

Q1.公网ip问题:

ip地址全0,port随意?还是我们ip是云服务器,port随意呢?
在这里插入图片描述
其实就是我们云服务器可能有好几个ip地址能够进行访问,我们单独写一个ip地址肯定是不行的,我们需要一个动态的ip地址,也就是这一个ip地址就能是整个云服务器主机所有暴露出来的ip地址,那么就简单了,我们的ip只要是全0,port随意即可。
在这里插入图片描述
Q2:系统端口号0-1023:
在这里插入图片描述

注意:0到1023是系统内定的系统端口号,一般都要有固定的应用层协议使用,例如:http对应固定端口号是80,https对应固定的端口号是443等等,所以0到1023不让普通用户去绑定,因为与本身的应用层协议是强相关的!这是因为这个应用层协议用这个端口用多了,形成了一种固定的绑定了。所以我们进行端口绑定的时候用的是1024及往上的端口号使用,我们一般用8000到9000左右,好记好用。

8、argc 和 argv的联合使用

在这里插入图片描述

在这里插入图片描述

9、总体代码

udp.hpp:

#pragma once 

#include <iostream>
#include <string>
#include <cstring>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Log.hpp"

extern Log log;

uint16_t defaultport = 8080;
std::string defaultip = "0.0.0.0";

enum 
{
    SOCKET_ERR=1,
    BIND_ERR
};

class UdpServer
{
public:
    // 构造函数
    UdpServer(const uint16_t &port = defaultport, const std::string &ip = defaultip)
        : _socketfd(0)
        , _port(port)
        , _ip(ip)
        , _isrunning(false)
    {}
    void Init()
    {
        // 1.创建udp套接字socket
        _socketfd = socket(AF_INET, SOCK_DGRAM, 0);
        // 创建失败
        if (_socketfd < 0)
        {
            log(Fatal, "socket create error,socketfd:%d", _socketfd);
            exit(SOCKET_ERR);
        }
        // 创建成功
        log(Info, "socket create sucess,socketfd:%d", _socketfd);

        // 2.绑定端口号bind socket
        struct sockaddr_in local; // 网络套接字结构体
        bzero(&local, sizeof(local)); // 将该套接字结构体对象全部清零
        local.sin_family = AF_INET; // 类型:ipv4
        local.sin_port = htons(_port); // 端口号:是在网络中来回发送的,我发过去要让对面知道我发的端口号是什么,所以必须是网络字节序列
        local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 1.string->unit_32 2.来回通信对方要知道发送的ip,所以ip的unit_32必须是网络序列的
        int n = bind(_socketfd, (const struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            log(Fatal, "bind error, erron:%d, errno string:%s", errno, strerror(errno));
            exit(BIND_ERR);
        }
        log(Info, "bind sucess");
    }
    void Run()
    {
        _isrunning = true;
        char inbuffer[1024];
        while (_isrunning)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            ssize_t n = recvfrom(_socketfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&client, &len);
            if (n < 0)
            {
                log(Warning, "recvfrom error");
                continue;
            }
            // 简单的数据处理一下
            inbuffer[n] = 0;
            std::string info = inbuffer;
            std::string echo_string = "server_echo#" + info;

            sendto(_socketfd, echo_string.c_str(), echo_string.size(), 0, (const struct sockaddr*)&client, len);
        }
    }
    // 析构函数
    ~UdpServer()
    {
        if (_socketfd > 0) 
        {
            close(_socketfd);
        }
    }
private:
    int _socketfd; // 网络文件描述符,表示socket返回的文件描述符
    uint16_t _port; // 表明服务器进程的端口号
    std::string _ip; // ip地址,任意地址绑定为0
    bool _isrunning; // 判断是否运行
};

main.cc:

#include "udp.hpp"
#include "Log.hpp"
#include <memory>

Log log;

void Usage(std::string proc)
{
    std::cout << "\n\rUsages: " << proc << "port[1024+]\n" << std::endl;
}

// 以后用的是./udpserver + port
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(0);
    }
    uint16_t port = std::stoi(argv[1]);
    std::unique_ptr<UdpServer> svr(new UdpServer(port)); // new一个对象

    svr->Init(); // 初始化
    svr->Run();  // 跑起来
}

六、udp_socket_client代码编写

1、客户端要绑定吗?

客户端要有具体的ip地址和端口号port的,所以一定是需要绑定的,但是很多人说不需要绑定,这种说法是错误的,因为只是因为客户端不需要用户显示的绑定!一般是由操作系统随机选择的绑定!
解释:一个端口号只能被一个进程绑定,对于server端是如此,对于client端也是如此,而我们计算机上有几百个应用,我们假如说是这前十几个应用把常见的端口号都占了的话,我们后面的应用进程开启来的时候,就不能用这几个常见端口号,打开一次失败一次,试完了这几个常见端口号以后,再用其他冷门的端口号启动就变得很慢,用户自定义的端口号是很粗糙很不安全的,所以由OS操作系统随机的分配更加的安全和有效公平。其实客户端的端口号是多少并不重要,只要能够保证主机上的唯一性即可。

系统什么时候给我绑定端口号的呢?首次发送数据的时候,客户端就进行随机绑定端口号了(即客户端代码跑到sendto的时候)。

2、代码部分

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

void Usage(std::string proc)
{
    std::cout << "\n\rUsages: " << proc << "serverip serverport\n" << std::endl;
}

// ./udpclient serverip serverport
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(0);
    }
    std::string serverip = argv[1]; // serverip
    uint16_t serverport = std::stoi(argv[2]); // serverport

    int socketfd = socket(AF_INET, SOCK_DGRAM, 0); // 创建套接字
    if (socketfd < 0)
    {
        std::cout << "socket create error" << std::endl;
        return 1;
    }

    std::string message;
    char buffer[1024];
    while (true)
    {
        // 数据
        std::cout << "Please Enter# ";
        getline(std::cin, message);
        // 给谁发
        struct sockaddr_in server;
        bzero(&server, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(serverport);
        server.sin_addr.s_addr = inet_addr(serverip.c_str());
        
        // 发送数据 -- 把数据发送到socketfd文件中,并将server信息提炼出来发送给server,可以理解成唤醒server
        socklen_t len = sizeof(server);
        sendto(socketfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, len);

        // 收数据 -- 从socket文件中的数据拿出来到buffer中,并将收到的对方的个人信息进行保存到temp中
        struct sockaddr_in temp;
        socklen_t len2 = sizeof(temp);
        ssize_t n = recvfrom(socketfd, buffer, 1023, 0, (struct sockaddr*)&temp, &len2);
        if (n > 0)
        {
            buffer[n] = 0;
            // 打印数据
            std::cout << buffer << std::endl;
        }
    }

    close(socketfd);
    return 0;
}

3、C/S逻辑

在这里插入图片描述

4、效果展示

在这里插入图片描述

七、改进代码(解耦合)

1、使用functional改进

在这里插入图片描述

2、改进使用linux命令(popen)

介绍127.0.0.1本地环回地址:
通常用来进行本地测试的。

bool SafeCheck(const std::string& cmd)
{
    std::vector<std::string> word_key = {
        "rm",
        "top",
        "cp",
        "yum",
        "while",
        "kill",
        "unlink"
        "uninstall",
        "top"
    };
    for (auto &word : word_key)
    {
        auto pos = cmd.find(word);
        if (pos != std::string::npos)
        {
            return false;
        }
    }
    return true;
}

std::string ExcuteCommand(const std::string& cmd)
{
    // 做一个保护
    if (!SafeCheck(cmd)) return "bad man";
    
    FILE* fp = popen(cmd.c_str(), "r"); // 管道创建好,子进程创建好,子进程通过管道放到父进程
    if (nullptr == fp)
    {
        perror("popen failed");
        return "error";
    }
    std::string result;
    char buffer[4096];
    while (true)
    {
        char* ok = fgets(buffer, sizeof(buffer), fp); // 写到buffer缓冲区中
        if (ok == nullptr)
        {
            break;
        }
        result += buffer;
    }
    pclose(fp);
    return result;
}

在这里插入图片描述

3、windows与linux进行数据收发

互通代码及成果展示

4、udp简易聊天室

udp简易聊天室

八、地址转换函数

1、介绍

字符串转in_addr的函数:
在这里插入图片描述

in_addr转字符串的函数:
在这里插入图片描述

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

2、关于inet_ntoa

inet_ntoa这个函数返回了一个char*, 很显然是这个函数自己在内部为我们申请了一块内存来保存ip的结果. 那么是否需要调用者手动释放呢?
在这里插入图片描述
man手册上说, inet_ntoa函数, 是把这个返回结果放到了静态存储区. 这个时候不需要我们手动进行释放.
那么问题来了, 如果我们调用多次这个函数, 会有什么样的效果呢? 参见如下代码:
在这里插入图片描述

因为inet_ntoa把结果放到自己内部的一个静态存储区, 这样第二次调用时的结果会覆盖掉上一次的结果.

思考: 如果有多个线程调用 inet_ntoa, 是否会出现异常情况呢?
在APUE中, 明确提出inet_ntoa不是线程安全的函数;
但是在centos7上测试, 并没有出现问题, 可能内部的实现加了互斥锁;
同学们课后自己写程序验证一下在自己的机器上inet_ntoa是否会出现多线程的问题;
在多线程环境下, 推荐使用inet_ntop, 这个函数由调用者提供一个缓冲区保存结果, 可以规避线程安全问题;

#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;
}

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

uniapp小程序上传pdf文件

<template><view class"mainInnBox"><view class"formBox"><!-- 注意&#xff0c;如果需要兼容微信小程序&#xff0c;最好通过setRules方法设置rules规则 --><u-form :model"form" ref"uForm" :rules&quo…

Window版本nginx修改文件访问句柄数被限制,解决大并发量访问时无法响应问题

目录 一、问题背景 二、问题分析 三、解决办法 一、问题背景 Windows版本因为文件访问句柄数被限制为1024了&#xff0c;当大并发量访问时就会无法响应。会有如下错误提示&#xff1a;maximum number of descriptors supported by select() is 1024 while connecting to ups…

C# 基础语法(一篇包学会的)

C#&#xff08;读作"C Sharp"&#xff09;是一种现代的、通用的面向对象编程语言&#xff0c;由微软公司开发。它结合了C和C的强大特性&#xff0c;并去掉了一些复杂性&#xff0c;使得开发者可以更加高效地编写代码。 一、入坑C# (一) 安装和设置 首先&#xff0c…

【LeetCode】从中序与后序遍历序列构造二叉树

目录 一、题目二、解法完整代码 一、题目 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二叉树的中序遍历&#xff0c; postorder 是同一棵树的后序遍历&#xff0c;请你构造并返回这颗 二叉树 。 示例 1: 输入&#xff1a;inorder [9,3,15,20,7], …

鸿蒙OpenHarmony Native API【Rawfile】

Rawfile Overview Description: 提供操作rawfile目录和rawfile文件的功能 提供操作rawfile目录和rawfile文件功能 功能包括遍历、打开、搜索、读取和关闭rawfile Since: 8 Version: 1.0 Summary Files File NameDescription[raw_dir.h]提供rawfile目录相关功能[raw…

Debian Linux下rclone挂载谷歌云盘碰到的坑

可能是明月好久没有使用境外服务器挂载境外的云盘缘故吧,今天一个代维客户需要他的Linux服务器挂载谷歌云盘好进行云备份,本来是个很简单的事儿,没想到在rclone连接谷歌云盘的时候卡壳了,可是把明月给难为坏了,搜索到的简体中文教程倒是很多,但没有一个提到这个“坑”,最…

Docker启动PostgreSql并设置时间与主机同步

在 Docker 中启动 PostgreSql 时&#xff0c;需要配置容器的时间与主机同步。可以通过在 Dockerfile 或者 Docker Compose 文件中设置容器的时区&#xff0c;或者使用宿主机的时间来同步容器的时间。这样可以确保容器中的 PostgreSql 与主机的时间保持一致&#xff0c;避免在使…

C语言数组的相关案例

引导案例&#xff1a; 数组的遍历&#xff1a;这里需要注意的是我们在遍历数组时是使用for循环&#xff0c;这里则需要计算数组的长度 计算公式&#xff1a;sizeof(数组名) / sizeof(数组的数据类型) #include<stdio.h> int main() {int arr[] { 1,2,3,4,5,6,7,8 ,9,…

大厂面试-基本功

大厂面试第4季 服务可用性多少个9是什么意思遍历集合add或remove操作bughashcode冲突案例BigdecimalList去重复IDEA Debugger测试框架ThreaLocal父子线程数据同步 InheritableThreadLocal完美解决线程数据同步方案 TransmittableThreadLocal 服务可用性多少个9是什么意思 遍历集…

增值税发票核验API在Java、Python、PHP中的使用教程

在企业经营中&#xff0c;发票扮演着记录交易、报销和纳税的重要角色。然而&#xff0c;由于发票的众多类型和复杂的制作方式&#xff0c;一些企业可能面临着假发票、冒充发票等风险。为了提高财务管理的效率和准确性&#xff0c;以及防范不法行为&#xff0c;增值税发票核验成…

定制QCustomPlot 带有ListView的QCustomPlot 全网唯一份

定制QCustomPlot 带有ListView的QCustomPlot 文章目录 定制QCustomPlot 带有ListView的QCustomPlot摘要需求描述实现关键字: Qt、 QCustomPlot、 魔改、 定制、 控件 摘要 先上效果,是你想要的,再看下面的分解,顺便点赞搜藏一下;不是直接右上角。 QCustomPlot是一款…

Jenkins-zookeeper-docker-xxljob-rancher

文章目录 Jenkins实战1 新建任务需要的配置pipeline Zookeeper基础 Docker基础实操windows11 docker mysql DockerhouseDockerhubxxl-Job基础实战 Rancher基础思考 实战1 Rancher的某个namespace的scale为0 Jenkins 实战 1 新建任务需要的配置pipeline 该代码是Jenkinsfile&…

【接口自动化_08课_Pytest+Yaml+Allure框架】

上节课一些内容 的补充 1、openxl这个方法&#xff0c;第一个元素是从1开始的&#xff0c;不是从0开始 回写的列在程序里写的是11&#xff0c;是因为是固定值 一、1. Yaml入门及应用 1、什么是yaml YAML&#xff08;/ˈjməl/&#xff0c;尾音类似camel骆驼&#xff09;是一…

探索Python日志管理的优雅之道:Loguru库入门指南

探索Python日志管理的优雅之道&#xff1a;Loguru库入门指南 背景&#xff1a;为何选择Loguru&#xff1f; 在Python开发过程中&#xff0c;日志记录是不可或缺的一部分&#xff0c;它帮助我们追踪程序的运行状态&#xff0c;调试程序错误&#xff0c;并记录关键信息。然而&am…

【Linux】-----权限详解

目录 一、Linux下的权限概念 Ⅰ、是什么&#xff1f; Ⅱ、Linux下的两种角色 角色 如何添加普通用户 身份的转化方式 身份的提权 添加普通用户至白名单 二、Linux下的权限管理 Ⅰ、文件访问者的分类(Linux下的“人”) Ⅱ、文件类型和访问权限(事物属性) 1.文件类型 …

使用AOP优化Spring Boot Controller参数:自动填充常用字段的技巧

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 &#x1f38f;&#xff1a;你只管努力&#xff0c;剩下的交给时间 &#x1f3e0; &#xff1a;小破站 使用AOP优化Spring Boot Controller参数&#xff1a;自动填充常用字段的技巧 前言为什么使用AOP为…

2-41 基于matlab的小车倒立摆系统的控制及GUI动画演示

基于matlab的小车倒立摆系统的控制及GUI动画演示。输入小车及倒立摆的初始参数&#xff0c;位置参数&#xff0c;对仿真时间和步长进行设置&#xff0c;通过LQR计算K值&#xff0c;进行角度、角速度、位置、速度仿真及曲线输出&#xff0c;程序已调通&#xff0c;可直接运行。 …

昇思MindSpore学习总结十七 —— 基于MindSpore通过GPT实现情感分类

1、要求 2、导入了一些必要的库和模块 以便在使用MindSpore和MindNLP进行深度学习任务时能使用各种功能&#xff0c;比如数据集处理、模型训练、评估和回调功能。 import os # 导入操作系统相关功能的模块&#xff0c;如文件和目录操作import mindspore # 导入MindSpore库&a…

HTTPServer改进思路2(mudou库核心思想融入)

mudou网络库思想理解 Reactor与多线程 服务器构建过程中&#xff0c;不仅仅使用一个Reactor&#xff0c;而是使用多个Reactor&#xff0c;每个Reactor执行自己专属的任务&#xff0c;从而提高响应效率。 首先Reactor是一种事件驱动处理模式&#xff0c;其主要通过IO多路复用…