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

【Linux进阶之路】网络 —— “?“ (下)

文章目录

  • 前言
  • 一、概念铺垫
    • 1.TCP
    • 2.全双工
  • 二、网络版本计算器
    • 1. 原理简要
    • 2. 实现框架&&代码
      • 2.1 封装socket
      • 2.2 客户端与服务端
      • 2.3 封装与解包
      • 2.4 请求与响应
      • 2.5 对数据进行处理
      • 2.6 主程序逻辑
    • 3.Json的简单使用
  • 总结
  • 尾序

前言

 在上文我们学习使用套接字的相关接口进行了编程,因此对网络编程有了一定的认识,可是我们之前只是以字符串的形式简单的收发信息,如果我们要发送和接受的信息更加复杂,比如:客户端发送一个结构体,服务端要如何接收这个结构体呢? 如果说还要对结构体的数据进行处理并返回呢?下面就让我们带着这些疑问开始今天的学习吧!

  • 说明:
  1. 每台计算机的结构体的对齐方式可能会有所不同,因此不能直接发送结构体。
  2. 因此要将结构体里的数据要以特定的形式,即协议的方式发送和接收。
  3. 对数据处理后,还要以协议的方式发送给客户端,从而客户端收到并进行对应的处理。

一、概念铺垫

1.TCP

  • 众所周知,TCP是可靠的传输控制协议,一般是通过三次握手和四次挥手来保证数据的传输是可靠的。
  • 说明:下面只是简单的理解,后面博主详细讲解的。
  • 三次握手 :
    在这里插入图片描述
  • 三次交互,建立连接。
  • 四次挥手:

在这里插入图片描述

  • 断开连接,就是要断的干净,避免之后一方进行死缠烂打。

2.全双工

  • 所谓的全双工,就是服务端和客户端都是可以收消息和发消息的,例如UDP和TCP协议都是全双工的。
  1. UDP

在这里插入图片描述

  1. TCP
    在这里插入图片描述
  • 理解传输控制协议:
    1. 对于UDP来说,在传输层对于发消息不做控制,但是对于收消息如何处理,则全权交由UDP决定。
    2. 对于TCP来说,用户只负责将消息发送到发送和接收缓存区,但对于消息如何处理,则全权由TCP决定。
    • 说明:处理一般涉及什么时候传,传多少,传错了怎么办等等。
  • 从UDP与TCP相比较,TCP多了一个发送缓冲区,这在一定程度上可以体现TCP的可靠性。

二、网络版本计算器

1. 原理简要

  1. 因为我们做的是网络版本的计算器,数据格式设定为[ 数据(空格)方法(空格)数据(换行符)]即可,而且在网络中我们一般是以字符串的形式进行发送的,因此我们还要将整形数据转换为字符串,便于之后的解析。
  2. 数据的封装,为了能将一个完整的数据解析出来,因此我们应该在数据的前面封装数据的长度,当截取数据时,我们按照长度截取即可检查是否可获取到一个完整的数据,并且长度应与数据分开,便于获取,这里我们用换行符作为分割符即可。这里实现了数据的封装也就间接的实现了对数据解包。
  • 举一个体现自定义协议的例子,比如 [1 + 1]封装为 [5\n][1 + 1\n],数据按上面的封装,而服务器读取时,假如只读取到了[5\n 1 +],通过读取5这个字符串,转换为int,可以验证读取的报文是否是完整的报文,那么数据不是无法进行解包的,会直接返回。
  1. 因为客户端和服务端都要遵循这种规则,即自定义协议是一种约定,因此双方都要遵守的,因此不存在数据被污染的情况,即网络中传输的数据都是符合要求的。
  2. 因此客户端传输的数据可以被服务端正确的提取,提取之后,我们要进行解析和处理数据,并将处理后的数据以:【结果 返回码】,返回码用于检查数据是否计算可靠,比如1 除 0 无法进行计算,设返回码为1表示除0错误。并以上述同样的方式进行封装,将封装之后的结果,返回给用户进行解析,并处理。

