Yabu.log

ITなどの雑記

Golangで作成したWEBサーバーのTCP通信(HTTP GET)をWiresharkで検証

ソースコード

サーバー側のソースコードです8888ポートでクライアントからの送信を待ち受けます

package main

import (
    "bufio"
    "fmt"
    "io/ioutil"
    "net"
    "net/http"
    "net/http/httputil"
    "strings"
)

func main() {
    listener, err := net.Listen("tcp", "localhost:8888")
    if err != nil {
        panic(err)
    }
    fmt.Println("Server is running at localhost:8888")
    for {
        conn, err := listener.Accept()
        if err != nil {
            panic(err)
        }
        go func() {
            //fmt.Printf("Accept %v\n",conn.RemoteAddr())
            //リクエストを読みこむ
            request, err := http.ReadRequest(
                bufio.NewReader(conn))
            if err != nil {
                panic(err)
            }
            dump, err := httputil.DumpRequest(request, true)
            if err != nil {
                panic(err)
            }
            fmt.Println(string(dump))

            //レスポンスを書き込む
            response := http.Response{
                StatusCode: 200,
                ProtoMajor: 1,
                ProtoMinor: 0,
                Body: ioutil.NopCloser(
                    strings.NewReader("hello world\n")),
            }
            response.Write(conn)
            conn.Close()
        }()
    }
}

クライアントの方は後述のcurlを利用します

通信方法

サーバーの方で8888のポートで受信を受け付けているのでこちらにcurlで通信を試みています

$ curl localhost:8888
hello world

サーバーの方でリクエストを受け取るとhello worldという文字列を返すようにしているのでコマンドを実行するとこちらの文字列が帰ります

localhostに対する送受信をキャプチャする準備

yuyubu.hatenablog.com

こちらの記事を参考にLoopback:Io0を選択しています

netstatコマンドを利用したTCPコネクションの状態

以下のタイミングでnetstatコマンドを実行してコネクションの状態を確認しました

サーバー起動時

サーバーは起動したタイミングではnetstatコマンドの出力に8888portの情報は含まれていません。

listner.Accept()メソッドに置いたブレイクポイントで処理を止めたタイミング

tcp4      78      0  localhost.ddi-tcp-1    localhost.53315        ESTABLISHED
tcp4       0      0  localhost.53315        localhost.ddi-tcp-1    ESTABLISHED

クライアント側のポート番号として53315が選ばれていることがわかります

なおこの状態でパケットをキャプチャすると以下の情報が得られました。 サーバーがGETを受け取ってOKを返す直前という感じです。

f:id:yuyubu:20180915165701p:plain

接続終了時

netstatコマンドにport8888の出力は含まれませんでした

次の記事でHTTP/1.1のKeep-Aliveを検証するのでこのnetstatの情報の差分は興味深い結果になると思います

キャプチャ結果

Wiresharkを起動しフィルタをtcp.port==8888に設定して通信内容を記録しました。すると下のような通信内容が得られました。

f:id:yuyubu:20180915165058p:plain

解説

  • 色枠1のところでTCPの3way HandShakeをしています
  • 色枠2のところでデータを送受信しています
  • 色枠3のところで切断処理をしています

3Way HandShake

TCPの接続開始を示すこの3つのパケットのやりとりを3 Way HandShakeと言います。

f:id:yuyubu:20180915165237p:plain

データ送受信

f:id:yuyubu:20180915165250p:plain

  • データ送信直後に、受信側が送信するパケットのACKがデータを受け取った分だけ増えていることが確認できます。
  • データ受信確認の後、送信側から送ったもので受信が確認できたオクテット数だけSEQが増えていることが確認できます

切断

f:id:yuyubu:20180915165307p:plain

サーバーから送ったFINのパケットに対してクライアントもFINのパケットを送り返しています。 こちらは確認応答のたびにACKを+1しています

感想

キャプチャ結果冒頭*1の[SYN]パケットを送った後の[RST,ACK]が送り返されて再度SYNパケットを送信している理由がわかりませんでした。 一般的な挙動には見えないのですが。。。これはGoのListnerの実装などを読めばわかるのでしょうか。次回はKeep-Aliveを実験してみたいと思います

参考

  • Goならわかるシステムプログラミング第6章TCPソケットとHTTPの実装
  • コンピュータネットワーク第5版
  • マスタリングTCP/IP入門

*1:①の上に位置する2つのパケット