Linux网络 TCP socket

TCP简介

TCP(Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。它位于OSI模型的第四层,主要为应用层提供数据传输服务。TCP通过三次握手建立连接,确保数据在发送和接收过程中的准确性和顺序性。

TCP的主要特点

  1. 可靠性:TCP通过序列号、确认应答、超时重传等机制保证数据可靠传输。
  2. 面向连接:通信双方在传输数据前需要建立连接,通信结束后释放连接。
  3. 流量控制:通过滑动窗口机制,TCP可以控制数据的发送速度,避免接收方缓冲区溢出。
  4. 拥塞控制:TCP可以根据网络状况调整发送速率,减少网络拥塞。
  5. 全双工通信:TCP连接允许数据在两个方向上同时传输。

TCP与UDP的比较

与TCP不同,用户数据报协议(UDP)是无连接的、不可靠的传输层协议。UDP适用于对实时性要求高、但可以容忍少量数据丢失的应用,如视频通话、在线游戏等。而TCP则适用于对数据完整性要求高的应用,如文件传输、电子邮件等。

函数介绍

socket()

int socket(int domain, int type, int protocol);
  • 功能:创建一个新的套接字。
  • 参数说明:
    • domain:指定协议族,如 AF_INET(IPv4)或 AF_INET6(IPv6)。
    • type:指定套接字类型,如 SOCK_STREAM(TCP)或 SOCK_DGRAM(UDP),由于使用TCP协议,所以使用SOCK_STREAM,表示面向流的传输协议。
    • protocol:指定协议类型,通常为 0,表示使用默认协议。
  • 返回值:成功时返回一个新的套接字描述符,失败时返回 -1。该套接字描述符本质与文件描述符一样,应用程序可以像读写文件一样用 read/write 在网络上收发数据。

bind()

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • 功能:将套接字绑定到指定的地址和端口。服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接; 服务器需要调用 bind 绑定一个固定的网络地址和端口号,客户端无需手动绑定。
  • 参数说明:
    • sockfd:由 socket() 函数返回的套接字描述符。
    • addr:指向包含地址和端口信息的 sockaddr 结构的指针。
    • addrlensockaddr 结构的长度。由于struct sockaddr *是一个通用指针类型,addr 参数实际上可以接受多种协议的 sockaddr 结构体,而它们的长度各不相同,所以需要第三个参数 addrlen指定结构体的长度。
我们的程序中对 addr 参数是这样初始化的 :
struct sockaddr_in socket;
bzero(&socket,sizeof(socket)); //将整个结构体清零
socket.sin_family=AF_INET; //设置地址类型为 AF_INET
socket.sin_port=htons(SERVER_PORT); //设置端口号,如8080
socket.sin_addr.s_addr=inet_addr(SERVER_IP); //设置IP地址,
//如"127.0.0.1",如果地址为 INADDR_ANY, 这个宏表示本地的任意 IP
//地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个 IP 地址, 
//这样设置可以在所有的 IP 地址上监听, 直到与某个客户端建立了连接
//时才确定下来到底用哪个 IP 地址
  • 返回值:成功时返回 0,失败时返回 -1。

listen()

int listen(int sockfd, int backlog);
  • 功能:服务器开始监听连接请求。
  • 参数说明:
    • sockfd:由 socket() 函数返回的套接字描述符。
    • backlog:指定连接请求队列的最大长度,最多允许有 backlog 个客户端处于连接等待状态, 如果接收到更多的连接请求就忽略。
  • 返回值:成功时返回 0,失败时返回 -1。
int _listensockfd = socket(AF_INET, SOCK_STREAM, 0);// 创建socket
struct sockaddr_in socket;
bzero(&socket,sizeof(socket)); //将整个结构体清零
socket.sin_family=AF_INET; //设置地址类型为 AF_INET
socket.sin_port=htons(SERVER_PORT); //设置端口号,如8080
socket.sin_addr.s_addr=inet_addr(SERVER_IP); 
int ret = bind(_listensockfd, (struct sockaddr *)&socket, sizeof(socket));
ret = listen(_listensockfd, BACKLOG);// 将socket设置为监听状态

accept()

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • 功能:接受客户端的连接请求。
  • 参数说明:
    • sockfd:由 socket() 函数返回的套接字描述符。
    • addr:指向用于存储客户端地址信息的 sockaddr 结构的指针,如果给 addr 参数传NULL,表示不关心客户端的地址。
    • addrlen:指向 sockaddr 结构长度的指针。
  • 返回值:成功时返回一个新的套接字描述符,代表与客户端的连接,之后服务器和客户端之间的交流就通过该返回值进行,失败时返回 -1。
 struct sockaddr_in peer;socklen_t peerlen = sizeof(peer);int sockfd = accept(_listensockfd, (struct sockaddr *)&peer, &peerlen);

connect()

int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
  • 功能:客户端连接到指定的服务器,connect bind 的参数形式一致, 区别在于 bind 的参数是自己的地址, connect 的参数是对方的地址。
  • 参数说明:
    • sockfd:由 socket() 函数返回的套接字描述符。
    • serv_addr:指向包含服务器地址信息的 sockaddr 结构的指针。
    • addrlensockaddr 结构的长度。
  • 返回值:成功时返回 0,失败时返回 -1。
// 填写网络信息
struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(server_port);
server_addr.sin_addr.s_addr = inet_addr(server_ip.c_str());
// client 无需显示bind,connect连接时自动bind
int n = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));

