【网络】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 socketstruct sockaddr_in local; // 网络套接字结构体bzero(&local, sizeof(local)); // 将该套接字结构体对象全部清零local.sin_family = AF_INET; // 类型:ipv4local.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 socketstruct sockaddr_in local; // 网络套接字结构体bzero(&local, sizeof(local)); // 将该套接字结构体对象全部清零local.sin_family = AF_INET; // 类型:ipv4local.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地址,任意地址绑定为0bool _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]; // serveripuint16_t serverport = std::stoi(argv[2]); // serverportint 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,可以理解成唤醒serversocklen_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;
}

在这里插入图片描述

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

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

相关文章

区间加减使得数组变成指定类型

这个问题要怎么去考虑呢&#xff0c;首先我们将两个数组做差得到相对大小&#xff0c;问题就变成了把我们构造的数组通过区间加一或者区间减一变成全部都是0的最小次数 这里就涉及到我们的一个技巧&#xff0c;我们需要把负数序列和正数序列分开处理&#xff0c;如何能得到最小…

【C++】一、Visual Studio 2017使用教程:内存窗口、预处理文件、obj文件,调试优化

文章目录 概述编译期&#xff08;Compile&#xff09;查看预处理后的文件查看obj文件开启编译器调试优化 链接期&#xff08;Linking&#xff09;报错信息概述自定义入口点 调试内存窗口值转16进制查看查看汇编代码 注意 概述 记录一下Cherno的vs配置下载地址 https://thecher…

Unity 调试死循环程序

如果游戏出现死循环如何调试呢。 测试脚本 我们来做一个测试。 首先写一个死循环代码&#xff1a; using System.Collections; using System.Collections.Generic; using UnityEngine;public class dead : MonoBehaviour {void Start(){while (true){int a 1;}}}Unity对象设…

Qt 4.8.7 + MSVC 中文乱码问题深入分析

此问题很常见&#xff0c;然而网上关于此问题的分析大多不够深刻&#xff0c;甚至有错误&#xff1b;加之Qt5又更改了一些编码策略&#xff0c;而很多文章并未提及版本问题&#xff0c;或是就算提了&#xff0c;读者也不重视。这些因素很容易让读者产生误导。今日我彻底研究透了…

html5——CSS背景属性设置

目录 背景颜色 background-color 背景图像 背景定位 背景样式简写 背景尺寸 ​编辑渐变属性 背景颜色 background-color 背景图像 background-image background-image:url(图片路径); 背景重复方式&#xff1a; background-repeat 属性&#xff1a; repeat&#…

Qt中在pro中实现一些宏定义

在pro文件中利用 DEFINES 定义一些宏定义供工程整体使用。&#xff08;和在cpp/h文件文件中定义使用有点类似&#xff09;可以利用pro的中的宏定义实现一些全局的判断 pro中实现 #自定义一个变量 DEFINES "PI\"3.1415926\"" #自定义宏 DEFINES "T…

Apache Flink 任务提交模式

Flink 任务提交模式 Flink可以基于多种模式部署&#xff1a;基于Standalone 部署模式&#xff0c;基于Yarn部署模式&#xff0c;基于Kubernetes部署模式以上不同集群部署模式下提交Flink任务会涉及申请资源&#xff0c;各角色交互过程&#xff0c;不同模式申请资源涉及到的角色…

2024信息创新与安全技术比赛规程及任务书

2024信息创新与安全技术比赛规程任务书 模块一&#xff1a;信创操作系统应用任务一&#xff1a;系统安装任务二&#xff1a;系统基本操作&#xff0c;以下操作都在Client-1进行。任务三&#xff1a;软件管理 模块二&#xff1a;办公软件技术应用任务一&#xff1a;文档编辑任务…

【栈和队列】算法题 ---- 力扣

通过前面栈和队列的学习&#xff0c;现在来看这些算法题目 一、有效的括号 本题让判断括号是否有效 第一眼看可能没一点思路&#xff0c;但仔细分析一下&#xff1b; 我们学习过栈数据结构&#xff0c;知道栈先进后出的原则&#xff0c;那我们就可以使用啊&#xff1b;把题目的…

MaxSite CMS v180 文件上传漏洞(CVE-2022-25411)

