كيفية ضبط مهلة لحظر المقابس في Boost Asio؟
-
08-07-2019 - |
سؤال
هل هناك طريقة لإلغاء عملية معلقة (بدون قطع الاتصال) أو تعيين مهلة لوظائف مكتبة التعزيز؟
أي.أريد تعيين مهلة لحظر المقبس في Boost asio؟
المقبس.read_some(boost::asio::buffer(pData, maxSize), error_);
مثال:أريد أن أقرأ بعضًا من المقبس، لكني أرغب في ظهور خطأ إذا مرت 10 ثوانٍ.
المحلول
في نظام التشغيل Linux/BSD، يتم دعم مهلة عمليات الإدخال/الإخراج على المقابس مباشرةً بواسطة نظام التشغيل.يمكن تفعيل الخيار عبر setsocktopt()
.لا أعرف إذا boost::asio
يوفر طريقة لإعداده أو يعرض البرنامج النصي للمأخذ للسماح لك بتعيينه مباشرة - الحالة الأخيرة ليست محمولة حقًا.
من أجل الاكتمال، إليك الوصف من صفحة الرجل:
SO_RCVTIMEO و SO_SNDTIMEO
Specify the receiving or sending timeouts until reporting an error. The argument is a struct timeval. If an input or output function blocks for this period of time, and data has been sent or received, the return value of that function will be the amount of data transferred; if no data has been transferred and the timeout has been reached then -1 is returned with errno set to EAGAIN or EWOULDBLOCK just as if the socket was specified to be non-blocking. If the timeout is set to zero (the default) then the operation will never timeout. Timeouts only have effect for system calls that perform socket I/O (e.g., read(2), recvmsg(2), send(2), sendmsg(2)); timeouts have no effect for select(2), poll(2), epoll_wait(2), etc.
نصائح أخرى
وعندما سئل هذا السؤال، أعتقد لم اسيو يكن لديك أي مثال حول كيفية تحقيق ما يحتاجه OP، وهذا هو انتهاء مهلة عملية حظر مثل عملية مأخذ توصيل حظر. الآن توجد أمثلة لتظهر لك بالضبط كيفية القيام بذلك. على سبيل المثال يبدو طويلا، ولكن هذا لأنه علق جيدا. وهو يبين كيفية استخدام ioservice في "طلقة واحدة" نوع من واسطة.
وأعتقد أن المثال هو حل كبير. الحلول الأخرى هنا كسر قابلية ولا تستفيد من ioservice. إذا قابلية ليست مهمة وioservice تبدو لكثير من النفقات العامة --THEN-- يجب أن لا تستخدم اسيو. لا يهم ما، سيكون لديك ioservice خلق (تقريبا جميع وظائف اسيو تعتمد على ذلك، حتى مآخذ متزامنة) لذلك، والاستفادة منه.
مهلة لعرقلة عملية برنامج التعاون الفني اسيو
تم تحديث الوثائق اسيو، حتى التحقق من ذلك للحصول على أمثلة جديدة حول كيفية التغلب على بعض استخدام اسيو "gotchas" لديهم.
هل يمكن القيام async_read وأيضا تحديد توقيت وقتك المطلوب بها. ثم إذا كان حرائق الموقت، دعوة إلغاء على كائن المقبس الخاص بك. إلا إذا حصلت القراءة الخاصة بك، يمكنك إلغاء جهاز ضبط الوقت الخاص بك. وهذا يتطلب منك استخدام كائن io_service بطبيعة الحال.
وتحرير: العثور على التعليمات البرمجية المتكررة بالنسبة لك أن تفعل ذلك
وكان لي نفس السؤال، وبعد بعض البحوث، وأبسط، أنظف حل أتمكن من الخروج مع الحصول على مأخذ الأصلي الأساسي، والقيام حدد حتى كان هناك بيانات للقراءة. حدد سوف تأخذ معلمة المهلة. وبطبيعة الحال، والعمل مع مقبس الأصلي يبدأ في الذهاب ضد جهة استخدام اسيو في المقام الأول، ولكن مرة أخرى، يبدو أن هذا أنظف الطريق. بقدر ما يمكنني أن أقول، لا اسيو لا توفر وسيلة للقيام بذلك لاستخدام متزامن بسهولة. الرمز:
// socket here is: boost::shared_ptr<boost::asio::ip::tcp::socket> a_socket_ptr
// Set up a timed select call, so we can handle timeout cases.
fd_set fileDescriptorSet;
struct timeval timeStruct;
// set the timeout to 30 seconds
timeStruct.tv_sec = 30;
timeStruct.tv_usec = 0;
FD_ZERO(&fileDescriptorSet);
// We'll need to get the underlying native socket for this select call, in order
// to add a simple timeout on the read:
int nativeSocket = a_socket_ptr->native();
FD_SET(nativeSocket,&fileDescriptorSet);
select(nativeSocket+1,&fileDescriptorSet,NULL,NULL,&timeStruct);
if(!FD_ISSET(nativeSocket,&fileDescriptorSet)){ // timeout
std::string sMsg("TIMEOUT on read client data. Client IP: ");
sMsg.append(a_socket_ptr->remote_endpoint().address().to_string());
throw MyException(sMsg);
}
// now we know there's something to read, so read
boost::system::error_code error;
size_t iBytesRead = a_socket_ptr->read_some(boost::asio::buffer(myVector), error);
...
ولعل هذا سيكون مفيدا لحالتك.
وTL، DR
socket.set_option(boost::asio::detail::socket_option::integer<SOL_SOCKET, SO_RCVTIMEO>{ 200 });
والجواب الكامل هذا السؤال تبقي يجري طلب مرارا وتكرارا لسنوات عديدة. إجابات رأيت حتى الآن ضعيفة جدا. سأضيف هذه المعلومات هنا في واحد من ظهوره الأول في هذه المسألة.
والجميع يحاول استخدام اسيو لتبسيط كود التواصل من شأنه أن يكون سعيدا تماما إذا كان المؤلف أن مجرد إضافة معلمة مهلة اختياري لجميع وظائف الإعلام والتوعية وتزامن المتزامن. للأسف، هذا ليس من المرجح أن يحدث (في رأيي المتواضع، فقط لأسباب أيديولوجية، بعد كل شيء، كما هو الحال في اسيو هو لسبب من الأسباب).
وهكذا هذه هي الطرق لسلخ هذا القط الفقراء متاح حتى الآن، فإن أيا منها فاتح للشهية خاصة. دعونا نقول أننا بحاجة 200MS المهلة.
1) جيد (السيئة) API المقبس القديم:
const int timeout = 200;
::setsockopt(socket.native_handle(), SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof timeout);//SO_SNDTIMEO for send ops
تجدر الإشارة تلك الخصائص: - CONST الباحث عن مهلة - على ويندوز نوع المطلوب هو في الواقع DWORD، ولكن المجموعة الحالية من المجمعين له أنه لحسن الحظ فإن نفسه، الباحث CONST ذلك العمل سواء في الفوز وPOSIX العالم. - (CONST تشار *) للقيمة. على مطلوب Windows CONST شار *، POSIX يتطلب CONST الفراغ *، في C ++ CONST شار * سوف تتحول الى CONST الفراغ * بصمت في حين أن العكس ليس صحيحا.
والمزايا: سوف يعمل، وربما تعمل دائما مثل API مقبس قديمة ومستقرة. بسيطا بما فيه الكفاية. بسرعة. العيوب: من الناحية الفنية قد يتطلب الملفات رأس المناسبة (مختلفة على الفوز وحتى النكهات UNIX مختلفة) لsetsockopt وحدات الماكرو، ولكن التنفيذ الحالي لاسيو يلوث مساحة الاسم العالمي معهم على أي حال. يتطلب متغير المهلة. لم تكتب آمنة. على ويندوز، يتطلب أن يكون مأخذ في وضع المتراكبة للعمل (والذي يستخدم تنفيذ اسيو الحالي لحسن الحظ، لكنه لا يزال تفصيل تطبيق). القبيح!
2) خيار مأخذ اسيو مخصص:
typedef boost::asio::detail::socket_option::integer<SOL_SOCKET, SO_RCVTIMEO> rcv_timeout_option; //somewhere in your headers to be used everywhere you need it
//...
socket.set_option(rcv_timeout_option{ 200 });
والمزايا: بسيطة بما فيه الكفاية. بسرعة. جميلة (مع الرموز المميزة ل typedef). العيوب: يعتمد على تفصيل تطبيق اسيو، والتي قد تتغير (ولكن OTOH كل شيء سيتغير في نهاية المطاف، ومثل هذه التفاصيل هو أقل عرضة للتغيير واجهات برمجة التطبيقات ثم العام تخضع لتوحيد). ولكن في حال حدث ذلك، سيكون لديك إما كتابة فئة وفقا ل<لأ href = "https://www.boost.org/doc/libs/1_68_0/doc/html/boost_asio/reference/SettableSocketOption.html" يختلط = "noreferrer"> https://www.boost.org/doc/libs/1_68_0/doc/html/boost_asio/reference/SettableSocketOption.html (والذي هو بطبيعة الحال بفضل PITA الرئيسية لoverengineering واضح من هذا الجزء من اسيو) أو الأفضل من ذلك تعود إلى 1.
و3) استخدام C ++ المتزامن / مرافق في المستقبل.
#include <future>
#include <chrono>
//...
auto status = std::async(std::launch::async, [&] (){ /*your stream ops*/ })
.wait_for(std::chrono::milliseconds{ 200 });
switch (status)
{
case std::future_status::deferred:
//... should never happen with std::launch::async
break;
case std::future_status::ready:
//...
break;
case std::future_status::timeout:
//...
break;
}
والمزايا: معيار. العيوب: دائما يبدأ موضوع جديد (عمليا)، والذي هو بطيء نسبيا (قد تكون جيدة بما فيه الكفاية للعملاء، ولكن سوف يؤدي إلى ضعف وزارة الخارجية لخوادم كما المواضيع والمآخذ هي موارد "مكلفة"). لا تحاول استخدام الأمراض المنقولة جنسيا :: :: إطلاق مؤجلة بدلا من الأمراض المنقولة جنسيا :: :: إطلاق المتزامن لتجنب إطلاق موضوع جديد في wait_for سيعود دائما future_status :: تأجيل دون محاولة تشغيل التعليمات البرمجية.
و4) طريقة المنصوص عليها اسيو - عمليات المتزامن استخدام فقط (وهي ليست حقا الإجابة على هذا السؤال)
والمزايا: جيدة بما فيه الكفاية للخوادم جدا إن لم يكن مطلوب قابلية هائلة للمعاملات قصيرة. العيوب: الالفاظ جدا (ولذا فإنني لن تشمل حتى الأمثلة - راجع الأمثلة اسيو). يتطلب إدارة عمر حذرين للغاية من جميع الأشياء الخاصة بك استخدامها على حد سواء عن طريق عمليات متزامنة ومعالجات إنجازها، والتي في الواقع يتطلب من جميع الطبقات التي تحتوي على واستخدام هذه البيانات في عمليات المتزامن أن تستمد من enable_shared_from_this، الأمر الذي يتطلب كل هذه الطبقات المخصصة على كومة، وهو ما يعني ( على الأقل لعمليات قصيرة) أن قابلية سيبدأ تفتق عن منصبه بعد حوالي 16 المواضيع كما في كل الوك كومة / dealloc سوف تستخدم حاجز الذاكرة.
متابعة لما ذكره grepsedawk.هناك بعض الأمثلة التي توضح كيفية إلغاء العمليات غير المتزامنة طويلة المدى بعد فترة من الوقت، ضمن المهلات القسم داخل اسيو دوكو. تعزيز أمثلة اسيو . لقد ساعدني عميل Async TCP أكثر من غيره.
مزامنة سعيدة :)
حتى بعد مرور سنوات على السؤال الأصلي، لا توجد إجابة مرضية حتى الآن.
لا يعد استخدام التحديد يدويًا خيارًا جيدًا
- يجب أن يكون رقم واصف الملف أقل من 1024
- قد يتم الإبلاغ عن FD بشكل زائف على أنه جاهز بسبب وجود مجموع اختباري خاطئ.
يتصل io_service.run_one()
هي أيضًا فكرة سيئة، لأنه قد تكون هناك خيارات أخرى غير متزامنة تحتاج إلى خدمة io_service دائمًا run()
.ومن الصعب فهم مستند Boost حول حظر عميل TCP.
إذن هذا هو الحل الخاص بي.الفكرة الرئيسية هي ما يلي:
{
Semaphore r_sem;
boost::system::error_code r_ec;
boost::asio::async_read(s,buffer,
[this, &r_ec, &r_sem](const boost::system::error_code& ec_, size_t) {
r_ec=ec_;
r_sem.notify();
});
if(!r_sem.wait_for(std::chrono::seconds(3))) // wait for 3 seconds
{
s.cancel();
r_sem.wait();
throw boost::system::system_error(boost::asio::error::try_again);
}
else if(r_ec)
throw boost::system::system_error(r_ec);
}
هنا Semaphore
هو مجرد كائن المزامنة (mutex) وشرط_متغير.
wait_for
يتم تنفيذه بواسطة http://en.cppreference.com/w/cpp/thread/condition_variable/wait_for
الكود الكامل موجود في https://github.com/scinart/cpplib/blob/master/include/asio.hpp
الأمثلة في https://github.com/scinart/cpplib/blob/master/test/test_asio.cpp
مثال أفضل في https://github.com/scinart/cpplib/blob/master/test/test_SyncBoostIO.cpp
ويمكنك لف المكالمات متزامن في العقود الآجلة وانتظر حتى إكمال مع مهلة (wait_timeout).
وبالتأكيد ليس حجم واحد يناسب الجميع، ولكن يعمل بشكل جيد لمثل التحايل بطيئة ربط مهلة.
في * لا شيء، وكنت استخدام منبه () لذلك سوف تفشل دعوة المقبس الخاص بك مع EINTR