libuv初步学习

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/okiwilldoit/article/details/79014979

一. 简介

Libevent、libev、libuv三个网络库,都是c语言实现的异步事件库Asynchronous event library)。

事件(Event):事件是异步事件通知机制的核心,比如fd事件、超时事件、信号事件、定时器事件。有时候也称事件为事件处理器(EventHandler),这个名称更形象,因为Handler本身表示了包含处理所需数据(或数据的地址)和处理的方法(回调函数),更像是面向对象思想中的称谓。

事件循环(EventLoop):等待并分发事件。事件循环用于管理事件。
对于应用程序来说,这些只是异步事件库提供的API,封装了异步事件库跟操作系统的交互,异步事件库会选择一种操作系统提供的机制来实现某一种事件,比如利用Unix/Linux平台的epoll机制实现网络IO事件,在同时存在多种机制可以利用时,异步事件库会采用最优机制。

对比下三个库:
libevent :名气最大,应用最广泛,历史悠久的跨平台事件库;
libev :较libevent而言,设计更简练,性能更好,但对Windows支持不够好;
libuv :开发node的过程中需要一个跨平台的事件库,他们首选了libev,但又要支持Windows,故重新封装了一套,linux下用libev实现,Windows下用IOCP实现;
可见,目前libuv的影响力最大,其次是libevent,libev关注的人较少。

libuv 采用了 异步 (asynchronous), 事件驱动 (event-driven)的编程风格,其主要任务是为开人员提供了一套事件循环和基于I/O(或其他活动)通知的回调函数,libuv 提供了一套核心的工具集,例如定时器,非阻塞网络编程的支持,异步访问文件系统,子进程以及其他功能。

二. 安装libuv

从githup上下载libuv。
$cd libuv 进入到libuv源码目录下,准备编译。
依次运行如下命令:

$ ./autogen.sh
$ ./configure
$ make clean;make
$ make install

这时,会在/usr/local/lib目录下生成libuv.so和libuv.a两个库。
为了方便部署程序,我们使用静态库,将libuv.a拷贝出来放到自己的项目中。

三.编写hello world代码

源文件:

/*
 * hello.cpp
 * empty msg loop
 *  这个例子新建了一个消息队列,但队列里没有任何消息,程序直接退出
 */

#include <stdlib.h>
#include <stdio.h>
#include "libuv/uv.h"

int main()
{
    uv_loop_t *loop = uv_loop_new();    //可以理解为新建一个消息队列
    uv_run(loop, UV_RUN_DEFAULT);   //启动消息队列的消费,UV_RUN_DEFAULT模式下,当消息数为0时,就会退出消息循环
    printf("hello world!\n");
    return 0;
}

Makefile文件:

INCLUDE = -I./include
LIB = -L./lib -luv -lrt

all:
    g++ -lpthread -o hello hello.cpp $(INCLUDE) $(LIB)

clean:
    rm -rf hello

四.编写tcp代码

libuv 的网络接口与 BSD 套接字接口存在很大的不同, 某些事情在 libuv 下变得更简单了, 并且所有接口都是都是非阻塞的, 但是原则上还是一致的。
libuv 中在网络 I/O 中使用了 uv_tcp_t 和 uv_udp_t 两个结构体。

tcp-server端代码:

服务器端的 sockets 处理流程如下:
1. uv_tcp_init 初始化 TCP 监视器.
2. uv_tcp_bind 绑定.
3. 在指定的监视器上调用 uv_listen 来设置回调函数, 当有新的客户端连接到来时, libuv 就会调用设置的回调函数.
4. uv_accept 接受连接.
5. 使用 stream operations 与客户端进行通信.
以下是一个简单的 tcp 服务器的例子,
该例会把从client接收到数据转化为大写字母,并回复给client:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "libuv/uv.h"

#define DEFAULT_PORT 8000
#define DEFAULT_BACKLOG 128

uv_loop_t *loop;
struct sockaddr_in addr;

void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) 
{
    buf->base = (char*) malloc(suggested_size);
    buf->len = suggested_size;
}

