문제를 열 필요가 없는 질문이 있습니까? 지터 채널에 가입하세요.
uvw
사용하고 프로젝트에 감사를 표하거나 지원하고 싶다면 스폰서가 되는 것을 고려해 보세요.
당신은 내가 변화를 만들 수 있도록 도와줄 수 있습니다. 저를 지지해주시고 지금도 응원해주시는 분들께 진심으로 감사드립니다.
uvw
헤더 전용, 이벤트 기반, 작고 사용하기 쉬운 최신 C++로 작성된 libuv
용 래퍼로 시작되었습니다.
이제 마침내 컴파일 가능한 정적 라이브러리로도 사용할 수 있게 되었습니다.
기본 아이디어는 우아한 C++ API 뒤에 libuv
의 C-ish 인터페이스를 래핑하는 것입니다.
uvw
는 libuv
의 API에 충실하며 인터페이스에 아무것도 추가하지 않습니다. 같은 이유로 라이브러리 사용자는 libuv
와 함께 사용되는 것과 동일한 규칙을 따라야 합니다.
예를 들어, 핸들은 다른 작업보다 먼저 초기화되어야 하며 더 이상 사용되지 않으면 닫혀야 합니다.
#include <uvw.hpp>#include <memory>void listening(uvw::loop &loop) { std::shared_ptr<uvw::tcp_handle> tcp = loop.resource<uvw::tcp_handle>(); tcp->on<uvw::listen_event>([](const uvw::listen_event &, uvw::tcp_handle &srv) { std::shared_ptr<uvw::tcp_handle> client = srv.parent().resource<uvw::tcp_handle>(); 클라이언트->온<uvw::close_event>([ptr = srv.shared_from_this()](const uvw::close_event &, uvw::tcp_handle &) { ptr->close(); }); client->on<uvw::end_event>([](const uvw::end_event &, uvw::tcp_handle &client) { client.close(); }); srv.accept(*클라이언트); 클라이언트->읽기(); }); tcp->바인드("127.0.0.1", 4242); TCP->듣기(); }void conn(uvw::loop &loop) {auto tcp = loop.resource<uvw::tcp_handle>(); tcp->on<uvw::error_event>([](const uvw::error_event &, uvw::tcp_handle &) { /* 오류 처리 */ }); tcp->on<uvw::connect_event>([](const uvw::connect_event &, uvw::tcp_handle &tcp) {auto dataWrite = std::unique_ptr<char[]>(new char[2]{ 'b' , 'c' }); tcp.write(std::move(dataWrite), 2); tcp.close(); }); tcp->connect(std::string{"127.0.0.1"}, 4242); }int main() {auto loop = uvw::loop::get_default();listen(*loop);conn(*loop); 루프->실행(); }
uvw
작성된 주된 이유는 C++에 유효한 libuv
래퍼가 존재하지 않는다는 사실입니다. 그게 다야.
uvw
사용하려면 사용자는 다음과 같은 시스템 전체 도구를 제공해야 합니다.
최소한 C++17을 지원하는 모든 기능을 갖춘 컴파일러입니다.
libuv
(사용 중인 uvw
태그에 따라 버전이 다름)
meson
사용하면 libuv가 다운로드됩니다.
테스트를 컴파일하고 문서를 추출하려면 아래 요구 사항이 필수입니다.
CMake 버전 3.13 이상.
Doxygen 버전 1.8 이상.
libuv
프로젝트 종속성의 일부이며 경우에 따라 CMake
에 의해 복제될 수 있습니다(자세한 내용은 아래 참조).
따라서 사용자는 테스트를 실행하거나 CMake
통해 uvw
라이브러리를 컴파일할 때 이를 설치할 필요가 없습니다.
프로젝트의 subprojects
디렉터리에 간단히 추가하여 meson과 함께 uvw
사용할 수 있습니다.
uvw
하위 프로젝트로 사용하지 않고 소스에서 컴파일하려면 uvw
소스 디렉터리에서 다음을 실행합니다.
$ meson setup build
정적 라이브러리를 원하면 --default-library=static
추가하세요.
$ cd build
$ meson compile
uvw
이중 모드 라이브러리입니다. 헤더 전용 형식이나 컴파일된 정적 라이브러리로 사용할 수 있습니다.
다음 섹션에서는 두 경우 모두 자신의 프로젝트에서 uvw
시작하고 실행하기 위해 수행할 작업을 설명합니다.
uvw
헤더 전용 라이브러리로 사용하려면 uvw.hpp
헤더 또는 다른 uvw/*.hpp
파일 중 하나를 포함하기만 하면 됩니다.
파일 상단에 다음 줄을 추가하면 됩니다.
#include <uvw.hpp>
그런 다음 적절한 -I
인수를 컴파일러에 전달하여 src
디렉터리를 포함 경로에 추가합니다.
이 경우 사용자는 libuv
에 대한 포함 디렉터리와 라이브러리 검색 경로를 올바르게 설정해야 합니다.
CMake
통해 사용하면 편의를 위해 uvw::uvw
대상이 내보내집니다.
uvw
컴파일된 라이브러리로 사용하려면 프로젝트를 포함하기 전에 cmake에서 UVW_BUILD_LIBS
옵션을 설정하세요.
이 옵션은 uvw::uvw-static
이라는 대상 생성을 트리거합니다. 편의를 위해 일치하는 libuv
버전도 uv::uv-static
으로 컴파일되고 내보내집니다.
CMake
사용하지 않거나 사용하고 싶지 않은 경우에도 모든 .cpp
파일을 컴파일하고 모든 .h
파일을 포함하여 작업을 완료할 수 있습니다. 이 경우 사용자는 libuv
에 대한 포함 디렉터리와 라이브러리 검색 경로를 올바르게 설정해야 합니다.
libuv
태그 v1.12.0 부터 uvw
의미 체계 버전 관리 체계를 따릅니다.
문제는 모든 uvw
버전이 바인딩된 libuv
버전을 명시적으로 추적해야 한다는 것입니다.
따라서 후자는 uvw
버전에 추가됩니다. 예를 들면:
vU.V.W_libuv-vX.Y
특히 다음 사항이 적용됩니다.
UVW는 uvw
의 메이저, 마이너 및 패치 버전입니다.
XY는 참조할 libuv
버전입니다(모든 패치 버전이 유효함).
즉, 이제부터 태그는 다음과 같이 표시됩니다.
v1.0.0_libuv-v1.12
uvw
의 브랜치 master
libuv
의 브랜치 v1.x를 따르는 작업 중인 브랜치입니다(적어도 마스터 브랜치가 유지되는 한).
문서는 doxygen
기반으로 합니다. 빌드하려면 다음을 수행하세요.
$ cd build
$ cmake ..
$ make docs
API 참조는 build/docs/html
디렉토리 내에 HTML 형식으로 생성됩니다.
즐겨 사용하는 브라우저로 탐색하려면:
$ cd build
$ your_favorite_browser docs/html/index.html
최신 릴리스, 즉 마지막 안정 태그에 대해 동일한 버전을 온라인에서도 사용할 수 있습니다.
이 문서는 대부분 명백한 이유로 공식 libuv API 문서에서 영감을 받았습니다.
테스트를 컴파일하고 실행하려면 uvw
libuv
및 googletest
필요합니다.
CMake
다른 것을 컴파일하기 전에 두 라이브러리를 모두 다운로드하고 컴파일합니다.
테스트를 빌드하려면 다음 안내를 따르세요.
$ cd build
$ cmake .. -DUVW_BUILD_TESTING=ON
$ make
$ ctest -j4 -R uvw
libuv
및 기타 종속성도 테스트하려면 -R uvw
생략하세요.
uvw
사용할 때 규칙은 단 하나입니다: 항상 리소스를 초기화하고 종료합니다.
리소스는 주로 핸들 과 요청이라는 두 가지 계열에 속합니다.
핸들은 활성 상태에서 특정 작업을 수행할 수 있는 수명이 긴 개체를 나타냅니다.
요청은 (일반적으로) 핸들이나 독립 실행형을 통해 수행되는 단기 작업을 나타냅니다.
다음 섹션에서는 이러한 종류의 리소스를 초기화하고 종료하는 것이 무엇을 의미하는지 간략하게 설명합니다.
자세한 내용은 온라인 설명서를 참조하세요.
초기화는 일반적으로 내부적으로 수행되며 loop::resource
멤버 함수를 사용하여 핸들이 생성되는 한 통과될 수도 있습니다.
반면에 핸들은 명시적으로 닫힐 때까지 자체적으로 활성 상태를 유지합니다. 따라서 사용자가 단순히 핸들을 잊어버리면 메모리 사용량이 늘어납니다.
따라서 규칙은 항상 핸들을 닫는 것이 됩니다. close
멤버 함수를 호출하는 것만큼 간단합니다.
일반적으로 요청 객체 초기화는 필요하지 않습니다. 어쨌든 요청을 생성하는 권장 방법은 여전히 loop::resource
멤버 함수를 이용하는 것입니다.
요청은 완료되지 않은 기본 활동에 묶여 있는 한 계속 살아 있습니다. 이는 사용자가 명시적으로 요청을 삭제할 필요가 없음을 의미합니다.
그러므로 요청을 하고 잊어버리는 것이 금세 규칙이 됩니다. 멤버 함수를 호출하는 것만큼 간단합니다.
uvw
사용하기 위해 가장 먼저 해야 할 일은 루프를 만드는 것입니다. 기본 설정으로 충분할 경우 다음과 같이 쉽습니다.
자동 루프 = uvw::loop::get_default();
루프 개체는 사용자가 원하는 경우 close
멤버 함수를 제공하더라도 명시적으로 닫을 필요가 없습니다.
루프는 run
멤버 함수를 사용하여 시작할 수 있습니다. 아래 두 호출은 동일합니다.
루프->실행(); 루프->실행(uvw::loop::run_mode::DEFAULT);
사용 가능한 모드는 DEFAULT
, ONCE
, NOWAIT
입니다. 자세한 내용은 libuv
문서를 참고하세요.
리소스를 생성하고 이를 지정된 루프에 바인딩하려면 다음을 수행하세요.
자동 tcp = loop->resource<uvw::tcp_handle>();
위의 줄은 tcp 핸들을 생성하고 초기화한 다음 해당 리소스에 대한 공유 포인터가 반환됩니다.
사용자는 포인터가 올바르게 초기화되었는지 확인해야 합니다. 오류가 있는 경우 초기화되지 않습니다.
다음과 같이 나중에 초기화하기 위해 초기화되지 않은 리소스를 생성할 수도 있습니다.
자동 tcp = loop->uninitialized_resource<uvw::tcp_handle>(); TCP->초기화();
모든 리소스는 어떠한 경우에도 건드리지 않는 임의의 사용자 데이터도 허용합니다.
사용자는 다음과 같이 data
멤버 함수를 통해 이를 설정하고 가져올 수 있습니다.
자원->데이터(std::make_shared<int>(42)); std::shared_ptr<void> data = 자원->데이터();
리소스는 std::shared_pointer<void>
기대하고 이를 반환하므로 모든 종류의 데이터를 환영합니다.
사용자는 data
멤버 함수를 호출할 때 void
이외의 유형을 명시적으로 지정할 수 있습니다.
std::shared_ptr<int> 데이터 = 자원->데이터<int>();
이전 섹션에서 핸들은 close
멤버 함수를 호출할 때까지 자체적으로 활성 상태를 유지한다는 점을 기억하세요.
아직 살아 있고 주어진 루프에 바인딩된 핸들이 무엇인지 알기 위해 walk
멤버 함수가 있습니다. 해당 유형의 핸들을 반환합니다. 따라서 모든 유형의 관심을 차단하려면 overloaded
를 사용하는 것이 좋습니다.
handler.parent().walk(uvw::오버로드{ [](uvw::timer_handle &h){ /* 여기에 타이머용 애플리케이션 코드 */ }, [](auto &&){ /* 다른 모든 유형 무시 */ } });
이 기능은 완전히 일반적인 접근 방식에도 사용할 수 있습니다. 예를 들어, 다음과 같이 보류 중인 모든 핸들을 쉽게 닫을 수 있습니다.
loop->walk([](auto &&h){ h.close(); });
그들을 추적할 필요가 없습니다.
uvw
리소스가 리스너가 연결된 작은 이벤트 발생기인 이벤트 기반 접근 방식을 제공합니다.
리소스에 리스너를 연결하는 것은 해당 작업에 대한 알림을 받는 데 권장되는 방법입니다.
리스너는 void(event_type &, resource_type &)
유형의 호출 가능한 객체입니다. 여기서:
event_type
은 설계된 이벤트의 유형입니다.
resource_type
은 이벤트를 시작한 리소스의 유형입니다.
이는 다음 함수 유형이 모두 유효함을 의미합니다.
void(event_type &, resource_type &)
void(const event_type &, resource_type &)
void(event_type &, const resource_type &)
void(const event_type &, const resource_type &)
이벤트가 게시될 때마다 리소스가 인수로 전달되므로 리소스에 대한 참조를 유지할 필요가 없습니다.
on
멤버 함수는 장기 실행 리스너를 등록하는 방법입니다.
resources.on<event_type>(리스너)
특정 유형에 대한 리스너가 존재하는지 확인하기 위해 클래스는 has
함수 템플릿을 제공합니다. 마찬가지로, reset
함수 템플릿은 수신기를 재설정하고 연결을 끊는 데 사용됩니다(있는 경우). 이미터 전체를 지우기 위한 비템플릿 버전의 reset
도 존재합니다.
오류 발생 시 거의 모든 리소스에서 error_event
발생합니다.
다른 모든 이벤트는 특정 리소스에 특정하며 API 참조에 문서화되어 있습니다.
아래 코드는 uvw
사용하여 간단한 TCP 서버를 만드는 방법을 보여줍니다.
자동 루프 = uvw::loop::get_default();auto tcp = loop->resource<uvw::tcp_handle>(); tcp->on<uvw::error_event>([](const uvw::error_event &, uvw::tcp_handle &) { /* 문제가 발생했습니다 */ }); tcp->on<uvw::listen_event>([](const uvw::listen_event &, uvw::tcp_handle &srv) { std::shared_ptr<uvw::tcp_handle> client = srv.parent().resource<uvw::tcp_handle>(); client->on<uvw::end_event>([](const uvw::end_event &, uvw::tcp_handle &client) { client.close(); }); client->on<uvw::data_event>([](const uvw::data_event &, uvw::tcp_handle &) { /* 데이터 수신 */ }); srv.accept(*클라이언트); 클라이언트->읽기(); }); tcp->바인드("127.0.0.1", 4242); TCP->듣기();
또한 uvw::tcp_handle
이미 기본적으로 IPv6를 지원합니다.
API 참조는 리소스 및 해당 메서드에 대한 자세한 내용을 제공하는 권장 문서입니다.
사용자가 아직 uvw
로 래핑되지 않은 기능을 사용해야 하거나 다른 이유로 libuv
로 정의된 기본 데이터 구조를 가져오려는 경우 uvw
의 거의 모든 클래스가 해당 기능에 대한 직접 액세스를 제공합니다.
사용자가 자신이 수행하는 작업과 위험이 무엇인지 정확히 알지 않는 한 이 기능을 직접 사용해서는 안 됩니다. 원시로 가는 것은 위험합니다. 주로 루프, 핸들 또는 요청의 수명 관리가 라이브러리에 의해 완전히 제어되고 이를 해결하기 위해 작업하면 문제가 빨리 중단될 수 있기 때문입니다.
즉, 원시로 가는 것은 raw
멤버 함수를 사용하는 문제입니다.
자동 루프 = uvw::loop::get_default();auto tcp = loop->resource<uvw::tcp_handle>();uv_loop_t *raw = loop->raw();uv_tcp_t *handle = tcp->raw() ;
위험은 본인 부담으로 진행하되, 버그가 있을 경우 어떠한 지원도 기대하지 마세요.
uvw
기반으로 구축된 추가 도구 및 라이브러리에 관심이 있으십니까? 그러면 다음이 유용할 수 있습니다.
uvw_net
: dns-sd/mdns와 같은 검색 구현도 포함하는 클라이언트 컬렉션(HTTP/Modbus/SunSpec)이 있는 네트워킹 라이브러리입니다.
원하는 경우 도구를 목록에 자유롭게 추가하세요.
기여하고 싶다면 브랜치 마스터에 대한 풀 요청으로 패치를 보내주세요.
지금까지 누가 참여했는지 알아보려면 기여자 목록을 확인하세요.
코드 및 문서 저작권 (c) 2016-2024 Michele Caini.
로고 저작권 (c) 2018-2021 Richard Caseres.
MIT 라이센스에 따라 공개된 코드 및 문서입니다.
CC BY-SA 4.0에 따라 공개된 로고.
이 프로젝트를 지원하고 싶다면 나에게 에스프레소를 제공해 주세요.
만약 그것이 충분하지 않다고 생각한다면, 당신이 선호하는 방식으로 저를 도와주세요.