2023,江端さんの技術メモ

"github.com/gorilla/websocket"の有名なバグ(らしい)

■PruneMobile側(sever.go)

■gtfs_hub側(gtfs_hub.go)

■対策

[Go言語/JavaScript]WebSocketsでチャットアプリを実装!~ユーザーリスト表示編~


PruneMobile側(sever.go)の方が先に落ちる(確認) → で書き込みできなくなってgtfs_hub側(gtfs_hub.go)も落ちる、と、多分、そんな感じ。

"repeated read on failed websocket connection"を普通に読めば、「間違ったWebsocket接続で、*何度*も読み込みを繰り返している」なので、read errorが発生したら、即コネクションクローズして、クライアントも落してしまえばいいのかな、と考えました。→ ダメでした。サーバが落ちて処理が止まります。

で、以前、

Golangのサーバなんか大嫌い

で、

"http.HandleFunc()"は、クライアントがやってくるごにに一つづつ立ち上があるfork()のようなものである

てなことを書いていたので、read errorqを検知したら、forループから直ちにbreakして、クライアントをクラッシュさせてしまうこと(ができるものと)しました。→ ダメでした。サーバが落ちて処理が止まります。

 

for (){
      (色々)
           err = webConn.ReadJSON(&locMsg2) // ここでPanic serving  ... repeated read on failed websocket connectionが発生している可能性あり
           fmt.Printf("c6: ")
           if err != nil{
              fmt.Printf("Before webConn.close(), After c6:")
               webConn.Close()
               break → ダメ
           }
}

javascript (onload.js) の方にも、終了理由がでるように、以下のコードを追加しました。

// サーバを止めると、ここに飛んでくる
  socket.onclose = function (event) {
      console.log("socket.onclose");
  
      let obj = JSON.parse(event.data);
  
      console.log("socket.onclose: obj.id:", obj.id);
      console.log("socket.onclose: obj.lat:", obj.lat);
      console.log("socket.onclose: obj.lng:", obj.lng);
      console.log("socket.onclose: obj.type:", obj.type);
      console.log("socket.onclose: obj.popup:", obj.popup);
  
      socket = null;
  
      // 切断が完全に完了したかどうか
      if(event.wasClean){
          var closed = "完了";
      } else {
          var closed = "未完了";
      }
      info.innerHTML += "切断処理:" + closed + "<br>";
      info.innerHTML += "コード:" + event.code + "<br>";
      info.innerHTML += "理由:" + event.reason + "<br>";
  
  }
  
  window.onunload = function(event){
      // 切断
      ws.close(4500,"切断理由");
  }

上記の「ダメ」の対策中

https://ja.stackoverflow.com/questions/12389/golang%E3%81%A7%E3%83%9A%E3%83%BC%E3%82%B8%E3%82%92%E5%86%8D%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%81%BF%E3%81%99%E3%82%8B%E3%81%A8websocket-server%E3%81%8C%E8%90%BD%E3%81%A1%E3%82%8B


■問題解決編

色々問題があったのですが、順番に説明していきます。


	var addr = flag.String("addr", "192.168.0.8:8080", "http service address") // テスト
	....
	.....
	http.Handle("/", http.FileServer(http.Dir(".")))


	http.HandleFunc("/echo", echo)                                           // echo関数を登録 (サーバとして必要)
	log.Fatal(http.ListenAndServeTLS(*addr, "./cert.pem", "./key.pem", nil)) // localhost:8080で起動をセット
}

まず第一に、http.HandleFunc()の誤解がありました。私は、これを、fork()のようにプロセスかスレッドを発生さるものと思っていましたが、これは、一言で言えば、単なるコールバック関数でした。

乱暴に言えば、Webからアクセスがあると、echo()というファンクションに吹っ飛ばされる、という現象を発生させる"だけ"で、それ意外のことは何にもしてくれないのです。

では、echo()の方はどうなっているかというと、

func echo(w http.ResponseWriter, r *http.Request) { // JavaScriptとの通信
	webConn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println("websocket connection err:", err)
		return
	}
	defer webConn.Close()

とすることで、webSocket通信の準備ができて、その通信路が"webConn"にできる、ということです。
で、それ意外のことは何もしてくれないのです。