send()

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
  • 功能:发送数据到已连接的套接字。
  • 参数说明:
    • sockfd:由 socket() 函数返回的套接字描述符。
    • buf:指向要发送数据的缓冲区的指针。
    • len:要发送的数据的长度。
    • flags:通常为 0,表示默认的发送行为。
  • 返回值:成功时返回发送的字节数,失败时返回 -1。

recv()

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
  • 功能:从已连接的套接字接收数据。
  • 参数说明:
    • sockfd:由 socket() 函数返回的套接字描述符。
    • buf:指向用于接收数据的缓冲区的指针。
    • len:缓冲区的长度。
    • flags:通常为 0,表示默认的接收行为。
  • 返回值:成功时返回接收的字节数,失败时返回 -1。

close()

int close(int sockfd);
  • 功能:关闭套接字。
  • 参数说明:
    • sockfd:由 socket() 函数返回的套接字描述符。
  • 返回值:成功时返回 0,失败时返回 -1。

案例 多线程远程命令执行

makefile

all: server client
server:TcpServermain.ccg++ -o $@ $^ -std=c++17 -lpthread
client:TcpClient.ccg++ -o $@ $^ -std=c++17
.PHONY:clean
clean:rm -f server client

Mutex.hpp

#pragma once
#include <iostream>
#include <pthread.h>
using namespace std;class Mutex
{
public:Mutex(const Mutex&)=delete;const Mutex& operator=(const Mutex&)=delete;Mutex(){pthread_mutex_init(&_lock,nullptr);}~Mutex(){pthread_mutex_destroy(&_lock);}void Lock(){pthread_mutex_lock(&_lock);}pthread_mutex_t * LockPtr(){return &_lock;}void Unlock(){pthread_mutex_unlock(&_lock);}
private:pthread_mutex_t _lock;
};
class LockGuard
{public:LockGuard(Mutex& m):_mutex(m){_mutex.Lock();}~LockGuard(){_mutex.Unlock();}private:Mutex& _mutex;
};

Cond.hpp

#pragma once
#include"Mutex.hpp"
class Cond
{public:Cond(){pthread_cond_init(&_cond,nullptr);}~Cond(){pthread_cond_destroy(&_cond);}void Wait(Mutex& mutex){pthread_cond_wait(&_cond,mutex.LockPtr());}void Notify(){pthread_cond_signal(&_cond);}void NotifyAll(){pthread_cond_broadcast(&_cond);}private:pthread_cond_t _cond;
};

Thread.hpp

#pragma once
#include <pthread.h>
#include <iostream>
#include <functional>
#include <string>
#include <unistd.h>
using namespace std;
using func_t = function<void(string)>;
static int number = 1;
enum STATUS
{NEW,RUNNING,STOP
};
class Thread
{
private:static void *Routine(void *arg){Thread *t = static_cast<Thread *>(arg);t->_func(t->_name);return nullptr;}public:Thread(func_t func): _func(func), _status(NEW), _joinable(true){_name = "Thread-" + to_string(number++);_pid = getpid();}bool Start(){if (_status != RUNNING){_status = RUNNING;int n = pthread_create(&_tid, nullptr, Routine, this);if (n != 0){return false;}return true;}return false;}bool Stop(){if (_status == RUNNING){_status = STOP;int n = pthread_cancel(_tid);if (n != 0){return false;}return true;}return false;}bool Join(){if (_joinable){_status = STOP;int n = pthread_join(_tid, nullptr);if (n != 0){return false;}return true;}return false;}void Detach(){_joinable = false;pthread_detach(_tid);}string Name(){return _name;}
private:string _name;pthread_t _tid;pid_t _pid;STATUS _status;bool _joinable;func_t _func;
};

