Rustの孊習AzulずのUDPチャットの方法





私はRustを孊び続けおいたす。 私はただあたり知らないので、倚くの間違いを犯したす。 前回、私はスネヌクゲヌムを䜜ろうずしたした。 サむクル、コレクション、3D Three.rsでの䜜業を詊したした 。 ggezずAmethystに぀いお孊びたした。 今回は、チャット甚のクラむアントずサヌバヌを䜜成しようずしたした。 GUIにはAzulを䜿甚したした。 Conrod 、 Yew 、 Orbtkも芋たした 。 マルチスレッド、チャネル、ネットワヌクを詊したした。 前の蚘事の間違いを考慮に入れお、これをより詳现にしようずしたした。 詳现に぀いおは、猫ぞようこそ。



→ ゜ヌス、Windows 10 x64で動䜜



ネットワヌキングでは、このプロトコルを䜿甚しお次のプロゞェクトを䜜成し、ここでトレヌニングしたかったので、UDPを䜿甚したした。 GUIに぀いおは、Rustのプロゞェクトをすぐに芋お、それらの基本的な䟋を芋お、AzulはDocument Object ModelずCSSのようなスタむル゚ンゞンを䜿甚しおいるので、私は長い間Web開発に埓事しおいたした。 䞀般的に、フレヌムワヌクは䞻芳的に遞択したした。 これたでのずころ、ディヌプアルファでは、スクロヌルは機胜せず、入力フォヌカスは機胜せず、カヌ゜ルはありたせん。 テキストフィヌルドにデヌタを入力するには、入力䞭にその䞊にカヌ゜ルを眮き、その䞊に盎接保持する必芁がありたす。 詳现



実際、蚘事のほずんどはコヌドコメントです。



アズヌル



機胜スタむル、DOM、CSSを䜿甚したGUIフレヌムワヌク。 むンタヌフェむスはルヌト芁玠で構成されたす。ルヌト芁玠には倚くの子孫があり、HTMLやXMLなどで独自の子孫を持぀こずができたす。 むンタヌフェヌス党䜓は、単䞀のDataModelのデヌタに基づいお䜜成されたす。 その䞭で、すべおのデヌタは䞀般にプレれンテヌションに転送されたす。 ASP.NETに慣れおいる人は、AzulずそのDataModelはRazorずそのViewModelに䌌おいたす。 HTMLず同様に、関数をDOM芁玠のむベントにバむンドできたす。 CSSフレヌムワヌクを䜿甚しお芁玠をスタむルできたす。 これはHTMLず同じCSSではありたせんが、非垞に䌌おいたす。 たた、Wang、UWPのAngularたたはMVVMのような双方向バむンディングもありたす。 サむトの詳现。



残りのフレヌムワヌクの簡単な抂芁





お客様



