Go言語でシンプルで簡単なHTTPサーバーの作り方

現在では、ごく当たり前にインターネットを使いGoogleなどで検索し、サイトを調べてそのサイトの記事を読んだりしますよね。それらは『サーバー』という存在のおかげで実現しています。

『サーバー』は普段はじっと待機しており、何か要求されるとそれに応答するシステムのことです。

例えば、スマートフォンから当サイトの当記事を見たい場合、当サイトにアクセスしますよね。当サイトにも『サーバー』というものがあり、読者の方からの「この記事を見たい!」という要求があれば、その記事のデータを準備して読者の方のスマートフォンへとデータを送ります。

このような、サイトやブログの記事(ページ)を返すサーバーを『ウェブサーバー』と言い、『HTTP』もしくは『HTTPS』というプロトコル(方式)によって通信されています。

Go言語でもそれらの機能を簡単に実現出来るのですが、その中でも今回は『HTTPサーバー』の作り方(プログラムコード)をご紹介します。

コンテンツの転載は固くお断りいたします。

まずはソースコード


package main

import (
	"fmt"
	"net/http"
	"time"
)

func handler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "サーバーからのお返事です!")
}

func main() {
	muxHTTP := http.NewServeMux()
	muxHTTP.HandleFunc("/", handler)

	ServerHTTP := &http.Server{
		Addr:           ":8080",
		Handler:        muxHTTP,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	err := ServerHTTP.ListenAndServe()
	if err != nil {
		fmt.Println(err)
	}
}


上記のコードはもっと短くすることも出来るのですが、ウェブサーバーのプログラミングをしていると、すぐに色んな機能を足して拡張することになるので、初めからこのように書いておくと便利かと思います。

実行してみる

上記のコードを『Test.go』と名前を付けて保存し、以下のコマンドを入力して実行してみて下さい。


go run Test.go

そしてブラウザを開きアドレスバーに『localhost:8080』と入力して開いてみて下さい。すると、ブラウザの画面の以下のように文字列が表示されます。

コードを解説

では、最初に書いたコードを1つ1つ解説していきます。

どのパッケージに入っているかを宣言

1行目の以下のコードを見て下さい。


package main

これは『main』という名前のパッケージ(package)を宣言しているところです。これを書いておくと、他のプログラムファイルや他の方が書いたコードと、このファイルに書いたコードを区別しやすくなります。今は、おまじないのように書いておいて大丈夫です。

必要な機能を取り込む

3行目から7行目で、このプログラムに必要な機能を取り込んでいます。プログラムはこうやって、予め用意されている機能があれば取り込んで利用していきます。


import (
	"fmt"
	"net/http"
	"time"
)

『import』と書くと、どの機能を取り込むかを宣言出来ます。このコードのように『fmt』『net/http』『time』の3つを取り込むように、複数宣言する場合は()でくくるとまとめることが出来ます。

『fmt』パッケージには、画面『コンソール』に文字列をフォーマットして表示したり、逆にユーザーから何か文字列を入力してもらい、それを読み込むがことが出来る機能などが含まれています。

『net/http』パッケージには、ネットワーク機能、主にHTTP関連の機能が含まれています。今回作成するプログラムはHTTPサーバーなので、これがメインで必要になるパッケージとなっています。

『time』パッケージには、時間や時刻に関する機能が含まれており、現在時刻を取得したり、時刻に関する計算を行うことが出来るようになります。

サーバーに要求があった時の処理を書く

サーバーの役割はユーザー(クライアント)からの要求に対して、何らかの処理を行ったり反応を示すことです。今回はアクセスがあったら単純な文字列をサーバーからユーザーへ送信します。


func handler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "サーバーからのお返事です!")
}

ユーザーからのアクセスに応える関数って?

9行目の最初に『func』と書くことで『関数』を宣言することが出来ます。プログラムを書く時は上から下へと処理を書いていきますが、1つの関数にまとめると長くなり読みづらくなります。

なので、その長くなったコードから分けられそうはコードは、別の関数として宣言しておくとコードが見やすくなります。さらに、様々なコードで同じ処理を行いたい場合にも、関数として別に書いておくと、その関数を呼び出すだけで良くなるのでコードの記述量が減ります。

さて、上記のコードでは『handler』という名前の関数を宣言していますが、この関数ではユーザーからアクセスがあった時に呼び出されるよう設定されています。つまり、ユーザーからアクセスがあると、この関数が呼び出され、この関数内に書いたコードが実行されます。

関数が呼び出される時にデータを渡される!


func handler(w http.ResponseWriter, r *http.Request)

また、呼び出される際に『w』と『r』という名前のデータを渡されます。

『w』は『http.ResponseWriter』という種類のデータであり、このデータを扱うことによって、ユーザーに文字列を送信したり、様々なアクションを起こすことが出来ます。

『r』は『*http.Request』という種類で、ユーザーに関する情報が入っており、どのURL(アドレス)にアクセスしてきたのか、どのブラウザを使っているか、携帯からのアクセスか、IPアドレスは?…などなど様々な情報が入っています。

