लर्निंग रस्ट: मैंने अज़ुल के साथ यूडीपी चैट कैसे किया



मैं जंग सीखता रहता हूं। मैं अभी भी बहुत कुछ नहीं जानता, इसलिए मैं बहुत सारी गलतियाँ करता हूँ। पिछली बार मैंने स्नेक गेम बनाने की कोशिश की थी। मैंने 3 डी थ्री के साथ साइकिल, संग्रह, काम करने की कोशिश की। Ggez और Amethyst के बारे में सीखा। इस बार मैंने चैट के लिए क्लाइंट और सर्वर बनाने की कोशिश की। GUI के लिए अज़ुल का उपयोग किया। कॉनरोड , यू और ऑर्बटॉक भी देखा। मैंने मल्टीथ्रेडिंग, चैनल और नेटवर्किंग की कोशिश की। मैंने पिछले लेख की गलतियों को ध्यान में रखा और इसे और अधिक विस्तृत बनाने की कोशिश की। विवरण के लिए, बिल्ली में आपका स्वागत है।

स्रोत, विंडोज 10 x64 पर काम करता है

नेटवर्किंग के लिए, मैंने यूडीपी का उपयोग किया क्योंकि मैं इस प्रोटोकॉल का उपयोग करके अपनी अगली परियोजना बनाना चाहता हूं और मैं यहां इसके साथ प्रशिक्षण लेना चाहता था। जीयूआई के लिए, मैंने जल्दी से रस्ट पर परियोजना की परियोजनाएं देखीं, उनके लिए मूल उदाहरणों को देखा और अज़ुल ने मुझे झुका दिया क्योंकि यह एक डॉक्यूमेंट ऑब्जेक्ट मॉडल और सीएसएस-शैली शैली इंजन का उपयोग करता है, और मैं लंबे समय से वेब विकास में लगा हुआ था। सामान्य तौर पर, मैंने विषयवस्तु को चुना है। यह अब तक, गहरे अल्फा में है: स्क्रॉलिंग काम नहीं करती है, इनपुट फोकस काम नहीं करता है, कोई कर्सर नहीं है। किसी पाठ क्षेत्र में डेटा दर्ज करने के लिए, आपको उस पर होवर करना होगा और टाइप करते समय इसे सीधे ऊपर रखना होगा। अधिक जानकारी ...

दरअसल, ज्यादातर लेख कोड टिप्पणी है।

Azul


कार्यात्मक शैली, डोम, सीएसएस का उपयोग करके जीयूआई फ्रेमवर्क। आपके इंटरफ़ेस में एक मूल तत्व होता है, जिसमें कई वंशज होते हैं, जिनके अपने वंशज हो सकते हैं, जैसे HTML और XML में। संपूर्ण इंटरफ़ेस एक एकल डेटामॉडल के डेटा के आधार पर बनाया गया है। इसमें, सभी डेटा को सामान्य रूप से प्रस्तुति में स्थानांतरित किया जाता है। यदि कोई ASP.NET से परिचित है, तो अज़ुल और उसका डेटामॉडल रेजर और उसके व्यूमॉडल की तरह हैं। HTML के रूप में, आप DOM तत्वों की घटनाओं के लिए फ़ंक्शन को बाध्य कर सकते हैं। आप CSS फ्रेमवर्क का उपयोग करके तत्वों को स्टाइल कर सकते हैं। यह HTML के समान CSS नहीं है, लेकिन इसके समान है। WPF, UWP में कोणीय या MVVM की तरह दो तरह से बाध्यकारी भी है। साइट पर अधिक जानकारी।

बाकी रूपरेखाओं का संक्षिप्त अवलोकन


  • ओर्बटक - लगभग अज़ुल के समान और गहरे अल्फ़ा में भी
  • Conrod - वीडियो आप क्रॉस-प्लेटफ़ॉर्म डेस्कटॉप एप्लिकेशन बना सकते हैं।
  • Yew WebAssembly और React के समान है। वेब विकास के लिए।

ग्राहक


वह संरचना जिसमें सॉकेट में पढ़ने और लिखने के लिए सहायक कार्य को समूहीकृत किया जाता है


struct ChatService {} impl ChatService { //1 fn read_data(socket: &Option<UdpSocket>) -> Option<String> { //2 let mut buf = [0u8; 4096]; match socket { Some(s) => { //3 match s.recv(&mut buf) { //4 Ok(count) => Some(String::from_utf8(buf[..count].into()) .expect("can't parse to String")), Err(e) => { //5 println!("Error {}", e); None } } } _ => None, } } //6 fn send_to_socket(message: String, socket: &Option<UdpSocket>) { match socket { //7 Some(s) => { s.send(message.as_bytes()).expect("can't send"); } _ => return, } } } 

