Linux使用libevent协助socket编程进行网络通信----C语言实现

本文主要是介绍Linux使用libevent协助socket编程进行网络通信----C语言实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

libevent库

这一篇是这一个课程的笔记
之后会发使用这一个库实现一个网页服务器的例程

这一个开源, 精简, 跨平台的库, 专注于网络通信

有两个版本比较稳定1.4和2.0

1.4比较简单适合用于学习

使用的话建议使用2.0

这两个版本的接口是不兼容的

libevent官网

libevent源码深度剖析-张亮_libevent源码深度剖析是c语言-CSDN博客

从官网下载以后可以使用./configure 检查安装环境以及生成Makefile, 之后使用make, 生成.o或者可执行文件, 最后sudo make install把必要的资源拷贝到系统的指定目录

一般直接看readme文件

建立成功以后, 可以使用sample目录里面运行demo验证安装情况

➜  sample gcc hello-world.c -o hello -levent

编译的时候需要链接这一个库, 这一个库的实际的安装目录是/usr/local/lib文件夹

image-20240414125346381

image-20240414115435365

特点

1)事件驱动(event-driven),高性能;
2)轻量级,专注于网络,不如ACE那么臃肿庞大;
3)源代码相当精炼、易读;
4)跨平台,支持Windows、Linux、*BSD和Mac Os;
5)支持多种I/O多路复用技术, epoll、poll、dev/poll、select和kqueue等;
6)支持I/O,定时器和信号等事件;
7)注册事件优先级;

框架

  1. 创建event_base
#include <event2/event.h>
struct event_base *event_base_new(void);
struct event_base *base = event_base_new();
  1. 创建事件event

事件有两种, 常规的事件event, 以及bufferevent

struct event *event_new(struct event_base* base, evutil_socket_t fd, short what,event_callback_fn cd, void *arg);
struct bufferevent * bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, enum bufferevent_options options);

what: EV_READ

​ EV_WRITE

​ EV_PERSIST 持续触发

cd : 回调函数

  1. 把事件放到base上面
int event_add(struct event *ev, const struct timeval *tv)
  1. 循环监听事件满足
int event_base_dispatch(struct event_base *event_base)
{return (event_base_loop(event_base, 0));  //调用event_base_loop()
}

event_new函数里面指定了参数EV_PERSIST才可以持续触发, 否则只触发一次, 之后跳出循环

通常使用EV_WRITE|EV_PERSIST, EV_READ|EV_PERSIST

int event_base_loopexit(struct event_base*base, const struct timeval*tv);

指定时间以后退出循环

int event_base_loopbreak(struct event_base*base);

立即停止循环

  1. 释放eventbase
void bufferevent_free(struct bufferevent *bev);

示例分析

这是一个事件异步通信模型, 这一个文件里的所有都是事件, 主要依赖回调函数

