libuv编译和基于tcp的简单聊天客户端+服务端

目录

1. 下载源码

2. 怎么编译

3. tcp client

4.tcp server

5. 编译

6. 执行效果



1. 下载源码

git clone https://github.com/libuv/libuv.git

官方文档

http://docs.libuv.org/en/v1.x/design.html

2. 怎么编译

linux环境下,官方建议是

$ sh autogen.sh
$ ./configure
$ make
$ make check
$ make install

我们自己看看可以配置什么

执行效果:

选择安装目录和只编译静态库

$ ./configure --enable-static --disable-shared prefix=$PWD/output

3. tcp client

//tcpclient.cpp
#include <iostream>
#include <string.h>
#include <assert.h>
#include "uv.h"


#define TCP_SERVER_IP "127.0.0.1"
#define TCP_SERVER_PORT 9000
#define MAX_STRING_LEN 256

uv_tcp_t tcp_client;
uv_connect_t connect_req;
uv_write_t write_req_;
uv_loop_t uv_loop_;
bool            need_loop_ = true;
//发送消息
char str_msg_[MAX_STRING_LEN]={0};
uv_buf_t uv_buf_ = {str_msg_, 0 };
uv_async_t async_handle_;


void on_close(uv_handle_t* handle) {
    std::cout<<__LINE__<<"******* 退出 ******"<<std::endl;
}

//给读取数据,新建buf
static void alloc_cb(uv_handle_t* handle, size_t suggested_size,uv_buf_t* buf)
{
    //std::cout<<__LINE__<<" alloc_cb-- suggested_size="<<suggested_size<<std::endl;
    buf->base = (char*)malloc(suggested_size);
    buf->len = suggested_size;
}

