هل لديك سؤال لا يتطلب منك فتح موضوع؟ انضم إلى قناة جيتر.
إذا كنت تستخدم uvw وتريد أن تقول شكرًا أو تدعم المشروع، فيرجى التفكير في أن تصبح راعيًا .
يمكنك مساعدتي في إحداث الفرق. شكرا جزيلا لأولئك الذين دعموني وما زالوا يدعمونني اليوم.
بدأ uvw كغلاف صغير الحجم وسهل الاستخدام لـ libuv مكتوب بلغة C++ الحديثة، وهو عبارة عن رأسية فقط، وقائم على الأحداث.
والآن أصبح متاحًا أخيرًا أيضًا كمكتبة ثابتة قابلة للتجميع.
الفكرة الأساسية هي تغليف واجهة C-ish الخاصة بـ libuv خلف واجهة برمجة تطبيقات C++ الرائعة.
لاحظ أن uvw يظل متوافقًا مع واجهة برمجة التطبيقات الخاصة بـ libuv ولا يضيف أي شيء إلى واجهته. لنفس الأسباب، يجب على مستخدمي المكتبة اتباع نفس القواعد المستخدمة مع libuv .
على سبيل المثال، يجب تهيئة المقبض قبل أي عملية أخرى وإغلاقه بمجرد عدم استخدامه.
#تتضمن <uvw.hpp>#تتضمن <الذاكرة>استماع باطل(uvw::loop &loop) {
std::shared_ptr<uvw::tcp_handle> tcp = حلقة.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>();
client->on<uvw::إغلاق_event>([ptr = srv.shared_from_this()](const uvw::إغلاق_event &, uvw::tcp_handle &) { ptr->إغلاق(); });
Client->on<uvw::end_event>([](const uvw::end_event &, uvw::tcp_handle &client) { client.إغلاق(); });
srv.accept(*client);
العميل->قراءة();
});
tcp->bind("127.0.0.1", 4242);
tcp->listen();
}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' ، 'ج' });
tcp.write(std::move(dataWrite), 2);
tcp.Close();
});
tcp->connect(std::string{"127.0.0.1"}, 4242);
}int main() {حلقة تلقائية = uvw::loop::get_default();listen(*loop);conn(*loop);
حلقة->تشغيل();
} السبب الرئيسي وراء كتابة uvw هو عدم وجود غلاف libuv صالح في لغة C++. هذا كل شيء.
لتتمكن من استخدام uvw ، يجب على المستخدمين توفير الأدوات التالية على مستوى النظام:
مترجم كامل المواصفات يدعم C++ 17 على الأقل.
libuv (أي إصدار يعتمد على علامة uvw المستخدمة)
إذا كنت تستخدم meson ، فسيتم تنزيل libuv لك
المتطلبات أدناه إلزامية لتجميع الاختبارات واستخراج الوثائق:
الإصدار 3.13 من CMake أو الأحدث.
دوكسيجين الإصدار 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
سيكون الفرع 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 : قم دائمًا بتهيئة الموارد وإنهائها.
تنتمي الموارد بشكل أساسي إلى عائلتين: المقابض والطلبات .
تمثل المقابض كائنات طويلة العمر قادرة على تنفيذ عمليات معينة أثناء نشاطها.
تمثل الطلبات (عادةً) عمليات قصيرة العمر يتم إجراؤها إما عبر معالج أو بشكل مستقل.
ستشرح الأقسام التالية باختصار ما يعنيه تهيئة هذه الأنواع من الموارد وإنهائها.
لمزيد من التفاصيل، يرجى الرجوع إلى الوثائق عبر الإنترنت.
عادةً ما يتم إجراء التهيئة أسفل الغطاء ويمكن حتى تمريرها، بقدر ما يتم إنشاء المقابض باستخدام وظيفة عضو loop::resource .
على الجانب الآخر، تحافظ المقابض على نفسها حتى يتم إغلاقها بشكل صريح. ولهذا السبب، سيزداد استخدام الذاكرة إذا نسي المستخدمون ببساطة أمر المقبض.
ولذلك فإن القاعدة سرعان ما تصبح قريبة من مقابضك دائمًا . الأمر بسيط مثل استدعاء وظيفة العضو close عليهم.
عادة، لا يلزم تهيئة كائن الطلب. على أية حال، الطريقة الموصى بها لإنشاء طلب هي من خلال وظيفة عضو loop::resource .
ستبقى الطلبات على قيد الحياة طالما أنها مرتبطة بالأنشطة الأساسية غير المكتملة. وهذا يعني أنه لا يتعين على المستخدمين تجاهل الطلب بشكل صريح.
ولذلك فإن القاعدة سرعان ما تصبح لا تتردد في تقديم طلب ونسيانه . الأمر بسيط مثل استدعاء وظيفة العضو عليهم.
أول شيء يجب فعله لاستخدام uvw هو إنشاء حلقة. في حالة ما إذا كان الخيار الافتراضي كافيًا، فمن السهل القيام بذلك:
حلقة تلقائية = uvw::loop::get_default();
لاحظ أن كائنات الحلقة لا تتطلب أن يتم إغلاقها بشكل صريح، حتى لو كانت توفر وظيفة العضو close في حالة رغبة المستخدم في القيام بذلك.
يمكن بدء الحلقات باستخدام وظيفة عضو run . النداءتان أدناه متكافئتان:
حلقة->تشغيل(); حلقة->run(uvw::loop::run_mode::DEFAULT);
الأوضاع المتاحة هي: DEFAULT ، ONCE ، NOWAIT . يرجى الرجوع إلى وثائق libuv لمزيد من التفاصيل.
من أجل إنشاء مورد وربطه بالحلقة المحددة، فقط قم بما يلي:
auto tcp = حلقة->resource<uvw::tcp_handle>();
يقوم السطر أعلاه بإنشاء مقبض TCP وتهيئته، ثم يتم إرجاع مؤشر مشترك لذلك المورد.
يجب على المستخدمين التحقق مما إذا تم تهيئة المؤشرات بشكل صحيح: في حالة وجود أخطاء، لن يتم ذلك.
من الممكن أيضًا إنشاء موارد غير مهيأة للبدء لاحقًا على النحو التالي:
auto tcp = حلقة->unitialized_resource<uvw::tcp_handle>(); tcp->init();
تقبل جميع الموارد أيضًا بيانات المستخدم العشوائية التي لن يتم المساس بها بأي حال من الأحوال.
يمكن للمستخدمين ضبطها والحصول عليها من خلال وظيفة عضو data كما يلي:
الموارد->البيانات(std::make_shared<int>(42)); std::shared_ptr<void> data = Resources->data();
تتوقع الموارد std::shared_pointer<void> وتعيده، لذلك نرحب بأي نوع من البيانات.
يمكن للمستخدمين تحديد نوع آخر غير void بشكل صريح عند استدعاء وظيفة عضو data :
std::shared_ptr<int> data = Resources->data<int>();
تذكر من القسم السابق أن المقبض سيبقى على قيد الحياة حتى يتم استدعاء وظيفة العضو close عليه.
لمعرفة ما هي المقابض التي لا تزال على قيد الحياة ومرتبطة بحلقة معينة، توجد وظيفة عضو walk . تقوم بإرجاع المقابض بأنواعها. لذلك، يوصى باستخدام overloaded لتتمكن من اعتراض جميع أنواع الاهتمام:
Handle.parent().walk(uvw::overloaded{
[](uvw::timer_handle &h){ /* رمز تطبيق المؤقتات هنا */ },
[](تلقائي &&){ /* تجاهل كافة الأنواع الأخرى */ }
});يمكن أيضًا استخدام هذه الوظيفة لنهج عام تمامًا. على سبيل المثال، يمكن إغلاق جميع المقابض المعلقة بسهولة كما يلي:
حلقة->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 member هي الطريقة التي يمكن اتباعها لتسجيل المستمعين الدائمين:
الموارد.على<event_type>(المستمع)
لمعرفة ما إذا كان هناك مستمع لنوع معين، يقدم الفصل قالب has . وبالمثل، يتم استخدام قالب وظيفة reset لإعادة ضبط المستمعين وبالتالي فصلهم، إن وجد. يوجد أيضًا إصدار غير قالب reset لمسح الباعث ككل.
تصدر جميع الموارد تقريبًا error_event في حالة حدوث أخطاء.
جميع الأحداث الأخرى خاصة بالمورد المحدد وموثقة في مرجع واجهة برمجة التطبيقات.
يوضح الكود أدناه كيفية إنشاء خادم TCP بسيط باستخدام uvw :
حلقة تلقائية = uvw::loop::get_default();auto tcp = حلقة->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.إغلاق(); });
Client->on<uvw::data_event>([](const uvw::data_event &, uvw::tcp_handle &) { /* البيانات المستلمة */ });
srv.accept(*client);
العميل->قراءة();
});
tcp->bind("127.0.0.1", 4242);
tcp->listen(); لاحظ أيضًا أن uvw::tcp_handle يدعم بالفعل IPv6 الجاهز.
مرجع واجهة برمجة التطبيقات (API) هو الوثائق الموصى بها للحصول على مزيد من التفاصيل حول الموارد وطرقها.
في حالة حاجة المستخدمين إلى استخدام وظائف لم يتم تغليفها بعد بواسطة uvw أو إذا كانوا يريدون الحصول على بنيات البيانات الأساسية كما حددها libuv لبعض الأسباب الأخرى، فإن جميع الفئات في uvw تقريبًا تمنح الوصول المباشر إليها.
يرجى ملاحظة أنه لا ينبغي استخدام هذه الوظائف بشكل مباشر إلا إذا كان المستخدمون يعرفون بالضبط ما يفعلونه وما هي المخاطر. يعد الانتقال إلى الوضع الخام أمرًا خطيرًا، ويرجع ذلك أساسًا إلى أن الإدارة مدى الحياة للحلقة أو المقبض أو الطلب يتم التحكم فيها بالكامل بواسطة المكتبة ويمكن أن يؤدي العمل حولها إلى إتلاف الأشياء بسرعة.
ومع ذلك، فإن التحول إلى الخام هو مسألة استخدام وظائف الأعضاء raw :
حلقة تلقائية = uvw::loop::get_default();auto tcp = حلقة->مورد<uvw::tcp_handle>();uv_loop_t *raw = حلقة->raw();uv_tcp_t *handle = tcp->raw() ;
اتبع الطريق الصحيح على مسؤوليتك الخاصة، ولكن لا تتوقع أي دعم في حالة وجود أخطاء.
هل أنت مهتم بالأدوات والمكتبات الإضافية التي تعتمد على uvw ؟ قد تجد ما يلي مفيدًا إذن:
uvw_net : مكتبة شبكات بها مجموعة من العملاء (HTTP/Modbus/SunSpec) تتضمن أيضًا تطبيقات الاكتشاف مثل dns-sd/mdns.
لا تتردد في إضافة أداتك إلى القائمة إذا أردت.
إذا كنت ترغب في المساهمة، يرجى إرسال التصحيحات كطلبات سحب ضد مدير الفرع.
تحقق من قائمة المساهمين لمعرفة من شارك حتى الآن.
الكود والوثائق حقوق الطبع والنشر (ج) 2016-2024 ميشيل كايني.
حقوق الطبع والنشر للشعار (ج) 2018-2021 لريتشارد كاسيريس.
تم إصدار الكود والوثائق بموجب ترخيص MIT.
تم إصدار الشعار تحت CC BY-SA 4.0.
إذا كنت ترغب في دعم هذا المشروع، يمكنك أن تقدم لي قهوة اسبريسو.
إذا وجدت أن هذا لا يكفي، فلا تتردد في مساعدتي بالطريقة التي تفضلها.