分散アプリケーション開発のコア技術シリーズのひとつ - TCP/IPをベースにした独自のメッセージ設計

この記事はオリジナルであり、グレープシティ技術チームによって最初に公開されました。転載する場合は出典を明記してください:グレープシティ公式ウェブサイト. グレープシティは、開発者に力を与えるための専門的な開発ツール、ソリューション、サービスを開発者に提供します。

序文

この記事の内容は主に次の部分に焦点を当てています。

  1. TCP/IP についての簡単な紹介。
  2. メッセージのご紹介。
  3. メッセージ分類 (ストリーム タイプと XML タイプ) に基づいたトランスポート形式。
  4. メッセージシステムの構成。

TCP/IP の簡単な紹介

TCP/IP (伝送制御プロトコル/インターネット プロトコル) は、インターネットにおける基本的な通信言語またはプロトコルです。実際には 2 層のプログラムであり、高レベルと低レベルに分かれています。より高いレベルは伝送制御プロトコルで、情報を集約したり、ファイルを小さなパケットに分割したりする役割を果たします。これらのパケットはネットワークを介して受信側の TCP 層に送信され、受信側の TCP 層はパケットを元のファイルに復元します。下位層はインターネット プロトコルで、パケットが宛先に正しく到達できるように各パケットのアドレス部分を処理します。ネットワーク上のゲートウェイ コンピューターは、そのアドレスに基づいて情報をルーティングします。同じファイルからのサブパケットであっても、異なる方法でルーティングされる可能性がありますが、最終的には宛先に収束します。TCP/IP は通信にクライアント/サーバー モデルを使用します。

アーキテクチャ的には、TCP/IP は 0SI の 7 層参照モデルに完全には準拠していません。従来のオープン システム相互接続参照モデルは、通信プロトコルの 7 層の抽象参照モデルであり、各層が特定のタスクを実行します。このモデルの目的は、さまざまなハードウェアが同じレベルで相互に通信できるようにすることです。これらの 7 つの層は、物理層、データリンク層、ネットワーク層、トランスポート層、セッション層、プレゼンテーション層、およびアプリケーション層です。TCP/IP通信プロトコルは4層の階層構造を採用しており、各層は次の層が提供するネットワークを呼び出して独自のニーズを満たします。これら 4 つの層は次のとおりです。

  • アプリケーション層: シンプル メール転送プロトコル (SMTP)、ファイル転送プロトコル (FTP)、リモート ネットワーク アクセス プロトコル (Telnet) など、アプリケーション間の通信のための層。
  • トランスポート層:ノード間のデータ伝送やアプリケーション間の通信サービスを提供する層で、主な機能はデータのフォーマット化、データの確認、欠損再送などです。伝送制御プロトコル (TCP)、ユーザー データグラム プロトコル (UDP) など TCP および UDP は、データ パケットに送信データを追加して次の層に送信します。この層は、データの送信と、データが送信されたかどうかの判断を担当します。配達されました。受け取ります。
  • 相互接続ネットワーク層: インターネット プロトコル (IP) など、各データ パケットが宛先ホストに到達できるようにするための基本的なデータ パケット送信機能の提供を担当します (ただし、正しく受信されたかどうかはチェックしません)。
  • ネットワーク インターフェイス層 (ホスト ネットワーク層): IP データグラムの受信と送信、ネットワークからの物理フレームの受信、IP データグラムの抽出と次の層への転送、実際のネットワーク メディアの管理、および実際のネットワークの使用方法の定義 (イーサネット、シリアル回線など)を使用してデータを送信します。

Tcp/IPでよく使われる関数

1.ソケット機能

int socket(int domain,int type,int protocol),

ドメインは使用するプロトコル スイートを指定します。通常は、インターネット プロトコル スイート (TCP/IP プロトコル スイート) を表す PF INET、type パラメーターはソケットのタイプを指定します、TCP の場合は SOCK STREAM、UDP の場合は SOCK DGRAM、プロトコルには通常、値が割り当てられます。 [0]の。ソケット関数呼び出しは整数のソケット記述子を返します。これは後で呼び出すことができます。

2.バインド関数:

バインド関数は、ソケットをローカル マシン上のポートに関連付け、そのポートでサービス リクエストをリッスンします。バインド関数のプロトタイプは次のとおりです。

int bind(int sockfd,struct sockaddr *my addr, int addrlen);

sockfd は、socket 関数の呼び出しによって返されるソケット記述子です。my addr は、ローカル IP アドレスやポート番号などの情報を含む sockaddr 型へのポインターです。addrlen は、多くの場合、sizeof (struct sockaddr) に設定されます。

3.connect接続機能:

