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寫的服務器之間的差異,結果看來是沒多大差別的。
以上就是本博客的全文,本人限於能力,上文中難免有錯誤的地方,若讀者發現上文的錯誤,請於評論區中指出,本人看到之後會立即修改的,謝謝。