【Linux】基于tcp的简单服务器程序

目录

服务端

成员变量

成员函数

工作流程

客户端

头文件和命名空间

信号处理函数

使用说明和重试机制

访问服务器的函数

主函数

总结和注意事项

所用到的其他类

线程池

线程类

翻译业务类

禁止拷贝类

守护锁类

网络地址转换类

日志类

守护进程类


服务端

这个TcpServer类是一个简单的TCP服务器实现,用于监听指定端口上的连接请求,并处理客户端的连接和数据交互。

成员变量

  1. _port: 服务器监听的端口号,默认为8080。

  2. _listensock: 监听套接字的文件描述符。

  3. _isrunning: 表示服务器是否正在运行的布尔变量。

  4. funcs: 一个无序映射,用于存储注册的服务函数。键是服务名称,值是对应的服务处理函数。

成员函数

  1. 构造函数 (TcpServer): 接收监听套接字和端口号作为参数,但都有默认值,因此可以无参构造。构造函数仅初始化成员变量。

  2. Init(): 初始化服务器,包括创建套接字、设置套接字选项、绑定到指定端口、开始监听等步骤。如果在任何步骤中发生错误,程序会记录错误并退出。

  3. Start(): 启动服务器的主循环,接受连接请求。对于每个接受的连接,都会创建一个新的任务并将其添加到线程池中执行。

  4. Read(int sockfd): 从给定的套接字读取数据。如果读取成功,返回读取到的字符串;如果读取失败或连接已关闭,记录相应的日志。

  5. Routine(int sockfd, InetAddr addr): 处理来自客户端的连接。首先调用默认服务,然后读取客户端发送的数据类型,并根据数据类型调用相应的服务函数。

  6. DefaultService(int sockfd, InetAddr &addr): 默认服务函数,向客户端发送可用的服务列表。

  7. Service(int sockfd, InetAddr addr): 一个示例服务函数,接收来自客户端的消息并将其回显。

  8. RegisterFunc(const std::string &name, callback_t func): 允许用户注册自定义的服务函数。这些函数可以根据从客户端接收到的数据类型被调用。

  9. 析构函数 (~TcpServer): 目前析构函数为空,但在实际应用中,通常会在这里关闭套接字和释放资源。

工作流程

  1. 服务器通过Init()函数进行初始化,包括套接字的创建、绑定和监听。

  2. 调用Start()函数开始接受连接。对于每个接受的连接,都会创建一个新的任务来处理它。

  3. 在任务中,首先调用DefaultService向客户端发送可用的服务列表。

  4. 然后读取客户端发送的数据类型,并根据该类型调用相应的服务函数。

  5. 服务函数处理完客户端的请求后,关闭与客户端的连接。

这个类提供了一个基本的TCP服务器框架,用户可以通过注册自定义的服务函数来扩展其功能。注意,这个实现是同步的,每个连接都会占用一个线程来处理,因此在高并发环境下可能需要更高效的IO模型,如异步IO或多线程/多进程模型。

