Linux --- 高级IO

一、基本概念

五种IO模型

  • 阻塞IO:在内核将数据准备好之前,系统调用会一直等待,所有的套接字,默认都是阻塞方式
  • 非阻塞IO:如果内核还未将数据准备好,系统调用仍然会直接返回,并且返回EWOULDBLOCK错误码
  • 信号驱动IO:内核将数据准备好的时候,使用SIGIO信号通知应用程序进行IO操作
  • IO多路转接:虽然从流程图上看起来和阻塞IO类似,实际上最核心在于IO多路转接能够同时等待多个文件描述符的就绪状态
  • 异步IO:由内核在数据拷贝完成时,通知应用程序(而信号驱动是告诉应用程序何时可以开始拷贝数据)
任何IO过程中,都包含两个步骤:第一是等待,第二是拷贝。而且在实际的应用场景中, 等待消耗的时间往往都远高于拷贝的时间。让IO更高效,最核心的办法就是让等待的时间尽量少
上面五种IO方案可以用钓鱼的例子来理解:
  • 阻塞IO,即钓鱼时,就坐在那里盯着水面,看鱼是否上钩
  • 非阻塞IO,即钓鱼时,可以玩玩手机,聊聊天,只要时不时关注一下是否有鱼上钩即可
  • 信号驱动IO,即在鱼竿上放置一个铃铛,铃铛响了再去收杆,其他时间自由分配
  • IO多路转接,即一个人拿上几十个鱼竿在钓鱼
  • 异步IO,即我让其他人来钓鱼,最后钓完鱼,我来收鱼即可

同步通信 vs 异步通信

同步和异步关注的是消息通信机制

  • 所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回. 但是一旦调用返回,就得到返回值了; 换句话说,就是由调用者主动等待这个调用的结果
  • 异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果,换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果,而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用

注意:这里说的同步/异步和进程线程的同步/异步没有任何关系!!!

进程/线程的同步和异步是根据事件的先后顺序决定的,这里可以理解为在IO时,我们是否主动去接收数据,即最终进行IO的是自己还是其他人。

二、非阻塞IO

#include <unistd.h>
#include <fcntl.h>int fcntl(int fd, int cmd, ... /* arg */ );

fcntl的功能取决于cmd参数的值,包括但不限于以下几种:

  1. F_DUPFD:复制文件描述符

  2. F_GETFD/F_SETFD:获取文件描述符标志 / 设置文件描述符标志

  3. F_GETFL/F_SETFL:获取文件状态标志 / 设置文件状态标志

  4. F_GETOWN/F_SETOWN:获取/设置异步I/O所有权。

  5. F_GETLK/F_SETLK/F_SETLKW:获取/设置/等待记录锁。

我们只需要用第三个功能就能实现让一个文件描述符变为非阻塞。

