C 言語でソケットを開く

胡金庫 2023年10月12日
C 言語でソケットを開く

この記事では、C 言語でソケットを開く方法のいくつかの方法について説明します。

C 言語で listen および accept 関数を使用してソケットを開く

listenaccept は UNIX ネットワーク機能の一部であり、Sockets API と呼ばれることもあり、プロセス間通信を処理するためのコア機能を実装します。ソケットは、通信パスのエンドポイントの抽象表現であることに注意してください。Unix ドメインとインターネットドメインなど、さまざまな種類のソケットがありますが、それらはすべて、2つのプロセス間の通信を確立するための同様の手順に従います。

ネットワークは、実行中の 2つのプログラム間の単なる通信であることに注意してください。最初に、socket 関数が呼び出されて、エンドポイントと対応するハンドルが作成されます。ソケットは、UNIX システムの通常のファイルまたはパイプと同様のファイル記述子で処理されます。ソケット呼び出しの後、開発しているプログラムの種類に応じて 2つのシナリオがあります。一般に、ネットワーク通信にはクライアントサーバーモデルがあり、1つのプログラム(サーバー)が他のプログラム(クライアント)が接続するのを待つ必要があります。

この例では、特定のエンドポイントをリッスンし、クライアント接続を受け入れる必要があるサーバー側プログラムを実装します。このシナリオでは、プログラムが着信接続を受け入れる特定のアドレスにバインドするために、ソケットの後にバインド関数が呼び出されます。bind の後、listen および accept 関数が呼び出され、いわゆるパッシブソケットが形成されます。accept は、別のプロセスが connect 関数呼び出しを使用して接続を確立しなくなるまで、プロセスをブロックすることに注意してください。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

#define handle_error(msg) \
  do {                    \
    perror(msg);          \
    exit(EXIT_FAILURE);   \
  } while (0)

#define SOCKET_PATH "/tmp/my.sock"

enum { LISTEN_QUEUE = 100 };

int main(int argc, char *argv[]) {
  int listenfd, connfd;
  socklen_t len;
  struct sockaddr_un servaddr, cliaddr;

  listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
  if (listenfd == -1) handle_error("socket");

  memset(&servaddr, 0, sizeof(servaddr));
  servaddr.sun_family = AF_UNIX;
  strncpy(servaddr.sun_path, SOCKET_PATH, sizeof(servaddr.sun_path) - 1);

  if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
    handle_error("bind");

  if (listen(listenfd, LISTEN_QUEUE) == -1) handle_error("listen");

  len = sizeof(cliaddr);
  connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &len);
  if (connfd == -1) handle_error("accept");

  unlink(SOCKET_PATH);
  exit(EXIT_SUCCESS);
}

通常、accept 呼び出しの後には、着信接続の処理を実装するコードが続きます。このサーバーとの接続を確立する外部プログラムは、アドレスとポートを知っている必要があります。それらの両方がプロセスに認識されていると仮定すると、socket 呼び出しの後に connect 関数を呼び出し、うまくいけばサーバープロセスに接続されます。接続が確立されると、両方のプロセスは、双方向チャネルを持つものとしてソケット記述子の書き込みと読み取りを行うことができます。実際のシナリオでは、複数のクライアントプロセスがサーバーに同時に接続することになっています。接続処理コードは同時に実装する必要があります。そうしないと、サーバーは一度に複数のクライアントにサービスを提供できなくなります。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

#define handle_error(msg) \
  do {                    \
    perror(msg);          \
    exit(EXIT_FAILURE);   \
  } while (0)

#define SOCKET_PATH "/tmp/my.sock"

enum { MAXLINE = 4096, LISTEN_QUEUE = 100 };

int main(int argc, char *argv[]) {
  int listenfd, connfd;
  socklen_t len;
  struct sockaddr_un servaddr, cliaddr;
  char buf[MAXLINE];

  listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
  if (listenfd == -1) handle_error("socket");

  memset(&servaddr, 0, sizeof(servaddr));
  servaddr.sun_family = AF_UNIX;
  strncpy(servaddr.sun_path, SOCKET_PATH, sizeof(servaddr.sun_path) - 1);

  if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
    handle_error("bind");

  if (listen(listenfd, LISTEN_QUEUE) == -1) handle_error("listen");

  len = sizeof(cliaddr);
  connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &len);
  if (connfd == -1) handle_error("accept");

  size_t ret = read(connfd, buf, MAXLINE);
  if (ret == -1) handle_error("read");

  printf("read %d bytes\nmessage: %s\n", connfd, buf);

  unlink(SOCKET_PATH);
  exit(EXIT_SUCCESS);
}
著者: 胡金庫
胡金庫 avatar 胡金庫 avatar

DelftStack.comの創設者です。Jinku はロボティクスと自動車産業で8年以上働いています。自動テスト、リモートサーバーからのデータ収集、耐久テストからのレポート作成が必要となったとき、彼はコーディングスキルを磨きました。彼は電気/電子工学のバックグラウンドを持っていますが、組み込みエレクトロニクス、組み込みプログラミング、フロントエンド/バックエンドプログラミングへの関心を広げています。

LinkedIn Facebook

関連記事 - C Networking