すべての人に良い一日を!
そして、今後の休日に!
私のリポジトリには、この記事の最後にあるコードが含まれています。
おそらく、すべてを再び書き直したという事実から始めますが、これは構造に影響しませんでした。 過去に多くの変更を行いましたが(サーバーとクライアントの両方で)、これまでにいくつかの側面が残っています(それらを作成してプレーヤーの初期位置を配置するプログラムを作成しませんでした(これまではすべて手動))。すぐに来ます。
批判を聞いて、このプロジェクトの分析で興味深い記事を書いてみます。
第1章:「作業クライアント」
いい名前ですね しかし、その背後にはクライアントの意味があります。 小さなスケルトンを作りたかったのですが、時間が経つにつれて試験が行われ、リファクタリングが少し難しくなりました。
(CODEによる)クライアントの解析を開始する前に、クライアントサーバーがどのように相互作用するかを説明する必要があります。
1.クライアントが言います:「サーバーよ、あなたのところに来ました!」
2.サーバーは彼に応答します:「クライアント、ここに壁とプレイヤーの座標があります。」
3.クライアントは再び「今から話します」と言います。
そして、それらの間で発生しました... TCPソケット上の通信。
主なメソッドが変更され、驚きました->別の名前空間とクラスから始めます。 将来、これをやり直します(すべてを別のファイルに分割することを約束しましたが、休日や試験に関連して困難であることが判明したため、別の名前空間に頼りました)。
上記のスキームで実際に機能する主な変数は、サーバーのポートとアドレスです。
クライアントは条件付きで2つのグループに分割できます。
まず、これはサービスグループです。 計算を実行し、サーバーから当社にコンソールにメッセージを出力する関数。
2番目に、これは相互作用アルゴリズムのグループです(上で示したように)。
サービスグループとすべてすべて
主に最初の名前空間にあるこのグループには、次のようなクラス/構造が含まれます。
// "" public struct WallState // "" public struct ShotState // public class Position // public struct PlayerState
私はすでにそれらを以前の投稿でレビューしましたが、その中の何も変更しませんでした(いくつかのフィールドを除き、それらを公開しました)。
名前を変更しても、メソッドの意味は変わりません-座標に応じてタンクを印刷します:
static void PrintCoordinate(int x, int y, int dir, int id)
そして、私たちの主な方法:
static void Work()
このメソッドは素晴らしい仕事をします-押されたキーを処理し、他のメソッド(構造内にある)を通してデータを収集し、それらをサーバーへの送信メソッドに送信します。
ネットワークグループ
サーバーと通信するメソッドのグループ。
ここにあります:
// static void Eventlistener() // static void Connect() // static void Disconnect() // static void MessageToServer(string data)
最初のメソッド(Eventlistener())は2番目のスレッドで開始され、サーバーをリッスンします。一方、メインスレッドは押されたキーを処理し、変更されたデータをサーバーに送信します(MessageToServer()メソッドを使用)。 残りのメソッドは、クライアントの起動/シャットダウン時にのみ使用されます。
第2章:自転車サーバー
サーバー(その主要部分)はマルチスレッドモードで動作します。 複数のストリームでのマルチスレッド読み取りおよび送信。
興味深い事実は、最大ワークロード(6人と想定)で、同時に起動されるスレッド(読み取りと送信の両方)の数は読み取りで6、6 * 6 = 36-全員への同時送信(合計-42)であり、それは論理的ですが、実際には、クライアントは1秒あたり2〜4アクション(pingを考慮に入れる)を実行できます。これにより、スレッド数が(送信によって)2〜4倍になります。
つまり、次の公式が得られます。Count+ Count * i +1。Countはユーザー数、iは同時に実行されるアクションの数、+ 1はメインストリームを考慮するためです。
なぜマルチスレッド読み取りですか? 私にとって非常に便利で、すべてを異なるストリームに分割し、それらからの情報を処理してクライアントに送信したり、別のボーナスを送信したりするのがはるかに簡単です-クライアントとの接続を切断することはありません。これは非常に重要ですデータはオフになります(次の各送信に次のポートが使用されます)。
ストリーム間の接続は、標準ライブラリからstd :: sync :: mpsc :: channel()関数を呼び出すことで作成されるTransmitter-Receiverタプルによって実装されます。
use std::sync::mpsc; let (sender, receiver) = mpsc::channel();
ただし、この方法には制限があります。トランスミッターがメッセージをレシーバーに送信(発言)しないことは不可能だからです。 なぜなら コンパイラーは、メッセージの受け渡しにどのタイプが使用されているかを知りません。
なぜ最初のストリームが必要なのか、なぜ送信機と受信機が必要なのか? これはスレッド並列化であるため、メインスレッドはすべての宛先にデータを送信するためのスレッドを作成します。
つまり、スキームを取得します。
ボックスが別々のストリームであり、一方から他方への矢印がトランスミッタの.send()メソッドです(つまり、レシーバにデータを送信します)。
しかし、データを受信するストリームには多くのストリームがあります(上記の式で見たように)、完全なスキームは次のようになります。
このすべての解析を始めましょう。 ネットワークインタラクションを開始する前に、ファイルからデータを読み取る必要があります。何かを送信する必要があり、カードをサーバーに縫い付けないでください(これらのカードを作成するプログラムを作成するため)。
lib.rs(mod Text)の関数を使用してファイルを読み取り、処理します。
サーバーの小さなスキーム:
そして、ここにコードがあります:
サーバーを手動で作成できる機能(net_serverモジュールから)
そして、メインから呼び出します:
fn start_game_handle(){ let mut i:usize = 0; println!(". - :"); let mut number_player = String::new(); //io::stdin().read_line(&mut number_player) // .unwrap(); io::stdin().read_line(&mut number_player) .unwrap(); let number_player: u32 = number_player.trim().parse().unwrap(); /* (1) ->(2) -> (3) */ let mut addrs:Vec<SocketAddr> = Vec::new(); println!(" IP:PORT :"); let mut ip_port = String::new(); io::stdin().read_line(&mut ip_port) .unwrap(); ip_port = slim(ip_port, ' '); ip_port = slim(ip_port, '\n'); ip_port = slim(ip_port, '\r'); ip_port = slim(ip_port, '\0'); println!("{:?}",ip_port); println!(" IP:PORT -(+{} ):",number_player); let mut game_port = String::new(); io::stdin().read_line(&mut game_port) .unwrap(); game_port = slim(game_port, ' '); game_port = slim(game_port, '\n'); game_port = slim(game_port, '\r'); game_port = slim(game_port, '\0'); let _port = slim_vec(game_port.clone(), ':');// - // let _port: u32 = _port[1].trim().parse().unwrap(); //let game_port: u32 = game_port.trim().parse().unwrap(); let mut exit_id: Vec<u32> = Vec::new(); // id , println!("[ !]"); let listener = TcpListener::bind(ip_port.as_str()).unwrap(); println!("{:?}", listener); let (sender, receiver) = mpsc::channel(); //let(sen_, recv_) = mpsc::channel(); let mut Connects:Vec<Connect> = Vec::new(); let mut k = 0; for i in 0..number_player { // println!(" :[{}]", i+1); match listener.accept(){ Ok((mut stream, addr)) => { /*let sender_clone = mpsc::Sender::clone(&sender); let (send_, recv_) = mpsc::channel(); thread::spawn(move|| { {send_.send(stream.try_clone().expect(" ..")).unwrap();} let q:[u8;8] = [0;8]; let mut buf:[u8; 256] = [0; 256]; println!(" [{}]", k); loop { stream.read(&mut buf); if buf.starts_with(&q) == false { sender_clone.send((String::from_utf8(buf.to_vec()).unwrap(), k)).unwrap(); } } }); {*/ addrs.push(addr); //let s_s = recv_.recv().unwrap(); Connects.push(Connect::new(stream, i)); /*k+=1; }*/ }, Err(e) => { }, }} let mut Connects_copy:Vec<TcpStream> = Vec::new(); //let mut Connects_copy_:Vec<TcpStream> = Vec::new(); { let mut i:usize = Connects.len() - 1; loop { match Connects[i].stream.try_clone() { Ok(mut srm) => { Connects_copy.push(srm); }, Err(e) => { Connects[i].stream.shutdown(Shutdown::Both).is_ok(); Connects.remove(i); }, } if i != 0{ i -= 1; } else { break; } }} for mut item in Connects_copy{ let sender_clone = mpsc::Sender::clone(&sender); thread::spawn(move ||{ let q:[u8;8] = [0;8]; let mut buf:[u8; 256] = [0; 256]; loop { item.read(&mut buf); println!(" [{:?}]", item); if buf.starts_with(&q) == false { sender_clone.send(String::from_utf8(buf.to_vec()).unwrap()).unwrap(); } } }); } for item_ in receiver{ println!(" "); let mut Connects_copy_:Vec<TcpStream> = Vec::new(); { let mut i:usize = Connects.len() - 1; loop { match Connects[i].stream.try_clone() { Ok(mut srm) => { Connects_copy_.push(srm); }, Err(e) => { Connects[i].stream.shutdown(Shutdown::Both).is_ok(); Connects.remove(i); }, } if i != 0{ i -= 1; } else { break; } }} for mut item in Connects_copy_{ let (sender_, recv_) = mpsc::channel(); sender_.send(item_.clone()).unwrap(); thread::spawn(move ||{ let s = recv_.recv().unwrap(); item.write(&s.into_bytes()); println!("{:?}", item.local_addr()); }); } } }
そして、メインから呼び出します:
fn main() { net_server::net_server::start_game_handle(); }
これが自転車の仕組みです。将来、この記事(および私のリポジトリ)にc#でサーバーを追加します。
結論!
私が失敗したこと:
1. WinFormバージョン。
2.レベルを視覚的に作成するためのプログラム。
3.可能な最小プレイヤー数に達したときのn秒での試合の開始。
私のリポジトリ
最初の記事
第二の記事
あなたの願いと訂正を待って、批判と多くのあなたに最高に感謝します!