Posted at

UbuntuのTerminalでUnix Domain Socketを使ってIPCするC言語実装の最小構成サンプル

More than 1 year has passed since last update.

今回のサンプルを実装した元々の目的はAndroid NativeでのSocketを使ったIPCの概要をざっくり知ることでした.


TL;DR (長い三行で)

実装はServer側とClient側の2種類,.c 2ファイル.

.cをgccでCompileして実行Fileを2つ出力.

それぞれ別Terminal(別Process)上で実行し,文字列メッセージを送受信する.


Socketの種類

Socket自体は通信方式やProtocolの実装は規定していないようで,今回のような同一Host内のIPCでも,他Host間のTCP/IP等の通信も可能になっているようです.

今回は同一Host内のIPCをSocketをつかって実装します.


Build方法

gccを使って.cを実行Fileにcompileします.

今回のサンプルは実装が超シンプルなので,何のOption指定もしなくても試すことはできます.

(gccとかに詳しい人には怒られそうですが...)

# Current Dirのserver.cというファイルをCompileしてserverという実行Fileを出力

$ gcc -o server server.c

# 出力した実行Fileを実行
$ ./server


Server側実装

Server側は起動後,接続を待ち続け,接続が来たらデータを読み取り,終わったらまた接続を待つ,という処理になります.

Server側はずっと動き続けるものになるので,終了させるときはctrl+cとかで強制終了するか,Clientからの終了コマンドを定義するか,が必要です.

socket // Socket自身のFile Descriptor作成


sockadddr_un // Socket接続待ちのAddress (Socket File) を指定

bind // SocketをSocket Fileに接続

listen // Socketを受信待ちSocketに指定

accept // Clientからの接続を受け付け

read // ClientからのDataを受信

close // Clientから受信したFile DescriptorをClose

close // Socket自身のFile DescriptorをClose


server.c

#include <stdio.h>

#include <sys/socket.h>
#include <sys/un.h>

void on_error(char* msg, int socket_fd) {
if (socket_fd != -1) {
close(socket_fd);
}
printf("TraceLog: server: %s\n", msg);
}

int main(void) {
printf("TraceLog: server: main() : E\n");

int err = 0;
int socket_fd = -1;

// Create server socket file discriptor.
socket_fd = socket(
AF_UNIX, // Local connection.
SOCK_STREAM, // Bi-directional access, bite stream.
0); // Default protocol.
if (socket_fd == -1) {
on_error("socket_fd != 0", socket_fd);
return -1;
}

// Struct for socket address.
struct sockaddr_un addr = { 0 }; // 0 init.
addr.sun_family = AF_UNIX; // Fixed.
strcpy(addr.sun_path, "socket_file"); // Common socket file.

// Remove old socket. If file exists, bind will be failed.
remove(addr.sun_path);

// Bind.
err = bind(
socket_fd, // Bind target socket.
(struct sockaddr*) &addr, // Socket address.
sizeof(struct sockaddr_un)); // Address size.
if (err != 0) {
on_error("bind failed.", socket_fd);
return -1;
}

// Mark passive socket.
err = listen(
socket_fd, // Target socket.
1); // Max connection count.
if (err != 0) {
on_error("listen failed.", socket_fd);
return -1;
}

// Wait for connection and receive.
while(1) {
// Wait for client connection.
int fd = accept(
socket_fd, // Target socket.
NULL, // Client address.
NULL); // Client address size.
if (fd == -1) {
on_error("accept failed.", socket_fd);
return -1;
}

// Receive.
char buffer[256];
int size = read(
fd, // Accepted fd.
buffer, // Receive buffer.
sizeof(buffer) - 1); // Receive size. Last byte is null char.
if (size == -1) {
on_error("read failed.", socket_fd);
return -1;
}

// Output received message.
buffer[size] = '\0';
printf("TraceLog: server: Received Msg = %s\n", buffer);

// Close client fd.
err = close(fd); // Closed client connection fd.
if (err != 0) {
on_error("close fd failed.", socket_fd);
return -1;
}

} // while(true)

// Close server socket.
close(socket_fd);

printf("## server main() : X\n");
return 0;
}



Client側実装

Client側の処理は接続先のSocket(Socket File)に対して接続,接続完了したらデータ送信,の流れです.

socket // Socket自身のFile Descriptor作成


sockadddr_un // Socket接続先のAddress (Socket File) を指定

connect // SocketをSocket Fileに接続している別のSocketに接続

write // 接続先SocketにDataを送信

close // Socket自身のFile DescriptorをClose


client.c

#include <stdio.h>

#include <sys/socket.h>
#include <sys/un.h>

#define MESSAGE "HELLO WORLD !"

void on_error(char* msg, int socket_fd) {
if (socket_fd != -1) {
close(socket_fd);
}
printf("TraceLog: client: %s\n", msg);
}

int main(void) {
printf("TraceLog: client: main() : E\n");

int err = 0;
int socket_fd = -1;

// Create client socket.
socket_fd = socket(
AF_UNIX, // Local connection.
SOCK_STREAM, // Bi-directional access, bite stream.
0); // Default protocol.
if (socket_fd == -1) {
on_error("socket_fd != 0", socket_fd);
return -1;
}

// Struct for socket address.
struct sockaddr_un addr = { 0 }; // 0 init.
addr.sun_family = AF_UNIX; // Fixed.
strcpy(addr.sun_path, "socket_file"); // Common socket file.

// Connect.
err = connect(
socket_fd, // Bind target socket.
(struct sockaddr*) &addr, // Socket address.
sizeof(struct sockaddr_un)); // Address size.
if (err != 0) {
on_error("connect failed.", socket_fd);
return -1;
}

// Send message.
err = write(
socket_fd, // Target socket.
MESSAGE, // Message.
strlen(MESSAGE)); // Message size.
if (err == -1) {
on_error("write failed.", socket_fd);
return -1;
}

// Close client socket.
close(socket_fd);

printf("TraceLog: client: main() : X\n");
return 0;
}



動作確認


  1. Server用Terminalで./serverを打ちServerを起動,接続待ち状態にする

  2. Client用Terminalで./clientを打ちClientを起動,メッセージ送信後,自動で終了

  3. Server用TerminalにClientからのメッセージが表示されていることを確認


おわり