#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#ifndef _WIN32
#include <netinet/in.h>
# ifdef _XOPEN_SOURCE_EXTENDED
#  include <arpa/inet.h>
# endif
#include <sys/socket.h>
#endif#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <event2/event.h>static const char MESSAGE[] = "Hello, World!\n";static const int PORT = 9995;
//cb指的是call back回调函数
static void listener_cb(struct evconnlistener *, evutil_socket_t,struct sockaddr *, int socklen, void *);
static void conn_writecb(struct bufferevent *, void *);
static void conn_eventcb(struct bufferevent *, short, void *);
static void signal_cb(evutil_socket_t, short, void *);int main(int argc, char **argv)
{struct event_base *base;struct evconnlistener *listener;//用于监听struct event *signal_event;		//一个事件struct sockaddr_in sin;			//socket结构
#ifdef _WIN32WSADATA wsa_data;WSAStartup(0x0201, &wsa_data);
#endifbase = event_base_new();		//获取一个底座if (!base) {fprintf(stderr, "Could not initialize libevent!\n");return 1;}memset(&sin, 0, sizeof(sin));sin.sin_family = AF_INET;sin.sin_port = htons(PORT);		//这里的ip地址使用的是0, 也就是监听所有的ip//这一个相当于socket, bind, listen, acceptlistener = evconnlistener_new_bind(base, listener_cb, (void *)base,LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,(struct sockaddr*)&sin,sizeof(sin));if (!listener) {fprintf(stderr, "Could not create a listener!\n");return 1;}//获取另一个事件, 这是一个信号事件signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base);if (!signal_event || event_add(signal_event, NULL)<0) {fprintf(stderr, "Could not create/add a signal event!\n");return 1;}//这一个这一个相当于while和epoll_waitevent_base_dispatch(base);//这三个函数很可能到不了evconnlistener_free(listener);event_free(signal_event);event_base_free(base);printf("done\n");return 0;
}static void
listener_cb(struct evconnlistener *listener, evutil_socket_t fd,struct sockaddr *sa, int socklen, void *user_data)
{struct event_base *base = user_data;struct bufferevent *bev;bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);if (!bev) {fprintf(stderr, "Error constructing bufferevent!");event_base_loopbreak(base);return;}bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL);bufferevent_enable(bev, EV_WRITE);bufferevent_disable(bev, EV_READ);bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
}static void
conn_writecb(struct bufferevent *bev, void *user_data)
{struct evbuffer *output = bufferevent_get_output(bev);if (evbuffer_get_length(output) == 0) {printf("flushed answer\n");bufferevent_free(bev);}
}static void
conn_eventcb(struct bufferevent *bev, short events, void *user_data)
{if (events & BEV_EVENT_EOF) {printf("Connection closed.\n");} else if (events & BEV_EVENT_ERROR) {printf("Got an error on the connection: %s\n",strerror(errno));/*XXX win32*/}/* None of the other events can happen here, since we haven't enabled* timeouts */bufferevent_free(bev);
}static void
signal_cb(evutil_socket_t sig, short events, void *user_data)
{struct event_base *base = user_data;struct timeval delay = { 2, 0 };//这是一个信号的回调函数printf("Caught an interrupt signal; exiting cleanly in two seconds.\n");event_base_loopexit(base, &delay);
}

常用函数

event_base_new创建base

#include <event2/event.h>
struct event_base *event_base_new(void);
struct event_base *base = event_base_new();

event_base_free销毁base

void event_base_free(struct event_base *base);

event_base_dispatch开启循环

int event_base_dispatch(struct event_base *event_base);

event_new创建事件

struct event *event_new(struct event_base*base, evutil_socket_t fd, short what, event_callback_fn cb, void *arg);

在这一个事件base里面加入一个事件

base: 使用的base

fd: 监听的文件描述符

what: 监听的这一个文件的什么事件(读写异常)

  • 常用

读事件EV_READ

写事件EV_ERITE

持续触发EV_PERSIST, 一般是配合event_base_dispatch函数使用

#define EV_TIMEOUT 0x01		//这一个废弃了
#define EV_READ 0x02
#define EV_WRITE 0x04
#define EV_SIGNAL 0x08
#define EV_PERSIST 0x10
#define EV_ET 0x20

cb: 回调函数

typedef void(*event_callback_fn)(evutil_socket_t fd, short, void *)

evutil_socket_t实际就是一个int类型, short是上面的what和arg

arg: 这一个函数的参数

返回值是成功创建的事件对象

event_add添加事件

int event_add(struct event *ev, const struct timeval *tv);

ev: event_new函数的返回值

tv: 时间, NULL这一个不会超时, 为非零的时候, 这一个事件没有触发也会被回调

不要设置 tv 为希望超时事件执行的时间。如果在 2010 年 1 月 1 日设置 “tv- >tv_sec=time(NULL)+10;”,超时事件将会等待40年,而不是10秒

struct timeval delay = { 2, 0 };

返回值 : 成功0, 失败-1

event_free销毁事件

void event_free(struct event *event);

event_del事件移出base

int event_del(struct event *ev);

未决和非未决

未决: 有资格被处理, 但是还没有被处理

非未决: 没有资格被处理

image-20240415222945402