サーバーはこの情報を元に処理を変えることも可能となります。

アクセスしてきたユーザーに文字列を送る


fmt.Fprint(w, "サーバーからのお返事です!")

アクセスしてきたユーザーに文字列を送る際は『fmt』パッケージの『Fprint』関数を使います。この関数は『fmt』パッケージに含まれているので『.』と書いてパッケージ名と関数名を繋げることで使えます。

そして、『Fprint』関数に渡すデータを()の中に書き、複数ある場合は『,』で区切ります。

まず1つ目に渡すデータとして、『handler』関数が呼び出された際に渡されたデータの『w(http.ResponseWriter)』を設定します。次に送信する文字列を『”』で囲って指定します。

これでユーザーからアクセスがあった時の処理はおしまいです。

サーバーを起動する際の設定を書く

ユーザーからのアクセスは何番ポートで待機するか、どんなアクセスがどんな関数を呼び出すのか、などなどを設定して起動します。


func main() {
	muxHTTP := http.NewServeMux()
	muxHTTP.HandleFunc("/", handler)

	ServerHTTP := &http.Server{
		Addr:           ":8080",
		Handler:        muxHTTP,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	err := ServerHTTP.ListenAndServe()
	if err != nil {
		fmt.Println(err)
	}
}

設定や起動処理は13行目から29行目にある『main』関数内に記述しています。

アクセスしてきたURL(アドレス)で関数に処理を振り分ける


muxHTTP := http.NewServeMux()
muxHTTP.HandleFunc("/", handler)

『http』パッケージの『NewServeMux』という関数を呼び出し、返ってきたデータを『muxHTTP』という名前の変数に保存しています。『NewServeMux』は『ServeMux』という種類のデータを返します。

次に、『ServeMux』は『HandleFunc』という関数を使うことで新しい振り分けルールを加えることが出来ます。

例えば、今回は『/』というアドレスにアクセスがあった場合に、先程記述した『handler』関数を呼び出すように設定しています。これは、『localhost:8080/』はもちろん、『localhost:8080/hello』や『localhost:8080/こんにちは』にアクセスしても『handler』が呼び出されます。

もし『localhost:8080/hello』にアクセスがあった場合に別の処理を行いたい場合、振り分けたい場合は『HandleFunc』関数に『(“/hello”, handlerHello)』といった感じに書きます。

待機するポート番号や、読み書きのデータに関する設定をする


ServerHTTP := &http.Server{
	Addr:           ":8080",
	Handler:        muxHTTP,
	ReadTimeout:    10 * time.Second,
	WriteTimeout:   10 * time.Second,
	MaxHeaderBytes: 1 << 20,
}

Go言語には『構造体』というデータの種類があり、構造体とは複数の種類のデータをひとまとめに扱うためのものです。

例えば『人』という構造体があれば、その構造体に含まれるデータとして文字列の『名前』、日付型の『生年月日』、数値として『身長』『体重』などが考えられますよね。

今回は『http』パッケージに含まれる『Server』という構造体にデータを設定して『ServerHTTP』として保存します。必要なデータは以下の5つです。

『Addr』にはユーザーからのアクセスを待つポート番号を指定します。指定の仕方は最初に『:』として次にポート番号です。今回は『8080』番です。

『Handler』には、振り分けルールの『muxHTTP』を設定します。

『ReadTimeout』には、ユーザーからのデータの読み込み時間の限界値を設定します。今回は『10 * time.Second』を設定し、これは1秒を表す『time.Second』に10を掛けて(*)、ユーザーからデータを読み込む際に10秒が経過したらエラーが発生し、読み込みを中断します。

『WriteTimeout』は、もうお分かりかと思いますが、サーバーからユーザーにデータを送信する際に、設定した時間よりも多く時間がかかった場合に送信を中止します。

『MaxHeaderBytes』は『HTTPヘッダー』というデータの最大サイズです。数値の指定には『1 << 20』としており、これは2進数で1を左に20桁分移動させるものです。

2進数で1は10進数で1。10は2、100は4、1000は8で、左に1つずれるごとに2倍になり、今回は20回なのでサイズで考えると『1MiB』になります。

最後にサーバーを起動させる


err := ServerHTTP.ListenAndServe()
if err != nil {
	fmt.Println(err)
}

最後に『ListenAndServe』という関数を使用してサーバーを起動し、新規アクセスの待機と、アクセスがあった際の処理を行うようにします。この関数は『err』というデータを返します。

『if err != nil』の部分で err が『nil』でない場合は、発生したエラーを『Println』関数を使って表示させています。

おわりに

これで、HTTPサーバーの作り方と解説はおしまいです。

説明は長くなりましたが、コードはいたってシンプルで実用的です。実際の処理の内容は違いますが、当サイトも当記事でご紹介したコードと似たものを使用し、Go言語で作ったサーバープログラムでコンテンツを配信しています。

広告を掲載している関係で表示速度は落ちていますが、Go言語で作ったサーバーは高速です。そして、とても簡単に作ることが出来ます。

Go言語の勉強、HTTPサーバー作りの参考になれば幸いです。