libuv在windows的簡單使用

libuv的官網:http://libuv.org/

關於libuv的介紹網上都有,大家可以去搜一下。

而如何在windows上安裝libuv,我這邊是使用vcpkg安裝的,學習的時候極力推薦使用這種方式安裝,關於vcpkg的使用可以參考這篇博客:https://blog.csdn.net/cjmqas/article/details/79282847

libuv底層在windows使用的是IOCP機制,IOCP是個神器,但是學起來是真的難,學好了,用起來也就容易了,但是學好談何容易。關於IOCP的介紹可以看這篇博客:https://blog.csdn.net/piggyxp/article/details/6922277

綜上所述,我就想在windows下試試使用libuv編寫一個簡單的服務器軟件來測試測試libuv的性能,代碼如下:

#include <iostream>
#include <uv.h>
#include <uv/errno.h>
#include <spdlog/spdlog.h>

// 客戶端關閉socket時回調函數
void OnClose(uv_handle_t* handle)
{
    free(handle);
}

// 接收socket數據前分配緩衝區函數回調函數
void ClientAlloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf)
{
    buf->base = reinterpret_cast<char*>(malloc(suggested_size));
    buf->len = suggested_size;
}

// 
void ClientRead(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
{
    sockaddr_in streamAddr;
    int nameLen = sizeof(sockaddr_in);

    // 當數據大於0時爲接收數據,小於0時是客戶端斷開連接
    if (nread > 0)
    {
        char szIpAdderss[17] = { 0, };
        uv_tcp_getpeername(reinterpret_cast<uv_tcp_t*>(stream), reinterpret_cast<sockaddr*>(&streamAddr), &nameLen);
        uv_ip4_name(&streamAddr, szIpAdderss, 17);
        
        buf->base[nread] = '\0';
        spdlog::info("ip {0}, port {1}, data length {2}, data {3}", szIpAdderss, ntohs(streamAddr.sin_port), nread, buf->base);
    }
    else if (nread < 0)
    {
        if (nread != UV_EOF)
        {
            spdlog::error("ClientRead fail, {0}", uv_strerror(nread));
        }
        else
        {
            sockaddr_in streamAddr;
            int nameLen = sizeof(sockaddr_in);
            char szIpAdderss[17] = { 0, };
            uv_tcp_getpeername(reinterpret_cast<uv_tcp_t*>(stream), reinterpret_cast<sockaddr*>(&streamAddr), &nameLen);
            uv_ip4_name(&streamAddr, szIpAdderss, 17);

            spdlog::info("ip {0}, port {1} disconnect", szIpAdderss, ntohs(streamAddr.sin_port));
            // 註冊斷開連接回調函數
            uv_close(reinterpret_cast<uv_handle_t*>(stream), OnClose);
        }
    }

    free(buf->base);
}

// 監聽回調函數
void OnConnect(uv_stream_t* server, int status)
{
    if (status == -1)
    {
        return;
    }

    // 分配客戶端對象
    uv_tcp_t *pClient = reinterpret_cast<uv_tcp_t*>(malloc(sizeof(uv_tcp_t)));

    // 爲客戶端對象綁定libuv循環
    int retValue = uv_tcp_init(uv_default_loop(), pClient);;
    if (retValue != 0)
    {
        spdlog::error("uv_tcp_init fail, {0}", uv_strerror(retValue));
        uv_close(reinterpret_cast<uv_handle_t*>(pClient), OnClose);
    }
    else
    {
        // 接收客戶端連接
        int retValue = uv_accept(server, reinterpret_cast<uv_stream_t*>(pClient));
        if (retValue != 0)
        {
            spdlog::error("uv_accept fail, {0}", uv_strerror(retValue));
            uv_close(reinterpret_cast<uv_handle_t*>(pClient), OnClose);
        }
        else
        {
            // 綁定客戶端讀分配內存和讀回調函數
            uv_read_start(reinterpret_cast<uv_stream_t*>(pClient), ClientAlloc, ClientRead);
        }
    } 
}

int main()
{
    // 初始化libuv循環
    auto pLoop = uv_default_loop();
    
    // 初始化libuv服務器
    uv_tcp_t server;
    uv_tcp_init(pLoop, &server);

    sockaddr_in bindAddr;
    int retValue = uv_ip4_addr("0.0.0.0", 43000, &bindAddr);
    if (retValue != 0)
    {
        spdlog::error("uv_ip4_addr fail, {0}", uv_strerror(retValue));
        return -1;
    }

    // 綁定地址和端口
    retValue = uv_tcp_bind(&server, reinterpret_cast<sockaddr*>(&bindAddr), 0);
    if (retValue != 0)
    {
        spdlog::error("uv_tcp_bind fail, {0}", uv_strerror(retValue));
        return -2;
    }

    // 開始監聽
    retValue = uv_listen(reinterpret_cast<uv_stream_t*>(&server), 128, OnConnect);
    if (retValue != 0)
    {
        spdlog::error("uv_listen fail, {0}", uv_strerror(retValue));
        return -3;
    }

    // 開啓libuv循環
    return uv_run(pLoop, UV_RUN_DEFAULT);
}

測試的時候使用的是上面介紹IOCP的博客所提供的客戶端,目的是對比使用IOCP寫的服務器和使用libuv寫的服務器之間的差異,結果看來是沒多大差別的。

以上就是本博客的全文,本人限於能力,上文中難免有錯誤的地方,若讀者發現上文的錯誤,請於評論區中指出,本人看到之後會立即修改的,謝謝。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章