#include <iostream>
#include <unistd.h>
#include <fcntl.h>// 设置非阻塞
void SetNonBlock(int fd)
{int fl = fcntl(fd, F_GETFL);if (fl < 0){std::cout << "fcntl err" << std::endl;exit(1);}fcntl(fd, F_SETFL, fl | O_NONBLOCK);
}int main()
{SetNonBlock(0);char buffer[1024];while (true){ssize_t n = read(0, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0;std::cout << buffer << std::endl;}else if (n == 0){std::cout << "end stdin" << std::endl;break;}else{// 非阻塞 --- 如果数据没有准备好,返回值会按照出错的方式返回 -1// 数据没有准备好 vs 真的出错了 如何分辨?// 如果数据没有准备好,read函数调用完成后,OS会将errno设置为EWOULDBLOCK / EAGAINif (errno == EWOULDBLOCK) // 没有数据{std::cout << "缓冲区中没有数据,error:" << errno << std::endl;}else if (errno == EINTR) // 被中断了,也不算出错{std::cout << "读过程被中断" << std::endl;}else // 读出错{std::cout << "read err" << std::endl;break;}}sleep(1);}return 0;
}

 三、I/O多路转接

1、select

1)接口介绍

#include <sys/select.h>  
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

参数

  • nfds:指定被监听的文件描述符的总数。通常设置为所有文件描述符中的最大值加1,因为文件描述符是从0开始计数的。
  • readfds:指向可读事件对应的文件描述符集合的指针。当select返回时,该集合将包含已准备好读取的文件描述符。
  • writefds:指向可写事件对应的文件描述符集合的指针。当select返回时,该集合将包含已准备好写入的文件描述符。
  • exceptfds:指向异常事件对应的文件描述符集合的指针。当select返回时,该集合将包含发生异常的文件描述符。
  • timeout:指定select的超时时间。如果设置为NULL,则select将一直阻塞,直到有文件描述符就绪;如果设置为0,则select将立即返回,无论文件描述符是否就绪;如果设置为一个非零的时间值,则select将在指定的时间内阻塞,直到有文件描述符就绪或超时。

返回值

  • 在正常情况下,select返回满足条件的文件描述符的个数
  • 如果超时,则返回0
  • 如果出错或被某个信号中断,则返回-1

2)代码 

// SelectServer.hpp
#pragma once
#include "Socket.hpp" // 我在https://blog.csdn.net/V_zjs/article/details/137426801?spm=1001.2014.3001.5501 中有写过,有需要可以看看
#include "Log.hpp" // 我在https://blog.csdn.net/V_zjs/article/details/138088701?spm=1001.2014.3001.5501 中有写过,有需要可以看看
#include <iostream>
#include <algorithm>using namespace zxws;const int defaultbackage = 5;
const int defaultport = 8080;
const int num = sizeof(fd_set) * 8;class SelectServer
{
public:SelectServer(int port = defaultbacklog) : _port(port), _listensocketfd(new TcpSocket()), _isrunning(false){}~SelectServer(){}void Init(){_listensocketfd->BuildListenSocket(_port, defaultbackage);for (auto &sock : _rfds_array){sock = nullptr;}_rfds_array[0] = _listensocketfd.get();}void Loop(){_isrunning = true;while (_isrunning){fd_set rds; // 输入输出型参数,rds需要每次都被设置FD_ZERO(&rds);int max_fd = _listensocketfd->GetSockfd();for (int i = 0; i < num; i++){if (_rfds_array[i] == nullptr)continue;FD_SET(_rfds_array[i]->GetSockfd(), &rds);max_fd = std::max(max_fd, _rfds_array[i]->GetSockfd());}Debug();struct timeval time = {5, 0};int n = select(max_fd + 1, &rds, nullptr, nullptr, &time /*nullptr*/);switch (n){case 0:lg(Info, "没有数据, time: %u.%u s", time.tv_sec, time.tv_usec);break;case -1:lg(Fatal, "select err");break;default:HandleEven(rds);break;}}_isrunning = false;}void Debug(){std::cout << "文件描述符有:";for (auto sock : _rfds_array){if (!sock)continue;std::cout << sock->GetSockfd() << " ";}std::cout << "\n";}private:void HandleEven(fd_set &rds){for (int i = 0; i < num; i++){if (_rfds_array[i] == nullptr)continue;int fd = _rfds_array[i]->GetSockfd();// fd 读事件就绪if (FD_ISSET(fd, &rds)){// 如果是监听套接字if (fd == _listensocketfd->GetSockfd()){std::string ip;uint16_t port;Socket *sock = _listensocketfd->AcceptConnection(&ip, &port);if (!sock){lg(Fatal, "获取数据失败");return;}lg(Info, "接收到客户端 ip: %s, port: %d, fd: %d", ip.c_str(), port, sock->GetSockfd());int pos = 0;for (; pos < num; pos++){if (_rfds_array[pos] == nullptr){_rfds_array[pos] = sock;break;}}if (pos == num){sock->CloseSockfd();delete sock;lg(Info, "数组已满,无法在添加");}}else // 如果是正常的套接字{std::string message;bool res = _rfds_array[i]->Recv(&message, 1024); // 这里的读取是有问题的,需要序列化和反序列化,定制协议的if (res == false)                                // 读写失败{// 关闭当前的描述符_rfds_array[i]->CloseSockfd();delete _rfds_array[i];_rfds_array[i] = nullptr;}else{lg(Info, "client # %s", message.c_str());}}}}}private:std::unique_ptr<Socket> _listensocketfd;bool _isrunning;uint16_t _port;Socket *_rfds_array[num];
};#include <iostream>
#include "SelectServer.hpp"
int main(int argc, char* argv[])
{if(argc!=2){std::cout << " Usrage: " << argv[0] << " port\n" << std::endl;return 1;}uint16_t port = std::stoi(argv[1]);std::unique_ptr<SelectServer> svr(new SelectServer(port));svr->Init();svr->Loop();return 0;
}

从上面的代码和测试来看,多路转接能够用单进程实现与多个客户端的通信

3)优缺点

优点

  • slect只负责等待,可以等待多个fd,IO的时候效率高

缺点

  • 每次都需要对select的参数进行重置
  • 编写代码的时候,select要使用数据结构对与客户端通信的fd进行记录,所以很多时候都需要遍历,影响select效率
  • 用户到内核,内核到用户,每次select调用和返回,都要对位图进行重新设置,用户和内核之间,要一直进行数据拷贝
  • select让OS在底层关注所有被置为1的fd时,需要对位图进行遍历,这就是select的第一个参数是max_fd+1的原因,OS需要轮询检测被置为1的fd是否有事件就绪
  • fd_set是一个系统提供的类型,fd_set大小是固定的为128字节,即select能够检测的fd总数有上限

总的来说:select的优点是所有多路转接都会有的优点,缺点则主要是select的输入输出型参数导致的。

2、poll

1)接口介绍