前言 CVE-2022-25411 是一个影响 Maxsite CMS v180 的远程代码执行漏洞。攻击者可以通过上传一个特制的 PHP 文件来利用这个漏洞&#xff0c;从而在受影响的系统上执行任意代码。 漏洞描述 该漏洞存在于 Maxsite CMS v180 的文件上传功能中。漏洞利用主要通过允许上传带有危…

嵌入式人工智能(10-基于树莓派4B的DS1302实时时钟RTC)

1、实时时钟&#xff08;Real Time Clock&#xff09; RTC&#xff0c;全称为实时时钟&#xff08;Real Time Clock&#xff09;&#xff0c;是一种能够提供实时时间信息的电子设备。RTC通常包括一个计时器和一个能够记录日期和时间的电池。它可以独立于主控芯片工作&#xff…

C语言函数:编程世界的魔法钥匙(2)-学习笔记

引言 注&#xff1a;由于这部分内容比较抽象&#xff0c;而小编我又是一个刚刚进入编程世界的计算机小白&#xff0c;所以我的介绍可能会有点让人啼笑皆非。希望大家多多包涵&#xff01;万分感谢&#xff01;待到小编我学有所成&#xff0c;一定会把这块知识点重新介绍一遍&a…

[Day 32] 區塊鏈與人工智能的聯動應用:理論、技術與實踐

AI中的神經網絡技術 神經網絡&#xff08;Neural Networks&#xff09;是人工智能&#xff08;AI&#xff09;領域的一個重要分支&#xff0c;靈感來自於生物神經系統。本文將深入探討神經網絡的基本概念、結構、工作原理及其在AI中的應用&#xff0c;並通過Python代碼詳細解釋…

HarmonyOS Web组件(二)

1. HarmonyOS Web组件 官方文档 1.1. 混合开发的背景和好处 混合开发&#xff08;Hybrid Development&#xff09;是一种结合原生应用和Web应用的开发模式&#xff0c;旨在同时利用两者的优势。随着移动应用需求的多样化和复杂化&#xff0c;单一的开发方式往往难以满足所有…

sass版本更新,不推荐使用嵌套规则后的声明

目前在 Sass 中不推荐使用嵌套规则后的声明&#xff0c;在 为了通知用户即将进行的更改&#xff0c;并给他们时间进行更改 与之兼容的样式表。在未来的版本中&#xff0c;Dart Sass 将更改为 匹配纯 CSS 嵌套生成的顺序。Deprecation Warning: Sasss behavior for declarations…

Pytorch学习笔记【B站:小土堆】

文章目录 1 基础环境配置&#xff08;CPU版&#xff09;2 PyTorch学习2.1 Dataset和DataLoader2.1.1 Dataset2.1.2 DataLoader 2.2 Tensorboardadd_scalaradd_imageadd_graph 2.3 Transforms2.3.1 ToTensor2.3.2 Normalize2.3.3 Resize2.3.4 Compose 2.4 torchvision中的数据集…

pnpm build打包时占内溢出

这两天在打包H5网页的时候失败&#xff0c;总是提示下方错误 FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory 严重错误&#xff1a;堆限制附近标记压缩无效分配失败 - JavaScript 堆内存不足 尝试了多种方法&…

Linux源码安装的Redis如何配置systemd管理并设置开机启动

文章目录 实验前提实验 实验前提 已完成源码安装并能正常启动redis /usr/local/bin/redis-server能正常启动redis 实验 vim /etc/systemd/system/redis.service内容如下&#xff1a; [unit] Descriptionredis-server Afternetwork.target[Service] Typeforking ExecStart/…

【Blockly图形化积木编程二次开发学习笔记】5.自动保存与恢复

文章目录 引用使用 引用 <script src"./blockly/appengine/storage.js"></script>使用 <script>window.setTimeout(BlocklyStorage.restoreBlocks, 0); // 从本地存储中恢复块BlocklyStorage.backupOnUnload(); // 用户离开页面时自动将块备份到…

降雨量预测 | Matlab基于ARIMA-RBF降雨量预测

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 降雨量预测 | Matlab基于ARIMA-RBF降雨量预测 注&#xff1a;程序和数据放在一个文件夹。 程序语言为matlab&#xff0c;程序可出预测效果图&#xff0c;指标图; 代码特点&#xff1a;参数化编程、参数可方便更改、代…