libev简单使用
https://zhuanlan.zhihu.com/p/163282654
socket编程最紧迫的需求就是有一个好用的buffer对象可以用,就像libevent的evbuffer类似的东西,现在我们定义一个简单的buffer
buffer.h
#pragma once
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
struct buffer {
unsigned char *head, *data, *tail, *end;
};
void buffer_init(struct buffer *buf);
void buffer_free(struct buffer *buf);
size_t buffer_length(const struct buffer *buf);
size_t buffer_size(const struct buffer *buf);
unsigned char *buffer_data(const struct buffer *buf);
void buffer_append_data(struct buffer *buf, const void *data, size_t len);
void buffer_append_null(struct buffer *buf);
void buffer_append_char(struct buffer *buf, char c);
void buffer_append_printf(struct buffer *buf, const char *fmt, ...);
void buffer_drain(struct buffer *buf, size_t len);
void buffer_reset(struct buffer *buf);
void buffer_hexdump(const struct buffer *buf);
#ifdef __cplusplus
}
#endif
我们对buffer的简单需求,就是可以在末尾添加数据(buffer_append_data),获取一块连续的内存(buffer_data),消费指定长度的数据(buffer_drain),这些操作内部都自动完成内存管理,使用者不需要考虑内存问题。
C语言事件循环库除了大名鼎鼎的libevent,还有libev。后者更加轻量。下面我们已libev为例,实现简单的io_copy功能,也即从一个描述符读数据,透传到另一个描述符。
先看使用示例:
开一个终端运行
$ nc -v -l 9999
Listening on [0.0.0.0] (family 0, port 9999)
运行
$ cc *.c -lev
$ ./a.out 127.0.0.1 9999 < /var/log/syslog
上面的示例程序把标准输入拷贝到127.0.0.1:9999标示的socket上
下面看main.c文件
// main.c
#include "io.h"
#include "xnet.h"
#include <ev.h>
#include <stdio.h>
#include <stdlib.h>
static void die(const char *s)
{
perror(s);
exit(1);
}
static void usage(const char *name)
{
fprintf(stderr, "usage: %s ip port
", name);
exit(1);
}
static void completion()
{
fprintf(stderr, "done
");
}
static void error(const char *errmsg)
{
fprintf(stderr, "error: %s
", errmsg);
}
int main(int argc, char *argv[])
{
struct ev_loop *loop;
int connfd;
struct io io;
if (argc != 3)
usage(argv[0]);
connfd = dial_tcp(argv[1], atoi(argv[2]));
if (connfd == -1)
die("dial_tcp");
loop = ev_default_loop(0);
io_init(&io, loop, completion, error);
io_copy(&io, connfd, 0); // 从描述符0拷贝数据转发给描述符connfd
ev_run(loop, 0);
}
定义struct io
// io.h
#pragma once
#include "buffer.h"
#include <ev.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*completion_cb)();
typedef void (*error_cb)(const char *errmsg);
struct io {
struct ev_io src_watcher;
struct ev_io dst_watcher;
struct buffer buf;
struct ev_loop *loop;
completion_cb completion;
error_cb error;
int src_done;
};
void io_init(struct io *io, struct ev_loop *loop, completion_cb completion, error_cb error);
void io_copy(struct io *io, int dst, int src);
#ifdef __cplusplus
}
#endif
// io.c
#include "io.h"
#include <errno.h>
#include <unistd.h>
#define HIGH_WATER_MARK (1024 * 1024 * 4)
#define LOW_WATER_MARK (1024 * 1024 * 1)
static void io_error(struct io *io)
{
ev_io_stop(io->loop, &io->src_watcher);
ev_io_stop(io->loop, &io->dst_watcher);
if (io->error)
io->error(strerror(errno));
}
static void io_completion(struct io *io)
{
ev_io_stop(io->loop, &io->src_watcher);
ev_io_stop(io->loop, &io->dst_watcher);
if (io->completion)
io->completion();
}
static void src_read_cb(struct ev_loop *loop, ev_io *w, int revents)
{
char buf[4096];
ssize_t nread;
struct io *io;
io = (struct io *)w->data;
nread = read(w->fd, buf, sizeof(buf));
switch (nread) {
case -1:
io_error(io);
return;
case 0:
ev_io_stop(loop, &io->src_watcher);
ev_io_start(loop, &io->dst_watcher);
io->src_done = 1; // 标示读完成了
return;
default:
buffer_append_data(&io->buf, buf, nread); // 把读到的数据添加到buffer末尾
ev_io_start(loop, &io->dst_watcher);
if (buffer_length(&io->buf) > HIGH_WATER_MARK) // buffer过大时停止读
ev_io_stop