m_shige1979のときどきITブログ

プログラムの勉強をしながら学習したことや経験したことをぼそぼそと書いていきます

Github(変なおっさんの顔でるので気をつけてね)

https://github.com/mshige1979

javascriptのfetchを使ってAPIより結果を取得

概要

fetch使ってみたい

APIはgo言語で作成した適当なやつ

サーバ側
package main

import (
	"encoding/json"
	"log"
	"math/rand"
	"net/http"
	"time"
)

func main() {
	http.HandleFunc("/sample_api1", sampleAPI1)
	http.HandleFunc("/sample_api2", sampleAPI2)
	http.HandleFunc("/sample_api3", sampleAPI3)
	http.ListenAndServe("127.0.0.1:8080", nil)
}

// Response ...
type Response struct {
	Status int    `json:"status"`
	Rssult string `json:"result"`
}

func sampleAPI1(w http.ResponseWriter, r *http.Request) {

	//if r.Method == http.MethodOptions {
	//	return
	//}
	log.Println("api1", r.Method)
	rand.Seed(time.Now().UnixNano())
	time.Sleep(time.Duration(int(rand.Intn(5))) * time.Second)
	response := Response{http.StatusOK, "ok1"}
	res, _ := json.Marshal(response)

	r.Close = true
	defer r.Body.Close()
	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Headers", "*")
	w.Header().Set("Accept", "application/json")
	w.Header().Set("Content-Type", "application/json")
	w.Write(res)

}
func sampleAPI2(w http.ResponseWriter, r *http.Request) {

	//if r.Method == http.MethodOptions {
	//	return
	//}
	log.Println("api2", r.Method)

	rand.Seed(time.Now().UnixNano())
	time.Sleep(time.Duration(int(rand.Intn(5))) * time.Second)
	response := Response{http.StatusOK, "ok2"}
	res, _ := json.Marshal(response)

	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Headers", "*")
	w.Header().Set("Content-Type", "application/json")
	w.Write(res)
}
func sampleAPI3(w http.ResponseWriter, r *http.Request) {

	//if r.Method == http.MethodOptions {
	//	return
	//}
	log.Println("api3", r.Method)

	rand.Seed(time.Now().UnixNano())
	time.Sleep(time.Duration(int(rand.Intn(5))) * time.Second)
	response := Response{http.StatusOK, "ok3"}
	res, _ := json.Marshal(response)

	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Headers", "*")
	w.Header().Set("Content-Type", "application/json")
	w.Write(res)
}

やって置かないとポートが違うとかなんとかでCORSエラーで弾かれる

javascript(Promise.all)
unction taskA() {
       
    return fetch("http://127.0.0.1:8080/sample_api1", {
        method: "GET", // *GET, POST, PUT, DELETE, etc.
        mode: "cors", // no-cors, cors, *same-origin
        cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
        credentials: "same-origin", // include, same-origin, *omit
        headers: {
            "Content-Type": "application/json; charset=utf-8",
        },
        redirect: "manual", // manual, *follow, error
        referrer: "no-referrer", // no-referrer, *client
        //body: JSON.stringify("{}"), // 本文のデータ型は "Content-Type" ヘッダーと一致する必要があります
    })
    .then(function(response) {
        console.log("taskA");
        return response.json();
    });
    
    
}
function taskB()  {

   return fetch("http://127.0.0.1:8080/sample_api2", {
        method: "GET", // *GET, POST, PUT, DELETE, etc.
        mode: "cors", // no-cors, cors, *same-origin
        cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
        credentials: "same-origin", // include, same-origin, *omit
        headers: {
            "Content-Type": "application/json; charset=utf-8",
        },
        redirect: "manual", // manual, *follow, error
        referrer: "no-referrer", // no-referrer, *client
        //body: JSON.stringify("{}"), // 本文のデータ型は "Content-Type" ヘッダーと一致する必要があります
    })
    .then(function(response) {
        console.log("taskB");
        return response.json();
    });

}
function taskC()  {

   return fetch("http://127.0.0.1:8080/sample_api3", {
        method: "GET", // *GET, POST, PUT, DELETE, etc.
        mode: "cors", // no-cors, cors, *same-origin
        cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
        credentials: "same-origin", // include, same-origin, *omit
        headers: {
            "Content-Type": "application/json; charset=utf-8",
        },
        redirect: "manual", // manual, *follow, error
        referrer: "no-referrer", // no-referrer, *client
        //body: JSON.stringify("{}"), // 本文のデータ型は "Content-Type" ヘッダーと一致する必要があります
    })
    .then(function(response) {
        console.log("taskC");
        return response.json();
    });

}