゜ケットの読み取りおよび曞き蟌み甚の補助機胜がグルヌプ化されおいる構造



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. デヌタモデルでミュヌテックスを取埗したす。 これにより、ミュヌテックスが解攟されるたで、むンタヌフェむスの再描画スレッドがブロックされたす。
  4. ナヌザヌが入力したテキストのコピヌを䜜成しおさらに転送し、テキスト入力フィヌルドをクリアしたす。
  5. メッセヌゞを送信しおいたす。
  6. このむベントを凊理した埌、むンタヌフェむスを再描画する必芁があるこずをフレヌムワヌクに通知したす。
  7. この機胜は、ナヌザヌがサヌバヌに接続するずきに機胜したす。
  8. 暙準ラむブラリからの時間の長さを衚すために構造を接続したす。
  9. すでにサヌバヌに接続しおいる堎合は、関数の実行を䞭断し、むンタヌフェむスを再描画する必芁がないこずをフレヌムワヌクに通知したす。
  10. Azulフレヌムワヌクのスレッドプヌルからスレッドで非同期に実行されるタスクを远加したす。 デヌタモデルを䜿甚したmutexの呌び出しは、mutexが解攟されるたでUIの曎新をブロックしたす。
  11. メむンスレッドで実行される定期的なタスクを远加したす。 このデヌモンでの長時間の蚈算は、むンタヌフェヌスの曎新によっおブロックされたす。
  12. ミュヌテックスを取埗したす。
  13. ナヌザヌが入力したポヌトを読み取り、それに基づいおロヌカルアドレスを䜜成し、リッスンしたす。
  14. ロヌカルアドレスに到着するパケットを読み取るUDP゜ケットを䜜成したす。
  15. ナヌザヌが入力したサヌバヌアドレスを読み取りたす。
  16. このサヌバヌからのみパケットを読み取るようにUDP゜ケットに指瀺したす。
  17. ゜ケットからの読み取り操䜜のタむムアりトを蚭定したす。 ゜ケットぞの曞き蟌みは埅機せずに行われたす。぀たり、デヌタを曞き蟌むだけで䜕も埅たず、゜ケットからの読み取り操䜜はストリヌムをブロックし、読み取り可胜なデヌタが到着するたで埅機したす。 タむムアりトを蚭定しない堎合、゜ケットからの読み取り操䜜は無期限に埅機したす。
  18. ナヌザヌがサヌバヌに既に接続しおいるこずを瀺すフラグを蚭定したす。
  19. 䜜成した゜ケットをデヌタモデルに枡したす。
  20. このむベントを凊理した埌、むンタヌフェむスを再描画する必芁があるこずをフレヌムワヌクに通知したす。
  21. Azulフレヌムワヌクのスレッドプヌルで実行される非同期操䜜。
  22. デヌタモデルから゜ケットのコピヌを取埗したす。
  23. ゜ケットからデヌタを読み取ろうずしおいたす。 ゜ケットのコピヌを䜜成せず、デヌタモデルのミュヌテックスにある゜ケットからメッセヌゞが到着するたでここで盎接埅機する堎合、ミュヌテックスを解攟するたでむンタヌフェむス党䜓の曎新が停止したす。
  24. 䜕らかのメッセヌゞが衚瀺された堎合、デヌタモデルを倉曎するず、modifyはlockず同じこずを行いたす。結果をラムダに枡し、ラムダコヌドの終了埌にミュヌテックスを解攟しお、ラップを解陀したす。
  25. 新しいメッセヌゞがあるこずを瀺すフラグを蚭定したす。
  26. すべおのチャットメッセヌゞの配列にメッセヌゞを远加したす。
  27. メむンスレッドで実行される繰り返し同期操䜜。
  28. 新しいメッセヌゞがある堎合は、むンタヌフェむスを最初から再描画しおこのデヌモンで䜜業を続ける必芁があるこずをフレヌムワヌクに通知したす。そうでない堎合は、最初からむンタヌフェむスを描画せずに、次のサむクルでこの関数を呌び出したす。
  29. ミュヌテックスがデヌタモデルでロックされないように、゜ケットのコピヌを䜜成したす。
  30. ミュヌテックスを取埗し、゜ケットぞのリンクを取埗したす。
  31. ゜ケットのコピヌを䜜成したす。 関数を終了するず、ミュヌテックスは自動的に解攟されたす。


Azulの非同期デヌタ凊理ずデヌモン



 // 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; }); }
      
      





modify関数はlockを呌び出し、デヌタモデルでmutexが実行される間、むンタヌフェむスの曎新をブロックしたす。



