《TCP/IP网络编程》学习笔记 | Chapter 1:理解网络编程和套接字
《TCP/IP网络编程》学习笔记 | Chapter 1:理解网络编程和套接字
- 《TCP/IP网络编程》学习笔记 | Chapter 1:理解网络编程和套接字
- 基本概念
- 服务端
- 客户端
- 基于 Linux 平台的 "Hello world!" 服务端和客户端
- 基于 Linux 的文件操作
- 打开文件
- 关闭文件
- 写文件
- 读文件
- 基于 Windows 平台的实现
- Winsock 的初始化
- 注销 Winsock 相关库
- 基于 Windows 的套接字相关函数
- 创建基于 Windows 的服务端和客户端
- 基于 Windows 的 I/O 函数
- 习题
- (1)套接字在网络编程中的作用是什么?为什么称它为套接字?
- (2)在服务器端创建套接字后,会依次调用listen函数和accept函数。请比较并说明两者作用。
- (3)Linux中,对套接字数据进行I/O时可以直接使用I/O相关函数;而在Windows中则不可以。原因为何?
- (4)创建套接字后一般会给它分配地址,为什么?为了完成地址分配需要调用哪些函数?
- (5)Linux中的文件描述符与Windows的句柄实际上非常类似。请以套接字为对象说明他们的含义。
- (6)底层文件I/O函数与ANSI标准定义的文件I/O函数之间有何区别?
《TCP/IP网络编程》学习笔记 | Chapter 1:理解网络编程和套接字
基本概念
网络编程是什么?
编写程序使两台联网的计算机相互交换数据。
服务端
步骤 1:创建套接字
int socket(int domain, int type, int protocol);
成功时返回文件描述符,失败时返回-1。
步骤 2:绑定IP地址和端口号
int bind(int socket, const struct sockaddr *address, socklen_t address_len);
成功时返回0,失败时返回-1
步骤 3:使转化为可接受请求状态
int listen(int sockfd, int backlog);
成功时返回文件描述符,失败时返回-1。
步骤 4:受理请求连接
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
成功时返回文件描述符,失败时返回-1。
客户端
步骤 1:创建socket
步骤 2:发起连接请求
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
成功时返回0,失败返回-1。
基于 Linux 平台的 “Hello world!” 服务端和客户端
服务端程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>void error_handling(char* message);int main(int argc,char* argv[])
{int sock;struct sockaddr_in serv_addr;char message[30];int str_len;if(argc!=3){printf("Usage:%s <IP> <port>\n",argv[0]);exit(1);}sock = socket(PF_INET,SOCK_STREAM,0);if(sock==-1){error_handling("socket() error!");}memset(&serv_addr,0,sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr(argv[1]);serv_addr.sin_port=htons(atoi(argv[2]));if(connect(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr))==-1){error_handling("connect() error!");}str_len = read(sock,message,sizeof(message)-1);if(str_len==-1){error_handling("read() error!");}printf("Message from server:%s \n",message);close(sock);return 0;
}void error_handling(char* message)
{fputs(message,stderr);fputc('\n',stderr);exit(1);
}
客户端程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>void error_handling(char* message);int main(int argc,char* argv[])
{int sock;struct sockaddr_in serv_addr;char message[30];int str_len;if(argc!=3){printf("Usage:%s <IP> <port>\n",argv[0]);exit(1);}sock = socket(PF_INET,SOCK_STREAM,0);if(sock==-1){error_handling("socket() error!");}memset(&serv_addr,0,sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr(argv[1]);serv_addr.sin_port=htons(atoi(argv[2]));if(connect(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr))==-1){error_handling("connect() error!");}str_len = read(sock,message,sizeof(message)-1);if(str_len==-1){error_handling("read() error!");}printf("Message from server:%s \n",message);close(sock);return 0;
}void error_handling(char* message)
{fputs(message,stderr);fputc('\n',stderr);exit(1);
}
基于 Linux 的文件操作
Linux中一切皆文件,socket自然也是文件。
每当生成文件或socket,操作系统都将返回分配给它们的整数(文件描述符),文件描述符只不过是为了方便的称呼操作系统创建的文件或socket而赋予的数。
分配给标准输入,输出及标准错误的文件描述符。
文件描述符 | 对象 |
---|---|
0 | 标准输入 :Standard Input |
1 | 标准输出 :Standard Ouput |
2 | 标准错误 :Standard Error |
其他的文件描述符从3开始的。
打开文件
#include <sys/stat.h>
#include <fcntl.h>int open(const char *path, int fla);
成功时返回文件描述符,失败时返回-1。
参数:
-
path: 文件名的字符串地址
-
flag: 文件打开模式信息,如需传递多个参数,应通过 | 运算符组合。
打开模式 | 含义 |
---|---|
O_CREAT | 必要时创建文件 |
O_TRUNC | 删除全部现有数据 |
O_APPEND | 维持现有数据,保存到其后面 |
O_RDONLY | 只读打开 |
O_WRONLY | 只写打开 |
O_RDWR | 读写打开 |
关闭文件
#include <unistd.h>int close(int fildes);
成功时返回0,失败时返回-1。
参数:
- fildes: 需要关闭的文件或socket的文件描述符
写文件
#include <unistd.h>ssize_t write(int fildes, const void *buf, size_t nbyte);
成功时返回写入的字节数,失败时返回-1。
参数:
-
fildes: 显示数据传输对象的文件描述符
-
buf:保存要传输数据的缓冲地址值
-
nbyte: 要传输数据的字节数
size_t : unsigned int,ssize_t:signed int,这些都是元数据类型,由操作系统定义,通过 typedef 声明。
示例程序:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>void error_handling(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}int main(void)
{int fd;char buf[] = "Let's go!\n";fd = open("data.txt", O_CREAT | O_WRONLY | O_TRUNC);if (fd == 1)error_handling("open() error!");printf("file descriptor: %d\n", fd);if (write(fd, buf, sizeof(buf)) == -1)error_handling("write() error!");close(fd);return 0;
}
运行结果:
注意:Windows 端可以用 type 命令打印 txt 文件的内容。
读文件
#include <unistd.h>ssize_t read(int fildes, void *buf, size_t nbyte);
成功时返回接收的字节数(但遇到文件结尾则返回0),失败时返回-1。
参数:
-
fildes:显示数据接收对象的文件描述符
-
buf:要保存接收数据的缓冲地址值
-
nbytes:要接收数据的最大字节数
示例程序:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>#define BUF_SIZE 100void error_handling(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}int main(void)
{int fd;char buf[BUF_SIZE];fd = open("data.txt", O_RDONLY);if (fd == -1)error_handling("open() error!");printf("file descriptor: %d\n", fd);if (read(fd, buf, BUF_SIZE) == -1)error_handling("read() error!");printf("file data: %s", buf);close(fd);return 0;
}
运行结果:
基于 Windows 平台的实现
Windows 套接字,简称 Winsock,大部分参考 BSD 系列 UNIX 套接字设计,和 Linux 套接字类似。
Winsock 的初始化
进行Winsock编程时,首先调用 WSAStartup 函数,设置程序中用到的Winsock版本,并初始化相应版本的库。
int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData);
参数:
- wVersionRequested:Winsock版本信息
- lpWSAData:WSADATA结构体变量的地址值
Winsock中存在多个版本,应准备WORD(typedef unsigned int WORD;)类型的套接字版本信息,若版本为1.2,则1是主版本号,2是副版本号,应传递 0x0201
0x0201 高8位为副版本号,低8位为主版本号。这里借助 MAKEWORD 宏函数来传递版本信息号。
MAKEWORD(1,2); // 主版本号是1,副版本号是2,返回0x0201
int main(int argc,char* argv[])
{WSADATA wsaData;....if(WSAStartup(MAKEWORD(1,2), &wsaData) != 0){....}return 0;
}
注销 Winsock 相关库
#include <Winsock2.h>int WSACleanup(void);
成功时返回0,失败时返回 SOCKET_ERROR。
基于 Windows 的套接字相关函数
创建socket:
SOCKET socket(int af,int type,int protocol);
成功时返回套接字句柄(对应linux中的文件描述符),失败返回 INVALID_SOCKET。
绑定IP和端口号:
int bind(SOCKET s,const struct sockaddr* name,int namelen);
成功返回0,失败返回SOCKET_ERROR。
将服务端设置为可接受请求状态:
int listen(SOCKET s,int backlog);
成功返回0,失败返回SOCKET_ERROR。
受理请求连接(accept):
SOCKET accept(SOCKET s,struct sockaddr* addr,int* addrlen);
成功时返回套接字句柄,失败时返回INVALID_SOCKET。
从客户端发起请求连接:
int connect(SOCKET s,const struct sockaddr* name,int namelen);
成功时返回0,失败时返回SOCKET_ERROR。
关闭套接字:
int closesocket(SOCKET s);
成功时返回0,失败时返回SOCKET_ERROR。
Windows中的句柄相当于linux中的文件描述符,但是Windows中的文件句柄和套接字句柄是有区别的。
创建基于 Windows 的服务端和客户端
服务端:
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>void ErrorHanding(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}int main(int argc, char *argv[])
{WSADATA wsaData;SOCKET hServerSock, hClientSock;SOCKADDR_IN serverAddr, clientAddr;int szClientAddr;char message[] = "Hello World!";if (argc != 2){printf("Usage: %s <port>\n", argv[0]);exit(1);}if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)ErrorHanding("WSAStartup() error!");hServerSock = socket(PF_INET, SOCK_STREAM, 0);if (hServerSock == INVALID_SOCKET)ErrorHanding("socket() error!");memset(&serverAddr, 0, sizeof(serverAddr));serverAddr.sin_family = AF_INET;serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);serverAddr.sin_port = htons(atoi(argv[1]));if (bind(hServerSock, (SOCKADDR *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)ErrorHanding("bind() error!");if (listen(hServerSock, 5) == SOCKET_ERROR)ErrorHanding("listen() error!");szClientAddr = sizeof(clientAddr);hClientSock = accept(hServerSock, (SOCKADDR *)&clientAddr, &szClientAddr);if (hClientSock == INVALID_SOCKET)ErrorHanding("accept() error!");send(hClientSock, message, sizeof(message), 0);closesocket(hClientSock);closesocket(hServerSock);WSACleanup();return 0;
}
客户端:
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>void ErrorHanding(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}int main(int argc, char *argv[])
{WSADATA wsaData;SOCKET hSocket;SOCKADDR_IN serverAddr;char message[30];int strLen;if (argc != 3){printf("Usage: %s <IP> <port>\n", argv[0]);exit(1);}if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)ErrorHanding("WSAStartup() error!");hSocket = socket(PF_INET, SOCK_STREAM, 0);if (hSocket == INVALID_SOCKET)ErrorHanding("socket() error!");memset(&serverAddr, 0, sizeof(serverAddr));serverAddr.sin_family = AF_INET;serverAddr.sin_addr.s_addr = inet_addr(argv[1]);serverAddr.sin_port = htons(atoi(argv[2]));if (connect(hSocket, (SOCKADDR *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)ErrorHanding("connect() error!");strLen = recv(hSocket, message, sizeof(message) - 1, 0);if (strLen == -1)ErrorHanding("recv() error!");printf("Message from server: %s\n", message);closesocket(hSocket);WSACleanup();return 0;
}
编译遇到报错:
参考 【三种解决方法】undefined reference to `__imp_WSAStartup‘ 给出的解决办法,手动添加编译参数 -lwsock32:
gcc hello_server_win.c -lwsock32 -o hServerWingcc hello_client_win.c -lwsock32 -o hClientWin
编译就可以成功了:
我们运行服务端程序,监听 9190 端口:
hServerWin 9190
再运行客户端程序:
hClientWin 127.0.0.1 9190
得到输出:
Message from server: Hello World!
基于 Windows 的 I/O 函数
Linux中socket也是文件,因此可以通过文件I/O函数read和write进行数据传输。而Windows严格区分文件I/O函数和套接字I/O函数。
int send(SOCKET s,const char* buf,int len,int flags);
成功时返回传输字节数,失败时返回SOCKET_ERROR。
参数:
- s:表示数据传输对象连接的套接字句柄值
- buf:保存待传输数据的缓冲地址值
- len:要传输的字节数
- flags:传输数据时用到的多种选项信息
int recv(SOCKET s, const char* buf, int len, int flags);
成功时返回接收的字节数(收到EOF时为0),失败返回SOCKET_ERROR。
- s:表示数据接收对象连接的套接字句柄值
- buf:保存待接收数据的缓冲地址值
- len:要接收的字节数
- flags:接收数据时用到的多种选项信息
习题
(1)套接字在网络编程中的作用是什么?为什么称它为套接字?
网络编程就是编写程序让两台联网的计算机相互交换数据。在我们不需要考虑物理连接的情况下,我们只需要考虑如何编写传输软件。操作系统提供了名为“套接字”,套接字是网络传输传输用的软件设备。
socket英文原意是插座,我们把插头插到插座上就能从电网获得电力供给,同样,为了与远程计算机进行数据传输,需要连接到Internet,而变成中的“套接字”就是用来连接该网络的工具。
(2)在服务器端创建套接字后,会依次调用listen函数和accept函数。请比较并说明两者作用。
listen:将套接字转为可接受连接方式(监听套接字)
accept:受理连接请求,并且在没有连接请求的情况调用该函数,不会返回(阻塞)。直到有连接请求为止。二者存在逻辑上的先后关系。
(3)Linux中,对套接字数据进行I/O时可以直接使用I/O相关函数;而在Windows中则不可以。原因为何?
Linux把套接字也看作是文件,所以可以用文件I/O相关函数;而Windows要区分套接字和文件,所以设置了特殊的函数。
(4)创建套接字后一般会给它分配地址,为什么?为了完成地址分配需要调用哪些函数?
要在网络上区分来自不同机器的套接字,所以需要地址信息。分配地址是通过bind()函数实现。
(5)Linux中的文件描述符与Windows的句柄实际上非常类似。请以套接字为对象说明他们的含义。
Linux的文件描述符是为了区分指定文件而赋予文件的整数值(相当于编号)。Windows的文件描述符其实也是套接字的整数值,其目的也是区分指定套接字。
(6)底层文件I/O函数与ANSI标准定义的文件I/O函数之间有何区别?
- ANSI标准定义的输入、输出函数是与操作系统(内核)无关的以C标准写成的函数;相反,底层文件I/O函数是操作系统直接提供的。
- 标准I/O分为全缓冲,行缓冲,不缓冲三种形式;文件I/O为不带缓冲的I/O。
- 文件I/O主要针对文件操作,它操作的是文件描述符;标准I/O针对的是控制台,它操作的是字符流。对于不同设备得特性不一样,必须有不同API访问才最高效。
相关文章:

《TCP/IP网络编程》学习笔记 | Chapter 1:理解网络编程和套接字
《TCP/IP网络编程》学习笔记 | Chapter 1:理解网络编程和套接字 《TCP/IP网络编程》学习笔记 | Chapter 1:理解网络编程和套接字基本概念服务端客户端 基于 Linux 平台的 "Hello world!" 服务端和客户端基于 Linux 的文件操作打开文件关闭文件…...

服务端监控工具:Nmon使用方法
在性能测试过程中,对服务端的各项资源使用情况进行监控是很重要的一环。这篇博客,介绍下服务端监控工具:nmon的使用方法。 一、认识nmon 1、简介 nmon是一种在AIX与各种Linux操作系统上广泛使用的监控与分析工具,它能在系统运行…...

Java中的线程安全问题(如果想知道Java中有关线程安全问题的基本知识,那么只看这一篇就足够了!)
前言:多线程编程已经广泛开始使用,其可以充分利用系统资源来提升效率,但是线程安全问题也随之出现,它直接影响了程序的正确性和稳定性,需要对其进行深入的理解与解决。 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解…...
基础设施即代码(IaC)在Python自动化运维中的应用探讨
基础设施即代码(IaC)在Python自动化运维中的应用探讨 目录 🌐 IaC概念与工具介绍🐍 使用Python实现基础设施自动化📦 版本控制与基础设施管理的最佳实践🔄 部署环境的一致性与可复现性 1. 🌐 …...
浅谈路由器
路由器是一种网络设备,它在网络中起着至关重要的作用,主要功能包括: 1、数据转发:路由器的主要任务是将数据包从一个网络转发到另一个网络。它根据数据包的目的地址来决定将数据包发送到哪个网络。 2、路径选择:路由器…...

openGauss数据库-头歌实验1-1 初识openGauss
一、历史与特性 任务描述 本关任务:了解openGauss的发展历史以及相关特性。 相关知识 为了完成本关任务,你需要掌握:1.openGauss的发展历程,2.openGauss的功能特性。 发展历程 2019年9月19日在华为全联接大会上,…...

QT找不到ffmpeg链接库解决方法
error: undefined reference to avformat_network_init() 一个神奇的报错,查了很久,检查步骤: 1、检查了 pro工程文件 2、链接库的真实性和正确性 在main.cpp中调用没有报错,在其它cpp文件中调用就报错。 破案了,…...

消息队列-Rabbitmq(消息发送,消息接收)
将来我们开发业务功能的时候,肯定不会在控制台收发消息,而是应该基于编程的方式。由于RabbitMQ采用了AMQP协议,因此它具备跨语言的特性。任何语言只要遵循AMQP协议收发消息,都可以与RabbitMQ交互。并且RabbitMQ官方也提供了各种不…...

2、顶点着色器之视图矩阵
1、作用:将物体从世界坐标系转换到相机坐标系,相当于从世界坐标系转换到相机的局部(本地)坐标系。 2、基于LookAt函数的视图矩阵: 相机位置eye:(ex,ey,ez),世界坐标系下的位置 目标位置center:(cx,cy,cz…...
crontab实现2026年开始每个月1号执行一次
要在 crontab 中设置一个任务,使其从 2026 年开始每个月的 1 号执行一次,可以使用以下格式: 0 0 1 * * <你的命令>这条规则的解释如下: 0 0:表示在每个月的 1 号的零点(00:00)执行。1&a…...

计算机网络803-(5)运输层
目录 一.运输层的两个主要协议:TCP 与 UDP 1.TCP/IP 的运输层有两个不同的协议: 2.端口号(protocol port number) (1)软件端口与硬件端口 (2)TCP 的端口 (3)三类端口 二.用户…...

八 MyBatis中接口代理机制及使用
八、MyBatis中接口代理机制及使用 实际上,第七章所讲内容mybatis内部已经实现了。直接调用以下代码即可获取dao接口的代理类: AccountDao accountDao (AccountDao)sqlSession.getMapper(AccountDao.class);使用以上代码的前提是:AccountMa…...

【解决】Ubuntu18.04 卸载python之后桌面异常且终端无法打开,重启后进入tty1,没有图形化界面
我因为python版本太过于混乱 (都是为了学习os) ,3.6—3.9版本我都安装了,指向关系也很混乱,本着“重装是最不会乱”的原则,我把全部版本都卸载了。然后装了3.9 发现终端打不开了,火狐浏览器的图…...

OpenEmbedded、yocto和poky是什么关系?
Yocto项目是基于OpenEmbedded构建系统发展而来的。Yocto采用了OpenEmbedded的许多核心概念和工具,比如BitBake构建工具。BitBake在这两个系统中都是用于解析和处理recipes文件,这些recipes文件包含了软件包构建的指令、依赖关系、安装步骤等内容。 它们…...

记录页面——一个蛮好看的登录页(uni-app)
效果图 <template><view class"container"><view class"flex-col login-box"><view class"flex-col" style"width: 80%"><view class"flex-col"><text class"welcome-text-font&qu…...

Android文件选择器[超级轻量级FilePicker测试没有问题][挣扎解决自带文件管理器获取不到绝对地址问题而是返回msf%3A1000038197]
超级轻量级FilePicker测试没有问题 本文摘录于:https://blog.csdn.net/gitblog_00365/article/details/141449437只是做学习备份之用,绝无抄袭之意,有疑惑请联系本人! 今天真的是发了疯的找文件管理器,因为调用系统自带的文件管理…...

【论文速读】| RED QUEEN: 保护大语言模型免受隐蔽多轮越狱攻击
基本信息 原文标题:RED QUEEN: Safeguarding Large Language Models against Concealed Multi-Turn Jailbreaking 原文作者:Yifan Jiang, Kriti Aggarwal, Tanmay Laud, Kashif Munir, Jay Pujara, Subhabrata Mukherjee 作者单位:Hippocr…...

39.第二阶段x86游戏实战2-HOOK实现主线程调用
免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 本次游戏没法给 内容参考于:微尘网络安全 本人写的内容纯属胡编乱造,全都是合成造假,仅仅只是为了娱乐,请不要…...
wordpress argon主题美化方面
1、页面前端额外CSS: /*字体*/ font-face {font-family: myFont1; src:url(https://blog.yangmumu.com/css/fonts/Dancing.ttf) ;font-display: swap; } font-face {font-family: myFont2; src:url(https://blog.yangmumu.com/css/fonts/Regular.ttf) ;font-displa…...

qt QRadioButton详解
QRadioButton 是一个可以切换选中(checked)或未选中(unchecked)状态的选项按钮。单选按钮通常呈现给用户一个“多选一”的选择,即在一组单选按钮中,一次只能选中一个按钮。 重要方法 QRadioButton(QWidget…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...

Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...

Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...