// 並列ですべて完了後にレスポンスを返却
console.log("start");
Promise.all([
    taskA(), taskB(), taskC()
]).then(function(data) {
    console.log("result start");
    console.log(data);
    console.log("result end");
});
console.log("end");

start
end
taskB
taskC
taskA
result start
(3) [{…}, {…}, {…}]0: {status: 200, result: "ok1"}1: {status: 200, result: "ok2"}2: {status: 200, result: "ok3"}length: 3__proto__: Array(0)
result end

※逐次処理も同じ感じで対応できそう

所感

今までajaxのcallbackでやってた部分が多いため、Promiseに対応していかないといけないが
ちょっと仕組みが難しく感じる
少しずつ確認して理解していくしかない…

javascriptのPromiseとかいうのを今更少しだけ学習する

なんか

知らんうちにfetchとか出てるんだけどPromiseとかいうやつ返すんですけど
この辺の使い方とか知らないとjqueryajaxしか使えなくなってくるのは恥ずかしい

Promise

非同期処理するやつ
Promiseで実行した結果はresolveが正常時、rejectが異常時に設定しないと取得できないらしい

サンプル

1つの処理についての非同期処理
unction sampleTask1() {
    return new Promise(function(resolve, reject) {
        setTimeout(function () {
            console.log('sample1');
            resolve("sample1");
        }, 3000);
    });
}

console.log("start");

sampleTask1()
    .then(function(result) {
        console.log("result", result);
    })
    .catch(function(error) {
        console.log("error");
    });

console.log("end");

start
end
sample1
result sample1

※単純に「new Promise」のオブジェクトより結果を判定する

複数の処理を非同期内で逐次処理で行う
console.log("start");
Promise.resolve()
    .then(function() {
        return new Promise(function(resolve, reject) {
            setTimeout(function() {
                console.log("sample2-1");
                resolve("sample2-1");
            }, 16);
        });
    })
    .then(function(result) {
        console.log("result", result);
    })
    .then(function() {
        return new Promise(function(resolve, reject) {
            setTimeout(function() {
                console.log("sample2-2");
                resolve("sample2-2");
            }, 10);
        })
    })
    .then(function(result) {
        console.log("result", result);
    })
    .then(function() {
        return new Promise(function(resolve, reject) {
            setTimeout(function() {
                console.log("sample2-3");
                resolve("sample2-3");
            }, 5);
        });
    })
    .then(function(result) {
        console.log("result", result);
    });
console.log("end");

start
end
sample2-1
result sample2-1
sample2-2
result sample2-2
sample2-3
result sample2-3

順番に処理を行うため、1つ前の実行結果を次の処理で取得するようになっていて、最後に一括で取得などを行いたい場合は少し工夫する必要がある。

複数の処理を実行し、全ての処理が完了後に結果を取得
console.log("start");
Promise.all([
    
     new Promise(function(resolve, reject) {
            setTimeout(function() {
                console.log("sample2-1");
                resolve("sample2-1");
            }, 16);
        })
    , new Promise(function(resolve, reject) {
            setTimeout(function() {
                console.log("sample2-2");
                resolve("sample2-2");
            }, 10);
        })
    ,  new Promise(function(resolve, reject) {
            setTimeout(function() {
                console.log("sample2-3");
                resolve("sample2-3");
            }, 25);
        })
    
]).then(function(data) {
    console.log("result start");
    console.log(data);
    console.log("result end");
});
console.log("end");
|<
↓
>||
start
end
sample2-2
sample2-1
sample2-3
result start
 (3) ["sample2-1", "sample2-2", "sample2-3"]0: "sample2-1"1: "sample2-2"2: "sample2-3"length: 3__proto__: Array(0)
result end

すべての処理が並列で動作し、最後に処理を実行するため、最後に取得するまでは値を取得できない
まあ、なんか方法はあるとは思うが…

所感