实际使用

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <event2/event.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>void write_cb(evutil_socket_t fd, short what, void *arg){char buf[1024];static int num = 0;sprintf(buf, "hello world-%d\n", num++);write(fd, buf, strlen(buf)+1);sleep(1);
}int main(void){int fd = open("myfifo", O_WRONLY | O_NONBLOCK);if(fd == -1){perror("open error");exit(1);}struct event_base *base = NULL;base = event_base_new();struct event *event = event_new(base, fd, EV_WRITE|EV_PERSIST, write_cb, NULL); event_add(event, NULL);event_base_dispatch(base);event_free(event);event_base_free(base);close(fd);return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <event2/event.h>
#include "fcntl.h"void read_cb(evutil_socket_t fd, short what, void *arg){char buf[1024];int len = read(fd, buf, sizeof(buf));printf("read event: %s \n", what & EV_READ ? "Yse" :"No" );printf("data len = %d, buf = %s\n", len, buf);sleep(1);
}int main(void){unlink("myfifo");mkfifo("myfifo", 0664);int fd = open("myfifo", O_RDONLY | O_NONBLOCK);if(fd == -1){perror("open error");exit(1);}struct event_base*base;base = event_base_new();struct event* ev = NULL;ev = event_new(base, fd, EV_READ| EV_PERSIST, read_cb, NULL);//添加事件,不加时间限制event_add(ev, NULL);//开始循环event_base_dispatch(base);event_free(ev);event_base_free(base);close(fd);return 0;}

其他函数

const char **event_get_supported_methods(void);

查看这一个系统支持的多路IO, epoll, poll, select之类的

const char * event_base_get_method(const struct event_base * base);

查看现在使用的IO

int event_reinit(struct event_base *base);

查看fork以后使用的这一个重新初始化这一个base

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <event2/event.h>int main(void){struct event_base *base;base = event_base_new();const char **buf;if(!base){printf("Could not initialize libevent!\n");return 1;}buf = event_get_supported_methods();//获取支持的IOprintf("Using Libevent with backend method %s.\n", event_base_get_method(base));//获取使用IOprintf("Starting Libevent %s.  Available methods are:\n", event_get_version());//获取版本for(int i = 0; buf[i] != NULL; i++){printf("%s\n", buf[i]);}
}
2024-4-14-libvent ./event 
Using Libevent with backend method epoll.
Starting Libevent 2.1.8-stable.  Available methods are:
epoll
poll
select

带缓冲区的事件

需要使用头文件<event2/bufferevent.h>

bufferevent, 里面有两个buffer, 一个读一个写, 使用队列实现, 读走以后就没有了, 先进先出

读 : 有数据 --> 读回调函数 --> 使用bufferevent_read --> 把数据读出来

写: 使用bufferevent_write --> 向缓冲区写数据 --> 缓冲区有数据自动写出 --> 写完 --> 回调函数

在这里面就不能使用read和write函数了

实际使用的时候需要在读缓冲回调函数里面使用bufferevent_read读取数据, 读取以后处理数据, 之后写入

写缓冲的回调函数用处不大

bufferevent_socket_new

struct bufferevent *bufferevent_socket_new(struct event_base *base,evutil_socket_t fd,enum bufferevent_options options);

base 是 event_base,options 是表示 bufferevent 选项 (BEV_OPT_CLOSE_ON_FREE 等) 的位掩码

fd是一个可选的表示套接字的文件 描述符。如果想以后设置文件描述符,可以设置fd为-1。 成功时函数返回一个 bufferevent,失败则返回 NULL。

optiona:

  • BEV_OPT_CLOSE_ON_FREE:释放 bufferevent 时关闭底层传输端口。这将关闭底 层套接字,释放底层 bufferevent 等。

主要使用的是这一个

  • BEV_OPT_THREADSAFE:自动为 bufferevent 分配锁,这样就可以安全地在多个线 程中使用 bufferevent。
  • BEV_OPT_DEFER_CALLBACKS:设置这个标志时,bufferevent 延迟所有回调,如 上所述。
  • BEV_OPT_UNLOCK_CALLBACKS:默认情况下,如果设置 bufferevent 为线程安全 的,则 bufferevent 会在调用用户提供的回调时进行锁定。设置这个选项会让 libevent 在执行回调的时候不进行锁定。

bufferevent_free

void bufferevent_free(struct bufferevent *bev);

bufferevent_setcb

typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);
typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short events, void *ctx);
void bufferevent_setcb(struct bufferevent *bufev, bufferevent_data_cb readcb, bufferevent_data_cb writecb, bufferevent_event_cb eventcb, void *cbarg);
void bufferevent_getcb(struct bufferevent *bufev, bufferevent_data_cb *readcb_ptr, bufferevent_data_cb *writecb_ptr, bufferevent_event_cb *eventcb_ptr, void **cbarg_ptr);

