C でのソケットプログラミング

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 でのソケットプログラミングソケットのサーバーおよびクライアント モデルの状態図

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 プロトコルを参照)
C
   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() などの呼び出しが無期限に待機することを意味します。必要に応じて、ソケットをノンブロッキング モードに設定できます。
クイズの作成