1 問題の紹介
UNIX システムの I/O コマンド セットは、Maltics および初期のシステムのコマンドから進化しました。そのモードは、open-write-read-close (open-write-read-close) です。ユーザー プロセスが I/O 操作を実行するとき、最初に「open」を呼び出して指定されたファイルまたはデバイスを使用する権利を取得し、開いたファイルまたはデバイス上でユーザーが何を行っているかを記述するファイル記述子と呼ばれる整数を返します。 . I/O 操作のプロセス。このユーザー プロセスは、「読み取り/書き込み」を複数回呼び出してデータを転送します。すべての転送操作が完了すると、ユーザー プロセスは呼び出しを閉じて、オブジェクトを使用して完了したことをオペレーティング システムに通知します。
TCP/ip プロトコルが UNIX カーネルに統合されると、UNIX システムに新しいタイプの I/O 操作が導入されることになります。 UNIX ユーザー プロセスとネットワーク プロトコル間の対話は、ユーザー プロセスと従来の I/O デバイス間の対話よりもはるかに複雑です。まず、ネットワーク操作を実行する 2 つのプロセスが同じマシン上にあります。それらの間の接続を確立するにはどうすればよいでしょうか。次に、ネットワーク プロトコルは多数ありますが、複数のプロトコルをサポートするための汎用メカニズムを確立するにはどうすればよいでしょうか。これらはすべて、ネットワーク アプリケーション プログラミング インターフェイスが解決する必要がある問題です。
UNIX システムには、UNIX BSD のソケットと UNIX System V の TLI の 2 種類のネットワーク アプリケーション プログラミング インターフェイスがあります。 Sun が TCP/IP をサポートする UNIX BSD オペレーティング システムを採用して以来、TCP/IP のアプリケーションはさらに発展し、そのネットワーク アプリケーション プログラミング インターフェイスであるソケットはネットワーク ソフトウェアで広く使用され、マイクロコンピュータ オペレーティング システム DOS や DOS の導入にも使用されています。 Windows システムは、ネットワーク アプリケーション ソフトウェアを開発するための強力なツールとなっています。この章では、この問題について詳しく説明します。
2 ソケット プログラミングの基本概念 ソケット プログラミングの使用を開始する前に、まず次の概念を確立する必要があります。
2.1 ネットワーク間プロセス通信
プロセス通信の概念は、もともとスタンドアロン システムから生まれました。各プロセスは独自のアドレス範囲内で実行されるため、通信する 2 つのプロセスが相互に干渉せず、協調して動作するように、オペレーティング システムは、UNIX BSD のパイプなど、プロセス通信に対応する機能を提供します。パイプ(名前付きパイプ)やソフト割り込み信号(シグナル)、UNIXシステムVメッセージ(メッセージ)、共有記憶領域(共有メモリ)やセマフォ(セマフォ)などがありますが、これらはローカルプロセス間の通信内での使用に限定されます。 。インターネット プロセス通信は、異なるホスト プロセス間の相互通信の問題を解決することを目的としています (同じマシン上のプロセス通信は特殊なケースとみなすことができます)。そのためには、まずネットワーク間のプロセス識別の問題を解決する必要があります。同一ホスト上では、異なるプロセスをプロセス番号 (プロセス ID) によって一意に識別できます。しかし、ネットワーク環境では、各ホストが独自に割り当てたプロセス番号ではプロセスを一意に識別できません。たとえば、ホスト A はプロセス番号 5 を割り当てますが、プロセス番号 5 はホスト B にも存在する可能性があります。したがって、「プロセス番号 5」という文は意味がありません。
第 2 に、オペレーティング システムは多くのネットワーク プロトコルをサポートしています。プロトコルが異なれば動作方法も異なり、アドレス形式も異なります。したがって、ネットワーク間のプロセス通信では、複数のプロトコルを識別するという問題も解決する必要があります。
上記の問題を解決するために、TCP/IP プロトコルでは次の概念が導入されています。
ポート
ネットワーク内で名前を付けてアドレス指定できる通信ポートは、オペレーティング システムによって割り当てられるリソースです。
OSI 7 層プロトコルの説明によると、トランスポート層とネットワーク層の機能上の最大の違いは、トランスポート層がプロセス通信機能を提供することです。この意味で、ネットワーク通信の最終アドレスはホスト アドレスだけではなく、プロセスを説明できるある種の識別子でもあります。この目的を達成するために、TCP/IP プロトコルは、通信プロセスを識別するために使用されるプロトコル ポート (略してポート) の概念を提案しています。
ポートは抽象的なソフトウェア構造です (一部のデータ構造と I/O バッファーを含む)。アプリケーション(プロセス)がシステムコールを通じて特定のポートとの接続(バインディング)を確立した後、トランスポート層からポートに送信されたデータは、対応するプロセスによって受信され、対応するプロセスによってトランスポートに送信されます。レイヤーはポート経由で出力されます。 TCP/IP プロトコルの実装では、ポート操作は一般的な I/O 操作と似ており、このプロセスはポートを取得します。これは、一般的な読み取りおよびアクセスを使用してアクセスできるローカルの一意の I/O ファイルを取得することと同じです。のプリミティブを書きます。
ファイル記述子と同様に、各ポートにはポート番号と呼ばれる整数の識別子があり、異なるポートを区別するために使用されます。 TCP/IP トランスポート層の 2 つのプロトコル、TCP と UDP は完全に独立した 2 つのソフトウェア モジュールであるため、それぞれのポート番号も互いに独立しています。たとえば、TCP にはポート番号 255 があり、UDP には 255 というポート番号を持つこともできます。ポート番号 255。競合はありません。
ポート番号の割り当ては重要な問題です。基本的な配分方式は2つあり、1つはグローバル配分と呼ばれるもので、認定された中央機関が利用者のニーズに応じて一元的に配分を行い、その結果を公開する集中管理方式です。 2 番目のタイプは、動的接続とも呼ばれるローカル割り当てです。つまり、プロセスがトランスポート層サービスにアクセスする必要がある場合、オペレーティング システムがローカルの一意のポート番号を返し、プロセスが接続します。適切なシステム コールを通じてポートに自身をリンク (結合) します。 TCP/IP ポート番号の割り当てでは、上記の 2 つの方法が組み合わされます。 TCP/IP はポート番号を 2 つの部分に分割し、そのうちの少数のポートは予約ポートであり、グローバルな方法でサービス プロセスに割り当てられます。したがって、各標準サーバーには世界的に認識されるポート (ウェルノウン ポート) があり、そのポート番号は、サーバーが同じマシン上にある場合でも同じです。残りのポートは空きポートであり、ローカルに割り当てられます。 TCP と UDP はどちらも、256 未満のポート番号のみを予約ポートにできると規定しています。
アドレス
ネットワーク通信では、通信する 2 つのプロセスは異なるマシン上にあります。相互接続ネットワークでは、2 つのマシンが異なるネットワーク上に存在することがあり、これらのネットワークはネットワーク相互接続デバイス (ゲートウェイ、ブリッジ、ルーターなど) を介して接続されます。したがって、次の 3 レベルのアドレス指定が必要です。
1. 特定のホストは複数のネットワークに接続でき、特定のネットワーク アドレスを指定する必要があります。
2. ネットワーク上の各ホストは独自のアドレスを持つ必要があります。ホスト上の各プロセスには、そのホスト上で一意の識別子が必要です。
通常、ホスト アドレスはネットワーク ID とホスト ID で構成され、TCP/IP プロトコルでは 32 ビットの整数値で表されます。TCP と UDP はどちらも 16 ビットのポート番号を使用してユーザー プロセスを識別します。
ネットワークバイトオーダー
異なるコンピューターは、マルチバイト値を異なる順序で格納します。一部のマシンは開始アドレスに下位バイトを格納します (下位バイトが最初)。また、一部のマシンは上位バイトを格納します。 (上位バイトが最初に保存されます)。データの正確性を確保するには、ネットワーク プロトコルでネットワーク バイト オーダーを指定する必要があります。 TCP/IP プロトコルは、プロトコル ヘッダー ファイルに含まれる 16 ビット整数と 32 ビット整数の高コストのストレージ形式を使用します。
コネクション
2 つのプロセス間の通信リンクはコネクションと呼ばれます。接続はそれ自体をいくつかのバッファーおよび一連のプロトコル メカニズムとして示し、接続がない場合よりも高い信頼性を外部的に示します。
準関連
要約すると、ネットワーク内のトリプレットはプロセスをグローバルに一意にマークできます:
(プロトコル、ローカルアドレス、ローカルポート番号)
そのようなトリプレットはハーフハーフアソシエーションと呼ばれ、接続の各半分を指定します。
完全な相関
完全なネットワーク間プロセス通信は 2 つのプロセスで構成される必要があり、同じ高レベル プロトコルのみを使用できます。つまり、通信の一方の端で TCP プロトコルを使用し、もう一方の端で UDP プロトコルを使用することは不可能です。したがって、完全なネットワーク間通信には、以下を識別するための 5 つのタプルが必要です:
(プロトコル、ローカル アドレス、ローカル ポート番号、リモート アドレス、リモート ポート番号)
このような 5 つのタプルは、アソシエーションと呼ばれます。同じプロトコルを持つ 2 つの準相関を適切な相関に結合することも、接続を完全に指定することもできます。
2.2 サービスモード
ネットワーク階層構造では、各層は厳密に一方向に依存しており、各レベルでの分業とコラボレーションはフェーザー間のインターフェイスに集中しています。 「サービス」は、フェーザー間の関係、つまりネットワーク内の各層によってすぐ上の層に提供される一連の操作を記述する抽象的な概念です。下位層はサービスプロバイダーであり、上位層はサービスを要求するユーザーです。サービスは、システム コールやライブラリ関数などのプリミティブで表現されます。システム コールは、オペレーティング システム カーネルによってネットワーク アプリケーションまたは高レベル プロトコルに提供されるサービス プリミティブです。ネットワークの n 層は、n-1 層よりも常に n+1 層により完全なサービスを提供する必要があります。そうでない場合、n 層は存在価値を持ちません。
OSI 用語では、ネットワーク層とその下の層は通信サブネットとも呼ばれ、ポイントツーポイント通信のみを提供し、プログラムやプロセスの概念はありません。トランスポート層は「エンドツーエンド」通信を実装し、ネットワーク間プロセス通信の概念を導入します。また、エラー制御、フロー制御、データ分類 (メッセージ分類)、接続管理などの問題を解決する必要があります。この目的のために、さまざまなサービス方法を提供します。
コネクション指向 (仮想回線) またはコネクションレス
コネクション指向のサービスは、電話システムのサービス モデルを抽象化したものです。つまり、完全なデータ送信はすべてこのプロセスを通過する必要があります。接続の確立、接続の使用、接続の終了。データ送信プロセス中、各データ パケットには宛先アドレスは含まれませんが、接続番号 (接続 ID) が使用されます。本質的に、接続はパイプであり、送受信されるデータは同じ順序であるだけでなく、同じ内容も含まれます。 TCP プロトコルは、接続指向の仮想回線を提供します。
コネクションレス サービスは、郵便システム サービスを抽象化したもので、各パケットは完全な宛先アドレスを運び、各パケットはシステム内で独立して送信されます。コネクションレス型サービスでは、パケットの順序は保証できず、パケットエラーの回復や再送もできず、送信の信頼性も保証されません。 UDP プロトコルは、コネクションレス データグラム サービスを提供します。
これら 2 つのサービスの種類とそのアプリケーションの例を以下に示します。
サービスの種類
サービス
例
接続指向
信頼性の高いメッセージ フロー
信頼性の高いバイト ストリーム
信頼性の低い接続
ファイル転送 FTP 書留郵便
ネットワーク データベース クエリ
シーケンス
ネットワーク送信では、連続する 2 つのメッセージがエンドツーエンド通信で異なるパスを通過する可能性があるため、宛先に到着したときの順序と送信されたときの順序が異なる場合があります。 「シーケンス」とは、データを受信する順序とデータを送信する順序が同じであることを意味します。 TCP プロトコルはこのサービスを提供します。
エラー制御
アプリケーションが受信したデータにエラーがないことを保証するメカニズム。エラーをチェックする方法は、一般に「チェックサム」方法です。エラーのない送信を保証する方法は、双方が確認応答テクノロジーを使用することです。 TCP プロトコルはこのサービスを提供します。
フロー制御
データが失われないように、データ送信中にデータ送信速度を制御するメカニズム。 TCP プロトコルはこのサービスを提供します。
バイトストリーム
バイトストリームメソッドは、送信されたメッセージをバイトシーケンスとして扱うことのみを指し、データストリームに境界を提供しません。 TCP プロトコルはバイト ストリーム サービスを提供します。
メッセージ
受信者は送信者のメッセージ境界を保存する必要があります。 UDP プロトコルはメッセージ サービスを提供します。
全二重/半二重
エンドツーエンドのデータは同時に双方向/一方向に送信されます。
キャッシュ/帯域外データ
バイトストリームサービスでは、メッセージ境界がないため、ユーザープロセスは特定の時間に任意の数のバイトを読み書きできます。正しい送信を保証するため、またはフロー制御のあるプロトコルを使用する場合は、キャッシュが必要です。ただし、対話型アプリケーションなどの特別なニーズでは、このキャッシュをキャンセルする必要がある場合があります。
データ送信の過程で、タイムリーに処理するための従来の送信方法ではユーザーに送信されることが予期されていない特定の種類の情報(UNIX システムの割り込みキー (Delete または Control-c) など)帯域外データと呼ばれる端末フロー制御文字 (Control-s および Control -q)。論理的には、ユーザー プロセスは独立したチャネルを使用してこのデータを送信しているように見えます。このチャネルは、接続されたストリームの各ペアに関連付けられます。 Berkeley Software Distribution での帯域外データの実装は RFC 1122 で指定されているホスト契約と矛盾しているため、相互運用性の問題を最小限に抑えるために、アプリケーション作成者は既存のサービスと相互運用するときに帯域外データを要求してはなりません。使わないほうがいいですよ。
2.3 クライアント/サーバー モデル
TCP/IP ネットワーク アプリケーションでは、2 つの通信プロセス間の対話の主なモードはクライアント/サーバー モデルです。つまり、クライアントはサービス リクエストをサーバーに送信し、サーバーはサービス リクエストを受信します。要求に応じて、対応するサービスが提供されます。クライアント/サーバー モデルの確立は、次の 2 つの点に基づいています。 まず、ネットワークを確立する理由は、ネットワーク内のソフトウェアとハードウェアのリソース、コンピューティング能力と情報が等しくなく、共有する必要があるため、ホストを作成します。サービスを提供するためのリソースが豊富であるのに対し、サービスを要求するためのリソースが少ない顧客は、この非相互効果を引き起こします。第 2 に、インターネット上のプロセス通信は完全に非同期であるため、相互に通信するプロセス間には親子関係も共有メモリ バッファも存在しません。そのため、通信してデータを提供するプロセス間の接続を確立するメカニズムが必要です。 2 つの間の同期。これはクライアント/サーバー モードに基づく TCP/IP です。
クライアント/サーバーモードのキー操作プロセスは、アクティブリクエストメソッドを採用しています:
まず、サーバーを起動し、リクエストに従って対応するサービスを提供する必要があります:
1. 通信チャネルを開き、ローカルホストに通知します。特定の認識されたアドレスで顧客リクエストを受信します (FTP は 21 です)。 2. 顧客リクエストがこのポートに到着するのを待ちます。
3. 繰り返しのサービスリクエストを受信し、リクエストを処理します。そして応答信号を送信します。同時サービス要求を受信すると、顧客要求を処理するために新しいプロセス (UNIX システムの fork や exec など) をアクティブ化する必要があります。新しいプロセスはこのクライアント要求を処理し、他の要求に応答する必要はありません。サービスが完了すると、この新しいプロセスとクライアントの間の通信リンクが閉じられ、終了します。
4. ステップ 2 に戻り、別の顧客リクエストを待ちます。
5. サーバーをシャットダウンします
クライアント側:
1. サーバーが配置されているホストの特定のポートに接続します。
2.応答を待って受信します。リクエストの送信を続けます...
3. リクエストが完了すると、通信チャネルが閉じられ、終了します。
上記のプロセスからわかります:
1. クライアント プロセスとサーバー プロセスの役割は非対称であるため、エンコーディングが異なります。
2. サービス プロセスは通常、ユーザーの要求に応じて最初に開始されます。このサービスプロセスは、システムが稼働している限り、正常終了または強制終了するまで存在します。
2.4 ソケットの種類
TCP/IPソケットは以下の3種類のソケットを提供します。
ストリーミングソケット (SOCK_STREAM)
データはエラーや重複なく送信され、送信された順序で受信されます。組み込みのフロー制御により、データ フローが制限を超えることがなくなり、データは長さ制限のないバイト ストリームと見なされます。ファイル転送プロトコル (FTP) はストリーミング ソケットを使用します。
データグラムソケット(SOCK_DGRAM)
はコネクションレス型サービスを提供します。データ パケットは独立したパケットの形式で送信され、エラーがないという保証は提供されず、
データが失われたり重複したり、順番が狂って受信される可能性があります。ネットワーク ファイル システム (NFS) はデータグラム ソケットを使用します。
Raw ソケット (SOCK_RAW)
このインターフェイスにより、IP や ICMP などの下位層プロトコルへの直接アクセスが可能になります。通常、新しいプロトコルの実装を検証したり、既存のサービスで構成された新しいデバイスにアクセスしたりするために使用されます。
3 つの基本的なソケット システム コール
ソケット プログラミングの原理をよりよく説明するために、いくつかの基本的なソケット システム コール手順を以下に示します。
3.1 ソケットの作成──socket()
ソケットを使用する前に、システムはソケットを作成する手段をアプリケーションに提供するために、最初にソケットを持っている必要があります。呼び出し形式は次のとおりです。
SOCKET PASCAL FARソケット(int af, int type, int protocol);
この呼び出しは、af、type、protocolの3つのパラメータを受け取る必要があります。パラメータ af は、通信が行われるエリアを指定します。UNIX システムでサポートされるアドレス ファミリは、AF_UNIX、AF_INET、AF_NS などです。一方、DOS および WINDOWS は、インターネット エリアである AF_INET のみをサポートします。したがって、アドレス ファミリはプロトコル ファミリと同じです。 type パラメータは、確立されるソケットのタイプを記述します。プロトコル パラメータは、ソケットで使用される特定のプロトコルを示します。呼び出し側が使用するプロトコルを指定したくない場合は、0 に設定され、デフォルトの接続モードが使用されます。これら 3 つのパラメータに基づいてソケットを確立し、対応するリソースをそれに割り当て、整数のソケット番号を返します。したがって、socket() システムコールは実際には、関連する 5 つの要素の「プロトコル」要素を指定します。
socket()の詳しい説明は5.2.23を参照してください。
3.2 ローカルアドレスの指定──bind()
socket()でソケットを作成すると、名前空間(アドレスファミリー)は存在しますが、名前は付けられません。 bind() は、ソケット アドレス (ローカル ホスト アドレスとローカル ポート アドレスを含む) を作成されたソケット番号に関連付けます。つまり、ローカルの準関連性を指定するためにソケットに名前を付けます。呼び出し形式は次のとおりです:
int PASCAL FAR binding(SOCKET s, const strUCt sockaddr FAR * name, int namelen);
パラメータ s は、socket() 呼び出しによって返された、接続されていないソケット記述子 (socket) です。 。 番号)。パラメータ名はソケットに割り当てられたローカルアドレス (名前) であり、その長さは可変であり、その構造は通信ドメインによって異なります。 namelen は名前の長さを示します。
エラーが発生しない場合、bind() は 0 を返します。それ以外の場合は、戻り値 SOCKET_ERROR が返されます。
アドレスは、ソケット通信の確立において重要な役割を果たします。ネットワーク アプリケーションの設計者は、ソケット アドレスの構造をよく理解しておく必要があります。たとえば、UNIX BSD には、TCP/IP プロトコルを使用する一連のデータ構造があります。
struct sockaddr_in{ /*AF_INET*/
u_short sin_port; bits ポート番号、ネットワークバイトオーダー*/
struct in_addr sin_addr; /*32 ビット IP アドレス、ネットワークバイトオーダー*/
char sin_zero[8] /*reserved*/
}
bind() について詳細な説明は5.2.2を参照してください。
3.3 ソケット接続の確立──connect() と accept()
これら 2 つのシステムコールは、完全な関連確立を完了するために使用され、そのうちの connect() は接続を確立するために使用されます。コネクションレス型のソケット プロセスでも connect() を呼び出すことができますが、この時点ではプロセス間で実際のメッセージ交換はなく、呼び出しはローカル オペレーティング システムから直接返されます。この利点は、プログラマがデータごとに宛先アドレスを指定する必要がなく、宛先ポートがどのソケットとも「接続」を確立していないデータグラムを受信した場合、そのポートが信頼できると判断できることです。操作する。 Accept() は、サーバーにクライアント プロセスからの実際の接続を待機させるために使用されます。
connect() の呼び出し形式は次のとおりです:
int PASCAL FAR connect(SOCKET s, const struct sockaddr FAR * name, int namelen);
パラメーター s は、接続を確立するためのローカル ソケット記述子です。パラメータ名は、相手のソケットのアドレス構造を記述するポインタを指します。相手のソケットアドレスの長さはnamelenで指定します。
エラーが発生しない場合、connect() は 0 を返します。それ以外の場合は、戻り値 SOCKET_ERROR が返されます。接続指向プロトコルでは、この呼び出しにより、ローカル システムと外部システム間の接続が実際に確立されます。
アドレス ファミリは常にソケット アドレス構造の最初の 2 バイトに含まれており、socket() 呼び出しを通じて特定のプロトコル ファミリに関連付けられているためです。したがって、bind() と connect() はパラメータとしてプロトコルを必要としません。
connect()の詳しい説明は5.2.4を参照してください。
accept() の呼び出し形式は次のとおりです:
SOCKET PASCAL FAR accept(SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen);
パラメータ s は、使用されるローカル ソケット記述子です。 accept( ) 呼び出しとして listen() を最初に呼び出す必要があります。 addr 接続されたエンティティのアドレスを受け取るために使用される、クライアントのソケット アドレス構造体へのポインタ。 addr の正確な形式は、ソケットの作成時に確立されたアドレス ファミリによって決まります。 addrlen は、クライアントのソケット アドレスの長さ (バイト数) です。エラーが発生しない場合、accept() は受信したソケットの記述子を表す SOCKET 型の値を返します。それ以外の場合は、戻り値 INVALID_SOCKET。
accept() は接続指向のサーバーに使用されます。パラメータ addr および addrlen には、クライアントのアドレス情報が格納されます。呼び出しの前に、パラメーター addr は初期値が空のアドレス構造体を指し、addrlen の初期値は 0 です。accept() を呼び出した後、サーバーは s 番号のソケットからのクライアント接続要求の受け入れを待機します。接続リクエストは、クライアントの connect() 呼び出しによって発行されます。接続要求が到着すると、accept() 呼び出しは、要求接続キューの最初のクライアント ソケットのアドレスと長さを addr と addrlen に入れ、s と同じ特性を持つ新しいソケット番号を作成します。新しいソケットを使用して、サーバーへの同時リクエストを処理できます。
accept() の詳細な説明については、5.2.1 を参照してください。
4 つのソケット システム コール、socket()、bind()、connect()、accept() により、完全な 5 要素の相関関係の確立を完了できます。 socket() は 5 つのタプル内のプロトコル要素を指定します。その使用法は、それがクライアントであるかサーバーであるか、および接続指向であるかどうかには関係ありません。 bind() は、5 つのタプルでローカル バイナリ、つまりローカル ホスト アドレスとポート番号を指定します。その使用法は、接続指向であるかどうかに関係します。サーバー側では、それに関係なく、bind() を呼び出す必要があります。接続指向かどうか; 主要な規律 規律 ユーザー側では、接続指向が採用されている場合、bind() は呼び出されない可能性がありますが、connect() を通じて自動的に完了できます。コネクションレスが使用されている場合、クライアントは、bind() を使用して一意のアドレスを取得する必要があります。
上記の説明はクライアント/サーバー モデルのみに当てはまります。実際、ソケットの使用は非常に柔軟です。従うべき唯一の原則は、プロセス通信の前に完全な相関関係を確立する必要があるということです。
3.4 接続の待機──listen()
この呼び出しは、サーバーに接続して接続を受け入れる意思があることを示すために使用されます。 listen() は accept() の前に呼び出す必要があり、その呼び出し形式は次のとおりです:
int PASCAL FAR listen(SOCKET s, int backlog);
Parameter s は確立されているがまだ確立されていないローカルソケット番号を識別します。接続されている場合、サーバーはサーバーからのリクエストを受信します。バックログはリクエスト接続キューの最大長を表し、キューに入れられるリクエストの数を制限するために使用されます。現在許可されている最大値は 5 です。エラーが発生しない場合、listen() は 0 を返します。それ以外の場合は、SOCKET_ERROR を返します。
listen() は、呼び出しプロセス中に binding() を呼び出していないソケットに必要な接続を完了し、一定の長さのバックログを持つ要求接続キューを確立できます。
listen() の呼び出しは、サーバーが接続リクエストを受信するための 4 つの手順のうちの 3 番目です。これは、socket() を呼び出してストリームソケットを割り当て、bind() を呼び出して s に名前を割り当てた後に呼び出され、accept() の前に呼び出す必要があります。
listen() の詳細な説明については、5.2.13 を参照してください。
セクション 2.3 で述べたように、クライアント/サーバー モデルには、反復サービスと同時サービスの 2 種類のサービスがあります。 accept() 呼び出しは、新しいソケット番号を返すため、同時サービスの実装に非常に便利です。その一般的な構造は次のとおりです。 )
error(“ソケットを作成できません”);
if (bind(initsockid,....)
error(“バインドエラー”);
if (listen(initsockid, 5) ) < 0)
error(“リッスンエラー”);
for (; {
newsockid = accept(initsockid, ...) /* ブロック*/
if (newsockid < ; 0)
error (“エラーを受け入れる”);
if (fork() == 0){ /* 子プロセス*/
do(newsockid); /* リクエストを処理します* /
exit(0) );
}
closesocket(newsockid) /* 親プロセス*/
}
このプログラムの実行の結果、newsockid がクライアントのソケットに関連付けられます。サブプロセスの開始後、継続するメイン サーバーの initsockid が閉じられ、新しい newsockid がクライアントとの通信に使用されます。メインサーバーの initsockid は、新しいクライアント接続要求を待ち続けることができます。 Unix などのプリエンプティブ マルチタスク システムでは、システム スケジューリングに従って複数のプロセスが同時に進行する可能性があるためです。したがって、並行サーバーを使用すると、サーバー プロセスに複数のサブプロセスが同時に接続され、異なるクライアント プログラムと通信できるようになります。クライアント プログラムの観点から見ると、サーバーは複数のクライアント要求を同時に処理できます。これがコンカレント サーバーという名前の由来です。
接続指向サーバーは重複サーバーになることもできます。その構造は次のとおりです。
int initsockid, newsockid;
if ((initsockid =ソケット(....))<0)
error( “ソケットを作成できません”);
if (bind(initsockid,....)<0)
error("バインドエラー");
if (listen(initsockid,5)<0)
error("リッスンエラー");
for (; {
newsockid = accept(initsockid, ...) /* ブロック*/
if (newsockid < 0)
error("エラーを受け入れる" );
do (newsockid); /* リクエストの処理*/
closesocket(newsockid);
中継サーバーは一度に 1 つのクライアント プログラムのみと接続を確立し、複数のクライアント プログラムを処理しますループ内で繰り返されるため、並行サーバーと重複サーバーにはそれぞれ利点と欠点があります。並行サーバーはクライアント プログラムの応答速度を向上させますが、重複サーバーはシステムのスケジューリングのオーバーヘッドを増加させるだけです。その逆なので、サーバーを二重化する場合は、
3.5 データ送信──send()とrecv()の後、使用するかどうかをユーザーが決定する必要があります。接続が確立されると、一般的に使用されるシステムコールは send.() と recv() です。
send() 呼び出しは、キー number で指定された接続されたデータグラムまたはストリームソケットに出力データを送信するために使用されます。次のように:
int PASCAL FAR send( SOCKET s, const char FAR *buf, int len, int flags);
パラメータ s は接続されたローカルソケット記述子であり、buf は送信されたソケットを含むバッファへのポインタです。 data を指定し、その長さを len で指定します。 帯域外データを送信するかどうかなどの送信制御方法を指定します。 エラーが発生しない場合、send() は送信された総バイト数を返します。 send() の詳細な説明は、recv() 呼び出しを使用して、キー s で指定された接続されたデータグラムまたはストリーム ソケットの入力データを受信します。 *buf, int len , int flags);
パラメータ s は接続されたソケット記述子です。 buf は入力データを受け取るバッファへのポインタで、その長さは len で指定されます。 flags は帯域外データを受信するかどうかなどの送信制御方法を指定します。エラーが発生しない場合、recv() は受信した合計バイト数を返します。接続が閉じられている場合は、0 が返されます。それ以外の場合は、SOCKET_ERROR を返します。
recv() の詳細については、5.2.16 を参照してください。
3.6 入出力多重化─select()
select() 呼び出しは、1 つ以上のソケットのステータスを検出するために使用されます。この呼び出しでは、ソケットごとに、読み取り、書き込み、またはエラーのステータス情報を要求できます。特定の状態を要求するソケットのセットは、fd_set 構造体によって示されます。戻り時に、この構造体は特定の条件を満たすソケットのサブセットを反映するように更新されます。同時に、select() 呼び出しは条件を満たすソケットの数を返します。呼び出し形式は次のとおりです:
int PASCAL。 FAR select (int nfds, fd_set FAR * readfds, fd_set FAR * writefds, fd_set FAR *Exceptfds, const struct timeval FAR * timeout);
パラメータ nfds は、チェックされるソケット記述子の値の範囲を指定します。この変数は通常無視されます。 。
パラメータ readfds は、読み取られてテストされるソケット記述子のセットへのポインタを指し、呼び出し元はそこからデータを読み取ることを望んでいます。パラメータ writefds は、書き込みをテストするソケット記述子のセットへのポインタです。 Exceptfds エラーを検出するためのソケット記述子のセットへのポインタ。 timeout は、select() 関数が待機する最大時間を指します。NULL に設定すると、ブロック操作になります。 select() は、fd_set 構造体に含まれる準備されたソケット記述子の総数を返すか、エラーが発生した場合は SOCKET_ERROR を返します。
select()の詳しい説明は5.2.18を参照してください。
3.7 ソケットを閉じる──closesocket()
closesocket() は、ソケット s を閉じ、ソケットに割り当てられたリソースを解放します。s にオープンな TCP 接続が含まれる場合、接続は解放されます。 closesocket() の呼び出し形式は次のとおりです:
BOOL PASCAL FAR closesocket(SOCKET s);
パラメータ s はクローズされるソケット記述子です。エラーが発生しない場合、closesocket() は 0 を返します。それ以外の場合は、戻り値 SOCKET_ERROR が返されます。
closesocket()の詳しい説明は5.2.3を参照してください。
2.4 典型的なソケット呼び出し処理の例
前述したように、TCP/IP プロトコルのアプリケーションは一般にクライアント/サーバー モードを採用しているため、実際のアプリケーションではクライアントとサーバーの 2 つのプロセスが存在し、サーバーが最初に起動され、そのシステムコールのタイミング図は次のとおりです。
接続指向プロトコル (TCP など) のソケット システム コールを図 2.1 に示します。
クライアント リクエストを受信するには、まずサーバーが accept() 呼び出しを完了して待機状態になるまで起動する必要があります。 。クライアントが以前に起動されていた場合、connect() はエラー コードを返し、接続は失敗します。
図 2.1 コネクション型ソケット システムコールのタイミング図
コネクションレス型プロトコルのソケット呼び出しを図 2.2 に示します。
図 2.2 コネクションレス型プロトコルのソケット呼び出しのタイミング図
コネクションレス型サーバーも起動する必要がありますまず、そうでない場合、顧客の要求はサービス プロセスに送信されません。コネクションレス型クライアントは connect() を呼び出しません。したがって、データが送信される前に、クライアントとサーバーの間に完全な相関関係は確立されていませんが、socket() と binding() を通じて準相関関係が確立されています。データを送信するとき、送信者はローカルのソケット番号に加えて受信者のソケット番号を指定する必要があるため、データの送受信プロセス中に完全な相関関係が動的に確立されます。
例
この例では、接続プロトコル指向のクライアント/サーバー モデルを使用しており、そのプロセスを図 2.3 に示します。
図 2.3 接続指向のアプリケーション フローチャート
サーバー側プログラム:
/* ファイル名: streams.c */
#include
#include
#define TRUE 1
/* このプログラムは、ループを通じて接続を受信するたびに、ソケットを確立し、無限ループを開始します。メッセージを印刷します。接続が切断されるか、終了メッセージを受信すると、接続は終了し、プログラムは新しい接続を受け取ります。コマンドラインの形式は次のとおりです: streams */
main(
{
int sock, length;
struct sockaddr_in server;
struct sockaddr tcpaddr;
int msgsock;
char 】 ;
int rval, len;
/* ソケットの作成*/
if (sock < 0) {
perror(") ;
exit(1);
}
/* ソケットに名前を付けるには任意のポートを使用します*/
server.sin_family =
server.sin_port = INADDR_ANY; struct sockaddr *)&server, sizeof(server)) < 0) {
exit(1);
/* 指定されたポート番号を見つけて出力しますそれを出力します */
length = sizeof(server);
if (getsockname(sock, (struct sockaddr *)&server, &length) < 0) {
perror(“ソケット名を取得しています”); 1);
}
printf(“ソケットポート#%d
”, ntohs(server.sin_port));
/* 接続の受信を開始します*/
len = sizeof(struct sockaddr);
do {
msgsock = accept(sock, (struct sockaddr *)&tcpaddr, (int *)&len);
if (msgsock == -1)
perror(“accept”) ;
else do{
memset(buf, 0, sizeof(buf));
if ((rval = recv(msgsock, buf, 1024)) < 0)
perror(“ストリームメッセージの読み取り”) ;
if (rval == 0)
printf(“接続を終了します
”);
else
}while (rval != 0) );
closesocket(msgsock);
} while (TRUE);
/* このプログラムにはすでに無限ループがあるため、ソケット「sock」が明示的に閉じられることはありません。ただし、プロセスが強制終了または正常に終了すると、すべてのソケットが自動的に閉じられます。 */
exit(0);
}
クライアントプログラム:
/* ファイル名: streamc.c */
#include
#include
#define DATA 「半分リーグ、半分リーグ」 ..."
/* このプログラムはソケットを確立し、コマンドラインで指定されたソケットに接続します。接続が終了すると、接続上で
メッセージを送信し、ソケットを閉じます。コマンドラインの形式は次のとおりです: streamc ホスト名 ポート番号
ポート番号はサーバー プログラムのポート番号と同じである必要があります*/
main(argc, argv)
int argc; []; (AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror(“ストリームソケットを開く”);exit(1);
} /* コマンドラインで指定した名前を使用してソケットに接続します*/
server.sin_family = AF_INET;
if ( hp == 0) {
fprintf(stderr, “%s: 不明なホスト
”, argv[1]);
}
memcpy((char*)&server.sin_addr, ( char*)hp->h_addr, hp->h_length);
sever.sin_port = htons(atoi(argv[2]));
if (connect(sock, (struct sockaddr*)&server, sizeof) (サーバー)) < 0) {
exit(3)
if (sock, DATA, sizeof(DATA));
perror(“ストリームソケットで送信”);
exit(0);
}
2.5 前のセクションでは、単純なソケットプログラムを紹介しました。例。この例から、ソケット プログラミングの使用にはほぼパターンがあることがわかります。つまり、すべてのプログラムがほぼ例外なく同じ関数を同じ順序で呼び出します。したがって、プログラムはこれらの関数を呼び出すだけで、通常のインターネット テストでソケット プログラムの設計の詳細を気にする必要はありません。
このセクションでは、上位層にいくつかの単純な機能を提供する一般的なネットワーク プログラム インターフェイスを紹介します。プログラマは、これらの機能を使用することで、インターネット テスト ネットワークでのほとんどのネットワーク データ送信を完了できます。これらの関数は、ソケット プログラミングを上位層から分離します。接続指向のストリーミング ソケットと非ブロッキング動作メカニズムを使用します。プログラムは、これらの関数を呼び出してネットワーク メッセージをクエリし、それに応じて応答するだけで済みます。これらの関数には以下が含まれます:
l InitSocketsStruct: ソケット構造を初期化し、サービス ポート番号を取得します。クライアントプログラムによって使用されます。
l InitPassiveSock: ソケット構造を初期化し、サービス ポート番号を取得し、メイン ソケットを確立します。サーバー プログラムによって使用されます。
l CloseMainSock: メインソケットを閉じます。サーバー プログラムによって使用されます。
l CreateConnection: 接続を確立します。クライアントプログラムによって使用されます。
l AcceptConnection: 接続を受け入れます。サーバー プログラムによって使用されます。
l CloseConnection: 接続を閉じます。
l QuerySocketsMsg: ソケット メッセージをクエリします。
l SendPacket: データを送信します。
l RecvPacket: データを受信します。
2.5.1 ヘッダー ファイル
/* ファイル名: tcpsock.h */
/* ヘッダー ファイルには、ソケット プログラムで一般的に使用されるシステム ヘッダー ファイルが含まれています (この例で指定されているヘッダー ファイルは、SCO Unix でのヘッダー ファイルです) , Unix の他のバージョンのヘッダー ファイルは若干異なる場合があります)、独自の 2 つのデータ構造とそのインスタンス変数、および提供する関数の説明を定義します。 */
#インクルード
#インクルード
#インクルード
#インクルード
#インクルード
#インクルード
#インクルード
#インクルード
#
#含める
# include
#include
typedef struct SocketsMsg{ /* ソケットメッセージ構造体*/
int AcceptNum; /* 受信を待っている外部接続があるかどうかを示します*/
int ReadNum; / * 接続数読み取りを待機している外部データを持つ */
int ReadQueue[32]; /* 読み取りを待機している外部データを持つ接続キュー */
int WriteNum /* データを送信できる接続の数 */
; int WriteQueue[32]; /* データを送信できる接続キュー*/
int ExceptNum; /* 例外のある接続の数*/
int ExceptQueue[32] /* 例外のある接続キュー*/
} ;
typedef struct Sockets { /* ソケット構造*/
int DaemonSock; /* データソケット数*/
int Sockets[64 ]; array*/
fd_set readfds, writefds, Excellentfds; /* 検出される読み取り可能、書き込み可能、および例外ソケットのセット*/
int Port; /* ポート番号* /
} Sockets; /* グローバル変数*/
SocketsStruct(char * サービス名) ;
int InitPassiveSock(char * サービス名) ;
int CreateConnection(struct in_) addr *sin_addr); acceptconnection(struct in_addr *int closeconnect); , void *buf, int size);
2.5.2 関数ソースファイル
/* ファイル名: tcpsock.c */
/* このファイルには 9 つの関数のソース コードが含まれており、いくつかの場所に中国語のコメントが含まれています*/
#include "tcpsock.h"
int InitSocketsStruct(char * servicename)
/* 成功した場合はソケット構造を初期化します。 return 1、そうでない場合はエラー コード (<0) を返します */
/* この関数は、アクティブなソケットのみを必要とするクライアント プログラムに使用され、サービス情報を取得するために使用されます。サービス
の定義は /etc/services ファイル*/
{
structservent *servrec;
struct sockaddr_in serv_addr;
if ((servrec = getservbyname(servicename, "tcp")) = = NULL ) {
return(-1);
}
bzero((char *)&Mysock, sizeof(Sockets)); /* ネットワークバイトのサービスポートOrder * /
return(1);
}
int InitPassiveSock(char * servicename)
/* パッシブソケットを初期化する場合は 1 を返し、それ以外の場合はエラーコード (<0) を返します。 /* この関数は、パッシブ ソケットを必要とするサーバー プログラムに使用され、サービス情報の取得に加えて、パッシブ ソケットも確立されます。 */
{
int mainsock, flag=1;
struct servent *servrec;
if ((servrec = getservbyname(servicename, "tcp")) {
return(-1);
bzero((char *)&Mysock, sizeof(Sockets));
Mysock.Port = servrec->s_port; /* ネットワークバイトオーダーのサービスポートif((mainsock = ソケット(AF_INET, SOCK_STREAM, 0)) < 0) {
return(-2)
}
bzero((char *)&serv_addr, sizeof(serv_addr)); .sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* 任意のネットワークインターフェース*/
if (bind(mainsock, (struct sockaddr)) *)&serv_addr, sizeof(serv_addr)) < 0) {
close(mainsock);
}
if (listen(mainsock, 5) == -1); * アクティブなソケットをパッシブなソケットに変え、接続を受信できるようにします*/
close(mainsock)
}
/* このソケットを非ブロッキングソケットとして設定します。 /
if (ioctl(mainsock, FIONBIO, &flag) == -1) {
close(mainsock);
}
Mysock.DaemonSock = mainsock; mainsock, &Mysock.readfds); /* メインソケットが「読み取り可能」であることを宣言します*/
FD_SET(mainsock, &Mysock.Exceptfds) /* メインソケット上の例外イベントへの関心を宣言します */
return(1) );
}
void CloseMainSock()
/* メインソケットを閉じ、その上のイベントの宣言をクリアします。プログラムの終了前にメインソケットを閉じることをお勧めします*/
close(Mysock.DaemonSock)
FD_CLR(Mysock.DaemonSock, &Mysock.readfds); , &Mysock.例外fds);
int CreateConnection(struct in_addr *sin_addr)
/* IPアドレスがsin_addrであるリモートホストへの接続を作成します。
Param: sin_addrはネットワークバイトオーダーでのIPアドレスを示します。
成功した場合はこの接続を示すソケット番号を返し、それ以外の場合はエラー コード (<0) を返します */
{
struct sockaddr_in server */
int tmpsock, flag=1; , i;
if ((tmpsock = ソケット(AF_INET, SOCK_STREAM, 0)) < 0)
server.sin_family =
server.sin_port; ;
server.sin_addr.s_addr = sin_addr->s_addr;
/* このソケットを非ブロッキングソケットとして設定します。 */
if (ioctl(tmpsock, FIONBIO, &flag) == -1) {
close (tmpsock);
return(-2);
/* サーバーに接続します。 */
if (connect(tmpsock, (struct sockaddr *)&server, sizeof(server)) < 0 ) {
if ((errno != EWOULDBLOCK) && (errno != EINPROGRESS)) {
/* エラー コードが EWOULDBLOCK および EINPROGRESS の場合、システムはソケットを閉じる必要はありません。後でソケットを開き続けます。 接続を確立します。 接続が正常に確立されたかどうかは、ソケットが「書き込み可能」かどうかを検出する select() 関数を使用して判断できます。 */
close(tmpsock);
return(-3); /* 接続エラー */
}
FD_SET(tmpsock, &Mysock.readfds);FD_SET(tmpsock, &Mysock.excelfds);
i = 0;
while (Mysock.Sockets[i] != 0) i++; /* 空のソケット位置を探します */
if (i >= 64) {
close(tmpsock);
return(-4); /* 接続が多すぎます */
}
Mysock.Sockets[i] = tmpsock;
Mysock.SockNum++;
return(i);
}
int AcceptConnection(struct in_addr *IPaddr)
/* 接続を受け入れます。成功した場合はデータ ソケット番号を返し、それ以外の場合は -1 を返します。 */
{
int newsock, len, flag=1, i;
struct sockaddr_in addr;
len = sizeof(addr);
bzero((char *)&addr, len);
if ((newsock = accept(Mysock.DaemonSock, &addr, &len)) == -1)
return(-1); /* エラーを受け入れます。 */
/* このソケットをノンブロッキングソケットとして設定します。 */
ioctl(newsock, FIONBIO, &flag);
FD_SET(newsock, &Mysock.readfds);
FD_SET(newsock, &Mysock.writefds);
FD_SET(newsock, &Mysock.excelfds);
/* パラメーターで IP アドレスを返します。 */
IPaddr->s_addr = addr.sin_addr.s_addr;
i = 0;
while (Mysock.Sockets[i] != 0) i++; /* 空のソケット位置を探します */
if (i >= 64) {
close(newsock);
return(-4); /* 接続が多すぎます */
}
Mysock.Sockets[i] = newsock;
Mysock.SockNum++;
return(i);
}
int CloseConnection(int Sockno)
/* Sockno で示された接続を閉じます。 */
{
int retcode;
if ((Sockno >= 64) (Sockno < 0) (Mysock.Sockets[Sockno] == 0))
return(0);
retcode = close(Mysock.Sockets[Sockno]);
FD_CLR(Mysock.Sockets[Sockno], &Mysock.readfds);
FD_CLR(Mysock.Sockets[Sockno], &Mysock.writefds);
FD_CLR(Mysock.Sockets[Sockno], &Mysock.excelfds);
Mysock.Sockets[Sockno] = 0;
Mysock.SockNum--;
return(retcode);
}
int QuerySocketsMsg()
/* クエリソケットメッセージ。成功した場合はメッセージ番号を返し、それ以外の場合は -1 を返します。
struct SockMsg に格納されるメッセージ情報。 */
{
fd_set rfds、wfds、efds;
int retcode、私;
struct timeval TimeOut;
rfds = Mysock.readfds;
wfds = Mysock.writefds;
efds = Mysock.excelfds;
TimeOut.tv_sec = 0; /* 立即復帰、妨げません。 */
TimeOut.tv_usec = 0;
bzero((char *)&SockMsg, sizeof(SockMsg));
if ((retcode = select(64, &rfds, &wfds, &efds, &TimeOut)) == 0)
return(0);
if (FD_ISSET(Mysock.DaemonSock, &rfds))
SockMsg.AcceptNum = 1; /* 一部のクライアントはサーバーを呼び出します。 */
for (i=0; i<64; i++) /* メッセージ内のデータ */
{
if ((Mysock.Sockets[i] > 0) && (FD_ISSET(Mysock.Sockets[ i], &rfds)))
SockMsg.ReadQueue[SockMsg.ReadNum++] = i;
}
for (i=0; i<64; i++) /* データ出力準備完了メッセージ */
{
if ((Mysock.Sockets[i] > 0) && (FD_ISSET(Mysock. Sockets[i], &wfds)))
SockMsg.WriteQueue[SockMsg.WriteNum++] = i;
}
if (FD_ISSET(Mysock.DaemonSock, &efds))
SockMsg.AcceptNum = -1; /* サーバーソケットエラー。 */
for (i=0; i<64; i++) /* エラーメッセージ */
{
if ((Mysock.Sockets[i] > 0) && (FD_ISSET(Mysock.Sockets[i) ], &efds)))
SockMsg.ExceptQueue[SockMsg.ExceptNum++] = i;
}
return(retcode);
}
int SendPacket(int Sockno, void *buf, int len)
/* パケットを送信します。成功した場合は送信データの数を返し、そうでない場合は -1 を返します */
{
int actlen;
if ((Sockno >= 64) (Sockno
return(0);
if ((actlen = send(Mysock.Sockets[Sockno], buf, len, 0))
return(-1);
return(actlen);
}
int RecvPacket(int Sockno, void *buf, int size)
/* パケットを受信します。成功した場合は受信データの数を返し、そうでない場合は接続
がピアによってシャットダウンされた場合は 0 を返し、それ以外の場合は 0-errno */
を返します。
{
int actlen;
if ((Sockno >= 64) (Sockno
return(0); recv(Mysock.Sockets[Sockno], buf, size, 0)) < 0)
return(0-errno); /* actlen は受信したデータの長さです、これは、接続が相手によって切断されたことを示します。 */
}
2.5.3 単純なサーバー プログラムの例
/* ファイル名:server.c */
/* これは、パッシブ ソケットを初期化した後、受信待ちをループします。接続。接続を受信すると、データ ソケットのシリアル番号とクライアントの IP アドレスが表示されます。データがデータ ソケットに到着すると、データを受信して、接続のデータ ソケットのシリアル番号と受信した文字列が表示されます。 */
#include "tcpsock.h"
main(argc, argv)
char **argv{
struct in_addr sin_addr;
チャーバフ[32];
/* サーバー プログラムの場合、多くの場合、ユーザーがプロセスを積極的に強制終了するか、システムがシャットダウンされた場合にのみ無限ループ状態になります。 kill を使用して強制終了されたサーバー プログラムの場合、メイン ソケットが閉じられず、リソースが積極的に解放されないため、その後のサーバー プログラムの再起動に影響を与える可能性があります。したがって、メインソケットを積極的に閉じることを習慣にしてください。次のステートメントにより、プログラムはまず CloseMainSock() 関数を実行して、SIGINT、SIGQUIT、SIGTERM などのシグナルを受信したときにメイン ソケットを閉じてから、プログラムを終了します。したがって、kill を使用してサーバー プロセスを強制終了する場合は、まず kill -2 PID を使用してサーバー プログラムにメイン ソケットを閉じるメッセージを与えてから、kill -9 PID を使用してプロセスを強制終了する必要があります。 */
(void) signal(SIGINT, CloseMainSock);
(void) signal(SIGTERM, CloseMainSock); ")) < 0) {
printf("InitPassiveSock: エラー コード = %d
", retcode);
exit(-1);
}
while (1) {
retcode = QuerySocketsMsg (); /* ネットワーク メッセージをクエリします*/
if (SockMsg.AcceptNum == 1) { /* 受信を待っている外部接続はありますか? */
retcode = AcceptConnection(&sin_addr);
printf("retcode = %d, IP = %s
", retcode, inet_ntoa(sin_addr.s_addr));
else if (SockMsg.AcceptNum = = -1) /* プライマリソケットエラー? */
printf("デーモン ソケット エラー。
");
for (i=0; i
if ((retcode = RecvPacket(SockMsg.ReadQueue[i], buf, 32)) > 0)
printf("sockno %d Recv string = %s
", SockMsg.ReadQueue[i], buf);
else /* 返されるデータ長はゼロで、接続が中断されていることを示します */
CloseConnection( SockMsg. ReadQueue[i]);
}
} /* end while */
}
2.5.4 簡単なクライアントプログラムの例
/* ファイル名: client.c */
/* クライアントプログラムは実行時には、まずデータ構造を初期化し、次にユーザーがコマンドを入力するのを待ちます。
send: 指定された接続にデータを送信します。
clos(e): 接続を指定します。
quit: クライアントプログラムを終了します
*/
#include "tcpsock.h"
int argc; char ** char cmd_buf [16]; s_addr = inet_addr("166.111.5.249") ; /* サーバープログラムを実行しているホストの IP アドレス */
if ((retcode = InitSocketsStruct("TestService")) < 0) { /* 初期化データ構造 * /
printf("InitSocketsStruct: エラー コード = %d
", retcode);
exit(1)
while (1) {
printf(">"); get(cmd_buf);
if (!strncmp (cmd_buf, "conn", 4)) {
retcode = CreateConnection(&sin_addr) /* 接続を作成します*/
printf("戻りコード: %d
", retcode);
else if( !strncmp(cmd_buf, "send", 4)) {
printf("%d", &sockno1); = SendPacket(sockno1, buf, 26); / * データ送信*/
printf("return code: %d
", retcode, sizeof(buf));
}
else if (!strncmp(cmd_buf, 「閉じる」、4)) {
printf("ソケット番号:");
scanf("%d", &sockno1); /* 接続を閉じます*/
printf("戻りコード: %d
", retcode );
}
else if (!strncmp(cmd_buf, "quit", 4))
exit(0); 記事については、PHP 中国語 Web サイト (www.php.cn) を参照してください。