接続指向クライアント プログラムは、connect 関数を使用してソケットを構成し、リモート サーバーとの TCP 接続を確立します。その関数プロトタイプは次のとおりです。

int connect(int sockfd, struct sockaddr *serv addr,int addrlen);

sockfd はソケット関数によって返されるソケット記述子、serv addr はリモート ホストの IP アドレスとポート番号を含むポインタ、addrlen はリモート アドレス構造の長さです。connect 関数は、エラーが発生すると -1 を返し、errno に対応するエラー コードを設定します。クライアント プログラムを設計するときに、bind 0 を呼び出す必要はありません。この場合、必要なのは宛先マシンの IP アドレスだけであり、クライアントは、クライアントとの接続を確立するためにどのポートを使用するかを気にする必要がありません。ソケット実行プログラムは、空いているポートを自動的に選択し、データがそのポートに到着するとプログラムに通知します。

4.リッスンリスニング機能:

ネットワーク リスニング (リッスン) 機能は、ソケットをパッシブ リスニング モードにし、ソケットの入力データ キューを確立し、到着したサービス要求をプログラムが処理するまでこのキューに保存します。

int listen(int sockfd, int backlog);

sockfd は、Socket システム コールによって返されるソケット記述子です。backlog は、リクエスト キューで許可されるリクエストの最大数を指定します。受信接続リクエストは、受信関数のキューで待機します。accept 0) (以下を参照)。バックログは、キュー内でサービスを待機しているリクエストの数を制限します。通常、システムのデフォルト値は 20 です。サービス要求が到着し、入力キューがいっぱいの場合、ソケットは接続要求を拒否し、クライアントはエラー メッセージを受け取ります。

5.受信機能を受け入れる:

accept0 関数を使用すると、サーバーはクライアントの接続要求を受け入れることができます。入力キューを確立した後、サーバーは accept 関数を呼び出し、スリープしてクライアントの接続要求を待ちます。

int accept(int sockfd, void *addr, int *addrlen);

sockfd は監視対象ソケット記述子、addr は通常、接続要求サービス (ホストは特定のポートから要求を送信する) を行うホストに関する情報を格納するために使用される sockaddr_in 変数へのポインタ、addrlen は通常、次へのポインタです。値が sizeof (struct sockaddr in) である整数ポインター変数。エラーが発生すると、accept 関数は -1 を返し、対応する errno エラー コードを設定します。

6.sendto関数とrecvfrom関数:

int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to, int tolen):

to は宛先マシンの IP アドレスとポート番号の情報を表し、tolen には sizeof (struct sockaddr) の値が割り当てられることがよくあります。sendto 関数は、送信されたデータ バイトの実際の長さを返すか、送信エラーの場合は -1 を返します。

int recyfrom(int sockfd,void *buf,int len,unsigned int flags,structsockaddr *from,int *fromlen);

from は、送信元ホストの IP アドレスとポート番号を保存する struct sockaddr 型の変数です。fromlen は、sizeof (struct sockaddr) に設定されることが多く、recvfrom() が返されるとき、fromlen には実際に from に格納されているデータ バイト数が含まれます。recvfrom() 関数は、受信したバイト数を返すか、エラーが発生した場合は -1 を返し、対応する errno エラー コードを設定します。

7.シャットダウン機能

ソケットを閉じるシャットダウン関数。この機能を使用すると、一方向のデータ転送を継続しながら、一方向のデータ転送のみを停止できます。

int shutdown(int sockfd,int how);

sockfd は、閉じる必要があるソケットの記述子です。how パラメータを使用すると、シャットダウン操作に次の方法を選択できます。

  • 0-1 データの受信の継続を許可しません
  • 1 -- データの送信を継続しない
  • 2-それ以上のデータの送受信を許可しない

shutdown は、操作が成功した場合は 0 を返し、エラーが発生した場合は -1 を返し、対応する errno エラー コードを設定します。

8.fcntl関数

fcntl 関数は、開いているファイルのプロパティを変更できます。

int fcntl (int fields, int cmd, .../* int arg */) ;

9.getsockopt関数とsetsockopt関数

これら 2 つの関数は、ソケットに関連付けられたオプションを取得または設定できます。ソケット層オプションを操作するには、層値を SOL SOCKET として指定する必要があります。他の層のオプション制御オプションを操作するには、適切なプロトコル番号を指定する必要があります。たとえば、オプションが TCP によって解析されることを示すには、レイヤーをプロトコル番号 TCP に設定する必要があります。

int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen);
int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);

10.機能選択

select関数は、多重化に使用されるシステムコールまたは関数です。通常、複数の入出力ストリームを処理して非同期 I/O 操作を実装するために使用されます。

