网络编程 -- 简易TCP网络程序

一 字符串回响

1.1 核心功能

  字符串回响程序类似于 echo 指令,客户端向服务器发送消息,服务器在收到消息后会将消息发送给客户端,该程序实现起来比较简单,同时能很好的体现 socket 套接字编程的流程。

  

1.2 程序结构

  这个程序我们已经基于 UDP 协议实现过了,换成 TCP 协议实现时,程序的结构是没有变化的,同样需要 server.hppserver.ccclient.hppclient.cc 这几个文件。

server.hpp 头文件

 #pragma once#include<iostream>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include "err.hpp"
#include<cstring>namespace My_server{const uint16_t default_port = 8888; // 默认端口号class server{public:server(const uint16_t port = default_port):_port(port){}~server(){}// 初始化服务器void InitServer(){}// 启动服务器void StartServer(){}private:int _sock; // 套接字(存疑)uint16_t _port; // 端口号};
}

注意: 这里的 _sock 套接字成员后面需要修改

server.cc 头文件

//智能指针头文件
#include<memory>
#include"server.hpp"using namespace My_server;int main(){std::unique_ptr<server> usvr(new server());usvr->InitServer();usvr->StartServer();return 0;
}

创建 client.hpp 客户端头文件

#pragma once#include<iostream>
#include<string>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include"err.hpp"namespace My_client{class client{public:client(const std::string& ip,const uint16_t port):server_ip(ip),server_port(port){}~client(){}//初始化客户端void InitClient(){}// 启动客户端void StartClient(){}private:int _sock;// 套接字std::string server_ip;//服务器ipuint16_t server_port; //服务器端口号};
}

创建client.cc文件
 

#include"client.hpp"
#include<memory>using namespace My_client;void Usage(char* program){std::cout<<"Usage : "<<std::endl;std::cout<<"\t "<<program<<" ServerIP ServerPort"<<std::endl;}int main(int argc,char *argv[]){if(argc!=3){Usage(argv[0]);return USAGE_ERR;}//获取服务器IP地址和端口号std::string ip(argv[1]);uint16_t port=std::stoi(argv[2]);std::unique_ptr<client> usvr(new client(ip,port));usvr->InitClient();usvr->StartClient();return 0;
}

同时需要Makefile文件

.PHONY:all
all:server clientserver:server.ccg++ -o $@ $^ -std=c++14client:client.ccg++ -o $@ $^ -std=c++14.PHONY:clean
clean:rm -rf server client

和 err.hpp 头文件

#pragma once// 错误码
enum
{USAGE_ERR=1 ,SOCKET_ERR,BIND_ERR
};

1.3 服务端

1.3.1 初始化服务端  

  基于 TCP 协议实现的网络程序也需要 创建套接字、绑定 IP 和端口号

在使用 socket 函数创建套接字时,UDP 协议需要指定参数2为 SOCK_DGRAMTCP 协议则是指定参数2为 SOCK_STREAM

InitServer() 初始化服务器函数 — 位于 server.hpp 服务器头文件中的 server

 #pragma once#include<iostream>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include "err.hpp"
#include<cstring>namespace My_server{const uint16_t default_port = 8888; // 默认端口号class server{public:server(const uint16_t port = default_port):_port(port){}~server(){}// 初始化服务器void InitServer(){//1 创建套接字_sock = socket(AF_INET,SOCK_STREAM,0);if(_sock==-1){//绑定失败std::cerr<<"Create Socket Fail!"<<strerror(errno)<<std::endl;exit(SOCKET_ERR);}std::cout<<"Create Socket Success!"<<_sock<<std::endl;//2 绑定端口号和IP地址struct sockaddr_in lockal;bzero(&lockal,sizeof lockal);lockal.sin_family = AF_INET;lockal.sin_addr.s_addr = INADDR_ANY;lockal.sin_port = htons(_port);if(bind(_sock,(const sockaddr*)&lockal,sizeof(lockal))){std::cout<<"Bind IP&&Port Fali:"<<strerror(errno)<<std::endl;exit(BIND_ERR);}}// 启动服务器void StartServer(){}private:int _sock; // 套接字(存疑)uint16_t _port; // 端口号};
}

