C ++でネットワークを操作するのは、1、2、3です。
network n = OpenNetwork(); connection c = n.OpenConnection("habrahabr.ru", 80); html_request h("GET", "/"); c << h; c.WaitForMessage(); n.Response(); std::cout << c;
こんにちは、カブラヴィテス様、今日は過去4年間の私の仕事についてお話ししたいと思います。 私の創造について、私の人生について。 それは、ネットワークを操作するための多機能ライブラリ、その歴史とその作成理由、解決すべきタスク、内部構造についてです。
すぐに予約をしたいのですが、私の作品は自転車かもしれませんが、作成時でも、新しいバージョンへの移行時でも、満足できるアナログは見つかりませんでした。
物語
開始する
すべては2008年の夏に始まりました。 winsockを使用して、最初のサーバーを作成しようとしました。
本当に明確なものは何もなかったので、先生は重要ではありませんでした 。
また、不便なAPIに非常に腹を立て、すぐにすべてをカプセル化することを望みました。 私はその時c ++を知りませんでした、そして最初のバージョンは関数の名前を変更するだけでした(今は失われています)。
ますますネイティブのwinsockスキームから離れ、コミュニケーションをより透明にしました。 (たとえば、connect関数を呼び出すと、ソケットがすぐに作成され、dns要求が行われ、接続が開かれました)。 徐々に便利になり、ゲームを作ろうと決心しました。
古代の伝説
TL2バージョンの前は、大幅な改善は行われていなかったため、300行のコードを禁止しています。
実際のタスクの出現により、1つの接続(チャット、テクスチャ、音楽)で複数のデータを転送するという問題が発生しました。 進歩自体が始まったのはこのことです。 6か月の間にこの問題を解決し、最適なパケット分割、サービスメッセージの形式を選択し、テストを行いました。 その結果、ライブラリは最大255個の「メッセージ」の同時送信をサポートし、それぞれ最大4ギガバイトのサイズで、タスクにうまく対処しました。
残念ながら、スタッフリーク、利他主義のため、ゲームを書いたことはありません。 はい、そして競合他社が登場しました。 したがって、将来のバージョンの開発に休むことができました。
TL4
次の繰り返しは、C ++への移行と新しい標準の追加、新しいチップの使用、新しい機能でした。
ライブラリは、サーバーとクライアントの2つのモードで同時に完全に動作するようになり、プロキシまたはp2pタスクを解決できます。 最大メッセージサイズは無限に増加し、同時に送信されるメッセージの最大数は最大2 ^ 32です(255はアクティブに送信されます)。 自動再接続機能が追加され、メッセージの送信が優先されました。
ライブラリは非ブロッキングソケットで非同期になり、メインスレッドの負荷を軽減しました。 (たとえば、デフォルトでは、接続を開くにはTL2で443ミリ秒かかり、この数はTL4で17ミリ秒に短縮されました)。
暗号化、圧縮などが追加され、リストは長期間継続できます。
解決すべき主なタスク
ライブラリは、プラットフォームに関係なく、次のことを実行できます。
クライアントに-接続を開き、任意のソース(ファイル、配列、その他の接続)からデータセットを転送し、接続が何回中断されてもデータが配信されることを確認します。
サーバーはポートをリッスンする必要があり、接続数、各接続のトラフィック、オンライン時間、平均速度などに関する情報を常に直感的に把握できます。
例「PROXY」
#include <tl4/def.h> using namespace ax::tl4; bool proxy( connection &c, message &m ) { std::string s; s << m; // host:pass, connection nc = c.GetNetwork().OpenConnection(s); nc << s; nc << c; nc >> c; return true; } void main() { network n = OpenNetwork(); port p = n.OpenPort(8080); p.NonTL4Compatible(true); p.OnOpenConnection(proxy); while (1) n.Response(); }
もちろん、例はエラー処理なしでシミュレートされ、実際に曲線(メッセージをstd ::文字列に変換しようとすると、プログラムは\ 0が到着するかタイムアウトによってブロックされます)が、本質は明らかです。
特徴
- プロトコル制限なし
- 暗号化、圧縮をサポート
- 最も正確な同期
- メッセージレベル、パッケージでのハンドラーの導入(暗号化)。 完全に変更可能なパケット構築パイプ。
- 接続を「仮想ネットワーク」に結合する(一度に処理されるグループ)。 ネットワーク間のすべての通信はスレッドセーフです。
- 1つの接続で最大40億のオブジェクトを同時に転送します。
- 構成可能な接続パラメーター、ソフトウェア速度制限など。
- 各接続またはグループのすべてのイベントのカスタムイベントハンドラー。
- オブジェクトの転送の優先順位、「今何を正確に転送するか」に対する完全な制御。
- 不完全なオブジェクト(トレント、インターネットラジオのストリームなど)を転送する機能。 サイズ制限はありません。
- 「新しいオブジェクト」、「オブジェクトの新しい断片」、「オブジェクトのダウンロードの完了」、「Nバイトごと」、「N秒ごと」の処理の可能性。
- 一箇所でのプロトコル開発。
一箇所のプロトコル
これは、コードを複製せずにクライアントサーバーアプリケーションを開発できる非常に便利な機能です。
小さな紹介。 各オブジェクトおよび各パッケージに対して、ハンドラーのキューが呼び出されます(明らかな理由でオンザフライで変更することはできません)。 ハンドラーには、エンコードとデコードの2つのメソッドがあります。 したがって、暗号化/復号化コード、圧縮/解凍コードは常に1つの場所にあります。 これにより、ハンドラーの正当性を確認できます-Testメソッドを呼び出すだけで、暗号化および復号化後に元のバージョンが取得されたランダムまたは提供されたデータを確認できます。
プロトコルにも同じことが言えます-プロトコル全体として、オブジェクトをメッセージに、メッセージをオブジェクトに変換できるのと同じメソッドがあります。
仕組み
ストリーム
プログラミングの世界のすべては、シーケンスを通して表現できます。 配列、文字列、ファイルはすべてバイトのシーケンスです。 変数とインバウンドアウトバウンド接続についても。
ストリームクラスシステムは、ストリームの物理ソースから抽象化し、情報で実際に発生する変換から抽象化するために作成されました。
stl :: streamに似ていますが、いくつか改善されています。
パイプ
単純なタスクを想像してください。ディスクから画像を転送したいのです。 これを行うには、ダウンロードしてbmp-> jpgに変換し、暗号化する必要があります。 これは、ハンドラーを次々に配置し、これが原子変換であると言うことで簡単に実行できます(給水を伴うアナロジー-パイプが突き合わせて行きます)。
pipe *ImageTransferPipe( std::string filename ) { pipe_constructor c; c.add(new BMP2JPG()); c.add(new BlowFishCrypt()); pipe p(c); return p.GetScheme().Source(new read_file_stream(filename)); }
最初はあまり明確で便利ではないようですが、APIを読んだ後は簡単になります。
すぐに使用可能な暗号化
ご想像のとおり、接続を作成する場合、論理的な機能は1つだけです。
connection::SetHandler( pipe * )
送信中の各バイトはこのハンドラーを通過し、受信中の各バイトは対応するプロセッサーも通過します(パイプ:: GetInverseScheme)。
複数のオブジェクトを一度に転送する
このアイデアは非常にシンプルで、必要な人とまだやっていない人に役立つと思います。
送信用のキュー内のすべてのオブジェクトは、255バイトずつ細断され、これらのブロックによって送信されます。 ブロックにはオブジェクトIDが提供されます。 オブジェクトは周期的に送信されます-最初、2番目、3番目、1番目、2番目、3番目。
ブロックは常に255バイトであり、サイズは提供されないことに注意してください。 突然ブロックが小さくなった場合、これはサービスメッセージによって調整されます。
オブジェクトの次の部分が突然「準備ができていない」(データなし)場合、準備ができた1つと引き換えに、非アクティブなキューに移動します。 これはすべて、システムメッセージによって規制されています。
システムメッセージ
それらにはいくつかの機能があります。 送信側は、アクティブ/非アクティブの回転を報告し、オブジェクトの終わりが近づいていることを警告し、オブジェクトの追加、「低レベル」例外の送信、オブジェクトのパイプ形式の送信を報告し、最も興味深いのは同期です。
マシン同期
正しく動作させるには、両端でオンにする必要があります。 このタスクのために、オブジェクトの優先度を認識する必要がありました。これらのサービスメッセージは、作成時にキューに追加する必要があります。
ホストはタイムスタンプをミリ秒で交換し、pingを受信します。 平均値を設定した後、彼らもそれを交換し始めます。 その結果、両方のマシンに応答時間があります。
最も興味深い部分は、サーバーソリューションから始まります。 pingが数百を超えるプレイヤーは私を理解するでしょう。 敵は数ミリ秒前に何か重要なことを知っているでしょう、そしてそれだけです。
次に、イベントがすべてのマシンに同時に到着するゲームを想像してください。これは単純に行われます。 同時メッセージを送信する場合、接続は遅延の順にソートされ、実際のデータは、接続の安定性を考慮して、推定到着時間が等しくなるように送信されます(有線ソリューションのエラーは10ミリ秒未満)。
なぜこの投稿なのか?
実際には、ライブラリは完全ではありません。 たとえば、予定されているすべての接続パラメーターがサポートされているわけではありません。暗号化アルゴリズムをそのまま使用したり、チェックサムを追加したりします。 一般的に、作業はまだ進行中です。
ここでは、宣伝するために表示されませんでしたが、アナログがあるかどうか
を確認し(人々がそれを持っている方法
を確認する
ためにいくつかのアイデアを盗みたい)、誰かがそれを必要とするなら(市場に行くのが理にかなっています)システム。
謝罪
プレゼンテーションの仕方、エラー、不正確さについておaび申し上げます。 カルマが低いため、「I am PR」というタグを追加できませんでした。 そして確かに無駄に、私はおそらくそれを行います。
UPD:
続き