20170104 go publish...22 hello world package main import "fmt" func main() {...
TRANSCRIPT
GO言語でWEBサーバを作れるか!?どのレベルで作るの?
2
自己紹介
岩松竜也いわまつ たつや
株式会社ビズリーチ HRMOS事業部 採用管理チーム Scalaエンジニア 2015年4月 新卒入社
最近はZ戦士などと認知されています🙄https://speakerdeck.com/iwamatsu0430/scalazdeuebuapurimocha-la-head-cha-la
3
目的
Go言語を1から学ぶ
インターネットの仕組みを学ぶ
Go言語を使ってWebサーバを作ってみる
4
時間割
第一部 Go Introduction 13:00 ~ 14:20
休憩 14:20 ~ 14:30
第二部 WEBサーバを作ろう! 14:30 ~ 16:50
休憩 16:50 ~ 17:00
第三部 低層に触れてみよう! 17:00 ~ 18:00
第一部 Golang Introduction
第一部Golang Introduction
Go言語とは
第一部Golang Introduction
7
Go言語とは
developed by
第一部Golang Introduction
8
Go言語とは主な設計者
Rob Pike Ken Thompson
UNIX、Plan9、UTF-8、 世界初のUNIX用ウィンドウシステム の開発など
左の人(右の人はDennis Ritchie) B言語、C言語、UNIXのオリジナル開発者 正規表現、UTF-8など
レジェンドすぎ…😱
第一部Golang Introduction
9
Go言語とは簡潔
不思議な文法が少ない(暗黙的な型変換とか)
使われていない変数やパッケージにうるさい
セミコロン不要、型推論ありと近代的
第一部Golang Introduction
10
Go言語とは様々な動作環境
Mac, Linux, Windows, Android, iOS
実行バイナリへクロスコンパイル可能
Goソースコード
コンパイル
第一部Golang Introduction
11
Go言語とは様々な動作環境
Mac, Linux, Windows, Android, iOS
実行バイナリへクロスコンパイル可能
Goソースコード
コンパイル
一つの環境だけでOK 例えばMacだけで
Windwosバイナリ(exe)が作成可能
すぐ使える! JVMや.NET、Ruby等のように
実行用の設定が不要!
第一部Golang Introduction
12
Go言語とは高速・高機能
コンパイル、実行が超早い
標準ライブラリがとても充実
Cっぽいけど並列処理機能、GCが標準で用意
第一部Golang Introduction
環境セットアップ
第一部Golang Introduction
14
環境セットアップ
http://golang-jp.org/
第一部Golang Introduction
15
環境セットアップ$ brew install go
$ vim ~/.bach_profile
# 設定例 (wgetの場合は配置先を指定すること) PATH=$PATH:/Users/{ユーザ名}/bin/go/1.7.4/bin # brewの場合は不要 export GOPATH=/Users/{ユーザ名}/.go # 適当な場所 export GOROOT=/Users/{ユーザ名}/bin/go/1.7.4 # brewの場合は不要
$ wget https://storage.googleapis.com/golang/go1.7.4.darwin-amd64.tar.gz
OR
AND THEN
第一部Golang Introduction
16
環境セットアップ
$ git clone https://github.com/iwamatsu0430/goeasy.git $ ./go run main.go
docker環境もあります😎
第一部Golang Introduction
Hello World
第一部Golang Introduction
18
Hello World
package main
import "fmt"
func main() { fmt.Println(“Hello, World!”) }
$ go run main.go
第一部Golang Introduction
19
Hello World
package main
import "fmt"
func main() { fmt.Println(“Hello, World!”) }
$ go run main.go
パッケージ指定 Java, Scala等と同じ
第一部Golang Introduction
20
Hello World
package main
import "fmt"
func main() { fmt.Println(“Hello, World!”) }
$ go run main.go
import文 他のパッケージを使えるように
第一部Golang Introduction
21
Hello World
package main
import "fmt"
func main() { fmt.Println(“Hello, World!”) }
$ go run main.go
main関数 プログラムの最初に呼ばれる
第一部Golang Introduction
22
Hello World
package main
import "fmt"
func main() { fmt.Println(“Hello, World!”) }
$ go run main.go
Println関数 console.log, printlnと同じ
第一部Golang Introduction
基本的な機能紹介
第一部Golang Introduction
24
基本的な機能紹介
https://play.golang.org/
$ go run main.go
書いてみよう!
OR
第一部Golang Introduction
25
変数
package main
import "fmt"
func main() { var foo int foo = 123 fmt.Println(foo) }
変数宣言 var {変数名} {型}
第一部Golang Introduction
26
変数Go言語の基本型
数値int float など
文字列 string
真理値 bool
配列[]int
[]string など
第一部Golang Introduction
27
変数値がないと…
初期値が入ってる(intの場合0)
第一部Golang Introduction
28
変数未使用の変数がいると
「使ってないぞ😡」と怒られる
第一部Golang Introduction
29
変数
package main
import "fmt"
func main() { var foo = 123
bar := 123 fmt.Println(foo) fmt.Println(bar) }
第一部Golang Introduction
30
変数
package main
import "fmt"
func main() { var foo = 123
bar := 123 fmt.Println(foo) fmt.Println(bar) }
型指定は省略可能 暗黙的に変換してくれる
第一部Golang Introduction
31
変数
package main
import "fmt"
func main() { var foo = 123
bar := 123 fmt.Println(foo) fmt.Println(bar) }
varも省略可能 変数名 := 値
第一部Golang Introduction
32
配列
package main
import "fmt"
func main() { arr := []int{1,2,3} arr = append(arr, 4) fmt.Println(arr) }
配列初期化 []型名{初期値, 初期値,,,}
[]型名 => 型名
要素の追加 非破壊的な操作になるので
arrを上書きしてあげる必要あり
第一部Golang Introduction
33
配列
arr := []int{1,2,3}
// 要素数の取得 len(arr)
// 範囲で取得 arr[1:3] // 1番目から3番目の要素を取得 arr[1:] // 1番目以降の要素を取得
// 配列同士の結合 append(arr, arr...)
基本的な使い方
詳しくは逆引きGolang (配列)でhttp://ashitani.jp/golangtips/tips_slice.html
第一部Golang Introduction
34
Map
package main
import "fmt"
func main() { m := map[string]int{"pen":100,"pineapple":200} fmt.Println(m) m["apple"] = 300 m["pen"] = 400 fmt.Println(m) }
mutableな挙動 存在しなければ追加 存在すれば更新
詳しくは逆引きGolang (Map)でhttp://ashitani.jp/golangtips/tips_map.html
第一部Golang Introduction
35
制御構文if
package main
import "fmt"
func main() { if 1 > 0 { fmt.Println("1は0より大きい!") } else if 1 == 0 { fmt.Println("1は0と同じ!") } else { fmt.Println("1は0より小さい!") } }
丸括弧()は不要 複雑な条件では付けても良い
else if スペースが必要
第一部Golang Introduction
36
制御構文if(複数の式)
package main
import "fmt"
func main() { if x := 1; x > 0 { fmt.Println("x > 0") } else { fmt.Println("x <= 0") } // fmt.Println(x) }
;で区切れる 初期化などをここでできる
最後の式が評価 boolを返す必要あり
ローカル変数 xはif内でしか扱えない
第一部Golang Introduction
37
制御構文for(基本)
package main
import "fmt"
func main() { for i := 0; i < 10; i++ { fmt.Println(i) } }
第一部Golang Introduction
38
制御構文for(while的)
package main
import "fmt"
func main() { i := 0 for i < 10 { fmt.Println(i) i++ } }
条件のみ go言語にwhileはない
第一部Golang Introduction
39
制御構文for(foreach的)
package main
import "fmt"
func main() { arr := []int{1,2,3} for i := range arr { fmt.Println(arr[i]) } }
range 位置 := range 配列
第一部Golang Introduction
40
制御構文for(foreach的2)
package main
import "fmt"
func main() { arr := []int{1,2,3} for i, num := range arr { fmt.Println(arr[i]) fmt.Println(num) } }
値も直接取れる 位置, 値 := range 配列
第一部Golang Introduction
41
制御構文
package main
import "fmt"
func main() { i := 0 for { fmt.Println(i) i++ if i >= 10 { break } } }
条件なしで無限 扱いには注意!
for(無限ループ)
break ブロックを抜ける時に使用
第一部Golang Introduction
42
制御構文
package main
import "fmt"
func main() { i := 3 switch i { case 0: fmt.Println("i = 0") case 1: fmt.Println("i = 1") default: fmt.Println("unknown") } }
switch(基本)
breakは不要 fall throughではない
第一部Golang Introduction
43
制御構文
package main
import "fmt"
func main() { i := 3 switch i { case 0: fmt.Println("i = 0") // case "1": fmt.Println("i = 1") default: fmt.Println("unknown") } }
switch(型チェック)
入力との型を確認 違う型が入ってくると コンパイルエラーになる
第一部Golang Introduction
44
制御構文
package main
import "fmt"
func main() { i := 1 switch i { case 0,1: fmt.Println("i = 0 or 1") case 2: fmt.Println("i = 2") default: fmt.Println("unknown") } }
switch(複数の条件)
,で複数の条件 処理をまとめたいときなど
第一部Golang Introduction
45
制御構文
package main
import "fmt"
func main() { i := 3 switch { case i % 2 == 0: fmt.Println("偶数") case i < 0: fmt.Println("負数") default: fmt.Println("unknown") } }
switch(if-else)
入力を置かない caseに条件を設定する
第一部Golang Introduction
46
制御構文
package main
import "fmt"
func main() { i := 4 switch { case i % 2 == 0: { fmt.Println("偶数") fmt.Println("😂 ") } default: fmt.Println("unknown") } }
switch(複数の処理)
{}で書ける 関数の呼び出しでも良い
第一部Golang Introduction
47
関数
package main
import "fmt"
func Foo() { fmt.Println("foo") }
func main() { Foo() Bar("bar!") }
func Bar(msg string) { fmt.Println(msg) }
関数の定義 func 関数名() {}
呼び出し 同じパッケージ内ならそのまま使用
引数の設定 他の言語と同じ
第一部Golang Introduction
48
関数
package main
import "fmt"
func sum(n int, m int) int { return n + m }
func main() { fmt.Println(sum(1,2)) }
返り値あり関数
返り値の定義 func 関数名() 返り値の型 {}
return 返り値を渡す
第一部Golang Introduction
49
関数
package main
import "fmt"
func sum(n int, m int) (l int) { l = n + m return }
func main() { fmt.Println(sum(1,2)) }
返り値あり関数
返り値用変数 func 関数名() (変数名 返り値の型) {}
return returnのみでよい
第一部Golang Introduction
50
関数
package main
import "fmt"
func tuple() (int, string) { return 1, "a" }
func main() { num, str := tuple() fmt.Println(num) fmt.Println(str) }
タプル
2つの返り値 変数初期化もできる
第一部Golang Introduction
51
関数
package main
import "fmt"
func tuple() (int, string) { return 1, "a" }
func main() { num, _ := tuple() fmt.Println(num) }
タプル
_で省略 値を使わない場合は
_でも大丈夫
第一部Golang Introduction
52
関数
// 関数オブジェクト foo := func(msg string) { fmt.Println(msg) } foo("hello!")
// 関数を引数に取る関数 bar := func(f func(string)) { f("world!") } bar(foo)
関数いろいろ
第一部Golang Introduction
53
関数
// 関数を引数に取って関数を返す関数 baz := func(f func(string)) func(string, int) { return func(msg string, n int) { for n > 0 { f(msg) n-- } } } baz(foo)("aaa", 3)
関数いろいろ
おっ関数型かな…?
第一部Golang Introduction
54
構造体
package main
import "fmt"
type foo struct { Bar int Baz string }
func main() { f := foo{} fmt.Println(f.Bar) f.Baz = "foobarbaz" fmt.Println(f.Baz) }
型の定義 type 型名 struct {}
フィールド定義 大文字: Public
小文字: Private(パッケージ内のみ)
型の初期化 foo型になっている
第一部Golang Introduction
55
構造体
// 初期値の設定も一緒に可能 f1 := foo{123, "foo"} fmt.Println(f1)
// フィールド名指定の初期化 f2 := foo{Baz: "foo", Bar: 123} fmt.Println(f2)
// 配列にもできる farr := []foo{f1, f2} fmt.Println(farr)
構造体いろいろ
第一部Golang Introduction
56
構造体
func (f foo) Qux() { fmt.Println(f.Baz) }
func main() { f := foo{123, "foo"} f.Qux() }
関数を持たせる
構造体の関数定義 func (変数名 型名) 関数名
第一部Golang Introduction
57
構造体
type qux struct { foo }
func (f foo) Corge() { fmt.Println(f.Baz) }
func main() { q := qux{} q.Baz = "world" q.Corge() }
埋め込み(embed)
継承 埋め込み 型名をそのまま指定
第一部Golang Introduction
58
interface
package main
import "fmt"
type foo struct {} type bar struct {}
type baz interface { Qux() }
interface宣言 type 型名 interface {}
持たせたいフィールドを指定
第一部Golang Introduction
59
interface
func (f foo) Qux() { fmt.Println("foo!") }
func (b bar) Qux() { fmt.Println("bar!") }
func main() { Corge(foo{}) Corge(bar{}) }
func Corge(b baz) { b.Qux() }
フィールドを実装 それぞれ実装すれば
同じinterfaceとみなせる
引数をinterfaceに 同じフィールドを持っていればOK
ダックタイピング的
第一部Golang Introduction
60
interface
package main
import "fmt"
func main() { f := func(x interface{}) { fmt.Println(x) } f(1) f("hoge") }
ちなみに
Any的な使い方も! 空のinterface{}を指定する
第一部Golang Introduction
var x int = 1 var y *int = &x fmt.Println(x) // ① fmt.Println(y) // ② fmt.Println(*y) // ③
61
ポインタ
yはxのポインタ メモリのアドレスを示している
第一部Golang Introduction
62
ポインタ
一個のマスごとにデータが入っている
メモリのイメージ
0x00000001 0x00000010 0x00000011
それぞれアドレスが割り振られている
第一部Golang Introduction
63
ポインタ
変数x
メモリ
①の場合
値が格納される xはアドレスを記憶
1
第一部Golang Introduction
1
64
ポインタ
変数x
メモリ
①の場合
値を取得 xの値をください
fmt.Println(x)
第一部Golang Introduction
65
ポインタ
変数x
メモリ
①の場合
アドレスを参照 ここにあるで
1
fmt.Println(x)
第一部Golang Introduction
66
ポインタ
変数x
メモリ
①の場合
1
fmt.Println(x)
変数xの値を取得 x = 1
第一部Golang Introduction
67
ポインタ
メモリ
②の場合
1
値が格納される xはアドレスを記憶
変数x 変数y
第一部Golang Introduction
68
ポインタ
メモリ
②の場合
1
変数x 変数y
アドレスのみ取得 y := &x
&をつけるとアドレスの取得となる
第一部Golang Introduction
69
ポインタ
メモリ
②の場合
1
変数x 変数yfmt.Println(y)
yの値を取得 値をください
第一部Golang Introduction
70
ポインタ
メモリ
②の場合
1
変数x 変数yfmt.Println(y)
xのアドレスを返す 持ってるのはこれだけやで
変数yの値を取得 y = 0x1040e0f8
第一部Golang Introduction
71
ポインタ
メモリ
③の場合
1
変数x 変数yfmt.Println(*y)
実体をください アドレスの先にある値を取得
第一部Golang Introduction
72
ポインタ
メモリ
③の場合
1
変数x 変数yfmt.Println(*y)
値を参照 ここやで
第一部Golang Introduction
73
ポインタ
メモリ
③の場合
1
変数x 変数yfmt.Println(*y)
変数yの実体を参照 y = 1
第一部Golang Introduction
74
ポインタ何が嬉しいの?
メモリを効率的に使える
第一部Golang Introduction
75
ポインタ何が嬉しいの?
第一部Golang Introduction
76
ポインタ何が嬉しいの?
アドレスが違う! 値だけコピーされる
第一部Golang Introduction
77
ポインタ何が嬉しいの?
第一部Golang Introduction
78
ポインタ何が嬉しいの?
アドレスが違う! 値だけコピーされる
第一部Golang Introduction
79
ポインタ何が嬉しいの?
第一部Golang Introduction
80
ポインタ何が嬉しいの?
アドレスが違う! 同じ値なのに
メモリがちょっともったいない…
第一部Golang Introduction
81
ポインタ何が嬉しいの?
第一部Golang Introduction
82
ポインタ何が嬉しいの?
同じアドレスのまま ひとつのアドレスだけで
演算できた!😂
第一部Golang Introduction
83
パッケージ
package main
import "fmt"
func main() { fmt.Println("Hello") }
import文 他のパッケージを使えるように
第一部Golang Introduction
84
パッケージ
package main
import ( "fmt" "math" )
func main() { fmt.Println("Pi =", math.Pi) }
複数import 丸括弧()で
Printlnの使い方 ,区切りで変数を入れることが出来る
第一部Golang Introduction
85
パッケージ自分のパッケージを作る
第一部Golang Introduction
86
パッケージ
package mypackage
import "fmt"
func Hello() { fmt.Println("Hello!") }
自分のパッケージを作るhello.go
第一部Golang Introduction
87
パッケージ
package main
import ( "fmt" "./mypackage" )
func main() { mypackage.Hello() fmt.Println("World!") }
自分のパッケージを作るmain.go
手元のパッケージ ./から始めること
第一部Golang Introduction
88
パッケージgithub上のパッケージ
$ go get github.com/iwamatsu0430/hwgo
github上のパッケージをDLしてくれる
https://github.com/iwamatsu0430/hwgo
$ ./go get github.com/iwamatsu0430/hwgo
第一部Golang Introduction
89
パッケージgithub上のパッケージ
package main
import "github.com/iwamatsu0430/hwgo"
func main() { hwgo.Hello() hwgo.World() hwgo.Say("A Happy New Year 2017!") }
第一部Golang Introduction
90
パッケージ紹介
https://golang.org/pkg/
標準パッケージ
第一部Golang Introduction
91
パッケージ紹介
http://go-search.org/
パッケージ検索
第一部Golang Introduction
92
パッケージ紹介
https://godoc.org/
人気パッケージ
第一部Golang Introduction
93
パッケージ紹介
巨人の肩の上に乗ろう!
第二部 WEBサーバを作ろう!
第二部WEBサーバを作ろう!
ざっくりWEB
第二部WEBサーバを作ろう!
96
ざっくりWEB
HTML CSS JavaScript
それぞれフォーマットが定められている
① WEBサイトのコンテンツ
第二部WEBサーバを作ろう!
97
ざっくりWEB② コンテンツの送信
HTML CSS JavaScript
同様にHTMLやCSS、JavaScriptの 送り方にもフォーマットが定められている
第二部WEBサーバを作ろう!
98
ざっくりWEB③ コンテンツのリクエスト
コンテンツをリクエストする際の フォーマットも定められている
hrmos.co ください
第二部WEBサーバを作ろう!
99
ざっくりWEB④ 接続
そもそもリクエストをする前の ユーザとサーバの接続にも決め事がある
WEBはたくさんの決め事(プロトコル) の上で成り立っている
第二部WEBサーバを作ろう!
100
ざっくりWEB
逆に言えばプロトコルを遵守すれば 自分たちでWEBが作れる!💪
第二部WEBサーバを作ろう!
OSI参照モデル
第二部WEBサーバを作ろう!
102
OSI参照モデル第七層 アプリケーション層 HTTP,SMTPなど
第六層 プレゼンテーション層 SMTP,FTPなど
第五層 セッション層 TLS,NetBIOSなど
第四層 トランスポート層 TCP,UDPなど
第三層 ネットワーク層 IP,ARP,ICMPなど
第二層 データリンク層 PPP,Ethernetなど
第一層 物理層 光ケーブル,無線など
あくまで参考
第二部WEBサーバを作ろう!
103
OSI参照モデル第七層 アプリケーション層 HTTP,SMTPなど
第六層 プレゼンテーション層 SMTP,FTPなど
第五層 セッション層 TLS,NetBIOSなど
第四層 トランスポート層 TCP,UDPなど
第三層 ネットワーク層 IP,ARP,ICMPなど
第二層 データリンク層 PPP,Ethernetなど
第一層 物理層 光ケーブル,無線など
あくまで参考
①HTMLとか 多分この辺
第二部WEBサーバを作ろう!
104
OSI参照モデル第七層 アプリケーション層 HTTP,SMTPなど
第六層 プレゼンテーション層 SMTP,FTPなど
第五層 セッション層 TLS,NetBIOSなど
第四層 トランスポート層 TCP,UDPなど
第三層 ネットワーク層 IP,ARP,ICMPなど
第二層 データリンク層 PPP,Ethernetなど
第一層 物理層 光ケーブル,無線など
あくまで参考
②③HTTP どういうフォーマット(プロトコル)で
送受信するか定義
第二部WEBサーバを作ろう!
105
OSI参照モデル第七層 アプリケーション層 HTTP,SMTPなど
第六層 プレゼンテーション層 SMTP,FTPなど
第五層 セッション層 TLS,NetBIOSなど
第四層 トランスポート層 TCP,UDPなど
第三層 ネットワーク層 IP,ARP,ICMPなど
第二層 データリンク層 PPP,Ethernetなど
第一層 物理層 光ケーブル,無線など
あくまで参考
④接続 この層より下が当てはまる
第二部WEBサーバを作ろう!
106
OSI参照モデル第七層 アプリケーション層 HTTP,SMTPなど
第六層 プレゼンテーション層 SMTP,FTPなど
第五層 セッション層 TLS,NetBIOSなど
第四層 トランスポート層 TCP,UDPなど
第三層 ネットワーク層 IP,ARP,ICMPなど
第二層 データリンク層 PPP,Ethernetなど
第一層 物理層 光ケーブル,無線など
以上の部分を自分たちで実装してみる
第二部WEBサーバを作ろう!
簡単なレスポンスを返そう
第二部WEBサーバを作ろう!
108
簡単なレスポンスを返そう
package main
import ( "fmt" "net" "os" )
func checkError(err error) { if err != nil { os.Exit(1) } }
エラーチェック エラーになっている場合、 プログラムを終了する
エラー型 Go言語標準の型 エラー情報を持つ
第二部WEBサーバを作ろう!
109
簡単なレスポンスを返そう
func main() { tcpAddr, err := net.ResolveTCPAddr("tcp", ":5555") checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr) checkError(err)
fmt.Print("accepting ... ") conn, err := listener.Accept() defer conn.Close() if err == nil { fmt.Println("!") conn.Write([]byte("hello")) conn.Write([]byte("\r\n")) } }
第二部WEBサーバを作ろう!
110
簡単なレスポンスを返そう
とりあえずなんか返ってきた👏
$ curl -v http://localhost:5555
第二部WEBサーバを作ろう!
111
簡単なレスポンスを返そう
func main() { tcpAddr, err := net.ResolveTCPAddr("tcp", ":5555") checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr) checkError(err)
fmt.Print("accepting ... ") conn, err := listener.Accept() defer conn.Close() if err == nil { fmt.Println("!") conn.Write([]byte("hello")) conn.Write([]byte("\r\n")) } }
TCPアドレスへ IPv4のアドレス情報(構造体)へ
パースする
エラーチェック パースに失敗したら終了
アドレスの指定 省略形だが、localhostの
5555ポートを使用するという意味
第二部WEBサーバを作ろう!
112
簡単なレスポンスを返そう
func main() { tcpAddr, err := net.ResolveTCPAddr("tcp", ":5555") checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr) checkError(err)
fmt.Print("accepting ... ") conn, err := listener.Accept() defer conn.Close() if err == nil { fmt.Println("!") conn.Write([]byte("hello")) conn.Write([]byte("\r\n")) } }
ポートの確保 既に使用されていれば ここでエラーとなる
第二部WEBサーバを作ろう!
113
簡単なレスポンスを返そう
func main() { tcpAddr, err := net.ResolveTCPAddr("tcp", ":5555") checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr) checkError(err)
fmt.Print("accepting ... ") conn, err := listener.Accept() defer conn.Close() if err == nil { fmt.Println("!") conn.Write([]byte("hello")) conn.Write([]byte("\r\n")) } }
接続待ち クライアントからの接続を
待ち続ける
defer メソッド終了時に必ず呼ばれる
第二部WEBサーバを作ろう!
114
簡単なレスポンスを返そう
panicとdefer
package main
import "fmt"
func main() { hello() }
func hello() { defer fmt.Println("defered") }
defer メソッド終了時に必ず呼ばれる
第二部WEBサーバを作ろう!
115
簡単なレスポンスを返そう
panicとdefer
package main
import "fmt"
func main() { hello() }
func hello() { defer fmt.Println(“defered") panic("Runtime Error") }
panic 実行時エラーの呼び出し
何らかの理由で発生することはある
第二部WEBサーバを作ろう!
116
簡単なレスポンスを返そう
panicとdefer
エラーの後もdeferが動く💪
第二部WEBサーバを作ろう!
117
簡単なレスポンスを返そう
panicとdeferとrecover
package main
import "fmt"
func main() { hello() fmt.Println("I'm alive") }
func hello() { defer fmt.Println("defered") panic("Runtime Error") }
第二部WEBサーバを作ろう!
118
簡単なレスポンスを返そう
panicとdeferとrecover
処理は続かない😢
第二部WEBサーバを作ろう!
119
簡単なレスポンスを返そう
panicとdeferとrecoverpackage main
import "fmt"
func main() { hello() fmt.Println("I'm alive") }
func hello() { defer func(){ fmt.Println("defered") recover() }() panic("Runtime Error") }
recover panicからの復旧
errorの内容も返してくれる
第二部WEBサーバを作ろう!
120
簡単なレスポンスを返そう
panicとdeferとrecover
処理が続いた!☺
第二部WEBサーバを作ろう!
121
簡単なレスポンスを返そう
func main() { tcpAddr, err := net.ResolveTCPAddr("tcp", ":5555") checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr) checkError(err)
fmt.Print("accepting ... ") conn, err := listener.Accept() defer conn.Close() if err == nil { fmt.Println("!") conn.Write([]byte("hello")) conn.Write([]byte("\r\n")) } }
キャスト バイト配列へキャストしている
型名(値)
レスポンス出力 バイト配列が必要
改行コード 出力の終わりを明示している
第二部WEBサーバを作ろう!
122
簡単なレスポンスを返そう
func main() { tcpAddr, err := net.ResolveTCPAddr("tcp", ":5555") checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr) checkError(err)
fmt.Print("accepting ... ") conn, err := listener.Accept() defer conn.Close() if err == nil { fmt.Println("!") conn.Write([]byte("hello")) conn.Write([]byte("\r\n")) } }
キャスト バイト配列へキャストしている
型名(値)
UTF-8となる 他の文字コードを扱う場合は 別途パッケージが必要になる
第二部WEBサーバを作ろう!
123
簡単なレスポンスを返そう
func listen(listener *net.TCPListener) { fmt.Print("accepting ... ") conn, err := listener.Accept() defer conn.Close() if err == nil { fmt.Println("!") conn.Write([]byte("hello")) conn.Write([]byte("\r\n")) } }
処理の切り出し
第二部WEBサーバを作ろう!
124
簡単なレスポンスを返そう
func main() { tcpAddr, err := net.ResolveTCPAddr("tcp", ":5555") checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr) checkError(err)
for { listen(listener) } }
永続化
第二部WEBサーバを作ろう!
125
簡単なレスポンスを返そう
func main() { tcpAddr, err := net.ResolveTCPAddr("tcp", ":5555") checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr) checkError(err)
for { listen(listener) } }
永続化
forを利用 無限ループにしてやる
第二部WEBサーバを作ろう!
126
簡単なレスポンスを返そう
何度でもレスポンスが返せるように☺
第二部WEBサーバを作ろう!
RFCを読んでみよう
第二部WEBサーバを作ろう!
128
RFCを読んでみようブラウザからアクセス
まだHTTPじゃない😭
第二部WEBサーバを作ろう!
129
RFCを読んでみよう
第二部WEBサーバを作ろう!
130
RFCを読んでみよう
必要なことは大体書いてある😂
第二部WEBサーバを作ろう!
HTMLを返そう
第二部WEBサーバを作ろう!
132
HTMLを返そう
この時間でRFCを見るのは流石にきついので…
$ curl -v https://hrmos.co
curlでレスポンスを見て、真似てみよう
とはいえ…
第二部WEBサーバを作ろう!
133
HTMLを返そう
サーバサイドの方は 周りのフォローお願いします🙇
15:35まで!
第二部WEBサーバを作ろう!
HTMLを返そう 模範解答
第二部WEBサーバを作ろう!
135
HTMLを返そう 模範解答
import ( "fmt" "net" "os" "strconv" ) strconv
文字列変換などのパッケージ
第二部WEBサーバを作ろう!
136
HTMLを返そう 模範解答
func createHtml() ([]byte, int) { content := []byte(`<html> <body> <h1>Hello, My WEB Server!</h1> </body> </html>`) return content, len(content) }
バッククウォート `で文字列を囲むと
改行などをそのまま扱える
第二部WEBサーバを作ろう!
137
HTMLを返そう 模範解答
func listen(listener *net.TCPListener) { conn, err := listener.Accept() defer conn.Close() if err == nil { content, contentLength := createHtml() header := "HTTP/1.1 200 OK" header += "\r\n" header += "Content-Type: text/html" header += "\r\n" header += "Content-Length: " + strconv.Itoa(contentLength) header += "\r\n" header += "\r\n" conn.Write([]byte(header)) conn.Write(content) conn.Write([]byte("\r\n")) } }
int => string 暗黙的な変換はない
第二部WEBサーバを作ろう!
138
HTMLを返そう 模範解答
ブラウザからアクセス
表示された!😉
第二部WEBサーバを作ろう!
ファイルを返そう
第二部WEBサーバを作ろう!
140
ファイルを返そう
適当なHTMLファイルを準備
$ echo "<html><body>I am File</body></html>" > index.html
このファイルを読み取り、返せるようにする
第二部WEBサーバを作ろう!
141
ファイルを返そう勘所
ファイルを開く
file, err = os.Open(“./index.html")
ファイルの読み込み
for { bytes := make([]byte, 1024) _, err := file.Read(bytes) if err != nil { break } }
第二部WEBサーバを作ろう!
142
ファイルを返そう勘所
ファイルを開く
file, err = os.Open(“./index.html")
ファイルの読み込み
for { bytes := make([]byte, 1024) _, err := file.Read(bytes) if err != nil { break } }
読み込みバッファ 1024バイトずつ読み込む
第二部WEBサーバを作ろう!
143
ファイルを返そう勘所
ファイルを開く
file, err = os.Open(“./index.html")
ファイルの読み込み
for { bytes := make([]byte, 1024) _, err := file.Read(bytes) if err != nil { break } }
Read bytesに読み込まれたデータが
入ってくる
第二部WEBサーバを作ろう!
144
ファイルを返そう勘所
ファイルを開く
file, err = os.Open(“./index.html")
ファイルの読み込み
for { bytes := make([]byte, 1024) _, err := file.Read(bytes) if err != nil { break } }
返り値 1つ目の返り値には、実際に
読み込んだデータのサイズが入る
第二部WEBサーバを作ろう!
145
ファイルを返そう勘所
ファイルを開く
file, err = os.Open(“./index.html")
ファイルの読み込み
for { bytes := make([]byte, 1024) _, err := file.Read(bytes) if err != nil { break } }
ファイルの終わり 全て読み込むまでループ
第二部WEBサーバを作ろう!
146
ファイルを返そう読み込みイメージ
バッファ(1024byte)
ファイル(???byte)
第二部WEBサーバを作ろう!
147
ファイルを返そう読み込みイメージ
バッファ(1024byte)
ファイル(???byte)読み込み 1024byte
第二部WEBサーバを作ろう!
148
ファイルを返そう読み込みイメージ
バッファ(1024byte)
ファイル(???byte)読み込み 1024byte
第二部WEBサーバを作ろう!
149
ファイルを返そう読み込みイメージ
バッファ(1024byte)
ファイル(???byte)読み込み 1024byte
第二部WEBサーバを作ろう!
150
ファイルを返そう読み込みイメージ
バッファ(1024byte)
ファイル(???byte)読み込み 1024byte
第二部WEBサーバを作ろう!
151
ファイルを返そう読み込みイメージ
バッファ(1024byte)
ファイル(???byte)読み込み終了 これ以上読み込めない
第二部WEBサーバを作ろう!
152
ファイルを返そう
サーバサイドの方は 周りのフォローお願いします🙇
16:10まで!
第二部WEBサーバを作ろう!
ファイルを返そう 模範解答
第二部WEBサーバを作ろう!
154
ファイルを返そう 模範解答
func createHtml() (content []byte, contentLength int) { file, err := os.Open("./index.html") if err != nil { return } for { bytes := make([]byte, 1024) length, err := file.Read(bytes) content = append(content, bytes…) contentLength += length if err != nil { break } } return }
第二部WEBサーバを作ろう!
155
ファイルを返そう 模範解答
ブラウザからアクセス
表示された!😉
第二部WEBサーバを作ろう!
リクエストをパース
第二部WEBサーバを作ろう!
157
リクエストをパース
コンテンツをリクエストする際の フォーマットも定められている
index.html ください
style.css ください
第二部WEBサーバを作ろう!
158
リクエストをパース
index.html ください
style.css ください
適切なファイルを返せるようにしよう
第二部WEBサーバを作ろう!
159
リクエストをパース勘所
for { bytes := make([]byte, 1024) length, err := conn.Read(bytes) }
ファイルの読み込みとほぼ同じ!
第二部WEBサーバを作ろう!
160
リクエストをパース勘所
[]byte => string
string(bytes)
文字列を改行で分割
import “strings"
strings.Split(str, "\n")
第二部WEBサーバを作ろう!
161
リクエストをパース
サーバサイドの方は 周りのフォローお願いします🙇
16:50まで!
第二部WEBサーバを作ろう!
リクエストをパース 模範解答
第二部WEBサーバを作ろう!
163
リクエストをパース 模範解答
import ( "bytes" "net" "os" "strconv" )
func readRequest(conn net.Conn) (request []byte) { for { bytes := make([]byte, 1024) length, _ := conn.Read(bytes) request = append(request, bytes...) if length < 1024 { return } } }
第二部WEBサーバを作ろう!
164
リクエストをパース 模範解答
func getTarget(request []byte) (target string) { rows := bytes.Split(request, []byte("\r\n")) if len(rows) > 0 { params := bytes.Split(rows[0], []byte(" ")) if len(params) >= 2 { target = string(params[1]) } } return }
バイト配列のSplit stringに戻さなくても
色々処理できる
第二部WEBサーバを作ろう!
165
リクエストをパース 模範解答
func loadFile(target string) (content []byte, contentLength int) { file, err := os.Open("." + target) if err != nil { return } for { bytes := make([]byte, 1024) length, err := file.Read(bytes) content = append(content, bytes...) contentLength += length if err != nil { break } } return }
第二部WEBサーバを作ろう!
166
リクエストをパース 模範解答
func createHeader(contentLength int) (header string) { header += "HTTP/1.1 200 OK" header += "\r\n" header += "Content-Type: text/html" header += "\r\n" header += "Content-Length: " + strconv.Itoa(contentLength) header += "\r\n" header += "\r\n" return }
第二部WEBサーバを作ろう!
167
リクエストをパース 模範解答
func listen(listener *net.TCPListener) { conn, err := listener.Accept() defer conn.Close() request := readRequest(conn) target := getTarget(request) if err == nil { content, contentLength := loadFile(target) header := createHeader(contentLength) conn.Write([]byte(header)) conn.Write(content) conn.Write([]byte("\r\n")) } }
第二部WEBサーバを作ろう!
168
リクエストをパース 模範解答
ブラウザからアクセス
簡易なWEBサーバになった😎
第二部WEBサーバを作ろう!
169
リクエストをパース 模範解答
もっと改良する場合は
ファイルがない場合の処理(404)
GETやPOSTを判別できるように
POSTのパラメータを受け取ってみる
第三部 低層に触れてみよう!
第三部低層に触れてみよう!
ざっくり低層
第二部WEBサーバを作ろう!
172
ざっくり低層第七層 アプリケーション層 HTTP,SMTPなど
第六層 プレゼンテーション層 SMTP,FTPなど
第五層 セッション層 TLS,NetBIOSなど
第四層 トランスポート層 TCP,UDPなど
第三層 ネットワーク層 IP,ARP,ICMPなど
第二層 データリンク層 PPP,Ethernetなど
第一層 物理層 光ケーブル,無線など
このあたりにも挑戦…
第二部WEBサーバを作ろう!
173
ざっくり低層物理層
1 or 0で電気信号が流れてくる
信号の受取 10001100…
第二部WEBサーバを作ろう!
174
ざっくり低層データリンク層
これはethernet 一定のプロトコルに従っている事を
確認し情報をパース
第二部WEBサーバを作ろう!
175
ざっくり低層データリンク層より↑
ethernetヘッダ IPヘッダ TCPヘッダ データ
IPヘッダ TCPヘッダ データ
TCPヘッダ データ
第二層
第三層
第四層
層と言っても単純な構造☺
第三部低層に触れてみよう!
Pingを送ってみよう
第二部WEBサーバを作ろう!
177
ざっくり低層
第七層 アプリケーション層 HTTP,SMTPなど
第六層 プレゼンテーション層 SMTP,FTPなど
第五層 セッション層 TLS,NetBIOSなど
第四層 トランスポート層 TCP,UDPなど
第三層 ネットワーク層 IP,ARP,ICMPなど
第二層 データリンク層 PPP,Ethernetなど
第一層 物理層 光ケーブル,無線など
Pingとは
ICMPの一種 通知タイプの一つ Echo, Echo Reply
第三部低層に触れてみよう!
178
Pingを送ってみようPing情報の確認
package main
import ( "os" "fmt" "syscall" )
receive.go
第三部低層に触れてみよう!
179
Pingを送ってみようPing情報の確認
func main() { fd, _ := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_ICMP) f := os.NewFile(uintptr(fd), fmt.Sprintf("fd %d", fd)) for { buffer := make([]byte, 1024) length, err := f.Read(buffer) if err != nil { fmt.Println(err) } fmt.Println("length:", length) fmt.Println(buffer[:length]) fmt.Printf("% X\n", buffer[:length]) fmt.Println("") } }
receive.go
第三部低層に触れてみよう!
180
Pingを送ってみよう
$ sudo -E go run receive.go
sudo必須!
$ ping 8.8.8.8
第三部低層に触れてみよう!
181
Pingを送ってみようEcho Replyを表示
ping 8.8.8.8 Echo Request
第三部低層に触れてみよう!
182
Pingを送ってみようEcho Replyを表示
pingへの応答 Echo Reply
表示 自分に返信されたpingを表示
第三部低層に触れてみよう!
183
Pingを送ってみようパケットの構成
MACヘッダ IPヘッダ ICMPヘッダ データ
青色の部分が取得できている
Version ヘッダ長 サービス種別 …
69 192 64 0 152 108 0 0 53 1 25 96 8 8 8 8 192 168 2 101 0 0 50 159 32 37 0 2 88 108 41 35 0 5 64 162 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
45 C0 40 00 98 6C 00 00 35 01 19 60 08 08 08 08 C0 A8 02 65 00 00 32 9F 20 25 00 02 58 6C 29 23 00 05 40 A2 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37
第三部低層に触れてみよう!
184
Pingを送ってみようパケットの構成
MACヘッダ IPヘッダ ICMPヘッダ データ
青色の部分が取得できている
Version ヘッダ長 サービス種別 …
69 192 64 0 152 108 0 0 53 1 25 96 8 8 8 8 192 168 2 101 0 0 50 159 32 37 0 2 88 108 41 35 0 5 64 162 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
45 C0 40 00 98 6C 00 00 35 01 19 60 08 08 08 08 C0 A8 02 65 00 00 32 9F 20 25 00 02 58 6C 29 23 00 05 40 A2 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37
version IPv4の場合は4が入る
第三部低層に触れてみよう!
185
Pingを送ってみようパケットの構成
MACヘッダ IPヘッダ ICMPヘッダ データ
青色の部分が取得できている
Version ヘッダ長 サービス種別 …
69 192 64 0 152 108 0 0 53 1 25 96 8 8 8 8 192 168 2 101 0 0 50 159 32 37 0 2 88 108 41 35 0 5 64 162 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
45 C0 40 00 98 6C 00 00 35 01 19 60 08 08 08 08 C0 A8 02 65 00 00 32 9F 20 25 00 02 58 6C 29 23 00 05 40 A2 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37
ヘッダ長 通常は5が入る
第三部低層に触れてみよう!
186
Pingを送ってみようパケットの構成
MACヘッダ IPヘッダ ICMPヘッダ データ
青色の部分が取得できている
Version ヘッダ長 サービス種別 …
69 192 64 0 152 108 0 0 53 1 25 96 8 8 8 8 192 168 2 101 0 0 50 159 32 37 0 2 88 108 41 35 0 5 64 162 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
45 C0 40 00 98 6C 00 00 35 01 19 60 08 08 08 08 C0 A8 02 65 00 00 32 9F 20 25 00 02 58 6C 29 23 00 05 40 A2 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37
サービス種別 Echo Reply
第三部低層に触れてみよう!
187
Pingを送ってみようパケットの構成
MACヘッダ IPヘッダ ICMPヘッダ データ
青色の部分が取得できている
Version ヘッダ長 サービス種別 …
69 192 64 0 152 108 0 0 53 1 25 96 8 8 8 8 192 168 2 101 0 0 50 159 32 37 0 2 88 108 41 35 0 5 64 162 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
45 C0 40 00 98 6C 00 00 35 01 19 60 08 08 08 08 C0 A8 02 65 00 00 32 9F 20 25 00 02 58 6C 29 23 00 05 40 A2 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37
送信元アドレス 先程のping送信先と一致
第三部低層に触れてみよう!
188
Pingを送ってみようパケットの構成
MACヘッダ IPヘッダ ICMPヘッダ データ
青色の部分が取得できている
Version ヘッダ長 サービス種別 …
69 192 64 0 152 108 0 0 53 1 25 96 8 8 8 8 192 168 2 101 0 0 50 159 32 37 0 2 88 108 41 35 0 5 64 162 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
45 C0 40 00 98 6C 00 00 35 01 19 60 08 08 08 08 C0 A8 02 65 00 00 32 9F 20 25 00 02 58 6C 29 23 00 05 40 A2 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37
宛先アドレス 自身のIPアドレスと一致
第三部低層に触れてみよう!
189
Pingを送ってみようパケットの構成
MACヘッダ IPヘッダ ICMPヘッダ データ
青色の部分が取得できている
Type Code CheckSum …
69 192 40 0 197 103 0 0 64 1 46 227 192 168 2 1 192 168 2 101 3 3 130 209 0 0 0 0 69 0 32 0 79 255 0 0 64 17 165 23 192 168 2 101 192 168 2 1 199 102 0 192 0 12 166 231 8 1 3 16
45 C0 28 00 C5 67 00 00 40 01 2E E3 C0 A8 02 01 C0 A8 02 65 03 03 82 D1 00 00 00 00 45 00 20 00 4F FF 00 00 40 11 A5 17 C0 A8 02 65 C0 A8 02 01 C7 66 00 C0 00 0C A6 E7 08 01 03 10
第三部低層に触れてみよう!
190
Pingを送ってみようパケットの構成
MACヘッダ IPヘッダ ICMPヘッダ データ
青色の部分が取得できている
Type Code CheckSum …
69 192 40 0 197 103 0 0 64 1 46 227 192 168 2 1 192 168 2 101 3 3 130 209 0 0 0 0 69 0 32 0 79 255 0 0 64 17 165 23 192 168 2 101 192 168 2 1 199 102 0 192 0 12 166 231 8 1 3 16
45 C0 28 00 C5 67 00 00 40 01 2E E3 C0 A8 02 01 C0 A8 02 65 03 03 82 D1 00 00 00 00 45 00 20 00 4F FF 00 00 40 11 A5 17 C0 A8 02 65 C0 A8 02 01 C7 66 00 C0 00 0C A6 E7 08 01 03 10
Type Replyの場合は0
第三部低層に触れてみよう!
191
Pingを送ってみようパケットの構成
MACヘッダ IPヘッダ ICMPヘッダ データ
青色の部分が取得できている
Type Code CheckSum …
69 192 40 0 197 103 0 0 64 1 46 227 192 168 2 1 192 168 2 101 3 3 130 209 0 0 0 0 69 0 32 0 79 255 0 0 64 17 165 23 192 168 2 101 192 168 2 1 199 102 0 192 0 12 166 231 8 1 3 16
45 C0 28 00 C5 67 00 00 40 01 2E E3 C0 A8 02 01 C0 A8 02 65 03 03 82 D1 00 00 00 00 45 00 20 00 4F FF 00 00 40 11 A5 17 C0 A8 02 65 C0 A8 02 01 C7 66 00 C0 00 0C A6 E7 08 01 03 10
Code こちらも0が入る
第三部低層に触れてみよう!
192
Pingを送ってみようPingの送信
import ( "encoding/binary" "fmt" "net" "os" "time" "syscall" "golang.org/x/net/ipv4" )
send.go
$ go get golang.org/x/net/ipv4
第三部低層に触れてみよう!
193
Pingを送ってみようPingの送信
func checksum(b []byte) uint16 { count := len(b) sum := uint32(0) for i := 0; i < count-1; i += 2 { sum += uint32(b[i])<<8 | uint32(b[i+1]) } if count&1 != 0 { sum += uint32(b[count-1]) << 8 } for (sum >> 16) > 0 { sum = (sum & 0xffff) + (sum >> 16) } return ^(uint16(sum)) }
send.go
<< >> シフト演算子
5(101) << 2 => 20(10100)
第三部低層に触れてみよう!
194
Pingを送ってみようPingの送信
func createICMPPackets() []byte { now := time.Now() tb, _ := now.MarshalBinary() icmp := make([]byte, 8+len(tb)) icmp[0] = byte(8) icmp[1] = byte(0) binary.BigEndian.PutUint16(icmp[4:6], uint16(os.Getpid() & 0xffff)) binary.BigEndian.PutUint16(icmp[6:8], uint16(0)) copy(icmp[8:], tb) cs := checksum(icmp) icmp[2] = byte(cs >> 8) icmp[3] = byte(cs) return icmp }
send.go
現在時刻 受信処理とセットにすれば送信に かかった時間を求められる(未対応)
第三部低層に触れてみよう!
195
Pingを送ってみようPingの送信
func createPackets() []byte { icmp := createICMPPackets() header := ipv4.Header{ Version: 4, Len: 20, TotalLen: 20 + len(icmp), TTL: 64, Protocol: 1, Dst: net.IPv4(127, 0, 0, 1), } headerBytes, _ := header.Marshal() return append(headerBytes, icmp...) }
send.go
IPヘッダ IPヘッダ用の構造体(Map)があるので
利用する
Marshal 構造体からバイト配列へ変換
第三部低層に触れてみよう!
196
Pingを送ってみようPingの送信
func main() { fd, _ := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW) syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_HDRINCL, 1)
addr := syscall.SockaddrInet4{ Port: 0, Addr: [4]byte{127, 0, 0, 1}, }
packets := createPackets() err := syscall.Sendto(fd, packets, 0, &addr) if err != nil { fmt.Println("Sendto:", err) } }
send.go
謎のおまじない… システムコールで何をしているか まだちょっと分かっていません🙇
第三部低層に触れてみよう!
197
Pingを送ってみようPingの送信
$ sudo -E go receive.go
$ sudo -E go send.go
返ってきた!😂
第三部低層に触れてみよう!
198
Pingを送ってみようTCPも自分で作れそう…?
とても大変そうなので断念😱
199
まとめ
Go言語にざっくり慣れられた?
WEBサーバをなんとなく作れた!
各自ブラッシュアップしてもられば!
ありがとうございました!
201
Credit
https://github.com/tenntenn/gopher-stickers
Gopher Stickers
The Go gopher was designed by Renee French. (http://reneefrench.blogspot.com/) The gopher stickers was made by Takuya Ueda (https://twitter.com/tenntenn). Licensed under the Creative Commons 3.0 Attributions license.
202
Credit
Raw sockets in Go: Link layerhttp://www.darkcoding.net/software/raw-sockets-in-go-link-layer/
Go で pinghttp://tyamagu2.xyz/articles/go_ping/