注意: 在绑定端口号时,一定需要把主机序列转换为网络序列

  为什么在绑定端口号阶段需要手动转换为网络序列,而在发送信息阶段则不需要? 这是因为在发送信息阶段,recvfrom / sendto 等函数会自动将需要发送的信息转换为网络序列,接收信息时同样会将其转换为主机序列,所以不需要手动转换

  如果使用的 UDP 协议,那么初始化服务器到此就结束了,但我们本文中使用的是 TCP 协议,这是一个 面向连接 的传输层协议,意味着在初始化服务器时,需要设置服务器为 监听 状态

  使用到的函数是 listen 函数

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int listen(int sockfd, int backlog);

返回值:监听成功返回 0,失败返回 -1.

  listen函数 使得一个进程可以接受其它进程的请求,从而成为一个服务器进程。在TCP服务器编程中listen函数把进程变为一个服务器,并指定相应的套接字变为被动连接。

  这里的参数2需要设置一个整数,通常为 16、32、64...,表示 全连接队列 的最大长度,关于 全连接队列 的详细知识放到后续博客中讲解,这里只需要直接使用。

server.hpp 服务器头文件

  我们改变一下网络文件的名字

 #pragma once#include<iostream>
#include<cerrno>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include "err.hpp"
#include<cstring>namespace My_server{const uint16_t default_port = 8888; // 默认端口号const int backlog=32; //全连接队列的最大长度class server{public:server(const uint16_t port = default_port):_port(port){}~server(){}// 初始化服务器void InitServer(){//1 创建套接字_listensock = socket(AF_INET,SOCK_STREAM,0);if(_listensock==-1){//绑定失败std::cerr<<"Create Socket Fail!"<<strerror(errno)<<std::endl;exit(SOCKET_ERR);}std::cout<<"Create Socket Success!"<<_listensock<<std::endl;//2 绑定端口号和IP地址struct sockaddr_in local;bzero(&local,sizeof(local));local.sin_family = AF_INET;local.sin_addr.s_addr = INADDR_ANY;//绑定任意可用IP地址local.sin_port = htons(_port);if(bind(_listensock,(const sockaddr*)&local,sizeof(local))){std::cout<<"Bind IP&&Port Fali:"<<strerror(errno)<<std::endl;exit(BIND_ERR);}//3. 监听if(listen(_listensock,backlog) == -1){std::cerr << "Listen Fail!" << strerror(errno) << std::endl;//新增一个报错exit(LISTEN_ERR);}std::cout<<"Listen Success!"<<std::endl;}// 启动服务器void StartServer(){}private:int _listensock; // 套接字(存疑)uint16_t _port; // 端口号};
}

  至此基于  TCP 协议 实现的初始化服务器函数就填充完成了,编译并运行服务器,显示初始化服务器成功。

1.3.2 启动服务器

1.3.2.1 处理连接请求

  TCP 是面向连接,当有客户端发起连接请求时,TCP 服务器需要正确识别并尝试进行连接,当连接成功时,与其进行通信,可使用 accept 函数进行连接。

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数解读:

  • sockfd 服务器用于处理连接请求的 socket 套接字
  • addr 客户端的 sockaddr 结构体信息
  • addrlen 客户端的 sockaddr 结构体大写

  其中 addraddrlen 是一个 输入输出型 参数,类似于 recvfrom 中的参数。

  返回值:连接成功返回一个用于通信的 socket 套接字(文件描述符),失败返回 -1。

  这也就意味着之前我们在 TcpServer 中创建的类内成员 sock_ 并非是用于通信,而是专注于处理连接请求,在 TCP 服务器中,这种套接字称为 监听套接字

使用 accept 函数处理连接请求

server.hpp 服务器头文件

 #pragma once#include<iostream>