这两个缓冲区的回调函数

readcb_ptr: 读缓冲回调

writecb: 写缓冲回调

eventcb: 事件回调, 用于处理状态以及异常

cbarg: 回调使用的参数

  • 回调函数参数

events: 实际的事件

BEV_EVENT_READING:读取操作时发生某事件,具体是哪种事件请看其他标志。

BEV_EVENT_WRITING:写入操作时发生某事件,具体是哪种事件请看其他标志。

BEV_EVENT_ERROR : 操作时发生错误。关于错误的更多信息,请调用 EVUTIL_SOCKET_ERROR()。

BEV_EVENT_TIMEOUT:发生超时。

BEV_EVENT_EOF:遇到文件结束指示。

BEV_EVENT_CONNECTED:请求的连接过程已经完成

ctx : 传递的参数

bufferevent_read

size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);

bufferevent_write

int bufferevent_write(struct bufferevent *bufev,const void *data, size_t size);

bufferevent_enable

void bufferevent_enable(struct bufferevent *bufev, short events);
void bufferevent_disable(struct bufferevent *bufev, short events);

启用以及禁用缓冲区

可以启用或者禁用 bufferevent 上的 EV_READ、EV_WRITE 或者 EV_READ | EV_WRITE 事件。

默认的时候读是disable, 写是enable的

short bufferevent_get_enabled(struct bufferevent *bufev);

获取缓冲区状态

网络通信

流程

使用头文件<event2/listener.h>

  • 客户端

socket, connect

int bufferevent_socket_connect(struct bufferevent *bev, struct sockaddr *address, int addrlen);

可以使用这一个函数进行封装使用

address, addrlen实际就是connect函数的参数2和参数3

  • 服务器端

socket, bind, listen, accept

struct evconnlistener *evconnlistener_new(struct event_base *base, evconnlistener_cb cb, void *ptr, unsigned flags, int backlog, evutil_socket_t fd);//建立一个监听器, 这一个不多使用(了解)
struct evconnlistener *evconnlistener_new_bind(struct event_base *base, evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,const struct sockaddr *sa, int socklen);//主要使用
void evconnlistener_free(struct evconnlistener *lev);

cb: 回调函数

ptr : 回调使用的参数

flags: 标志

主要使用的有两个

  1. LEV_OPT_CLOSE_ON_FRE, 释放bufferevent时候关闭这一个传输端口, 释放底层套接字
  2. LEV_OPT_REUSABLE: 端口可以复用

backlog: listen的第二个参数, 传-1表示使用最大值

sa: 服务器的IP+Port, 这一个是服务器自己的地址结构

成功的话返回一个监听器

typedef void (*evconnlistener_cb)(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *address, int socklen, void *arg);

这一个是他的回调函数, 会在客户端连接的时候调用这一个函数

listener: evconnlistener_new_bind函数的返回值

fd: 用于通信的文件描述符

address: 客户端的IP+端口

