คุณมี คำถาม ที่ไม่จำเป็นต้องเปิดประเด็นหรือไม่? เข้าร่วมช่อง 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
หากคุณต้องการสนับสนุนโครงการนี้ คุณสามารถเสนอเอสเปรสโซให้ฉันได้
หากคุณพบว่ายังไม่เพียงพอ โปรดช่วยฉันตามที่คุณต้องการ