libuv之管道

     由于libuv的异步特性,相比一般轮询具有实时性高速度快的优点,这里介绍使用libuv实现进程间通讯,采用有名管道的通讯方式。当然还有其他IO方式,如下图。

loop     

libuv的使用离不开loop循环,可以把它理解成一个队列,你可以向这个队列里添加自己要做的事情,libuv会根据你的设定执行队列里的任务。

uv_loop_t * loop = uv_loop_new();//创建一个loop循环
uv_timer_t time1;
uv_timer_t time2;

uv_timer_init(loop, &time1);       //初始化timer
uv_timer_start(&time1,fun1, 0,10); //添加循环事件,每10ms执行一次

uv_timer_init(loop, &time2);
uv_timer_start(&time2,fun2, 0,20);

uv_run(loop, UV_RUN_DEFAULT);     //启动loop循环

上面的代码作用是每10ms执行一次fun1,每20ms执行一次fun2。这里需要注意的是,一个线程里一般只有一个loop循环,写多个并不能让效率提高,以为在一个线程里都是事件顺序执行的。也就是说假如在fun1里sleep 1秒,那么在这1s内整个线程也是被阻塞的,fun2并不会被执行。

pipe

     正常的pipe进程间通讯采用的是write/read的方式,libuv追其根源也是调用write/read函数,但是速度更快,效率更高,更节省cpu资源。

#include "uv.h"

//server进程
uv_loop_t *loop_fun_server;
#define      FUN_MAX_LINE  32
void data_fun_echo_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf)
{
    printf("%s",buf->base);
}

void data_fun_alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) 
{
  buf->base = g_fun_buffer;
  buf->len = FUN_MAX_LINE;
}

void listen_loop_connection(uv_stream_t *server, int status) 
{
    if (status == -1) 
	{
        // error!
		perror("connect");
        return;
    }

    uv_pipe_t *client = (uv_pipe_t*) malloc(sizeof(uv_pipe_t));
    uv_pipe_init(loop_fun_server, client, 0);
    if (uv_accept(server, (uv_stream_t*) client) == 0) {
		printf("connected\n");
        uv_read_start((uv_stream_t*) client, data_fun_alloc_buffer, data_fun_echo_read);
    }
    else 
	{
        uv_close((uv_handle_t*) client, NULL);
		perror("accept");
    }
}

void main()
{
    loop_fun_server = uv_loop_new();
    uv_pipe_t server;
    uv_connect_t connect_req;
    uv_pipe_init(loop_fun_server, &server, 1);
    int r;
    unlink("/tmp/pipe_fun_server");

    if ((r = uv_pipe_bind(&server, "/tmp/pipe_fun_server"))) 
    {
        perror("pipe bind:");
    }
    if ((r = uv_listen((uv_stream_t*) &server, 128, listen_loop_connection))) 
    {
        perror("pipe listen:");
    }
    uv_run(loop_fun_server, UV_RUN_DEFAULT);
}
#include "uv.h"

//client进程
static uv_loop_t *loop_pipe_fun_client;
static uv_connect_t* clientconnect;
void tid_on_write(uv_write_t *req, int status)
{
    if (status != 0) 
	{
	  perror("on write");
	  return;
    } 
	free(req);
}
void fun_pipe_send_buff(uv_connect_t* connect, char *buffer, int buffersize)
{
	uv_buf_t write_buff;
	uv_write_t* write_req = NULL;
	int ret;
	if (NULL == connect)
	{
		return;
	}
	write_req = (uv_write_t*)malloc(sizeof(*write_req));
	

	write_buff = uv_buf_init(buffer, buffersize);
	ret = uv_write((uv_write_t*)write_req, connect->handle, &write_buff, 1,tid_on_write);
	if (ret != 0)
	{
		perror("uv write");
	}
}

void fun_client_connect_cb(uv_connect_t* connect, int status) 
{
	clientconnect = connect;
	if (status < 0) 
	{
		perror("failed!");
	} else 
	{
		printf("connected! sending msg...\n");		
	}
}
//向server发送数据
void fun_pipe_send_buff(uv_connect_t* connect, char *buffer, int buffersize)
{
	uv_buf_t write_buff;
	uv_write_t* write_req = NULL;
	int ret;
	if (NULL == connect)
	{
		return;
	}
	write_req = (uv_write_t*)malloc(sizeof(*write_req));
	

	write_buff = uv_buf_init(buffer, buffersize);
	ret = uv_write((uv_write_t*)write_req, connect->handle, &write_buff, 1,tid_on_write);
	if (ret != 0)
	{
		perror("uv write");
	}
}
//每5ms发送一次数据
void process_fun_check(uv_timer_t *handle)
{
    char buf[6] = "hello";
    fun_pipe_send_buff(clientconnect,buf,strlen(buf));
}
void pipe_write()
{
   uv_pipe_t		client;
   uv_connect_t	connect_req;
   uv_loop_t *loop_pipe_fun_client = uv_loop_new();
   uv_timer_t time;
   uv_pipe_init(loop_pipe_fun_client, &client, 1);//1-进程间通讯;0-线程间通讯
   uv_pipe_connect(&connect_req, &client, "/tmp/pipe_fun_server", fun_client_connect_cb);

   uv_timer_init(loop_pipe_fun_client, &time);
   uv_timer_start(&time,process_fun_check, 1,5);

   uv_run(loop_pipe_fun_client, UV_RUN_DEFAULT);
}

在server中,只要client有数据发送就会触发data_fun_echo_read函数,就不用消耗资源写一个read循环了。

注意

    在libuv的所有异步通讯中,uv_read_start(uv_stream_t* stream, uv_alloc_cb alloc_cb, uv_read_cb read_cb)函数担任了接收数据的重任,alloc_cb开辟用于存储接收到数据的空间,read_cb接收数据。如果开辟的空间大于实际接收到数据长度就不会有什么问题,小于实际接收数据长度的话,收到的数据就会被截断,只接受开辟空间大小的数据。比如:开辟了10byte,实际缓存里有15byte,那么只会读取前10byte,后面会留到下次再读。这样一来数据处理就会变得麻烦,所以开辟空间一般比实际用到的大一点,方便使用。

   其次就是由于read_cb是回调函数,所以在这个函数里不要有太多的复杂处理,会影响接收数据的效率,严重时甚至会导致内存错误。这里也挺无奈,异步带来了高效率,同时要求跟他配合使用的函数也要高效简洁。

   

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值