この記事では、ソケットを用いたプロセス間通信について紹介します。
ソケットによるプロセス間通信は、Unix系システムでよく使われる方法の一つです。
ソケットを使うことで、プロセス間でデータをやり取りすることができます。ここでは、C++でのソケットによるプロセス間通信の概要と簡単な実装例を紹介します。
目次
ソケットの概要
ソケット(Socket)は、コンピューターネットワークにおいて、プロセス間通信や異なるコンピュータ間での通信を可能にするソフトウェアのインタフェースです。
ソケット通信の手順
サーバ側
- ソケットを作成する。
- ソケットにアドレス(IPアドレスとポート番号)をバインドする。
- 接続要求を待つ。
- クライアントからの接続を受け入れる。
- データを送受信する。
- ソケットを閉じる。
クライアント側
- ソケットを作成する。
- サーバーのアドレスに接続する。
- データを送受信する。
- ソケットを閉じる。
実装例
今回はTCPソケットを使った簡単な実装例を紹介します。
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 8080
#define BUFFER_SIZE 1024
void startServer() {
int server_fd, new_socket;
struct sockaddr_in address;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE] = {0};
// ソケットファイルディスクリプタの作成
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// サーバーアドレスの設定
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
// ソケットをポートにバインドする
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// 接続をリッスンする
if (listen(server_fd, 3) < 0) {
perror("listen");
close(server_fd);
exit(EXIT_FAILURE);
}
// クライアントからの接続を受け入れる
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept");
close(server_fd);
exit(EXIT_FAILURE);
}
// データを受信する
read(new_socket, buffer, BUFFER_SIZE);
std::cout << "Server received: " << buffer << std::endl;
// クライアントに応答を送信する
const char *response = "Hello from server";
send(new_socket, response, strlen(response), 0);
std::cout << "Response sent\n";
// ソケットを閉じる
close(new_socket);
close(server_fd);
}
void startClient() {
int sock = 0;
struct sockaddr_in serv_addr;
char buffer[BUFFER_SIZE] = {0};
// ソケットファイルディスクリプタの作成
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
std::cerr << "Socket creation error" << std::endl;
exit(EXIT_FAILURE);
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// サーバーアドレスを設定
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
std::cerr << "Invalid address/ Address not supported" << std::endl;
close(sock);
exit(EXIT_FAILURE);
}
// サーバーに接続する
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
std::cerr << "Connection Failed" << std::endl;
close(sock);
exit(EXIT_FAILURE);
}
// サーバーにメッセージを送信する
const char *message = "Hello from client";
send(sock, message, strlen(message), 0);
std::cout << "Message sent\n";
// サーバーからの応答を受信する
read(sock, buffer, BUFFER_SIZE);
std::cout << "Client received: " << buffer << std::endl;
// ソケットを閉じる
close(sock);
}
int main(int argc, char const *argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <server/client>" << std::endl;
return EXIT_FAILURE;
}
std::string mode = argv[1];
if (mode == "server") {
startServer();
} else if (mode == "client") {
startClient();
} else {
std::cerr << "Invalid mode. Use 'server' or 'client'." << std::endl;
return EXIT_FAILURE;
}
return 0;
}
ヘッダーファイルのインクルード
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>
iostream
は標準入出力、cstring
は文字列操作、unistd.h
とarpa/inet.h
はUNIX系システムでソケットプログラミングに必要なヘッダーです。
定数定義
#define PORT 8080
#define BUFFER_SIZE 1024
PORT
は使用するポート番号、BUFFER_SIZE
はデータのバッファーサイズを定義します。
サーバ側の関数
void startServer() {
int server_fd, new_socket;
struct sockaddr_in address;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE] = {0};
// ソケットファイルディスクリプタの作成
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// サーバーアドレスの設定
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
// ソケットをポートにバインドする
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// 接続をリッスンする
if (listen(server_fd, 3) < 0) {
perror("listen");
close(server_fd);
exit(EXIT_FAILURE);
}
// クライアントからの接続を受け入れる
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept");
close(server_fd);
exit(EXIT_FAILURE);
}
// データを受信する
read(new_socket, buffer, BUFFER_SIZE);
std::cout << "Server received: " << buffer << std::endl;
// クライアントに応答を送信する
const char *response = "Hello from server";
send(new_socket, response, strlen(response), 0);
std::cout << "Response sent\n";
// ソケットを閉じる
close(new_socket);
close(server_fd);
}
socket()
でソケットを作成し、bind()
でポートにバインドします。listen()
でクライアントからの接続待ちを開始します。accept()
でクライアントの接続を受け入れ、新しいソケットを作成します。read()
でクライアントからのデータを受信し、send()
で応答を送信します。close()
でソケットを閉じます。
クライアント側の関数
void startClient() {
int sock = 0;
struct sockaddr_in serv_addr;
char buffer[BUFFER_SIZE] = {0};
// ソケットファイルディスクリプタの作成
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
std::cerr << "Socket creation error" << std::endl;
exit(EXIT_FAILURE);
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// サーバーアドレスを設定
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
std::cerr << "Invalid address/ Address not supported" << std::endl;
close(sock);
exit(EXIT_FAILURE);
}
// サーバーに接続する
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
std::cerr << "Connection Failed" << std::endl;
close(sock);
exit(EXIT_FAILURE);
}
// サーバーにメッセージを送信する
const char *message = "Hello from client";
send(sock, message, strlen(message), 0);
std::cout << "Message sent\n";
// サーバーからの応答を受信する
read(sock, buffer, BUFFER_SIZE);
std::cout << "Client received: " << buffer << std::endl;
// ソケットを閉じる
close(sock);
}
socket()
でソケットを作成し、connect()
でサーバーに接続します。send()
でサーバーにメッセージを送信し、read()
でサーバーからの応答を受信します。close()
でソケットを閉じます。
main関数
int main(int argc, char const *argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <server/client>" << std::endl;
return EXIT_FAILURE;
}
std::string mode = argv[1];
if (mode == "server") {
startServer();
} else if (mode == "client") {
startClient();
} else {
std::cerr << "Invalid mode. Use 'server' or 'client'." << std::endl;
return EXIT_FAILURE;
}
return 0;
}
- プログラムの実行時に引数として
"server"
または"client"
を指定します。 - 引数に応じて
startServer()
またはstartClient()
を呼び出します。
実行結果
まず、以下のコマンドでサーバを起動します。
./<実行ファイル名> server
別のターミナルを開き、以下のコマンドでクライアントを起動します。
./<実行ファイル名> client
クライアント側に以下のメッセージが表示されるはずです。
Message sent
Client received: Hello from server
また、サーバ側には以下のメッセージが表示されます。
Server received: Hello from client
Response sent