allによる同時並列と逐次処理で次の処理へ流せるのは少しだけ理解した
次はfetchを利用した手法で見てみる

golangのpanic発生しても処理を続行したい

golangでのエラーとかって

基本的にはエラーオブジェクトのチェックしかしていない

panic発生したら

落ちます

package main

import (
    "log"
)

func main() {
    log.Println("start")


    // 意図的にpanicを起こす
    panic("aaaa")
    //var a *interface{}
    //*a = 0
    //x := 10
    //y := 0
    //b := x / y
    //log.Println(b)

    log.Println("end")
}

2019/08/14 10:04:05 start
panic: aaaa

goroutine 1 [running]:
main.main()
	/sample2-b.go:12 +0x79
exit status 2

そうならないようにerrオブジェクトや変数のチェックで抑制していますが
まれになんらかの理由でオブジェクトにnullが挿入される自体が発生してpanicがおきたらマズい

javaのtry-catchとかあればよいがそういうのはないため、
deferとrecoverを併用することで対応できるとのこと

ドキュメントより以下、引用(http://golang.jp/effective_go#recover

panicが呼び出されたとき(これには、配列のインデックスが範囲外であるときや、型アサーションに失敗したような暗黙的なものも含む)は、すぐさま、カレントの関数を停止し、ゴルーチンのスタックの巻き戻しを開始します。その途中、遅延指定されている関数をすべて実行します。この巻き戻しがゴルーチンのスタックの先頭にたどり着くと、プログラムは終了します。ただし、組み込み関数recoverを使うことで、ゴルーチンの制御を取り戻し、通常の実行を再開させることが可能です。

書き直し

package main

import (
	"log"
	"fmt"
	"runtime"
)

func main() {
	log.Println("start")

	// 今回は即時関数にしているがerrorの戻り値を取得できるならなんでもよい
	err := func() (errRes error) {
		// panic発生時に補足するやつ
		defer func() {
			if err := recover(); err != nil {
				// エラー情報を戻り値のerrResへ設定
				errRes = fmt.Errorf("error: %s", err)

				// stack trace
				for i:=0;;i++ {
					_, file, line, ok := runtime.Caller(i)
					if ok == false {
						break
					}
					log.Printf("depth: %d, line: %4d, file:%v\n", i, line, file)
				}
			}
		}()

		// 意図的にpanicを起こす
		//panic("aaaa")
		//var a *interface{}
		//*a = 0
		x := 10
		y := 0
		b := x / y
		log.Println(b)

		// ここまできたら正常
		return nil
	}()

	// errorチェック
	if err != nil {
		log.Println(err)
	}

	log.Println("end")
}

2019/08/14 10:13:38 start
2019/08/14 10:13:38 depth: 0, line:   22, file: /work/golang/sample2.go
2019/08/14 10:13:38 depth: 1, line:  522, file:/usr/local/Cellar/go/1.12.7/libexec/src/runtime/panic.go
2019/08/14 10:13:38 depth: 2, line:   61, file:/usr/local/Cellar/go/1.12.7/libexec/src/runtime/panic.go
2019/08/14 10:13:38 depth: 3, line:   37, file:/work/golang/sample2.go
2019/08/14 10:13:38 depth: 4, line:   42, file:/work/golang/sample2.go
2019/08/14 10:13:38 depth: 5, line:  200, file:/usr/local/Cellar/go/1.12.7/libexec/src/runtime/proc.go
2019/08/14 10:13:38 depth: 6, line: 1337, file:/usr/local/Cellar/go/1.12.7/libexec/src/runtime/asm_amd64.s
2019/08/14 10:13:38 error: runtime error: integer divide by zero
2019/08/14 10:13:38 end

Webフレームワークとかは

API配下のpanicは拾ってくれるが、内部でgoroutineなどを任意で定義した場合にpanicを起こすと
Webフレームワークのアプリ自体が停止する可能性がある・・・

所感

なんかスマートじゃない感じ…
現状、これしか見つからないからこの方法で対応を試みます。

golangでUUIDの生成をライブラリでお試し

仕事でコード書くより

設計の仕事ばっかり、スケジュール管理とかめんどくさいので
他の人やってくんないかな…

UUIDって?

UUID(Universally Unique Identifier)とは、ソフトウェア上でオブジェクトを一意に識別するための識別子である。

*1

ざっくりいうとユニークIDみたいやつ
基本的にはsha1とかmd5とか乱数で生成していたけど
そういったものにフォーマットがついた感じ

例:3cc807ab-8e31-3071-aee4-f8f03781cb91

バージョン

現在1から5までの方法がある

確認用ライブラリ

https://github.com/google/uuid
今度改めて自身でアルゴリズムは検討するが今回はちょっとお試しでやってみる

ソース

package main

import (
    "github.com/google/uuid"
    "fmt"
)

func main() {
    fmt.Println("version1 NewUUID --")
    for i:=0;i<10;i++ {
        uuidObj, _ := uuid.NewUUID()
        fmt.Println("  ", uuidObj.String())
    }

    fmt.Println("version2 NewDCESecurity --")
    for i:=0;i<10;i++ {
        uuidObj, _ := uuid.NewUUID()
        domain := uuidObj.Domain()
        id := uuidObj.ID()

        uuidObj2, _ := uuid.NewDCESecurity(domain, id)
        fmt.Println("  ", uuidObj2.String())
    }

    fmt.Println("version3 NewMD5 --")
    for i:=0;i<10;i++ {
        uuidObj, _ := uuid.NewUUID()
        data := []byte("wnw8olzvmjp0x6j7ur8vafs4jltjabi0")
        uuidObj2 := uuid.NewMD5(uuidObj, data)
        fmt.Println("  ", uuidObj2.String())
    }

    fmt.Println("version5 NewSHA1 --")
    for i:=0;i<10;i++ {
        uuidObj, _ := uuid.NewUUID()
        data := []byte("wnw8olzvmjp0x6j7ur8vafs4jltjabi0")
        uuidObj2 := uuid.NewSHA1(uuidObj, data)
        fmt.Println("  ", uuidObj2.String())
    }

    fmt.Println("version4 NewRandom --")
    for i:=0;i<10;i++ {
        uuidObj, _ := uuid.NewRandom()
        fmt.Println("  ", uuidObj.String())
    }

}

結果

mshige1979MBA:golang matsumotoshigeharu$ go run sample1.go
version1 NewUUID --
   ef45b54c-9f70-11e9-a730-6476baad914e
   ef45c294-9f70-11e9-a730-6476baad914e
   ef45c2b2-9f70-11e9-a730-6476baad914e
   ef45c2c6-9f70-11e9-a730-6476baad914e
   ef45c2e4-9f70-11e9-a730-6476baad914e
   ef45c2f8-9f70-11e9-a730-6476baad914e
   ef45c316-9f70-11e9-a730-6476baad914e
   ef45c334-9f70-11e9-a730-6476baad914e
   ef45c352-9f70-11e9-a730-6476baad914e
   ef45c366-9f70-11e9-a730-6476baad914e
version2 NewDCESecurity --
   ef45c38e-9f70-21e9-a730-6476baad914e
   ef45c3ac-9f70-21e9-a730-6476baad914e
   ef45c3ca-9f70-21e9-a731-6476baad914e
   ef45c3e8-9f70-21e9-a732-6476baad914e
   ef45c410-9f70-21e9-a732-6476baad914e
   ef45c442-9f70-21e9-a732-6476baad914e
   ef45c46a-9f70-21e9-a733-6476baad914e
   ef45c49c-9f70-21e9-a733-6476baad914e
   ef45c4c4-9f70-21e9-a734-6476baad914e
   ef45c4ec-9f70-21e9-a735-6476baad914e
version3 NewMD5 --
   3cc807ab-8e31-3071-aee4-f8f03781cb91
   7e7e001e-6957-38d5-b035-41926dd035dd
   91d52425-9470-3e2b-8e5f-649b7b4ec795
   157ffb9e-d6b2-3b99-9fca-1d347f60b3e3
   33b0480d-6d5f-358a-a37a-893e5850fa62
   4b41d96b-dd72-3314-9cc2-1ab241eacb30
   9337ce23-6909-3137-98d2-a9372bf57692
   1e15f3d0-a040-3eca-a8bc-c677bc8bc619
   b90f3b5d-059c-3c0e-b3d8-85c577763bf6
   f741b68d-a9d2-3396-b499-745920a718ff
version5 NewSHA1 --
   13f575fa-02d7-5c38-8e66-48524dd233a4
   a56079b2-5373-5656-911a-483e3a92f19c
   18c2d54b-1a94-5928-8a4a-2f3dd5fc3e57
   8ac4f8a2-89b8-5ea3-81eb-d1654deaab51
   bb6bff8b-615f-5791-a9fb-e3543fd9d02e
   8209f28f-ccc9-5a90-beaf-d7e8e9927183
   387ab566-8b80-5c6c-a156-8e49a5c9c3fe
   75a4de2d-8049-5bb1-80c9-7250959af3d2
   5991cc2e-2bac-5f83-9ae0-b4a45882a11a
   b8e33f96-449d-5972-8c9c-98b0487549dd
version4 NewRandom --
   01a2c8b4-e3ec-4df5-a027-b7ffe5c6ed5d
   eb2aa249-3614-4833-b979-99c87c4189a8
   4c8bda02-4b27-460a-b46f-d65b4e4f5e48
   23df6b4c-e10f-4bcd-b935-0224f3216680
   2ab1a839-08b2-4069-9bd7-a772c5934e66
   dab1ade8-a446-4609-844b-257a7ab1ba0b
   eb770f37-a59a-4888-9789-d585f514ae93
   b9999fdb-cd20-4bdb-b671-6228667a6746
   8160d492-1ead-4d33-b4e8-d242cf2df332
   63b96ec6-e9d7-4fa4-abb7-31b9e12fca0d

*1:出典: フリー百科事典『ウィキペディアWikipedia)』