#pragma once#include "ErrInfo.hpp"
#include "Log.hpp"
#include "NoCopy.hpp"
#include <arpa/inet.h>
#include <cstring>
#include <iostream>
#include <netinet/in.h>
#include <signal.h>
#include <string>
#include <sys/socket.h>
#include <sys/types.h>
#include <unordered_map>#include "Thread.hpp"
#include "ThreadPool.hpp"
#include"InetAddr.hpp"
#include"LockGuard.hpp"
#include"Translate.hpp"static const int default_backlog = 5;
static const int DefaultListenSocket = -1;
static const uint16_t DefaultPort = 8080;using task_t = std::function<void()>;
using callback_t = std::function<void(int, InetAddr&)>;class TcpServer : public NoCopy
{
public:TcpServer(int listensocket = DefaultListenSocket, uint16_t port = DefaultPort): _port(port),_listensock(listensocket),_isrunning(false){}void Init(){// 创建套接字_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock < 0){lg.LogMessage(Fatal, "创建套接字失败 错误码:%d  错误信息:%s \n", errno, strerror(errno));exit(Socket_Err);}lg.LogMessage(Info, "创建套接字成功!套接字:%d\n", _listensock);// 减少bind错误int opt = 1;setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));// 填充信息sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_port = htons(_port);local.sin_family = AF_INET;local.sin_addr.s_addr = htonl(INADDR_ANY);// 进行绑定int n = bind(_listensock, (sockaddr *)&local, sizeof(local));if (n != 0){lg.LogMessage(Fatal, "绑定失败! 错误码:%d, 错误信息:%s\n", errno, strerror(errno));exit(Bind_Err);}lg.LogMessage(Info, "绑定成功!\n");// 设置监听状态n = listen(_listensock, default_backlog);if (n != 0){lg.LogMessage(Fatal, "监听失败!错误码:%d,错误信息:%s\n", errno, strerror(errno));exit(Listen_Err);}lg.LogMessage(Info, "监听成功!\n");ThreadPool<task_t>::GetInstance()->Start();funcs.insert(std::make_pair("defaultService", std::bind(&TcpServer::DefaultService, this, std::placeholders::_1, std::placeholders::_2)));}void Start(){_isrunning = true;signal(SIGCHLD, SIG_IGN);while (_isrunning){sockaddr_in peer;memset(&peer, 0, sizeof(peer));socklen_t len = sizeof(peer);int sockfd = accept(_listensock, (sockaddr *)&peer, &len);if (sockfd < 0){lg.LogMessage(Warning, "接受套接字失败, 错误码: %d, 错误信息: %s\n", errno, strerror(errno));continue;}lg.LogMessage(Debug, "接受套接字成功,新的套接字为: %d\n", sockfd);// 处理信息task_t t = std::bind(&TcpServer::Routine, this, sockfd, InetAddr(peer));ThreadPool<task_t>::GetInstance()->Push(t);}}std::string Read(int sockfd){char type[1024];ssize_t n = read(sockfd, type, sizeof(type) - 1);if (n > 0){type[n] = 0;}else if (n == 0) // read如果返回值是0,表示读到了文件结尾(对端关闭了连接!){lg.LogMessage(Info, "client quit...\n");}else{lg.LogMessage(Error, "read socket error, errno code: %d, error string: %s\n", errno, strerror(errno));}return type;}void Routine(int sockfd, InetAddr addr){funcs["defaultService"](sockfd, addr);std::string type = Read(sockfd);lg.LogMessage(Debug, "%s select %s\n", addr.PrintDebug().c_str(), type.c_str());if (type == "ping")funcs[type](sockfd, addr);else if (type == "translate")funcs[type](sockfd, addr);else if (type == "transform")funcs[type](sockfd, addr);else{}close(sockfd);}void DefaultService(const int sockfd, const InetAddr &addr){(void)addr;std::string service_list = " |";for (auto func : funcs){service_list += func.first;service_list += "|";}write(sockfd, service_list.c_str(), service_list.size());}void Service(int sockfd, InetAddr addr){char buffer[1024];// 一直进行IOwhile (true){ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0;std::cout << addr.PrintDebug() << "# " << buffer << std::endl;std::string echo_string = "server echo# ";echo_string += buffer;write(sockfd, echo_string.c_str(), echo_string.size());}else if (n == 0) // read如果返回值是0,表示读到了文件结尾(对端关闭了连接!){lg.LogMessage(Info, "client quit...\n");break;}else{lg.LogMessage(Error, "read socket error, errno code: %d, error string: %s\n", errno, strerror(errno));break;}}}void RegisterFunc(const std::string &name, callback_t func){funcs[name] = func;}~TcpServer(){}private:uint16_t _port;int _listensock;bool _isrunning;std::unordered_map<std::string, callback_t> funcs;
};

客户端

这个代码实现了一个简单的TCP客户端,用于连接到一个TCP服务器,获取服务器提供的服务列表,发送用户选择的服务,然后发送和接收消息。下面是对这个客户端类的详细介绍:

头文件和命名空间

代码首先包含了一系列必要的头文件,这些头文件提供了网络编程、字符串处理、输入输出等功能。然后使用了std命名空间,以简化代码中的标准库类型和函数的调用。

信号处理函数

handler函数是一个信号处理函数,用于处理接收到的信号。在这个例子中,它仅仅打印出接收到的信号编号,并退出程序。但这个函数在main函数中被设置为忽略SIGPIPE信号,这是为了防止在写已关闭的socket时程序异常退出。

使用说明和重试机制

Usage函数提供了程序的使用说明。如果用户没有提供正确的命令行参数,这个函数会被调用,显示如何使用这个程序。

程序中定义了一个重试计数Retry_Count,设置为5。这意味着如果连接服务器失败,客户端会尝试重新连接最多5次。

访问服务器的函数

visitServer函数是客户端的核心功能。它尝试连接到指定的服务器,并交换数据。这个函数的主要步骤如下:

  1. 创建Socket:使用socket函数创建一个TCP socket。

  2. 连接到服务器:使用服务器的IP地址和端口号连接到服务器。如果连接失败,会打印错误信息并返回false

  3. 读取服务列表:从服务器读取提供的服务列表,并显示给用户。

  4. 用户交互:提示用户选择一个服务,并将选择发送给服务器。然后等待用户输入一条消息,并将其发送给服务器。

  5. 接收服务器响应:读取并显示服务器的响应。如果服务器的响应是“quit”,则函数返回true,表示交互结束。

  6. 错误处理和资源清理:在函数结束时,关闭socket。

主函数

main函数是程序的入口点。它首先检查命令行参数的数量,确保用户提供了服务器的IP地址和端口号。然后,它进入一个循环,尝试连接到服务器。如果连接失败,会等待一秒钟后重试,直到达到最大重试次数。如果成功连接到服务器并完成了交互,循环会提前结束。

总结和注意事项

这个客户端程序是一个简单的TCP客户端示例,用于连接到TCP服务器并进行基本的文本交互。它展示了如何使用socket API进行网络通信,包括创建socket、连接到服务器、发送和接收数据等。

需要注意的是,这个程序没有进行复杂的错误处理或安全性检查。在实际应用中,应该添加更多的错误处理和安全性措施,例如检查输入的有效性、使用加密通信等。此外,这个程序使用了阻塞式的socket API,这意味着在读写数据时程序会停止执行直到操作完成。在需要高性能或并发处理的应用中,可能需要使用非阻塞式的socket API或异步I/O库。

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include "ErrInfo.hpp"using namespace std;void handler(int signo)
{std::cout << "signo: " << signo << std::endl;exit(0);
}#define Retry_Count 5void Usage(const std::string &process)
{std::cout << "Usage: " << process << " server_ip server_port" << std::endl;
}bool visitServer(std::string &serverip, uint16_t &serverport, int *cnt)
{// 1. 创建socketstring inbuffer;char service_list[1024];ssize_t m = 0;ssize_t n = 0;int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){cerr << "socket error" << endl;return false;}bool ret = true;struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);inet_pton(AF_INET, serverip.c_str(), &server.sin_addr); // 1. 字符串ip->4字节IP 2. 网络序列n = connect(sockfd, (sockaddr*)&server, sizeof(server)); if (n < 0){cerr << "connect error" << endl;ret = false;goto END;}*cnt = 0;m = read(sockfd, service_list, sizeof(service_list) - 1);if (m > 0){service_list[m] = 0;cout << "服务器提供的服务列表是: " << service_list << endl;}cout << "请你选择服务#  ";getline(cin, inbuffer);write(sockfd, inbuffer.c_str(), inbuffer.size());cout << "Enter> ";getline(cin, inbuffer);if (inbuffer == "quit")return true;n = write(sockfd, inbuffer.c_str(), inbuffer.size());if (n > 0){char buffer[1024];m = read(sockfd, buffer, sizeof(buffer) - 1);if (m > 0){buffer[m] = 0;cout << buffer << endl;}else if (m == 0){return true;}else{ret = false;goto END;}}else{std::cout << "hello write Error" << std::endl;ret = false;goto END;}END:close(sockfd);return ret;
}int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);return 1;}std::string serverip = argv[1];uint16_t serverport = stoi(argv[2]);signal(SIGPIPE, SIG_IGN);int cnt = 1;while (cnt <= Retry_Count){bool result = visitServer(serverip, serverport, &cnt);if (result){break;}else{sleep(1);std::cout << "server offline, retrying..., count : " << cnt << std::endl;cnt++;}}if (cnt >= Retry_Count){std:cout << "server offline" << std::endl;}return 0;
}