#include <poll.h>  
int poll(struct pollfd *fds, nfds_t nfds, int timeout);

参数

  • fds:该结构体数组用于指定程序关心的文件描述符集,每个struct pollfd代表一个被监视的文件描述符。
  • nfds:指定fds数组的长度,即要监视的文件描述符的数量。
  • timeout:指定poll函数等待事件发生的超时时间(以毫秒为单位)。
    • 特定时间值:表示poll调用将在指定的时间内进行阻塞等待,如果超时时间内没有文件描述符上的事件就绪,则poll将超时返回。
    • 0:表示poll调用将进行非阻塞等待,无论是否有文件描述符上的事件就绪,都会立即返回。
    • -1:表示poll调用后将阻塞等待,直到至少有一个文件描述符上的事件就绪。

返回值

  • 如果函数调用成功,则返回有事件就绪的文件描述符个数。
  • 如果timeout时间耗尽且没有文件描述符上的事件就绪,则返回0
  • 如果函数调用失败,则返回-1,并设置相应的错误码。
struct pollfd {  int fd;          /* 文件描述符 */  short events;    /* 等待的事件集合,例如POLLIN(读就绪)、POLLOUT(写就绪)等 */  short revents;   /* 实际发生的事件集合,由内核在调用返回时设置 */  
};
// 将输入、输出分离,不用每次都取重新设置参数

