EPOLL单线程版本 基于reactor 的 httpserver文件下载 支持多个客户端同时处理
之前写了一个httpserver的问价下载服务器 如果有多个客户端请求过来只能串行处理必须得等当前的操作完成之后才会处理
另外还存在 文件大的时候 会出错 处理不了 原因就是 sendfile是在一个while循环中处理的
当调用send失败返回-1之后 就 结束了 而一般来讲 send的时候发送的数据超过内核中的send buffer的大小的时候 就会 失败了
这个时候 必须 要保存下来当前文件的已发送的字节数 以及当前文件的偏移指针 等下一次 EPOLLOUT事件的时候再次 发送给客户端
目前已经实现了这个功能 采用的是单线程版本的reactor模式
支持 多个客户端同时下载文件
还存在bug 但是 功能是有了
#include <stdio.h>
#include <stdlib.h>#include <signal.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <unordered_map>
#include <memory>
#include <vector>#include <arpa/inet.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/sendfile.h>#include <dirent.h>typedef int (*READ_CB)(void *user_data);
typedef int (*WRITE_CB)(void *user_data);
typedef int (*ACCEPT_CB)(int epoll_fd,int fd,void *user_data);#define READ_ONETIME 100#define MAX_SESSIONS 1024
typedef struct
{int fd;int file_fd = -1;char write_buffer[1024];char read_buffer[1024]; int write_offset;int read_offset;int send_file_read_len = 0;char writeable;char is_dir;char head_has_send = 0;char file_path[512]={0};int file_size = 0;READ_CB read_cb;WRITE_CB write_cb;ACCEPT_CB accept_cb;
}Session;typedef struct
{int epoll_fd;int server_fd;int count;Session sessions[MAX_SESSIONS];}Reactor;int create_socket(bool is_tcp,bool block_mode,const char *led_ip,int port)
{#define LISTEN_BACKLOG 10int socket_fd ;const char *server_ip = led_ip;struct sockaddr_in server_addr;if(is_tcp){if(block_mode){socket_fd = socket(AF_INET,SOCK_STREAM,0); }else{socket_fd = socket(AF_INET,SOCK_STREAM|SOCK_NONBLOCK,0);}}else{if(block_mode){socket_fd = socket(AF_INET,SOCK_DGRAM,0); }else{socket_fd = socket(AF_INET,SOCK_DGRAM|SOCK_NONBLOCK,0);}}int opt = 1;if (socket_fd == -1) {printf("Create socket error\n");goto ERROR;}setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt));bzero(&server_addr,sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(port);inet_pton(AF_INET,server_ip,&server_addr.sin_addr);if (bind(socket_fd, (struct sockaddr *) &server_addr,sizeof(server_addr)) == -1){printf("Bind error\n");goto ERROR;}if (listen(socket_fd, LISTEN_BACKLOG) == -1){printf("listen error\n");goto ERROR;}return socket_fd;ERROR:if(socket_fd>0){close(socket_fd);}return -1;
}void set_nonblock(int fd)
{int opts=fcntl(fd, F_GETFL); if(opts<0) { fprintf(stderr, "fcntl(sock,GETFL)\n"); return ;} opts = opts|O_NONBLOCK; if(fcntl(fd,F_SETFL,opts)<0) { fprintf(stderr, "fcntl(sock,SETFL,opts)\n"); return; } }int reactor_init(Reactor &rt,ACCEPT_CB accept_cb,READ_CB read_cb,WRITE_CB write_cb)
{rt.epoll_fd = epoll_create(10); if(rt.epoll_fd == -1){perror("epoll_create failed");return -1;}rt.server_fd = create_socket(true, true, "0,0,0,0", 1234);if(rt.server_fd == -1){perror("create_socket failed");close(rt.epoll_fd);return -1;}struct epoll_event event;event.data.fd = rt.server_fd;event.events = EPOLLIN|EPOLLET|EPOLLOUT;int ret = epoll_ctl(rt.epoll_fd,EPOLL_CTL_ADD ,rt.server_fd,&event);if(ret == -1){perror("epoll_ctl failed");close(rt.epoll_fd);close(rt.server_fd); return -1;}for(int i = 0;i<MAX_SESSIONS;i++){rt.sessions[i].accept_cb = accept_cb;rt.sessions[i].read_cb = read_cb; rt.sessions[i].write_cb = write_cb; }rt.count = 0;printf("Reactor init success epollfd = %d serverfd = %d\n",rt.epoll_fd,rt.server_fd);return 0;
}int reactor_run(Reactor &rt)
{struct epoll_event events[100];while(true) {int ready_count = epoll_wait(rt.epoll_fd, events, 100, -1);//printf("ready_count = %d\n",ready_count);for(int i = 0;i<ready_count;i++){int index = events[i].data.fd;//printf("index = %d epollfd = %d events[i].data.fd = %d events=%08X\n",index,rt.epoll_fd,events[i].data.fd,events[i].events); Session * session = &rt.sessions[index];if(events[i].data.fd == rt.server_fd){printf("index = %d epollfd = %d cfd = %d\n",index,rt.epoll_fd,events[i].data.fd);session->accept_cb(rt.epoll_fd,events[i].data.fd,&rt);}else{if(events[i].events & EPOLLIN){session->read_cb(session);}if(events[i].events & EPOLLOUT){session->write_cb(session);} }}}
}int reactor_deinit(Reactor &rt)
{if(rt.epoll_fd >0){close(rt.epoll_fd);}return 0;
}int Accept_cb(int epoll_fd,int fd,void *user_data)
{if(fd > 0 && epoll_fd >0){int cfd = accept(fd,NULL,NULL);if(cfd == -1){perror("accept failed");return -1;}set_nonblock(cfd);printf("Accept_cb epollfd = %d cfd = %d\n",epoll_fd,cfd);struct epoll_event ev = {0};ev.data.fd = cfd;ev.events = EPOLLIN|EPOLLOUT|EPOLLET;int ret = epoll_ctl(epoll_fd,EPOLL_CTL_ADD,cfd,&ev);if(ret == -1){perror("epoll_ctrl failed");return -1;}Reactor *reactor = (Reactor*)user_data;reactor->sessions[cfd].fd = cfd;//session->fd = cfd;return 0;}return -1;
}void http_request(Session *session)
{char method[12]={0},path[512]={0},protocol[20]={0},headers[512]={0};printf("buf len[%d] content[%s]\n",session->read_offset,session->read_buffer);char *p = strstr(session->read_buffer,"\r\n\r\n");int ret = sscanf(session->read_buffer,"%[^ ] %[^ ] %[^ \r\n]%[^\r\n]",method,path,protocol,headers);printf("sscanf ret is %d headers is %s\n",ret,headers);if(ret !=3){printf("Wait a whole http header\n");session->writeable = 0;return ;}else{printf("This is a whole http packet\n");}session->writeable = 1;session->read_offset = 0;if(strcasecmp(method,"get") == 0){if(strcmp(path,"/") == 0){ strcpy(session->file_path ,"./");}else{strcpy(session->file_path ,path+1);}struct stat st;int ret = stat(session->file_path,&st);if(ret == -1){printf("file doest not exist\n");//SendHead(event,404,"Not found",GetFileType(".html"),-1);//SendFile(event,"404.html");session->is_dir = -1;return ;}if(S_ISDIR(st.st_mode)){printf("Directory\n");//SendHead(event,200,"OK",GetFileType(".html"),-1);//SendDir(event,file);session->is_dir = 1;}else{printf("File\n");session->file_size = st.st_size;//SendHead(event,200,"OK",GetFileType(file),st.st_size);//SendFile(event,file);session->is_dir = 0;}}}#define BURSIZE 1024
int hex2dec(char c)
{if ('0' <= c && c <= '9') {return c - '0';} else if ('a' <= c && c <= 'f') {return c - 'a' + 10;} else if ('A' <= c && c <= 'F') {return c - 'A' + 10;} else {return -1;}
}char dec2hex(short int c)
{if (0 <= c && c <= 9) {return c + '0';} else if (10 <= c && c <= 15) {return c + 'A' - 10;} else {return -1;}
}/** 编码一个url*/
void urlencode(char url[])
{int i = 0;int len = strlen(url);int res_len = 0;char res[BURSIZE];for (i = 0; i < len; ++i) {char c = url[i];if (('0' <= c && c <= '9') ||('a' <= c && c <= 'z') ||('A' <= c && c <= 'Z') || c == '/' || c == '.') {res[res_len++] = c;} else {int j = (short int)c;if (j < 0)j += 256;int i1, i0;i1 = j / 16;i0 = j - i1 * 16;res[res_len++] = '%';res[res_len++] = dec2hex(i1);res[res_len++] = dec2hex(i0);}}res[res_len] = '\0';strcpy(url, res);
}/** 解码url*/
void urldecode(char url[])
{int i = 0;int len = strlen(url);int res_len = 0;char res[BURSIZE];for (i = 0; i < len; ++i) {char c = url[i];if (c != '%') {res[res_len++] = c;} else {char c1 = url[++i];char c0 = url[++i];int num = 0;num = hex2dec(c1) * 16 + hex2dec(c0);res[res_len++] = num;}}res[res_len] = '\0';strcpy(url, res);
}const char *GetFileType(const char *filename)
{const char *dot = strrchr(filename,'.');if(dot == NULL){return "text/plain; charset=utf-8";}if(strcmp(dot,".jpg") == 0 ||strcmp(dot,".jpeg") == 0){return "image/jpg";}if(strcmp(dot,".html") == 0 ||strcmp(dot,".htm") == 0){return "text/html; charset=utf-8";} if(strcmp(dot,".png") == 0){return "image/png";} if(strcmp(dot,".bmp") == 0){return "image/bmp";} if(strcmp(dot,".gif") == 0){return "image/gif";} if(strcmp(dot,".css") == 0){return "text/css";} if(strcmp(dot,".mp3") == 0){return "audio/mpeg";} return "text/plain; charset=utf-8";
}int SendHead(int cfd,int status ,const char *desc,const char *type,int size)
{char buf[4096] = {0};sprintf(buf,"http/1.1 %d %s\r\n",status,desc);sprintf(buf+strlen(buf),"content-type: %s\r\n",type);sprintf(buf+strlen(buf),"content-length: %d\r\n\r\n",size); printf("SendHead buf[%s]\n",buf);return send(cfd,buf,strlen(buf),0);
}int SendDir(Session *session,const char *dirname)
{char buf[4096] = {0};sprintf(buf,"<html><head><title>%s</title></head><body><table>",dirname);printf("SendDir dirname=[%s]\n",dirname);struct dirent **namelist;int count = scandir(dirname,&namelist,NULL,alphasort);printf("SendDir count=[%d]\n",count);for(int i = 0;i< count;i++){char *name = namelist[i]->d_name;struct stat st;char sub_path[1024]={0};sprintf(sub_path,"%s/%s",dirname,name);stat(sub_path,&st);if(S_ISDIR(st.st_mode)){sprintf(buf+strlen(buf),"<tr><td><a href=\"%s/\">%s</a></td><td>%ld</td></tr>",name,name,st.st_size);}else{sprintf(buf+strlen(buf),"<tr><td><a href=\"%s\">%s</a></td><td>%ld</td></tr>",name,name,st.st_size);}//printf("cfd:%d Sendbuf[%s]\n",cfd,buf);send(session->fd,buf,strlen(buf),0);memset(buf,0,sizeof(buf));free(namelist[i]);}sprintf(buf,"</table></body></html>");//printf("cfd:%d Sendbuf[%s]\n",cfd,buf);send(session->fd,buf,strlen(buf),0);free(namelist);return 0;
}int SendFile(Session *session,const char* filename)
{if(session->file_fd == -1){session->file_fd = open(filename,O_RDONLY); }if(session->file_fd >0){#if 1while(1){char buf[1024];int len = read(session->file_fd,buf,sizeof (buf));if(len >0){session->send_file_read_len+=len;int ret = send(session->fd,buf,len,0);if(ret >0){session->write_offset += ret;//printf("This time send [%d] total send [%d] bytes\n",ret,session->write_offset);}else if(ret ==0){printf("Send file return 0 close socket this time len = %d total len = %d \n",len,session->send_file_read_len);close(session->file_fd); close(session->fd);}else{int seek_ret = lseek(session->file_fd,session->write_offset,SEEK_SET);//printf("Seekret = %d session->writeoffset = %d\n",seek_ret,session->write_offset);if(seek_ret == -1){perror("lseek failed");}session->send_file_read_len-=len;//printf("Send file return -1 wait next send this time len = %d total len = %d\n",len,session->send_file_read_len);return -1;}}else if(len == 0){printf("Read file end this time len = %d total len = %d\n",len,session->send_file_read_len);close(session->file_fd); close(session->fd);session->write_offset = 0;session->send_file_read_len = 0;session->fd = 0;session->file_fd = -1;session->writeable = 0;return 0;break;}else{close(session->file_fd); close(session->fd);perror("read error");}}#elseoff_t offset = 0;int file_size = lseek(fd,0,SEEK_END);lseek(fd,0,SEEK_SET);while(offset <file_size){int send_len = sendfile(cfd,fd,&offset,file_size-offset);if(send_len == -1){if(errno == EAGAIN){//perror("sendfile no data send");}else{perror("sendfile ret -1");}}else{printf("Send len:%d\n",send_len);}}#endif}else{perror("open file failed");}//close(fd);return 0;
}void http_response(Session *session)
{//printf("session->writeable = %d\n",session->writeable);if(session->writeable == 0){printf("Not writable\n");return ;}if(session->is_dir == -1){if(session->head_has_send == 0){SendHead(session->fd,404,"Not found",GetFileType(".html"),-1); session->head_has_send = 1;}SendFile(session,"404.html"); session->writeable = 0;}else if(session->is_dir == 1){if(session->head_has_send == 0){SendHead(session->fd,200,"OK",GetFileType(".html"),-1); session->head_has_send = 1;}SendDir(session,session->file_path); }else if(session->is_dir == 0){if(session->head_has_send == 0){SendHead(session->fd,200,"OK",GetFileType(session->file_path),session->file_size); session->head_has_send = 1;}SendFile(session,session->file_path); }}int Read_cb(void *user_data)
{int nread,offset = 0; if(user_data == NULL) return -1;Session *sesion = (Session *)(user_data);printf("Enter readcb1111 sesion->fd = %d\n",sesion->fd); if(sesion){while ((nread = read(sesion->fd, sesion->read_buffer+sesion->read_offset, 1024-1)) > 0) { sesion->read_offset += nread; http_request(sesion);} printf("nread = %d\n",nread);if (nread == -1 && errno != EAGAIN) { perror("read error"); } //conn->recv_size = offset;}return 0;
}int Write_cb(void *user_data)
{if(user_data == NULL) return -1;Session *session = (Session *)(user_data);http_response(session);return 0;
}int main(int argc ,char *argv[])
{printf("Reactor\n");signal(SIGPIPE, SIG_IGN);Reactor reactor;reactor_init(reactor,Accept_cb,Read_cb,Write_cb);reactor_run(reactor);reactor_deinit(reactor);return 0;
}
相关文章:

EPOLL单线程版本 基于reactor 的 httpserver文件下载 支持多个客户端同时处理
之前写了一个httpserver的问价下载服务器 如果有多个客户端请求过来只能串行处理必须得等当前的操作完成之后才会处理 另外还存在 文件大的时候 会出错 处理不了 原因就是 sendfile是在一个while循环中处理的 当调用send失败返回-1之后 就 结束了 而一般来讲 se…...

uniapp实现微信小程序隐私协议组件封装
uniapp实现微信小程序隐私协议组件封装。 <template><view class"diygw-modal basic" v-if"showPrivacy" :class"showPrivacy?show:" style"z-index: 1000000"><view class"diygw-dialog diygw-dialog-modal bas…...

【Node.js】NPM 和 package.json
NPM npm 是 Node.js 的包管理工具,基于命令行,用于安装、升级、移除、管理依赖项。 常用命令: npm init:初始化一个新的 npm 项目,创建 package.json 文件。(括号里为默认值) description&am…...

周总结【java项目】
项目进度: 学习了JavaFX,下载了sceneBuilder辅助工具构建窗口(目前建立了登陆,注册,忘记密码的界面),然后是学习了MySQL的连接,现在的项目是刚连上数据库; 下一步&…...

《深度不确定条件下的决策:从理论到实践》PDF
制定未来计划时需要预测变化,尤其是制定长期计划或针对罕见事件的计划时。当这些变化存在高度不确定性的时候,这种预期就变得越来越困难。 今天给大家介绍的这本《深度不确定条件下的决策:从理论到实践》正是解决以上问题的良方。完整书籍文…...

【MySQL】表的基础增删改查
前面我们已经知道怎么来创建表了,接下来就来对创建的表进行一些基本操作。 这里先将上次创建的表删除掉: mysql> use test; Database changedmysql> show tables; ---------------- | Tables_in_test | ---------------- | student | -----…...

第11章 Redis(二)
11.11 Redis 哨兵机制和集群有什么区别 难度:★★★ 重点:★★ 白话解析 前面的题目都是Redis的原理,接下来就是实际使用的问题了,首先Redis为了保证高可用,在微服务场景下必须是部署集群的,而Redis的集群部署通常就两种方式:主从和Redis Cluster。 参考答案 1、主从…...

mybatis配置entity下不同文件夹同类型名称的多个类型时启动springboot项目出现TypeException源码分析
记录问题:当配置了 mybatis.type-aliases-packagecom.runjing.erp.entity 配置项时,如果entity文件夹下存在不同子文件夹下的同名类型时,mybatis初始化加载映射时会爆出org.apache.ibatis.type.TypeException: The alias TestDemo…...

淘宝商品评论数据分析接口,淘宝商品评论接口
淘宝商品评论数据分析接口可以通过淘宝开放平台API获取。 通过构建合理的请求URL,可以向淘宝服务器发起HTTP请求,获取商品评论数据。接口返回的数据一般为JSON格式,包含了商品的各种评价信息。 获取到商品评论数据后,可以对其进…...

RK3288 android7.1 修改双屏异触usb tp触摸方向
一,问题描述: android机器要求接两个屏(lvdsmipi)两个usb tp要实现双屏异触。由于mipi的方向和lvds方向转成一样的了。两个usb tp的方向在异显示的时候也要作用一样。这个时候要根据pid和vid修改触摸上报的数据。usb tp有通用的触…...

软考 系统架构设计师系列知识点之软件架构风格(8)
接前一篇文章:软考 系统架构设计师系列知识点之软件架构风格(7) 这个十一注定是一个不能放松、保持“紧”的十一。由于报名了全国计算机技术与软件专业技术资格(水平)考试,11月4号就要考试,因此…...

ubuntu安装ssh
安装 OpenSSH 服务器(如果尚未安装): apt-get update && apt-get upgrade -y sudo apt-get install -y openssh-server 检查 SSH 服务是否正在运行: sudo service ssh status 如果 SSH 服务未运行,请通过以…...

webpack不同环境下使用CSS分离插件mini-css-extract-plugin
1.背景描述 使用mini-css-extract-plugin插件来打包css文件(从css文件中提取css代码到单独的文件中,对css代码进行代码压缩等)。 本次采用三个配置文件: 公共配置文件:webpack.common.jsdev开发环境配置文件&#x…...

[MongoDB]-权限验证管理
[MongoDB]-权限验证管理 senge | 2023年9月 背景说明:现有两套MongoDB副本集群给开发人员使用时未开启认证。 产生影响:用户若输入账号以及密码则会进行校验,但用户可以在不输入用户名和密码的情况下也可直接登录。 倘若黑客借此进行攻击勒索…...

bootstrapjs开发环境搭建
Bootstrapjs是一个web前端页面应用开发框架,其提供功能丰富的JavaScript工具集以及用户界面元素或组件的样式集,本文主要描述bootstrapjs的开发环境搭建。 如上所示,使用nodejs运行时环境、使用npm包管理工具、使用npm初始化一个项目工程test…...

远程实时监控管理:5G物联网技术助力配电站管理
配电站远程监控管理系统是基于物联网和大数据处理等技术的一种创新解决方案。该系统通过实时监测和巡检配电场所设备的状态、环境情况、安防情况以及火灾消防等信息,实现对配电站的在线实时监控与现场设备数据采集。 配电站远程监控管理系统通过回传数据进行数据系…...

ubuntu 23.04安装中文输入法
使用ubuntu 23.04安装中文输入法,尝试了最新的搜狗,谷歌拼音,fcitx的原始拼音,最终的结果就是使用了谷歌拼音。 搜狗输入法:好用,但是用了没几天发现各种闪退,一打开就闪烁,根本无法…...

java:解析json的几种方式
Java是一种流行的编程语言,它提供了很多实用的库和工具,在处理JSON数据时也不例外。在本文中,我们将介绍Java中如何解析JSON数据。 JSON是一种轻量级的数据交换格式,它已经成为Web应用程序中最流行的数据格式之一。Java提供了许多…...

pytorch_神经网络构建1
文章目录 pytorch简介神经网络基础分类问题分析:逻辑回归模型逻辑回归实现多层神经网络多层网络搭建保存模型 pytorch简介 为什么神经网络要自定义数据类型torch.tensor? tensor可以放在gpu上训练,支持自动求导,方便快速训练,同时支持numpy的运算,是加强版,numpy不支持这些 为…...

Android 多线程并发详解
一,基础概念 1.1什么是CPU 中央处理器(CPU),是电子计算机的主要设备之一,电脑中的核心配件。其功能主要是解释计算机指令以及处理计算机软件中的数据。CPU是计算机中负责读取指令,对指令译码并执行指令的…...

系统架构设计:8 论软件架构风格
目录 一 软件架构风格 1 数据流风格 (1)批处理风格 (2)管道-过滤器风格...

[Elasticsearch] 邻近匹配 (一) - 短语匹配以及slop参数
本文翻译自Elasticsearch官方指南的Proximity Matching一章。 邻近匹配(Proximity Matching) 使用了TF/IDF的标准全文搜索将文档,或者至少文档中的每个字段,视作"一大袋的单词"(Big bag of Words)。match查询能够告诉我们这个袋子中是否包含了…...

Bootstrap中让元素尽可能往父容器的左侧靠近或右侧造近(左浮动和右浮动)
在Bootstrap中,float-left是一个用于浮动元素的CSS类。它的作用是将一个元素向左浮动,使其在父容器内尽可能靠近左侧边缘,同时允许其他元素在其右侧排列。 使用float-left类可以创建多列布局,将元素水平排列在一行上,…...

网络流量安全分析-工作组异常
在网络中,工作组异常分析具有重要意义。以下是网络中工作组异常分析的几个关键点: 检测网络攻击:网络中的工作组异常可能是由恶意活动引起的,如网络攻击、病毒感染、黑客入侵等。通过对工作组异常的监控和分析,可以快…...

Flink之Watermark源码解析
1. WaterMark源码分析 在Flink官网中介绍watermark和数据是异步处理的,通过分析源码得知这个说法不够准确或者说不够详细,这个异步处理要分为两种情况: watermark源头watermark下游 这两种情况的处理方式并不相同,在watermark的源头确实是异步处理的,但是在下游只是做的判断,这…...

基于支持向量机SVM和MLP多层感知神经网络的数据预测matlab仿真
目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 一、支持向量机(SVM) 二、多层感知器(MLP) 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 .…...

【微服务】RedisSearch 使用详解
目录 一、RedisJson介绍 1.1 RedisJson是什么 1.2 RedisJson特点 1.3 RedisJson使用场景 1.3.1 数据结构化存储 1.3.2 实时数据分析 1.3.3 事件存储和分析 1.3.4 文档存储和检索 二、当前使用中的问题 2.1 刚性数据库模式限制了敏捷性 2.2 基于磁盘的文档存储导致瓶…...

第三章 栈、队列和数组
第三章 栈、队列、数组 栈栈的基本概念栈的顺序实现栈的链接实现栈的简单应用和递归 队列队列的基本概念队列的顺序实现队列的链接实现 数组数组的逻辑结构和基本运算数组的存储结构矩阵的压缩存储 小试牛刀 栈和队列可以看作是特殊的线性表,是运算受限的线性表 栈 …...

使用GitLab CI/CD 定时运行Playwright自动化测试用例
创建项目并上传到GitLab npm init playwright@latest test-playwright # 一路enter cd test-playwright # 运行测试用例 npx playwright test常用指令 # Runs the end-to-end tests. npx playwright test# Starts the interactive UI mode. npx playwright...

Suricata + Wireshark离线流量日志分析
目录 一、访问一个404网址,触发监控规则 1、使用python搭建一个虚拟访问网址 2、打开Wireshark,抓取流量监控 3、在Suricata分析数据包 流量分析经典题型 入门题型 题目:Cephalopod(图片提取) 进阶题型 题目:抓到一只苍蝇(数据包筛选…...