当前位置: 首页 > 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…...

浅谈 React Hooks

React Hooks 是 React 16.8 引入的一组 API&#xff0c;用于在函数组件中使用 state 和其他 React 特性&#xff08;例如生命周期方法、context 等&#xff09;。Hooks 通过简洁的函数接口&#xff0c;解决了状态与 UI 的高度解耦&#xff0c;通过函数式编程范式实现更灵活 Rea…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

mongodb源码分析session执行handleRequest命令find过程

mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程&#xff0c;并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令&#xff0c;把数据流转换成Message&#xff0c;状态转变流程是&#xff1a;State::Created 》 St…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…...

大模型多显卡多服务器并行计算方法与实践指南

一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...

人机融合智能 | “人智交互”跨学科新领域

本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

【笔记】WSL 中 Rust 安装与测试完整记录

#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统&#xff1a;Ubuntu 24.04 LTS (WSL2)架构&#xff1a;x86_64 (GNU/Linux)Rust 版本&#xff1a;rustc 1.87.0 (2025-05-09)Cargo 版本&#xff1a;cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

掌握 HTTP 请求:理解 cURL GET 语法

cURL 是一个强大的命令行工具&#xff0c;用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中&#xff0c;cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...

十九、【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建

【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建 前言准备工作第一部分:回顾 Django 内置的 `User` 模型第二部分:设计并创建 `Role` 和 `UserProfile` 模型第三部分:创建 Serializers第四部分:创建 ViewSets第五部分:注册 API 路由第六部分:后端初步测…...

rknn toolkit2搭建和推理

安装Miniconda Miniconda - Anaconda Miniconda 选择一个 新的 版本 &#xff0c;不用和RKNN的python版本保持一致 使用 ./xxx.sh进行安装 下面配置一下载源 # 清华大学源&#xff08;最常用&#xff09; conda config --add channels https://mirrors.tuna.tsinghua.edu.cn…...