PlantUMLをVisualStudioCodeで書く

PlantUMLって

なんかUMLをテキストで書いて絵にするツールらしい

環境

今回はMacでやります

インストール

graphvizをインストール
brew install graphviz

デモ

画面

f:id:m_shige1979:20181103220003p:plain

動画


plantuml

所感

Excelファイルによるドキュメント資料からの脱却を目指したい
まあ、まだまだ無理でしょうけど少しづつやればなんとか…

curlを最新版へソースインストール

ruby関連の勉強を始めてみようとしたけど

なんか出る

/tmp/ruby-build.20181006035131.5059 ~/.rbenv/plugins/ruby-build
curl: (35) Peer reports incompatible or unsupported protocol version.
~

環境

vangrant上のcentos7(64bit)

なんかcurlが古いらしい

$ curl -V
curl 7.29.0 (x86_64-redhat-linux-gnu) libcurl/7.29.0 NSS/3.15.4 zlib/1.2.7 libidn/1.28 libssh2/1.4.3
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp scp sftp smtp smtps telnet tftp
Features: AsynchDNS GSS-Negotiate IDN IPv6 Largefile NTLM NTLM_WB SSL libz
$

証明書関連で動かなくなった感じ?

最新をソースコードよりインストールする

yumでツールをインストール
sudo yum install -y openssl-devel libmetalink-devel libssh2-devel c-ares-devel lbzip2
ダウンロード&インストール
wget https://curl.haxx.se/download/curl-7.61.1.tar.gz
tar zxf curl-7.61.1.tar.gz
cd curl-7.61.1
./configure --enable-libcurl-option
make
sudo make install
インストールパスを間違えたので古いやつを削除してリンクにする
sudo rm -f /usr/bin/curl
sudo ln -s /usr/local/bin/curl /usr/bin/curl
確認
$ curl -V
curl 7.61.1 (x86_64-pc-linux-gnu) libcurl/7.61.1 OpenSSL/1.0.2k zlib/1.2.7
Release-Date: 2018-09-05
Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IPv6 Largefile NTLM NTLM_WB SSL libz UnixSockets HTTPS-proxy
$

