服务器开发LInux/C++

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

2020-12-02  本文已影响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

上一篇下一篇

猜你喜欢

热点阅读