つまり、2つのWebブラウザからアクセスがくると、その度、echo()に飛んできて、その度に、異なるwebConnを生成する、ということです。

ここまでの説明で分かると思いますが、つまり、Webブラウザがくる度に、それをどっかに格納しておかないと、通信路の情報が上書きされてしまいます

なので、基本的には、以下のwebConnを、配列に格納しておきます。

var clients = make(map[*websocket.Conn]bool) // 接続されるクライアント
という動的な配列を準備しておき、アクセスがある度に、
// クライアントを新しく登録(だけ)
    m1Mutex.Lock() // 同時書き込みを回避するため、ミューテックスロックで囲っておく
    clients[webConn] = true  // これで、Webからのアクセスがある度に、通信路情報が動的に追加される
    m1Mutex.Unlock()

というように、Webブラウザとの通信路を別々に管理しておきます。

もし、fork()みたいなことがしたいのであれば、goroutineを起動して、そのgoroutineにWebブラウザの通信路を明け渡す必要があります。それでも、通信路の全体管理は、echo()が握っているので、webConnを消滅されたい場合は、echo()の方でやって貰う必要があります。

この方法を実現する方法として、GO言語のサンプルプログラムで良く登場するのが、以下のような方法です。

// 江端のPCでは、c:\users\ebata\test20230412\main.go

package main

(中略)

func handleConnections(w http.ResponseWriter, r *http.Request) {
    // 送られてきたGETリクエストをwebsocketにアップグレード
    ws, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Fatal(err)
    }
    // 関数が終わった際に必ずwebsocketnのコネクションを閉じる
    defer ws.Close()

    // クライアントを新しく登録
    clients[ws] = true

    for {
        var msg Message
        // 新しいメッセージをJSONとして読み込みMessageオブジェクトにマッピングする
        err := ws.ReadJSON(&msg)
        if err != nil {
            log.Printf("error: %v", err)
            delete(clients, ws)
            break
        }
        // 新しく受信されたメッセージをブロードキャストチャネルに送る
        broadcast <- msg
    }
}

func handleMessages() {
    for {
        // ブロードキャストチャネルから次のメッセージを受け取る
        msg := <-broadcast
        // 現在接続しているクライアント全てにメッセージを送信する
        for client := range clients {
            err := client.WriteJSON(msg)
            if err != nil {
                log.Printf("error: %v", err)
                client.Close()
                delete(clients, client)
            }
        }
    }
}

上記の例では、 http.HandleFunc("/ws", handleConnections) で、handleConnectionsをコールバック関数にしておき、こちらにWebブラウザからのアクセスを全部任せます

でもって、ついでにWebからやってくる通信の全てを受けつけています(ReadJSON())。さらに、webブラウザが突然閉じられた場合などは、通信エラーとして検知して、それをクローズした後に、webConnの配列から取り除いています。これで、このブラウザからの通信路は閉じらて、消されます。

で、この通信の内容を、チャネルを使って、go handleMessagerで作った、fork()みたいなgoroutineに流し込んでいます。

結論として、fork()みたなことがやりたければ、「自力で作れ」ということになります


しかし、WebSocket単位で別々のgoroutine作るのも面倒くさいです。それに、もしそのようなgoroutineを作ったとすれば、ブロードキャストを実現しなければなりませんが、チャネルには、ブロードキャスト機能がありません(どうしても使わなければならない時は、私はredisを使っています)。

ですので、私、チャネルの配列を作って疑似的なブロードキャストを実現しようとしたのですが、このサンプルプログラムが見るからなくて、困っていました。

『echo()関数の中で、全てのWebコネクションを相手にできないかな』と考え始めました。

基本形はこんな形

for {
     message <- channel // 外部から送られてきたデータ
     for client := range clients{ //clientsはWebConnのリスト
           client.WriteJSON(message)
           client.ReadJSON(message2}
     }
}

問題は、Webブラウザは終了処理などなどをせずに、閉じられてしまうことにあります(私たちが普通にやっていることです)。

とすれば、echo()の中でコネクションの切断を検知して、それをWebConnのリスト から取り除く必要があります。

で、こんなことやってみたんですよ。