#include<cerrno>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include "err.hpp"
#include<cstring>namespace My_server{const uint16_t default_port = 8888; // 默认端口号const int backlog=32; //全连接队列的最大长度class server{public:server(const uint16_t port = default_port):_port(port){}~server(){}// 初始化服务器void InitServer(){//1 创建套接字_listensock = socket(AF_INET,SOCK_STREAM,0);if(_listensock==-1){//绑定失败std::cerr<<"Create Socket Fail!"<<strerror(errno)<<std::endl;exit(SOCKET_ERR);}std::cout<<"Create Socket Success!"<<_listensock<<std::endl;//2 绑定端口号和IP地址struct sockaddr_in local;bzero(&local,sizeof(local));local.sin_family = AF_INET;local.sin_addr.s_addr = INADDR_ANY;//绑定任意可用IP地址local.sin_port = htons(_port);if(bind(_listensock,(const sockaddr*)&local,sizeof(local))){std::cout<<"Bind IP&&Port Fali:"<<strerror(errno)<<std::endl;exit(BIND_ERR);}//3. 监听if(listen(_listensock,backlog) == -1){std::cerr << "Listen Fail!" << strerror(errno) << std::endl;//新增一个报错exit(LISTEN_ERR);}std::cout<<"Listen Success!"<<std::endl;}// 启动服务器void StartServer(){while(!_quit){//1 处理连接请求struct sockaddr_in client;socklen_t len=sizeof(client);int sock=accept(_listensock,(struct sockaddr*)&client,&len);//2 如果连接失败 继续尝试连接if(sock==-1){std::cerr<<"Accept Fail!"<<strerror(errno)<<std::endl;continue;}//连接成功,获取客户端信息std::string clientip=inet_ntoa(client.sin_addr);uint16_t clientport= ntohs(client.sin_port);std::cout<<"Server accept"<<clientip + "-"<<clientport<<sock<<" from "<<_listensock<<" success!"<<std::endl;//3 这里因为是字节流传递,一般而言我们会自己写一个函数Service(sock,clientip,clientport);}}private:int _listensock; // 套接字(存疑)uint16_t _port; // 端口号bool _quit; // 判断服务器是否结束运行};
}
1.3.2.2 业务处理

对于 TCP 服务器来说,它是面向字节流传输的,我们之前使用的文件相关操作也是面向字节流,凑巧的是在 Linux 中网络是以挂接在文件系统的方式实现的,种种迹象表明:可以通过文件相关接口进行通信

  • read 从文件中读取信息(接收消息)
  • write 向文件中写入信息(发送消息)

这两个系统调用的核心参数是 fd文件描述符),即服务器与客户端在连接成功后,获取到的 socket 套接字,所以接下来可以按文件操作的套路,完成业务处理