ThreadPool.hpp

#pragma once
#include <iostream>
#include <string>
#include <queue>
#include <vector>
#include <memory>
#include "Mutex.hpp"
#include "Cond.hpp"
#include "Thread.hpp"
using thread_t = shared_ptr<Thread>;
const static int defaultnum = 5;template <class T>
class ThreadPool
{
private:bool IsEmpty() { return _taskq.empty(); }void HandlerTask(string name){cout << "线程: " << name << ", 进入HandlerTask的逻辑" << endl;while (true){// 1. 拿任务T t;{LockGuard lockguard(_lock);while (IsEmpty() && _isrunning){_wait_num++;_cond.Wait(_lock);_wait_num--;}// 2. 任务队列为空 && 线程池退出了if (IsEmpty() && !_isrunning)break;t = _taskq.front();_taskq.pop();}// 2. 处理任务t(); // 规定,未来所有的任务处理,全部都是必须提供t()方法!}cout << "线程: " << name << " 退出";}ThreadPool(const ThreadPool<T> &) = delete;ThreadPool<T> &operator=(const ThreadPool<T> &) = delete;ThreadPool(int num = defaultnum) : _num(num), _wait_num(0), _isrunning(false){for (int i = 0; i < _num; i++){_threads.push_back(make_shared<Thread>(bind(&ThreadPool::HandlerTask, this, std::placeholders::_1)));cout << "构建线程" << _threads.back()->Name() << "对象 ... 成功" << endl;}}public:static ThreadPool<T> *getInstance(){if (instance == NULL){LockGuard lockguard(mutex);if (instance == NULL){cout << "单例首次被执行,需要加载对象..." << endl;instance = new ThreadPool<T>();instance->Start();}}return instance;}void Equeue(T in){LockGuard lockguard(_lock);if (!_isrunning)return;_taskq.push(in);if (_wait_num > 0)_cond.Notify();}void Start(){if (_isrunning)return;_isrunning = true;for (auto &thread_ptr : _threads){cout << "启动线程" << thread_ptr->Name() << " ... 成功";thread_ptr->Start();}}void Wait(){for (auto &thread_ptr : _threads){thread_ptr->Join();cout << "回收线程" << thread_ptr->Name() << " ... 成功";}}void Stop(){LockGuard lockguard(_lock);if (_isrunning){_isrunning = false; // 不工作// 1. 让线程自己退出(要唤醒) && // 2. 历史的任务被处理完了if (_wait_num > 0)_cond.NotifyAll();}}private:vector<thread_t> _threads;int _num;int _wait_num;std::queue<T> _taskq; // 临界资源Mutex _lock;Cond _cond;bool _isrunning;static ThreadPool<T> *instance;static Mutex mutex; // 只用来保护单例
};template <class T>
ThreadPool<T> *ThreadPool<T>::instance = NULL;
template <class T>
Mutex ThreadPool<T>::mutex; // 只用来保护单例

InetAddr.hpp

#pragma once
#include <string>
#include <iostream>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <cstring>
using namespace std;
class InetAddr
{
public:InetAddr();InetAddr(int port, string ip = ""): _port(port), _ip(ip){bzero(&_sockaddr, sizeof(_sockaddr));_sockaddr.sin_family = AF_INET;_sockaddr.sin_port = htons(_port);if (_ip.empty())_sockaddr.sin_addr.s_addr = INADDR_ANY;else_sockaddr.sin_addr.s_addr = inet_addr(_ip.c_str());}InetAddr(const struct sockaddr_in &sockaddr){_port = ntohs(sockaddr.sin_port);char buf[64];_ip = inet_ntop(AF_INET, &sockaddr.sin_addr, buf, sizeof(buf));}bool operator==(const InetAddr &other){return _ip == other._ip;}InetAddr operator=(const InetAddr &other){_ip = other._ip;_port = other._port;_sockaddr = other._sockaddr;return *this;}struct sockaddr *getSockaddr(){return (struct sockaddr *)&_sockaddr;}int getSockaddrLen(){return sizeof(_sockaddr);}const string &getIp(){return _ip;}int getPort(){return _port;}private:string _ip;int _port;struct sockaddr_in _sockaddr;
};

Common.hpp

enum
{SOCKET_ERROR=1,BIND_ERROR,LISTEN_ERROR,ACCEPT_ERROR,CONNECT_ERROR
};

TcpServer.hpp

#pragma once
#include <iostream>
#include <pthread.h>
#include <functional>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <memory>
#include "Common.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"
using namespace std;#define BACKLOG 8
using handler_t = function<string(string)>;
static const uint16_t gport = 8080;class TcpServer
{using task_t = function<void()>;struct ThreadData{int sockfd;TcpServer *self;};public:TcpServer(handler_t handler, int port = gport): _handler(handler), _port(port), _isrunning(false){}void InitServer(){// 创建socket_listensockfd = socket(AF_INET, SOCK_STREAM, 0);if (_listensockfd < 0){cout << "socket error" << endl;exit(SOCKET_ERROR);}cout << "socket create success,sockfd is: " << _listensockfd << endl;// 填写IP端口struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = INADDR_ANY;// bindint ret = bind(_listensockfd, (struct sockaddr *)&local, sizeof(local));if (ret < 0){cout << "bind error" << endl;exit(BIND_ERROR);}cout << "bind success" << endl;// 将socket设置为监听状态ret = listen(_listensockfd, BACKLOG);if (ret < 0){cout << "listen error" << endl;exit(LISTEN_ERROR);}cout << "listen success" << endl;}void HandleRequest(int sockfd) // TCP为全双工通信{char buffer[1024];while (true){// int n = read(sockfd, buffer, sizeof(buffer) - 1);int n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);if (n > 0){buffer[n] = 0;cout << buffer << endl;string cmd_result = _handler(buffer);// write(sockfd, cmd_result.c_str(), cmd_result.size());send(sockfd, cmd_result.c_str(), sizeof(cmd_result), 0);}else if (n == 0){// 如果读取的值为0,说明client退出cout << "client quit" << endl;break;}else{// 读取失败break;}}close(sockfd);}void Start(){_isrunning = true;while (_isrunning){// 获取新连接struct sockaddr_in peer;socklen_t peerlen = sizeof(peer);int sockfd = accept(_listensockfd, (struct sockaddr *)&peer, &peerlen);if (sockfd < 0){cout << "accept error" << endl;exit(ACCEPT_ERROR);}cout << "accept success,sockfd is: " << sockfd << endl;InetAddr addr(peer);cout << "client info: " << addr.getIp() << ":" << addr.getPort() << endl;// 将任务交给线程池ThreadPool<task_t>::getInstance()->Equeue([&](){this->HandleRequest(sockfd);});}}void Stop(){_isrunning = false;}private:int _listensockfd; // 监听socketuint16_t _port;bool _isrunning;// 处理上层任务的入口handler_t _handler;
};

TcpServermain.cc

#include "TcpServer.hpp"
#include "CommandExec.hpp"
int main()
{Command cmd;unique_ptr<TcpServer> server = make_unique<TcpServer>([&](string cmdstr){ return cmd.Execute(cmdstr); });server->InitServer();server->Start();return 0;
}

TcpClient.cc

#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <cstring>
using namespace std;
#include "Common.hpp"
//./client server_ip server_port
int main(int argc, char *argv[])
{if (argc != 3){cout << "Usage:./client server_ip server_port" << endl;return 0;}string server_ip = argv[1];int server_port = stoi(argv[2]);int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){cout << "socket create error" << endl;exit(SOCKET_ERROR);}// 填写网络信息struct sockaddr_in server_addr;bzero(&server_addr, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(server_port);server_addr.sin_addr.s_addr = inet_addr(server_ip.c_str());// client 无需显示bind,connect连接时自动bindint n = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));if (n < 0){cout << "connect error" << endl;exit(CONNECT_ERROR);}string message;while (true){char buffer[1024];cout << "input message: ";getline(cin, message);n = send(sockfd, message.c_str(), message.size(), 0);if (n > 0){int m = recv(sockfd, buffer, sizeof(buffer) - 1, 0);if (m > 0){buffer[m] = 0;cout << buffer;}elsebreak;}elsebreak;}close(sockfd);return 0;
}

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

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

相关文章

【从零开始入门unity游戏开发之——C#篇46】C#补充知识点——命名参数和可选参数

考虑到每个人基础可能不一样&#xff0c;且并不是所有人都有同时做2D、3D开发的需求&#xff0c;所以我把 【零基础入门unity游戏开发】 分为成了C#篇、unity通用篇、unity3D篇、unity2D篇。 【C#篇】&#xff1a;主要讲解C#的基础语法&#xff0c;包括变量、数据类型、运算符、…

大模型WebUI:Gradio全解11——Chatbot:融合大模型的多模态聊天机器人(6)

大模型WebUI&#xff1a;Gradio全解11——Chatbot&#xff1a;融合大模型的多模态聊天机器人&#xff08;6&#xff09; 前言本篇摘要11. Chatbot&#xff1a;融合大模型的多模态聊天机器人11.6 为LLM Agent构建UI11.6.1 使用transformers.agents11.6.2 使用Langchain agents11…

springboot基于前后端分离的摄影知识网站

Spring Boot 基于前后端分离的摄影知识网站 一、项目概述 Spring Boot 基于前后端分离的摄影知识网站&#xff0c;是一个专为摄影爱好者、专业摄影师打造的知识共享与交流平台。借助 Spring Boot 强大的后端架构搭建能力&#xff0c;结合前端独立开发的灵活性&#xff0c;整合…

VD:生成a2l文件

目录 前言Simulink合并地址 ASAP2 editor 前言 我之前的方法都是通过Simulink模型生成代码的过程中顺便就把a2l文件生成出来了&#xff0c;这时的a2l文件还没有地址&#xff0c;所以紧接着会去通过elf文件更新地址&#xff0c;一直以为这是固定的流程和方法&#xff0c;今天无…

物联网与前沿技术融合分析

【前言】 在科技发展的滚滚浪潮中&#xff0c;物联网作为连接物理世界与数字世界的桥梁&#xff0c;正日益凸显其关键作用。近年来&#xff0c;物联网与其他前沿技术的融合不断催生新的应用场景与创新模式&#xff0c;为各个领域带来了深刻变革。 物联网与人工智能的深度融合&…

【VRChat · 改模】Unity2019、2022的版本选择哪个如何决策,功能有何区别;

总览 1.Unity2019、2022的版本的选择 2.Unity添加着色器教程 一、Unity2019、2022的版本的选择 1.Unity2019 和 Unity2022 的区别&#xff0c;VRChat SDK 为何要区分两个版本 我是外行&#xff0c;最开始以为的是&#xff0c;2019 和 2022 的变化是基于这个模型本身的。 也…

Elasticsearch 和arkime 安装

安装一定要注意版本号&#xff0c;不然使用不了 这里Ubuntu使用ubuntu-20.04.6-desktop-amd64.iso elasticsearch这里使用Elasticsearch 7.17.5 | Elastic arkime这里使用wget https://s3.amazonaws.com/files.molo.ch/builds/ubuntu-20.04/arkime_3.4.2-1_amd64.deb 大家想…

【王树森搜素引擎技术】相关性03:文本匹配(TF-IDF、BM25、词距)

链路上的相关性模型 召回海选 打分量&#xff1a;数万模型&#xff1a;文本匹配分数线性模型或双塔BERT模型 粗排 打分量&#xff1a;数千模型&#xff1a;双塔BERT&#xff0c;或单塔BERT模型&#xff08;交叉&#xff09; 精排 打分量&#xff1a;数百模型&#xff1a;单塔B…

庄小焱——2024年博文总结与展望

摘要 大家好&#xff0c;我是庄小焱。岁末回首&#xff0c;2024 年是我在个人成长、博客创作以及生活平衡方面收获颇丰的一年。这一年的经历如同璀璨星辰&#xff0c;照亮了我前行的道路&#xff0c;也为未来的发展奠定了坚实基础。 1. 个人成长与突破 在 2024 年&#xff0c…

GraphRAG: Auto Prompt Tuning 实践

GraphRAG 的 Auto Prompt Tuning 功能是一个强大的工具&#xff0c;用于优化知识图谱的生成过程。以下是对该功能的详细介绍和分析&#xff1a; 自动提示调优&#xff08;Auto Prompt Tuning&#xff09; 1. 概念 GraphRAG 的自动提示调优功能旨在为特定领域的知识图谱生成创…

MySQL下载安装DataGrip可视化工具

目录 WinMySQL下载安装步骤MySQL配置添加环境变量 Mac下载安装配置环境变量 DataGrip可视化工具以Win为例了。Mac忘记截图了。步骤都一样 Win MySQL下载 官网&#xff1a; https://www.mysql.com/ 直接进下载界面&#xff1a; https://downloads.mysql.com/archives/installe…

ASP.NET Core - 配置系统之配置提供程序

ASP.NET Core - 配置系统之配置提供程序 3. 配置提供程序3.1 文件配置提供程序3.1.1 JSON配置提供程序3.1.2 XML配置提供程序3.1.3 INI配置提供程序 3.2 环境变量配置提供程序3.3 命令行配置提供程序3.4 内存配置提供程序3.5 配置加载顺序 3.6 默认配置来源 3. 配置提供程序 前…

网络安全 | 什么是正向代理和反向代理?

关注&#xff1a;CodingTechWork 引言 在现代网络架构中&#xff0c;代理服务器扮演着重要的角色。它们在客户端和服务器之间充当中介&#xff0c;帮助管理、保护和优化数据流。根据代理的工作方向和用途&#xff0c;代理服务器可分为正向代理和反向代理。本文将深入探讨这两种…

回归预测 | MATLAB实TCN时间卷积神经网络多输入单输出回归预测

效果一览 基本介绍 回归预测 | MATLAB实TCN时间卷积神经网络多输入单输出回归预测 …………训练集误差指标………… 1.均方差(MSE)&#xff1a;166116.6814 2.根均方差(RMSE)&#xff1a;407.5741 3.平均绝对误差&#xff08;MAE&#xff09;&#xff1a;302.5888 4.平均相对…

JavaScript 日期对象、DOM节点操作

日期对象 日期对象&#xff1a;使用new关键字实例化出来的对象 const date new Date() //这样就获取到了一个日期对象 直接打印对象&#xff1a; console.log(date) 打印到控制台如截图所示 时间对象内的方法们&#xff1a; 1.获取对象 对象.getFullYear() -- 获取当前…

Python制作简易PDF查看工具PDFViewerV1.0

PDFViewer PDF浏览工具&#xff0c;Python自制PDF查看工具&#xff0c;可实现基本翻页浏览功能&#xff0c;其它功能在进一步开发完善当中&#xff0c;如果有想一起开发的朋友&#xff0c;可以留言。本软件完全免费&#xff0c;自由使用。 软件界面简洁&#xff0c;有菜单栏、…

开源AI智能名片2+1链动模式S2B2C商城小程序源码在活动运营中的应用与优化

摘要&#xff1a;在数字化时代&#xff0c;开源AI智能名片21链动模式S2B2C商城小程序源码作为一种创新的商业应用模式&#xff0c;为企业提供了强大的运营工具。本文旨在探讨该源码在活动运营中的应用与优化策略&#xff0c;包括活动类型与时间节点的梳理、活动模块化设计、后台…

Centos7系统下安装和卸载TDengine Database

记录一下Centos7系统下安装和卸载TDengine Database 安装TDengine Database 先看版本信息 [root192 ~]# cat /etc/centos-release CentOS Linux release 7.9.2009 (Core) [root192 ~]# uname -r 3.10.0-1160.119.1.el7.x86_64 [root192 ~]# uname -a Linux 192.168.1.6 3.10…

【专题三:穷举vs暴搜vs深搜vs回溯vs剪枝】46. 全排列

1.题目解析 2.讲解算法原理 1.首先画出决策树&#xff0c;越详细越好 2.设计代码 全局变量 List<List<Integer>> retList<Integer> pathboolean[] check dfs函数 仅关心某一节点在干什么 细节问题回溯 干掉path最后一个元素修改check权限 剪枝 check中为…

【氮化镓】香港科技大学陈Kevin-单片集成GaN比较器

一、引言(Introduction) GaN HEMT的重要性 文章开篇便强调了氮化镓(GaN)高电子迁移率晶体管(HEMT)在下一代功率转换系统中的巨大潜力。GaN HEMT具备高开关频率、低导通电阻、高击穿电压以及宽工作温度范围等优势,使其成为功率电子领域的热门研究对象。这些特性使得GaN…