//读取网络数据,并且释放buf
static void read_cb(uv_stream_t* stream, ssize_t nread,const uv_buf_t* buf)
{
    //std::cout<<__LINE__<<" alloc_cb-- nread="<<nread<<std::endl;
    do{
        if (nread < 0) {/* Error or EOF */
            if (nread == UV_EOF) {
                fprintf(stdout, "client(%d)eof\n", stream->u.fd);
            } else if (nread == UV_ECONNRESET) {
                fprintf(stdout, "client(%d)conn reset\n", stream->u.fd);
            } else {
                fprintf(stdout, "[%d] %s\n", __LINE__, uv_strerror(nread));
            }
            
            uv_close((uv_handle_t*)stream, on_close);
            break;;
        } else if (0 == nread)  {/* Everything OK, but nothing read. */

        } else {
            char recvMsg[MAX_STRING_LEN]={0};
            memcpy(recvMsg, buf->base, nread);
            std::cout<<std::endl<<"server: "<<recvMsg<<std::endl;
        }
    }while(0);

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

static void write_cb(uv_write_t *req, int status) {
    if (req && status == 0)
    {
        //std::cout << __LINE__ << "  write_cb sucess" << std::endl;
    }
    else
    {
        std::cout << __LINE__ << "  write_cb status = UV_ECONNRESET" <<"status="<< status << std::endl;
        uv_close((uv_handle_t*)req->handle, NULL);
    }
}

static void on_connect(uv_connect_t* req, int status) {
    assert(0 == status);
    //std::cout<<__LINE__<<"  on_connect   status="<<status<<std::endl;

    uv_read_start(req->handle, alloc_cb, read_cb);

    assert(0==uv_write(&write_req_ , (uv_stream_t *)&tcp_client, &uv_buf_, 1,write_cb ));
}

void on_client_close(uv_handle_t* handle)
{
    std::cout<<__LINE__<<"  on_client_close"<<std::endl;
}
//完全关闭loop
void on_close_walk(uv_handle_t* handle, void* arg)
{
    //std::cout<<__LINE__<<"  on_close_walk"<<std::endl;
    if (!uv_is_closing(handle)) {
        std::cout<<__LINE__<<"  call uv_close"<<std::endl;
        uv_close(handle, on_client_close);
    }
}

static void on_thread_input_msg(void *argc)
{
    std::cout<<"thread input msg ..."<<std::endl;
    uv_tcp_t *pClient = (uv_tcp_t*)argc;
    while(1)
    {
        uv_sleep(500);
        std::cout<<std::endl<<"请输入(q退出):"<<std::endl;

        //控制台输入字符串
        std::string str_input;
        std::getline(std::cin, str_input);
        
        std::cout<<"client: "<<str_input.c_str()<<std::endl;

        if(str_input=="q")
        {
            //此处标志位,然后在相应的回调中close,否则会出现异常
            need_loop_ = false;
            uv_async_send(&async_handle_);
            break;
        }
        
        memset(uv_buf_.base, 0x0, MAX_STRING_LEN);
        memcpy(uv_buf_.base , str_input.c_str(), str_input.length());
        uv_buf_.len =  str_input.length();

        uv_async_send(&async_handle_);
    }
}

static void on_async_send(uv_async_t* handle)
{
     //std::cout<<__LINE__<<"  in  on_async_send ... "<<std::endl;
     if(!need_loop_)
     {
            uv_walk(&uv_loop_, on_close_walk, (void*)handle);
     }

     if( uv_buf_.len == 0)
     {
        std::cout<<"空消息,不发送 ..."<<std::endl;
        return;
     }

    int ret=uv_write(&write_req_ , (uv_stream_t *)&tcp_client, &uv_buf_, 1,write_cb );
    if(ret==0)
    {
        //std::cout<<"发送成功 ..."<<std::endl;
    }
    else{
        std::cout<<"发送失败 ..."<<uv_strerror(ret)<<std::endl;
    }
}

int main()
{
    int ret = 0;
    std::cout<<"hello tcp client..."<<std::endl;
    assert(ret ==uv_loop_init(&uv_loop_));
    ret = uv_tcp_init(&uv_loop_,  &tcp_client);
    assert(ret == 0);

    //本地客户端端口
    struct sockaddr_in client_addr;
    uv_ip4_addr("127.0.0.1", 9999, &client_addr);
    uv_tcp_bind(&tcp_client, (const sockaddr*)&client_addr, 0);

    struct sockaddr_in server_addr;
    assert(0 == uv_ip4_addr(TCP_SERVER_IP, TCP_SERVER_PORT, &server_addr));

    ret = uv_tcp_connect(&connect_req, &tcp_client, (const struct sockaddr*) &server_addr, on_connect );
    assert(ret == 0);

    //异步发送信息的方式
    uv_async_init(&uv_loop_, &async_handle_, on_async_send);

    //线程从控制台输入字符串
    uv_thread_t thread_msg;
    uv_thread_create(&thread_msg, on_thread_input_msg, &tcp_client);

    ret = uv_run(&uv_loop_, UV_RUN_DEFAULT);
    if(ret==0)
    {
        std::cout<<"loop 正常退出 ..."<<std::endl;
    }
    else{
        std::cout<<"loop 异常退出  ..."<<uv_strerror(ret)<<std::endl;
    }
    
    uv_loop_close(&uv_loop_);

    //getchar();

    return 0;
}

4.tcp server

//tcpserver.cpp
#include <iostream>
#include <assert.h>
#include "uv.h"


uv_tcp_t tcp_server;
uv_connect_t connect_req;
uv_write_t write_req;
struct sockaddr_in addr;
uv_buf_t buf;
uv_thread_t thread_recv;
uv_thread_t thread_watch_;
uv_loop_t loop_;

#define TCP_SERVER_IP "127.0.0.1"
#define TCP_SERVER_PORT 9000

static void write_cb(uv_write_t *req, int status) {
    std::cout<<__LINE__<<" write_cb-- status="<<status<<std::endl;
    //assert(status == UV_ECANCELED);
    //uv_close((uv_handle_t*) req->handle, NULL);
    if (status) {
        fprintf(stderr, "Write error %s\n", uv_strerror(status));
    }
    free(req);
}

static void close_cb(uv_handle_t* handle) {
     std::cout<<__LINE__<<" -- close_cb="<<std::endl;
}

static void alloc_cb(uv_handle_t* handle, size_t suggested_size,uv_buf_t* buf)
{
    std::cout<<__LINE__<<" alloc_cb-- suggested_size="<<suggested_size<<std::endl;
    buf->base = (char*)malloc(suggested_size);
    buf->len = suggested_size;
}

void on_close(uv_handle_t* handle) {
    free(handle);
}

static void read_cb(uv_stream_t* stream, ssize_t nread,const uv_buf_t* buf)
{
    std::cout<<__LINE__<<" alloc_cb-- nread="<<nread<<std::endl;
     if (nread < 0) {/* Error or EOF */
        if (nread == UV_EOF) {
            fprintf(stdout, "client(%d)eof\n", stream->u.fd);
        } else if (nread == UV_ECONNRESET) {
            fprintf(stdout, "client(%d)conn reset\n", stream->u.fd);
        } else {
            fprintf(stdout, "[%d] %s\n", __LINE__, uv_strerror(nread));
        }
        
        uv_close((uv_handle_t*)stream, on_close);
        return;
    } else if (0 == nread)  {/* Everything OK, but nothing read. */

    } else {
        std::cout<<__LINE__<<" read data = "<<nread<<"   data:"<<buf->base<<std::endl;

        //再发送到client
        uv_write_t* req = (uv_write_t*)malloc(sizeof(uv_write_t));
        uv_buf_t uvBuf = uv_buf_init(buf->base, nread);
        if (uv_write(req, stream, &uvBuf, 1, write_cb)) {
            std::cout<<"uv_write failed"<<std::endl;
        }
    }

    free(buf->base);
}

static void connection_cb(uv_stream_t* server, int status)
{
    std::cout<<__LINE__<<" connection_cb-- status="<<status<<std::endl;
    //为tcp client申请资源;
    uv_tcp_t *tcp_client = (uv_tcp_t *) malloc(sizeof(uv_tcp_t));
    uv_tcp_init(&loop_,  tcp_client);
    uv_accept((uv_stream_t*)&tcp_server, (uv_stream_t*)tcp_client);

    uv_read_start((uv_stream_t*)tcp_client, alloc_cb, read_cb);

    struct sockaddr sockname, peername;
    int namelen = sizeof(peername);
    assert(0==uv_tcp_getpeername(tcp_client, &peername, &namelen )) ;
    struct sockaddr_in check_addr = *(struct sockaddr_in*) &peername;
    char check_ip[17];
    uv_ip4_name(&check_addr, (char*)check_ip, sizeof check_ip);
    std::cout<<__LINE__<<" -- peer ip="<<check_ip <<" : port="<<check_addr.sin_port <<std::endl;
}

static void thread_recv_data(void* arg)
{
    std::cout<<__LINE__<<" -- thread_recv_data"<<std::endl;
    //
    int ret =  uv_run(&loop_, UV_RUN_DEFAULT);
    if(0 == ret)
    {
        std::cout<<__LINE__<<" stoped... ret="<<ret<<std::endl;
    }
    else{
        //如果调用了uv_stop(),并且仍有活动句柄或请求,则返回非零
        std::cout<<__LINE__<<" stoped... ret="<<ret<<std::endl;
    }
}

static void thread_watch(void* arg)
{
    while(1)
    {
        int c = getchar();
        if(c=='q')
        {
            break;
        }
    }

     uv_stop(&loop_);
}

int main()
{
    std::cout<<"hello, this is tcp server..."<<std::endl;
    assert(0 == uv_ip4_addr("0.0.0.0", TCP_SERVER_PORT, &addr));
    assert(0==uv_loop_init(&loop_));
    int r = uv_tcp_init(&loop_, &tcp_server);
    assert(r == 0);
    r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0);
    assert(r == 0);
    r = uv_listen((uv_stream_t*)&tcp_server, 128, connection_cb);
    assert(r == 0);

    assert(0 == uv_is_writable((uv_stream_t*) &tcp_server));
    assert(0 == uv_is_readable((uv_stream_t*) &tcp_server));

    uv_thread_create(&thread_recv, thread_recv_data, (void*)&tcp_server);
    uv_thread_create(&thread_watch_, thread_watch, nullptr);

    uv_thread_join(&thread_watch_);
    uv_thread_join(&thread_recv);

    uv_loop_close(&loop_);
    uv_close((uv_handle_t*)&tcp_server, close_cb);

    return 0;
}