Service() 业务处理函数 — 位于 server.hpp 服务器头文件中的 server

  void Service(int sock,const std::string& clientip,const uint16_t& clientport){char buff[1024];std::string who=clientip + "-" + std::to_string(clientport);while(true){//以C语言格式读取,预留'\0'的位置ssize_t n = read(sock,buff,sizeof(buff)-1);if(n>0){//读取成功std::cout<<"Server get: "<<buff<<" from "<<who<<std::endl;//实际处理可以交给上层逻辑指定std::string respond = _func(buff);//发送给服务器write(sock,buff,strlen(buff));}else if(n==0){//表示当前读到了文件末尾,结束读取std::cout<<"Client "<<who<<" "<<sock<<" quit!"<<std::endl;close(sock);break;}else{// 读取出问题(暂时)std::cerr << "Read Fail!" << strerror(errno) << std::endl;close(sock); // 关闭文件描述符break;}}}
1.3.2.2.1 回调函数

为了更好的实现功能解耦,这里将真正的业务处理函数交给上层处理,编写完成后传给 TcpServer 对象即可,当然,在 TcpServer 类中需要添加对应的类型

这里设置回调函数的返回值为 string,参数同样为 string

server.hpp 服务器头文件

 #pragma once#include<iostream>
#include<cerrno>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include "err.hpp"
#include<cstring>
#include <unistd.h>
#include<functional>namespace My_server{const uint16_t default_port = 8888; // 默认端口号const int backlog=32; //全连接队列的最大长度using func_t = std::function<std::string(std::string)>;class server{public:server(const func_t &func,const uint16_t port = default_port):_func(func),_port(port),_quit(false){}~server(){}// 初始化服务器void InitServer(){//1 创建套接字_listensock = socket(AF_INET,SOCK_STREAM,0);if(_listensock==-1){//绑定失败std::cerr<<"Create Socket Fail!"<<strerror(errno)<<std::endl;exit(SOCKET_ERR);}std::cout<<"Create Socket Success!"<<_listensock<<std::endl;//2 绑定端口号和IP地址struct sockaddr_in local;bzero(&local,sizeof(local));local.sin_family = AF_INET;local.sin_addr.s_addr = INADDR_ANY;//绑定任意可用IP地址local.sin_port = htons(_port);if(bind(_listensock,(const sockaddr*)&local,sizeof(local))){std::cout<<"Bind IP&&Port Fali:"<<strerror(errno)<<std::endl;exit(BIND_ERR);}//3. 监听if(listen(_listensock,backlog) == -1){std::cerr << "Listen Fail!" << strerror(errno) << std::endl;//新增一个报错exit(LISTEN_ERR);}std::cout<<"Listen Success!"<<std::endl;}// 启动服务器void StartServer(){while(!_quit){//1 处理连接请求struct sockaddr_in client;socklen_t len=sizeof(client);int sock=accept(_listensock,(struct sockaddr*)&client,&len);//2 如果连接失败 继续尝试连接if(sock==-1){std::cerr<<"Accept Fail!"<<strerror(errno)<<std::endl;continue;}//连接成功,获取客户端信息std::string clientip=inet_ntoa(client.sin_addr);uint16_t clientport= ntohs(client.sin_port);std::cout<<"Server accept"<<clientip + "-"<<clientport<<sock<<" from "<<_listensock<<" success!"<<std::endl;//3 这里因为是字节流传递,一般而言我们会自己写一个函数Service(sock,clientip,clientport);}}void Service(int sock,const std::string& clientip,const uint16_t& clientport){char buff[1024];std::string who=clientip + "-" + std::to_string(clientport);while(true){//以C语言格式读取,预留'\0'的位置ssize_t n = read(sock,buff,sizeof(buff)-1);if(n>0){//读取成功std::cout<<"Server get: "<<buff<<" from "<<who<<std::endl;//实际处理可以交给上层逻辑指定std::string respond = _func(buff);//发送给服务器write(sock,buff,strlen(buff));}else if(n==0){//表示当前读到了文件末尾,结束读取std::cout<<"Client "<<who<<" "<<sock<<" quit!"<<std::endl;close(sock);break;}else{// 读取出问题(暂时)std::cerr << "Read Fail!" << strerror(errno) << std::endl;close(sock); // 关闭文件描述符break;}}}private:int _listensock; // 套接字(存疑)uint16_t _port; // 端口号bool _quit; // 判断服务器是否结束运行func_t _func;// 回调函数};
}
1.3.2.2.2 server.cc文件

对于当前的 TCP 网络程序(字符串回响)来说,业务处理函数逻辑非常简单,无非就是直接将客户端发送过来的消息,重新转发给客户端

server.cc 服务器源文件

//智能指针头文件
#include<memory>
#include"server.hpp"
#include<string>using namespace My_server;
// 业务处理回调函数(字符串回响)
std::string echo(std::string request){return request;
}int main(){std::unique_ptr<server> usvr(new server(echo));usvr->InitServer();usvr->StartServer();return 0;
}

  尝试编译并运行服务器,可以看到当前 bash 已经被我们的服务器程序占用了,重新打开一个终端,并通过 netstat 命令查看网络使用情况(基于 TCP 协议)

1.4 客户端

1.4.1 初始化客户端

 对于客户端来说,服务器的 IP 地址与端口号是两个不可或缺的元素,因此在客户端类中,  server_ipserver_port 这两个成员是少不了的,当然得有 socket 套接字

初始化客户端只需要干一件事:创建套接字,客户端是主动发起连接请求的一方,也就意味着它不需要使用 listen 函数设置为监听状态

注意: 客户端也是需要 bind 绑定的,但不需要自己手动绑定,由操作系统帮我们自动完成

client.hpp 客户端头文件

#pragma once#include<iostream>
#include<string>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include"err.hpp"
#include<cerrno>
#include<cstring>namespace My_client{class client{public:client(const std::string& ip,const uint16_t port):server_ip(ip),server_port(port){}~client(){}//初始化客户端void InitClient(){// 1. 创建套接字_sock = socket(AF_INET, SOCK_STREAM, 0);if (_sock == -1){std::cerr << "Create Socket Fail!" << strerror(errno) << std::endl;exit(SOCKET_ERR);}   std::cout << "Create Sock Succeess! " << _sock << std::endl;}// 启动客户端void StartClient(){}private:int _sock;// 套接字std::string server_ip;//服务器ipuint16_t server_port; //服务器端口号};
}

