C:\Users\ebata\goga\1-10>のI_hate_go_server.md が本体です。
1. Golangのサーバなんか大嫌い
このドキュメントは、絶対的な意味において「無保証」です
Golangで作るサーバは、HandleやらHandlerやら、ハンドル、ハンドルとうるさい! と叫びたくなること、甚しいです。
さすがに、C言語のfork()まで戻りたいとは思えませんが、『あれは、あれで、何をやっているのか分かった』とは言えました。
で、もう正しい理解かどうかは、無視して、もう、誰の話も聞かん! 江端はこういう風に理解すると決めた!! ことを記載しておきます。
2. サーバ側の江端の理解
2.1. http.Handle()は、ブラウザに入力するURLと、index.htmlの場所を教えるものである
http.Handle("/", http.FileServer(http.Dir(".")))
は、https://xxx.xxx/ でアクセスできて(http://xxx.xxx/yyyy のように"yyyy"はない)、index.htmlが、goのサーバのプログラムと同じディレクトリ(".")にいる、と宣言するもの。
http.Handle("/tomo", http.FileServer(http.Dir("./js")))
であれば、https://xxx.xxx/tomo でアクセスできてindex.htmlが、goのサーバのプログラムと同じディレクトリのしたのjs("./js")にいる、と宣言するもの。
2.2. "http.HandleFunc()"は、クライアントがやってくるごにに一つづつ立ち上があるfork()のようなものである→大嘘でした(現在修正検討中)
具体的には、こちらを読んで頂くと良いと思います。
repeated read on failed websocket connection (一応解決)
要するにwebブラウザ(クライアント)からのアクセスがあれば、この関数がfork()の用に立ち上って、Webブラウザとの面倒を見る。→大嘘でした
面倒見ません。
まず第一に、http.HandleFunc()の誤解がありました。私は、これを、fork()のようにプロセスかスレッドを発生さるものと思っていましたが、これは、一言で言えば、単なるコールバック関数でした。
乱暴に言えば、Webからアクセスがあると、func()というファンクションに吹っ飛ばされる、という現象を発生させる"だけ"で、それ意外のことは何にもしてくれないのです。
これは、index.htmlの内容をクライアントの押しつけるfork()関数と考えれば足る。→大嘘でした
(後で述べるが)これで、
http://localhost:8080
でアクセスできるようになる
http.ServerFileというのは、実装されているので、わざわざ main.goに書く必要はない。
一方、
http.HandleFunc("/chat", HandleClients)
は、
func HandleClients(w http.ResponseWriter, r *http.Request) {
//色々
}
で定義されている、コードをfork()のように立ち上げるものである、と考えれば足る。→大嘘でした(現在修正検討中)
単にその関数に飛んでいくだけです(但し、ソケット情報を付けてくれます)
"/chat"とは何か?
(後で述べるが)これで、
http://localhost:8080/chat
でアクセスできるようになる
2.3. "http.ListenAndServe(":8080", nil)"は、「localhost:8080をサーバにするぞ」を実行するものである
上記の関数は、"/"やら、"/chat"やらの(相対的)なパスを指定しているが、これは、サーバのアクセスするアドレスとポートを決定するものである。
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal("error starting http server::", err)
return
}
で、これを宣言することで、サーバとして使えるようになる。
ちなみに、(":8080", nil)の"nil"は、上記のhttp.ServerFile()と、http.HandleFunc()を使うぜ、の意味になる(直接編集することもできるらしい)。
2.4. upgrader.Upgrade(w, r, nil)は、HTTP通信からWebSocket通信に更新してくれるものである
これは"github.com/gorilla/websocket"が提供してくれるもので、HTTP通信(一方通行)からWebSocket通信(相互通行)に更新してくれる便利なものらしい。
websocket, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Fatal("error upgrading GET request to a websocket::", err)
}
こうしてしまえば、websocket.ReadJSON()やら、websocket.WriteHSON()やらが、バカスカ使えるようになる。
2.5. これは何だろう
http.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir("static"))))
まあ、"/static/" は、通信コネクションでいいして、http.StripPrefix("/static", http.FileServer(http.Dir("static")))については、「/static」をhttp.FileServer()が捜索するURLから除く という意味です。
2.6. 乱暴に纏めると
http.HandleFunc()と、http.ListenAndServe()の』2つだけ覚えておけば、いいんじゃない?、と思う。
3. クライアント側の江端の理解
3.1. Dialer構造体のdial関数は、サーバとの接続要求をするものである
一般的にクライアントはWebブラウザなんだけど、これをgolangのプログラムからwebsocketでアクセスしようとする場合は、こんな感じになる。
var addr = flag.String("addr", "0.0.0.0:8080", "http service address")
func bus(bus_num int) {
var bus BUS
///////////// 描画処理ここから ////////////////
_ = websocket.Upgrader{} // use default options
flag.Parse()
log.SetFlags(0)
u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo2"}
log.Printf("connecting to %s", u.String())
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil {
log.Fatal("dial:", err)
}
defer c.Close()
まず、グローバルで、以下のようにサーバを場所を書いておく。
var addr = flag.String("addr", "0.0.0.0:8080", "http service address")
(よく分からないんだけど)以下のような書き方でwebsocket(のインスタンス?)が作れるらしい。
_ = websocket.Upgrader{} // use default options
以下で、/echo2を使うぜ、の宣言
u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo2"}
で、以下で、websocket用のソケットができるらしい。
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
この後はc.ReadJSON()やら、c.WriteJSON()やらを使い倒す、ことができるようになります。
4. 江端の理解 その他について
4.1. flag.Parse()って何?
var addr = flag.String("addr", "0.0.0.0:8080", "http service address")
を"固定するもの"でいいのかな? → 間違っています。→ golang でコマンドライン引数を使う
4.2. log.SetFlags(0)って何?
import "log"
で、logを使う場合に、logの設定をリセットするもの、で良さそうです。
以上
内容間違っていたら、優しくご指摘下さい。