  1. सॉकेट से डेटा पढ़ें
  2. सॉकेट से पढ़ने के लिए डेटा के लिए बफर।
  3. कॉल को अवरुद्ध करना। यहां, डेटा पढ़ने या टाइमआउट होने तक निष्पादन थ्रेड बंद हो जाता है।
  4. हमें UTF8 एन्कोडिंग में बाइट सरणी से एक स्ट्रिंग मिलती है।
  5. यदि कनेक्शन समय-समय पर बाधित होता है या कोई अन्य त्रुटि उत्पन्न होती है तो हम यहां पहुंचते हैं।
  6. एक तार को सॉकेट में भेजता है।
  7. UTF8 एन्कोडिंग में स्ट्रिंग बाइट्स में कनवर्ट करें और सॉकेट में डेटा भेजें। सॉकेट में डेटा लिखना अवरुद्ध नहीं है, अर्थात्। निष्पादन का धागा अपना काम जारी रखेगा। यदि डेटा नहीं भेजा जा सका, तो हम प्रोग्राम को "भेज नहीं सकते" संदेश के साथ बाधित करते हैं।

एक संरचना जो समूहों को उपयोगकर्ता से घटनाओं को संभालने और हमारे DataModel को संशोधित करने के लिए कार्य करती है


 struct Controller {} //1 const TIMEOUT_IN_MILLIS: u64 = 2000; impl Controller { //2 fn send_pressed(app_state: &mut azul::prelude::AppState<ChatDataModel>, _event: azul::prelude::WindowEvent<ChatDataModel>) -> azul::prelude::UpdateScreen { //3 let data = app_state.data.lock().unwrap(); //4 let message = data.messaging_model.text_input_state.text.clone(); data.messaging_model.text_input_state.text = "".into(); //5 ChatService::send_to_socket(message, &data.messaging_model.socket); //6 azul::prelude::UpdateScreen::Redraw } //7 fn login_pressed(app_state: &mut azul::prelude::AppState<ChatDataModel>, _event: azul::prelude::WindowEvent<ChatDataModel>) -> azul::prelude::UpdateScreen { //8 use std::time::Duration; //9 if let Some(ref _s) = app_state.data.clone().lock().unwrap().messaging_model.socket { return azul::prelude::UpdateScreen::DontRedraw; } //10 app_state.add_task(Controller::read_from_socket_async, &[]); //11 app_state.add_daemon(azul::prelude::Daemon::unique(azul::prelude::DaemonCallback(Controller::redraw_daemon))); //12 let mut data = app_state.data.lock().unwrap(); //13 let local_address = format!("127.0.0.1:{}", data.login_model.port_input.text.clone().trim()); //14 let socket = UdpSocket::bind(&local_address) .expect(format!("can't bind socket to {}", local_address).as_str()); //15 let remote_address = data.login_model.address_input.text.clone().trim().to_string(); //16 socket.connect(&remote_address) .expect(format!("can't connect to {}", &remote_address).as_str()); //17 socket.set_read_timeout(Some(Duration::from_millis(TIMEOUT_IN_MILLIS))) .expect("can't set time out to read"); // 18 data.logged_in = true; // 19 data.messaging_model.socket = Option::Some(socket); //20 azul::prelude::UpdateScreen::Redraw } //21 fn read_from_socket_async(app_data: Arc<Mutex<ChatDataModel>>, _: Arc<()>) { //22 let socket = Controller::get_socket(app_data.clone()); loop { //23 if let Some(message) = ChatService::read_data(&socket) { //24 app_data.modify(|state| { //25 state.messaging_model.has_new_message = true; //26 state.messaging_model.messages.push(message); }); } } } //27 fn redraw_daemon(state: &mut ChatDataModel, _repres: &mut azul::prelude::Apprepres) -> (azul::prelude::UpdateScreen, azul::prelude::TerminateDaemon) { //28 if state.messaging_model.has_new_message { state.messaging_model.has_new_message = false; (azul::prelude::UpdateScreen::Redraw, azul::prelude::TerminateDaemon::Continue) } else { (azul::prelude::UpdateScreen::DontRedraw, azul::prelude::TerminateDaemon::Continue) } } //29 fn get_socket(app_data: Arc<Mutex<ChatDataModel>>) -> Option<UdpSocket> { //30 let ref_model = &(app_data.lock().unwrap().messaging_model.socket); //31 match ref_model { Some(s) => Some(s.try_clone().unwrap()), _ => None } } } 