实际使用

  • 服务器端
  1. 创建event_base
  2. 建立一个监听服务器evconnlistener_new_bind, 设置一个回调函数, 有连接的时候这一个回调函数会被调用, 获取fd
  3. 封装listner_cb, 在这一个函数里面和客户端通信
  4. 在连接回调函数里面使用获取的fd创建一个buffer_event, 使用bufferevent_socket_new
  5. 设置一下回调函数, bufferevent_setcb, 给bufferevent的read, write和event设置回调函数
  6. 开启读写缓冲区
  7. 初始化回调函数, 监听的事件满足的时候, 回调函数被调用, 在read_cb里面使用bufferevent_read
  8. 启动监听event_base_dispatch
  9. 释放
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <event2/event.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <arpa/inet.h>
#include <ctype.h>void event_cb(struct bufferevent *bev, short events, void *arg){if(events & BEV_EVENT_EOF){printf("connection closed\n");}else if(events & BEV_EVENT_ERROR){printf("some other error\n");}bufferevent_free(bev);printf("event_cb\n");
}
void read_cb(struct bufferevent *bev, void *arg){char buf[1024];int len = bufferevent_read(bev, buf, sizeof(buf));if(len <= 0){printf("read_cb: connection closed\n");bufferevent_free(bev);return;}buf[len] = '\0';printf("read_cb: %s\n", buf);for(int i = 0;i<len;i++){buf[i] = toupper(buf[i]);}bufferevent_write(bev, buf, len);
}void write_cb(struct bufferevent *bev, void *arg){printf("write_cb\n");
}
void listener_cb(struct evconnlistener *listener, evutil_socket_t fd,struct sockaddr *addr, int socklen, void *arg){char buf[1024];int len;printf("connect from %s:%d\n", inet_ntop(AF_INET, &((struct sockaddr_in*)addr)->sin_addr,buf, sizeof(buf)),ntohs(((struct sockaddr_in*)addr)->sin_port));struct bufferevent *bev = bufferevent_socket_new(evconnlistener_get_base(listener), fd, BEV_OPT_CLOSE_ON_FREE);if(bev == NULL){printf("bufferevent_socket_newi error");return;}bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);bufferevent_enable(bev, EV_READ);
}int main(void){struct sockaddr_in serv;memset(&serv, 0, sizeof(serv));serv.sin_family = AF_INET;serv.sin_port = htons(9876);serv.sin_addr.s_addr = htonl(INADDR_ANY);//创建event_basestruct event_base *base = event_base_new();if(base == NULL){printf("event_base_new");return -1;}//创建监听器struct evconnlistener *listener = evconnlistener_new_bind(base, listener_cb, base, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,-1, (struct sockaddr*)&serv, sizeof(serv));if(listener == NULL){printf("evconnlistener_new_bind");return -1;}//启动事件循环event_base_dispatch(base);//释放资源evconnlistener_free(listener);event_base_free(base);return 0;
}
  • 客户端
  1. 获取一个base
  2. bufferevent_socket_new, 获取一个用于通信的bufferevent对象
  3. 使用bufferevent_socket_connect进行连接服务器
  4. 使用bufferevent_setcb()给这一个对象设置一个回调函数
  5. 读写缓冲区的使能以及失能
  6. 收发数据
  7. 释放资源
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <event2/event.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>void read_cb(struct bufferevent *bev, void *ctx){char buf[1024] = {0};int len = bufferevent_read(bev, buf, sizeof(buf)-1);if(len <= 0){fprintf(stderr, "read data error!\n");return;}printf("recv data: %s\n", buf);
}
void read_terminal(evutil_socket_t fd, short what, void *arg){char buf[1024] = {0};int len = read(fd, buf, sizeof(buf)-1);if(len <= 0){fprintf(stderr, "read data error!\n");return;}struct bufferevent *bev = (struct bufferevent *)arg;bufferevent_write(bev, buf, len);
}
void write_cb(struct bufferevent *bev, void *ctx){printf("write data\n");
}
void event_cb(struct bufferevent *bev, short what, void *ctx){if(what & BEV_EVENT_EOF){printf("connection closed\n");}else if(what & BEV_EVENT_ERROR){printf("some other error\n");}else if(what & BEV_EVENT_CONNECTED){printf("connected\n");return;}bufferevent_free(bev);
}
int main(void){struct event_base *base;struct bufferevent *bev;struct sockaddr_in serv;base = event_base_new();if(!base){fprintf(stderr, "Could not initialize libevent!\n");return 1;}int fd = socket(AF_INET, SOCK_STREAM, 0);memset(&serv, 0, sizeof(serv));bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);if(!bev){fprintf(stderr, "Could not create bufferevent!\n");return 1;}//连接服务器serv.sin_family = AF_INET;serv.sin_port = htons(9876);inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr);bufferevent_socket_connect(bev, (struct sockaddr *)&serv, sizeof(serv));bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);bufferevent_enable(bev, EV_READ|EV_WRITE);//监听标准输入struct event *ev = event_new(base, STDIN_FILENO, EV_READ|EV_PERSIST, read_terminal, bev);event_add(ev, NULL);event_base_dispatch(base);event_free(ev);event_base_free(base);bufferevent_free(bev);return 0;}