所用到的其他类

线程池

#pragma once#include <iostream>
#include <queue>
#include <vector>
#include <pthread.h>
#include <functional>
#include "Log.hpp"
#include "Thread.hpp"
#include "LockGuard.hpp"static const int defaultnum = 10;class ThreadData
{
public:ThreadData(const std::string &name) : threadname(name){}~ThreadData(){}public:std::string threadname;
};template <class T>
class ThreadPool
{
private:ThreadPool(int thread_num = defaultnum) : _thread_num(thread_num){pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_cond, nullptr);// 构建指定个数的线程for (int i = 0; i < _thread_num; i++){// 待优化std::string threadname = "thread-";threadname += std::to_string(i + 1);ThreadData td(threadname);// Thread<ThreadData> t(threadname,//                      std::bind(&ThreadPool<T>::ThreadRun, this, std::placeholders::_1), td);// _threads.push_back(t);_threads.emplace_back(threadname,std::bind(&ThreadPool<T>::ThreadRun, this,std::placeholders::_1),td);lg.LogMessage(Info, "%s is created...\n", threadname.c_str());}}ThreadPool(const ThreadPool<T> &tp) = delete;const ThreadPool<T> &operator=(const ThreadPool<T>) = delete;public:// 有线程安全问题的static ThreadPool<T> *GetInstance(){if (instance == nullptr){LockGuard lockguard(&sig_lock);if (instance == nullptr){lg.LogMessage(Info, "创建单例成功...\n");instance = new ThreadPool<T>();}}return instance;}bool Start(){// 启动for (auto &thread : _threads){thread.Start();lg.LogMessage(Info, "%s is running ...\n", thread.ThreadName().c_str());}return true;}void ThreadWait(const ThreadData &td){lg.LogMessage(Debug, "no task, %s is sleeping...\n", td.threadname.c_str());pthread_cond_wait(&_cond, &_mutex);}void ThreadWakeup(){pthread_cond_signal(&_cond);}void ThreadRun(ThreadData &td){while (true){// checkSelf()// checkSelf();// 取任务T t;{LockGuard lockguard(&_mutex);while (_q.empty()){ThreadWait(td);lg.LogMessage(Debug, "thread %s is wakeup\n", td.threadname.c_str());}t = _q.front();_q.pop();}// 处理任务t();// lg.LogMessage(Debug, "%s handler task %s done, result is : %s\n",//               td.threadname, t.PrintTask().c_str(), t.PrintResult().c_str());}}void Push(T &in){LockGuard lockguard(&_mutex);_q.push(in);ThreadWakeup();}~ThreadPool(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond);}// for debugvoid Wait(){for (auto &thread : _threads){thread.Join();}}private:std::queue<T> _q;std::vector<Thread<ThreadData>> _threads;int _thread_num;pthread_mutex_t _mutex;pthread_cond_t _cond;static ThreadPool<T> *instance;static pthread_mutex_t sig_lock;
};template <class T>
ThreadPool<T> *ThreadPool<T>::instance = nullptr;
template <class T>
pthread_mutex_t ThreadPool<T>::sig_lock = PTHREAD_MUTEX_INITIALIZER;