int select(int n, fd set * readfds, fd set * writefds, fd set * exceptfds,struct timeval * timeout);

パラメータ n は、最大のファイル記述子に 1 を加えたものを表します。パラメータ readfds、writefds、およびExceptfds は記述子グループと呼ばれ、記述子の読み取り、書き込み、または例外ステータスを返すために使用されます。

11.投票機能

int poll(struct pollfd fds[], nfds t nfds, int timeout);

このうち、fds は、状態を検出する必要があるソケット記述子を格納するために使用される、struct pollfd 構造体型の配列です。struct pollfd の定義は次のとおりです。

struct pollfd {
        //descriptor to check
        int fd;
        //events of interest on fd
        short events;
        //events that occurred on fd
        short revents;
}

ニュースは何ですか

メッセージは、分散アプリケーション開発においてネットワーク上の 2 つの論理エンティティ間で通信するときのプログラミング レベルの最小単位です。

上記の定義については、次のような説明があります。

(1) メッセージの概念は開発作業に存在し、プログラミング レベルにあります。システムの実行中は、アプリケーション ユーザーには透過的です。

(2) ネットワーク上の 2 つの論理エンティティとは、独立して実行できる 2 つのプログラムを指します。これらは、ネットワーク内の 2 つの異なる物理デバイスに展開することも、同じ物理デバイスに展開することもできますが、通常は 2 つの独立したプロセスです。親子関係 (これは、IPC プログラミングにおける最も基本的なメッセージの概念とは異なります)。

(3) メッセージは分散通信におけるプログラミングレベルの最小単位であり、通信に含まれるデータの量に関係なく、1 つまたは複数のメッセージを送受信することによってプログラム コードが実装されます。

(4) ネットワーク上の 2 つのアプリケーション間の通信には、データ ストリーム送信とリモート プロシージャ (関数) 呼び出しの 2 種類があります。

(5) メッセージを使用して、分散アプリケーション間で構造化されたデータ通信を実現できます。言い換えれば、プログラマーが通信レベルで直面するのは、もはや実際のバイト ストリームではなく、複数のデータ型を組み合わせることができる構造化データ ユニットです。

実際、この構造化されたデータ単位自体は「メッセージ」であり、外部的には構造体またはクラスとして表現できます。したがって、上記の定義に基づくメッセージ メカニズムが確立されると、プログラマはコーディング プロセス中に分散通信が必要な場合に対応するメッセージを生成し、対応する送信インターフェイスと受信インターフェイスを呼び出すだけで簡単に実装できます。 TCP/IP の知識を理解し、ソケット プログラミングの基本スキルを習得します。また、多すぎるシリアル メッセージ、多すぎる同時メッセージ、ネットワーク フロー制御などの他の問題を考慮する必要がないため、分散アプリケーションを真に実現できます。ビジネス実装に開発努力が集中するため、分散システム、特に大規模分散システムの開発効率と品質が大幅に向上します。

メッセージの存在形式は、従来の C 言語では構造体 struct、オブジェクト指向言語 (C++ または Java) ではクラスになります。

メッセージ分類に基づく送信フォーマット

メッセージ送信の形式の違いに基づいて、メッセージはストリーム メッセージと XML メッセージに分類できます。ストリーム メッセージはバイナリ バイト ストリーミング形式に基づいて送信され、XML メッセージは XML 形式の文字列に基づいて送信されます。

ストリーミングメッセージ

ストリーム メッセージとは、コンピュータ システム内でストリーム (ストリーム) 形式で送信および処理されるメッセージを指します。ストリームメッセージは、送信側で一定の順序で生成され、ストリームの形で受信側に送信される一連の連続データで構成されます。送信プロセス中に、受信側はストリーム内のデータを 1 つずつ読み取ることができます。ストリーム メッセージの場合、プログラマがメッセージをどのように表現しても、メッセージは実際に送信される前にバイナリ ストリーム形式に変換する必要があります。この変換プロセスはストリーム化、またはシリアル化と呼ばれます。

XMLメッセージ

XML メッセージングとは、メッセージ形式として Extensible Markup Language (XML) を使用するデータ送信方法を指します。XML はデータの記述と保存に使用されるテキスト マークアップ言語であり、タグを使用してデータの構造とプロパティを定義します。XMLメッセージの仕組みでは、プログラマがメッセージの内容をXML形式で表現した後、送受信時の形式変換作業(安全に送信するための暗号化作業を除く)を行う必要がなく、そのままXML文字列形式で送信することができます。 。XML メッセージも広く使用されており、たとえば Web サービスの SOAP プロトコルは XML メッセージに基づいて設計および実装されています。