2. 实现框架&&代码

  1. 实现服务器和封装socket套接字。
  2. 对请求和响应分别进行序列化和反序列化。
  3. 对序列化的数据进行封装与解包。
  4. 服务器对解析的数据进行处理和返回。
  • 代码框架:
    在这里插入图片描述

2.1 封装socket

 在之前我们实现代码时,主要目的是为了熟悉系统调用接口,熟练使用之后这里我们可以将Socket进行封装(包含客户端与服务端的常用的接口),方便我们之后进行调用:

#pragma once#include<iostream>
#include<string>#include<cstring>
#include<strings.h>
#include<unistd.h>//网络相关的头文件。
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>//小组件
#include"Log.hpp"using std::string;enum FAIL
{CREAT = 1,SIP_TO_NIP,BIND,LISTEN,ACCEPT,CONNECT,
};
uint16_t defaultport = 8080;
string defaultip = "0.0.0.0";
class Sock
{
public:Sock(uint16_t port = defaultport,string ip = defaultip):_port(port),_ip(ip){}~Sock(){if(_sockfd > 0){close(_sockfd);}}//创建套接字void Socket(){_sockfd = socket(AF_INET,SOCK_STREAM,0);if(_sockfd < 0){lg(CRIT,"socket create fail,reason is\%s,errno is %d",strerror(errno),errno);exit(CREAT);}lg(INFORE,"sockfd is %d,create success!",_sockfd);}//获取套接字int GetSocket(){return _sockfd; }//绑定void Bind(){sockaddr_in server;memset(&server,0,sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(_port);if(inet_pton(AF_INET,_ip.c_str(),&server.sin_addr) != 1){lg(CRIT,"string_ip to inet_ip fail,reason is %s\,errno is %d",strerror(errno),errno);exit(SIP_TO_NIP);}if(bind(_sockfd,(sockaddr*)&server,sizeof(server)) == -1){lg(CRIT,"bind fail,reason is %s,errno \is %d",strerror(errno),errno);exit(BIND);}lg(INFORE,"bind success!");}//监听void Listen(){if(listen(_sockfd,_backlog) == -1){lg(CRIT,"bind fail,reason is %s,errno is\%d",strerror(errno),errno);exit(LISTEN);}lg(INFORE,"lisen success!");}//接收连接int Accept(sockaddr_in* client,socklen_t* len){int fd = accept(_sockfd,(sockaddr*)client,len);if(fd < 0){lg(CRIT,"accept fail,reason is %s,\errno is %d",strerror(errno),errno);exit(ACCEPT);}uint16_t port = ntohs(client->sin_port);char ip[64] = {0};inet_ntop(AF_INET,&(client->sin_addr),ip,sizeof(ip) - 1);lg(INFORE,"accept success,get a new link,ip is\%s, port is %d",ip,port);return fd;}//连接void Connect(sockaddr_in* server){memset(server,0,sizeof(sockaddr_in));server->sin_family = AF_INET;server->sin_port = htons(_port);if(inet_pton(AF_INET,_ip.c_str(),\&(server->sin_addr)) == -1){lg(WARNNING,"inet_pton fail,reason is %s\,errno is %d",strerror(errno),errno);return;}int res = connect(_sockfd,\(sockaddr*)server,sizeof(sockaddr_in));if(res == -1){lg(CRIT,"connect fail,reason is %s,\errno is %d",strerror(errno),errno);exit(CONNECT);return;}lg(INFORE,"connect success!");}//从指定的套接字文件描述符里面读取数据。string Read(int fd){char buffer[128] = {0};ssize_t n = read(fd,buffer,sizeof(buffer) - 1);if(n < 0){lg(CRIT,"read fail,reason is %s,\errno is %d",strerror(errno),errno);sleep(1);return "";}else if(n == 0){lg(INFORE,"read nothing!");sleep(1);return "";}buffer[n] = '\0';return buffer;}//向指定的套接字文件描述符里面写数据。int Write(int fd,const string& str){ssize_t n = write(fd,str.c_str(),str.size());if(n < 0){lg(CRIT,"write fail,reason is %s,errno \is %d",strerror(errno),errno);sleep(1);return n;}else if(n == 0){lg(INFORE,"write nothing!");sleep(1);return n;}return n;  }void Close(int fd){close(fd);}
private:int _sockfd;uint16_t _port;string _ip;int _backlog = 5;//?
};
  • 以后我们直接用这个小组件即可,不用再手搓系统调用的接口了。