for {
     message <- channel // 外部から送られてきたデータ
     for client := range clients{ //clientsはWebConnのリスト
           err = client.WriteJSON(message)
           if err != nil{
               client.Close()
               delete(client, clients)
           err =  client.ReadJSON(message2}
           if err != nil{
               client.Close()
               delete(client, clients)
     }
}

これでは、for ルーチンの中で、回している変数を減らすという処理が不味かったようで、この場合、 echo()が終了してしまうようでした。当然、repeated.....も含めて、エラーの嵐になりました。

なので、こんな風に変更しました。


var delete_client *websocket.Comm
for {
     delete_client = nil
     message <- channel // 外部から送られてきたデータ
     for client := range clients{ //clientsはWebConnのリスト
           err = client.WriteJSON(message)
           if err != nil{
               delete_client = client
           }
           err =  client.ReadJSON(message2}
           if err != nil{
               delete_client = client
           }
     }
     if delete_client != nil{
        delete_client.Close()
        delete(clients, delete_client)
}

つまり、ループの外でコネクション処理を行う、ということです。
ただ、この方法では、ループを回っている途中に2つ以上のWebブラウザが落された場合にどうなるか、という問題が残ります。
この場合、「次のループでエラーを検知するのを待つ」という処理で対応する、と腹を括りました。
なぜなら、repeat..... のエラーは、積り積って発生することを経験的に知っていましたので、それまで通信障害したまま走って貰えばいい、と割り来って考えることにしました。


結論としては、

(1)http.HandleFunc()は、Webからの接続要求時に、飛される先の関数を記述するもの(コールバック関数)であり、
(2)そのコールバック関数の中だけで、入出力処理がしたいのであれば、ループを壊さないような工夫をして、上手く運用すれば、上手くいく(ことがある)

という結論になりそうです。

 

2023,江端さんの忘備録

「人生最後に食べる食事」というのを、心から真剣に1分くらい考えたことがあります。

I once thought sincerely and seriously for about a minute about "the last meal of my life."

自分でも驚いたのですが、意外なモノが心に浮んできました。

To my own surprise, an unexpected meal came to mind.

―― 京都等持院駅にある『お好みジャンボ』の『お好み焼き』

"Okonomiyaki" at "Okonomiyaki Jumbo" at Kyoto Tojiin Station

学生時代の京都を、「和菓子」で思い出すような奴は、いないと思います(思いたい)。

-----

私、修士2年生(研究室のゼミの中で最年長)の時、ゼミ室に残っている全員分のお好みやきを買いに、自らバイクを出して、10人分くらいのお好み焼きを、テイクアウトして持ち返っていました。

When I was a second-year master's student (the oldest in my lab's seminar), I had taken my bike out to buy okonomiyaki for everyone remaining in the seminar room and had brought back take-out okonomiyaki for about 10 people.

今、GoogleMapでルート検索したのですが、結構な距離がありました(バイクで往復30分)。

I just searched the route on GoogleMap and it was quite a distance (30 minutes round trip by bike).

大学在学中、結構な頻度で通っていた、という記憶があります。

I remember that I went there quite often while I was in college.

今の『孤食の江端』からは(私ですら)想像もできないのですが ―― ゼミ生たちと一緒になって、騒ぎながら食べるお好み焼きは、本当に美味しかったのです。

I can't imagine (even I can't) from the "solitary Ebata" of today -- the okonomiyaki was really delicious with getting together with the seminar students and making a lot of noise.

-----

本日、今通っている大学のゼミの新人歓迎会に参加させて頂き、同じくゼミ室でピザを食してきました。

Today, I attended a welcome party for newcomers to the seminar at the university I currently belong, and had pizza in the seminar room with them.

出席者の半分くらいは、国際留学生だったので、この機会に『日本/日本人に対して言いたいこと』を聞き出し回っていました ―― コラムのネタの仕込みも兼ねて。

About half of the attendees were international students, so I took the opportunity to go around asking them what they wanted to say to Japan/Japanese people -- as well as preparing material for my column.

後日、このお話もしたいと思います。

I would like to talk about this later.

-----

ともあれ、彼らが、私と同じように、このピザの味が、「最後の食事」としてエントリーされるような思い出になるといいんだけどな、と思いながら、このパーティを眺めていました。

Anyway, I watched the party, hoping that they could remember the taste of pizza as the "last meal", like me.

2023,江端さんの忘備録

今日は新学期始めての全体ゼミの発表の日です。

Today is the day of the presentation of the first general seminar of the new semester.

発表時間は7分ですが、調子にのって、資料を46ページも作ってしまいました。

The presentation time is 7 minutes, but I ended up making 46 pages of materials.

コラム執筆のノリで作ってしまいました。

I made it with the feel of writing column.

しかし、修正している時間がないので、プレゼン発表の方でなんとかします。

However, I don't have time to fix it, so I will do something about it in the presentation presentation.

つまり、『運用』でなんとかします。

In other words, we will do something with "operation".

-----

バグのあるプログラムを、バグを修正しないままにリリースしなければならないことがあります。

Sometimes I have needed to release a buggy program without fixing the bug.

この場合、そのプログラムの利用者に対して『こういう操作をしないようにお願いする』という対応があります。

In this case, there is a response to the user of the program to "ask not to perform this kind of operation".

これを『運用で対応する』という言い方をします。

This is called "responding with operation".

『過去の私と現在の私の間で行われる、共同開発』

-----

私の人生において『運用で対応する』は、もっとも使われてきたフレーズの一つです。

In my life, "respond with operations" is one of the most used phrases.

"入試"であれ、"仕事"であれ ―― そして"結婚生活"であれ。

Even if it's "entrance exams", "work" or "marriage."

2023,江端さんの忘備録

GO言語のhttp.HandleFunc()の解釈を豪快に間違えていました。

I was boldly misinterpreting http.HandleFunc() in the GO language.

Golangのサーバなんか大嫌い

加えて、長期間ウソを垂れ流してきました。

In addition, I have been lying for a long period of time.

本当に申し訳ありません ―― が、誰からもクレームが付かないということは、もはや「オオカミ少年」と同じ扱いなのかもれれません(単に興味がないだけかも)

I am truly sorry -- but the fact that no one has complained about it suggests that it is now treated like the "boy who warned wolf coming" (or maybe they are just not interested in it).

私、これでも研究員なので、ファクトチェックには気を付けるようにしているです。まあ、努力目標ですが。

I am still a researcher in this, so I try to be careful about fact-checking. Well, it is an effort goal.

-----

これを発見することができたのは、自分の作ったプログラムの動作不良です。

I was able to discover this because of a malfunctioning program I created.

repeated read on failed websocket connection

というか、『今まで、よく問題なく動いてきたなぁ』と逆に感心してしまったくらいです。

In fact, I was so impressed that I was even saying to myself, 'How has it been working without any problems up to now?

これまで専用端末として使っていたものを、公開サービスに作りかえようとして、今回の発見に至りました。

I came to this discovery in an attempt to recreate what I had been using as a dedicated terminal into a public service.

ですが、私は、すでに間違った解釈のプログラムを、バラまいています。

But I am already spreading around a program that is misinterpreted.

-----

『他人の作ったプログラムを、安易に流用すると、こういう怖いリスクが待っている』

"If you easily use a program created by someone else, this kind of scary risk awaits you."

ということは、覚えておいて欲しいです。

So, please keep that in mind.

決して『言い訳』ではありません。

It is never an 'excuse'.

2023,江端さんの忘備録

ノンアルコールビールとは、ビール風味の炭酸水飲料であり、基本的にはコーラなどの炭酸飲料と同じです。

Non-alcoholic beer is a beer-flavored carbonated water beverage, basically the same as cola or other carbonated beverages.

もちろんアルコールはゼロ(*)ですし、カロリーを鑑みても、他の飲料水よりも、はるかに健康的な飲み物のはずです。

Of course, it has zero alcohol(*), and in light of the calories, it should be a much healthier drink than other drinking water.

(*)アルコール度数1%未満のもので、ノンアルコールビールと称呼しているものもありますが、ここでは対象外とします。

(*) Some beers with less than 1% alcohol by volume are called non-alcoholic beers, but are not included here.

―― なぜ、ノンアルコールビールを駅の自販機で取り扱ってくれないの?

"Why don't they carry non-alcoholic beer in the station vending machines?"

私は、普通に通勤や通学や会社の食堂で、普通に飲みたい。

I like to drink normally, on my way to work, to school, or in the company cafeteria.

-----

そもそも、あの缶のデザインからして、まぎらわしい。

To begin with, the design of that can is confusing.

あれでは、本物のビールを飲んでいるように見えます。

That design makes it look like you are drinking real beer.

私、ノンアルコールビールを飲みながら運転することがありますが、多分近い未来、警察に捕まるでしょう(直ぐに解放されるとは思いますが)。

I sometimes drive while drinking non-alcoholic beer and will probably be caught by the police in the near future (although I am sure I will be released soon).

私は、ビールの代替品として飲んでいるのではなく、あのビール独特の麦の味が好きなのです。

I don't drink it as an alternative to beer, I like the unique barley taste of that beer.

そして、せっかくお金を払うのであれば、好きなものを飲みたいのです。

And if I am going to pay for something, I want to drink what I like.

-----

缶のデザインを、本物のビールとそっくりに作っているのは、味覚だけではなく、視覚的にも、『自分はビールを飲んでいるのだと、騙されたい』と思うユーザが多いからだろうなぁ、と思っています。

I wonder if the reason why the can design is made to look exactly like real beer is because many users want to be fooled into thinking that they are drinking beer, not only in terms of taste, but also visually.

気持ちは分かる。

I understand how you feel.

実際、ノンアルコールビールは、飲酒運転防止に大きく貢献していると思っています。

思わず、「アッラーアクバル!」と叫びそうになりました。

In fact, I believe that non-alcoholic beer contributes significantly to the prevention of drunk driving.

まあ、嫌なものが見えてきたりもしますが。

―― アルコール摂取量に応じた、人格の崩壊の有様

Well, sometimes I see things I don't like.

-----

我が家では、私が飲まなくなったことにより、アルコールリテラシーが著しく低い家庭になってしまいました。

My family has become significantly less alcohol literate since I stopped drinking.

なにしろ、『麦茶を炭酸水で割れば、ノンアルコールビールになる』と信じていていた者がいたくらです ―― 嫁さんと次女です。

After all, there was persons who believed that "if you mix barley tea with soda water, you can make non-alcoholic beer". They were my wife and my second daughter.

2018,江端さんの忘備録

最近、ある理由があって、現在、アルコール摂取を完全に止めています。1ミリリットルも口にしていません。

Recently, there are certain reasons, I am completely stopping alcohol consumption now. I do not even mention 1 milliliter.

ところが、4月は飲み会が多くて、正直困っています。

However, in April there are many drinking party and I am embarrassed to be honest.

飲み会の席で、

At the drinking party 's seat,

同僚:「江端さん。ドクターストップですか?」

Colleague: "Mr. Ebata, the doctor stooped it?"

と言われる度に、

Every time I responded

江端:「はっはっは、この私を『医者』ごときが止められると思います?」

Ebata: "Ha-ha, do you think that even a doctor can stop me?"

とニッコリ笑って応えると、『いろいろ察して』貰えるようで、この話題には誰も触れなくなります。

with smiling, nobody comes to touch this topic to be recognized something.

それはさておき。

Set it aside.

-----

酒が飲めなくなると、色々と見えてくるものがあります。

When I do not drink, I can see something various.

―― アルコール摂取量に応じた、人格の崩壊の有様

"Personal collapse presumably according to alcohol sumption"

が、はっきりと見えて、正直、本当に「怖い」です。

To me, it is clearly visible, honestly, it's really "scary."

飲み会の開始時の、前や隣りの席の人間が、どんどん、異様・・・もとい、別な人格(あるいは、隠れていた人格)に変化している様を、まざまざと「観測」することになるからです。

Because I will "observe" a lot about people at the next seats, who is changing to a different personality (or hidden personality) from the start time of the drinking party.

今まで、「怖い」と感じなかったのは、私もアルコールを摂取して、同じ世界線を生きていたからだろうと考えています(要するに、自分も同じように「酔っぱらって」、「人格崩壊・・・もとい、人格変化」をしていた、ということ)

I have not able to be feel "fear" since now. I think the reason is that I also change the my personality with drinking, and start to live in the another world.

気分は、岡部の「リーディングシュタイナー」です。

The feeling is, ... let me think... " Okabe's Reading Steiner"

-----

私の嫁さんは、生まれながらにして、アルコール分解能力を持たない、いわゆる「下戸」です。

My wife is a so-called "Geko" who has no alcohol decomposing ability by nature.

ですので、私は、嫁さんに、

So I asked her

「今まで、ずっとこんな怖い人生を生きてきたのか?」

"Have you ever lived such a scary life until now?"

と尋ねたところ、嫁さんは、『特に、これといった感想はないなぁ』と応えました。

However she responded, "I never thought of scary, in particular"

こんな身近に、『孤独の観測者』がいたのかと、感慨ひとしおでした。

I felt a deep emotion that there was an "observer of loneliness" close to me.

2018,江端さんの忘備録

元アイドルグループのメンバが、飲酒によるトラブルを起こしたとかで、騒ぎになっています。

One of the members of the former idol group caused a trouble due to drinking, and it became a fuss.

It is very unlikely for me to make such a rush at this timing.

『江端も、か・・・』と妙な納得をされないよう、お願い致します。

I don't hope you will be convinced saying "Ebata, too ...".

それはさておき。

Apart from that.

-----

―― ノンアルコールビールが、死ぬほど旨い

"Non-alcoholic beer is delicious enough"

と感じます。

I feel that.

もちろん、アルコール入りのビールの方が美味しいはずです。

Of course, alcohol beer should be more delicious.

しかし、アルコールを摂取できない身の上では、ノンアルコールビールは、ソフトドリンクよりも、ずっと美味しいのです。

However, I can not consume alcohol now, non-alcoholic beer is much more delicious than soft drink.

居酒屋には、ノンアルコールビールのサーバがあっても良いと思うんですよ ―― どこかにないかな、そんな居酒屋。

I think that some drink-bars should have a non alcoholic beer server. I wonder if there is something, such a bar.

-----

業界の人には、是非、以下の商品開発を試みて見て頂きたいのです。

I would like engineer to try to develop the following product by all means.

●ノンアルコール日本酒

- Non alcoholic sake

●ノンアルコールワイン

- Non alcohol wine

ここで、ちょっとググってみたら、あるんですね、ちゃんとそういう製品って。

Here, I use a search engine, I found the products like that.

ちょっと感激しました。

I was deeply moved.

でも、

However,

●ノンアルコールウイスキー

- Non alcoholic whiskey

は、さすがにないだろうと思ったら、これもありました。

has not been, but I could find it.

(以下が説明書の内容)

(The description below)

■イスラムの戒律ではアルコールの摂取が禁止されており、通常ムスリムは飲酒できず酒類はノンハラル(非合法)食品とされています。

Under Islamic precepts, alcohol intake is prohibited, Usually, Muslim can not drink alcohol and any alcohol is regarded as nonhalal (illegal) food.

■しかし、このウイスキーに関しては、イスラム食品&栄養評議会によってハラル(合法)食品の認定を受けており、ムスリムが教義的罪を背負わずに楽しめる飲料となっています。

However, regarding this whiskey, it is certified by Halal (legal) food by the Islamic Food & Nutrition Council, and It is a beverage that Muslims can enjoy without carrying doctrinal sins.

-----

思わず、「アッラーアクバル!」と叫びそうになりました。

I was almost to cry "Allah Akbar!"

2022/08,江端さんの技術メモ

以下の記述は間違っているようですので、参照にしないで下さい(現在検証中)。
(昨日、ソフト外注会社の方に教えて貰いました)

 

Golangで、http.HandleFunc と http.Handleについて、ずっと混乱しつづけています。

というか、私は、使い方が分かればよくて、その理屈なんぞ1mmも興味がないので、コードを書きながら理解しています(結局、遅くなっているような気がしますが)。

1. http.Handle()は、index.htmlをベースとしたサーバを立てるもの

// main16.go 現在の居場所は、c:\Users\ebata\hirohakama\199A2\others
/*
.
├── main16.go
├── index.html (A)
└── chart            # chartフォルダに静的ファイルがある
    └── index.html (B)
*/

package main

import (
	"net/http"
)

func main() {

	// 静的ファイル配信.
	// ディレクトリ名をURLパスに使う場合
	// 例:http://localhost:8080/chart/で index.html (B) の方を表示
	http.Handle("/chart/", http.FileServer(http.Dir("./")))

	// 例:http://localhost:8080/で index.html (A) の方を表示
	http.Handle("/", http.FileServer(http.Dir("./")))

	// ディレクトリ名とURLパスを変える場合
	// 例:http://localhost:8080/mysecret/sample1.txt
	// http.Handle("/mysecret/", http.StripPrefix("/mysecret/", http.FileServer(http.Dir("./contents"))))

// 例:http://localhost:8080/で index.html (A) の方を表示
	http.Handle("/", http.FileServer(http.Dir("./")))
	// 8080ポートで起動
	http.ListenAndServe(":8080", nil)
}

これで、main16.goが置いている場所が、基準点となります(それだけです)。

で、色々考えずに、基本は、

http.Handle("/", http.FileServer(http.Dir("./")))

としておきましょう(というか、これがデフォルトなら、記載すらしなくてもいい)

2. http.HandleFunc()は、ソースコードで書いたものをサーバとするもの

// main15.go 現在の場所はc:\Users\ebata\hirohakama\199A2\others

/*
.
└── main15.go

*/

package main

import (
	"io"
	"log"
	"net/http"
)

func h1(w http.ResponseWriter, _ *http.Request) {
	io.WriteString(w, "Hello from a HandleFunc #1!\n")
}

func h2(w http.ResponseWriter, _ *http.Request) {
	io.WriteString(w, "Hello from a HandleFunc #2!\n")
}

func main() {

	// http://localhost:8080/ で h1の内容を表示 (プログラムの内容を)
	http.HandleFunc("/", h1)

	// http://localhost:8080/endpoint で h2の内容を表示
	http.HandleFunc("/endpoint", h2)

	log.Fatal(http.ListenAndServe(":8080", nil))
}

3. http.Handle()1つとhttp.handleFunc()1つが混在しているものは、それぞれサーバが2つある、ということ

// main13.go

package main

import (
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"time"

	"github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{}

type GetLoc struct {
	ID    int     `json:"id"`
	Lat   float64 `json:"lat"`
	Lng   float64 `json:"lng"`
	TYPE  string  `json:"type"` // "PERSON","BUS","CONTROL
	POPUP int     `json:"popup"`
	//Address string  `json:"address"`
}

func echo3(w http.ResponseWriter, r *http.Request) {
	upgrader.CheckOrigin = func(r *http.Request) bool { return true } // おまじない
	conn2, err := upgrader.Upgrade(w, r, nil) //conn2でwebsocketを作成
	if err != nil {
		log.Println("websocket connection err:", err)
		return
	}
	defer conn2.Close()

	for {
		gl2 := new(GetLoc)
		gl2.ID = rand.Intn(20) // ここで乱数を発生されて、javascriptで受信させる
		gl2.Lat = 181.0
		gl2.Lng = 181.0
		gl2.TYPE = "BUS"
		gl2.POPUP = 101

		err := conn2.WriteJSON(&gl2)
		if err != nil {
			log.Println("ReadJSON:", err)
			break
		}
		fmt.Println("echo3:", gl2)
		time.Sleep(time.Second * 1)
	}

}

//var addr = flag.String("addr", "0.0.0.0:5000", "http service address") // テスト

func main() {

	http.Handle("/chart/", http.FileServer(http.Dir("./")))

	http.HandleFunc("/echo3", echo3)

	//log.Println("server starting...", "http://localhost:8080")
	//log.Fatal(http.ListenAndServe("localhost:8080", nil))
	log.Fatal(http.ListenAndServe(":8080", nil))
}

index.html

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>test</title>

</head>

<script type="text/javascript" src="moment.js"></script>
<script type="text/javascript" src="Chart.js"></script>
<script type="text/javascript" src="chartjs-plugin-streaming.js"></script> 



<script>  
    var ws;

    // websocketのオープン(この段階で接続完了)
    ws = new WebSocket('ws://localhost:8080/echo3')  // ユーザ登録画面

    ws.onopen = function (event) {
    }

    ws.onmessage = function (event) {
        // 送られてきたデータを受信して、JSON形式に変更
        var obj = JSON.parse(event.data);
        console.log("obj:",obj);
        console.log("obj.id:",obj.id);
        aa = obj.id;
    }
</script>  

<body BGCOLOR="black" text="white"  STYLE="overflow: hidden;">

	<center>
	  <font size="5">Waking Time(min.) <br></font> <!--- 意味のない表示 -->
	  <font size="5"> 歩行時間(分)</font> <!--- 意味のない表示 -->
	</center>
	
    <canvas id="myChart" width="100" height="85"></canvas>


<script>  
    var ctx = document.getElementById('myChart').getContext('2d');
			var chart = new Chart(ctx, {
				type: 'line',
				data: {
					datasets: [{
                        data: [],  // 1つめ
                        borderColor: "rgba(255,0,0,1)",
                        backgroundColor: "rgba(0,0,0,0)",  
                        lineTension: 0,
                        label: 'Time',
					}]
				},
				options: {
					scales: {
						xAxes: [{
                            type: 'realtime',
                            realtime: {
                                duration: 30000, // 300000ミリ秒(5分)のデータを表示 (コメントアウトすると早く動く)
                                onRefresh: function(chart) {
                                    chart.data.datasets.forEach(function(dataset) {
                                        dataset.data.push({
                                            x: Date.now(),
                                            //y: (Math.floor(Math.random()*16)+30) //30-45の乱数(整数)
                                            y: aa, // この"aa"が、送られてきたデータ
                                        });
                                    });
                                }
                            }

                        }],
                        yAxes: [{
					        ticks: {
					        	max: 20,
					        	min: 0
        					}
                        }]
					}
				}
			});

</script>

</body>
</html>

 

この場合、

  • /chart/index.htmlをベースとするサーバと、
  • echo3()が作るサーバ

の2つがある、ということ。

実際のところ、echo3は、/chart/index.html のクライアント(データの送信元)でもあるんだけど、要求があれば、ポコポコ作り出される、という点ではサーバでもある、という形になっています。

―― という説明を、次に私が頭を抱えた時に、私が思い出せるのかが、不安です

 

2023,江端さんの忘備録

『今のパーソナルコンピュータ(PC)の性能は、20年前のスーパーコンピュータ(スパコン)の性能と同じである』

"The performance of a personal computer (PC) today is the same as that of a supercomputer 20 years ago"

という話を聞いて、ちょっと計算してみました。

After hearing the story, I did some calculations.

- 2000年以降、PCとスパコンの性能差は、ざっくり1万倍になる

(引用先を失念しました。どなたか教えて頂ければ、記載いたします)

- After 2000, the performance difference between PCs and supercomputers have been roughly 10,000 times

- ムーアの法則「18ヶ月で性能が倍になる」

- Moore's Law "doubles performance in 18 months."

から考えると、

Given that from the information,

2^(x/1.5)= 10000 を解けばいい。

Solve 2^(x/1.5) = 10000.

x = 19.93 年

x = 19.93 years

なるほど、『私たちは、みんな、20年前のスパコンを使っている』ことになる訳です。

I see, 'We are all using 20-year-old supercomputers'.

つまり、理屈上、2003年の段階で、スパコンで計算していた天気予報や地震予測計算は、自分の部屋でもできます。

In other words, in theory, as of 2003, weather forecasts and earthquake prediction calculations that were being calculated on supercomputers can be done in your own room.

私から見れば『20年間待ち続ければいい』のです。

From my point of view, 'I just have to wait for 20 years'.

逆に言えば、スパコンへの開発や投資コスト(約1300億円、保守コスト抜き)は、この『20年間の先行』にあると言うことです。

Conversely, the cost of development and investment in supercomputers (about 130 billion yen, not including maintenance costs) is in this '20-year leading'.

皆さんが、どう考えるのは分かりませんが、私は、概ね妥当なコストだと思います。

I don't know what you all think, but I think the cost is generally reasonable.