例: ストリーミング メッセージに基づく設計と実装方法

以下の編集者が、2 つのアプリケーションで個人情報 (身長、名前、年齢など) を送受信する方法を簡単に紹介します。

(1) 人々の情報を保存するクラスを定義します。

struct Person {
        char name[20] ;
        float height;
        int age;
}
struct Person p;
strcpy(p.name ,"Michael Zhang");
height = 170.00;
age = 30;

(2) 情報列の構造化

char sendStream[1024] = {0};
sprintf(sendStream,"|%s|%f"%d",p.name, p.height, p.age);

(3) 送信者はバイト ストリームを送信します。

/*注: 这里省略建立/管理/关闭 TCP 连接的代码*/
char datalen[4+1] = (0);
sprintf(datalen,"04d" , strlen (sendStream) );
if(SendBytes ( socket, datalen, 4) == -1) {
        return -l;
}
if(SendBytes(socket, sendStream, strlen(sendStream)) == -1) {
        return -1
}

上記のコードの SendBytes 関数は、実際には、戻る前に特定の長さのすべてのバイト ストリームが正常に送信されることを保証していることに注意してください。これは主に、ソケット上で send または write 関数を呼び出しても、特定の長さのバイト ストリームが送信されるかどうかを保証できないためです。完全に一度に送信されます。SendBytes の基本的な考え方は、すべてのバイトが正常に送信されるまでループで送信することです。実装コードは次のとおりです。

int SendBytes (int sd, const void *buffer, unsigned len) {
        int rez = 0;
        int leftlen = len;
        int readlen = 0:
}
while(true) {
        rez = write (socket, (char *)buffer+readlen, len-readlen);
        if(rez < 0) {
                if (errno != EWOULDBLOCK && errno != EINTR) {
                        ErrorMsg("Error is serious );
                        DisConnect(socket);
        }
    return -l:
    }
    readlen += rez;
    leftlen -= rez;
    if(leftlen <= 0){
    break;
    }
   }
return len:
}

(4) 受信側はバイト ストリームを受信します。

char datalen[4+1] = {0};
char receiveStream[1024] = {0};
sprintf(datalen,"%04d", strlen(sendStream)) ;
if(ReceiveBytes(socket, datalen, 4) == -1 {
        return -l;
}
int packet len = atoi(datalen) :
if(ReceiveBytes (socket, receiveStream, packet len) == -1) {
        return -l;
}

ReceiveBytes 関数は、送信者がバイト ストリームを送信する 3 番目のステップを参照できます。

(5) バイト ストリームが逆シリアル化されて、次の構造が取得されます。

struct Person p;
sscanf(receiveStream,"%[`|]|%f|%d", p.name, &p.height, &p.age) ;

要約する

この記事では、TCP/IP プロトコルとその一般的に使用されるインターフェイス機能を簡単に紹介し、次に TCP/IP プロトコルでのメッセージの分類と送信形式を紹介し、最後にメッセージ送信の簡単な例で終わります。コンテンツについてコメントや提案がある場合は、メッセージを残してコメント エリアで議論してください。

参考書籍:『メッセージの設計と開発 - 分散アプリケーション開発のコアテクノロジー』何暁超著

拡張リンク:

フォーム駆動からモデル駆動へ、ローコード開発プラットフォームの開発トレンドを読み解く

ローコード開発プラットフォームとは何ですか?

ブランチベースのバージョン管理は、プロジェクトの提供からカスタマイズされた製品開発へのローコード移行を支援します

Lei Jun: Xiaomi の新しいオペレーティング システム ThePaper OS の正式版がパッケージ化されました。Gome App の抽選ページのポップアップ ウィンドウは創設者を侮辱しています。Ubuntu 23.10 が正式にリリースされました。金曜日を利用してアップグレードするのもいいでしょう! Ubuntu 23.10 リリース エピソード: ヘイトスピーチが含まれていたため、ISO イメージが緊急に「リコール」されました 23 歳の博士課程の学生が Firefox で 22 年間続いた「ゴーストバグ」を修正しました RustDesk リモート デスクトップ 1.2.3 がリリースされましたWayland を強化して TiDB 7.4 をサポート リリース: MySQL 8.0 と正式互換. Logitech USB レシーバーを取り外した後、Linux カーネルがクラッシュしました. マスターは Scratch を使用して RISC-V シミュレータをこすり、Linux カーネルを正常に実行しました. JetBrains が Writerside ツールを開始しました技術文書の作成に。
{{名前}}
{{名前}}

おすすめ

転載: my.oschina.net/powertoolsteam/blog/10120032