当前位置: 首页 > news >正文

【Linux网络】网络编程套接字 -- 基于socket实现一个简单UDP网络程序

  • 认识端口号
  • 网络字节序
    • 处理字节序函数 htonl、htons、ntohl、ntohs
  • socket
    • socket编程接口
    • sockaddr结构
    • 结尾实现UDP程序的socket接口使用解析
      • socket
      • 处理 IP 地址的函数
      • 初始化sockaddr_in
      • bind
      • recvfrom
      • sendto
  • 实现一个简单的UDP网络程序
    • 封装服务器相关代码
    • 封装客户端相关代码
    • 实验结果

认识端口号

我们把数据从A主机发送到B主机,是目的吗?不是,真正通信的不是这两个机器!其实是这两台机器上面的软件(人)

数据有IP(公网)标识一台唯一的主机,用谁来标识各自主机上客户或者服务进程的唯一性呢?
为了更好的表示一台主机上服务进程的唯一性,我们采用端口号port标识服务器进程,客户端进程的唯一性!

端口号(port)是传输层协议的内容:

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

ip地址(主机全网唯一性) + 该主机上的端口号,标识该服务器上进程的唯一性
IP保证全网唯一,port保证在主机内部的唯一性

主机上对应的服务进程,在全网中是唯一的一个进程。
网络通信的本质:其实就是进程间通信

  1. 需要让不同的进程,先看到同一份资源—网络
  2. 通信就是在做IO,所以我们所有的上网行为,无外乎两种:我要把我的数据发出去、我要收到别人给我发的数据

进程已经有pid,为什么要有port呢?

  1. 系统是系统,网络是网络,单独设置—系统与网络解耦
  2. 需要客户端每次都能找到服务器进程—服务器的唯一性不能做任何改变— IP+port不能随意改变不会轻易改变。
  3. 不是所有的进程都要提供网络服务或者请求,但是所有的进程都需要pid。

进程+port–>网络服务进程
底层OS如何根据port找到指定的进程:OS内部采用hash方案,在OS内部维护了一个基于端口号的哈希表,key就是端口号,value就是task_struct的地址。有这个端口号就可以找到PCB,继而找到文件描述符表,文件描述符对象,对象找到了那么这个文件的缓冲区也就能找到,然后就可以将数据拷贝到缓冲区,最后就相当于我们将网络数据放到了文件中,如同读文件一样就将数据读上去了。

一个进程可以绑定多个端口号;但是一个端口号不能被多个进程绑定;

理解源端口号和目的端口号:
传输层协议(TCP和UDP)的数据段中有两个端口号,分别叫做源端口号目的端口号。 就是在描述 “数据是谁发的,要发给谁”;

认识TCP(Transmission Control Protocol 传输控制协议)协议:

  • 传输层协议
  • 有连接(相当于打电话必须先接通才能通话)
  • 可靠传输
  • 面向字节流
    认识UDP协议

认识UDP(User Datagram Protocol 用户数据报协议)协议:

  • 传输层协议
  • 无连接(相当于发送邮件只需要知道你的邮箱地址不需要你同意直接就能发给你)
  • 不可靠传输
  • 面向数据报

网络字节序

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

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

在这里插入图片描述

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

在这里插入图片描述

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

处理字节序函数 htonl、htons、ntohl、ntohs

在这里插入图片描述
函数原型:

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

函数作用:
将数据在不同字节序之间进行转换。

函数的详细介绍:

  • htonl() 函数:将一个 32 位无符号整数(unsigned int)从本地字节序转换为网络字节序(大端字节序)。
  • htons() 函数:将一个 16 位无符号整数(unsigned short)从本地字节序转换为网络字节序(大端字节序)。
  • ntohl() 函数:将一个 32 位无符号整数(unsigned int)从网络字节序(大端字节序)转换为本地字节序。
  • ntohs() 函数:将一个 16 位无符号整数(unsigned short)从网络字节序(大端字节序)转换为本地字节序。

socket

socket编程接口

// 创建 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);
  • 网络套接字编程(多应用于:网络跨主机之间多主机通信、支持本地通信)
  • 原始套接字(可以跨过传输层向下访问更底层的接口)
  • unix或间套接字(仅本地通信)

按道理要实现上述三种套接字应该要三套不同的接口,但是设计者只设计了一套接口,通过不同的参数解决所有网络或其他场景下的通信问题

sockaddr结构

socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及后面要讲的UNIX Domain Socket。然而,各种网络协议的地址格式并不相同
在这里插入图片描述

  • IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型,16位端口号和32位IP地址。
  • IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6。 这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容。
  • socket API可以都用struct sockaddr *类型表示,在使用的时候需要强制转化成sockaddr_in; 这样的好处是程序的通用性,可以接收IPv4,IPv6,以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数;

sockaddr 结构:

struct sockaddr{__SOCKADDR_COMMON (sa_);	/* Common data: address family and length.  */char sa_data[14];		/* Address data.  */};

sockaddr_in 结构:

/* Structure describing an Internet socket address.  */
struct sockaddr_in{__SOCKADDR_COMMON (sin_);in_port_t sin_port;			/* Port number.  */struct in_addr sin_addr;		/* Internet address.  *//* Pad to size of `struct sockaddr'.  */unsigned char sin_zero[sizeof (struct sockaddr) -__SOCKADDR_COMMON_SIZE -sizeof (in_port_t) -sizeof (struct in_addr)];};

虽然socket api的接口是sockaddr,但是我们真正在基于IPv4编程时,使用的数据结构是sockaddr_in;这个结构里主要有三部分信息:地址类型、端口号、IP地址。

in_addr结构:

typedef uint32_t in_addr_t;
struct in_addr{in_addr_t s_addr;};

in_addr用来表示一个IPv4IP地址。其实就是一个32位的整数。

显示当前户籍UDP连接状况与端口号的使用情况:

sudo netstat -nuap

结尾实现UDP程序的socket接口使用解析

socket

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

函数作用:
用于创建一个新的网络套接字的系统调用。

函数参数:
domain 参数指定了网络协议族:

  • AF_INET 表示 IPv4 协议
  • AF_INET6 表示 IPv6 协议
  • AF_UNIX 表示 Unix 域协议

type 参数指定了套接字的类型

  • SOCK_STREAM 表示面向连接的流套接字
  • SOCK_DGRAM 表示无连接的数据报套接字
  • SOCK_RAW 表示原始套接字。

protocol 参数指定了使用的协议

  • IPPROTO_TCP 表示 TCP 协议
  • IPPROTO_UDP 表示 UDP 协议
  • 参数设置为 0 时,系统会根据指定的 domain 和 type 参数选择一个默认的协议。这通常是最常用的协议,例如对于 AF_INET 和 SOCK_STREAM 的组合,通常使用的协议是 TCP(即 IPPROTO_TCP)。

使用 socket() 函数的一般流程如下:

  1. 创建一个套接字:调用 socket() 函数,指定 domain、type 和 protocol 参数,返回一个新的套接字描述符。
  2. 绑定套接字到本地地址:调用 bind() 函数,将套接字和一个本地地址绑定,以便其他程序可以通过该地址找到该套接字。
  3. 监听连接请求:如果创建的是面向连接的流套接字,可以调用 listen() 函数,开始监听连接请求。
  4. 接受连接请求:如果创建的是面向连接的流套接字,可以调用 accept() 函数,接受一个连接请求,返回一个新的套接字描述符,用于与客户端通信。
  5. 发送和接收数据:调用 send() 和 recv() 函数,向对方发送数据或接收对方发送的数据。
  6. 关闭套接字:调用 close() 函数,关闭套接字描述符,释放相关资源。

处理 IP 地址的函数

在这里插入图片描述

int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);
in_addr_t inet_network(const char *cp);
char *inet_ntoa(struct in_addr in);
struct in_addr inet_makeaddr(int net, int host);
in_addr_t inet_lnaof(struct in_addr in);
in_addr_t inet_netof(struct in_addr in);
  • inet_aton() 函数:将一个字符串形式的 IP 地址转换为一个二进制形式的 IP 地址。如果转换成功,函数返回非零值,否则返回零。
  • inet_addr() 函数:将一个字符串形式的 IP 地址转换为一个 32 位的整数,该整数表示为网络字节序。如果转换成功,函数返回一个非零值(即返回一个网络字节序表示的 IP 地址),否则返回 INADDR_NONE。
  • inet_network() 函数:将一个字符串形式的 IP 地址的网络部分转换为一个 32 位的整数,该整数表示为网络字节序。
  • inet_ntoa() 函数:将一个二进制形式的 IP 地址转换为一个字符串形式的 IP 地址。注意,该函数返回的是一个指向静态缓冲区的指针,因此不要将其作为返回值传递给其他函数。
  • inet_makeaddr() 函数:根据网络号和主机号创建一个 IP 地址。
  • inet_lnaof() 函数:从一个二进制形式的 IP 地址中提取主机号部分。
  • inet_netof() 函数:从一个二进制形式的 IP 地址中提取网络号部分。