1.4.2 启动客户端

1.4.2.1 尝试进行连接

  因为 TCP 协议是面向连接的,服务器已经处于处理连接请求的状态了,客户端现在需要做的就是尝试进行连接,使用 connect 函数进行连接。

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

参数解读:

  • sockfd 需要进行连接的套接字
  • addr 服务器的 sockaddr 结构体信息
  • addrlen 服务器的 sockaddr 结构体大小

返回值:连接成功返回 0,连接失败返回 -1.

  在连接过程中,可能遇到很多问题,比如 网络传输失败、服务器未启动 等,这些问题的最终结果都是客户端连接失败,如果按照之前的逻辑(失败就退出),那么客户端的体验感会非常不好,因此在面对连接失败这种常见问题时,客户端应该尝试重连,如果重连数次后仍然失败,才考虑终止进程

注意: 在进行重连时,可以使用 sleep() 等函数使程序睡眠一会,给网络恢复留出时间

StartClient() 启动客户端函数 — 位于 client.hpp 中的client 类.

#pragma once#include<iostream>
#include<string>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include"err.hpp"
#include<cerrno>
#include<cstring>
#include<unistd.h>
namespace My_client{class client{public:client(const std::string& ip,const uint16_t port):server_ip(ip),server_port(port){}~client(){}//初始化客户端void InitClient(){// 1. 创建套接字_sock = socket(AF_INET, SOCK_STREAM, 0);if (_sock == -1){std::cerr << "Create Socket Fail!" << strerror(errno) << std::endl;exit(SOCKET_ERR);}   std::cout << "Create Sock Succeess! " << _sock << std::endl;}// 启动客户端void StartClient(){//填充服务器的sockaddr_int 结构体信息struct sockaddr_in server;socklen_t len=sizeof(server);bzero(&server,len);server.sin_family = AF_INET;inet_aton(server_ip.c_str(), &server.sin_addr); // 将点分十进制转化为二进制IP地址的另一种方法server.sin_port = htons(server_port);//尝试重连五次int n=5;while(n){int ret = connect(_sock,(const struct sockaddr*)&server,len);if(ret==0){// 连接成功,可以跳出循环break;}// 尝试进行重连std::cerr << "网络异常,正在进行重连... 剩余连接次数: " << --n << std::endl;sleep(1);}// 如果剩余重连次数为 0,证明连接失败if(n == 0){std::cerr << "连接失败! " << strerror(errno) <<    std::endl;close(_sock);exit(CONNECT_ERR);//新加错误标识符}// 连接成功std::cout << "连接成功!" << std::endl;// 进行业务处理// Service();}private:int _sock;// 套接字std::string server_ip;//服务器ipuint16_t server_port; //服务器端口号};
}
1.4.2.2 业务处理

客户端在进行业务处理时,同样可以使用 readwrite 进行网络通信

Service() 业务处理函数 — 位于 client.hpp 客户端头文件中的 TcpClient

// 业务处理
void Service()
{char buff[1024];std::string who = server_ip + "-" + std::to_string(server_port);while(true){// 由用户输入信息std::string msg;std::cout << "Please Enter >> ";std::getline(std::cin, msg);// 发送信息给服务器write(_sock, msg.c_str(), msg.size());// 接收来自服务器的信息ssize_t n = read(_sock, buff, sizeof(buff) - 1);if(n > 0){// 正常通信buff[n] = '\0';std::cout << "Client get: " << buff << " from " << who << std::endl;}else if(n == 0){// 读取到文件末尾(服务器关闭了)std::cout << "Server " << who  << " quit!" << std::endl;close(_sock); // 关闭文件描述符break;}else{// 读取异常std::cerr << "Read Fail!" << strerror(errno) << std::endl;close(_sock); // 关闭文件描述符break;}}
}

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

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

相关文章

基于Zookeeper 简单实现分布式任务协调组件

优质博文&#xff1a;IT-BLOG-CN 一、什么是 Zookeeper ZooKeeper是一个分布式的&#xff0c;开放源码的分布式应用程序协调服务&#xff0c;是Google的Chubby一个开源的实现&#xff0c;是Hadoop和Hbase的重要组件。 它是一个为分布式应用提供一致性服务的软件&#xff0c;提…

【管理】杨三角模型

企业成功 成功 战略 X 组织能力 1&#xff09;组织能力对于企业的重要性是和战略一样的&#xff1b; 2&#xff09;组织能力必须与战略适配&#xff0c;才能共同造就企业的成功。 员工思维&#xff1a;员工愿不愿意参与战略落地的实践&#xff1f;员工每天所关心、追求和重视的…

【C++】哈希封装map与set

目录 前言&#xff1a; 一&#xff0c;底层哈希结构 1-1&#xff0c;迭代器的封装 1-2&#xff0c;哈希表的封装 二&#xff0c;unordered_map的封装 三&#xff0c;unordered_set的封装 前言&#xff1a; 上一篇文章说明了哈希结构&#xff0c;这一篇文章来说明如何使用…

安装WSL2

PS C:\Users\pc> wsl --set-default-version 2 有关与 WSL 2 关键区别的信息&#xff0c;请访问 https://aka.ms/wsl2操作成功完成。PS C:\Users\pc> wsl --update 正在检查更新。 已安装最新版本的适用于 Linux 的 Windows 子系统。PS C:\Users\pc> wsl --shutdownPS…

PyTorch深度解析:Tensor——神经网络的核心构建块

在深度学习和神经网络的研究与应用中&#xff0c;Tensor&#xff08;张量&#xff09;无疑是一个核心概念。特别是在PyTorch这一强大的深度学习框架中&#xff0c;Tensor更是扮演了举足轻重的角色。本文将深入探讨PyTorch中的Tensor&#xff0c;从其基本定义、特性、操作到实际…

回溯算法练习day.3

39.组合总和 链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a; 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 &#xff0c;并以列表形式返…

uniapp Android 插件开发教程

一、下载uniapp提供的SDK Android 离线SDK - 正式版 | uni小程序SDK 二、在uniapp创建一个项目 查看包名&#xff1a;发行--> 原生app 云打包 三、进入dcloud官网 开发者中心 进入 应用管理 --> 我的应用 --> 点击应用名称-->各平台信息-->新增 这里需要这…

SQLite FTS3 和 FTS4 扩展(三十二)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLite FTS5 扩展&#xff08;三十&#xff09; 下一篇&#xff1a;SQLite—系列文章目录 概述 FTS3 和 FTS4 是 SQLite 虚拟表模块&#xff0c;允许用户执行 对一组文档进行全文搜索。最常见&#xff08;和最…

paddle.net怎么付款?paddle.net怎么订阅?

有需要的小伙伴可以使用Fomepay的卡进行订阅支付&#xff0c;我这里使用的是491090卡段&#xff0c;开卡步骤很简单&#xff0c;点击获取卡片 1、注册 2、填写姓名使用拼音或者英文名都可以 3、支付宝或者微信支付

基于51单片机的数字万用表设计

基于51单片机的数字万用表设计 &#xff08;仿真&#xff0b;程序&#xff0b;原理图PCB&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 1.能够切换测量电压、电流、电阻&#xff1b; 2.数码管实时显示测量值&#xff1b; 3.短路报警&#xff1b; 4.测量…

Vue.extend()和我的两米大砍刀

Vue.extends是什么&#xff1f; 一个全局API,用于注册并挂载组件。 传统的引用组件的方式是使用import直接引入&#xff0c;但是使用Vue.extends()也可以实现。 使用规则 <div id"mount-point"></div>// 创建构造器 var Profile Vue.extend({templat…

Spring 声明式事务控制

1. 编程式事务控制相关对象 1.1 PlatformTransactionManager PlatformTransactionManager 接口是 spring 的事务管理器&#xff0c;它提供了我们常用的操作事务的方法。 PlatformTransactionManager 是接口类型&#xff0c;不同的 Dao 层技术则有不同的实现类。例如:Dao层技…

3分钟看懂Microchip 32位MCU CAN模块的配置

文章目录 CAN模块系统框图Microchip MCC Harmony下CAN模块配置选项CAN模块工作模式CAN模块中断模式CAN工作速率Bit Timing Calculation配置CAN 接收的配置CAN 发送的配置CAN 过滤器工作流程说明CAN 过滤器的配置 CAN模块系统框图 CAN的英文全称&#xff1a;Control Area Networ…

连续时间折线图的前后端实现

技术栈 vue3VChartegg.jsMySQL 需求 根据已有任务数据&#xff0c;获取连续天的任务完成的数量&#xff0c;并且通过接口返回后做成图表。预期数据如下&#xff1a; [{"x": "2024-01-01","y": 0},{"x": "2024-01-02",&q…

Python实现KDJ工具判断信号:股票技术分析的工具系列(8)

Python实现KDJ工具判断信号&#xff1a;股票技术分析的工具系列&#xff08;8&#xff09; 介绍算法公式 代码rolling函数介绍完整代码data代码KDJ.py 介绍 KDJ是一种技术指标&#xff0c;用于衡量价格动量&#xff0c;帮助交易者识别趋势的强度和转折点。 先看看官方介绍&am…

Vue入门篇:概念,快速入门,插值表达式,核心特性,基本Vue指令

目录 1.Vue是什么2.快速入门3.插值表达式{{}}1.作用:2.语法:3.插值表达式的注意点: 4.Vue响应式核心特性5.Vue指令 1.Vue是什么 Vue是一个流行的JavaScript框架&#xff0c;用于构建用户界面。它是一种用于构建单页面应用程序&#xff08;SPA&#xff09;的渐进式框架&#xff…

机器学习系统的设计

1.混淆矩阵 混淆矩阵作用就是看一看在测试集样本集中&#xff1a; 真实值是 正例 的样本中&#xff0c;被分类为 正例 的样本数量有多少&#xff0c;这部分样本叫做真正例&#xff08;TP&#xff0c;True Positive&#xff09;&#xff0c;预测为真&#xff0c;实际为真真实值…

NtripShare2024年第一季度主要技术进展

迷迷糊糊又是一个月没有写点什么&#xff0c;近期想清楚NtripShare在2024的要做什么事情&#xff0c;暂且将NtripShare要做的主要事情为搭建由软件与硬件之间的技术桥梁。 在过去的几年时间里NtripShare对硬件方面一直是规避的态度&#xff0c;今年开始要做一点软硬件搭界的技…

mmcv bug记录

图像分类任务要用到mmcv框架&#xff0c;记录遇到的问题 1. Can‘t import build_from_cfg from mmcv. 解决命令&#xff1a;pip install openmim && mim install mmcv-full 2. python分布式训练 解决方案&#xff1a; 租用多张A40卡&#xff0c;执行下述命令&…