คุณมี คำถาม ที่ไม่จำเป็นต้องเปิดประเด็นหรือไม่? เข้าร่วมช่อง Gitter
หากคุณใช้ uvw และต้องการกล่าวขอบคุณหรือสนับสนุนโครงการ โปรด พิจารณาเป็นผู้สนับสนุน
คุณสามารถช่วยฉันสร้างความแตกต่างได้ ขอบคุณมากสำหรับผู้ที่สนับสนุนฉันและยังคงสนับสนุนฉันมาจนถึงทุกวันนี้
uvw เริ่มต้นจาก wrapper แบบส่วนหัวเท่านั้น ตามเหตุการณ์ ขนาดเล็กและใช้งานง่ายสำหรับ libuv ที่เขียนด้วยภาษา C++ สมัยใหม่
ในที่สุดมันก็พร้อมใช้งานในรูปแบบไลบรารีสแตติกที่คอมไพล์ได้แล้ว
แนวคิดพื้นฐานคือการรวมอินเทอร์เฟซ C-ish ของ libuv ไว้ด้านหลัง C++ API ที่สวยงาม
โปรดทราบว่า uvw ยังคงเป็นจริงกับ API ของ libuv และไม่ได้เพิ่มสิ่งใดลงในอินเทอร์เฟซ ด้วยเหตุผลเดียวกัน ผู้ใช้ไลบรารีจะต้องปฏิบัติตามกฎเดียวกันกับที่ใช้กับ libuv
ตามตัวอย่าง ควรเตรียมใช้ งานหมายเลข อ้างอิงก่อนการดำเนินการอื่นๆ และปิดเมื่อไม่ได้ใช้งานอีกต่อไป
#include <uvw.hpp>#include <memory>ฟังเป็นโมฆะ (uvw::loop &loop) {
std::shared_ptr<uvw::tcp_handle> tcp = loop.resource<uvw::tcp_handle>();
tcp->บน<uvw::listen_event>([](const uvw::listen_event &, uvw::tcp_handle &srv) {
std::shared_ptr<uvw::tcp_handle> ลูกค้า = srv.parent().resource<uvw::tcp_handle>();
ลูกค้า->บน<uvw::close_event>([ptr = srv.shared_from_this()](const uvw::close_event &, uvw::tcp_handle &) { ptr->ปิด(); });
ลูกค้า -> บน <uvw::end_event>([](const uvw::end_event &, uvw::tcp_handle &client) { client.close(); });
srv.accept(*ลูกค้า);
ลูกค้า -> อ่าน ();
-
tcp->bind("127.0.0.1", 4242);
TCP->ฟัง();
} เป็นโมฆะ conn (uvw::loop &loop) {auto tcp = loop.resource<uvw::tcp_handle>();
tcp->on<uvw::error_event>([](const uvw::error_event &, uvw::tcp_handle &) { /* จัดการข้อผิดพลาด */ });
tcp->บน<uvw::connect_event>([](const uvw::connect_event &, uvw::tcp_handle &tcp) {auto dataWrite = std::unique_ptr<char[]>(ถ่านใหม่ [2]{ 'b' , 'ค' });
tcp.write(std::move(dataWrite), 2);
tcp.ปิด();
-
tcp->เชื่อมต่อ(std::string{"127.0.0.1"}, 4242);
}int main() {วนซ้ำอัตโนมัติ = uvw::loop::get_default();listen(*loop);conn(*loop);
วนซ้ำ -> วิ่ง ();
- สาเหตุหลักที่เขียน uvw ก็คือความจริงที่ว่าไม่มี wrapper libuv ที่ถูกต้องใน C ++ นั่นคือทั้งหมดที่
เพื่อให้สามารถใช้ uvw ได้ ผู้ใช้จะต้องมีเครื่องมือทั้งระบบดังต่อไปนี้:
คอมไพเลอร์ที่มีคุณสมบัติครบถ้วนซึ่งรองรับอย่างน้อย C++17
libuv (เวอร์ชันใดขึ้นอยู่กับแท็กของ uvw ที่ใช้งานอยู่)
หากคุณใช้ meson libuv จะถูกดาวน์โหลดสำหรับคุณ
ข้อกำหนดด้านล่างนี้จำเป็นสำหรับการรวบรวมการทดสอบและการแยกเอกสารประกอบ:
CMake เวอร์ชัน 3.13 หรือใหม่กว่า
Doxygen เวอร์ชัน 1.8 หรือใหม่กว่า
โปรดทราบว่า libuv เป็นส่วนหนึ่งของการขึ้นต่อกันของโปรเจ็กต์ และ CMake อาจถูกโคลนในบางกรณี (ดูรายละเอียดเพิ่มเติมด้านล่าง)
ด้วยเหตุนี้ ผู้ใช้จึงไม่จำเป็นต้องติดตั้งเพื่อรันการทดสอบหรือเมื่อไลบรารี uvw ถูกคอมไพล์ผ่าน CMake
คุณสามารถใช้ uvw กับ meson ได้โดยเพิ่มลงในไดเร็กทอรี subprojects ในโครงการของคุณ
หากต้องการคอมไพล์ uvw จากซอร์สโดยไม่ใช้เป็นโปรเจ็กต์ย่อย ในไดเร็กทอรีซอร์ส uvw ให้รัน:
$ meson setup build
หากคุณต้องการไลบรารีแบบคงที่ ให้เพิ่ม --default-library=static
$ cd build
$ meson compile
uvw เป็นไลบรารีสองโหมด สามารถใช้ในรูปแบบส่วนหัวเท่านั้นหรือเป็นไลบรารีสแตติกที่คอมไพล์แล้ว
ส่วนต่อไปนี้จะอธิบายสิ่งที่ต้องทำในทั้งสองกรณีเพื่อเริ่ม uvw และรันในโครงการของคุณเอง
หากต้องการใช้ uvw เป็นไลบรารีส่วนหัวเท่านั้น สิ่งที่คุณต้องทำทั้งหมดคือการรวมส่วนหัว uvw.hpp หรือไฟล์ uvw/*.hpp อื่นไฟล์ใดไฟล์หนึ่ง
เป็นเรื่องของการเพิ่มบรรทัดต่อไปนี้ที่ด้านบนของไฟล์:
#รวม <uvw.hpp>
จากนั้นส่งอาร์กิวเมนต์ -I ที่เหมาะสมไปยังคอมไพเลอร์เพื่อเพิ่มไดเร็กทอรี src ให้กับเส้นทางรวม
โปรดทราบว่าผู้ใช้จะต้องตั้งค่าเส้นทางการค้นหาไดเรกทอรีรวมและไลบรารีสำหรับ libuv อย่างถูกต้องในกรณีนี้
เมื่อใช้ผ่าน CMake เป้าหมาย uvw::uvw จะถูกส่งออกเพื่อความสะดวก
หากต้องการใช้ uvw เป็นไลบรารีที่คอมไพล์ ให้ตั้งค่าตัวเลือก UVW_BUILD_LIBS ใน cmake ก่อนที่จะรวมโปรเจ็กต์
ตัวเลือกนี้จะทริกเกอร์การสร้างเป้าหมายที่ชื่อ uvw::uvw-static libuv เวอร์ชันที่ตรงกันยังถูกรวบรวมและส่งออกเป็น uv::uv-static เพื่อความสะดวก
ในกรณีที่คุณไม่ได้ใช้หรือไม่ต้องการใช้ CMake คุณยังสามารถคอมไพล์ไฟล์ .cpp ทั้งหมดและรวมไฟล์ .h ทั้งหมดเพื่อให้งานสำเร็จลุล่วงได้ ในกรณีนี้ ผู้ใช้จะต้องตั้งค่าพาธการค้นหาไดเร็กทอรีรวมและไลบรารีสำหรับ libuv อย่างถูกต้อง
เริ่มต้นด้วยแท็ก v1.12.0 ของ libuv , uvw เป็นไปตามรูปแบบการกำหนดเวอร์ชันเชิงความหมาย
ปัญหาคือ uvw เวอร์ชันใดก็ตามจำเป็นต้องติดตามเวอร์ชันของ libuv ที่ถูกผูกไว้อย่างชัดเจน
ด้วยเหตุนี้ส่วนหลังจะถูกผนวกเข้ากับเวอร์ชันของ uvw เป็นตัวอย่าง:
vU.V.W_libuv-vX.Y
โดยเฉพาะอย่างยิ่งสิ่งต่อไปนี้มีผลบังคับใช้:
UVW เป็น uvw เวอร์ชันหลัก รอง และแพทช์
XY คือเวอร์ชันของ libuv ที่จะอ้างอิง (โดยที่เวอร์ชันแพตช์ใดๆ ถูกต้อง)
กล่าวอีกนัยหนึ่ง แท็กจะมีลักษณะเช่นนี้นับจากนี้เป็นต้นไป:
v1.0.0_libuv-v1.12
Branch master ของ uvw จะเป็นสาขาที่กำลังดำเนินการซึ่งอยู่ต่อจากสาขา v1.x ของ libuv (อย่างน้อยตราบใดที่ยังคงเป็นสาขา หลัก )
เอกสารประกอบนี้อิงจาก doxygen วิธีสร้าง:
$ cd build
$ cmake ..
$ make docs
การอ้างอิง API จะถูกสร้างขึ้นในรูปแบบ HTML ภายในไดเร็กทอรี build/docs/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
ละเว้น -R uvw หากคุณต้องการทดสอบ libuv และการขึ้นต่อกันอื่นๆ
มีกฎเพียงข้อเดียวเมื่อใช้ uvw : เตรียมใช้งานทรัพยากรและยุติทรัพยากรเหล่านั้นเสมอ
ทรัพยากรส่วนใหญ่เป็นของสองตระกูล: handles และ requests
หมายเลขอ้างอิงแสดงถึงวัตถุที่มีอายุการใช้งานยาวนานซึ่งสามารถดำเนินการบางอย่างได้ในขณะที่ใช้งานอยู่
คำขอแสดงถึง (โดยทั่วไป) การดำเนินงานระยะสั้นที่ดำเนินการผ่านหมายเลขอ้างอิงหรือแบบสแตนด์อโลน
ส่วนต่อไปนี้จะอธิบายโดยย่อว่าการเริ่มต้นและยุติทรัพยากรประเภทนี้หมายความว่าอย่างไร
สำหรับรายละเอียดเพิ่มเติม โปรดดูเอกสารออนไลน์
โดยปกติการกำหนดค่าเริ่มต้นจะดำเนินการภายใต้ประทุนและสามารถส่งต่อได้ ตราบใดที่หมายเลขอ้างอิงถูกสร้างขึ้นโดยใช้ฟังก์ชันสมาชิก loop::resource
ในอีกด้านหนึ่ง ที่จับจะรักษาตัวเองให้คงอยู่จนกว่าจะมีอันหนึ่งปิดไว้อย่างชัดเจน ด้วยเหตุนี้ การใช้หน่วยความจำจะเพิ่มขึ้นหากผู้ใช้ลืมเกี่ยวกับที่จับ
ดังนั้นกฎจึง ปิดแฮนเดิลของคุณอย่างรวดเร็วเสมอ ง่ายพอๆ กับการเรียกใช้ฟังก์ชัน close member
โดยทั่วไปแล้วไม่จำเป็นต้องเริ่มต้นออบเจ็กต์คำขอ อย่างไรก็ตาม วิธีที่แนะนำในการสร้างคำขอยังคงอยู่ผ่านฟังก์ชันสมาชิก loop::resource
คำขอจะคงอยู่ต่อไปตราบเท่าที่คำขอเหล่านั้นเชื่อมโยงกับกิจกรรมที่ซ่อนอยู่ที่ยังไม่เสร็จสิ้น ซึ่งหมายความว่าผู้ใช้ไม่จำเป็นต้องละทิ้งคำขออย่างชัดเจน
ดังนั้นกฎจึงกลายเป็นเรื่องอย่างรวดเร็ว ที่จะร้องขอและลืมมันไป ง่ายพอๆ กับการเรียกใช้ฟังก์ชันสมาชิกกับฟังก์ชันเหล่านั้น
สิ่งแรกที่ต้องทำเพื่อใช้ uvw คือการสร้างลูป ในกรณีที่ค่าเริ่มต้นเพียงพอ ก็ทำได้ง่าย ๆ ดังนี้:
วนซ้ำอัตโนมัติ = uvw::loop::get_default();
โปรดทราบว่าวัตถุวนซ้ำไม่จำเป็นต้องปิดอย่างชัดเจน แม้ว่าวัตถุเหล่านั้นจะมีฟังก์ชันสมาชิก close ในกรณีที่ผู้ใช้ต้องการทำเช่นนั้นก็ตาม
สามารถเริ่มลูปได้โดยใช้ฟังก์ชันสมาชิก run การโทรทั้งสองด้านล่างนี้เทียบเท่ากัน:
วนซ้ำ -> วิ่ง (); วนซ้ำ -> วิ่ง (uvw::loop::run_mode::DEFAULT);
โหมดที่ใช้ได้คือ: DEFAULT , ONCE , NOWAIT โปรดดูเอกสารประกอบของ libuv สำหรับรายละเอียดเพิ่มเติม
ในการสร้างทรัพยากรและผูกเข้ากับลูปที่กำหนด ให้ทำดังต่อไปนี้:
auto tcp = loop->ทรัพยากร<uvw::tcp_handle>();
บรรทัดด้านบนสร้างและเตรียมใช้งานตัวจัดการ TCP จากนั้นตัวชี้ที่แชร์ไปยังทรัพยากรนั้นจะถูกส่งกลับ
ผู้ใช้ควรตรวจสอบว่าพอยน์เตอร์ได้รับการเริ่มต้นอย่างถูกต้องหรือไม่ ในกรณีที่มีข้อผิดพลาด จะไม่เป็นเช่นนั้น
นอกจากนี้ยังสามารถสร้างทรัพยากรที่ยังไม่ได้เตรียมใช้งานเพื่อเริ่มต้นในภายหลังได้ดังนี้:
auto tcp = loop->uninitialized_resource<uvw::tcp_handle>(); tcp->init();
ทรัพยากรทั้งหมดยังยอมรับข้อมูลผู้ใช้โดยพลการซึ่งจะไม่ถูกแตะต้องไม่ว่าในกรณีใด
ผู้ใช้สามารถตั้งค่าและรับข้อมูลผ่านฟังก์ชันสมาชิก data ได้ดังนี้
ทรัพยากร -> ข้อมูล (std::make_shared<int>(42)); std::shared_ptr<โมฆะ> ข้อมูล = ทรัพยากร -> ข้อมูล ();
ทรัพยากรคาดหวัง std::shared_pointer<void> และส่งคืน ดังนั้นจึงยินดีต้อนรับข้อมูลทุกประเภท
ผู้ใช้สามารถระบุประเภทอื่นที่ไม่ใช่ void ได้อย่างชัดเจนเมื่อเรียกใช้ฟังก์ชันสมาชิก data :
std::shared_ptr<int> ข้อมูล = ทรัพยากร->ข้อมูล<int>();
จำไว้จากส่วนก่อนหน้านี้ว่าตัวจัดการจะรักษาตัวเองให้คงอยู่จนกว่าจะมีการเรียกใช้ฟังก์ชันสมาชิก close
หากต้องการทราบว่าจุดจับที่ยังมีชีวิตอยู่และผูกไว้กับลูปที่กำหนดคืออะไร จึงมีฟังก์ชันสมาชิก walk อยู่ มันจะส่งคืนหมายเลขอ้างอิงพร้อมประเภทของพวกเขา ดังนั้นจึงแนะนำให้ใช้แบบ overloaded เพื่อให้สามารถดักจับความสนใจได้ทุกประเภท:
handle.parent().walk(uvw::overloaded{
[](uvw::timer_handle &h){ /* รหัสแอปพลิเคชันสำหรับตัวจับเวลาที่นี่ */ }
[](auto &&){ /* ละเว้นประเภทอื่นๆ ทั้งหมด */ }
-ฟังก์ชันนี้ยังสามารถใช้เป็นแนวทางทั่วไปได้ ตัวอย่างเช่น สามารถปิดที่จับที่ค้างอยู่ทั้งหมดได้อย่างง่ายดายดังนี้:
วนซ้ำ -> เดิน ([] (อัตโนมัติ &&h) { h.close (); });ไม่จำเป็นต้องติดตามพวกเขา
uvw เสนอแนวทางตามเหตุการณ์โดยที่ทรัพยากรเป็นตัวปล่อยเหตุการณ์ขนาดเล็กที่ผู้ฟังแนบมาด้วย
การแนบผู้ฟังเข้ากับแหล่งข้อมูลเป็นวิธีที่แนะนำในการรับการแจ้งเตือนเกี่ยวกับการดำเนินงานของพวกเขา
Listener เป็นอ็อบเจ็กต์ที่สามารถเรียกได้ประเภท 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 member เป็นวิธีการลงทะเบียน Listener ระยะยาว:
resources.on<event_type>(ผู้ฟัง)
หากต้องการทราบว่ามี Listener สำหรับประเภทที่กำหนดหรือไม่ คลาสจึงเสนอเทมเพลตฟังก์ชัน has ในทำนองเดียวกัน เทมเพลตฟังก์ชัน reset ใช้เพื่อรีเซ็ตและตัดการเชื่อมต่อ Listener ถ้ามี มี reset เวอร์ชันที่ไม่ใช่เทมเพลตเพื่อล้างตัวปล่อยโดยรวม
ทรัพยากรเกือบทั้งหมดปล่อย error_event ในกรณีที่เกิดข้อผิดพลาด
เหตุการณ์อื่นๆ ทั้งหมดเป็นเหตุการณ์เฉพาะสำหรับทรัพยากรที่กำหนดและบันทึกไว้ในการอ้างอิง API
รหัสด้านล่างแสดงวิธีสร้างเซิร์ฟเวอร์ tcp อย่างง่ายโดยใช้ uvw :
วนซ้ำอัตโนมัติ = uvw::loop::get_default();auto tcp = loop->ทรัพยากร<uvw::tcp_handle>();
tcp->on<uvw::error_event>([](const uvw::error_event &, uvw::tcp_handle &) { /* มีบางอย่างผิดพลาด */ });
tcp->บน<uvw::listen_event>([](const uvw::listen_event &, uvw::tcp_handle &srv) {
std::shared_ptr<uvw::tcp_handle> ลูกค้า = srv.parent().resource<uvw::tcp_handle>();
ลูกค้า -> บน <uvw::end_event>([](const uvw::end_event &, uvw::tcp_handle &client) { client.close(); });
ลูกค้า->บน<uvw::data_event>([](const uvw::data_event &, uvw::tcp_handle &) { /* ข้อมูลที่ได้รับ */ });
srv.accept(*ลูกค้า);
ลูกค้า -> อ่าน ();
-
tcp->bind("127.0.0.1", 4242);
TCP->ฟัง(); โปรดทราบว่า uvw::tcp_handle รองรับ IPv6 นอกกรอบแล้ว
การอ้างอิง API เป็นเอกสารที่แนะนำสำหรับรายละเอียดเพิ่มเติมเกี่ยวกับทรัพยากรและวิธีการ
ในกรณีที่ผู้ใช้จำเป็นต้องใช้ฟังก์ชันการทำงานที่ยังไม่ครอบคลุมโดย uvw หรือหากพวกเขาต้องการรับโครงสร้างข้อมูลพื้นฐานตามที่กำหนดโดย libuv ด้วยเหตุผลอื่น คลาสเกือบทั้งหมดใน uvw จะให้การเข้าถึงโดยตรง
โปรดทราบว่าไม่ควรใช้ฟังก์ชันนี้โดยตรง เว้นแต่ผู้ใช้จะทราบแน่ชัดว่ากำลังทำอะไรอยู่และมีความเสี่ยงอะไรบ้าง การทำ Raw เป็นสิ่งที่อันตราย โดยหลักแล้วเป็นเพราะการจัดการอายุการใช้งานของลูป หมายเลขอ้างอิง หรือคำขอได้รับการควบคุมอย่างสมบูรณ์โดยไลบรารี และการแก้ไขรอบนั้นอาจทำให้สิ่งต่างๆ เสียหายได้อย่างรวดเร็ว
ดังที่กล่าวไว้ การทำ Raw เป็นเรื่องของการใช้ฟังก์ชัน raw Member:
วนซ้ำอัตโนมัติ = 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 : ไลบรารีเครือข่ายที่มีคอลเลกชันไคลเอนต์ (HTTP/Modbus/SunSpec) ซึ่งรวมถึงการดำเนินการค้นพบ เช่น dns-sd/mdns
คุณสามารถเพิ่มเครื่องมือของคุณลงในรายการได้ตามต้องการ
หากคุณต้องการมีส่วนร่วม โปรดส่งแพตช์เป็นการร้องขอดึงกับสาขาหลัก
ตรวจสอบรายชื่อผู้ร่วมให้ข้อมูลเพื่อดูว่าใครได้เข้าร่วมแล้วบ้าง
รหัสและเอกสารประกอบ ลิขสิทธิ์ (c) 2016-2024 Michele Caini
โลโก้ลิขสิทธิ์ (c) 2018-2021 Richard Caseres
รหัสและเอกสารประกอบที่เผยแพร่ภายใต้ใบอนุญาต MIT
โลโก้เผยแพร่ภายใต้ CC BY-SA 4.0
หากคุณต้องการสนับสนุนโครงการนี้ คุณสามารถเสนอเอสเปรสโซให้ฉันได้
หากคุณพบว่ายังไม่เพียงพอ โปรดช่วยฉันตามที่คุณต้องการ