初始化sockaddr_in

sockaddr_in是一个 IPv4 地址结构体,用于存储 IP 地址和端口号信息。在使用套接字函数时,通常需要将地址信息存储在 sockaddr_in 结构体中,并将其作为参数传递给函数

	struct sockaddr_in local; // 定义了一个变量,栈,用户bzero(&local, sizeof(local));//用于将指定的内存区域清零local.sin_family = AF_INET;//将结构体成员 sin_family 设置为 AF_INET,表示使用 IPv4 地址族local.sin_port = htons(_port); //结构体成员 sin_port 设置为要使用的端口号,使用 htons() 函数将端口号转换为网络字节序(大端字节序)local.sin_addr.s_addr = inet_addr(_ip.c_str());//将结构体成员 sin_addr 设置为要使用的 IP 地址,使用 inet_addr() 函数将 IP 地址转换为网络字节序(大端字节序)//在实际开发中,可以使用 inet_pton() 函数将字符串形式的 IP 地址转换为一个 struct in_addr 类型的结构体,该结构体包含了 IP 地址的二进制表示

bind

int bind(int socket, const struct sockaddr *address,socklen_t address_len);

函数作用:
用于将一个本地地址(IP 地址和端口号)与一个套接字关联起来的函数

函数参数:

  • socket 参数是一个指定了套接字的文件描述符。
  • addr 参数是一个指向 struct sockaddr 类型的结构体的指针,该结构体包含了要绑定的本地地址信息。
  • addrlen 参数是 addr 结构体的长度

函数返回值:
返回值为 0 表示绑定成功,-1 表示绑定失败,错误码保存在 errno 变量中。

recvfrom

ssize_t recvfrom(int socket, void *restrict buffer, size_t length,int flags, struct sockaddr *restrict address,socklen_t *restrict address_len);

函数作用:
用于从已连接或未连接的套接字接收数据的函数

函数参数:

  • socket 参数是指定了要接收数据的套接字的文件描述符。
  • buf 参数是一个指向接收数据的缓冲区的指针。
  • len 参数是缓冲区的大小。
  • flags 参数是一组标志位,可以用来指定接收数据的行为。为0表示默认
  • address 参数是一个指向 struct sockaddr 类型的结构体的指针,用于存储发送数据的远程地址。
  • addrlen 参数是 src_addr 结构体的长度。

函数返回值:
函数返回值为接收到的数据的字节数,如果没有数据可用,则返回 0。如果发生错误,则返回 -1,错误码保存在 errno 变量中

sendto

ssize_t sendto(int socket, const void *message, size_t length,int flags, const struct sockaddr *dest_addr,socklen_t dest_len);

函数作用:
用于向已连接或未连接的套接字发送数据的函数

函数参数:

  • socket 参数是指定了要发送数据的套接字的文件描述符。
  • buf 参数是一个指向要发送数据的缓冲区的指针。
  • len 参数是要发送数据的字节数。
  • flags 参数是一组标志位,可以用来指定发送数据的行为。
  • dest_addr 参数是一个指向 struct sockaddr 类型的结构体的指针,用于指定接收数据的远程地址。
  • dest_len 参数是 dest_addr 结构体的长度。

函数返回值:
函数返回值为发送数据的字节数,如果发生错误,则返回 -1,错误码保存在 errno 变量中。


实现一个简单的UDP网络程序

封装服务器相关代码

udpServer.hpp