私たちのスタむル



 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. 最終的なDOMを䜜成し、むンタヌフェむスを再描画する必芁があるたびに呌び出される関数。
  2. すでにサヌバヌに接続しおいる堎合は、メッセヌゞを送信および読み取るためのフォヌムを衚瀺したす。それ以倖の堎合は、サヌバヌに接続するためのフォヌムを衚瀺したす。
  3. サヌバヌぞの接続に必芁なデヌタを入力するためのフォヌムを䜜成したす。
  4. ログむンのテキスト碑文でボタンを䜜成したす。
  5. それをDOMオブゞェクトに倉換したす。
  6. 行クラスを远加したす。
  7. CSSクラスオレンゞを远加したす。
  8. ボタンをクリックするむベントハンドラヌを远加したす。
  9. ナヌザヌずcssクラスの行に衚瀺するテキストを含むテキストラベルを䜜成したす。
  10. モデルのプロパティずcssクラスの行のテキストを䜿甚しおテキストを入力するためのテキストボックスを䜜成したす。
  11. テキストフィヌルドをDataModelのプロパティにバむンドしたす。 これは双方向バむンディングです。 TextInputを線集するず、モデルのプロパティ内のテキストが自動的に倉曎されたす。逆も同様です。 モデルのテキストを倉曎するず、TextInputのテキストも倉曎されたす。
  12. UI芁玠を配眮するルヌトDOM芁玠を䜜成したす。
  13. メッセヌゞを送受信するためのフォヌムを䜜成したす。
  14. クリックするず、テキスト「送信」ずクラス「行」、「オレンゞ」、CSSを含むボタンを䜜成したす。
  15. モデルプロパティself.messaging_model.text_input_stateの双方向バむンディングずクラス「row」のcssでテキスト入力フィヌルドを䜜成したす。
  16. チャットで䜜成されたメッセヌゞを衚瀺するテキストラベルを远加したす。


むンタヌフェヌスの状態を保存するモデル



Azulのドキュメントには、デヌタベヌスぞの接続を含むすべおのアプリケヌションデヌタを栌玍する必芁があるず曞かれおいるため、UDP゜ケットをそこに配眮したす。



 //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. デヌタモデル。 Azulで䜿甚できるように。 圌女はレむアりト特性を実装する必芁がありたす。
  3. ナヌザヌがサヌバヌに接続されおいるかどうかを確認するフラグ。
  4. サヌバヌにメッセヌゞを送信し、サヌバヌから受信したメッセヌゞを保存するためのフォヌムを衚瀺するためのモデル。
  5. サヌバヌに接続するためのフォヌムを衚瀺するためのモデル。
  6. ナヌザヌが入力したポヌト。 ゜ケットでそれをリッスンしたす。
  7. ナヌザヌが入力したサヌバヌのアドレス。 接続したす。
  8. ナヌザヌメッセヌゞ。 サヌバヌに送信したす。
  9. サヌバヌから送信されたメッセヌゞの配列。
  10. サヌバヌず通信するための゜ケット。
  11. サヌバヌから新しいメッセヌゞが到着したかどうかを確認するフラグ。


最埌に、アプリケヌションぞのメむン゚ントリポむント。 GUI描画ずナヌザヌ入力凊理からサむクルを開始したす



 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. 1぀の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. 新しいスレッドを開始したす。 moveは、倉数がそれぞれラムダずフロヌを匕き継ぐこずを意味したす。 より具䜓的には、新しいスレッドはrxおよび゜ケット倉数を「吞収」したす。
  2. 顧客によっお私たちに接続されたアドレスのコレクション。 それらすべおにメッセヌゞを送信したす。 䞀般的に、実際のプロゞェクトでは、クラむアントを私たちから切断し、この配列からアドレスを削陀するこずを凊理する必芁がありたす。
  3. 無限ルヌプを開始したす。
  4. チャネルからデヌタを読み取りたす。 ここでは、新しいデヌタが到着するたでストリヌムがブロックされたす。
  5. 配列にそのようなアドレスがない堎合は、そこに远加したす。
  6. バむト配列からUTF8文字列をデコヌドしたす。
  7. すべおの顧客に送信するバむトの配列を䜜成したす。
  8. アドレスのコレクションを調べお、党員にデヌタを送信したす。
  9. UDP゜ケットぞの曞き蟌み操䜜はブロックされないため、ここでは、関数は受信者にメッセヌゞが到着しおほが即座に実行されるたで埅機したせん。
  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. このアドレスでリッスンするUDP゜ケットを䜜成したす。
  3. 読み取り操䜜のタむムアりトを蚭定したす。 読み取り操䜜はブロックされおおり、新しいデヌタが到着するか、タむムアりトが発生するたでフロヌをブロックしたす。
  4. Functionから䜜成された゜ケットを返したす。
  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. タむムアりトたたはその他の゚ラヌが発生した堎合は、ルヌプの次の反埩に進みたす。


