0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ソケットで通信する

0
Last updated at Posted at 2025-12-22

こんにちは。今回はソケットで通信する方法について解説します。

ソケットとは?

ソケットとは、異なるプロセス(同一端末上にあるとは限らない)間での通信を実現するものです。
主に以下の2種類があります。

  • TCP/UDPソケット
  • UNIX Domainソケット

上記はIPアドレス等を通じたネットワークを通した通信、下記はファイルを用いた同一ファイルシステム上の通信になっています。
今回は汎用性の高いTCP/UDPソケットの例を示します。

Server側

まずはImport

#include <netinet/in.h>
#include <signal.h>
#include <sys/socket.h>
#include <unistd.h>
int main() {}

int main() {}の中身

signal handlerを設定

// SIGPIPEで死なないようにする
signal(SIGPIPE, SIG_IGN);

ソケットを立ち上げる

int server_fd = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

意味

  • AF_INET
    IPv4 を使う
  • SOCK_STREAM
    → ストリーム型(= TCP)
  • 0
    → プロトコルを自動選択(TCP が選ばれる)
    server_fdにはファイルディスクリプタが代入される。

setsockoptは、ソケットの動作をカスタマイズする関数

引数 意味
server_fd 設定対象のソケット
SOL_SOCKET ソケット自体の設定
SO_REUSEADDR アドレス(IP+ポート)の再利用を許可
&opt 有効化(1 = true)
sizeof(opt) サイズ

アドレスを設定する、バインドする、リッスンする

sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = INADDR_ANY;
bind(server_fd, (sockaddr *)&addr, sizeof(addr));
listen(server_fd, 5);

addr.sin_addr.s_addr = INADDR_ANY;はすべてのIPからのリクエストを受け付ける(0.0.0.0にバインドする)
listenの第二引数は、Queに入れる通信街の数の上限を定める(Handshake済み、Accept待ちの行列)
理論的な上限はSOMAXCONNに定義される

とりあえずAcceptする

int client_fd = accept(server_fd, nullptr, nullptr);

client_fdには新たなファイルディスクリプタが代入されます。

通信する

char buf[1024];
int n = read(client_fd, buf, sizeof(buf));
if (n > 0) {
  write(client_fd, buf, n); // エコー
}

nには飛んできたバイト数が代入される。

最後にはしっかりクローズ

close(client_fd);
close(server_fd);

Client側

まずは諸々宣言

#include <arpa/inet.h> // inet_pton
#include <cstring>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

int main() {}

int main(){}の中身

まずは設定

int sock = socket(AF_INET, SOCK_STREAM, 0);
// 接続先情報を用意
sockaddr_in server{};
server.sin_family = AF_INET;
server.sin_port = htons(8080); // サーバのポート
inet_pton(AF_INET, "127.0.0.1", &server.sin_addr);

inet_ptonは通信用のIPをソケット用のバイナリに変換する。成功したら1を返す関数。

接続して書いて閉じる

// サーバに接続
connect(sock, (sockaddr *)&server, sizeof(server));
// 送信
write(sock, "hi", 2);
// 受信
char buf[1024];
read(sock, buf, sizeof(buf));
// 終了
close(sock);

おまけ

コード全体

server.cpp
#include <netinet/in.h>
#include <signal.h>
#include <sys/socket.h>
#include <unistd.h>

int main() {
  // SIGPIPEで死なないようにする
  signal(SIGPIPE, SIG_IGN);

  // 通信口を作る
  int server_fd = socket(AF_INET, SOCK_STREAM, 0);

  // ポート再利用を許可
  int opt = 1;
  setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

  // アドレス設定
  sockaddr_in addr{};
  addr.sin_family = AF_INET;
  addr.sin_port = htons(8080);
  addr.sin_addr.s_addr = INADDR_ANY;

  // ポートを開放
  bind(server_fd, (sockaddr *)&addr, sizeof(addr));

  // 接続待ち
  listen(server_fd, 5);

  // クライアント接続を待つ
  int client_fd = accept(server_fd, nullptr, nullptr);

  // 通信
  char buf[1024];
  int n = read(client_fd, buf, sizeof(buf));
  if (n > 0) {
    write(client_fd, buf, n); // エコー
  }

  // 終了処理
  close(client_fd);
  close(server_fd);
}
client.cpp
#include <arpa/inet.h> // inet_pton
#include <cstring>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