这些事件类型由一组位掩码(bitmask)表示,可以使用按位或运算符组合多个事件。以下是一些常用的事件选项:

  1. POLLIN:数据可读。当指定的文件描述符上有数据可读时,此事件被触发。这通常用于非阻塞套接字,以检测何时可以读取新数据。

  2. POLLOUT 或 POLLWRNORM:数据可写。当指定的文件描述符准备好写数据时,此事件被触发。这可以用于检查套接字是否准备好发送数据。

  3. POLLRDNORM:等价于POLLIN。这是POSIX规范中定义的别名,与POLLIN完全相同。

  4. POLLWRBAND:优先带外数据可写。这个选项用于带外数据(out-of-band data)的写操作,但现代系统中这个特性已经很少使用。

  5. POLLPRI:优先数据可读。当指定的文件描述符上有优先(例如带外)数据可读时,此事件被触发。这通常用于处理如SIGURG信号等紧急数据。

  6. POLLERR:错误发生。当指定的文件描述符发生错误时,此事件被触发。这可以用于检测套接字错误,例如连接被对方关闭。

  7. POLLHUP:挂起(hang up)。当指定的文件描述符被关闭,或者远程连接关闭时,此事件被触发。这可以用于检测连接断开。

  8. POLLNVAL:无效请求。当指定的文件描述符无效时,此事件被触发。这通常意味着fd的值超出了文件描述符的范围,或者文件描述符已经被关闭。

2)代码

// PollServer.hpp
#pragma once
#include "Socket.hpp"
#include "Log.hpp"
#include <poll.h>
#include <iostream>
#include <algorithm>
using namespace zxws;const int defaultbackage = 5;
const int defaultport = 8080;
const int gnum = 1024;class PollServer
{
public:PollServer(int port = defaultbacklog) : _port(port), _listensocketfd(new TcpSocket()), _isrunning(false), _num(gnum),_rfds(nullptr){}~PollServer(){delete []_rfds;}void Init(){_listensocketfd->BuildListenSocket(_port, defaultbackage);_rfds = new struct pollfd[_num];for (int i = 0; i < _num; i++){_rfds[i].fd = -1;_rfds[i].events = 0;_rfds[i].revents = 0;}_rfds[0].fd = _listensocketfd->GetSockfd();_rfds[0].events |= POLLIN;}void Loop(){_isrunning = true;while (_isrunning){int time = 1000; // 1sint n = poll(_rfds, _num, time);switch (n){case 0:lg(Info, "没有数据");break;case -1:lg(Fatal, "poll err");break;default:HandleEven();break;}}_isrunning = false;}private:void HandleEven(){for (int i = 0; i < _num; i++){if (_rfds[i].fd == -1)continue;int fd = _rfds[i].fd;short event = _rfds[i].revents; // 注意这里要的是 输出型参数 revents 不是 events,别写错了!!!// 读事件就绪if (event & POLLIN){// 如果是监听套接字if (fd == _listensocketfd->GetSockfd()){std::string ip;uint16_t port;int sock = _listensocketfd->AcceptConnection(&ip, &port);if (sock == -1){lg(Fatal, "获取数据失败");return;}lg(Info, "接收到客户端 ip: %s, port: %d, fd: %d", ip.c_str(), port, sock);int pos = 0;for (; pos < _num; pos++){if (_rfds[pos].fd == -1){_rfds[pos].fd = sock;_rfds[pos].events = POLLIN;break;}}if (pos == _num){lg(Info, "数组已满,无法在添加");}}else // 如果是正常的套接字{char buffer[1024]{};ssize_t n = recv(fd, buffer, 1023, 0); // 这里的读取是有问题的,需要序列化和反序列化,定制协议的if (n <= 0)                            // 读写失败{// 关闭当前的描述符close(fd);_rfds[i].fd = -1;_rfds[i].events = _rfds[i].revents = 0;}else{buffer[n] = 0;lg(Info, "client # %s", buffer);}}}}}private:std::unique_ptr<Socket> _listensocketfd;bool _isrunning;uint16_t _port;struct pollfd *_rfds;int _num;
};#include <iostream>
#include "PollServer.hpp"
int main(int argc, char* argv[])
{if(argc!=2){std::cout << " Usrage: " << argv[0] << " port\n" << std::endl;return 1;}uint16_t port = std::stoi(argv[1]);std::unique_ptr<PollServer> svr(new PollServer(port));svr->Init();svr->Loop();return 0;
}

3)优缺点

优点

  • 可以等待多个fd,效率高
  • 输入,输出参数分离(events和revents),不用对poll参数进行重置
  • poll关心的fd没有上限,可以根据需要创建数组大小

缺点

  • 用户到内核空间,要有数据拷贝 --- 必要开销
  • poll应用层,要遍历。在内核层面,要遍历检测,关心的fd是否有对应的事件就绪---OS需要轮询检测被置为1的fd是否有事件就绪

3、epoll

1)接口介绍