  1. मिलीसेकंड में मध्यांतर जिसके बाद सॉकेट से पढ़ने का अवरुद्ध संचालन बाधित हो जाएगा।
  2. फ़ंक्शन तब पूरा होता है जब उपयोगकर्ता सर्वर पर एक नया संदेश भेजना चाहता है।
  3. हम अपने डेटा मॉडल के साथ म्यूटेक्स पर कब्जा कर लेते हैं। यह इंटरफ़ेस redraw थ्रेड को ब्लॉक करता है जब तक कि म्यूटेक्स को मुक्त नहीं किया जाता है।
  4. हम उपयोगकर्ता द्वारा दर्ज किए गए पाठ की एक प्रति बनाते हैं ताकि इसे आगे स्थानांतरित किया जा सके और पाठ इनपुट क्षेत्र को साफ़ किया जा सके।
  5. हम एक संदेश भेज रहे हैं।
  6. हम फ्रेमवर्क को सूचित करते हैं कि इस घटना को संसाधित करने के बाद, हमें इंटरफ़ेस को फिर से तैयार करना होगा।
  7. फ़ंक्शन तब काम करता है जब उपयोगकर्ता सर्वर से कनेक्ट करना चाहता है।
  8. हम मानक पुस्तकालय से समय की लंबाई का प्रतिनिधित्व करने के लिए संरचना को जोड़ते हैं।
  9. यदि हम पहले से ही सर्वर से जुड़े हैं, तो हम फ़ंक्शन के निष्पादन को बाधित करते हैं और फ्रेमवर्क को बताते हैं कि इंटरफ़ेस को फिर से तैयार करने की कोई आवश्यकता नहीं है।
  10. एक कार्य जोड़ें जो अज़ुल फ्रेमवर्क के थ्रेड पूल से धागे में अतुल्यकालिक रूप से निष्पादित किया जाएगा। म्यूटेक्स तक पहुँचने के लिए यूआई को अपडेट करने वाले डेटा मॉडल ब्लॉकों के साथ म्यूटेक्स तक पहुंचना।
  11. एक आवर्ती कार्य जोड़ें जो मुख्य थ्रेड में चलता है। इस डेमॉन में किसी भी लंबी गणना को इंटरफ़ेस अपडेट द्वारा अवरुद्ध किया जाता है।
  12. हम म्यूटेक्स के कब्जे में हैं।
  13. हम उपयोगकर्ता द्वारा दर्ज किए गए पोर्ट को पढ़ते हैं और इसके आधार पर एक स्थानीय पता बनाते हैं, हम सुनेंगे।
  14. एक यूडीपी सॉकेट बनाएं जो स्थानीय पते पर पहुंचने वाले पैकेट को पढ़ता है।
  15. हम उपयोगकर्ता द्वारा दर्ज किए गए सर्वर पते को पढ़ते हैं।
  16. हम अपने यूडीपी सॉकेट को केवल इस सर्वर से पैकेट पढ़ने के लिए कहते हैं।
  17. सॉकेट से रीड ऑपरेशन के लिए टाइमआउट सेट करें। सॉकेट के लिए लेखन बिना प्रतीक्षा के होता है, अर्थात, हम केवल डेटा लिखते हैं और किसी भी चीज़ का इंतजार नहीं करते हैं, और सॉकेट से रीड ऑपरेशन स्ट्रीम को ब्लॉक करता है और तब तक इंतजार करता है जब तक कि पढ़ा जाने वाला डेटा नहीं आ जाता। यदि आप टाइमआउट सेट नहीं करते हैं, तो सॉकेट से रीड ऑपरेशन अनिश्चित काल तक इंतजार करेगा।
  18. एक ध्वज सेट करें जो दर्शाता है कि उपयोगकर्ता पहले ही सर्वर से जुड़ा हुआ है।
  19. हम डेटा मॉडल में निर्मित सॉकेट पास करते हैं।
  20. हम फ्रेमवर्क को सूचित करते हैं कि इस घटना को संसाधित करने के बाद, हमें इंटरफ़ेस को फिर से तैयार करना होगा।
  21. एक अतुल्यकालिक ऑपरेशन जो अज़ुल फ्रेमवर्क के थ्रेड पूल में चलता है।
  22. हमारे डेटा मॉडल से सॉकेट की एक प्रति प्राप्त करें।
  23. सॉकेट से डेटा पढ़ने की कोशिश कर रहा है। यदि आप सॉकेट की एक प्रति नहीं बनाते हैं और सीधे हमारे डेटा मॉडल में म्यूटेक्स में आने वाले सॉकेट से एक संदेश आने तक यहां प्रतीक्षा करते हैं, तो जब तक हम म्यूटेक्स जारी नहीं करते हैं, तब तक संपूर्ण इंटरफ़ेस अपडेट होना बंद हो जाएगा।
  24. यदि हमें किसी प्रकार का संदेश मिलता है, तो अपने डेटा मॉडल को बदलते हुए, संशोधित करता है और लॉक के रूप में एक ही चीज़ करता है ()। अनअम्ब्रप () लैम्बडा के परिणाम को पास करने और लैम्बडा कोड समाप्त होने के बाद म्यूटेक्स को जारी करने के साथ।
  25. यह इंगित करने के लिए एक ध्वज सेट करें कि हमारे पास एक नया संदेश है।
  26. सभी चैट संदेशों की सरणी में एक संदेश जोड़ें।
  27. मुख्य धागे में चल रहा एक दोहराए जाने वाला तुल्यकालिक ऑपरेशन।
  28. यदि हमारे पास एक नया संदेश है, तो हम फ्रेमवर्क को सूचित करते हैं कि हमें इंटरफ़ेस को खरोंच से फिर से तैयार करने की आवश्यकता है और इस डेमॉन के साथ काम करना जारी रखें, अन्यथा, हम शुरुआत से इंटरफ़ेस नहीं बनाएंगे, लेकिन फिर भी अगले चक्र में इस फ़ंक्शन को कॉल करें।
  29. हमारे सॉकेट की एक प्रति बनाता है ताकि म्यूटेक्स को हमारे डेटा मॉडल के साथ लॉक न रखें।
  30. हमें म्यूटेक्स मिलता है और सॉकेट का लिंक मिलता है।
  31. सॉकेट की एक प्रति बनाएँ। फ़ंक्शन से बाहर निकलने पर म्यूटेक्स स्वचालित रूप से मुक्त हो जाएगा।

अज़ुल में अतुल्यकालिक डेटा प्रसंस्करण और डेमॉन