void echo_write(uv_write_t *req, int status)
{
    if (status < 0) {
        fprintf(stderr, "Write error %s\n", uv_strerror(status));
    }
    free(req);
}

void echo_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf)
{
    if (nread < 0) 
    {
        if (nread != UV_EOF)
            fprintf(stderr, "Read error %s\n", uv_err_name(nread));
        uv_close((uv_handle_t*) client, NULL);
    } 
    else if (nread > 0) 
    {
        printf("echo_read:%s\r\n",buf->base);

        uv_write_t *req = (uv_write_t *) malloc(sizeof(uv_write_t));

        int i=0;
        while(i < nread)
        {
            buf->base[i] = toupper(buf->base[i]);
            i++;
        }

        uv_buf_t wrbuf = uv_buf_init(buf->base, nread);
        uv_write(req, client, &wrbuf, 1, echo_write);
    }

    if (buf->base)
    {
        free(buf->base);
    }
}

void on_new_connection(uv_stream_t *server, int status)
{
    if (status < 0) 
    {
        fprintf(stderr, "New connection error %s\n", uv_strerror(status));
        return;
    }

    uv_tcp_t *client = (uv_tcp_t*) malloc(sizeof(uv_tcp_t));
    uv_tcp_init(loop, client);
    if (uv_accept(server, (uv_stream_t*) client) == 0)
    {
        uv_read_start((uv_stream_t*) client, alloc_buffer, echo_read);
    }
    else 
    {
        uv_close((uv_handle_t*) client, NULL);
    }
    printf("on new connection, status:%d\r\n", status);
}

int main() 
{
    printf("buliding tcp\n");
    loop = uv_default_loop();

    uv_tcp_t server;
    uv_tcp_init(loop, &server);

    printf("port%d\n", DEFAULT_PORT);

    uv_ip4_addr("0.0.0.0", DEFAULT_PORT, &addr);

    uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0);
    printf("listening %d\n", DEFAULT_PORT);
    int r = uv_listen((uv_stream_t*) &server, DEFAULT_BACKLOG, on_new_connection);
    if (r) 
    {
        fprintf(stderr, "Listen error %s\n", uv_strerror(r));
        return 1;
    }
    return uv_run(loop, UV_RUN_DEFAULT);
}

tcp client代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libuv/uv.h"

using namespace std;

uv_loop_t *loop;
struct sockaddr_in req_addr;


void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf)
{
    buf->base = (char*) malloc(suggested_size);
    buf->len = suggested_size;
}

void echo_read(uv_stream_t *server, ssize_t nread, const uv_buf_t *buf)
{
  if (nread < 0) {
    fprintf(stderr, "error echo_read");
    return;
  }

  printf("response from server: %s\n", buf->base);
}


void on_write_end(uv_write_t *req, int status)
{
  if (status < 0) {
    fprintf(stderr, "error on_write_end");
    return;
  }

  uv_read_start(req->handle, alloc_buffer, echo_read);
}

//连接tcp server
void on_connect(uv_connect_t *req, int status)
{
  if (status < 0) {
    fprintf(stderr, "On_connection error: %d\n", status);
    return;
  }

  //输入想要发送的数据
  char buffer[100];
  cin >> buffer;

  uv_buf_t buf = uv_buf_init(buffer, sizeof(buffer));

  buf.len = strlen(buffer);
  buf.base = buffer;

  uv_stream_t* tcp = req->handle;

  uv_write_t write_req;

  int buf_count = 1;
  uv_write(&write_req, tcp, &buf, buf_count, on_write_end);
}

int main(void) 
{

  loop = uv_default_loop();

  uv_tcp_t client;
  uv_tcp_init(loop, &client);

 //服务器端ip和port
  uv_ip4_addr("0.0.0.0", 8000, &req_addr);

  uv_connect_t connect_req;

  //与server建立连接
  uv_tcp_connect(&connect_req, &client, (const struct sockaddr*)&req_addr, on_connect);

  return uv_run(loop, UV_RUN_DEFAULT);
}

五.编写udp代码

udp-server端代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "libuv/uv.h"

#define DEFAULT_PORT 8001

uv_loop_t *loop;