rbenvでインストールができるようになった\(^o^)/

[vagrant@localhost ~]$ rbenv install 2.3.7
Downloading ruby-2.3.7.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.7.tar.bz2
Installing ruby-2.3.7...
Installed ruby-2.3.7 to /home/vagrant/.rbenv/versions/2.3.7

[vagrant@localhost ~]$

go言語の学習:websocketで画像を送信し、webで表示

できたの


なにこれ?

websocketサーバより1秒単位で画像をバイナリ形式で送信したもの

ソース(サーバ側)

package main

import (
	"log"
	"net/http"
	"golang.org/x/net/websocket"
	"time"
	"io/ioutil"
	"image"
	"gocv.io/x/gocv"
	"image/color"
	"bytes"
	"image/jpeg"
	"os/user"
)

var (
	black = color.RGBA{0, 0, 0, 0}
)

func main() {
	name := "server"
	log.Println(name + " start")

	http.Handle("/ws", websocket.Handler(WsHandler))
	err := http.ListenAndServe(":8001", nil)
	if err != nil {
		panic("ListenAndServe: " + err.Error())
	}

	log.Println(name + " end")
}

func WsHandler(ws *websocket.Conn) {
	log.Println("connect")

	// 自動送信
	go autoSend(ws)

	// 受信処理監視
	for {
		var b []byte
		err := websocket.Message.Receive(ws, &b)
		if err != nil {
			log.Println(err)
			break
		}
	}
}