#include <sys/epoll.h>
int epoll_create(int size);

参数

  • size:这是 epoll 实例的大小,即可以监听的文件描述符数量的一个估计值。然而,在实际使用中,这个参数在很多系统上已经不再具有实际的含义,可以将其设置为一个大于0的任意值。

返回值

  • 如果成功,epoll_create 返回一个非负整数,代表 epoll 实例的文件描述符。这个描述符将在后续的 epoll 操作中使用,如添加、删除、修改监听的文件描述符,以及等待 I/O 事件的发生。
  • 如果失败,epoll_create 返回 -1,并设置 errno 表示错误原因。

 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

参数

  1. epfd:这是通过epoll_create创建的epoll实例的文件描述符。
  2. op:操作类型,用于指定要进行的操作,有以下三种取值:
    • EPOLL_CTL_ADD:向epoll实例中添加一个文件描述符及其关联的事件。
    • EPOLL_CTL_MOD:修改已经存在于epoll实例中的文件描述符的监听事件内容。
    • EPOLL_CTL_DEL:从epoll实例中删除一个文件描述符。
  3. fd:这是要进行操作的目标文件描述符,即要注册、修改或删除的文件描述符。
  4. event:这是一个指向epoll_event结构体的指针,用于描述所监听事件的相关信息。如果是删除操作(即opEPOLL_CTL_DEL),该参数可以为NULL。
typedef union epoll_data {void        *ptr;int          fd;uint32_t     u32;uint64_t     u64;
} epoll_data_t;struct epoll_event {  __uint32_t events;      /* Epoll events */ epoll_data_t data;      /* User data variable */
};

返回值

  • 如果操作成功,epoll_ctl函数返回0。
  • 如果操作失败,epoll_ctl函数返回-1,并设置errno表示错误原因。

 
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);

参数

  1. epfd:这是通过 epoll_create 或 epoll_create1 创建的 epoll 实例的文件描述符。
  2. events:这是一个指向 epoll_event 结构体数组的指针,用于存储 epoll_wait 返回的就绪事件。
  3. maxevents:告诉内核这个 events 数组有多大,即最大可以返回多少个就绪事件。
  4. timeout:指定等待事件的超时时间(毫秒)。如果 timeout 是 -1,那么 epoll_wait 将一直阻塞,直到有文件描述符就绪。如果 timeout 是 0,epoll_wait 将立即返回,不管是否有文件描述符就绪。

返回值

  • 如果成功,epoll_wait 返回就绪事件的数量。这些事件被存储在 events 数组中。方便用户在遍历时,只会遍历需要处理的事件。
  • 如果调用被中断(比如收到了一个信号),epoll_wait 返回 -1,并设置 errno 为 EINTR。
  • 如果出错,epoll_wait 返回 -1,并设置 errno 为其他值(比如 EBADF 表示 epfd 不是一个有效的文件描述符)。

2)原理

OS如何知道网卡上的数据是否就绪?硬件中断

OS是如何把外设上面的数据,拿到内存中?根据中断号,执行中断向量表中的方法

所以我们根本没必要像select和poll一样,让OS去轮询的检查是否有文件描述符的事件就绪,我们可以让硬件去通知OS关心的文件描述符上的事件就绪,这样就大大提高了效率。

epoll和poll,select的主要区别在于epoll对于事件的维护是由OS帮助用户完成的,不需要用户进行维护,而poll和select则需要用户对事件手动进行维护,所以epoll原理复杂,但是代码写起来比较简单,同时效率也更高