5. 编译

g++  -g -O0 -o client tcpclient.cpp -I./libuv/output/include -L./libuv/output/lib -luv -lpthread -ldl
g++  -g -O0 -o server tcpserver.cpp -I./libuv/output/include -L./libuv/output/lib -luv -lpthread -ldl

6. 执行效果

~/work/tcpdemo$ ./client 
hello tcp client...
thread input msg ...

请输入(q退出):
hello
client: hello

server: hello

请输入(q退出):
gogogogogo  gooooooo
client: gogogogogo  gooooooo

server: gogogogogo  gooooooo

请输入(q退出):
q
client: q
93  call uv_close
93  call uv_close
发送失败 ...bad file descriptor
86  on_client_close
86  on_client_close
loop 正常退出 ...
~/work/tcpdemo$ ./server 
hello, this is tcp server...
94 -- thread_recv_data
75 connection_cb-- status=0
89 -- peer ip=127.0.0.1 : port=3879
34 alloc_cb-- suggested_size=65536
45 alloc_cb-- nread=5
60 read data = 5   data:hello
19 write_cb-- status=0
34 alloc_cb-- suggested_size=65536
45 alloc_cb-- nread=20
60 read data = 20   data:gogogogogo  gooooooo
19 write_cb-- status=0
34 alloc_cb-- suggested_size=65536
45 alloc_cb-- nread=-4095
client(0)eof

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值