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

C++网络编程(一)本地socket通信

C++网络编程(一) socket通信

前言

本次内容简单描述C++网络通信中,采用socket连接客户端与服务器端的方法,以及过程中所涉及的函数概要与部分函数使用细节。记录本人C++网络学习的过程。

网络通信的Socket

socket,即“插座”,在网络中译作中文“套接字”,应用于计算机网络建立起数据连接。基于TCP/IP协议进行通信,将本地的ip地址与端口号相结合。两个程序可以通过各自的socket建立一个通道用于数据传输。

socket提供多种机制以供选择,但常用的一般为两种,即SOCK_STREAM(流)和SOCK_DGRAM(数据报)。

流socket基于TCP协议,其建立的通道是一个具有有序、可靠、全双工的字节流。

数据报socket基于UDP协议,其建立的通道传送数据是不可靠的,尽最大努力交付的,但传输速度较快。

目前在网络中使用的数据传送基本是基于TCP协议。

网络通信基本过程

请添加图片描述

首先建立起服务端,在服务端中采用socket()构建方法,建立服务端响应socket,其主要作用是用于接收由客户端socket发起的建立请求。随后bind()将创建的响应socket与本地的ip地址及设置的端口绑定。listen()为响应socket设置被动监听状态,监听客户端发来的建立请求。accept()代表一切就绪,此时服务器程序运行进入阻塞状态,等待客户端程序发来请求。

客户端中同样需要先建立起客户端socket,该socket所采用的协议应当与服务器端socket相同。客户端socket不需要绑定本地ip与端口,但应当指明所连接的服务器端的ip地址与端口号,通过connet()主动发起连接请求,与服务器端socket相连。

服务器端accept()程序收到客户端socket发来的请求后,会立即创建一个新的服务socket,与客户端的socket相对应,用于数据的收发与传送。即,服务器端会有两个socket,而客户端仅有一个socket

当双方数据交互结束后,各自调用close(),释放掉本地的socket。

代码示例

//server.cpp
#include<stdio.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<cerrno>#define MY_PORT 1234//端口号
#define BUF_SIZE 1024//最大缓存
#define QUEUE 20//最大连接数int main(){int server_sockfd = socket(AF_INET,SOCK_STREAM,0);//建立响应socketstruct sockaddr_in server_sockaddr;//保存本地地址信息server_sockaddr.sin_family = AF_INET;//采用ipv4server_sockaddr.sin_port = htons(MY_PORT);//指定端口server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);//获取主机接收的所有响应if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))==-1){//绑定本地ip与端口perror("Bind Failure\n");printf("Error: %s\n",strerror(errno));//输出错误信息return -1;}printf("Listen Port : %d\n",MY_PORT);if(listen(server_sockfd,QUEUE) == -1){//设置监听状态perror("Listen Error");return -1;}char buffer[BUF_SIZE];//一次传输的数据缓存struct sockaddr_in client_addr;//保存客户端地址信息socklen_t length = sizeof(client_addr);//需要的内存大小printf("Waiting for connection!\n");int connect_fd = accept(server_sockfd,(struct sockaddr *)&client_addr,&length);//等待连接,返回服务器端建立连接的socketif(connect_fd == -1){//连接失败perror("Connect Error");return -1;}printf("Connection Successful\n");while(1){//数据收发与传输memset(buffer,0,sizeof(buffer));int len = recv(connect_fd,buffer,sizeof(buffer),0);//接收数据// input "exit" or runtime errorif(strcmp(buffer,"exit\n")==0 ||len <= 0) break;printf("client send message: %s",buffer);strcpy(buffer,"successful");send(connect_fd,buffer,strlen(buffer),0);//发送数据printf("send message: %s\n",buffer);}close(connect_fd);//关闭数据socketclose(server_sockfd);//关闭响应socketreturn 0;
}
//client.cpp
#include<stdio.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<cerrno>#define MY_PORT 1234
#define BUF_SIZE 1024
#define SERVER_IP "127.0.0.1"//服务端ip,这里是本机int main(){int client_sockfd = socket(AF_INET,SOCK_STREAM,0);//建立客户端socketstruct sockaddr_in servaddr;//保存服务器端地址信息memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family = AF_INET;//ipv4协议servaddr.sin_port = htons(MY_PORT);//端口servaddr.sin_addr.s_addr = inet_addr(SERVER_IP);//ip地址printf("connect to %s:%d\n",SERVER_IP,MY_PORT);int connect_fd = connect(client_sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));//建立连接if(connect_fd<0){perror("Connect Error");_exit(1);}printf("Connect Successful\n");char sendbuf[BUF_SIZE];char recvbuf[BUF_SIZE];while(fgets(sendbuf,sizeof(sendbuf),stdin) != NULL){//数据传送memset(recvbuf,0,sizeof(recvbuf));printf("send message:%s",sendbuf);send(client_sockfd,sendbuf,strlen(sendbuf),0);if(strcmp(sendbuf,"exit\n")==0) break;int len = recv(client_sockfd,recvbuf,sizeof(recvbuf),0);if(len<=0){printf("receive failure");break;}printf("recv message:%s\n",recvbuf);memset(sendbuf,0,sizeof(sendbuf));}close(client_sockfd);//关闭客户端socketreturn 0;
}

