最近、ネットワークプログラミングに興味が出てきたので、その一端に触れるためにHTTPサーバーを実装してみることにしました。
もちろん、有名なサーバーレベルのものは自分では作れるはずもないので、あくまでも自己学習の1つとして簡易的なものを作っていきます。
今回の目標
最初から難しい目標を立てても挫折しそうなので、まずはリクエストに対して「HTTP 200 OK」を返すことだけを目標にします。階段を1段ずつ登っていく感じで「楽しくプログラミング」がモットーです(・∀・)
実装してみた
Google先生をはじめとするいろんな情報に頼って実装してみたのがこちら。今後の開発も考えて、コードはGitHubで管理することにしました。
ソースコード:https://github.com/corgi0901/simple-server
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(void) {
int rsock, wsock;
struct sockaddr_in addr, client;
int len;
int ret;
/* make socket */
rsock = socket(AF_INET, SOCK_STREAM, 0);
if (rsock < 0) {
fprintf(stderr, "Error. Cannot make socket\n");
return -1;
}
/* socket setting */
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = INADDR_ANY;
/* binding socket */
ret = bind(rsock, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0) {
fprintf(stderr, "Error. Cannot bind socket\n");
return -1;
}
/* listen socket */
listen(rsock, 5);
/* accept TCP connection from client */
len = sizeof(client);
wsock = accept(rsock, (struct sockaddr *)&client, &len);
/* send message */
write(wsock, "HTTP1.1 200 OK", 14);
/* close TCP session */
close(wsock);
close(rsock);
return 0;
}
解説
プログラムの簡単な解説です。
1. ソケットを作る
最初に通信のためのソケットを作ります。ソケットというのは通信の口みたいなもので、このソケットを読み書きすることで通信を実現できます。
int rsock;
rsock = socket(AF_INET, SOCK_STREAM, 0);
ちなみに、第1引数の「AF_INET」はIPv4による接続を表し、第2引数の「SOCK_STREAM」はバイトストリーム形式の通信を表しています。
2.ソケットにアドレスを割り当てる
作成したソケットにアドレスを割り当てます。「ソケットに名前を付ける」とイメージすればわかりやすいかも。
struct sockaddr_in addr;
/* socket setting */
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = INADDR_ANY;
/* binding socket */
bind(rsock, (struct sockaddr *)&addr, sizeof(addr));
割り当てる具体的な情報は、sockaddr_in構造体に定義しておきます。今回はIPv4で8080番ポートを指定しています。アドレスは特に指定しないのでINADDR_ANYです。
3.接続を待ち受ける
bindしたソケットに対してlistenで接続を待ちます。第2引数は接続待ちキューの最大長ですが、今回は適当に5を指定します。
listen(rsock, 5);
4.接続を受け付ける
接続要求に対してacceptで受け付けます。acceptの戻り値として接続済みのソケットが返ってきます。
int wsock;
int len;
struct sockaddr_in client;
len = sizeof(client);
wsock = accept(rsock, (struct sockaddr *)&client, &len);
このとき、第2引数のclientには接続元のアドレス情報が格納されます。
5.データを書き込む
acceptで受け取ったソケットに対してデータを書き込みます。今回はどんな接続に対しても「HTTP1.1 200 OK」を返しています。
write(wsock, "HTTP1.1 200 OK", 14);
これで、リクエストに対して返事を返すことができます。
実際に通信してみた
作成したサーバープログラムに対してブラウザからリクエストを送り、そのレスポンスを見てみます。
Chromeの開発者ツールで確認したところ、確かに「200 OK」の結果が返せています(・∀・) 何の実用性もないですが、思った通りに動くと嬉しいですね!
わずか50行ほどのソースでサーバープログラムの一端に触れることができました。もう少し実用的なものになるように、今後少しずつ手を入れていきたいと思います。
ではではノシ