【Linux进阶之路】网络 —— “?“ (下)
文章目录
- 前言
- 一、概念铺垫
- 1.TCP
- 2.全双工
- 二、网络版本计算器
- 1. 原理简要
- 2. 实现框架&&代码
- 2.1 封装socket
- 2.2 客户端与服务端
- 2.3 封装与解包
- 2.4 请求与响应
- 2.5 对数据进行处理
- 2.6 主程序逻辑
- 3.Json的简单使用
- 总结
- 尾序
前言
在上文我们学习使用套接字的相关接口进行了编程,因此对网络编程有了一定的认识,可是我们之前只是以字符串的形式简单的收发信息,如果我们要发送和接受的信息更加复杂,比如:客户端发送一个结构体,服务端要如何接收这个结构体呢? 如果说还要对结构体的数据进行处理并返回呢?下面就让我们带着这些疑问开始今天的学习吧!
- 说明:
- 每台计算机的结构体的对齐方式可能会有所不同,因此不能直接发送结构体。
- 因此要将结构体里的数据要以特定的形式,即协议的方式发送和接收。
- 对数据处理后,还要以协议的方式发送给客户端,从而客户端收到并进行对应的处理。
一、概念铺垫
1.TCP
- 众所周知,TCP是可靠的传输控制协议,一般是通过三次握手和四次挥手来保证数据的传输是可靠的。
- 说明:下面只是简单的理解,后面博主详细讲解的。
- 三次握手 :

- 三次交互,建立连接。
- 四次挥手:

- 断开连接,就是要断的干净,避免之后一方进行死缠烂打。
2.全双工
- 所谓的全双工,就是服务端和客户端都是可以收消息和发消息的,例如UDP和TCP协议都是全双工的。
- UDP

- TCP

- 理解传输控制协议:
- 对于UDP来说,在传输层对于发消息不做控制,但是对于收消息如何处理,则全权交由UDP决定。
- 对于TCP来说,用户只负责将消息发送到发送和接收缓存区,但对于消息如何处理,则全权由TCP决定。
- 说明:处理一般涉及什么时候传,传多少,传错了怎么办等等。
- 从UDP与TCP相比较,TCP多了一个发送缓冲区,这在一定程度上可以体现TCP的可靠性。
二、网络版本计算器
1. 原理简要
- 因为我们做的是网络版本的计算器,数据格式设定为
[ 数据(空格)方法(空格)数据(换行符)]即可,而且在网络中我们一般是以字符串的形式进行发送的,因此我们还要将整形数据转换为字符串,便于之后的解析。- 数据的封装,为了能将
一个完整的数据解析出来,因此我们应该在数据的前面封装数据的长度,当截取数据时,我们按照长度截取即可检查是否可获取到一个完整的数据,并且长度应与数据分开,便于获取,这里我们用换行符作为分割符即可。这里实现了数据的封装也就间接的实现了对数据解包。
- 举一个体现自定义协议的例子,比如 [1 + 1]封装为 [5\n][1 + 1\n],数据按上面的封装,而服务器读取时,假如只读取到了[5\n 1 +],通过读取5这个字符串,转换为int,可以验证读取的报文是否是完整的报文,那么数据不是无法进行解包的,会直接返回。
- 因为
客户端和服务端都要遵循这种规则,即自定义协议是一种约定,因此双方都要遵守的,因此不存在数据被污染的情况,即网络中传输的数据都是符合要求的。- 因此客户端传输的数据可以被服务端正确的提取,提取之后,我们要进行解析和处理数据,并将处理后的数据以:【结果 返回码】,
返回码用于检查数据是否计算可靠,比如1 除 0 无法进行计算,设返回码为1表示除0错误。并以上述同样的方式进行封装,将封装之后的结果,返回给用户进行解析,并处理。
2. 实现框架&&代码
- 实现服务器和封装socket套接字。
- 对请求和响应分别进行序列化和反序列化。
- 对序列化的数据进行封装与解包。
- 服务器对解析的数据进行处理和返回。
- 代码框架:

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函数是对接收的消息的处理方法。
};
- 根据上面的信息,我们可以大致了解服务器的基本框架:
- 创建套接字,绑定套接字,监听套接字。
- 接收外面的请求,建立连接,接收信息。
- 调用处理信息的接口,返回处理之后的信息。
- 因此: 我们可以让服务器与处理信息的逻辑进行解耦,并且使用封装之后的套接字是很方便的。
- 客户端:
#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;
}
- 封装数据,我们将在报头处封装有效载荷的长度,并以换行符作为分割符。
- 解析数据,首先要找到有效载荷的长度,并检验是否存在一个完整的报文。
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;//同理。
};
- Request,是客户端对服务器发送的请求,要客户端进行序列化,服务端进行反序列化,并进行解析。
- 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的使用。
- 安装Json库
sudo yum install -y jsoncpp-devel
- 说明: 普通用户需要输入root密码并且要添加到系统的信任白名单中,所以这里建议直接su命令切到root用户直接安装。
- 简单使用
- 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;
}
- 编译运行查看结果
g++ test.cc -std=c++11 -ljsoncpp