函数分析

主机字节序和网络字节序

主机字节序即在计算机内部存储数据的格式,通常为小端序,即低地址存放低字节的内容,比如说一个ipv4的地址,当其作为数字存储时,一共需要四个字节来保存值,对于"127.0.0.1"来说,主机字节序中,最低位的字节保存了127,最高位则是1。

网络字节序是TCP/IP中规定好的一种数据格式,采用大端序,即高地址存放低字节的内容,同样对于"127.0.0.1"来说,网络字节序中,最低位的字节保存了1,最高位则是127。

字节地址0123
主机字节序01111111000000000000000000000001
网络字节序00000001000000000000000001111111
#include <netinet/in.h>
unsigned long int htonl(unsigned long int hostlong);//主机转网络
unsigned short int htons(unsigned short int hostshort);
unsigned long int ntohl(unsigned long int netlong);//网络转主机
unsigned short int ntohs(unsigned short int netshort);

htonl表示host to network long,依次类推

INADDR_ANY其值为"0.0.0.0",其意义便是不监听特定的ip,而接收所有通过指定端口发送至本机的信号。

Socket地址

通用地址

#include <bits/socket.h>//<sys/socket.h>头文件包含了此头
struct sockaddr
{sa_family_t sa_family; //地址族类型char sa_data[14]; //地址值
}

专用地址

TCP/IP协议族有sockaddr_in和sockaddr_in6两个专用socket地址结构体,它们分别用于IPv4和IPv6,此处仅列述IPv4。

#include <netinet/in.h>
struct sockaddr_in
{sa_family_t sin_family;/*地址族:AF_INET*/u_int16_t sin_port;/*端口号,要用网络字节序表示*/struct in_addr sin_addr;/*IPv4地址结构体,见下面*/unsigned char sin_zero[8];/*为了保持与struct sockaddr长度相同*/
};struct in_addr
{u_int32_t s_addr;/*IPv4地址,要用网络字节序表示*/
};

在实际使用时,sockaddr_in需要转化为通用socket地址类型sockaddr(强制转换即可),因为所有socket编程接口使用的地址参数的类型都是sockaddr。

IP地址转换

#include <arpa/inet.h>
in_addr_t inet_addr(const char* strptr);
int inet_aton(const char* cp,struct in_addr* inp);
char* inet_ntoa(struct in_addr in);

inet_addr函数将用点分十进制字符串表示的IPv4地址转化为用网络字节序整数表示的IPv4地址。它失败时返回INADDR_NONE。

inet_aton函数完成和inet_addr同样的功能,但是将转化结果存储于参数inp指向的地址结构中。它成功时返回1,失败则返回0。

inet_ntoa函数将用网络字节序整数表示的IPv4地址转化为用点分十进制字符串表示的IPv4地址。但需要注意的是,该函数内部用一个静态变量存储转化结果,函数的返回值指向该静态内存,因此inet_ntoa是不可重入的。

注:以上函数仅用于IPv4的地址转换。

创建socket

#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain,int type,int protocol);
/*
domain参数:告诉系统使用哪个底层协议族
type参数:指定服务类型
protocol参数:在前两个参数构成的协议集合下,再选择一个具体的协议
*/

参数通常取值:

domain:PF_INET(Protocol Family of Internet,用于IPv4)、PF_INET6(用于IPv6)

type:SOCK_STREAM(流,TCP)、SOCK_DGRAM(数据报,UDP)

protocol:0(默认协议)

当socket系统调用成功时返回一个socket文件描述符,失败则返回-1并设置errno。

绑定IP地址和端口

#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen);

bind将my_addr所指的socket地址分配给未命名的sockfd文件描述符,addrlen参数指出该socket地址的长度。