线程类

#pragma once#include <iostream>
#include <string>
#include <functional>
#include <pthread.h>// 设计方的视角
//typedef std::function<void()> func_t;
template<class T>
using func_t = std::function<void(T&)>;template<class T>
class Thread
{
public:Thread(const std::string &threadname, func_t<T> func, T &data):_tid(0), _threadname(threadname), _isrunning(false), _func(func), _data(data){}static void *ThreadRoutine(void *args) // 类内方法,{// (void)args; // 仅仅是为了防止编译器有告警Thread *ts = static_cast<Thread *>(args);ts->_func(ts->_data);return nullptr;}bool Start(){int n = pthread_create(&_tid, nullptr, ThreadRoutine, this/*?*/);if(n == 0) {_isrunning = true;return true;}else return false;}bool Join(){if(!_isrunning) return true;int n = pthread_join(_tid, nullptr);if(n == 0){_isrunning = false;return true;}return false;}std::string ThreadName(){return _threadname;}bool IsRunning(){return _isrunning;}~Thread(){}
private:pthread_t _tid;std::string _threadname;bool _isrunning;func_t<T> _func;T _data;
};

翻译业务类

#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <unordered_map>
#include "Log.hpp"const std::string unknown = "unknown";
const std::string mydict = "./recource/dict.txt";
const std::string sep = " ";class Translate
{
public:Translate(std::string dict_path = mydict) : _dict_path(dict_path){LoadDict();Parse();}void LoadDict(){std::ifstream in(_dict_path);std::string line;while(std::getline(in, line)){lines.push_back(line);}in.close();lg.LogMessage(Debug, "Load dict txt success, path: %s\n", _dict_path.c_str());}void Parse(){for(auto &line : lines){auto pos = line.find(sep); if(pos == std::string::npos) continue;else{std::string word = line.substr(0, pos);std::string chinese = line.substr(pos+sep.size());_dict.insert(std::make_pair(word, chinese));}}lg.LogMessage(Debug, "Parse dict txt success, path: %s\n", _dict_path.c_str());}void debug(){// for(auto &line : lines)std::cout << line << std::endl;for(auto &elem : _dict){std::cout << elem.first << " : " << elem.second << std::endl;}}std::string Excute(const std::string &word){auto iter = _dict.find(word);if (iter == _dict.end())return unknown;elsereturn _dict[word];}~Translate(){}private:std::string _dict_path;std::unordered_map<std::string, std::string> _dict;std::vector<std::string> lines;
};

禁止拷贝类

#pragma once#include<iostream>
class NoCopy
{public:NoCopy(){}NoCopy(const NoCopy&) = delete;NoCopy& operator=(const NoCopy&) = delete;~NoCopy(){}
};

守护锁类

#pragma once#include <pthread.h>// 不定义锁,默认认为外部会给我们传入锁对象
class Mutex
{
public:Mutex(pthread_mutex_t *lock):_lock(lock){}void Lock(){pthread_mutex_lock(_lock);}void Unlock(){pthread_mutex_unlock(_lock);}~Mutex(){}private:pthread_mutex_t *_lock;
};class LockGuard
{
public:LockGuard(pthread_mutex_t *lock): _mutex(lock){_mutex.Lock();}~LockGuard(){_mutex.Unlock();}
private:Mutex _mutex;
};

网络地址转换类

#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>class InetAddr
{
public:InetAddr(struct sockaddr_in &addr):_addr(addr){_port = ntohs(_addr.sin_port);// _ip = inet_ntoa(_addr.sin_addr); //char *inet_ntoa(struct in_addr in),返回的是字符串地址,那么字符串在哪里?char ipbuffer[64];inet_ntop(AF_INET, &addr.sin_addr, ipbuffer, sizeof(ipbuffer)); // 1. 网络转本机 2. 4字节ip,字符串风格的ip_ip = ipbuffer;}std::string Ip() {return _ip;}uint16_t Port() {return _port;};std::string PrintDebug(){std::string info = _ip;info += ":";info += std::to_string(_port);  // "127.0.0.1:4444"return info;}const  struct sockaddr_in& GetAddr(){return _addr;}bool operator == (const InetAddr&addr){//other codereturn this->_ip == addr._ip && this->_port == addr._port;}~InetAddr(){}
private:std::string _ip;uint16_t _port;struct sockaddr_in _addr;
};

日志类

#pragma once#include <iostream>
#include <fstream>
#include <string>
#include <cstdarg>
#include <ctime>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>enum
{Debug = 0,Info,Warning,Error,Fatal
};enum
{Screen = 10,OneFile,ClassFile
};std::string LevelToString(int level)
{switch (level){case Debug:return "Debug";case Info:return "Info";case Warning:return "Warning";case Error:return "Error";case Fatal:return "Fatal";default:return "Unknown";}
}const int defaultstyle = Screen;
const std::string default_filename = "log.";
const std::string logdir = "log";class Log
{
public:Log() : style(defaultstyle), filename(default_filename){mkdir(logdir.c_str(), 0775);}void Enable(int sty) //{style = sty;}std::string TimeStampExLocalTime(){time_t currtime = time(nullptr);struct tm *curr = localtime(&currtime);char time_buffer[128];snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",curr->tm_year + 1900, curr->tm_mon + 1, curr->tm_mday,curr->tm_hour, curr->tm_min, curr->tm_sec);return time_buffer;}void WriteLogToOneFile(const std::string &logname, const std::string &message){umask(0);int fd = open(logname.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);if (fd < 0)return;write(fd, message.c_str(), message.size());close(fd);}void WriteLogToClassFile(const std::string &levelstr, const std::string &message){std::string logname = logdir;logname += "/";logname += filename;logname += levelstr;WriteLogToOneFile(logname, message);}void WriteLog(const std::string &levelstr, const std::string &message){switch (style){case Screen:std::cout << message;break;case OneFile:WriteLogToClassFile("all", message);break;case ClassFile:WriteLogToClassFile(levelstr, message);break;default:break;}}void LogMessage(int level, const char *format, ...) // 类C的一个日志接口{char leftbuffer[1024];std::string levelstr = LevelToString(level);std::string currtime = TimeStampExLocalTime();std::string idstr = std::to_string(getpid());char rightbuffer[1024];va_list args; // char *, void *va_start(args, format);// args 指向了可变参数部分vsnprintf(rightbuffer, sizeof(rightbuffer), format, args);va_end(args); // args = nullptr;snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%s][%s] ",levelstr.c_str(), currtime.c_str(), idstr.c_str());std::string loginfo = leftbuffer;loginfo += rightbuffer;WriteLog(levelstr, loginfo);}~Log() {}private:int style;std::string filename;
};Log lg;class Conf
{
public:Conf(){lg.Enable(ClassFile);}~Conf(){}
};Conf conf;

守护进程类

#pragma once#include <iostream>
#include <cstdlib>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>const char *root = "/";
const char *dev_null = "/dev/null";void Daemon(bool ischdir, bool isclose)
{signal(SIGCHLD, SIG_IGN);signal(SIGPIPE, SIG_IGN);if (fork() > 0)exit(0);setsid();if (ischdir)chdir(root);if (isclose){close(0);close(1);close(2);}else{int fd = open(dev_null, O_RDWR);if (fd > 0){dup2(fd, 0);dup2(fd, 1);dup2(fd, 2);close(fd);}}
}

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

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

相关文章

2024-04-30 区块链-以太坊-相关文档

摘要: 2024-04-30 区块链-以太坊-文档 以太坊-相关文档: https://github.com/ethereum/go-ethereum https://geth.ethereum.org/ https://geth.ethereum.org/docs https://ethereum.org/zh/ 以太坊开发文档 | ethereum.org 以太坊开发文档_w3cschool 以太坊开发文档 基础主题 …

思考!思考!jmeter线程数≠用户并发数

最近又在搞性能测试了&#xff0c;相较于之前的写脚本出数据就完事&#xff0c;这次深入的思考了一下测试出来的指标&#xff0c;到底有什么意义&#xff1f;&#xff1f;&#xff1f; 绞尽脑汁思考了好几天&#xff0c;终于有了点思路&#xff0c;写出来与大家分享&#xff0…

element_Plus中表格和分页的使用

HTML 表格&#xff08;:data"filterData"绑定的数据&#xff09; <el-table ref"multipleTableRef" :data"filterData" style"width: 100%"selection-change"handleSelectionChange"><el-table-column type"…

图像增强与特效-API调用实践-百度AI

百度智能云-图像增强-清晰度 文章目录 介绍实践Python 解释器获取token调用 最近在整理草稿箱。2022-07-25。我的token应该早过期了哈&#xff0c;需要大家去官网查看最新的api接口申请替换钥匙喔。 介绍 图像清晰度增强官网介绍&预览 API文档 API调用方式 ApiExplorer平…

【译】Celery文档3:在Django中使用Celery

https://docs.celeryq.dev/en/latest/django/first-steps-with-django.html#django-first-steps First steps with Django Django3.1后默认支持Celery&#xff0c;不再需要安装额外的库。 Django项目布局大概是这样的&#xff1a; - proj/- manage.py- proj/- __init__.py- se…

书生·浦语大模型实战营Day01全链路开源体系

书生浦语大模型实战营-全链路开源体系 大模型 通用人工智能的重要途经 专用大模型–通用大模型 大模型&#xff1a;通用人工智能的重要途经&#xff08;专用大模型–通用大模型–多模态大模型–智能体&#xff09; InternLM发展历程 InternLM–书生 万卷&#xff08;数据…

海报生成器源码(Python版)

海报生成器源码&#xff08;Python版&#xff09; 效果图部分源码领取源码下期更新预报 效果图 部分源码 from flask import Flask, Response, request from flask_cors import CORS from flask_uploads import UploadSet, configure_uploads, IMAGES, patch_request_classimp…

使用ldirectord实现LVS健康检测

注意&#xff1a;在LVS/DR模式环境下&#xff0c;具体配置见上篇LVS/DR配置 1.1 ldirectord介绍 LVS监控: ldirectord 主要设计用于监控Linux虚拟服务器&#xff08;LVS&#xff09;架构中的服务器池状态。LVS是一种负载均衡解决方案&#xff0c;用于将网络流量和请求分发到多…

Enhancing Diffusion——利用三维透视几何约束增强扩散模型

概述 透视在艺术中被广泛研究&#xff0c;但现代高质量图像生成方法却缺乏透视精度。新的生成模型引入了几何约束&#xff0c;通过训练过程提高透视精度。这样可以生成更逼真的图像&#xff0c;并提高相关深度估计模型的性能。 最近的图像生成技术使研究人员能够创造性地进行…

点成分享 | 温度控制的艺术:TX150系列水浴中的稳定性与均匀性解析

前言 在实验室和工业生产中&#xff0c;温度控制对于确保实验结果的精确性和产品的高质量至关重要&#xff0c;尤其是针对温度敏感的样品和原材料&#xff0c;如蛋白酶等&#xff0c;微小的温度误差都会对实验结果可靠性和生产质量造成影响。而在控温性能中&#xff0c;稳定性…

自学Java要到什么程度才足够能力去实习和就业?

引言 Java&#xff0c;作为当今软件开发领域的主流编程语言之一&#xff0c;对于初学者而言&#xff0c;明确掌握到什么程度才能开始寻找实习和入职机会是至关重要的。这涉及到对Java知识体系的理解深度、技能掌握程度以及实际项目经验的积累。 本文将分别从实习和入职两个不…

学习VUE2第6天

一.请求拦截器 可以节流&#xff0c;防止多次点击请求 toast是单例 二.前置路由守卫 在Vue.js中&#xff0c;前置路由守卫是指在路由转换实际发生之前执行的钩子函数。这是Vue Router&#xff08;Vue.js官方的路由管理器&#xff09;提供的一种功能&#xff0c;允许开发者在用…

UNI-APP_拨打电话权限如何去掉,访问文件权限关闭

uniapp上架过程中一直提示&#xff1a;允许“app名”拨打电话和管理通话吗&#xff1f; uniapp配置文件&#xff1a;manifest.json “permissionPhoneState” : {“request” : “none”//拨打电话权限关闭 }, “permissionExternalStorage” : {“request” : “none”//访…

CAN总线介绍及在ZYNQ 7020中的应用

一、CAN总线协议介绍 1.CAN是 Controller Area Network 的缩写&#xff08;以下称为 CAN &#xff09;&#xff0c;是 ISO 国际标准化的串行通信协议。可以用来满足“多总线通信时&#xff0c;线束的数量过多”、“通过多个 LAN &#xff0c;进行大量数据的高速通信”的需要。…

设计数据库

一、设计数据库 ER图&#xff1a;Entity-Relation实体关系图 RDBMS关系型数据库管理系统里不支持多对多的关系&#xff0c;只支持一对一或一对多关系 1、数据库三大范氏 第一范式&#xff1a;每个字段不能再分 第二范式&#xff1a;每个表都有主键 第三范式&#xff1a;从…

FIFO Generate IP核使用——Native接口Basic页配置

Xilinx FIFO Generator IP核是一个经过全面验证的先入先出&#xff08;FIFO&#xff09;内存队列&#xff0c;专为需要按顺序存储和检索的应用而设计。该IP核为所有FIFO配置提供了优化解决方案&#xff0c;并在利用最小资源的同时实现最大性能&#xff08;高达500MHz&#xff0…

《Python编程从入门到实践》day19

#昨日知识点回顾 使用unittest模块测试单元和类 #今日知识点学习 第12章 武装飞船 12.1 规划项目 游戏《外星人入侵》 12.2 安装pygame 终端管理器执行 pip install pygame 12.3 开始游戏项目 12.3.1 创建Pygame窗口及响应用户输入 import sysimport pygameclass…

预编码算法学习笔记

预编码算法学习笔记 摘要&#xff1a; 本文将深入探讨预编码算法的学习要点&#xff0c;包括其基本概念、原理、实现方法以及在通信系统中的应用。通过详细的阐述和实例分析&#xff0c;帮助读者全面理解预编码算法&#xff0c;并掌握其在实际问题中的应用技巧。 关键词&…

vim与tmux配置文件及常用命令总结

一. 配置文件 1. .tmux.conf # 使用CtrlA作为前缀组合键 set -g prefix C-a unbind C-b bind C-a send-prefix# 支持鼠标操作 setw -g mouse on# 为了能够重新加载配置文件而无需重启tmux&#xff0c;可以绑定一个快捷键 bind r source-file ~/.tmux.conf \; display-message…

mysql linux远程连接失败处理

问题一&#xff1a;远程连接不上mysql&#xff0c;提示Access denied 一、通过下面命令连接不上数据库 mysql -uroot -p mysql -uroot -h 10.5.122.100 -P 3306 -p报错信息如下&#xff1a; ERROR 1045 (28000): Access denied for user ‘root’‘localhost’ (using passwo…