#pragma once#include <iostream>
#include <string>
#include <strings.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>namespace Server
{using namespace std;static const string defaultIp = "0.0.0.0"; //TODOstatic const int gnum = 1024;enum {USAGE_ERR = 1, SOCKET_ERR, BIND_ERR};class udpServer{public:udpServer(const uint16_t &port, const string &ip = defaultIp):_port(port), _ip(ip), _sockfd(-1){}void initServer(){// 1. 创建socket_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(_sockfd == -1){cerr << "socket error: " << errno << " : " << strerror(errno) << endl;exit(SOCKET_ERR);}cout << "socket success: " << " : " << _sockfd << endl;// 2. 绑定port,ip(TODO)// 未来服务器要明确的port,不能随意改变struct sockaddr_in local; // 定义了一个变量,栈,用户bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = inet_addr(_ip.c_str());//local.sin_addr.s_addr = htonl(INADDR_ANY); // 任意地址bind,服务器的真实写法int n = bind(_sockfd, (struct sockaddr*)&local, sizeof(local));if(n == -1){cerr << "bind error: " << errno << " : " << strerror(errno) << endl;exit(BIND_ERR);}// UDP Server 的预备工作完成}void start(){// 服务器的本质其实就是一个死循环char buffer[gnum];for(;;){// 读取数据struct sockaddr_in peer;socklen_t len = sizeof(peer); //必填ssize_t s = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);// 1. 数据是什么 2. 谁发的?if(s > 0){buffer[s] = 0;string clientip = inet_ntoa(peer.sin_addr); //1. 网络序列 2. int->点分十进制IPuint16_t clientport = ntohs(peer.sin_port);string message = buffer;cout << clientip <<"[" << clientport << "]# " << message << endl;}}}~udpServer(){}private:uint16_t _port;string _ip; // 实际上,一款网络服务器,不建议指明一个IPint _sockfd;// func_t _callback; //回调};
}

udpServer.cc

#include "udpServer.hpp"
#include <memory>using namespace std;
using namespace Server;static void Usage(string proc)
{cout << "\nUsage:\n\t" << proc << " local_port\n\n";
}// ./udpServer port
int main(int argc, char *argv[])
{if(argc != 2){Usage(argv[0]);exit(USAGE_ERR);}uint16_t port = atoi(argv[1]);std::unique_ptr<udpServer> usvr(new udpServer(port));usvr->initServer();usvr->start();return 0;
}

封装客户端相关代码

udpClient.hpp

