C 言語でソケットを開く
この記事では、C 言語でソケットを開く方法のいくつかの方法について説明します。
C 言語で listen
および accept
関数を使用してソケットを開く
listen
と accept
は 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);
}