int main() {
  // 通信口を作る
  int sock = socket(AF_INET, SOCK_STREAM, 0);

  // 接続先情報を用意
  sockaddr_in server{};
  server.sin_family = AF_INET;
  server.sin_port = htons(8080); // サーバのポート
  inet_pton(AF_INET, "127.0.0.1", &server.sin_addr);

  // サーバに接続
  connect(sock, (sockaddr *)&server, sizeof(server));

  // 送信
  write(sock, "hi", 2);

  // 受信
  char buf[1024];
  read(sock, buf, sizeof(buf));

  // 終了
  close(sock);
}

ファイルを使用してネットワークより早い通信をする場合(同一ファイルシステム上のプロセスなら可能)

使われるファイルのパスは/tmp/echo.sock

server.cpp
#include <signal.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <cstring>

int main() {
  // SIGPIPEで死なないようにする
  signal(SIGPIPE, SIG_IGN);

  const char* SOCKET_PATH = "/tmp/echo.sock";

  // 通信口を作る
  int server_fd = socket(AF_UNIX, SOCK_STREAM, 0);

  // アドレス設定
  sockaddr_un addr{};
  addr.sun_family = AF_UNIX;
  std::strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1);

  // 既存ソケットファイルがあれば削除
  unlink(SOCKET_PATH);

  // バインド
  bind(server_fd, (sockaddr*)&addr, sizeof(addr));

  // 接続待ち
  listen(server_fd, 5);

  // クライアント接続を待つ
  int client_fd = accept(server_fd, nullptr, nullptr);

  // 通信
  char buf[1024];
  int n = read(client_fd, buf, sizeof(buf));
  if (n > 0) {
    write(client_fd, buf, n); // エコー
  }

  // 終了処理
  close(client_fd);
  close(server_fd);
  unlink(SOCKET_PATH);
}
client.cpp
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <cstring>

int main() {
  const char* SOCKET_PATH = "/tmp/echo.sock";

  // 通信口を作る
  int sock = socket(AF_UNIX, SOCK_STREAM, 0);

  // 接続先情報を用意
  sockaddr_un server{};
  server.sun_family = AF_UNIX;
  std::strncpy(server.sun_path, SOCKET_PATH, sizeof(server.sun_path) - 1);

  // サーバに接続
  connect(sock, (sockaddr*)&server, sizeof(server));

  // 送信
  write(sock, "hi", 2);

  // 受信
  char buf[1024];
  read(sock, buf, sizeof(buf));

  // 終了
  close(sock);
}

Server側が受け付けたリクエストをForkして処理する場合

作成されているものがファイルディスクリプタであることに注意して、適切にcloseする必要がある。

server.cpp
#include <netinet/in.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <cstdlib>

int main() {
  // SIGPIPEで死なないようにする
  signal(SIGPIPE, SIG_IGN);

  // ゾンビプロセス対策(簡易)
  signal(SIGCHLD, SIG_IGN);

  // 通信口を作る
  int server_fd = socket(AF_INET, SOCK_STREAM, 0);

  // ポート再利用を許可
  int opt = 1;
  setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

  // アドレス設定
  sockaddr_in addr{};
  addr.sin_family = AF_INET;
  addr.sin_port = htons(8080);
  addr.sin_addr.s_addr = INADDR_ANY;

  // ポートを開放
  bind(server_fd, (sockaddr *)&addr, sizeof(addr));

  // 接続待ち
  listen(server_fd, 5);

  while (true) {
    // クライアント接続を待つ
    int client_fd = accept(server_fd, nullptr, nullptr);
    if (client_fd < 0) {
      continue;
    }

    pid_t pid = fork();

    if (pid == 0) {
      // ===== 子プロセス =====
      close(server_fd); // 子では不要

      char buf[1024];
      int n = read(client_fd, buf, sizeof(buf));
      if (n > 0) {
        write(client_fd, buf, n); // エコー
      }

      close(client_fd);
      _exit(0); // 子プロセス終了
    } else if (pid > 0) {
      // ===== 親プロセス =====
      close(client_fd); // 親では不要
    } else {
      // fork失敗
      close(client_fd);
    }
  }

  close(server_fd);
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?