3)epoll的两种工作模式 --- LT模式 & ET模式

  • 1、水平触发Level Triggered 工作模式,epoll默认状态下就是LT工作模式
  • 当文件描述符上的事件就绪时,上层可以选择不处理,也可以只处理一部分,epoll会再次通知你,直到数据被处理完
  • 2、边缘触发Edge Triggered工作模式
  • 当文件描述符上的事件就绪时,上层必须要全部处理完,否则只能等待下次该文件描述符上有新的事件就绪,epoll才会通知你(简单来说就是只有当前文件描述符关心的事件出现变化时---有新的数据到来,epoll才会通知你处理),不然你将无法处理之前剩余的没有处理的数据

其中ET模式的效率会更高,为什么?

从整体上来说,通知一次就处理好数据,和通知多次才把数据处理好,显然前者更优,而ET模式就是倒逼上层要一次性将数据全部处理完,具体到原理,上层将数据接收完,使得接收方的缓冲区会更大,让tcp在通信时,返回给发送方更大的滑动窗口大小,更有可能让发送方发送更多的数据(这里之所以说可能,是因为发送的数据大小还和拥塞窗口有关),从而提高效率

当然LT模式我们也可以让上层一次性将数据读完,但是相较于ET模式的"倒逼"策略,终归是有一定的选择空间的,就跟我们写作业一样,有deadline和没deadline那是两种完全不同的情况。

那么采用ET模式,我们如何保证一次性将数据全部取完呢?我们肯定就需要循环读取数据了,所以阻塞式IO肯定是不行的,一旦它阻塞住,我们将无法往下执行,所以我们只能选择非阻塞式等待,一旦读取的数据量小于缓冲区的大小,就说明数据被读取完了 --- ET模式只能采用非阻塞IO。

4)代码

namespace zxws
{const static int defaultepfd = -1;const static int size = 1024;class Epoller{public:Epoller() : _epfd(defaultepfd) {}void Init(){_epfd = epoll_create(size);if (defaultepfd == _epfd){lg(Fatal, "epoll_create error, %s : %d", strerror(errno), errno);exit(-1);}lg(Info, "create epoll success , epfd :%d", _epfd);}void AddEvent(int sockfd, int events){struct epoll_event ev;ev.events = events;ev.data.fd = sockfd;int n = ::epoll_ctl(_epfd, EPOLL_CTL_ADD, sockfd, &ev);if (n < 0){lg(Error, "epoll_ctl add error, %s : %d", strerror(errno), errno);}}void ModifyEvent(int sockfd, int events){struct epoll_event ev;ev.data.fd = sockfd;ev.events = events;int n = ::epoll_ctl(_epfd, EPOLL_CTL_MOD, sockfd, &ev);if (n < 0){lg(Error, "epoll_ctl modify error, %s : %d", strerror(errno), errno);}}void DelEvent(int sockfd){int n = ::epoll_ctl(_epfd, EPOLL_CTL_DEL, sockfd, nullptr);if (n < 0){lg(Error, "epoll_ctl delete error, %s : %d", strerror(errno), errno);}}int Wait(struct epoll_event *rev, int maxevents, int timeout){int n = ::epoll_wait(_epfd, rev, maxevents, timeout);return n;}~Epoller(){if (_epfd >= 0)close(_epfd);}private:int _epfd;};// 服务端TcpServer的写法和之前的select和epoll类似,这里就不赘诉了,有兴趣的可以自己实现一下
}

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

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

相关文章

java—Spring框架

Spring 简介 Spring框架由Rod Johnson开发&#xff0c;2004年发布了Spring框架的第一版。Spring是一个从实际开发中抽取出来的框架&#xff0c;因此它完成了大量开发中的通用步骤&#xff0c;留给开发者的仅仅是与特定应用相关的部分&#xff0c;从而大大提高了企业应用的开发…

MacOS 开发 — Packages 程序 macOS新版本 演示选项卡无法显示