bind成功时返回0,失败则返回-1并设置errno。其中两种常见的errno是EACCES和EADDRINUSE,它们的含义分别是:

  • EACCES,被绑定的地址是受保护的地址,仅超级用户能够访问。比如普通用户将socket绑定到知名服务端口(端口号为0~1023)上时,bind将返回EACCES错误。
  • EADDRINUSE,被绑定的地址正在使用中。比如将socket绑定到一个处于TIME_WAIT状态的socket地址。

监听socket

#include <sys/socket.h>
int listen(int sockfd, int backlog);
/*
sockfd: 指定被监听的socket
backlog: 设置内核监听队列的最大长度
*/

监听队列:即处于向服务端socket发出连接请求,但TCP建立三次握手中尚未进行第二次握手的序列。

监听队列的长度如果超过backlog,服务器将不受理新的客户连接,客户端也将收到ECONNREFUSED错误信息。

listen成功时返回0,失败则返回-1并设置errno。

注:listen()函数执行成功后,客户端发来连接请求,在经过listen后,TCP的三次握手已经完成,建立了连接。

接收连接

#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen);
/*
sockfd:是执行过listen系统调用的监听socket
addr:用来获取被接受连接的远端socket地址
addrlen:指出远程socket的长度
返回值:一个新的连接socket
*/

函数调用成功返回的socket唯一地标识了被接受的这个连接,服务器可通过读写该socket来与被接受连接对应的客户端通信。

调用失败则返回-1并设置errno。

发起连接

#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr* serv_addr, socklen_t addrlen);

sockfd参数由socket系统调用返回一个socket。serv_addr参数是服务器监听的socket地址,addrlen参数则指定这个地址的长度。

connect成功时返回0。一旦成功建立连接,sockfd就唯一地标识了这个连接,客户端就可以通过读写sockfd来与服务器通信。connect失败则返回-1并设置errno。其中两种常见的errno是ECONNREFUSED和ETIMEDOUT,它们的含义如下:

  • ECONNREFUSED,目标端口不存在,连接被拒绝。
  • ETIMEDOUT,连接超时。

关闭连接

#include <unistd.h>
int close(int fd);//fd是待关闭的socket

close系统调用并非总是立即关闭一个连接,而是将fd的引用计数减1。只有当fd的引用计数为0时,才真正关闭连接。类似于Linux系统中的硬链接数。比方说在多进程程序中,父进程和子进程都对一个socket操作,只有在所有进程中都调用close()关闭该socket,该socket才会被关闭。

有一个函数可以立即终止连接:

#include <sys/socket.h>
int shutdown(int sockfd,int howto);

sockfd参数是待关闭的socket。howto参数决定了shutdown的行为,可取值如下:

  • SHUT_RD 关闭读
  • SHUT_WR 关闭写
  • SHUT_RDWR 关闭读写

数据读写

#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void* buf, size_t len, int flags);
ssize_t send(int sockfd, const void* buf, size_t len, int flags);

此处函数仅针对TCP流数据读写,对UDP数据报的读写暂且不表。

recv读取sockfd上的数据,buf和len参数分别指定读缓冲区的位置和大小,flags通常设置为0即可。recv成功时返回实际读取到的数据的长度,它可能小于我们期望的长度len。因此我们可能要多次调用recv,才能读取到完整的数据。recv可能返回0,这意味着通信对方已经关闭连接了。recv出错时返回-1并设置errno。

send往sockfd上写入数据,buf和len参数分别指定写缓冲区的位置和大小。send成功时返回实际写入的数据的长度,失败则返回-1并设置errno。

网络信息API

#include <netdb.h>
struct hostent*gethostbyname(const char* name);//name:指定目标主机的主机名
struct hostent*gethostbyaddr(const void* addr, size_t len, int type);
/*
addr:指定目标主机的IP地址
len:指定addr所指IP地址的长度
type:指定addr所指IP地址的类型(AF_INTE、AF_INET6)
*/struct hostent
{char* h_name;/*主机名*/char** h_aliases;/*主机别名列表,可能有多个*/int h_addrtype;/*地址类型(地址族)*/int h_length;/*地址长度*/char** h_addr_list;/*按网络字节序列出的主机IP地址列表(数字形式,不是点分十进制)*/
};

gethostbyname函数根据主机名称获取主机的完整信息,gethostbyaddr函数根据IP地址获取主机的完整信息。gethostbyname函数通常先在本地的/etc/hosts配置文件中查找主机,如果没有找到,再去访问DNS服务器。

//示例
struct hostent *res = gethostbyname("www.baidu.com");
if (res)
{for (int i = 0; res->h_addr_list[i]; i++)printf("IP addr %d: %s\n", i + 1, inet_ntoa(*(struct in_addr *)res->h_addr_list[i]));
}