2.2 客户端与服务端

 这里我们使用上面封装的socket接口,实现的服务端与客户端。

  • 服务端
#pragma once 
#include<iostream>
#include<pthread.h>
#include<functional>
#include"../Tools/Socket.hpp"
#include"../Tools/Log.hpp"
using cal_t = function<string(string&)>;
class TcpServer;struct ThreadData
{ThreadData(int fd,TcpServer* tp):_fd(fd),_tp(tp){}int _fd;TcpServer* _tp;
};
class TcpServer
{
public:TcpServer(uint16_t port = 8080,cal_t cal = nullptr):_socket(port),_cal(cal){}~TcpServer(){}void Init(){_socket.Socket();_socket.Bind();_socket.Listen();}static void* Rouetine(void* args){//分离线程pthread_detach(pthread_self());auto thread_ptr = static_cast<ThreadData*>(args);TcpServer* tp = thread_ptr->_tp;int fd = thread_ptr->_fd;tp->Server(fd);return nullptr;}void Run(){for(;;){sockaddr_in client;socklen_t len = sizeof(client);int fd = _socket.Accept(&client,&len);pthread_t tid;pthread_create(&tid,nullptr,Rouetine,\new ThreadData(fd,this));}}void Server(int fd){string mes;for(;;){sleep(10);//收消息string str = _socket.Read(fd);//啥也没读到if(str == "") break;mes += str;//处理消息string ans;string echo_mes;//一次处理一批while((echo_mes = _cal(mes)) != ""){ans += echo_mes;}//没有读取到整段的报文或者报文为空。int res = _socket.Write(fd,ans);if(res <= 0) break;}_socket.Close(fd);}
private:Sock _socket;cal_t _cal; //这里的cal函数是对接收的消息的处理方法。
};
  • 根据上面的信息,我们可以大致了解服务器的基本框架:
  1. 创建套接字,绑定套接字,监听套接字。
  2. 接收外面的请求,建立连接,接收信息。
  3. 调用处理信息的接口,返回处理之后的信息。