void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf)
{
    buf->base = (char*) malloc(suggested_size);
    buf->len = suggested_size;
}

void on_send(uv_udp_send_t *req, int status)
{
    if (status < 0) {
        fprintf(stderr, "Send error %s\n", uv_strerror(status));
    }
}

void on_recv(uv_udp_t *req, ssize_t nread, const uv_buf_t *buf, const struct sockaddr *addr, unsigned flags)
{
    if (nread < 0) {
        fprintf(stderr, "Read error %s\n", uv_err_name(nread));
        uv_close((uv_handle_t*) req, NULL);
        free(buf->base);
        return;
    }

    char sender[17] = { 0 };
    uv_ip4_name((const struct sockaddr_in*)addr, sender, 16);
    printf("on_recv:%s, recv from:%s\r\n", buf->base, sender);

    int i=0;
    while(i < nread)
    {
        buf->base[i] = toupper(buf->base[i]);
        i++;
    }

    uv_buf_t wrbuf = uv_buf_init(buf->base, nread);

    uv_udp_send_t send_req;
    uv_udp_t send_socket;
    uv_udp_init(loop, &send_socket);

    uv_udp_send(&send_req, &send_socket, &wrbuf, 1, addr, on_send);

    if (buf->base)
    {
        free(buf->base);
    }

    uv_udp_recv_stop(req);
}

int main() 
{
    printf("buliding udp\n");
    loop = uv_default_loop();

    uv_udp_t recv_socket;

    //server端接收数据
    uv_udp_init(loop, &recv_socket);
    struct sockaddr_in recv_addr;
    uv_ip4_addr("0.0.0.0", DEFAULT_PORT, &recv_addr);
    uv_udp_bind(&recv_socket, (const struct sockaddr *)&recv_addr, UV_UDP_REUSEADDR);
    uv_udp_recv_start(&recv_socket, alloc_buffer, on_recv);

    return uv_run(loop, UV_RUN_DEFAULT);
}

udp-client代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <iostream>
#include "libuv/uv.h"

using namespace std;

#define DEFAULT_PORT 8001

uv_loop_t *loop;

void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf)
{
    buf->base = (char*) malloc(suggested_size);
    buf->len = suggested_size;
}

void on_recv(uv_udp_t *req, ssize_t nread, const uv_buf_t *buf, const struct sockaddr *addr, unsigned flags)
{
    if (nread < 0) {
        fprintf(stderr, "Read error %s\n", uv_err_name(nread));
        uv_close((uv_handle_t*) req, NULL);
        free(buf->base);
        return;
    }

    //nread=0表示没有更多的数据可读
    if(nread > 0)
    {
        printf("on_recv:%s,len:%d\r\n", buf->base, nread);
    }
}

void on_send_end(uv_udp_send_t *req, int status)
{
    if (status < 0) {
        fprintf(stderr, "Send error %s\n", uv_strerror(status));
    }

    uv_udp_recv_start(req->handle, alloc_buffer, on_recv);
}



int main() 
{
    loop = uv_default_loop();

    //输入想要发送的数据
    char buffer[100];
    cin >> buffer;

    uv_buf_t buf = uv_buf_init(buffer, sizeof(buffer));
    buf.len = strlen(buffer);
    buf.base = buffer;

    uv_udp_t send_socket;
    uv_udp_init(loop, &send_socket);
    uv_udp_send_t send_req;

    struct sockaddr_in addr;
    uv_ip4_addr("0.0.0.0", DEFAULT_PORT, &addr);

    uv_udp_send(&send_req, &send_socket, &buf, 1, (const struct sockaddr *)&addr, on_send_end);

    return uv_run(loop, UV_RUN_DEFAULT);
}

libuv也支持管道通信和unix domain socket,通过uv_pipe_t 结构体来实现tcp 本地socket通信,多用于本地进程通信。但是现在libuv的uv_pipe_t仅支持发送TCP sockets或者other pipes。
参考文献:
http://docs.libuv.org/en/v1.x/guide/processes.html#sending-file-descriptors-over-pipes

猜你喜欢

转载自blog.csdn.net/okiwilldoit/article/details/79014979