20170104 go publish...22 Hello World package main import "fmt" func main() { fmt.Println(“Hello,...

Post on 14-Jul-2020

0 views 0 download

Transcript of 20170104 go publish...22 Hello World package main import "fmt" func main() { fmt.Println(“Hello,...

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/