  • 因此: 我们可以让服务器与处理信息的逻辑进行解耦,并且使用封装之后的套接字是很方便的。
  • 客户端:
#pragma once
#include<iostream>
#include<string>#include"../Tools/Log.hpp"
#include"../Tools/protocol.hpp"
#include"../Tools/Socket.hpp"using std::string;
string default_ip = "59.110.171.164";
uint16_t default_port = 8080;
class TcpClient
{
public:TcpClient(string ip = default_ip,uint16_t port = default_port):_sock(port,ip){}void Init(){}void Run(){string res;for(;;){_sock.Socket();sockaddr_in server;_sock.Connect(&server); int fd = _sock.GetSocket();while(true){cout << "Please Enter@";int x,y;char oper;cin >> x >> oper >> y;Request req(x,y,oper);string str = req.Serialize();//为了更好的体现自定义协议,这里我们多次进行写入。_sock.Write(fd,str);_sock.Write(fd,str);_sock.Write(fd,str);_sock.Write(fd,str);_sock.Write(fd,str);sleep(10);//一次读一批res += _sock.Read(fd);Response resq;//一次处理一批:while(resq.Deserialize(res));}_sock.Close(fd);}}
private:Sock _sock;
};
  • 说明:这里我们让客户端一次发一批消息,处理一批消息,服务端一次处理一批消息,发一批消息,这样更加能够体现自定义协议的功能。

2.3 封装与解包

//.....char space = ' ';
char newline = '\n';
//解包
string Decode(string& str)
{int pos = str.find(newline);if(pos == string::npos) return "";int len = stoi(str.substr(0,pos));int totalsize = pos + len + 2;//如果总的报文的长度大于读取的字符串的长度,说明没有一个完整的报文。if(totalsize > str.size()){return "";}//将有效载荷截取出来string actual_load = str.substr(pos + 1,len);//将完整的报文丢弃,便于下一次进行读取。str.erase(0,totalsize);return actual_load; 
}
//编码
string InCode(const string& str)
{//一个完整的报文:有效载荷的长度 + 换行符 + 有效载荷 + 换行。string text = to_string(str.size()) + newline + str + newline;return text;
}
  1. 封装数据,我们将在报头处封装有效载荷的长度,并以换行符作为分割符。
  2. 解析数据,首先要找到有效载荷的长度,并检验是否存在一个完整的报文。

2.4 请求与响应

struct Request
{Request(int x, int y, char oper):_x(x), _y(y), _oper(oper){}Request(){}bool Deserialize(string& str){cout << "+++++++++++++++++++++++++++++" << endl;//首先把字符串的报头和有效载荷进行分离string content = Decode(str);if(content == "") return false;//解析字符串:字符 + 空格 + 字符int left = content.find(space);int right = content.rfind(space);if (left + 1 != right - 1){//说明是无效的字符return false;}_x = stoi(content.substr(0, left));_y = stoi(content.substr(right + 1));_oper = content[left + 1];cout << "解析的字符串:"<< _x << _oper << _y << endl; cout << "待读取的字符串:" << endl << str << endl;cout << "-------------------------------" << endl;return true;}string Serialize(){string package;//首先对结构体进行编码//编码格式:字符 + 空格 + 操作符 + 空格 + 字符package = to_string(_x) + space + _oper + space\+ to_string(_y);	//对报文再进行封装package = InCode(package);return package;}int _x = 0;int _y = 0;char _oper = '0';//给出一个缺省值,避免编译器告警。
};struct Response
{Response(int res, int code):_res(res), _code(code){}Response(){}bool Deserialize(string& str){string content = Decode(str);if (content == "") return false;int pos = content.find(space);_res = stoi(content.substr(0,pos));_code = stoi(content.substr(pos + 1));//for debug:cout << "+++++++++++++++++++++++++++++++" << endl;cout <<"转换结果:"<< _res << " " << _code << endl;cout << "待读取的字符串" << endl << str << endl;cout << "-------------------------------" << endl;return true;}string Serialize(){string package = to_string(_res) + space \+ to_string(_code);package = InCode(package);return package;}int _res = 0;int _code = 0;//同理。
};
  1. Request,是客户端对服务器发送的请求,要客户端进行序列化,服务端进行反序列化,并进行解析。
  2. Response,是服务端对客户端发送的响应,要服务端进行序列化,客户端进行反序列化,并进行解析。

2.5 对数据进行处理

#include<iostream>
#include"../Tools/Log.hpp"
#include"../Tools/protocol.hpp"enum CAL 
{DIV_ZERO = 1,MOD_ZERO,
};
struct CalHelper
{string Cal(string& str){Request req;if(req.Deserialize(str) == false) return "";int x = req._x;int y = req._y;char op = req._oper;int res = 0, code = 0;switch(op){case '+':res = x + y;break;case '-':res = x - y;break;case '*': res = x * y;break;case '/':if(!y){code = DIV_ZERO;break;}res = x / y;break;case '%':if(!y){code = MOD_ZERO;break;}res = x % y;break;default:break;}return Response(res,code).Serialize();}
};

  • 这是服务器对客户端请求的处理,包含请求的反序列化和对数据的处理,以及结果的序列化。

2.6 主程序逻辑

  • client.cc
#include<iostream>
#include<memory>
#include"clientcal.hpp"
using std::unique_ptr;
void Usage(char* pragma_name)
{cout << endl << "Usage: " << pragma_name << \"+ ip + port[8000-8888]" << endl << endl; 
}
int main(int argc,char* argv[])
{if(argc != 3){Usage(argv[0]);return 1;}string ip = argv[1];uint16_t port = stoi(argv[2]);unique_ptr<TcpClient> cp(new TcpClient(ip,port));cp->Init();cp->Run();return 0;
}
  • server.cc
#include<iostream>
#include<memory>
#include<functional>
#include"server.hpp"
#include"servercal.hpp"
using std::unique_ptr;void Usage(char* pragma_name)
{cout << endl << "Usage: " << pragma_name \<< " + port[8000-8888]" << endl << endl; 
}
int main(int argc,char* argv[])
{if(argc != 2){Usage(argv[0]);return 1;}uint16_t port = stoi(argv[1]);CalHelper cal;unique_ptr<TcpServer> tp(new TcpServer(port,\bind(&CalHelper::Cal,&cal,placeholders::_1)));//bind是C++的一个接口,用于封装函数,便于使用。//因为cal是库里面的,因此要指定作用域,并传this指针,//绑定参数,进而封装出指定类型的函数。tp->Init();tp->Run();return 0;
}
  • bind的使用:跳转详见目录
  • 运行结果:
    在这里插入图片描述
  • 这里我们传数据,接收数据,处理数据都是一批一批的进行的,因此可以看见待处理的字符串。

3.Json的简单使用

  • 在上面实现的过程中,唯一比较难设计的就是序列化与反序列化的过程,上面我们为了进一步的理解,所以自己设计,但是市面上有一些简单好用的序列化与反序列化工具,下面我们介绍一种。

在网络中,序列化与反序列化有现成的工具,比如json 和 protobuf这两个工具,下面我们简单介绍Json的使用。

  1. 安装Json库
sudo yum install -y jsoncpp-devel
  • 说明: 普通用户需要输入root密码并且要添加到系统的信任白名单中,所以这里建议直接su命令切到root用户直接安装。
  1. 简单使用
  • test.cc
#include<iostream>
#include<string>
#include<jsoncpp/json/json.h>
using namespace std;
int main()
{Json::Value root;Json::StyledWriter writer;//Json::FastWriter writer;//StyleWriter打印起来比较有风格。//FastWrier打印比较紧凑,比较省空间。root["x"] = 1;root["y"] = 2;root["oper"] = '+';string res = writer.write(root);//序列化之后的结果:cout << "序列化之后的结果:" << endl;cout << res << endl;Json::Value des;Json::Reader r;r.parse(res,des);int x = des["x"].asInt();int y = des["y"].asInt();char oper = des["oper"].asInt();//反序列化的结果:cout << "反序列化的结果为:" << endl;cout << x << " " << oper << " " << y << endl;return 0;
}
  1. 编译运行查看结果
g++  test.cc -std=c++11 -ljsoncpp

在这里插入图片描述

总结

  1. 铺垫TCP三次握手,四次挥手的概念,以及理解全双工。
  2. 实现了自定义协议(封装报头) + 序列化与反序列化的 网络版本的计算器。
  3. 介绍了Json工具的基本使用。

 了解自定义协议之后,我们将在下篇认识现成的应用层协议之Http。

尾序

  • 我是舜华,期待与你的下一次相遇!

相关文章:

【Linux进阶之路】网络 —— “?“ (下)

文章目录 前言一、概念铺垫1.TCP2.全双工 二、网络版本计算器1. 原理简要2. 实现框架&&代码2.1 封装socket2.2 客户端与服务端2.3 封装与解包2.4 请求与响应2.5 对数据进行处理2.6 主程序逻辑 3.Json的简单使用 总结尾序 前言 在上文我们学习使用套接字的相关接口进行了…...

【AIGC】Stable Diffusion的建模思想、训练预测方式快速

在这篇博客中&#xff0c;将会用机器学习入门级描述&#xff0c;来介绍Stable Diffusion的关键原理。目前&#xff0c;网络上的使用教程非常多&#xff0c;本篇中不会介绍如何部署、使用或者微调SD模型。也会尽量精简语言&#xff0c;无公式推导&#xff0c;旨在理解思想。让有…...

JVM(类加载机制)

类加载就是 .class 文件, 从文件(硬盘) 被加载到内存(元数据区)中的过程 类加载的过程 加载: 找 .class 文件的过程, 打开文件, 读文件, 把文件读到内存中 验证: 检查 .class 文件的格式是否正确 .class 是一个二进制文件, 其格式有严格的说明 准备: 给类对象分配内存空间 (先在…...

C++ 实战项目之 Boost 搜索引擎

项目地址&#xff1a;https://gitee.com/Vertas/boost-searcher-project 1. 项目背景 日常生活中我们使用过很多搜索引擎&#xff0c;比如百度&#xff0c;搜狗&#xff0c;360搜索等。我们今天是要实现一个像百度这样的搜索引擎嘛&#xff1f;那是不可能的&#xff0c;因为像…...

部署LVS+Keepalived高可用群集(抢占模式,非抢占模式,延迟模式)

目录 一、LVSKeepalived高可用群集 1、实验环境 2、 主和备keepalived的配置 2.1 yum安装ipvsadm和keepalived工具 2.2 添加ip_vs模块并开启ipvsadm 2.3 修改keepalived的配置文件 2.4 调整proc响应参数&#xff0c;关闭linux内核的重定向参数响应 2.5 将主服务器的kee…...

性别和年龄的视频实时监测项目

注意&#xff1a;本文引用自专业人工智能社区Venus AI 更多AI知识请参考原站 &#xff08;[www.aideeplearning.cn]&#xff09; 性别和年龄检测 Python 项目 首先介绍性别和年龄检测的高级Python项目中使用的专业术语 什么是计算机视觉&#xff1f; 计算机视觉是使计算机能…...

【Spring面试题】

目录 前言 1.Spring框架中的单例bean是线程安全的吗? 2.什么是AOP? 3.你们项目中有没有使用到AOP&#xff1f; 4.Spring中的事务是如何实现的&#xff1f; 5.Spring中事务失效的场景有哪些&#xff1f; 6.Spring的bean的生命周期。 7.Spring中的循环引用 8.构造方法…...

打车代驾小程序开发 醉酒不用怕一键找代驾

近年来&#xff0c;随着我国私家车市场的不断扩大&#xff0c;驾驶员的安全驾驶意识不断提高&#xff0c;以及交通法规对酒后驾驶的严格把握&#xff0c;代驾市场的潜力也在迸发。代驾小程序开发平台成为了代驾人不可或缺的线上接单平台。那么代驾小程序开发需要实现哪些功能呢…...

蓝桥集训之统计子矩阵

统计子矩阵 核心思想&#xff1a;矩阵前缀和 双指针 用i和j双指针 遍历所有子矩阵的列用s和t双指针 遍历所有子矩阵的行求其子矩阵的和 若>k 将s向下移动 矩阵和必定减小(元素个数减少)直到满足<k 因为列一定 行数即为方案数(从t行往上数到s行 共t-s1个区间[t,t][t-1,t]…...

架构师十项全能 你会几个?

架构设计导论 架构师核心能力 架构设计原则 架构设计模式 架构设计核心维度 架构图绘制 企业架构设计 分布式架构理论 微服务架构设计 响应式架构设计 架构设计评估 单元化架构设计 服务网络架构设计 DDD领域驱动设计 技术选型 服务治理设计 安全架构设计 云架构设计 数据库架构…...

数据库(mysql)-新手笔记(主外键,视图)

主外键 主键(唯一性,非空性) 主键是数据库表中的一个或多个字段&#xff0c;其值唯一标识表中的每一行/记录。 唯一性: 主键字段中的每个值都必须是唯一的&#xff0c;不能有两个或更多的记录具有相同的主键值 非空性&#xff1a;主键字段不能包含NULL值。 外键(引用完整 …...

西门子PLC的交互界面怎样设计?

西门子PLC的交互界面设计集中于提供一个直观、多功能且用户友好的环境&#xff0c;旨在使工程师和技术人员能够有效地进行编程、监控和维护。下面是一些设计西门子PLC交互界面时的关键考虑因素&#xff1a; 1. **图形化编程环境**&#xff1a;设计时&#xff0c;重点在于提供直…...

备份 ChatGPT 的聊天纪录

备份 ChatGPT 的聊天纪录 ChatGPT 在前阵子发生了不少次对话纪录消失的情况&#xff0c;让许多用户觉得困扰不已&#xff0c;也担心自己想留存的聊天记录消失不见。 好消息是&#xff0c;OpenAI 在 2023 年 4 月 11 日推出了 ChatGPT 聊天记录备份功能&#xff0c;无论是免费…...

支持向量机 SVM | 线性可分:软间隔模型

目录 一. 软间隔模型1. 松弛因子的解释小节 2. SVM软间隔模型总结 线性可分SVM中&#xff0c;若想找到分类的超平面&#xff0c;数据必须是线性可分的&#xff1b;但在实际情况中&#xff0c;线性数据集存在少量的异常点&#xff0c;导致SVM无法对数据集线性划分 也就是说&…...

基于Java的生活废品回收系统(Vue.js+SpringBoot)

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容三、界面展示3.1 登录注册3.2 资源类型&资源品类模块3.3 回收机构模块3.4 资源求购/出售/交易单模块3.5 客服咨询模块 四、免责说明 一、摘要 1.1 项目介绍 生活废品回收系统是可持续发展的解决方案&#xff0c;旨在鼓…...

Linux:好用的Linux指令

进程的Linux指令 1.查看进程信息 ​​​​ps ajx | head -1 && ps ajx | grep 进程名创建一个进程后输入上述代码&#xff0c;会打印进程信息&#xff0c;当我们在code.exe中写入打印pid&#xff0c;ppid&#xff0c;这里也和进程信息一致。 while :; do ps ajx | he…...

Python Tkinter GUI 基本概念

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd;如果停止&#xff0c;就是低谷&#xf…...

Python实习生(自动化测试脚本开发) - 面经 - TCL新技术有限公司

JD&#xff1a; 招聘流程&#xff1a; 2024.1.3 Boss直聘 沟通 2024.1.4 约面 2024.1.6 上午面试 面试流程&#xff1a; 上来第一步&#xff0c;直接问Python基础语法&#xff0c;讲一下基础的数据类型 就记得元组和字典 分别具体说一下元组和字典 流程控制语句有哪些&…...

遥遥领先!基于transformer变体的时间序列预测新SOTA!

目前&#xff0c;以CNN、RNN和 Transformer 模型为代表的深度学习算法已经超越了传统机器学习算法&#xff0c;成为了时间序列预测领域一个新的研究趋向。这其中&#xff0c;基于Transformer架构的模型在时间序列预测中取得了丰硕的成果。 Transformer模型因其强大的序列建模能…...

Java实现从本地读取CSV文件数据

一、前言 最近项目中需要实现这样一个功能&#xff0c;就是从本地读取CSV文件&#xff0c;并以指定行作为标题行&#xff0c;指定行开始作为数据读取行&#xff0c;读取数据并返回给前端&#xff0c;下面具体说下是如何通过java实现。 二、如何实现&#xff1f; 1.引入相关mav…...

数据结构(一)——概述

一、绪论 1.1数据结构的基本概念 数据&#xff1a;用来描述客观事物的数、计算机中是字符及所有能输入并被程序识别和处理的符号的集合。 数据元素&#xff1a;数据的基本单位&#xff0c;一个数据元素可由若干数据项组成。 数据结构&#xff1a;指相互之间存在一种或多种特…...

昇腾芯片解析:华为自主研发的人工智能处理器全面分析

在当今科技发展的浪潮中&#xff0c;昇腾芯片作为一种新兴的处理器&#xff0c;正引起广泛的关注和讨论。升腾芯片究竟是由哪家公司生产的&#xff1f;这个问题一直困扰着许多人。下面小编将全面介绍、分析升腾芯片的生产商及各类参数、应用&#xff0c;以便读者对其有更全面的…...

新手做抖音小店怎么快速出体验分?教给大家一个方法!

大家好&#xff0c;我是电商糖果 新店怎么出体验分&#xff1f; 这是不是很多新店商家最苦恼事情&#xff1f; 因为没有体验分的店铺&#xff0c;平台不会给推流&#xff0c;开了精选联盟也没有办法带货。 总之就是运营的时候&#xff0c;比较受限。 那么抖音小店怎么快速出…...

Apollo决策规划 - EM planner

旨在对b站老王所讲的百度Apollo - EM planner算法做浓缩版总结 0 决策规划背景 基于图搜索 优点&#xff1a; 可以得到全局层面最优解&#xff0c;适用于比较低维数的规划问题 缺点&#xff1a; 规划问题维数较高时&#xff0c;面临指数爆炸问题 基于采样 优点&#xff1a;…...

Qt: 事件过滤器的更多用法

不懂事件循环怎么回事的可以看下面的文章 Qt事件循环完整流程 常规使用 定义一个窗口MainWindow &#xff0c;之后在窗口里添加一个事件过滤函数eventFilter&#xff0c;将窗口的某一个或一些字控件安装上事件过滤器。 这种情况下MainWindow 就是pushButton11的时间过滤器&am…...

解决:ModuleNotFoundError: No module named ‘paddle‘

错误显示&#xff1a; 原因&#xff1a; 环境中没有‘paddle’的python模块&#xff0c;但是您在尝试导入 解决方法&#xff1a; 1.普通方式安装&#xff1a; pip install paddlepaddle #安装命令 2.镜像源安装 pip install paddlepaddle -i https://pypi.tuna.tsinghua.e…...

上海雷卯可以解决YPbPr/ YCbCr接口 ESD/EOS静电浪涌问题

YPbPr /YCbCr 接口传输的是视频信号&#xff0c;不传输音频信号。YPbPr 和 YCbCr 都是视频信号的颜色编码格式&#xff0c;多应用于机顶盒&#xff08;Set-top box&#xff09;,TV电视&#xff0c;投影仪&#xff0c;游戏机和DVD播放器。 YPbPr&#xff1a;是一种模拟视频接口…...

【FPGA/IC】CRC电路的Verilog实现

前言 在通信过程中由于存在各种各样的干扰因素&#xff0c;可能会导致发送的信息与接收的信息不一致&#xff0c;比如发送数据为 1010_1010&#xff0c;传输过程中由于某些干扰&#xff0c;导致接收方接收的数据却成了0110_1010。为了保证数据传输的正确性&#xff0c;工程师们…...

go语言添加代理

LiteIDE 工具->管理 https://mirrors.aliyun.com/goproxy/或https://goproxy.cn,direct 命令行 go env -w GOPROXYhttps://goproxy.cn,direct...

kafka 可视化工具

kafka可视化工具 随着科技发展&#xff0c;中间件也百花齐放。平时我们用的redis&#xff0c;我就会通过redisInsight-v2 来查询数据&#xff0c;mysql就会使用goland-ide插件来查询&#xff0c;都挺方便。但是kafka可视化工具就找了半天&#xff0c;最后还是觉得redpandadata…...