俺の報告

RoomClipを運営するエンジニアの日報(多分)です。

TCPソケットをつかって簡単なサーバクライアントプロセスを作る - 日報 #149

WebRTCが巷で流行っているそうです。
まだほとんど触っていないので詳細は分かりませんが、
リアルタイムコネクションとおっしゃっているのだから、
きっとなんかリアルタイムなコネクションがあるのでしょう。
そうなるときっとsocket系の話になるのでしょう。

ということで、TCPソケットについて少し調べました。

そもそもこのTCPソケットについては昔から「得体の知れないすごいやつ」感がすごくて、
ApacheやらNginxみたいな、「多分C系で書かれてるんだろうなぁ…」みたいなアプリケーションだけができる特殊な世界観だと思ってました。
ですが、Node.jsみたいなものがではじめてからというもの、
そしてBSDソケットについて知見が深まってからというもの、
「サーバって意外と単純な仕組みなんじゃないか?」
というふうな考えになりつつあります。
Apache httpの開発者にぶっ殺されるような発言ですが、
勇気をだして言ってみました。

正確に言うと、
「得体が知れないほど複雑怪奇な世界」というわけではなく、 あくまで、「むちゃくちゃ複雑というわけではなく、理解はできそうな世界」なんじゃないかなと。
そう思い始めております。

ということで、
なにをさて置いても、自作してみるのが一番、
ってことでTCPソケットを作って「むっっっちゃ簡単なサーバ・クライアントソフト」を作ってみました。

以下は簡単なコード。
gccコンパイルできました。

まずはサーバ側。
server.cについて。ソケット作って、バインドして、リッスンして、アクセプトしたら、書き出す。
そんな簡単なサーバ。

// まずは server.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main()
{
 int socket_conn;
 struct sockaddr_in addr;
 struct sockaddr_in client;
 int len_sockaddr_client;
 int socket_accept;

// ソケット初期化
// ストリーム型で接続するので SOCK_STREAM。UDPとかのダイアグラム型ならSCOK_DGRAMかな?
 socket_conn = socket(AF_INET, SOCK_STREAM, 0);
 if (socket_conn < 0) printf("Cannnot connect.\n");
 addr.sin_family = AF_INET; // ちょー一般的なアドレスファミリ
 addr.sin_port = htons(89898); // ポート指定
 addr.sin_addr.s_addr = INADDR_ANY; // 0.0.0.0からの接続を受け付けるという意味

// 満を持してバインド
if (bind(socket_conn, (struct sockaddr*)addr, sizeof(addr)) < 0) printf("Cannnot bind.\n");

// そしてリッスン
 listen(socket_conn, 5); // サーバー側でacceptされるまでのキュー数がこの5という数値

 // アクセプト準備
 len_sockaddr_client = sizeof(client);
 socket_accept = accept(socket_conn, (struct sockaddr *)&client, &len_sockaddr_client);

// とりあえず、文字列だけをソケットに書き出す
 write(socket_accept, "hogehoge", 8); // メッセージとバイト数

// ごみ処理
 close(socket_accept);
 close(socket_conn);
 return 0;
}

そして、クライアント側。
要はブラウザ的な。
127.0.0.1localhostでサーバを待機させるので、アドレスはそちらに設定。

// そして client.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main()
{
 int sock_conn;
 struct sockaddr_in server_addr;
 char res[5];
 memset(res, '\0', sizeof(res));

 // ソケット初期化 
 // server.cと同じアドレス構造を使う
 sock_conn = socket(AF_INET, SOCK_STREAM, 0);
 if (socket_conn < 0) printf("Cannnot connect.\n");
 server_addr.sin_family = AF_INET;
 server_addr.sin_port = htons(89898);
 server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

 // 自分で作ったソケットを先方のソケットにつなげる
 connect(sock_conn, (struct sockaddr *)&server_addr, sizeof(server_addr));

 // ソケットをリードする
 read(sock_conn, res, sizeof(res));

 // 結果出力
 printf("%s\n", buf);

 // ごみ処理
 close(sock_conn);
 return 0;
}

こんな感じで、あとはコンパイルして、serverをたたき、
clientを叩けばhogeを受信できます。
その昔、ターミナルでttyに対して標準出力を投げ合うことでチャットのようなことをやったことがありますが、
その感覚にすごく似ていますね。
つまるところ、BSDソケットが極めて優秀なAPIのために、
ファイルのリードライト感覚でTCPの通信ができるだなんて、
本当にすばらしい世界ですね。

サービスには全く関係ないけどね!