MacOS 开发 — Packages 程序 macOS新版本 演示选项卡无法显示 问题描述 &#xff1a; 之前写过 Packages 的使用以及如何打包macOS程序。最近更新了新的macOS系统&#xff0c;发现Packages的演示选项卡无法显示&#xff0c;我尝试从新安转了Packages 也是没作用&#xff0c;…

2024年新一代WebOffice内嵌网页组件——猿大师办公助手

背景 WebOffice控件这个中间件软件产品已存在二十余年&#xff0c;在国内众多大中小型企业、各级政府机关、科研机构和学校等事业单位的OA、ERP、文档系统、云盘等信息化B/S系统中得到了大量使用&#xff0c;为我国的信息化事业也做出了不小贡献。随着操作系统、浏览器及Offic…

亚信安全新一代终端安全TrustOne2024年重磅升级

以极简新主义为核心&#xff0c;亚信安全新一代终端安全TrustOne自2023年发布以来&#xff0c;带动了数字化终端安全的革新。60%&#xff0c;安装部署及管理效率的提升&#xff1b;50%&#xff0c;安全管理资源的节省&#xff1b;100%&#xff0c;信创非信创场景的全覆盖。Trus…

sqlmap使用之-post注入、head注入(ua、cookie、referer)

1、post注入 1.1、方法一&#xff0c;通过保存数据包文件进行注入 bp抓包获取post数据 将数据保存到post.txt文件 加上-r指定数据文件 1.2、方法二、通过URL注入 D:\Python3.8.6\SQLmap>python sqlmap.py -u "http://localhost/login.php" --data "userna…

百度文心4.0 Turbo开放,领跑国内AI大模型赛道!

百度文心4.0 Turbo开放&#xff0c;领跑国内AI大模型赛道&#xff01; 前言 文心一言大模型 就在7月5日&#xff0c;在2024世界人工智能大会 (WAIC) 上&#xff0c;百度副总裁谢广军宣布文心大模型4.0 Turbo正式向企业客户全面开放&#xff01;这一举动直接引发了业界的关注。那…

SpringCloud教程 | 第八篇: 使用seata处理分布式事务

1、参考程序员江小北 2、打算降低nacos版本&#xff0c;先学通再看看升级到高版本nacos能不能正常使用。 3、nacos用1.4.1&#xff0c;正常启动单机版的了 4、seata用2.0.0 我看江小北说用的1.4.0的seata&#xff0c;但是图片中的目录文件都找不到&#xff0c;倒是在2.0.0的…

【git】:初识Git 和 Git 的安装

目录 学习 Git 的目标 Git 安装 Linux-centos Linux-ubuntu Windows 学习 Git 的目标 技术目标 掌握 Git 企业级应用&#xff0c;深刻理解Git操作过程与操作原理&#xff0c;理解工作区&#xff0c;暂存区&#xff0c;版本库的含义 掌握 Git 版本管理&#xff0c;自由进⾏…

DWG文件发布至IIS后无法下载和预览解决办法

问题描述 DWG文件发布至IIS后无法下载和预览 原因分析&#xff1a; iis里面需要添加扩展 解决方案&#xff1a; 在服务器端IS属性的HTTP头下的MIME内容中添加扩展名“.dwg” MIME类型填入application/acad

利用Python进行数据分析PDF下载经典数据分享推荐

本书由Python pandas项目创始人Wes McKinney亲笔撰写&#xff0c;详细介绍利用Python进行操作、处理、清洗和规整数据等方面的具体细节和基本要点。第2版针对Python 3.6进行全面修订和更新&#xff0c;涵盖新版的pandas、NumPy、IPython和Jupyter&#xff0c;并增加大量实际案例…

【想心静?】红尘中修炼的功夫,才是真正的功夫