这篇关于Linux使用libevent协助socket编程进行网络通信----C语言实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/921453

相关文章

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

C语言中%zu的用法解读

《C语言中%zu的用法解读》size_t是无符号整数类型,用于表示对象大小或内存操作结果,%zu是C99标准中专为size_t设计的printf占位符,避免因类型不匹配导致错误,使用%u或%d可能引发... 目录size_t 类型与 %zu 占位符%zu 的用途替代占位符的风险兼容性说明其他相关占位符验证示

MySQL中EXISTS与IN用法使用与对比分析

《MySQL中EXISTS与IN用法使用与对比分析》在MySQL中,EXISTS和IN都用于子查询中根据另一个查询的结果来过滤主查询的记录,本文将基于工作原理、效率和应用场景进行全面对比... 目录一、基本用法详解1. IN 运算符2. EXISTS 运算符二、EXISTS 与 IN 的选择策略三、性能对比

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

Python实现网格交易策略的过程

《Python实现网格交易策略的过程》本文讲解Python网格交易策略,利用ccxt获取加密货币数据及backtrader回测,通过设定网格节点,低买高卖获利,适合震荡行情,下面跟我一起看看我们的第一... 网格交易是一种经典的量化交易策略,其核心思想是在价格上下预设多个“网格”,当价格触发特定网格时执行买

Oracle数据库定时备份脚本方式(Linux)

《Oracle数据库定时备份脚本方式(Linux)》文章介绍Oracle数据库自动备份方案,包含主机备份传输与备机解压导入流程,强调需提前全量删除原库数据避免报错,并需配置无密传输、定时任务及验证脚本... 目录说明主机脚本备机上自动导库脚本整个自动备份oracle数据库的过程(建议全程用root用户)总结

使用Python构建智能BAT文件生成器的完美解决方案

《使用Python构建智能BAT文件生成器的完美解决方案》这篇文章主要为大家详细介绍了如何使用wxPython构建一个智能的BAT文件生成器,它不仅能够为Python脚本生成启动脚本,还提供了完整的文... 目录引言运行效果图项目背景与需求分析核心需求技术选型核心功能实现1. 数据库设计2. 界面布局设计3

使用IDEA部署Docker应用指南分享

《使用IDEA部署Docker应用指南分享》本文介绍了使用IDEA部署Docker应用的四步流程:创建Dockerfile、配置IDEADocker连接、设置运行调试环境、构建运行镜像,并强调需准备本... 目录一、创建 dockerfile 配置文件二、配置 IDEA 的 Docker 连接三、配置 Do

Linux如何查看文件权限的命令

《Linux如何查看文件权限的命令》Linux中使用ls-R命令递归查看指定目录及子目录下所有文件和文件夹的权限信息,以列表形式展示权限位、所有者、组等详细内容... 目录linux China编程查看文件权限命令输出结果示例这里是查看tomcat文件夹总结Linux 查看文件权限命令ls -l 文件或文件夹

Android Paging 分页加载库使用实践

《AndroidPaging分页加载库使用实践》AndroidPaging库是Jetpack组件的一部分,它提供了一套完整的解决方案来处理大型数据集的分页加载,本文将深入探讨Paging库... 目录前言一、Paging 库概述二、Paging 3 核心组件1. PagingSource2. Pager3.