相关文章:

C++网络编程(一)本地socket通信

C网络编程(一) socket通信 前言 本次内容简单描述C网络通信中&#xff0c;采用socket连接客户端与服务器端的方法&#xff0c;以及过程中所涉及的函数概要与部分函数使用细节。记录本人C网络学习的过程。 网络通信的Socket socket,即“插座”,在网络中译作中文“套接字”,应…...

【Docker】Linux下Docker安装使用与Docker-compose的安装

【Docker】的安装与启动 sudo yum install -y yum-utils device-mapper-persistent-data lvm2 sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo sudo yum install docker-cesudo systemctl enable dockersudo systemct…...

构造函数与普通函数,显式原型与隐式原型,原型与原型链

原型与原型链1 学前先了解一些概念1.1 构造函数和普通函数的区别1.1.1 调用方式1.1.2 函数中this的指向不同1.1.3 写法不同1.2 问题明确2 原型与原型链2.1 原型2.2 显式原型与隐式原型2.3 原型链3 原型链环形结构1 学前先了解一些概念 1.1 构造函数和普通函数的区别 构造函数…...

跨过社科院与杜兰大学金融管理硕士项目入学门槛,在金融世界里追逐成为更好的自己

没有人不想自己变得更优秀&#xff0c;在职的我们也是一样。当我们摸爬滚打在职场闯出一条路时&#xff0c;庆幸的是我们没有沉浸在当下&#xff0c;而是继续攻读硕士学位&#xff0c;在社科院与杜兰大学金融管理硕士项目汲取能量&#xff0c;在金融世界里追逐成为更好的自己。…...

macOS 13.3 Beta 3 (22E5236f)With OpenCore 0.9.1开发版 and winPE双引导分区原版镜像

原文地址&#xff1a;http://www.imacosx.cn/112494.html&#xff08;转载请注明出处&#xff09;镜像特点完全由黑果魏叔官方制作&#xff0c;针对各种机型进行默认配置&#xff0c;让黑苹果安装不再困难。系统镜像设置为双引导分区&#xff0c;全面去除clover引导分区&#x…...

InceptionTime 复现

下载数据集&#xff1a; https://www.cs.ucr.edu/~eamonn/time_series_data/ 挂梯子&#xff0c;开全局模式即可 配置环境 虚拟环境基于python3.9&#xff0c; tensorflow下载&#xff1a;pip install tensorflow&#xff0c;不需要tensorflow-gpu&#xff08;高版本python&…...

谷粒学院开发(二):教师管理模块

前后端分离开发 前端 html, css, js, jq 主要作用&#xff1a;数据显示 ajax后端 controller service mapper 主要作用&#xff1a;返回数据或操作数据 接口 讲师管理模块&#xff08;后端&#xff09; 准备工作 创建数据库&#xff0c;创建讲师数据库表 CREATE TABLE edu…...

2021牛客OI赛前集训营-提高组(第三场) T4扑克

2021牛客OI赛前集训营-提高组&#xff08;第三场&#xff09; 题目大意 小A和小B在玩扑克牌游戏&#xff0c;规则如下&#xff1a; 从一副52张牌&#xff08;没有大小王&#xff09;的扑克牌中随机发3张到每个玩家手上&#xff0c;每个玩家可以任意想象另外两张牌&#xff0…...

【OJ比赛日历】快周末了,不来一场比赛吗? #03.11-03.17 #12场