刘君亮想要去山中静坐&#xff0c;先生说&#xff1a; 你若是以厌弃身外事物的心去静中寻求天理&#xff0c;反而只会养出骄傲怠惰的习气。你若能不厌弃身外事物&#xff0c;然后再到静处涵养天理&#xff0c;却是好的。 去一个安静的地方&#xff0c;去沉静一下自己的内心也…

Java之网络面试经典题(一)

目录 ​编辑 一.Session和cookie Cookie Session 二.HTTP和HTTPS的区别 三.浅谈HTTPS为什么是安全的&#xff1f; 四.TCP和UDP 五.GET和Post的区别 六.forward 和 redirect 的区别&#xff1f; 本专栏全是博主自己收集的面试题&#xff0c;仅可参考&#xff0c;不能相…

kind kubernetes(k8s虚拟环境)使用本地docker的镜像

kubernetes中&#xff0c;虽然下载镜像使用docker&#xff0c;但是存储在docker image里的镜像是不能被k8s直接使用的&#xff0c;但是kind不同&#xff0c;可以使用下面的方法&#xff0c;让kind kubernetes环境使用docker image里的镜像。 kind – Quick Start 例如&#x…

Java数据结构-链表与LinkedList

链表 链表的概念 链表是一种物理存储结构上非连续的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的引用链接次序实现的。 通俗来说&#xff0c;相比较于顺序表&#xff08;物理上连续&#xff0c;逻辑上也连续&#xff09;&#xff0c;链表物理上不一定连续。 链表是…

VBA语言専攻T3学员领取资料通知

T3学员领取资料通知0713 各位学员∶本周MF系列VBA技术资料增加646-650讲&#xff0c;T3学员看到通知后请免费领取,领取时间7月12日晚上19:00-7月13日晚上18:00。本次增加内容&#xff1a; MF646:OnKey禁用ShiftCTRL向右键 MF647:每隔一行插入一空行 MF648:取消合并单元格 …

druid(德鲁伊)数据线程池连接MySQL数据库

文章目录 1、druid连接MySQL2、编写JDBCUtils 工具类 1、druid连接MySQL 初学JDBC时&#xff0c;连接数据库是先建立连接&#xff0c;用完直接关闭。这就需要不断的创建和销毁连接&#xff0c;会消耗系统的资源。 借鉴线程池的思想&#xff0c;数据连接池就这么被设计出来了。…

pnpm9.5.0(catalog协议)

catalog(目录协议) 目录是工作区功能&#xff0c;用于将依赖版本范围定义为可重用常量&#xff0c;目录中定义的常量可以在package.json中使用&#xff0c; 结合 pnpm-workspace.yaml使用 定义pnpm-workspace.yaml packages&#xff1a;定义多仓库 packages:- packages/*cata…

docker(六)--创建镜像

六、创建镜像 1.创建镜像两种方式 方式1&#xff1a; 更新镜像 docker commit 方式2&#xff1a;构建镜像 docker build 2.更新镜像 1&#xff09;用法 docker commit -m“描述信息” -a作者 容器id或者容器名 镜像名:tag 2&#xff09;步骤 ①根据镜像运行容器 ②进入容…

飞睿智能无线高速uwb安全数据传输模块,低功耗、抗干扰超宽带uwb芯片传输速度技术新突破

在信息化的时代&#xff0c;数据传输的速度和安全性无疑是每个企业和个人都极为关注的话题。随着科技的飞速发展&#xff0c;超宽带&#xff08;Ultra-Wideband&#xff0c;简称UWB&#xff09;技术凭借其性能和广泛的应用前景&#xff0c;逐渐成为了数据传输领域的新星。今天&…

Android高级——Logger日志系统

Logger日志系统 Logger日志系统是基于内核中的Logger日志驱动程序实现将日志记录保存在内核空间中使用一个环形缓冲区来保存日志&#xff0c;满了之后&#xff0c;新的日志就会覆盖旧的日志 日志类型 main&#xff0c;记录应用程序级别system&#xff0c;记录系统级别radio&…