总结
- 铺垫TCP三次握手,四次挥手的概念,以及理解全双工。
- 实现了自定义协议(封装报头) + 序列化与反序列化的 网络版本的计算器。
- 介绍了Json工具的基本使用。
了解自定义协议之后,我们将在下篇认识现成的应用层协议之Http。
尾序
我是舜华,期待与你的下一次相遇!
相关文章:
【Linux进阶之路】网络 —— “?“ (下)
文章目录 前言一、概念铺垫1.TCP2.全双工 二、网络版本计算器1. 原理简要2. 实现框架&&代码2.1 封装socket2.2 客户端与服务端2.3 封装与解包2.4 请求与响应2.5 对数据进行处理2.6 主程序逻辑 3.Json的简单使用 总结尾序 前言 在上文我们学习使用套接字的相关接口进行了…...
【AIGC】Stable Diffusion的建模思想、训练预测方式快速
在这篇博客中,将会用机器学习入门级描述,来介绍Stable Diffusion的关键原理。目前,网络上的使用教程非常多,本篇中不会介绍如何部署、使用或者微调SD模型。也会尽量精简语言,无公式推导,旨在理解思想。让有…...
JVM(类加载机制)
类加载就是 .class 文件, 从文件(硬盘) 被加载到内存(元数据区)中的过程 类加载的过程 加载: 找 .class 文件的过程, 打开文件, 读文件, 把文件读到内存中 验证: 检查 .class 文件的格式是否正确 .class 是一个二进制文件, 其格式有严格的说明 准备: 给类对象分配内存空间 (先在…...
C++ 实战项目之 Boost 搜索引擎
项目地址:https://gitee.com/Vertas/boost-searcher-project 1. 项目背景 日常生活中我们使用过很多搜索引擎,比如百度,搜狗,360搜索等。我们今天是要实现一个像百度这样的搜索引擎嘛?那是不可能的,因为像…...
部署LVS+Keepalived高可用群集(抢占模式,非抢占模式,延迟模式)
目录 一、LVSKeepalived高可用群集 1、实验环境 2、 主和备keepalived的配置 2.1 yum安装ipvsadm和keepalived工具 2.2 添加ip_vs模块并开启ipvsadm 2.3 修改keepalived的配置文件 2.4 调整proc响应参数,关闭linux内核的重定向参数响应 2.5 将主服务器的kee…...
性别和年龄的视频实时监测项目
注意:本文引用自专业人工智能社区Venus AI 更多AI知识请参考原站 ([www.aideeplearning.cn]) 性别和年龄检测 Python 项目 首先介绍性别和年龄检测的高级Python项目中使用的专业术语 什么是计算机视觉? 计算机视觉是使计算机能…...
【Spring面试题】
目录 前言 1.Spring框架中的单例bean是线程安全的吗? 2.什么是AOP? 3.你们项目中有没有使用到AOP? 4.Spring中的事务是如何实现的? 5.Spring中事务失效的场景有哪些? 6.Spring的bean的生命周期。 7.Spring中的循环引用 8.构造方法…...
打车代驾小程序开发 醉酒不用怕一键找代驾
近年来,随着我国私家车市场的不断扩大,驾驶员的安全驾驶意识不断提高,以及交通法规对酒后驾驶的严格把握,代驾市场的潜力也在迸发。代驾小程序开发平台成为了代驾人不可或缺的线上接单平台。那么代驾小程序开发需要实现哪些功能呢…...
蓝桥集训之统计子矩阵
统计子矩阵 核心思想:矩阵前缀和 双指针 用i和j双指针 遍历所有子矩阵的列用s和t双指针 遍历所有子矩阵的行求其子矩阵的和 若>k 将s向下移动 矩阵和必定减小(元素个数减少)直到满足<k 因为列一定 行数即为方案数(从t行往上数到s行 共t-s1个区间[t,t][t-1,t]…...
架构师十项全能 你会几个?
架构设计导论 架构师核心能力 架构设计原则 架构设计模式 架构设计核心维度 架构图绘制 企业架构设计 分布式架构理论 微服务架构设计 响应式架构设计 架构设计评估 单元化架构设计 服务网络架构设计 DDD领域驱动设计 技术选型 服务治理设计 安全架构设计 云架构设计 数据库架构…...
数据库(mysql)-新手笔记(主外键,视图)
主外键 主键(唯一性,非空性) 主键是数据库表中的一个或多个字段,其值唯一标识表中的每一行/记录。 唯一性: 主键字段中的每个值都必须是唯一的,不能有两个或更多的记录具有相同的主键值 非空性:主键字段不能包含NULL值。 外键(引用完整 …...
西门子PLC的交互界面怎样设计?
西门子PLC的交互界面设计集中于提供一个直观、多功能且用户友好的环境,旨在使工程师和技术人员能够有效地进行编程、监控和维护。下面是一些设计西门子PLC交互界面时的关键考虑因素: 1. **图形化编程环境**:设计时,重点在于提供直…...
备份 ChatGPT 的聊天纪录
备份 ChatGPT 的聊天纪录 ChatGPT 在前阵子发生了不少次对话纪录消失的情况,让许多用户觉得困扰不已,也担心自己想留存的聊天记录消失不见。 好消息是,OpenAI 在 2023 年 4 月 11 日推出了 ChatGPT 聊天记录备份功能,无论是免费…...
支持向量机 SVM | 线性可分:软间隔模型
目录 一. 软间隔模型1. 松弛因子的解释小节 2. SVM软间隔模型总结 线性可分SVM中,若想找到分类的超平面,数据必须是线性可分的;但在实际情况中,线性数据集存在少量的异常点,导致SVM无法对数据集线性划分 也就是说&…...
基于Java的生活废品回收系统(Vue.js+SpringBoot)
目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容三、界面展示3.1 登录注册3.2 资源类型&资源品类模块3.3 回收机构模块3.4 资源求购/出售/交易单模块3.5 客服咨询模块 四、免责说明 一、摘要 1.1 项目介绍 生活废品回收系统是可持续发展的解决方案,旨在鼓…...
Linux:好用的Linux指令
进程的Linux指令 1.查看进程信息 ps ajx | head -1 && ps ajx | grep 进程名创建一个进程后输入上述代码,会打印进程信息,当我们在code.exe中写入打印pid,ppid,这里也和进程信息一致。 while :; do ps ajx | he…...
Python Tkinter GUI 基本概念
归纳编程学习的感悟, 记录奋斗路上的点滴, 希望能帮到一样刻苦的你! 如有不足欢迎指正! 共同学习交流! 🌎欢迎各位→点赞 👍 收藏⭐ 留言📝如果停止,就是低谷…...
Python实习生(自动化测试脚本开发) - 面经 - TCL新技术有限公司
JD: 招聘流程: 2024.1.3 Boss直聘 沟通 2024.1.4 约面 2024.1.6 上午面试 面试流程: 上来第一步,直接问Python基础语法,讲一下基础的数据类型 就记得元组和字典 分别具体说一下元组和字典 流程控制语句有哪些&…...
遥遥领先!基于transformer变体的时间序列预测新SOTA!
目前,以CNN、RNN和 Transformer 模型为代表的深度学习算法已经超越了传统机器学习算法,成为了时间序列预测领域一个新的研究趋向。这其中,基于Transformer架构的模型在时间序列预测中取得了丰硕的成果。 Transformer模型因其强大的序列建模能…...
Java实现从本地读取CSV文件数据
一、前言 最近项目中需要实现这样一个功能,就是从本地读取CSV文件,并以指定行作为标题行,指定行开始作为数据读取行,读取数据并返回给前端,下面具体说下是如何通过java实现。 二、如何实现? 1.引入相关mav…...
深度解析MPC-HC:开源媒体播放器的技术架构与性能优化策略
深度解析MPC-HC:开源媒体播放器的技术架构与性能优化策略 【免费下载链接】mpc-hc MPC-HCs main repository. For support use our Trac: https://trac.mpc-hc.org/ 项目地址: https://gitcode.com/gh_mirrors/mpc/mpc-hc 在多媒体播放领域,Windo…...
别再折腾WSL了!用Docker Desktop 5分钟在Windows上跑通CP2K 2025.1
5分钟在Windows上部署CP2K 2025.1:Docker方案全指南 对于计算化学和材料科学领域的研究者来说,CP2K作为一款强大的原子模拟软件包,其功能覆盖从量子化学计算到分子动力学模拟的广泛场景。然而,传统在Windows系统上部署CP2K往往需…...
LinkSwift:八大主流网盘直链下载的终极解决方案
LinkSwift:八大主流网盘直链下载的终极解决方案 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼云盘 /…...
告别屏幕偏色!用高通QDCM 6.0 + CA-410为你的安卓设备做一次专业级色彩校准
高通QDCM 6.0与CA-410联袂:解锁安卓设备专业级色彩校准全流程 当你在不同设备上查看同一张照片时,是否发现色彩表现天差地别?专业设计师的作品在手机上显示偏黄,视频创作者的内容在平板上泛青——这些恼人的色差问题,根…...
程序员和科研党必备:用site、filetype、intitle语法,5分钟精准挖到技术文档和论文
程序员和科研党必备:5分钟掌握精准搜索技术文档与论文的终极指南 在信息爆炸的时代,程序员调试一个框架的API参数、科研人员追踪某篇论文的引用文献、技术作者查找某个开源项目的设计文档,往往需要耗费数小时在浩如烟海的网络资源中筛选有效信…...
解锁SketchUp 3D打印新维度:深度探索STL插件技术指南
解锁SketchUp 3D打印新维度:深度探索STL插件技术指南 【免费下载链接】sketchup-stl A SketchUp Ruby Extension that adds STL (STereoLithography) file format import and export. 项目地址: https://gitcode.com/gh_mirrors/sk/sketchup-stl 你是否曾在S…...
GHelper:华硕笔记本性能控制的终极轻量级解决方案
GHelper:华硕笔记本性能控制的终极轻量级解决方案 【免费下载链接】g-helper Lightweight, open-source control tool for ASUS laptops and ROG Ally. Manage performance modes, fans, GPU, battery, and RGB lighting across Zephyrus, Flow, TUF, Strix, Scar, …...
小龙虾的自我养成之路
我为什么会发出这个疑问呢?是因为我研究Web开发中的一个问题时,请求体在 Filter(过滤器)处被读取了之后,在 Controller(控制层)就读不到值了,使用 RequestBody 的时候。 无论是字节流…...
保姆级教程:用ESP32和Mixly做个电压监测器,手机实时看数据还能微信报警
智能家居电压监测系统:用ESP32与Mixly打造实时报警装置 最近在整理工作室时,发现角落里闲置的ESP32开发板,突然想到可以用它做个实用的家庭电压监测器。家里老房子电路老化,时不时会出现电压不稳的情况,之前烧坏过两台…...
开发者被动收入流:3个自动化方案
面向软件测试从业者的专业实践指南在追求职业发展的道路上,许多软件测试工程师将大量精力投入到发现缺陷、编写脚本和保障质量中,却常常陷入“用时间换金钱”的线性增长困境。然而,随着技术工具与平台生态的成熟,一种新的可能性正…...
