سؤال

يمكنني إنشاء TcpListener وTcpAcceptor بسهولة تامة في Rust، لكنني لا أفهم تمامًا ما يفعلونه.يبدو أن المستمع لا يستمع حقًا وهو مجرد بنية إعدادات للمتقبل.

let tcp_listener = TcpListener::bind(addr);
let mut acceptor = tcp_listener.listen();
drop(acceptor);

أحد الأشياء التي جعلتني في حيرة من أمري هو مكالمة الإسقاط.ليس لدي أي فكرة عن مكان تعريف الإفلات، لقد بحثت في المكتبة القياسية قليلاً ولكن لا يمكنني العثور عليها إلا كطريقة للسمات.

وأيضاً لماذا يؤدي إسقاط المتقبل إلى وقف الاستماع؟ألا ينبغي أن يؤدي ذلك إلى وقف القبول فقط؟بالتأكيد تريد إسقاط المستمع.لماذا يوجد حتى TcpListener حيث يبدو أن المتقبل يفعل كل شيء؟

هل كانت مفيدة؟

المحلول

باختصار، في إطار عمل مآخذ توصيل BSD، إليك كيفية بدء تشغيل الخادم عادةً:

  • يتم إنشاء مقبس الخادم الرئيسي باستخدام socket() استدعاء النظام؛
  • يرتبط المقبس الرئيسي بعنوان/منفذ محدد باستخدام bind() استدعاء النظام؛
  • تم "بدء" استخدام المقبس الرئيسي listen() استدعاء النظام؛من هذه اللحظة وحتى يتم إغلاق المقبس الرئيسي، فإنه سيتولى المنفذ الذي يرتبط به وسيستمع للاتصالات الواردة؛
  • عندما يستمع المقبس الرئيسي، فمن الممكن الاتصال accept();سيتم حظر هذا الاستدعاء حتى يتصل أحد العملاء ثم سيعيد مقبسًا جديدًا يمثل اتصالاً بهذا العميل؛كل الاتصالات مع هذا العميل تمر عبر هذا المقبس؛عندما ينتهي التفاعل، يجب إغلاق هذا المقبس.

تتبع تسمية الطرق الرئيسية التي يستخدمها مكدس TCP في Rust تسمية مآخذ توصيل BSD القياسية، ومع ذلك، فإن Rust API أكثر تجريدًا، لذلك يتم إخفاء بعض المفاهيم ذات المستوى المنخفض.

TcpListener::bind() يقوم بإنشاء مقبس رئيسي جديد (socket() استدعاء النظام) وربطه بالعنوان المقدم (bind() استدعاء النظام).يقوم بإرجاع كائن، TcpListener, ، والذي يقوم بتغليف المقبس الرئيسي في حالة "تم إنشاؤه، ولكن ليس نشطًا".

TcpListener يوفر طريقة تسمى TcpListener::listen() الذي يلتف listen() استدعاء النظام و"تنشيط" مقبس الخادم.يقوم بإرجاع كائن جديد، TcpAcceptor, ، والذي يوفر واجهة ملائمة لقبول الاتصالات الواردة.إنه يمثل المقبس الرئيسي في حالة "تم إنشاؤه ونشط".

TcpAcceptor بدوره لديه عدة طرق (TcpAcceptor::incoming() و TcpAcceptor::accept()) الذي التفاف accept() استدعاء النظام وحظر المهمة الحالية حتى يقوم بعض العملاء بإنشاء اتصال ينتج عنه TcpStream - كائن يمثل مقبس العميل.

يتم تحرير جميع موارد نظام التشغيل المرتبطة بجميع المقابس عند مدمرات وتسمى.يمكن ربط المدمرات في Rust بأي بنية مخصصة.لإضافة أداة تدمير إلى البنية الخاصة بك، يجب عليك تنفيذها Drop سمة لذلك.يتم استدعاء Destructor عندما يخرج مثيل هذه البنية عن النطاق، على سبيل المثال:

struct Test { value: int }

impl Drop for Test {
    fn drop(&mut self) {
        println!("Dropping Test: value is {}", self.value);
    }
}

fn main() {
    let test = Test { value: 10 };
    // main ends, `test` goes out of scope
}

يطبع هذا البرنامج Dropping Test: value is 10.

ما هو مهم للهياكل مع Drop السمة المطبقة عليها هي أنها غير قابلة للنسخ ضمنيًا.وهذا يعني أنه عندما يتم تمرير مثيلات هذه الهياكل إلى الوسائط الوظيفية أو تعيينها لمتغيرات مختلفة، فإنها تكون كذلك خرجت من مواقعها الأصلية، أي أنه لم يعد بإمكانك استخدامها من خلال متغيراتها الأصلية.على سبيل المثال (باستخدام Test التعريف أعلاه):

let test = Test { value: 10 };
let test2 = test;
println!("{}", test.value);  // error: use of moved value: `test`

drop() تم تعريف الوظيفة التي أربكتك في core::mem وحدة؛ هنا هو توثيقها.تنفيذها هو جداً بسيط؛تبدو هكذا:

fn drop<T>(x: T) { }

لا يفعل شيئا على الإطلاق!ولكن لأنه يقبل حجته من حيث القيمة، يتم نقل هذه الحجة إلى drop() يتصل.ثم تخرج الوسيطة المذكورة على الفور عن النطاق لأن الوظيفة تعود.لو T يحتوي على أداة تدمير مرتبطة به، وسيتم تشغيله هنا.لذا، drop() هو في الأساس "ثقب أسود":يتم تدمير كل ما تضعه فيه (ما لم يكن قابلاً للنسخ ضمنيًا).من الخصائص الرائعة جدًا للغة أنه يمكن كتابة هذه الوظيفة بلغة Rust البسيطة، دون أي نوع من السحر.

في الحقيقة، drop() له فائدة قليلة لأن جميع أدوات التدمير يتم تشغيلها دائمًا عندما تخرج الكائنات المقابلة عن النطاق.يكون مفيدًا في بعض الأحيان عندما تكون هناك حاجة إلى بعض المنطق المعقد بموارد قابلة للتدمير، ولكنه نادرًا ما يحدث.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top