C でのソケットプログラミング
ソケットプログラミング は、ネットワーク上の 2 つのノードを接続して相互に通信する方法です。 1 つのソケット (ノード) は IP の特定のポートで待機し、もう 1 つのソケットは他のソケットに接続して接続を形成します。クライアントがサーバーに接続している間、サーバーはリスナー ソケットを形成します。
ソケット プログラミングは、インスタント メッセージング アプリケーション、バイナリ ストリーミング、ドキュメント コラボレーション、オンライン ストリーミング プラットフォームなどで広く使用されています。
例
この C プログラムでは、クライアント/サーバー モデルを示すために、サーバーとクライアントの間で 1 つの hello メッセージを交換しています。
サーバー.c
C #include #include #include #include #include #include #define PORT 8080 int main ( int argc char const * argv []) { int server_fd new_socket ; ssize_t valread ; struct sockaddr_in address ; int opt = 1 ; socklen_t addrlen = sizeof ( address ); char buffer [ 1024 ] = { 0 }; char * hello = 'Hello from server' ; // Creating socket file descriptor if (( server_fd = socket ( AF_INET SOCK_STREAM 0 )) < 0 ) { perror ( 'socket failed' ); exit ( EXIT_FAILURE ); } // Forcefully attaching socket to the port 8080 if ( setsockopt ( server_fd SOL_SOCKET SO_REUSEADDR | SO_REUSEPORT & opt sizeof ( opt ))) { perror ( 'setsockopt' ); exit ( EXIT_FAILURE ); } address . sin_family = AF_INET ; address . sin_addr . s_addr = INADDR_ANY ; address . sin_port = htons ( PORT ); // Forcefully attaching socket to the port 8080 if ( bind ( server_fd ( struct sockaddr * ) & address sizeof ( address )) < 0 ) { perror ( 'bind failed' ); exit ( EXIT_FAILURE ); } if ( listen ( server_fd 3 ) < 0 ) { perror ( 'listen' ); exit ( EXIT_FAILURE ); } if (( new_socket = accept ( server_fd ( struct sockaddr * ) & address & addrlen )) < 0 ) { perror ( 'accept' ); exit ( EXIT_FAILURE ); } // subtract 1 for the null // terminator at the end valread = read ( new_socket buffer 1024 - 1 ); printf ( '%s n ' buffer ); send ( new_socket hello strlen ( hello ) 0 ); printf ( 'Hello message sent n ' ); // closing the connected socket close ( new_socket ); // closing the listening socket close ( server_fd ); return 0 ; }
client.c
C #include #include #include #include #include #define PORT 8080 int main ( int argc char const * argv []) { int status valread client_fd ; struct sockaddr_in serv_addr ; char * hello = 'Hello from client' ; char buffer [ 1024 ] = { 0 }; if (( client_fd = socket ( AF_INET SOCK_STREAM 0 )) < 0 ) { printf ( ' n Socket creation error n ' ); return -1 ; } serv_addr . sin_family = AF_INET ; serv_addr . sin_port = htons ( PORT ); // Convert IPv4 and IPv6 addresses from text to binary // form if ( inet_pton ( AF_INET '127.0.0.1' & serv_addr . sin_addr ) <= 0 ) { printf ( ' n Invalid address/ Address not supported n ' ); return -1 ; } if (( status = connect ( client_fd ( struct sockaddr * ) & serv_addr sizeof ( serv_addr ))) < 0 ) { printf ( ' n Connection Failed n ' ); return -1 ; } // subtract 1 for the null // terminator at the end send ( client_fd hello strlen ( hello ) 0 ); printf ( 'Hello message sent n ' ); valread = read ( client_fd buffer 1024 - 1 ); printf ( '%s n ' buffer ); // closing the connected socket close ( client_fd ); return 0 ; }
コンパイル中
gcc client.c -o clientgcc server.c -o server
出力Client:Hello message sentHello from serverServer:Hello from clientHello message sentソケットプログラミングのコンポーネント
1. ソケット
ソケット プログラムがネットワークにアクセスし、ネットワーク経由で他のプロセス/ノードと通信するために使用されるコア コンポーネントの 1 つです。これは、通信のエンドポイントとして機能する IP アドレスとポート番号の単純な組み合わせです。
例: 192.168.1.1:8080 ここで、コロンで区切られた 2 つの部分は、 IPアドレス(192.168.1.1) そして ポート番号( 8080 )。ソケットの種類:
- TCP ソケット (ストリーム ソケット): 信頼性の高い接続ベースの通信を提供します(つまり、 TCPプロトコル )。
- UDP ソケット (データグラム ソケット): コネクションレス通信は高速ですが、信頼性が低くなります(つまり、 UDPプロトコル )。
2. クライアントサーバーモデル
の クライアントサーバーモデル クライアントとサーバーが相互に対話して情報やサービスを交換するソケット プログラミングで使用されるアーキテクチャを指します。このアーキテクチャにより、クライアントはサービス リクエストを送信し、サーバーはそれらのサービス リクエストを処理して応答を送信できます。
サーバーおよびクライアント モデルの状態図
ソケットのサーバーおよびクライアント モデルの状態図 C でのソケット プログラミングは、ネットワーク通信を処理する強力な方法です。
サーバー側プロセスの作成
サーバーは次の手順で作成されます。
1. ソケットの作成
このステップには、socket() 関数を使用したソケットの作成が含まれます。
パラメータ:
- ソックスFD: ソケット記述子 (ファイルハンドルのような) 整数
- ドメイン: 整数は通信ドメインを指定します。同じホスト上のプロセス間の通信には、POSIX 標準で定義されている AF_LOCAL を使用します。 IPV4 で接続された異なるホスト上のプロセス間の通信には、IPV6 で接続されたプロセスには AF_INET と AF_I NET 6 を使用します。
- タイプ: コミュニケーションタイプ
SOCK_STREAM: TCP(信頼性のある接続指向)
SOCK_DGRAM: UDP(信頼性の低いコネクションレス) - プロトコル: インターネット プロトコル (IP) のプロトコル値。0 です。これは、パケットの IP ヘッダーのプロトコル フィールドに表示されるのと同じ番号です。(詳細については、man プロトコルを参照)
sockfd = socket ( domain type protocol )
2. ソケットオプションを設定する
これは、ファイル記述子 sockfd によって参照されるソケットのオプションを操作するのに役立ちます。これは完全にオプションですが、アドレスとポートの再利用に役立ちます。アドレスがすでに使用されているなどのエラーを防ぎます。
C setsockopt ( sockfd level optname optval socklen_t optlen );
3.バインド
ソケットの作成後、bind() 関数はソケットを addr(カスタム データ構造) で指定されたアドレスとポート番号にバインドします。コード例では、サーバーをローカルホストにバインドしているため、INADDR_ANY を使用して IP アドレスを指定しています。
C++ bind ( sockfd sockaddr * addr socklen_t addrlen );
パラメータ:
- 靴下 :socket() 関数を使用して作成されたソケット ファイル記述子。
- アドレス : ソケットをバインドするための IP アドレスとポート番号を含む構造体 sockaddr へのポインター。
- 追加 : addr 構造体の長さ。
4. 聞く
このステップでは、サーバーは listen() 関数を使用してサーバーソケットをパッシブモードにし、クライアントが接続を確立するためにサーバーに近づくのを待ちます。バックログは、sockfd の保留中の接続のキューが増加する最大長を定義します。キューがいっぱいのときに接続要求が到着すると、クライアントは ECONNREFUSED を示すエラーを受け取ることがあります。
C listen ( sockfd backlog );
パラメータ :
- 靴下 :socket() 関数を使用して作成されたソケット ファイル記述子。
- やり残し : サーバーが接続の受け入れを待機している間に保留中の接続を保持するキューのサイズを表す数値。
5. 同意する
このステップでは、サーバーは、待機中のソケットの保留中の接続のキューから最初の接続要求を抽出します。 sockfd は、 受け入れる() 関数を実行し、そのソケットを参照する新しいファイル記述子を返します。この時点で、クライアントとサーバー間の接続が確立され、データを転送できる状態になります。
C new_socket = accept ( sockfd sockaddr * addr socklen_t * addrlen );
パラメータ:
- 靴下 :ソケットファイル記述子はsocket()およびbind()によって返されます。
- アドレス : クライアントの IP アドレスとポート番号を保持する構造体 sockaddr へのポインタ。
- 追加 : アドレス構造の長さを指定する変数へのポインタ。
6. 送受信
このステップでは、サーバーはクライアントからデータを送受信できます。
送信(): クライアントにデータを送信する
C send ( sockfd * buf len flags );
パラメータ:
- 靴下 :socket() 関数によって返されるソケット ファイル記述子。
- バフ : 送信するデータを含むバッファへのポインタ。
- のみ : 送信するデータのバイト数。
- フラグ : データの送信方法に関するさまざまなオプションを指定する整数。通常、デフォルトの動作には 0 が使用されます。
受け取る() : クライアントからデータを受信します。
C recv ( sockfd * buf len flags );
パラメータ:
- 靴下 :socket() 関数によって返されるソケット ファイル記述子。
- バフ : 保存するデータを含むバッファへのポインタ。
- のみ : 送信するデータのバイト数。
- フラグ : データの送信方法に関するさまざまなオプションを指定する整数。通常、デフォルトの動作には 0 が使用されます。
6.閉じる
情報の交換が完了すると、サーバーは close() 関数を使用してソケットを閉じ、システム リソースを解放します。
C close ( fd );
パラメータ:
- fd: ソケットのファイル記述子。
クライアント側プロセスの作成
クライアント側プロセスを作成するには、次の手順に従います。
1. ソケット接続
このステップには、サーバーのソケット作成と同じ方法で行われるソケットの作成が含まれます。
2.接続する
connect() システムコールは、ファイル記述子 sockfd によって参照されるソケットを addr によって指定されたアドレスに接続します。 addrにはサーバーのアドレスとポートを指定します。
C++ connect ( sockfd sockaddr * addr socklen_t addrlen );
パラメータ
- 靴下 :socket() 関数によって返されるソケット ファイル記述子。
- アドレス : サーバーの IP アドレスとポート番号を含む struct sockaddr へのポインター。
- 追加 : アドレスのサイズ。
3. 送受信
このステップでは、クライアントはサーバーからデータを送受信できます。これは、サーバーがクライアントからデータを送受信する方法と同様に、send() 関数とrecieve() 関数を使用して行われます。
4.閉じる
情報の交換が完了したら、クライアントも作成されたソケットを閉じ、サーバーと同じ方法で close() 関数を使用してシステム リソースを解放する必要があります。
ソケットプログラミングにおける一般的な問題とその修正
- 接続失敗: 接続の失敗を回避するには、クライアントが正しい接続に接続しようとしていることを確認する必要があります。 IPアドレスとポート 。
- ポートバインディングエラー: これらのエラーは、ポートが別のアプリケーションによってすでに使用されている場合に発生し、このシナリオではそのポートへのバインドが失敗します。別のポートを使用してみるか、そのポートを使用していた以前のアプリケーションを閉じてください。
- ブロッキングソケット: デフォルトではソケットはブロックされています。これは、クライアント接続またはデータがない場合、accept() や recv() などの呼び出しが無期限に待機することを意味します。必要に応じて、ソケットをノンブロッキング モードに設定できます。