美文网首页服务器开发LInux/C++
libuv学习笔记5------TCP客户端的实现

libuv学习笔记5------TCP客户端的实现

作者: _李恒 | 来源:发表于2020-12-02 10:22 被阅读0次

    前一节我们讲到了如何用libuv实现一个TCP服务器,用libuv实现一个客户端与用libuv实现一个TCP服务器极为类似。不同的地方在于不需要进行uv_tcp_bind操作,将uv_listen改为uv_tcp_connect。

    实现一个TCP客户端的基本步骤为:

    1.uv_tcp_init建立tcp句柄

    2.uv_tcp_connect建立tcp连接

    3.使用stream操作来和客户端通信

    所用到的API为:

    1.uv_tcp_init

    2.uv_ip4_addr

    3.uv_tcp_connect

    4.uv_write/uv_read_start

    下面讲只针对TCP客户端的实现介绍一个新的API函数uv_tcp_connect。

    1.uv_tcp_connect函数讲解

    int uv_tcp_connect(uv_connect_t* req,uv_tcp_t* handle,const struct sockaddr* addr,uv_connect_cb cb);

    参数1:连接请求对象

    参数2:TCP客户端对象

    参数3:填充好的struct sockaddr_in结构体

    参数4:回调函数

    
    struct sockaddr_in addr;
     
    uv_connect_t* connect = (uv_connect_t*)malloc(sizeof(uv_connect_t));
     
     
    uv_ip4_addr("192.168.65.205",DEFAULT_PORT,&addr);
     
    int r = uv_tcp_connect(connect,&mysocket,(const struct sockaddr*)&addr,on_connect);
     
    if(r)
    {
        fprintf(stderr, "connect error %s\n", uv_strerror(r));
        return 1;
    }
    

    2.uv_tcp_connect回调函数介绍

    void (uv_connect_cb)(uv_connect_t req, int status);

    在连接成功/失败后调用此函数。

    status:返回的状态,小于零代表出错。

    req:连接请求对象,req->handle指向TCP客户端对象(mysocket),可直接将req->handle用于uv_write,uv_read_start等流操作中。

    3.传输指定文件给服务器的代码实现

    #include <stdio.h>
    #include <uv.h>
    #include <stdlib.h>
     
    uv_loop_t *loop;
    #define DEFAULT_PORT 7000
     
    uv_tcp_t mysocket;
     
    char *path = NULL;
    uv_buf_t iov;
    char buffer[128];
     
    uv_fs_t read_req;
    uv_fs_t open_req;
    void on_read(uv_fs_t *req);
    void on_write(uv_write_t* req, int status)
    {
        if (status < 0) 
        {
            fprintf(stderr, "Write error: %s\n", uv_strerror(status));
            uv_fs_t close_req;
            uv_fs_close(uv_default_loop(), &close_req, open_req.result, NULL);
            uv_close((uv_handle_t *)&mysocket,NULL);
            exit(-1);
        }
        else 
        {
            uv_fs_read(uv_default_loop(), &read_req, open_req.result, &iov, 1, -1, on_read);
        }
    }
     
    void on_read(uv_fs_t *req)
    {
        if (req->result < 0) 
        {
            fprintf(stderr, "Read error: %s\n", uv_strerror(req->result));
        }
        else if (req->result == 0) 
        {
            uv_fs_t close_req;
            // synchronous
            uv_fs_close(uv_default_loop(), &close_req, open_req.result, NULL);
            uv_close((uv_handle_t *)&mysocket,NULL);
        }
        else
        {
            iov.len = req->result;
            uv_write((uv_write_t *)req,(uv_stream_t *)&mysocket,&iov,1,on_write);
        }
    }
     
    void on_open(uv_fs_t *req)
    {
        if (req->result >= 0) 
        {
            iov = uv_buf_init(buffer, sizeof(buffer));
            uv_fs_read(uv_default_loop(), &read_req, req->result,&iov, 1, -1, on_read);
        }
        else 
        {
            fprintf(stderr, "error opening file: %s\n", uv_strerror((int)req->result));
            uv_close((uv_handle_t *)&mysocket,NULL);
            exit(-1);
        }
    }
     
    void on_connect(uv_connect_t* req, int status)
    {
        if(status < 0)
        {
            fprintf(stderr,"Connection error %s\n",uv_strerror(status));
     
            return;
        }
     
        fprintf(stdout,"Connect ok\n");
     
        uv_fs_open(loop,&open_req,path,O_RDONLY,-1,on_open);
     
     
    }
     
    int main(int argc,char **argv)
    {
        if(argc < 2)
        {
            fprintf(stderr,"Invaild argument!\n");
     
            exit(1);
        }
        loop = uv_default_loop();
     
        path = argv[1];
     
        uv_tcp_init(loop,&mysocket);
     
        struct sockaddr_in addr;
     
        uv_connect_t* connect = (uv_connect_t*)malloc(sizeof(uv_connect_t));
     
     
        uv_ip4_addr("127.0.0.1",DEFAULT_PORT,&addr);
     
        int r = uv_tcp_connect(connect,&mysocket,(const struct sockaddr *)&addr,on_connect);
     
        if(r)
        {
            fprintf(stderr, "connect error %s\n", uv_strerror(r));
            return 1;
        }
     
        return uv_run(loop,UV_RUN_DEFAULT);
    }
    

    这段代码利用了libuv文件操作、流操作、TCP实现等知识。代码的基本逻辑如下:

    1.创建TCP客户端对象,和TCP连接请求对象,并与服务器建立连接。

    2.连接成功后打开要传输的文件。

    3.打开文件成功后读取一定的文件内容,并在读取成功后将文件内容利用stream操作发送给服务器。

    4.发送成功后再次读取文件内容,并在读取成功后发送给服务器。

    5.循环进行第4步操作,直到读到文件末尾。

    6.关闭文件、关闭流。

    测试:

    1.打开服务器,监听7000端口,并将输出重定向到文件:

    nc -l 7000 > test_recv.jpg

    2.编译并运行程序:

    gcc main.c -luv

    ./a.out test.jpg

    相关文章

      网友评论

        本文标题:libuv学习笔记5------TCP客户端的实现

        本文链接:https://www.haomeiwen.com/subject/aibcwktx.html