Yabu.log

ITなどの雑記

Golangで書かれたWebサーバーでHTTP/1.1のkeep-aliveを検証する

HTTP/1.1にはKeep-Aliveという通信方法があります。HTTP/1.0の頃は1セットの通信が終わるたびに接続・切断処理が入っていたので非効率だったため、複数のリクエストが来た場合にコネクションを使い回す機能になります。 今回はGolangで書かれたサーバーを実行してWiresharkで通信の様子をキャプチャしています

コード

keep-aliveの実装コードの一部です。Requestを終わりまで読み取り、コネクションを再利用します。

//Accept後のソケットでなんども応答を返すためにループ
for {
  //タイムアウトを設定
  conn.SetReadDeadline(time.Now().Add(5 * time.Second))
  //リクエストを読み込む
  request, err := http.ReadRequest(bufio.NewReader(conn))
  if err != nil {
    //タイムアウトもしくはソケットクローズ時は終了
    //それ以外はエラーにする
    neterr, ok := err.(net.Error) //ダウンキャスト
    if ok && neterr.Timeout() {
      fmt.Println("timeout")
      break
    } else if err == io.EOF {
      break
    }
    panic(err)
  }
  //リクエストを表示
  dump, err := httputil.DumpRequest(request, true)
  if err != nil {
    panic(err)
  }
  fmt.Println(string(dump))
  content := "Hello World\n"

  //レスポンスを書き込む
  //HTTP/1.1かつ,ContentLengthの設定が必要
  response := http.Response{
    StatusCode:    200,
    ProtoMajor:    1,
    ProtoMinor:    1,
    ContentLength: int64(len(content)),
    Body: ioutil.NopCloser(
      strings.NewReader(content)),
  }
  response.Write(conn)
}

通信内容

curlで2回リクエストを投げています。成功すると2回hello worldが帰ってきます

$ curl localhost:8888 localhost:8888
hello world
hello world

キャプチャ結果

続いてWiresharkのキャプチャ結果を貼りますちょっと分かり辛いかもしれませんが、

  • 「接」がTCPの接続処理(3Way HandShake)です
  • 「通」がデータの通信処理です
  • 「断」がTCPの切断処理です(FIN)

f:id:yuyubu:20180915230426p:plain
図1.keep aliveなしの場合

f:id:yuyubu:20180915230429p:plain
図2.keep aliveありの場合

keep aliveが無い方は2回のリクエストに対してそれぞれ接続、切断処理を行なっていますが、keep aliveがある方はコネクションを使いまわして全部で1度しか接続、切断処理をしていないことがわかります。