アプリケヌションのレむダヌに぀いお



オフトピック6月2日の職堎での小さな教育プログラム。 ここに眮くこずにしたした。誰かが圹に立぀かもしれたせん。 6月のシャヌプな詩人はCの䟋であり、ASP.NETに぀いお話しおいる
それで、䜕もありたせんでした、それは倕方でした、そしお、私はアルテムずビクタヌのために建築に関する小さな教育プログラムを曞くこずに決めたした。 さあ、行きたしょう。



実際、ここに远加したのは、モヌドの回埩ず1週間に1回しか蚘事を曞くこずができず、資料が既にそこにあり、来週はHabrに䜕か他のものをアップロヌドしたかったからです。



通垞、アプリケヌションは階局化されたす。 各レむダヌには、それらが配眮されおいるレむダヌの動䜜特性を実装するオブゞェクトが含たれおいたす。 など。 これらはレむダヌです。



  1. プレれンテヌション局。
  2. ビゞネスロゞックのレむダヌ。
  3. デヌタアクセス局。
  4. ゚ンティティナヌザヌ、動物など




各レむダヌには、独自のDTOず、任意のメ゜ッドを持぀完党に任意のクラスを含めるこずができたす。 䞻なこずは、それらが配眮されおいるレむダヌに関連する機胜を実行するこずです。 単玔なアプリケヌションでは、䞀郚のレむダヌが欠萜しおいる堎合がありたす。 たずえば、レむダヌビュヌは、MVC、MVP、MVVMパタヌンを介しお実装できたす。 これは完党にオプションです。 䞻なこずは、このレむダヌにあるクラスがレむダヌに割り圓おられた機胜を実装するこずです。 パタヌンずアヌキテクチャは単なる掚奚事項であり、指瀺ではないこずに泚意しおください。 パタヌンずアヌキテクチャは法埋ではありたせん。これはアドバむスです。



したがっお、暙準のEntity Frameworkを䜿甚した暙準のASP.NETアプリケヌションの䟋を䜿甚しお、各レむダヌを芋おみたしょう。



プレれンテヌション局



ここにMVCがありたす。 これは、ナヌザヌむンタラクションを提䟛するレむダヌです。 コマンドはここにあり、ナヌザヌはここからデヌタを取埗したす。 必ずしも人々ではありたせん。APIがあれば、ナヌザヌは別のプログラムです。 車は車ず通信したす。



ビゞネスロゞック局



ここでは、通垞、クラスはUserServiceなどのServiceず呌ばれたすが、それは䜕でもかたいたせん。 メ゜ッドを持぀クラスのセット。 䞻なこずは、アプリケヌションの蚈算ず蚈算がここで行われるこずです。 これは最も厚くおかさばる局です。 ほずんどのコヌドずさたざたなクラスがありたす。 これは実際、私たちのアプリケヌションです。



デヌタアクセス局



通垞、ここでEFは䜜業単䜍ずリポゞトリのパタヌンを実装したす。 そのため、DbContextは䜜業単䜍であり、DBはそれをリポゞトリず蚭定したす。 実際、これはデヌタを配眮し、そこからデヌタを取埗する堎所です。 デヌタ゜ヌスがデヌタベヌス、別のアプリケヌションのAPI、メモリ内のキャッシュ、たたは䜕らかの皮類の乱数ゞェネレヌタヌであるかどうかに関係なく。 任意のデヌタ゜ヌス。



゚ンティティ



はい、あらゆる皮類のナヌザヌ、動物などです。 1぀の重芁なポむント-圌らは圌らだけに特城的なある皮の行動を持っおいるかもしれたせん。 䟋



 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(); } } }
      
      







PS



さお、蚘事の文法䞊の誀りを修正しおくれたNastyaに感謝したす。そうです、Nastyaはあなたが赀い卒業蚌曞で無駄にならず、䞀般的にはクヌルです。愛しおる<3。



All Articles