CompHub 实时聚合多平台的数据类(Kaggle、天池…)和OJ类(Leetcode、牛客…&#xff09;比赛。本账号同时会推送最新的比赛消息&#xff0c;欢迎关注&#xff01;更多比赛信息见 CompHub主页 或 点击文末阅读原文以下信息仅供参考&#xff0c;以比赛官网为准目录2023-03-11&…...

C++-说一说异常机制

C异常机制是一种处理程序错误的高级方法。当程序出现错误时&#xff0c;可以通过抛出异常来通知调用者进行处理&#xff0c;或者在异常对象被捕获之后终止程序执行。 异常处理语法 在C中&#xff0c;可以使用 throw 抛出异常&#xff0c; try-catch 处理异常&#xff0c;try块中…...

k8s CSI插件浅析

Kubernetes CSI (Container Storage Interface)插件是一种可插拔的存储插件&#xff0c;可以将外部存储系统的功能集成到Kubernetes集群中。它允许Kubernetes管理员动态地将外部存储系统映射到容器中&#xff0c;以满足应用程序对持久化存储的需求。 CSI插件基于一组规范定义的…...

九、CSS3新特性三

文章目录一、逐帧动画二、flex弹性盒子三、少量元素侧轴对齐方式四、折行侧轴对齐方式五、项目属性六、网格布局七、网格布局的对齐方式八、网格布局的项目合并一、逐帧动画 一张背景图&#xff0c;改变back-position-x的位置让他动起来 step-start 逐帧动画 animation: play …...

Dynamics365 本地部署整体界面

昨天已经登陆上去了然后今天开机突然又登陆不上去了 具体原因也不知道 然后我把注册插件删除又重新下载结果还是登陆不上去于是返回之前的断点就可以登陆上去了重复昨天的操作这里就不截图了6、注册新步骤右键单击&#xff08;插件&#xff09;BasicPlugin.FollowUpPlugin&…...

Binder ——binder的jni注册和binder驱动

环境&#xff1a;Android 11源码Android 11 内核源码源码阅读器 sublime textbinder的jni方法注册zygote启动1-1、启动zygote进程zygote是由init进程通过解析init.zygote.rc文件而创建的&#xff0c;zygote所对应的可执行程序是app_process&#xff0c;所对应的源文件是app_mai…...

Python+Yolov8目标识别特征检测

Yolov8目标识别特征检测如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01;前言这篇博客针对<<Yolov8目标识别特征检测>>编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0c;易读。 学习与应用推荐…...

欢迎使用Markdown编辑器

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注…...

Mac环境安装python

一、介绍&#xff1a; Python是跨平台的&#xff0c;它可以运行在Windows、Mac和各种Linux/Unix系统上。在Windows上写Python程序&#xff0c;放到Linux上也是能够运行的。 要开始学习Python编程&#xff0c;首先就得把Python安装到你的电脑里。安装后&#xff0c;你会得到Pyt…...

2023年全国最新交安安全员精选真题及答案16

百分百题库提供交安安全员考试试题、交安安全员考试预测题、交安安全员考试真题、交安安全员证考试题库等&#xff0c;提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 一、判断题&#xff1a; 1.施工单位应当向作业人员提供安全防护用具和安全防护服…...

项目实战-瑞吉外卖day02(B站)持续更新

瑞吉外卖-Day02课程内容完善登录功能新增员工员工信息分页查询启用/禁用员工账号编辑员工信息1. 完善登录功能1.1 问题分析前面我们已经完成了后台系统的员工登录功能开发&#xff0c;但是目前还存在一个问题&#xff0c;接下来我们来说明一个这个问题&#xff0c; 以及如何处理…...

2018年MathorCup数学建模D题公交移动支付问题的评估方案解题全过程文档及程序

2018年第八届MathorCup高校数学建模挑战赛 D题 公交移动支付问题的评估方案 原题再现&#xff1a; 随着智能手机的普及和移动支付技术的提高,越来越多的支付手段可以转移到手机端。现有的现金缴费和实体公交卡刷卡的付费方式存在缺点&#xff0c;如公交卡在使用过程中存在着充…...

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…...

【Oracle APEX开发小技巧12】

有如下需求&#xff1a; 有一个问题反馈页面&#xff0c;要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据&#xff0c;方便管理员及时处理反馈。 我的方法&#xff1a;直接将逻辑写在SQL中&#xff0c;这样可以直接在页面展示 完整代码&#xff1a; SELECTSF.FE…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log&#xff0c;共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题&#xff0c;不能使用ELK只能使用…...

多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验

一、多模态商品数据接口的技术架构 &#xff08;一&#xff09;多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如&#xff0c;当用户上传一张“蓝色连衣裙”的图片时&#xff0c;接口可自动提取图像中的颜色&#xff08;RGB值&…...

工程地质软件市场:发展现状、趋势与策略建议

一、引言 在工程建设领域&#xff0c;准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具&#xff0c;正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命

在华东塑料包装行业面临限塑令深度调整的背景下&#xff0c;江苏艾立泰以一场跨国资源接力的创新实践&#xff0c;重新定义了绿色供应链的边界。 跨国回收网络&#xff1a;废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点&#xff0c;将海外废弃包装箱通过标准…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

微信小程序云开发平台MySQL的连接方式

注&#xff1a;微信小程序云开发平台指的是腾讯云开发 先给结论&#xff1a;微信小程序云开发平台的MySQL&#xff0c;无法通过获取数据库连接信息的方式进行连接&#xff0c;连接只能通过云开发的SDK连接&#xff0c;具体要参考官方文档&#xff1a; 为什么&#xff1f; 因为…...

什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南

文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/55aefaea8a9f477e86d065227851fe3d.pn…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...