 // Problem - blocks UI :( fn start_connection(app_state: &mut AppState<MyDataModel>, _event: WindowEvent<MyDataModel>) -> UpdateScreen { //   app_state.add_task(start_async_task, &[]); //  app_state.add_daemon(Daemon::unique(DaemonCallback(start_daemon))); UpdateScreen::Redraw } fn start_daemon(state: &mut MyDataModel, _repres: &mut Apprepres) -> (UpdateScreen, TerminateDaemon) { // UI    thread::sleep(Duration::from_secs(10)); state.counter += 10000; (UpdateScreen::Redraw, TerminateDaemon::Continue) } fn start_async_task(app_data: Arc<Mutex<MyDataModel>>, _: Arc<()>) { // simulate slow load app_data.modify(|state| { // UI    thread::sleep(Duration::from_secs(10)); state.counter += 10000; }); } 

डेमन को हमेशा मुख्य धागे में निष्पादित किया जाता है, इसलिए वहां अवरुद्ध करना अपरिहार्य है। एक अतुल्यकालिक कार्य के साथ, यदि आप करते हैं, उदाहरण के लिए, इस तरह, 10 सेकंड के लिए कोई लॉक नहीं होगा।

 fn start_async_task(app_data: Arc<Mutex<MyDataModel>>, _: Arc<()>) { //  UI.  . thread::sleep(Duration::from_secs(10)); app_data.modify(|state| { state.counter += 10000; }); } 

फ़ंक्शन संशोधित करने के लिए लॉक () और डेटा मॉडल के साथ म्यूटेक्स कहता है, इसलिए इसके निष्पादन की अवधि के लिए इंटरफ़ेस को अपडेट करना ब्लॉक करता है।

हमारी शैलियों


 const CUSTOM_CSS: &str = " .row { height: 50px; } .orange { background: linear-gradient(to bottom, #f69135, #f37335); font-color: white; border-bottom: 1px solid #8d8d8d; }"; 

दरअसल, हमारे DOM को उसके उपयोगकर्ता को प्रदर्शित करने के लिए कार्य


 impl azul::prelude::Layout for ChatDataModel { //1 fn layout(&self, info: azul::prelude::WindowInfo<Self>) -> azul::prelude::Dom<Self> { //2 if self.logged_in { self.chat_form(info) } else { self.login_form(info) } } } impl ChatDataModel { //3 fn login_form(&self, info: azul::prelude::WindowInfo<Self>) -> azul::prelude::Dom<Self> { //4 let button = azul::widgets::button::Button::with_label("Login") //5 .dom() //6 .with_class("row") //7 .with_class("orange") //8 .with_callback( azul::prelude::On::MouseUp, azul::prelude::Callback(Controller::login_pressed)); //9 let port_label = azul::widgets::label::Label::new("Enter port to listen:") .dom() .with_class("row"); //10 let port = azul::widgets::text_input::TextInput::new() //11 .bind(info.window, &self.login_model.port_input, &self) .dom(&self.login_model.port_input) .with_class("row"); // 9 let address_label = azul::widgets::label::Label::new("Enter server address:") .dom() .with_class("row"); //10 let address = azul::widgets::text_input::TextInput::new() //11 .bind(info.window, &self.login_model.address_input, &self) .dom(&self.login_model.address_input) .with_class("row"); //12 azul::prelude::Dom::new(azul::prelude::NodeType::Div) .with_child(port_label) .with_child(port) .with_child(address_label) .with_child(address) .with_child(button) } //13 fn chat_form(&self, info: azul::prelude::WindowInfo<Self>) -> azul::prelude::Dom<Self> { //14 let button = azul::widgets::button::Button::with_label("Send") .dom() .with_class("row") .with_class("orange") .with_callback(azul::prelude::On::MouseUp, azul::prelude::Callback(Controller::send_pressed)); //15 let text = azul::widgets::text_input::TextInput::new() .bind(info.window, &self.messaging_model.text_input_state, &self) .dom(&self.messaging_model.text_input_state) .with_class("row"); //12 let mut dom = azul::prelude::Dom::new(azul::prelude::NodeType::Div) .with_child(text) .with_child(button); //16 for i in &self.messaging_model.messages { dom.add_child(azul::widgets::label::Label::new(i.clone()).dom().with_class("row")); } dom } } 

  1. फ़ंक्शन जो अंतिम डोम बनाता है, और हर बार आपको इंटरफ़ेस को फिर से शुरू करने की आवश्यकता होती है।
  2. यदि हम पहले से ही सर्वर से जुड़े हैं, तो हम संदेश भेजने और पढ़ने के लिए फ़ॉर्म दिखाते हैं, अन्यथा हम सर्वर से कनेक्ट करने के लिए फ़ॉर्म प्रदर्शित करते हैं।
  3. सर्वर से कनेक्ट करने के लिए आवश्यक डेटा दर्ज करने के लिए एक फॉर्म बनाता है।
  4. पाठ शिलालेख लॉगिन के साथ एक बटन बनाएं।
  5. इसे DOM ऑब्जेक्ट में कनवर्ट करें।
  6. इसमें पंक्ति वर्ग जोड़ें।
  7. इसमें सीएसएस क्लास ऑरेंज मिलाएं।
  8. बटन पर क्लिक करने के लिए एक ईवेंट हैंडलर जोड़ें।
  9. उपयोगकर्ता और सीएसएस वर्ग पंक्ति को प्रदर्शित करने के लिए पाठ के साथ एक पाठ लेबल बनाएं।
  10. हम अपने मॉडल और सीएसएस वर्ग पंक्ति की संपत्ति से पाठ के साथ पाठ दर्ज करने के लिए एक पाठ बॉक्स बनाते हैं।
  11. हमारे DataModel की संपत्ति के लिए पाठ क्षेत्र को बांधें। यह दो तरह से बाध्यकारी है। अब TextInput का संपादन हमारे मॉडल की संपत्ति में पाठ को स्वचालित रूप से बदल देता है और विपरीत भी सच है। यदि हम अपने मॉडल में पाठ बदलते हैं, तो TextInput में पाठ बदल जाएगा।
  12. हम एक मूल DOM तत्व बनाते हैं जिसमें हम अपने UI तत्वों को रखते हैं।
  13. संदेश भेजने और पढ़ने के लिए एक फॉर्म बनाता है।
  14. पाठ "भेजें" के साथ एक बटन बनाएं और कक्षाओं पर क्लिक करें "पंक्ति", "नारंगी" और एक ईवेंट हैंडलर जब इसे क्लिक किया जाता है।
  15. हम मॉडल संपत्ति self.messaging_model.text_input_state और वर्ग "पंक्ति" के साथ सीएसएस के साथ दो-तरफ़ा बाइंडिंग के साथ एक टेक्स्ट इनपुट फ़ील्ड बनाते हैं।
  16. चैट में लिखे गए संदेश प्रदर्शित करने वाले टेक्स्ट लेबल जोड़ें।

हमारा मॉडल जो हमारे इंटरफ़ेस की स्थिति को संग्रहीत करता है


अज़ुल प्रलेखन कहता है कि इसे डेटाबेस से कनेक्शन सहित सभी एप्लिकेशन डेटा को स्टोर करना चाहिए, इसलिए मैंने इसमें यूडीपी सॉकेट डाल दिया।

 //1 #[derive(Debug)] //2 struct ChatDataModel { //3 logged_in: bool, //4 messaging_model: MessagingDataModel, //5 login_model: LoginDataModel, } #[derive(Debug, Default)] struct LoginDataModel { //6 port_input: azul::widgets::text_input::TextInputState, //7 address_input: azul::widgets::text_input::TextInputState, } #[derive(Debug)] struct MessagingDataModel { //8 text_input_state: azul::widgets::text_input::TextInputState, //9 messages: Vec<String>, //10 socket: Option<UdpSocket>, //11 has_new_message: bool, } 

  1. यह हमें हमारी संरचना को स्ट्रिंग के रूप में प्रदर्शित करने की अनुमति देगा {{?}
  2. हमारे डेटा मॉडल। ताकि इसका उपयोग अज़ुल में किया जा सके। उसे लेआउट विशेषता को लागू करना चाहिए।
  3. यह जांचने के लिए कि उपयोगकर्ता सर्वर से जुड़ा हुआ है या नहीं।
  4. सर्वर को संदेश भेजने और सर्वर से प्राप्त संदेशों को सहेजने के लिए एक फॉर्म प्रदर्शित करने के लिए एक मॉडल।
  5. सर्वर से कनेक्ट करने के लिए फ़ॉर्म प्रदर्शित करने के लिए मॉडल।
  6. उपयोगकर्ता द्वारा दर्ज किया गया पोर्ट। हम इसे अपने सॉकेट के साथ सुनेंगे।
  7. उपयोगकर्ता द्वारा दर्ज किए गए सर्वर का पता। हम इससे जुड़ेंगे।
  8. उपयोगकर्ता संदेश। हम इसे सर्वर पर भेज देंगे।
  9. सर्वर से आए संदेशों की एक सरणी।
  10. सॉकेट जिसके माध्यम से हम सर्वर के साथ संवाद करते हैं।
  11. यह जांचने के लिए कि कोई नया संदेश सर्वर से आया है या नहीं।

और अंत में, आवेदन के लिए मुख्य प्रवेश बिंदु। जीयूआई ड्राइंग और उपयोगकर्ता इनपुट प्रसंस्करण से एक चक्र शुरू होता है


 pub fn run() { //1 let app = azul::prelude::App::new(ChatDataModel { logged_in: false, messaging_model: MessagingDataModel { text_input_state: azul::widgets::text_input::TextInputState::new(""), messages: Vec::new(), socket: None, has_new_message: false, }, login_model: LoginDataModel::default(), }, azul::prelude::AppConfig::default()); // 2 let mut style = azul::prelude::css::native(); //3 style.merge(azul::prelude::css::from_str(CUSTOM_CSS).unwrap()); //4 let window = azul::prelude::Window::new(azul::prelude::WindowCreateOptions::default(), style).unwrap(); //5 app.run(window).unwrap(); } 

  1. हम स्टार्ट डेटा के साथ एक एप्लिकेशन बनाते हैं।
  2. डिफ़ॉल्ट रूप से एप्लिकेशन द्वारा उपयोग की जाने वाली शैलियाँ।
  3. हमारी अपनी शैलियों को उनसे जोड़ें।
  4. हम एक विंडो बनाते हैं जिसमें हमारा एप्लिकेशन प्रदर्शित होगा।
  5. इस विंडो में एप्लिकेशन लॉन्च करें।

सर्वर


आवेदन के लिए मुख्य प्रवेश बिंदु


यहां हमारे पास आमतौर पर एक कंसोल एप्लिकेशन है।

 pub fn run() { //1 let socket = create_socket(); //2 let (sx, rx) = mpsc::channel(); //3 start_sender_thread(rx, socket.try_clone().unwrap()); loop { //4 sx.send(read_data(&socket)).unwrap(); } } 

  1. एक सॉकेट बनाएं।
  2. हम एक sx संदेश प्रेषक और कई rx प्राप्तकर्ता के साथ एक तरफ़ा चैनल बनाते हैं।
  3. हम सभी प्राप्तकर्ताओं को एक अलग स्ट्रीम में संदेश भेजना शुरू करते हैं।
  4. हम सॉकेट से डेटा पढ़ते हैं और इसे स्ट्रीम में भेजते हैं, जो सर्वर से जुड़े क्लाइंट को संदेश भेजता है।

ग्राहकों को संदेश भेजने के लिए एक स्ट्रीम बनाने का कार्य


 fn start_sender_thread(rx: mpsc::Receiver<(Vec<u8>, SocketAddr)>, socket: UdpSocket) { //1 thread::spawn(move || { //2 let mut addresses = Vec::<SocketAddr>::new(); //3 loop { //4 let (bytes, pre) = rx.recv().unwrap(); // 5 if !addresses.contains(&pre) { println!(" {} connected to server", pre); addresses.push(pre.clone()); } //6 let result = String::from_utf8(bytes) .expect("can't parse to String") .trim() .to_string(); println!("received {} from {}", result, pre); //7 let message = format!("FROM: {} MESSAGE: {}", pre, result); let data_to_send = message.as_bytes(); //8 addresses .iter() .for_each(|s| { //9 socket.send_to(data_to_send, s) //10 .expect(format!("can't send to {}", pre).as_str()); }); } }); } 

  1. एक नया सूत्र शुरू करें। चाल का मतलब है कि चर क्रमशः लैम्ब्डा और प्रवाह पर ले जाते हैं। विशेष रूप से, हमारे नए धागे आरएक्स और सॉकेट चर को "अवशोषित" करेंगे।
  2. ग्राहकों द्वारा हमसे जुड़े पते का एक संग्रह। हम उन सभी को हमारे संदेश भेजेंगे। सामान्य तौर पर, एक वास्तविक परियोजना में ग्राहक को हमसे अलग करने और इस सरणी से उसका पता हटाने की प्रक्रिया करना आवश्यक होगा।
  3. हम एक अनंत लूप शुरू करते हैं।
  4. हम चैनल से डेटा पढ़ते हैं। यहां नया डेटा आने तक स्ट्रीम को ब्लॉक कर दिया जाएगा।
  5. यदि हमारे सरणी में ऐसा कोई पता नहीं है, तो इसे वहां जोड़ें।
  6. एक बाइट सरणी से एक UTF8 स्ट्रिंग को डिकोड करें।
  7. हम बाइट्स की एक सरणी बनाते हैं जिसे हम अपने सभी ग्राहकों को भेजने जा रहे हैं।
  8. हम पते के संग्रह से गुजरते हैं और सभी को डेटा भेजते हैं।
  9. यूडीपी सॉकेट के लिए लेखन ऑपरेशन गैर-अवरुद्ध है, इसलिए यहां फ़ंक्शन तब तक इंतजार नहीं करेगा जब तक संदेश प्राप्तकर्ता पर नहीं पहुंचता और लगभग तुरंत निष्पादित होता है।
  10. एक त्रुटि के मामले में उम्मीद है कि दिए गए संदेश के साथ कार्यक्रम से एक आपातकालीन निकास होगा।

फ़ंक्शन उपयोगकर्ता इनपुट के आधार पर एक सॉकेट बनाता है


 const TIMEOUT_IN_MILLIS: u64 = 2000; fn create_socket() -> UdpSocket { println!("Enter port to listen"); //1 let local_port: String = read!("{}\n"); let local_address = format!("127.0.0.1:{}", local_port.trim()); println!("server address {}", &local_address); //2 let socket = UdpSocket::bind(&local_address.trim()) .expect(format!("can't bind socket to {}", &local_address).as_str()); //3 socket.set_read_timeout(Some(Duration::from_millis(TIMEOUT_IN_MILLIS))) .expect("can't set time out to read"); //4 socket } 

  1. हम उस पोर्ट को पढ़ते हैं जिसे हमारा सर्वर सुनता है और उसके आधार पर एक स्थानीय सर्वर पता बनाता है।
  2. इस पते पर एक यूडीपी सॉकेट बनाएं।
  3. रीड ऑपरेशन के लिए टाइमआउट सेट करें। रीड ऑपरेशन अवरुद्ध है और यह तब तक स्ट्रीम को ब्लॉक करेगा जब तक कि नया डेटा नहीं आता या टाइमआउट नहीं होता।
  4. हम फ़ंक्शन से निर्मित सॉकेट लौटाते हैं।
  5. फ़ंक्शन सॉकेट से डेटा पढ़ता है और प्रेषक पते के साथ इसे वापस करता है।

सॉकेट से डेटा पढ़ने का कार्य


 fn read_data(socket: &UdpSocket) -> (Vec<u8>, SocketAddr) { //1 let mut buf = [0u8; 4096]; //2 loop { match socket.recv_from(&mut buf) { //3 Ok((count, address)) => { //4 return (buf[..count].into(), address); } //5 Err(e) => { println!("Error {}", e); continue; } }; } } 

  1. बफर वह जगह है जहां हम डेटा पढ़ेंगे।
  2. एक लूप शुरू करता है जो मान्य डेटा पढ़ने तक चलेगा।
  3. हमें बाइट्स की संख्या और प्रेषक का पता मिलता है।
  4. हमने सरणी को इसकी शुरुआत से बाइट्स की संख्या में पढ़ा और इसे बाइट वेक्टर में परिवर्तित कर दिया।
  5. यदि कोई टाइमआउट या अन्य त्रुटि होती है, तो लूप के अगले पुनरावृत्ति पर आगे बढ़ें।

आवेदन में परतों के बारे में


ऑफटॉपिक: काम पर दो जून के लिए एक छोटा शैक्षिक कार्यक्रम। मैंने इसे यहां लगाने का फैसला किया, शायद कोई काम आएगा। जून के तेज-कवि C # में उदाहरण हैं और हम ASP.NET के बारे में बात कर रहे हैं
इसलिए, ऐसा करने के लिए कुछ भी नहीं था, यह शाम को था, और मैंने आर्टेम और विक्टर के लिए वास्तुकला पर एक छोटा शैक्षिक कार्यक्रम लिखने का फैसला किया। अच्छा, चलिए।

वास्तव में, मैंने यहां जोड़ा क्योंकि मोड रिकवरी और मैं केवल सप्ताह में एक बार लेख लिख सकता हूं, और सामग्री पहले से ही है और अगले हफ्ते मैं कुछ और अपलोड करना चाहता था।

आमतौर पर, एक आवेदन स्तरित होता है। प्रत्येक परत में ऐसी वस्तुएं होती हैं जो उस परत की व्यवहार विशेषता को लागू करती हैं जिसमें वे स्थित हैं। और ऐसा है। ये परतें हैं।

  1. प्रस्तुति परत।
  2. परत व्यापार तर्क।
  3. डेटा एक्सेस लेयर।
  4. संस्थाओं (उपयोगकर्ता, पशु, आदि)


प्रत्येक परत में अपने स्वयं के डीटीओ हो सकते हैं और पूरी तरह से मनमाने तरीकों के साथ कक्षाएं हो सकती हैं। मुख्य बात यह है कि वे उस परत से जुड़ी कार्यक्षमता का प्रदर्शन करते हैं जिसमें वे स्थित हैं। सरल अनुप्रयोगों में, कुछ परतें गायब हो सकती हैं। उदाहरण के लिए, MVC, MVP, MVVM पैटर्न के माध्यम से एक लेयर व्यू को लागू किया जा सकता है। जो पूरी तरह से वैकल्पिक है। मुख्य बात यह है कि जो कक्षाएं इस परत में हैं, वे परत को सौंपी गई कार्यक्षमता को लागू करते हैं। याद रखें, पैटर्न और वास्तुकला केवल सिफारिशें हैं, न कि दिशाएं। पैटर्न और वास्तुकला एक कानून नहीं है, यह सलाह है।

और इसलिए, हम मानक एंटिटी फ्रेमवर्क का उपयोग करते हुए एक मानक ASP.NET अनुप्रयोग के उदाहरण पर प्रत्येक परत पर विचार करेंगे।

प्रस्तुति परत


हमारे यहां एमवीसी है। यह वह परत है जो उपयोगकर्ता को बातचीत प्रदान करती है। कमांड यहाँ आते हैं और उपयोगकर्ताओं को यहाँ से डेटा मिलता है। जरूरी नहीं कि लोग, अगर हमारे पास एक एपीआई है, तो हमारा उपयोगकर्ता एक अलग कार्यक्रम है। कारों के साथ कारों का संवाद।

व्यापार तर्क परत


यहां, आमतौर पर, कक्षाओं को सेवा कहा जाता है, उदाहरण के लिए, उपयोगकर्ता सेवा, हालांकि यह कुछ भी हो सकता है। तरीकों के साथ कक्षाओं का एक सेट। मुख्य बात यह है कि हमारे आवेदन की गणना और गणना यहां होती है। यह सबसे मोटी और सबसे भारी परत है। इसमें अधिकांश कोड और विभिन्न वर्ग हैं। यह, वास्तव में, हमारा आवेदन है।

डेटा एक्सेस लेयर


आमतौर पर यहां ईएफ कार्य और रिपोजिटरी पैटर्न की इकाई को लागू करता है। तो हाँ, DbContext है, आप कह सकते हैं, कार्य की इकाई, और DB यह रिपोजिटरी है। यह, वास्तव में, वह स्थान है जहां हम डेटा डालते हैं और हम इसे कहां से प्राप्त करते हैं। भले ही डेटा स्रोत एक डेटाबेस, किसी अन्य एप्लिकेशन का एपीआई, मेमोरी में कैश या किसी प्रकार का रैंडम नंबर जनरेटर हो। कोई भी डेटा स्रोत।

तथ्य


हां, उपयोगकर्ता, पशु और अन्य सभी प्रकार के। एक महत्वपूर्ण बिंदु - उनके पास केवल किसी प्रकार का व्यवहार हो सकता है। उदाहरण के लिए:

 class User { public string FirstName { get; set; } public string LastName { get; set; } public string FullName { get { return FirstName + " " + LastName; } } public bool Equal(User user) { return this.FullName == user.FullName; } } 

खैर, और एक बहुत ही सरल उदाहरण है। शोभा थी


 using System; using System.Collections.Generic; using System.Text; //Entities class User { public int Id { get; set; } public string Name { get; set; } } //Data Access Layer class UserRepository { private readonly Dictionary<int, User> _db; public UserRepository() { _db = new Dictionary<int, User>(); } public User Get(int id) { return _db[id]; } public void Save(User user) { _db[user.Id] = user; } } //Business Logic Layer class UserService { private readonly UserRepository _repo; private int _currentId = 0; public UserService() { _repo = new UserRepository(); } public void AddNew() { _currentId++; var user = new User { Id = _currentId, Name = _currentId.ToString() }; _repo.Save(user); } public string GetAll() { StringBuilder sb = new StringBuilder(); for (int i = 1; i <= _currentId; i++) { sb.AppendLine($"Id: {i} Name: {_repo.Get(i).Name}"); } return sb.ToString(); } } //presentation Layer aka Application Layer class UserController { private readonly UserService _service; public UserController() { _service = new UserService(); } public string RunExample() { _service.AddNew(); _service.AddNew(); return _service.GetAll(); } } namespace ConsoleApp1 { class Program { static void Main(string[] args) { var controller = new UserController(); Console.WriteLine(controller.RunExample()); Console.ReadLine(); } } } 


पुनश्च


शाओ, मैं लेख में व्याकरण की त्रुटियों को सुधारने के लिए अपने नस्तास्या को धन्यवाद कहना चाहता हूं। तो हाँ, नास्त्य आप लाल डिप्लोमा के साथ व्यर्थ नहीं हैं और आम तौर पर शांत हैं। आई लव यू <३।

Source: https://habr.com/ru/post/hi433624/


All Articles