【Linux后端服务器开发】select多路转接IO服务器
目录
一、高级IO
二、fcntl
三、select函数接口
四、select实现多路转接IO服务器
一、高级IO
在介绍五种IO模型之前,我们先讲解一个钓鱼例子。
- 有一条大河,河里有很多鱼,分布均匀。
- 张三是一个钓鱼新手,他钓鱼的时候很紧张,一刻也不敢放松,于是就死死的盯住鱼线,只要鱼线颤动就说明有鱼咬钩了,他便提竿将鱼放入鱼桶中,再重新钓鱼。
- 李四是一个钓鱼老手,他钓鱼的时候很放松,一边闭目养神一边听着音乐,只是用手感受鱼竿的震动,一旦鱼竿震动就说明有鱼咬钩了,他便提竿将鱼放入鱼桶中,再重新钓鱼。
- 王五也是一个钓鱼老手,也是一边闭目养神一边听着音乐,但是他怕自己不能清楚的感受到鱼竿的震动,于是他在鱼竿上系了一个铃铛,一旦有鱼咬钩了,铃铛就会响动提醒王五,于是王五就提竿将鱼放入鱼桶中,再重新钓鱼。
- 赵六是一个卖鱼的,他是做生意的,并不是为了享受钓鱼的过程,于是他开了一个大卡车,上面固定有很多个鱼竿,他就在车上等待并且循环的查看所有鱼竿,发现那个鱼竿震动或鱼线颤动他就将哪个杆上钓到的鱼取下来,再重新将杆放入水中继续钓鱼。
- 田七是一个有钱的老板,他只是想吃河里的鱼,并不想自己钓鱼,于是他就雇了一个员工小王,让他帮自己钓鱼,一旦钓到鱼将鱼桶装满了,就给自己打电话,自己就开车来取鱼。
好了,例子结束了,以上五个人有五种不同的钓鱼方式,那么谁的钓鱼效率最高呢?答案毫无疑问就是赵六,在相同的时间里,赵六能钓到最多的鱼。
钓鱼的过程就类似于IO过程,钓鱼的过程 = 等 + 钓,IO的过程 = 等 + 读/写
- 鱼是数据
- 大河是内核空间,鱼线颤动、鱼竿震动、铃铛响就是数据就绪的事件
- 鱼竿是文件描述符
- 提竿的动作就是recv/read的调用
- 张三、李四、王五、赵六、田七是不同的进程或线程,员工小王是操作系统
从钓鱼策略角度,张三是阻塞式IO,李四是非阻塞IO,王五是信号驱动式IO,赵六是多路转接(多路复用)IO,田七是异步IO。
从效率上看,张三、李四、王五、田七钓鱼的效率是一样的,因为他们都是只有一个鱼竿,而鱼咬钩的概率是一样的,即阻塞式IO、非阻塞IO、异步IO的效率是一样的。
张三、李四、王五、赵六都亲自参与了钓鱼,即阻塞式IO、非阻塞IO、信号驱动式IO、多路转接IO都亲自参与了IO,称为同步IO。
田七并没有亲自参与钓鱼,即异步IO没有亲自参与IO的任何一个阶段。
- 阻塞式IO:在内核将数据准备好之前,系统调用会一直等待。所有套接字默认都是阻塞IO。
- 非阻塞IO:如果内核还没将数据准备好,系统调用仍然会直接返回,并且返回EWOULDBLOCK错误码。非阻塞IO需要程序员循环的方式尝试读写文件描述符(轮询),这对CPU来说是较大的浪费,一般只有特定场景下才使用。
- 信号驱动IO:内核将数据准备好的时候,使用SIGO信号通知应用程序进行IO操作。
- 多路转接IO:虽然从流程图上看起来和阻塞IO类似,实际上最核心的在于IO多路转接能够同时等待多个文件描述符的就绪状态,并且多路转接将等待事件就绪与处理就绪事件做了分离。
- 异步IO:由内核在数据拷贝完成时,通知应用程序(而信号驱动IO是告诉应用程序何时可以开始拷贝数据)
在任何IO过程中,都包含两个步骤:第一是等待,第二是拷贝。
在实际的应用场景中,等待消耗的时间往往都远远高于拷贝的时间,让IO更高效,最核心的办法就是让等待的时间尽量减少。
二、fcntl
基于fcntl,我们实现一个Set_Nonblock函数,将文件描述符设置为非阻塞。
#include <unistd.h>
#include <fcntl.h>int fcntl(int fd, int cmd, ...);// 复制一个现有的描述符 (cmd = F_DUPFD)
// 获得 / 设置文件描述符标记 (cmd = F_GETFD 或 cmd = F_SETFD)
// 获得 / 设置文件状态标记 (cmd = F_GETFL 或 cmd = F_SETFL)
// 获得 / 设置异IO所有权 (cmd = F_GETOWN 或 cmd = F_SETOWN)
// 获得 / 设置记录锁 (cmd = F_GETLK 或 cmd = SETLK)
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>void Set_Nonblock(int fd)
{int f1 = fcntl(fd, F_GETFL);if (f1 < 0){perror("fcntl");return;}fcntl(fd, F_SETFL, f1 | O_NONBLOCK);
}int main()
{Set_Nonblock(0);while (1){char buf[1024];ssize_t read_size = read(0, buf, sizeof(buf) - 1);if (read_size < 0){perror("read");sleep(1);continue;}printf("input: %s\n", buf);}return 0;
}
- 我们通过获取/设置文件状态标记,便可以将一个文件描述符设置为非阻塞
- 使用F_GETFL将当前的文件描述符的属性取出来(一个位图结构)
- 再使用F_SETFL将文件描述符设置回去,设置回去的同时,加上一个O_NONBOLOCK参数
- 轮询的方式读取标准输入
三、select函数接口
系统提供select函数来实现多路转接IO模型
- select系统调用是用来让我们的程序监视多个文件描述符的状态变化的
- 程序会停在select这里等待,直到被监视的文件描述符有一个或多个发生了状态变化
#include <sys/select.h>int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *excptfds, struct timeval* timeout);# 参数解释:
# 参数nfds是需要监视的最大文件描述符值+1
# rdset、wrset、exset分别对应于需要检测的可读文件描述符的集合、可写文件描述符的集合、异常文件描述符集合
# 参数timeout结构为timeval,用来设置select()的等待时间# 参数timeout的取值:
# nullptr:表示select()没有timeout,select将一直被阻塞,直到某个文件描述符上发生事件
# 0:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生
# 特定的时间值:如果在指定的时间段里没有时间发生,select将超时返回
fd_set结构:一个整数结构(位图结构),使用位图中的位来表示需要监视的文件描述符
/* The fd_set member is required to be an array of longs. */
typedef long int __fd_mask;typedef struct
{/* XPG4.2 requires this member name. Otherwise avoid the namefrom the global namespace. */
#ifdef __USE_XOPEN__fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];
#define __FDS_BITS(set) ((set)->fds_bits)
#else__fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS];
#define __FDS_BITS(set) ((set)->__fds_bits)
#endif
} fd_set;
用于操作fd_set的一组接口,方便位图的操作
void FD_CLR(int fd, fd_set *set); // 用来清除描述词组set中相关fd的位
int FD_ISSET(int fd, fd_set *set); // 用来测试描述词组set中相关fd的位是否为真
void FD_SET(int fd, fd_set *set); // 用来设置描述词组set中相关fd的位
void FD_ZERO(fd_set *set); // 用来清除描述词组set的全部位
timeval结构:用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生或函数返回,返回值为0。
struct timeval
{__time_t tv_sec; /* Seconds. */__suseconds_t tv_usec; /* Microseconds. */
};
select函数返回值
- 执行成功则返回文件描述词状态已改变的个数
- 如果返回0则代表在描述词状态改变前已超过timeout时间,没有返回
- 当有错误发生时则返回-1,错误原因存于errno,此时参数readfds、writefds、exceptfds和timeout的变为不可预测
select的执行过程
理解select的关键在于理解fd_set,为方便说明,取fd_set长度为1字节,fd_set中的每一位bit可以对应一个文件描述符fd,1字节长度的fd_set最大可以对应8个fd
- 执行fd_set set; FD_ZERO(&set); 则set用位表示是0000 0000
- 若fd=5,执行FD_SET(fd, &set); 后set变为0001 0000(第5位置1)
- 若再加入fd=2,fd=1,则set变为0001 0011
- 执行select(6, &set, nullptr, nullptr, nullptr)阻塞等待
- 若fd=1,fd=2上都发生可读事件,则select返回,此时select变为0000 0011(注意:没有发生事件的fd=5被清空)
socket就绪条件
读就绪:
①socket内核中,接收缓冲区的字节数,大于等于低水位标记SO_RECVLOWAT。此时可以无阻塞的读该文件描述符,并且返回值大于0;
②socket TCP通信中,对端关闭连接,此时对socket读返回0;
③监听的socket上有新的连接请求;
④socket上有未处理的错误。
写就绪:
①socket内核中,发送缓冲区的可用字节数(发送缓冲区的闲置空间大小)大于等于低水位标记SO_SNDLOWAT,此时可以无阻塞的写,并且返回值大于0;
②socket的写操作被关闭,对一个写操作被关闭的文件描述符进行写操作,会触发SIGPIPE信号;
③socket使用非阻塞connect连接成功或失败之后;
④socket上有未读取的错误。
select的特点
- 可监控的文件描述符个数取决于与sizeof(fd_set)的值,不同的系统的fd_set值不同,通常情况下服务器支持可监控的最大文件描述符个数是数千个。
- 将fd加入select监控集的同时,还要再使用一个数组数据结构array保存放到select中的fd。一是用于在select返回后,array作为源数据和fd_set进行FD_ISSET判断;二是select返回后会把以前加入的但是无事发生的fd清空,则每次开始select前都需要重新从array取得fd逐一加入,扫描array的同时取得fd最大值fdmax,用于select的第一个参数。
- fd_set的大小可调整,涉及到重新编译内核。
select的缺陷
- 每次调用select,都需要手动设置fd集合,从接口使用角度来说并不方便
- 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
- 每次调用select,都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
- select支持的文件描述符数量上限太小
四、select实现多路转接IO服务器
Log.hpp
#pragma once#include <iostream>
#include <string>
#include <cstdarg>
#include <ctime>
#include <unistd.h>#define DEBUG 0
#define NORMAL 1
#define WARNING 2
#define ERROR 3
#define FATAL 4#define NUM 1024const char* To_Levelstr(int level)
{switch (level){case DEBUG:return "DEBUG";case NORMAL:return "NORMAL";case WARNING:return "WARNING";case ERROR:return "ERROR";case FATAL:return "FATAL";default:return nullptr;}
}void Log_Message(int level, const char *format, ...)
{char logprefix[NUM];snprintf(logprefix, sizeof(logprefix), "[%s][%ld][pid: %d]",To_Levelstr(level), (long int)time(nullptr), getpid());char logcontent[NUM];va_list arg;va_start(arg, format);vsnprintf(logcontent, sizeof(logcontent), format, arg);std::cout << logprefix << logcontent << std::endl;
}
Sock.hpp
#pragma once#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include "Log.hpp"enum
{USAGE_ERR = 1,SOCKET_ERR,BIND_ERR,LISTEN_ERR
};class Sock
{const static int backlog = 32;public:static int Socket(){// 1. 创建socket文件套接字对象int sock = socket(AF_INET, SOCK_STREAM, 0);if (sock < 0){Log_Message(FATAL, "create socket error");exit(SOCKET_ERR);}Log_Message(NORMAL, "create socket success: %d", sock);int opt = 1;setsockopt(sock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));return sock;}static void Bind(int sock, int port){// 2. bind绑定自己的网络信息struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(port);local.sin_addr.s_addr = INADDR_ANY;if (bind(sock, (struct sockaddr *)&local, sizeof(local)) < 0){Log_Message(FATAL, "bind socket error");exit(BIND_ERR);}Log_Message(NORMAL, "bind socket success");}static void Listen(int sock){// 3. 设置socket 为监听状态if (listen(sock, backlog) < 0) // 第二个参数backlog后面在填这个坑{Log_Message(FATAL, "listen socket error");exit(LISTEN_ERR);}Log_Message(NORMAL, "listen socket success");}static int Accept(int listensock, std::string *clientip, uint16_t *clientport){struct sockaddr_in peer;socklen_t len = sizeof(peer);int sock = accept(listensock, (struct sockaddr *)&peer, &len);if (sock < 0)Log_Message(ERROR, "accept error, next");else{Log_Message(NORMAL, "accept a new link success, get new sock: %d", sock); // ?*clientip = inet_ntoa(peer.sin_addr);*clientport = ntohs(peer.sin_port);}return sock;}
};
SelectServer.hpp
#pragma once#include <iostream>
#include <string>
#include <functional>#include "Sock.hpp"using namespace std;static const int g_defaultport = 8080;
static const int g_fdnum = sizeof(fd_set) - 1;
static const int g_defaultfd = -1;using func_t = function<string(const string)>;class SelectServer
{
public:SelectServer(func_t func, int port = g_defaultport): _func(func), _port(port), _listensock(g_defaultfd), _fdarray(nullptr){}void Init(){_listensock = Sock::Socket();Sock::Bind(_listensock, _port);Sock::Listen(_listensock);_fdarray = new int[g_fdnum];for (int i = 0; i < g_fdnum; ++i)_fdarray[i] = g_defaultfd;_fdarray[0] = _listensock; // 不变}void Print_FD_List(){ cout << "fd list: ";for (int i = 0; i < g_fdnum; ++i)if (_fdarray[i] != g_defaultfd)cout << _fdarray[i] << " ";cout << endl;}void Accepter(int listensock){Log_Message(DEBUG, "Accept in");string clientip;uint16_t clientport = 0;int sock = Sock::Accept(listensock, &clientip, &clientport); // accept = 等 + 获取连接if (sock < 0)return;Log_Message(NORMAL, "accept success [%s: %d]", clientip.c_str(), clientport);// sock我们能直接recv/read吗?不能,只有select有资格检测事件是否就绪// 将新的sock托管给select:将新的sock添加到_fdarray数组中int i = 0;for (; i < g_fdnum; ++i){if (_fdarray[i] != g_defaultfd)continue;elsebreak;}if (i == g_fdnum){Log_Message(WARNING, "server if full, please wait");close(sock);}else{_fdarray[i] = sock;}Print_FD_List();Log_Message(DEBUG, "Accept out");}void Recver(int sock, int pos){Log_Message(DEBUG, "in Recver");// 1. 读取requestchar buffer[1024];ssize_t s = recv(sock, buffer, sizeof(buffer) - 1, 0);if (s > 0){buffer[s] = 0;Log_Message(NORMAL, "client# %s", buffer);}else if (s == 0){close(sock);_fdarray[pos] = g_defaultfd;Log_Message(NORMAL, "client quit");return;}else{close(sock);_fdarray[pos] = g_defaultfd;Log_Message(ERROR, "client quit: %s", strerror(errno));return;}// 2. 处理requeststring response = _func(buffer);// 3. 返回responsewrite(sock, response.c_str(), response.size());Log_Message(DEBUG, "out Recver");}// 1. handler event rfds中,不仅仅是有一个fd是就绪的,可能存在多个// 2. 我么你的select目前只处理read事件void Handler_Read_Envent(fd_set& rfds){for (int i = 0; i < g_fdnum; ++i){// 过滤掉非法的fdif (_fdarray[i] == g_defaultfd)continue;// 正常的fdif (FD_ISSET(_fdarray[i], &rfds) && _fdarray[i] == _listensock)Accepter(_listensock);else if (FD_ISSET(_fdarray[i], &rfds))Recver(_fdarray[i], i);}}void Start(){while (1){fd_set rfds;FD_ZERO(&rfds);int maxfd = _fdarray[0];for (int i = 0; i < g_fdnum; ++i){// 将全部合法的fd添加到读文件描述符中if (_fdarray[i] == g_defaultfd)continue;FD_SET(_fdarray[i], &rfds);// 更新所有的fd中最大的fdif (maxfd < _fdarray[i])maxfd = _fdarray[i];}Log_Message(NORMAL, "maxfd is: %d", maxfd);// 一般而言,要是用select,需要程序员自己维护一个保存所有合法fd的数组int n = select(maxfd + 1, &rfds, nullptr, nullptr, nullptr);switch (n){case 0:Log_Message(NORMAL, "timeout ...");break;case -1:Log_Message(WARNING, "select error, code: %d, err string: %s", errno, strerror(errno));break;default:// 有事件就绪Log_Message(NORMAL, "have event ready!");Handler_Read_Envent(rfds);break;}}}~SelectServer(){if (_listensock < 0)close(_listensock);if (_fdarray)delete[] _fdarray;}private:int _port;int _listensock;int* _fdarray;func_t _func;
};
main.cc
#include "SelectServer.hpp"
#include <memory>using namespace std;static void Usage(std::string proc)
{std::cerr << "Usage:\n\t" << proc << " port" << "\n\n";exit(USAGE_ERR);
}std::string Transaction(const std::string &request)
{return request;
}// ./select_server 8081
int main(int argc, char *argv[])
{if(argc != 2)Usage(argv[0]);unique_ptr<SelectServer> svr(new SelectServer(Transaction, atoi(argv[1])));// std::cout << "test: " << sizeof(fd_set) * 8 << std::endl;// unique_ptr<SelectServer> svr(new SelectServer(Transaction));svr->Init();svr->Start();return 0;
}
执行效果:运行服务器之后,通过telnet连接服务器,向服务器发送数据并得到响应
相关文章:
![](https://img-blog.csdnimg.cn/b91cb4468da34417acce03f08c68f4f1.png)
【Linux后端服务器开发】select多路转接IO服务器
目录 一、高级IO 二、fcntl 三、select函数接口 四、select实现多路转接IO服务器 一、高级IO 在介绍五种IO模型之前,我们先讲解一个钓鱼例子。 有一条大河,河里有很多鱼,分布均匀。张三是一个钓鱼新手,他钓鱼的时候很紧张&a…...
![](https://img-blog.csdnimg.cn/fbb73867ba20493d88f21117cf2f5a1b.png)
支持向量机(iris)
代码: import pandas as pd from sklearn.preprocessing import StandardScaler from sklearn import svm import numpy as np# 定义每一列的属性 colnames [sepal-length, sepal-width, petal-length, petal-width, class] # 读取数据 iris pd.read_csv(data\\i…...
![](https://img-blog.csdnimg.cn/f8460fa6496b489b80c66381595a86aa.png)
24考研数据结构-第二章:线性表
目录 第二章:线性表2.1线性表的定义(逻辑结构)2.2 线性表的基本操作(运算)2.3 线性表的物理/存储结构(确定了才确定数据结构)2.3.1 顺序表的定义2.3.1.1 静态分配2.3.1.2 动态分配2.3.1.3 mallo…...
![](https://www.ngui.cc/images/no-images.jpg)
Mybatis 动态 sql 是做什么的?都有哪些动态 sql?能简述动态 sql 的执行原理不?
OGNL表达式 OGNL,全称为Object-Graph Navigation Language,它是一个功能强大的表达式语言,用来获取和设置Java对象的属性,它旨在提供一个更高的更抽象的层次来对Java对象图进行导航。 OGNL表达式的基本单位是"导航链"&a…...
![](https://www.ngui.cc/images/no-images.jpg)
250_C++_typedef std::function<int(std::vector<int> vtBits)> fnChkSstStt
假设我们需要定义一个函数类型来表示一个能够计算整数向量中所有元素之和的函数。 首先,我们定义一个函数,它的参数是一个 std::vector 类型的整数向量,返回值是 int 类型,表示所有元素之和: int sumVectorElements(std::vector<int> vt) {int sum = 0;for (int n…...
![](https://www.learnfk.com/guide/images/wuya.png)
无涯教程-jQuery - Transfer方法函数
Transfer 效果可以与effect()方法一起使用。这会将元素的轮廓转移到另一个元素。尝试可视化两个元素之间的交互时非常有用。 Transfer - 语法 selector.effect( "transfer", {arguments}, speed ); 这是所有参数的描述- className - 传输元素将收到的可选类名。…...
![](https://img-blog.csdnimg.cn/img_convert/66e275769c838bd850b071e4b5440110.jpeg)
openGauss学习笔记-24 openGauss 简单数据管理-模式匹配操作符
文章目录 openGauss学习笔记-24 openGauss 简单数据管理-模式匹配操作符24.1 LIKE24.2 SIMILAR TO24.3 POSIX正则表达式 openGauss学习笔记-24 openGauss 简单数据管理-模式匹配操作符 数据库提供了三种独立的实现模式匹配的方法:SQL LIKE操作符、SIMILAR TO操作符…...
![](https://img-blog.csdnimg.cn/2ea113b8646f4a1f936efad8f67148b2.png)
JAVASE---数据类型与变量
1. 字面常量 常量即程序运行期间,固定不变的量称为常量,比如:一个礼拜七天,一年12个月等。 public class Demo{ public static void main(String[] args){ System.Out.println("hello world!"); System.Out.println(…...
![](https://img-blog.csdnimg.cn/6d6e651230f849d1bbd09b2edc8e225c.png)
IDEA Groovy 脚本一键生成实体类<mybatisplus>
配置数据库(mysql) 一键生成(右键点击table) 配置自己的groovy脚本 import com.intellij.database.model.DasTable import com.intellij.database.util.Case import com.intellij.database.util.DasUtil import com.intellij.data…...
![](https://www.learnfk.com/guide/images/wuya.png)
无涯教程-jQuery - Puff方法函数
吹气效果可以与show/hide/toggle一起使用。通过按比例放大元素并同时隐藏它,可以形成粉扑效果。 Puff - 语法 selector.hide|show|toggle( "puff", {arguments}, speed ); 这是所有参数的描述- model - 效果的模式。可以是"显…...
![](https://img-blog.csdnimg.cn/39da6d7b7191490d9c70a9b979413c59.png#pic_center)
什么叫前后端分离?为什么需要前后端问题?解决了什么问题?
单体架构出现的问题 引出:来看一个单体项目架构的结构 通过上述可以看到单体架构主要存在以下几点问题: 开发人员同时负责前端和后端代码开发,分工不明确开发效率低前后端代码混合在一个工程中,不便于管理对开发人员要求高(既会前…...
![](https://www.ngui.cc/images/no-images.jpg)
Vector<T> 动态数组(随机访问迭代器)(答案)
答案如下 //------下面的代码是用来测试你的代码有没有问题的辅助代码,你无需关注------ #include <algorithm> #include <cstdlib> #include <iostream> #include <vector> #include <utility> using namespace std; struct Record { Record…...
![](https://img-blog.csdnimg.cn/851694ab2a0f4678b24833c732f27bde.png)
Istio 故障注入与重试的实验
故障注入 Istio流量治理有故障注入的功能,在接收到用户请求程序的流量时,注入故障现象,例如注入HTTP请求错误,当有流量进入Sidecar时,直接返回一个500的错误请求代码。 通过故障注入可以用来测试整个应用程序的故障恢…...
![](https://www.ngui.cc/images/no-images.jpg)
Java设计模式-中介者模式
中介者模式 1.中介者模式含义 中介者模式,就是用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地互相引用,从而使其耦合松散,而且可以独立的改变它们之间的交互。 其实中介者模式很简单的,就像它的名字一样&a…...
![](https://img-blog.csdnimg.cn/50bab17e9aa14575a1f5262f5116401e.png)
OpenCV实现高斯模糊加水印
# coding:utf-8 # Email: wangguisendonews.com # Time: 2023/4/21 10:07 # File: utils.pyimport cv2 import PIL from PIL import Image import numpy as np from watermarker.marker import add_mark, im_add_mark import matplotlib.pyplot as plt# PIL Image转换成OpenCV格…...
![](https://img-blog.csdnimg.cn/img_convert/751e3d2775d3c0ac4eb4a11058dd1edf.png)
JMeter 怎么查看 TPS 数据教程,简单易懂
TPS 是软件测试结果的测量单位。一个事务是指一个客户机向服务器发送请求然后服务器做出反应的过程。客户机在发送请求时开始计时,收到服务器响应后结束计时,以此来计算使用的时间和完成的事务个数。在 JMeter 中,我们可以使用以下方法查看 T…...
![](https://img-blog.csdnimg.cn/9c3da8a40ace4dbda5be871c58b09377.jpeg#pic_center)
2023年的深度学习入门指南(19) - LLaMA 2源码解析
2023年的深度学习入门指南(19) - LLaMA 2源码解析 上一节我们学习了LLaMA 2的补全和聊天两种API的使用方法。本节我们来看看LLaMA 2的源码。 补全函数text_completion源码解析 上一节我们讲了LLaMA 2的编程方法。我们来复习一下: generator Llama.build(ckpt_di…...
![](https://www.ngui.cc/images/no-images.jpg)
慕课网Go-2.数组、slice、map、list
数组 package mainimport "fmt"func main() {var course1 [3]stringcourse1[0] "go"course1[1] "grpc"course1[2] "gin"for _, value : range course1 {fmt.Println(value)}course2 : [3]string{2: "grpc"}fmt.Println(…...
![](https://www.ngui.cc/images/no-images.jpg)
Django的Rest framework搭建自定义授权登录
系列文章目录 提示:阅读本章之前,请先阅读目录 文章目录 系列文章目录一、前言User模型User的viewsUser的serializersutils的md5加密自定义认证方法配置路由总路由分路由rest的配置 一、前言 之前的文章有写过通过jwt认证的文章,今天这一篇是…...
![](https://img-blog.csdnimg.cn/9d660f4827474d0eacc655c7f1ade7e6.png)
01 矩阵(力扣)多源广度优先搜索 JAVA
给定一个由 0 和 1 组成的矩阵 mat ,请输出一个大小相同的矩阵,其中每一个格子是 mat 中对应位置元素到最近的 0 的距离。 两个相邻元素间的距离为 1 。 输入:mat [[0,0,0],[0,1,0],[0,0,0]] 输出:[[0,0,0],[0,1,0],[0,0,0]] 输入…...
![](https://img-blog.csdnimg.cn/7daef9861075445baad7236f956efc61.png)
怎么绘制简爱思维导图?用这个工具绘制很简单
怎么绘制简爱思维导图?绘制思维导图是一项非常有用的技能,有助于梳理思路、整理知识、更好地理解和记忆信息。因此,无论你是学生、教师、工程师、项目经理或者只是想要更好地组织自己的想法,学会绘制思维导图都是非常有益的。下面…...
![](https://img-blog.csdnimg.cn/5ab219b0f78e477eb26735d39c1c92f2.png)
EC200U-CN学习(三)
EC200U系列内置丰富的网络协议,集成多个工业标准接口,并支持多种驱动和软件功能(适用于Windows 7/8/8.1/10、Linux和Android等操作系统下的USB驱动),极大地拓展了其在M2M领域的应用范围,如POS、POC、ETC、共…...
![](https://img-blog.csdnimg.cn/8c2b7d45d31d4b5199f56548c05dd697.png)
【windows】连接共享打印机提示:0x0000011B
【问题现象】 添加共享打印机的时候, 提示错误:0x0000011B。 【解决方法】 按winr键,在运行输入regedit 然后在注册表中找到路径: 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print 打开后,在右侧…...
![](https://img-blog.csdnimg.cn/img_convert/526a580aad687ec3af395611e0a6701b.webp?x-oss-process=image/format,png)
基于“RWEQ+”集成技术在土壤风蚀模拟与风蚀模数估算、变化归因分析中的实践应用及SCI论文撰写
【查看原文】基于“RWEQ”集成技术在土壤风蚀模拟与风蚀模数估算、变化归因分析中的实践应用及SCI论文撰写 土壤风蚀是一个全球性的环境问题。中国是世界上受土壤风蚀危害最严重的国家之一,土壤风蚀是中国干旱、半干旱及部分湿润地区土地荒漠化的首要过程。中国风…...
![](https://img-blog.csdnimg.cn/9c60a86576bd4be29cb5b94afd9786f0.png#pic_center)
Flutter-基础Widget
Flutter页面-基础Widget 文章目录 Flutter页面-基础WidgetWidgetStateless WidgetStateful WidgetState生命周期 基础widget文本显示TextRichTextDefaultTextStyle 图片显示FlutterLogoIconImageIamge.assetImage.fileImage.networkImage.memory CircleAvatarFadeInImage 按钮R…...
![](https://img-blog.csdnimg.cn/img_convert/fab51e52065103c9903e07bbe269e059.png)
【数据分析专栏之Python篇】二、Jupyer Notebook安装配置及基本使用
文章目录 前言一、Jupter Notebook是什么1.1 简介1.2 组成部分1.3 Jupyter Notebook的主要特点 二、为什么使用Jupyter Notebook?三、安装四、Jupyter Notebok配置4.1 基本配置4.2 配置开机自启与后台运行4.3 开启代码自动补全 五、两种键盘输入模式5.1 编辑模式5.2 命令模式5…...
![](https://img-blog.csdnimg.cn/6402e8a8be3c44389bfe482315786b73.png)
ubuntu22.04 DNSSEC(加密DNS服务) configuration
/etx/systemd/resolved.conf是ubuntu下DNS解析服务配置文件,systemd为ubuntu下system and service配置目录 step 1——修改resolved.conf参数 管理员权限打开 /systemd/resolved.conf sudo nano /etc/systemd/resolved.conf修改如下: # This file i…...
![](https://img-blog.csdnimg.cn/9f35caf91bfa4858ad0c90c9ec9501c8.png)
Qt 第一讲
登录框设置 #include "zuoye.h" #include "ui_zuoye.h"Zuoye::Zuoye(QWidget *parent): QWidget(parent), ui(new Ui::Zuoye) {ui->setupUi(this);//界面this->resize(540,420); //设置尺寸this->setFixedSize(540,420);//固定尺寸this->setS…...
![](https://img-blog.csdnimg.cn/914b3107503f498d9a3b9ea986df1c67.png)
IDEA 使用 maven 搭建 spring mvc
1. 创建项目 1.1 创建成功之后配置 Spring MVC 1.2 勾选 Spring MVC 2.更改配置文件 2.1 更改web.xml配置 更改为 <servlet-mapping><servlet-name>dispatcher</servlet-name><url-pattern>/</url-pattern></servlet-mapping>2.2 dispat…...
![](https://img-blog.csdnimg.cn/246731daedb64c8287090a14cef17573.png)
Hi3536网络应用调优
目录 1. 为什么UDP接收或发送会丢包? 2. 使用 socket 接口时,如何正确工作在非阻塞模式下? 3. TOE 使能及使用注意事项 4. TOE 模式下使用 socket 接口时的注意事项 1. 为什么UDP接收或发送会丢包? 用户态应用程序在接收 UDP 数据时࿰…...
![](/images/no-images.jpg)
网站做等保测评/廊坊seo关键词优化
https://blog.csdn.net/zzq900503/article/details/49123387 运用场景: 导出excel后 发现 数值类型的字段 在excel中表现为文本类型,这样不利于计算。 解决办法: // 创建一个可写入的工作表 // Workbook的createSheet方法有两个参数,第一…...
小电影网站怎么做的/电商培训视频教程
论文:Group Sampling for Scale Invariant Face Detection 论文链接:http://openaccess.thecvf.com/content_CVPR_2019/papers/Ming_Group_Sampling_for_Scale_Invariant_Face_Detection_CVPR_2019_paper.pdf 这篇是发表在CVPR2019的关于人脸检测的文章…...
![](http://www.cocoachina.com/cms/uploads/allimg/130710/4196_130710105651_1.png)
mc做地图画网站/百度竞价优化排名
我们可以把Block当做Objective-C的匿名函数。Block允许开发者在两个对象之间将任意的语句当做数据进行传递,往往这要比引用定义在别处的函数直观。另外,block的实现具有封闭性(closure),而又能够很容易获取上下文的相关状态信息。Block的创建…...
![](http://blog.51cto.com/attachment/201111/174830477.jpg)
雅江网站建设/免费b站推广网站入口
上一节,我们将Models加入了实体对象模型(Entity Frmaework模型)接下来我们要完成控制层的代码编写: 1.在Controllers(控制器)目录点右建,添加一个控制器: 2.添加Home控制器: 3.添加A…...
![](https://img-blog.csdnimg.cn/img_convert/29517c21a7d6cee34a59b2647a64b892.gif)
可以免费做推广的网站/百度app下载安装普通下载
XD中的图层面板Adobe XD里画出来的每一个元素都是一个独立的图层,和PS一样可以通过快捷键CtrlG键进行自由组合,也可以通过CtrlShiftG取消组合。单击工具栏中的“图层”工具,或者按CtrlY键打开和关闭图层面板。在未选择任何对象的情况下&#…...
![](https://images2017.cnblogs.com/blog/793034/201801/793034-20180122174826022-1145524321.jpg)
wordpress 登录后查看/网站关键词优化wang
一、控制面板中安装Telnet相关组件 单击“开始”菜单,单击“控制面板”在控制面板中单击打开“程序和功能”项目在左侧的蓝色区域界面单击“打开或关闭 Windows 功能”弹出“Windows 功能”对话框,在列表中勾选“Telnet客户端”,单击“确定”…...