#pragma once#include <iostream>
#include <string>
#include <strings.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>namespace Client
{using namespace std;class udpClient{public:udpClient(const string &serverip, const uint16_t &serverport) : _serverip(serverip),_serverport(serverport), _sockfd(-1), _quit(false){}void initClient(){// 创建socket_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd == -1){cerr << "socket error: " << errno << " : " << strerror(errno) << endl;exit(2);}cout << "socket success: " << " : " << _sockfd << endl;// 2. client要不要bind[必须要的],client要不要显示的bind,需不需程序员自己bind?不需要// 写服务器的是一家公司,写client是无数家公司 -- 由OS自动形成端口进行bind!-- OS在什么时候,如何bind}void run(){struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(_serverip.c_str());server.sin_port = htons(_serverport);string message;while(!_quit){cout << "Please Enter# ";cin >> message;sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof(server));}}~udpClient(){}private:int _sockfd;string _serverip;uint16_t _serverport;bool _quit;};
} // namespace Client

udpClient.cc


#include "udpClient.hpp"
#include <memory>using namespace Client;static void Usage(string proc)
{cout << "\nUsage:\n\t" << proc << " server_ip server_port\n\n";
}// ./udpClient server_ip server_port
int main(int argc, char *argv[])
{if(argc != 3){Usage(argv[0]);exit(1);}string serverip = argv[1];uint16_t serverport = atoi(argv[2]);unique_ptr<udpClient> ucli(new udpClient(serverip, serverport));ucli->initClient();ucli->run();return 0;
}

实验结果

在这里插入图片描述


如有错误或者不清楚的地方欢迎私信或者评论指出🚀🚀

相关文章:

【Linux网络】网络编程套接字 -- 基于socket实现一个简单UDP网络程序

认识端口号网络字节序处理字节序函数 htonl、htons、ntohl、ntohs socketsocket编程接口sockaddr结构结尾实现UDP程序的socket接口使用解析socket处理 IP 地址的函数初始化sockaddr_inbindrecvfromsendto 实现一个简单的UDP网络程序封装服务器相关代码封装客户端相关代码实验结…...

Python学习笔记第六十四天(Matplotlib 网格线)

Python学习笔记第六十四天 Matplotlib 网格线普通网格线样式网格线 后记 Matplotlib 网格线 我们可以使用 pyplot 中的 grid() 方法来设置图表中的网格线。 grid() 方法语法格式如下&#xff1a; matplotlib.pyplot.grid(bNone, whichmajor, axisboth, )参数说明&#xff1a…...

机器学习与模式识别3(线性回归与逻辑回归)

一、线性回归与逻辑回归简介 线性回归主要功能是拟合数据&#xff0c;常用平方误差函数。 逻辑回归主要功能是区分数据&#xff0c;找到决策边界&#xff0c;常用交叉熵。 二、线性回归与逻辑回归的实现 1.线性回归 利用回归方程对一个或多个特征值和目标值之间的关系进行建模…...

vue启动配置npm run serve,动态环境变量,根据不同环境访问不同域名

首先创建不同环境的配置文件&#xff0c;比如域名和一些常量&#xff0c;创建一个env文件,先看看文件目录 env.dev就是dev环境的域名&#xff0c;.test就是test环境域名&#xff0c;其他同理&#xff0c;然后配置package.json文件 {"name": "require-admin&qu…...

HTML <strike> 标签

HTML5 中不支持 <strike> 标签在 HTML 4 中用于定义删除线文本。 定义和用法 <strike> 标签可定义加删除线文本定义。 浏览器支持 元素ChromeIEFirefoxSafariOpera<strike>YesYesYesYesYes 所有浏览器都支持 <strike> 标签。 HTML 与 XHTML 之间…...

数学建模-模型详解(1)

规划模型 线性规划模型&#xff1a; 当涉及到线性规划模型实例时&#xff0c;以下是一个简单的示例&#xff1a; 假设我们有两个变量 x 和 y&#xff0c;并且我们希望最大化目标函数 Z 5x 3y&#xff0c;同时满足以下约束条件&#xff1a; x > 0y > 02x y < 10…...

MySQL 数据库表的基本操作

一、数据库表概述 在数据库中&#xff0c;数据表是数据库中最重要、最基本的操作对象&#xff0c;是数据存储的基本单位。数据表被定义为列的集合&#xff0c;数据在表中是按照行和列的格式来存储的。每一行代表一条唯一的记录&#xff0c;每一列代表记录中的一个域。 二、数…...

企业微信电脑端开启chrome调试

首先&#xff1a; Mac端调试开启的快捷键&#xff1a;control shift command d Window端调试开启的快捷键: control shift alt d 这边以Mac为例&#xff0c;我们可以在电脑顶部看到调试的入口&#xff1a; 然后我们点击 『浏览器、webView相关』菜单&#xff0c;勾选上…...

Maven官网下载配置新仓库

1.Maven的下载 Maven的官网地址&#xff1a;Maven – Download Apache Maven 点击Download&#xff0c;查找 Files下的版本并下载如下图&#xff1a; 2.Maven的配置 自己在D盘或者E盘创建一个文件夹&#xff0c;作为本地仓库&#xff0c;存放项目依赖。 将下载好的zip文件进行解…...

银河麒麟V10 达梦安装教程

安装前先准备要安装包&#xff0c;包需要需要区分X86和arm架构。 版本为&#xff1a;dm8_20230419_FTarm_kylin10_sp1_64.iso 达梦数据库下载地址&#xff1a; https://www.aliyundrive.com/s/Qm7Es5BQM5U 第一步创建用户 su - root 1. 创建安装用户组 dminstall。 groupad…...

Python操作MongoDB数据库

安装MongoDB库 pip install pymongopython 代码 Author: tkhywang 2810248865qq.com Date: 2023-08-21 10:22:30 LastEditors: tkhywang 2810248865qq.com LastEditTime: 2023-08-21 11:17:45 FilePath: \PythonProject02\MongoDB 数据库.py Description: 这是默认设置,请设置…...

《HeadFirst设计模式(第二版)》第十一章代码——代理模式

代码文件目录&#xff1a; RMI&#xff1a; MyRemote package Chapter11_ProxyPattern.RMI;import java.rmi.Remote; import java.rmi.RemoteException;public interface MyRemote extends Remote {public String sayHello() throws RemoteException; }MyRemoteClient packa…...

QT的工程文件认识

目录 1、QT介绍 2、QT的特点 3、QT模块 3.1基本模块 3.2扩展模块 4、QT工程创建 1.选择应用的窗体格式 2.设置工程的名称与路径 3.设置类名 4.选择编译器 5、QT 工程解析 xxx.pro 工程配置 xxx.h 头文件 main.cpp 主函数 xxx.cpp 文件 6、纯手工创建一个QT 工程…...

typeScript安装及TypeScript tsc 不是内部或外部命令,也不是可运行的程序或批处理文件解决办法

一、typeScript安装&#xff1a; 1、首先确定系统中已安装node, winr 输入cmd 打开命令行&#xff0c;得到版本号证明系统中已经安装node node -v //v18.17.0 2、使用npm 全局安装typeScript # 全局安装 TypeScript npm i -g typescript 二、检查是否安装成功ts #检查t…...

SWUST 派森练习题:P111. 摩斯密码翻译器

描述 摩斯密码&#xff08;morse code)&#xff0c;又称摩斯电码、摩尔斯电码&#xff08;莫尔斯电码&#xff09;&#xff0c;是一种时通时断的信号代码&#xff0c;通过不同的信号排列顺序来表达不同的英文字母、数字和标点符号&#xff1b;通信时&#xff0c;将英文字母等内…...

如何在控制台查看excel内容

背景 最近发现打开电脑的excel很慢&#xff0c;而且使用到的场景很少&#xff0c;也因为mac自带了预览的功能。但是shigen就是闲不住&#xff0c;想自己搞一个excel预览软件&#xff0c;于是在一番技术选型之后&#xff0c;我决定使用python在控制台显示excel的内容。 具体的需…...

Echarts、js编写“中国主要城市空气质量对比”散点图 【亲测】

本次实验通过可视化工具Echarts来对全国主要城市的&#xff30;&#xff2d;2.5的值进行直观的展示&#xff0c;使人们可以快速的发现信息的关键点&#xff0c;从而对各个城市的空气质量情况有直观的了解。 先看效果 上代码&#xff1a; <!DOCTYPE html> <html>&…...

linux不分区直接在文件系统根上开swap

root下&#xff0c;直接创swapfile dd if/dev/zero of/swapfile bs1M count8192然后 mkswap swapfile swapon swapfile修改fstab # /etc/fstab: static file system information. # # Use blkid to print the universally unique identifier for a # device; this may be us…...

React请求机制优化思路 | 京东云技术团队

说起数据加载的机制&#xff0c;有一个绕不开的话题就是前端性能&#xff0c;很多电商门户的首页其实都会做一些垂直的定制优化&#xff0c;比如让请求在页面最早加载&#xff0c;或者在前一个页面就进行预加载等等。随着react18的发布&#xff0c;请求机制这一块也是被不断谈起…...

CompletableFuture总结和实践

CompletableFuture被设计在Java中进行异步编程。异步编程意味着在主线程之外创建一个独立的线程&#xff0c;与主线程分隔开&#xff0c;并在上面运行一个非阻塞的任务&#xff0c;然后通知主线程进展&#xff0c;成功或者失败。 一、概述 1.CompletableFuture和Future的区别&…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

Debian系统简介

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

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...

鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/

使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题&#xff1a;docker pull 失败 网络不同&#xff0c;需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

GC1808高性能24位立体声音频ADC芯片解析

1. 芯片概述 GC1808是一款24位立体声音频模数转换器&#xff08;ADC&#xff09;&#xff0c;支持8kHz~96kHz采样率&#xff0c;集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器&#xff0c;适用于高保真音频采集场景。 2. 核心特性 高精度&#xff1a;24位分辨率&#xff0c…...

在Ubuntu24上采用Wine打开SourceInsight

1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...

第7篇:中间件全链路监控与 SQL 性能分析实践

7.1 章节导读 在构建数据库中间件的过程中&#xff0c;可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中&#xff0c;必须做到&#xff1a; &#x1f50d; 追踪每一条 SQL 的生命周期&#xff08;从入口到数据库执行&#xff09;&#…...

什么是VR全景技术

VR全景技术&#xff0c;全称为虚拟现实全景技术&#xff0c;是通过计算机图像模拟生成三维空间中的虚拟世界&#xff0c;使用户能够在该虚拟世界中进行全方位、无死角的观察和交互的技术。VR全景技术模拟人在真实空间中的视觉体验&#xff0c;结合图文、3D、音视频等多媒体元素…...

go 里面的指针

指针 在 Go 中&#xff0c;指针&#xff08;pointer&#xff09;是一个变量的内存地址&#xff0c;就像 C 语言那样&#xff1a; a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10&#xff0c;通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...

图解JavaScript原型:原型链及其分析 | JavaScript图解

​​ 忽略该图的细节&#xff08;如内存地址值没有用二进制&#xff09; 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么&#xff1a;保存在堆中一块区域&#xff0c;同时在栈中有一块区域保存其在堆中的地址&#xff08;也就是我们通常说的该变量指向谁&…...