Linux网络-自定义协议、序列化和反序列化、网络计算服务器的实现和Windows端客户端
文章目录
- 前言
- 一、自定义协议
- 传结构体对象
- 序列化和反序列化
- 什么是序列化?
- 反序列化
- 二、计算器服务端(线程池版本)
- 1.main.cc
- 2.Socket.hpp
- 3.protocol.hpp
- 4.Calculator.hpp
- 5.serverCal.hpp
- 6.threadPool.hpp
- 7.Task.hpp
- 8. log.hpp
- 客户端
- Windows客户端
- 运行
前言
我们已经学会了Tcp、Udp网络传输协议,并且之前我们也实现了简易的聊天室和翻译器。
我们知道,传输层是OS系统里就给我们写好的, 应用层才是我们自己需要去编写的,现在我们对应用层来进行一些初步的了解。
一、自定义协议
在之前我们使用Tcp和Udp服务进行网络通信,我们一直都是以字符串的形式互相发送消息。
那么我们就只能发送字符串吗? 当然不是,Udp是基于数据流进行网络通信,Udp是基于字节流进行网络通信,虽然我们对字节流和数据流并没有一个特别清晰的认识,但是我们可以知道的是,我们其实是可以传各种各样的类型进行通行的。
这里就提出一种方案
传结构体对象
从我们之前写的代码来看,只要我们在发送数据和接收数据时制定一个协议,每次只读写一个结构体对象的大小,那么我们就可以将该结构体进行通信。 而实际上,底层的很多库也确实是这么做的,但是这并不代表这种方式不存在明显弊端。
最大的弊端就是,你能保证在不同环境之下,同样的结构体类型在不同主机、不同环境下的大小是一样的吗。 结构体的大小涉及到许多,比如说结构体字节对齐,32位和64位系统下内置类型大小可能不同…
所以我们并不推崇这种方案。
而实际上我们其实更推崇传字符串的方式。
序列化和反序列化
什么是序列化?
在现实生活中,我们介绍自己,有些人习惯先说自己的名字,有些人习惯先说自己来自于哪一个城市。 如果你需要去统计大量的人的信息,最好就是先列一个表格,然后让他们严格按照表格上的个人信息顺序去介绍自己,这就是序列化。
再比如说我们今天要写一个网络版本的计算器,我可以写成1+1,也可以写成一加一,再也可以写写成1 + 1,中间带几个空格。 那么这样的话,我们服务器接受到的数据就是形形色色的,不利于我们去解析。
于是我们就制定一个协议,你必须要写成"1 + 1"的形式,否则就是违反协议!
反序列化
反序列化很简单,就比如说我们收到了一段数据,它是一个字符串形式的"1 + 1",我们就需要将该字符串进行解析,反序列化成int x = 1, char op = ‘+’ , int y = 1.
这也是一种反序列化。
二、计算器服务端(线程池版本)
1.main.cc
#include "serverCal.hpp"void Usage(const char *mes)
{std::cout << "Usage: " << mes << " port[8080-9000]" << std::endl;
}const std::string default_ip = "0.0.0.0";enum{Usage_Err = 1
};int main(int argc, char *argv[])
{if (argc != 2){Usage("./serverCal");exit(Usage_Err);}ServerCal sc;sc.Init(AF_INET, default_ip, atoi(argv[1]));sc.Run();return 0;
}
2.Socket.hpp
对套接字进行封装
#pragma once#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include "log.hpp"
enum
{Socket_Err = 1,Bind_Err,Listen_Err
};extern Log lg;const int backlog = 10;class Socket
{
public:Socket(): _sockfd(-1){}int Getfd(){return _sockfd;}void Init(){int socket_fd = socket(AF_INET, SOCK_STREAM, 0);if (socket_fd < 0){lg(Fatal, "Socket Create Failed...");exit(Socket_Err);}lg(Info, "Socket Create Succeeded...");_sockfd = socket_fd;}void Bind(const int sinfamily, const std::string &ip, const uint16_t port){memset(&_sockaddr, 0, sizeof _sockaddr);switch (sinfamily){case AF_INET:_sockaddr.sin_family = AF_INET;break;case AF_INET6:_sockaddr.sin_family = AF_INET6;break;}_sockaddr.sin_port = htons(port);inet_aton(ip.c_str(), &(_sockaddr.sin_addr));int n = bind(_sockfd, (const struct sockaddr *)&_sockaddr, sizeof _sockaddr);if (n < 0){lg(Fatal, "Bind Failed...");exit(Bind_Err);}lg(Info, "Bind Succeeded..., port: %d", port);}void Listen(){int n = listen(_sockfd, backlog);if (n < 0){lg(Fatal, "Listen Failed...");exit(Listen_Err);}lg(Info, "Listen Succeeded...");}int Accept(struct sockaddr_in *clientsock, socklen_t *len){int fd = accept(_sockfd, (sockaddr *)clientsock, len);if (fd < 0){lg(Warning, "Accept Failed...");return -1;}lg(Info, "Accept Succeeded..., Get A new Link, fd: %d", fd);return fd;}int Connect(const std::string &ip, const std::string &port){struct sockaddr_in serversock;serversock.sin_port = htons(atoi(port.c_str()));serversock.sin_family = AF_INET;inet_aton(ip.c_str(), &serversock.sin_addr);int n = connect(_sockfd, (const struct sockaddr *)&serversock, sizeof serversock);if (n < 0){lg(Warning, "Accept Failed...");return n;}lg(Info, "Connect Succeeded...");return n;}~Socket(){close(_sockfd);}private:int _sockfd;struct sockaddr_in _sockaddr;
};
3.protocol.hpp
序列化和反序列化的协议制定
#pragma once
#include <iostream>
#include <string>
#include "log.hpp"extern Log lg;
const char blank_space_sep = ' ';
const char protocol_sep = '\n';enum Code
{Div_Zero_Err = 1,Mod_Zeor_Err,Operatorr_Err
};class Request
{
public:Request() {} // 提供一个无参构造Request(int x, int y, char op): _x(x), _y(y), _operator(op) {}bool serialize(std::string *out_str){// 协议规定 字符串格式应序列化为"len\n""_x + _y\n"std::string main_body = std::to_string(_x);main_body += blank_space_sep;main_body += _operator;main_body += blank_space_sep;main_body += std::to_string(_y);*out_str = std::to_string(main_body.size());*out_str += protocol_sep;*out_str += main_body;*out_str += protocol_sep;return true;}bool deserialize(std::string &in_str){// 协议规定 in_str的格式应为"len\n""_x + _y\n..."size_t pos = in_str.find(protocol_sep);if (pos == std::string::npos){// 说明没找到'\n'lg(Warning, "Message Format Error..., No Found The First Second \\n");return false;} std::string sl = in_str.substr(0, pos);int len = std::stoi(sl); // 如果这里的sl不是一串数字,stoi就会抛异常! BUGint total_len = sl.size() + 1 + len + 1;if (in_str.size() < total_len){lg(Warning, "Message Format Error..., Lenth Error");return false;}if (in_str[total_len - 1] != '\n'){lg(Warning, "Message Format Error..., No Found The Second \\n");return false;}std::string main_body = in_str.substr(pos + 1, len);// main_body"_x + _y"int left = main_body.find(blank_space_sep);if (left == std::string::npos){// 说明没找到' 'lg(Warning, "Message Format Error..., No Found The First ' '");return false;}int right = main_body.rfind(blank_space_sep);if (left == right){// 说明只有一个' 'lg(Warning, "Message Format Error...,No Found The Second ' '");return false;}_x = std::stoi(main_body.substr(0, left)); // 如果这里的sl不是一串数字,stoi就会抛异常! BUG_y = std::stoi(main_body.substr(right + 1)); // 如果这里的sl不是一串数字,stoi就会抛异常! BUG_operator = main_body[left + 1];in_str.erase(0, total_len);return true;}void print(){std::cout << _x << " " << _operator << " " << _y << std::endl;}~Request() {}public:int _x;int _y;char _operator;
};class Respond
{
public:Respond() {} // 提供一个无参构造Respond(int result, int code): _result(result), _code(code) {}bool serialize(std::string *out_str){// 协议规定 字符串格式应序列化为"_result _code"*out_str = std::to_string(_result);*out_str += blank_space_sep;*out_str += std::to_string(_code);return true;}bool deserialize(const std::string &in_str){// 协议规定 in_str的格式应为"_result _code"size_t pos = in_str.find(blank_space_sep);if (pos == std::string::npos){// 没找到字符' 'lg(Warning, "Result Message Error...");return false;}_result = std::stoi(in_str.substr(0, pos));_code = std::stoi(in_str.substr(pos + 1));return true;}void print(){std::cout << _result << " " << _code << std::endl;}~Respond() {}public:int _result;int _code = -1; // 表示结果可信度 0表示可信
};
4.Calculator.hpp
计算器功能接口函数
#pragma once
#include"protocol.hpp"class Calculator{public:Calculator() {}Respond calculate(const Request& rq){Respond rs;switch (rq._operator){case '+':rs._result = rq._x + rq._y;break;case '-':rs._result = rq._x - rq._y;break;case '*':rs._result = rq._x * rq._y;break;case '/':if(rq._y == 0){lg(Warning,"Found Div Zero Error...");rs._code = Div_Zero_Err;return rs;}rs._result = rq._x / rq._y;break;case '%':if(rq._y == 0){lg(Warning,"Found Mod Zero Error...");rs._code = Mod_Zeor_Err;return rs;}rs._result = rq._x - rq._y;break;default:lg(Warning,"Found Operator Error...");rs._code = Operatorr_Err;return rs;}rs._code = 0;return rs;}
};
5.serverCal.hpp
代码如下(示例):
#pragma once#include "Socket.hpp"
#include "protocol.hpp"
#include "threadPool.hpp"
#include "Task.hpp"
class ServerCal
{
public:ServerCal(){}void Init(const int sinfamily, const std::string &ip, const uint16_t port){_listensock.Init();_listensock.Bind(sinfamily, ip, port);_listensock.Listen();}void Run(){ThreadPool<Task> *tp = ThreadPool<Task>::GetInstance();tp->Start();struct sockaddr_in client;while (true){memset(&client, 0, sizeof client);socklen_t len;int socketfd = _listensock.Accept(&client, &len);if (socketfd < 0)continue;tp->Push(socketfd);}}private:Socket _listensock;
};
6.threadPool.hpp
很熟悉的线程池封装
#pragma once#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <pthread.h>
#include <unistd.h>struct ThreadInfo
{pthread_t tid;std::string name;
};static const int defalutnum = 10;template <class T>
class ThreadPool
{
public:void Lock(){pthread_mutex_lock(&mutex_);}void Unlock(){pthread_mutex_unlock(&mutex_);}void Wakeup(){pthread_cond_signal(&cond_);}void ThreadSleep(){pthread_cond_wait(&cond_, &mutex_);}bool IsQueueEmpty(){return tasks_.empty();}std::string GetThreadName(const pthread_t tid){for (const auto &ti : threads_){if (ti.tid == tid)return ti.name;}return "None";}public:static void *HandlerTask(void *args){ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);std::string name = tp->GetThreadName(pthread_self());while (true){tp->Lock();while (tp->IsQueueEmpty()){tp->ThreadSleep();}T t = tp->Pop();tp->Unlock();t();}}void Start(){int num = threads_.size();for (int i = 0; i < num; i++){threads_[i].name = "thread-" + std::to_string(i + 1);pthread_create(&(threads_[i].tid), nullptr, HandlerTask, this);}}T Pop(){T t = tasks_.front();tasks_.pop();return t;}void Push(const T &t){Lock();tasks_.push(t);Wakeup();Unlock();}static ThreadPool<T> *GetInstance(){if (nullptr == tp_) // ???{pthread_mutex_lock(&lock_);if (nullptr == tp_){// std::cout << "log: singleton create done first!" << std::endl;tp_ = new ThreadPool<T>();}pthread_mutex_unlock(&lock_);}return tp_;}private:ThreadPool(int num = defalutnum) : threads_(num){pthread_mutex_init(&mutex_, nullptr);pthread_cond_init(&cond_, nullptr);}~ThreadPool(){pthread_mutex_destroy(&mutex_);pthread_cond_destroy(&cond_);}ThreadPool(const ThreadPool<T> &) = delete;const ThreadPool<T> &operator=(const ThreadPool<T> &) = delete; // a=b=c
private:std::vector<ThreadInfo> threads_;std::queue<T> tasks_;pthread_mutex_t mutex_;pthread_cond_t cond_;static ThreadPool<T> *tp_;static pthread_mutex_t lock_;
};template <class T>
ThreadPool<T> *ThreadPool<T>::tp_ = nullptr;template <class T>
pthread_mutex_t ThreadPool<T>::lock_ = PTHREAD_MUTEX_INITIALIZER;
7.Task.hpp
派发给线程池的任务
#pragma once
#include "Socket.hpp"
#include "protocol.hpp"
#include "Calculator.hpp"
class Task
{
public:Task(int socket_fd): _socket_fd(socket_fd){}void run(){char in_buffer[1024];Calculator cal;std::string message = "";while (true){memset(in_buffer, 0, sizeof in_buffer);//std:: cout << "开始等待读取..." <<std::endl;int n = read(_socket_fd, (void *)in_buffer, sizeof in_buffer - 1);//std::cout << n << " " << strerror(errno) <<std::endl;//std::cout << "读取到的有效字符为" << n << std::endl;if (n == 0){lg(Warning, "Connection closed by foreign host, socketfd[%d] closed...", _socket_fd);break;}else if (n < 0){lg(Warning, "Read Error, socketfd[%d]...", _socket_fd);break;}in_buffer[n] = 0;message += in_buffer;//std::cout << "报文大小: "<< message.size() <<" ,报文内容: "<< message << std::endl;Request rq;if(!rq.deserialize(message)) continue;Respond rs = cal.calculate(rq);std::string res;rs.serialize(&res);printf("%d %c %d = %d\n",rq._x,rq._operator,rq._y,rs._result);write(_socket_fd, res.c_str(), res.size());}}void operator()(){run();close(_socket_fd);}~Task(){}private:int _socket_fd;
};
8. log.hpp
输出日志消息
#pragma once#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>#define SIZE 1024#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4#define Screen 1
#define Onefile 2
#define Classfile 3#define LogFile "log.txt"class Log
{
public:Log(){printMethod = Screen;path = "./log/";}void Enable(int method){printMethod = method;}std::string levelToString(int level){switch (level){case Info:return "Info";case Debug:return "Debug";case Warning:return "Warning";case Error:return "Error";case Fatal:return "Fatal";default:return "None";}}void printLog(int level, const std::string &logtxt){switch (printMethod){case Screen:std::cout << logtxt << std::endl;break;case Onefile:printOneFile(LogFile, logtxt);break;case Classfile:printClassFile(level, logtxt);break;default:break;}}void printOneFile(const std::string &logname, const std::string &logtxt){std::string _logname = path + logname;int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); // "log.txt"if (fd < 0)return;write(fd, logtxt.c_str(), logtxt.size());close(fd);}void printClassFile(int level, const std::string &logtxt){std::string filename = LogFile;filename += ".";filename += levelToString(level); // "log.txt.Debug/Warning/Fatal"printOneFile(filename, logtxt);}~Log(){}void operator()(int level, const char *format, ...){time_t t = time(nullptr);struct tm *ctime = localtime(&t);char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);// 格式:默认部分+自定义部分char logtxt[SIZE * 2];snprintf(logtxt, sizeof(logtxt), "%s %s", leftbuffer, rightbuffer);// printf("%s", logtxt); // 暂时打印printLog(level, logtxt);}private:int printMethod;std::string path;
};Log lg;
客户端
#include "Socket.hpp"
#include "protocol.hpp"void Usage(const char *mes)
{std::cout << "Usage: " << mes << " ip[xxx.xxx.xxx.xxx] port[8080-9000]" << std::endl;
}int main(int argc, char *argv[])
{if (argc != 3){Usage("./clientCal");}Socket local;local.Init();int n = local.Connect(argv[1], argv[2]);if (n < 0){return 1;}int localfd = local.Getfd();std::cout << " 简易计算器, 目前仅支持\" + - * / %\"运算符 " << std::endl;std::cout << " 数字和运算符请用空格或回车隔开" << std::endl;Request rq;Respond rs;std::string message;char buffer[1024];while (true){memset(buffer, 0, sizeof buffer);std::cout << "请输入您的算式@ ";std::cin >> rq._x >> rq._operator >> rq._y;rq.serialize(&message);write(localfd, message.c_str(), message.size());// 开始等待结果n = read(localfd, buffer, sizeof buffer - 1);if (n == 0){lg(Warning, "Connection closed by foreign host, socketfd[%d] closed...",localfd);break;}else if (n < 0){lg(Warning, "Read Error, socketfd[%d]...", localfd);break;}buffer[n] = 0;std::string res = buffer;rs.deserialize(res);if(rs._code != 0){switch(rs._code){case 1:std::cout << "出现除0错误" << std::endl;break;case 2:std::cout << "出现模0错误" << std::endl;break;case 3:std::cout << "使用了除 + - * / % 以外的运算符" << std::endl;break;default:std::cout << "发生未知错误" <<std::endl;break;}continue;}printf("%d %c %d = %d\n",rq._x,rq._operator,rq._y,rs._result);}return 0;
}
Windows客户端
#include<iostream>
#include<string>
#include<WinSock2.h>
#include<Windows.h>
#include<functional>
#include<stdlib.h>
#include"protocol.hpp"#pragma comment(lib, "ws2_32.lib")
#pragma warning(disable:4996)
#pragma execution_character_set("utf-8")const int server_port = 8889;
const std::string server_ip = "43.143.58.29";int main()
{//初始化网络环境WSADATA wsd;WSAStartup(MAKEWORD(2, 2), &wsd);system("chcp 65001");//申请套接字SOCKET socket_fd = socket(AF_INET, SOCK_STREAM, 0);if (socket_fd == SOCKET_ERROR){perror("Socket Error");exit(1);}//创建并初始化Server端sockaddr_in结构体struct sockaddr_in server;memset(&server, 0, sizeof server);server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(server_ip.c_str());server.sin_port = htons(server_port);//开始连接服务器int n = connect(socket_fd, (const struct sockaddr*)&server, sizeof server);if (n < 0){//连接失败std::cout << "Connect Failed" << std::endl;return 1;}std::cout << " 简易计算器, 目前仅支持\" + - * / %\"运算符 " << std::endl;std::cout << " 数字和运算符请用空格或回车隔开" << std::endl;Request rq;Respond rs;std::string message;char buffer[1024];while (true){memset(buffer, 0, sizeof buffer);std::cout << "请输入您的算式@ ";std::cin >> rq._x >> rq._operator >> rq._y;rq.serialize(&message);send(socket_fd, message.c_str(), (int)message.size(),0);// 开始等待结果n = recv(socket_fd, buffer, sizeof buffer - 1,0);if (n == 0){lg(Warning, "Connection closed by foreign host, socketfd[%d] closed...", socket_fd);break;}else if (n < 0){lg(Warning, "Read Error, socketfd[%d]...", socket_fd);break;}buffer[n] = 0;std::string res = buffer;rs.deserialize(res);if (rs._code != 0){switch (rs._code){case 1:std::cout << "出现除0错误" << std::endl;break;case 2:std::cout << "出现模0错误" << std::endl;break;case 3:std::cout << "使用了除 + - * / % 以外的运算符" << std::endl;break;default:std::cout << "发生未知错误" << std::endl;break;}continue;}printf("%d %c %d = %d\n", rq._x, rq._operator, rq._y, rs._result);}//清理环境closesocket(socket_fd);WSACleanup();return 0;
}
运行
相关文章:
Linux网络-自定义协议、序列化和反序列化、网络计算服务器的实现和Windows端客户端
文章目录 前言一、自定义协议传结构体对象 序列化和反序列化什么是序列化?反序列化 二、计算器服务端(线程池版本)1.main.cc2.Socket.hpp3.protocol.hpp4.Calculator.hpp5.serverCal.hpp6.threadPool.hpp7.Task.hpp8. log.hpp 客户端Windows客…...
Hive知识体系保姆级教程
一. Hive概览 1.1 hive的简介 Hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供类SQL查询功能。 其本质是将SQL转换为MapReduce/Spark的任务进行运算,底层由HDFS来提供数据的存储,说白了h…...
三大网络简介
一、三大网是哪三大网 三大网络为电话网、电视广播网、互联网,如果这三大网络使用都是“ip分组交换”技术的话,他们将会被融合成为一个网络, 但是由于历史原因,他们使用了不同的通信技术,三大网各自拥有相当的独立性&a…...
Element-UI全面入门与实战技巧
本文详细介绍了Element-UI的安装、配置、组件使用、布局技巧、交互设计、表单处理、主题定制等内容,旨在帮助开发者快速掌握Element-UI,并能在实际项目中灵活应用。 文章目录 一、Element-UI概述与安装1.1 Element-UI简介1.2 环境搭建1.3 安装Element-UI…...
第103天: 权限提升-Linux 系统辅助项目脏牛Dirty内核漏洞SUIDGUID
项目下载地址 综合类探针: https://github.com/liamg/traitor 自动化提权: https://github.com/AlessandroZ/BeRoot 信息收集: https://github.com/rebootuser/LinEnum https://github.com/sleventyeleven/linuxprivchecker 漏洞探针…...
如何用群晖当异地组网服务器?
在当今信息化时代,远程通信成为了企业和个人之间不可或缺的一部分。特别是对于跨地区的通信需求,一个可靠的异地组网服务器是必不可少的。而群晖(Synology)作为一款功能强大的网络存储设备,可以被用作办公室或家庭的异…...
文件怎么去重?5个技巧,教你删除重复文件!
一般来说,在处理大量文件时,你可能会遇到重复的类似文件。这些文件占据了电脑上不必要的磁盘空间,导致系统性能下降。而这些文件可以是不同类型的,如照片、视频、音频、存档、文档等。正因如此,您需要通过文件去重来删…...
标准发布实施 | 《村镇污水处理一体化集成装备技术规范》
根据《中华人民共和国标准化法》以及国家标准化管理委员会、民政部联合制定的《团体标准管理规定》,依据全国团体标准信息平台和《中华环保联合会团体标准管理办法(试行)》,全国团体标准《村镇污水处理一体化集成装备技术指南》&a…...
人工智能--教育领域的运用
文章目录 🐋引言 🐋个性化学习 🦈体现: 🦈技术解析: 🐋智能辅导与虚拟助手 🦈体现: 🦈技术解析: 🐋自动评分与评估 …...
【设计模式深度剖析】【3】【行为型】【职责链模式】| 以购物中心客户服务流程为例加深理解
👈️上一篇:命令模式 | 下一篇:策略模式👉️ 设计模式-专栏👈️ 文章目录 职责链模式定义英文原话直译如何理解呢? 职责链模式的角色1. Handler(抽象处理者)2. ConcreteHandler(具体处理者…...
评价GPT-4的方案
评价GPT-4的方案 引言: 随着人工智能技术的不断发展,自然语言处理领域取得了显著的突破。其中,GPT-4作为最新的大型语言模型之一,备受关注。本方案旨在对GPT-4进行全面评价,包括其技术特点、性能表现、应用场景以及潜在的影响等方面。 一、技术特点 1. 模型规模和参数数…...
LeetCode | 1624.两个相同字符之间的最长子字符串
这道题拿到手想法就是去双重遍历暴力解,对于每个字符,从后往前遍历字符串,找到从后往前一直到本次遍历的这个字符串这段子串中和这个字符串相同的字符位置,然后得到子字符串的长度,和ans存储的值做一个比较,…...
【CS.AI】GPT-4o:重新定义人工智能的新标杆
文章目录 1 序言2 GPT-4o的技术亮点3 GPT-4o与前代版本的对比3.1 热门AI模型对比表格GPT-3.5GPT-4GPT-4oBERTT5 3.2 其他 4 个人体验与感受5 结论 1 序言 嘿,大家好!今天要聊聊一个超级酷的AI新突破——GPT-4o!最近,OpenAI发布了…...
野火FPGA跟练(四)——串口RS232、亚稳态
目录 简介接口与引脚通信协议亚稳态RS232接收模块模块框图时序波形RTL 代码易错点Testbench 代码仿真 RS232发送模块模块框图时序波形RTL 代码Testbench 代码仿真 简介 UART:Universal Asynchronous Receiver/Transmitter,异步串行通信接口。发送数据时…...
Qt for Android 申请摄像头权限
步骤 1. 添加用户权限 方式1: AndroidManifest.xml 中新增(不添加后面申请选项时不弹窗) 或者再Qt Creator中直接添加 方式2: .pro 中引用multimedia 模块,编译时配置自动添加 <uses-permission android:name"android.permissi…...
kivy 百词斩项目 报错
AttributeError: FigureCanvasKivyAgg object has no attribute resize_event AttributeError: FigureCanvasKivyAgg object has no attribute resize_event 是一种常见的Python错误,当你试图访问一个对象(在这个例子中是 FigureCanvasKivyAgg 对象&am…...
ChatTTS 文字生成语言本地模型部署
ChatTTS部署 官方信息 [ChatTTS首页](https://chattts.com/)搭建步骤 1、下载源码 git clone https://github.com/2noise/ChatTTS.git 2、按照环境 pip install torch ChatTTS pip install -r requirements.txt 3、下载模型 git clone https://www.modelscope.cn/pzc163/ch…...
多曝光融合算法(三)cv2.createAlignMTB()多曝光图像融合的像素匹配问题
文章目录 1.cv2.createAlignMTB() 主要是计算2张图像的位移,假设位移移动不大2.多曝光图像的aline算法:median thresold bitmap原理讲解3.图像拼接算法stitch4.多曝光融合工具箱 1.cv2.createAlignMTB() 主要是计算2张图像的位移,假设位移移动…...
C/C++|类型推导中的模式匹配
在C11及以上的相关语法中,特别是在模版元编程的范式里,类型推导是了重中之重。 在 《Effective Modern C 》 中第一章主要就是讲各种类型推导。 当然了,谈到类型推导,我们不得不先搞懂类型推导中的模式匹配,这是基础&a…...
The 18th Northeast Collegiate Programming Contest(5/9/13)
心得 赛中ac:5,目前ac:9,题目总数:13 中档可做题还是很多的,可惜遇到了难绷的queueforces, 最后15min才判出来,oi赛制5wa4遗憾离场,赛后把几个题都给调过了࿰…...
Vue前端在线预览文件插件
Vue前端在线预览文件插件 一、使用场景 1.1.像文档资料等,只想让他人在线预览,但不能下载。此等场景需求可以用到此插件。 二、此文档介绍两种插件 1.view.xdocin插件 (上线后免费几天,然后收费,添加作者后,可以延…...
【ai】Audio2Face
Audio2Face 简介 Audio2Face是英伟达Omniverse平台的一部分,它使用先进的AI技术来生成基于音频输入的逼真面部动画。这个技术主要利用深度学习模型来解析人声,进而驱动一个三维模型的面部表情。下面是Audio2Face工作流程的详细说明: 预备阶段 在使用Audio2Face之前,需要准…...
2024.6.9 一
装饰器(Decorators) 装饰器是 Python 的一个重要部分。简单地说:他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短,我们在用到装饰器时,常用到一个的符号,这个叫做语法糖,在函数定义前加上decorator_name, 那么后面的函数执行…...
地图之战争迷雾/地图算法/自动导航(一)
战争迷雾 TiledMap 创建黑色覆盖块,然后使用碰撞组件,控制黑色块的显示和隐藏 地图算法 在有些游戏中,地图需要随机生成,比如游戏中的迷宫等,这就需要地图生成的算法;在角色扮演类游戏中,角色…...
【wiki知识库】06.文档管理页面的添加--前端Vue部分
📝个人主页:哈__ 期待您的关注 目录 一、🔥今日目标 二、🐻前端Vue模块的改造 BUG修改 1.wangeditor无法展示问题 2.弹窗无法正常关闭问题 2.1 添加admin-doc.vue 2.1.1 点击admin-ebook中的路由跳转到admin-doc 2.2.2 进入…...
新电脑必装的7款软件,缺一不可
如果你买了新电脑或者是重装了新系统,那么这7款软件你一定要安装。 1、SpaceSniffer 如果你的C盘经常爆红,但是不知道是什么原因,那么你应该需要SpaceSniffer这款软件,它可以把你C盘中文件的空间占用情况,以大小方框…...
程序员学习Processing和TouchDesigner视觉编程相关工具
Proessing Processing 是一种用于视觉艺术和创意编程的开发环境和编程语言。它最初是为了帮助非专业程序员学习编程,特别是那些对于创意编程和视觉表达感兴趣的人。Processing 提供了简单易用的 API,使得绘制图形、创建动画和交互式应用变得相对容易。 …...
gitlabcicd-k8s部署gitlab
一.安装准备环境 存储使用nfs挂载持久化 k8s环境 helm安装 建议helm 3 二.部署gitlab-deploy.yaml nfs的ip是192.168.110.190 挂载目录是/data/data 注意所需要的目录需要创建:/data/data/gitlab/config ,/data/data/gitlab/logs ,/dat…...
浅谈JDBC
文章目录 一、什么是 JDBC?二、JDBC 操作流程三、JDBC代码例子 一、什么是 JDBC? JDBC是一种可用于执行SQL语句的JAVA API,是链接数据库和JAVA应用程序的纽带。JDBC一般需要进行3个步骤:与数据库建立一个链接、向数据库发送SQL语…...
【数据结构初阶】--- 顺序表
顺序表,好像学C语言时从来没听过,实际上就是给数组穿了层衣服,本质是一模一样的。 这里的顺序表实际是定义了一个结构体,设计各种函数来实现它的功能,比如说数组中的增删改查插入,这些基本操作其实平时就会…...
做网站 分工/全网推广平台推荐
1.基于贫血模型的传统开发模式 // BO,不包含业务逻辑 // 虚拟钱包 public class VirtualWalletBo {// 省略 getter/setter/constructor 方法 private Long id; private Long createTime; private BigDecimal balance; }// Service public class VirtualWalletSer…...
怎么做网站滑动图片部分h5/免费做网站网站的软件
https://zhidao.baidu.com/question/1494951482361210539.html转载于:https://www.cnblogs.com/SEC-fsq/p/7602426.html...
城乡建设部网官方网站/市场推广方案ppt
我的环境 f.lux 我的使用感受是让屏幕看起来舒服一些,因为我有近视,所以需要保护眼睛。 f.lux官网:https://justgetflux.com/ f.lux v4.47 windows 10 x64 显示器:戴尔(DELL) P2414H 我最常用的色温值:5200 f.lux zipc…...
建网站需要什么技术/中国国家培训网官网入口
个人用户字体文件在~/.local/share/fonts系统字体文件在/usr/share/fonts字体配置文件在/etc/fonts/...
网站建设电销异议处理话术/优化关键词快速排名
齐博提取出来的 判断是否为手机浏览器的函数index的代码为if($IsMob){header("location:wapindex.php");exit;}common.inc.php 里面关于$IsMob的定义为$IsMobis_mobile();function.inc.php 中提取函数//检查是否为手机访问function is_mobile(){$regex_match"/(…...
wordpress 访客插件/网页设计代码
上篇博客试图采用RecyclerView这个组件来实现一个滑动效果,结果发现大部分代码都是通过java代码来控制填充,设计时不仅操作麻烦(最主要看不懂),而且不能像xml文件一样实时预览效果。 碰巧发现了这篇博客 Android ListV…...