// 任意の画像を送信する
func autoSend(ws *websocket.Conn) {
	t := time.NewTicker(1 * time.Second)
	for {
		select {
			case <-t.C:
				// ユーザディレクトリ取得
				user, err := user.Current()
				if err != nil {
					log.Println(err)
					return
				}

				// 画像ファイル読み込み
				b, err := ioutil.ReadFile(user.HomeDir + "/Desktop/" + "pug3_pic1.jpg")
				if err != nil {
					break
				}

				// 画像変換
				atom, err := gocv.IMDecode(b, gocv.IMReadUnchanged)
				if err != nil {
					log.Println(err)
					return
				}
				defer atom.Close()

				// 日時取得
				now := time.Now()

				// 偶数
				if int(now.Second()) % 2 == 0 {
					gocv.Rectangle(&atom, image.Rect(400, 400, 900, 700), black, 2)
				} else {
					gocv.Rectangle(&atom, image.Rect(400, 400, 900, 700), black, -1)
				}

				// 文字列を出力
				timeStr := now.Format("2006/01/02 15:04:05")
				gocv.PutText(&atom, timeStr, image.Pt(20, atom.Rows() - 40), gocv.FontHersheyComplex, 1, black, 1)

				// 画像に変換
				image, err := atom.ToImage()
				if err != nil {
					log.Println(err)
					return
				}
				var jpegBytes []byte
				buf := bytes.NewBuffer(jpegBytes)
				if err := jpeg.Encode(buf, image, nil); err != nil {
					log.Println(err)
				}
				b = buf.Bytes()

				websocket.Message.Send(ws, b)
				log.Println("send")
		}
	}
}

ソース(クライアント側)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <img id="img1" src="">

<script>
    var uri = "ws://localhost:8001" + "/ws";
    webSocket = new WebSocket(uri);
    webSocket.binaryType = 'blob';
    webSocket.onopen = function(e) {
        console.log("open");
    };
    webSocket.onmessage = function(e) {
        console.log("message");
        var data = e.data;
        if (data.constructor === Blob) {
            document.getElementById('img1').src = URL.createObjectURL(data);
        }
    };
    webSocket.onclose = function(e) {
        console.log("close");
    };
</script>
</body>
</html>

ソース(クライアント版2)

package main

import (
	"log"
	"golang.org/x/net/websocket"
	"strconv"
)

var (
	doneCh chan bool
	origin = "http://localhost:8001/"
	url    = "ws://localhost:8001/ws"
)

func main() {
	name := "client"
	log.Println(name + " start")

	doneCh = make(chan bool)

	ws, err := websocket.Dial(url, "", origin)
	if err != nil {
		log.Fatal(err)
		return
	}

	go receiveMsg(ws)

	<-doneCh
	log.Println(name + " end")
}

// 受信
func receiveMsg(ws *websocket.Conn) {
	var b []byte

	for {
		err := websocket.Message.Receive(ws, &b)
		if err != nil {
			log.Println(err)
			doneCh <- true
			break
		}
		log.Printf("Receive datasize=" + strconv.Itoa(len(b)))
	}
}

所感

いろいろ悩んだけど基本は画像をバイナリで送信するだけだった感じ
動画のストリーミングという感じではないのでそこまで難しくないかも…
最初はwebsocketの送受信がうまく行かなかったけどw