wsarecv: An existing connection was forcibly closed by the remote host. がどうにも分からない

PruneMobile というソフトウェアを作っています。

クライアント側のプログラムで、Golang(Go言語)の 並列(通信)処理を始めて本格的に実装したのですが、サーバ側にエラーが乱発しています。

Golangの通信プログラムの仕様は今一つ分かなくて(C言語なら「私がマニュアルだ」と傲慢に言い放ちますが)、試行錯誤の最中です。

"Go"は"C"と、ポインタや配列も違って、勉強し直し状態です。

それはさておき、クライアント側のプログラムは、以下の通り。ポイントは、

go passenger(1)
go passenger(2)
go passenger(3)
go passenger(4)

でスレッド処理をしてみたところです。

// client6.go ペアは server12.go

package main

import (
	"flag"
	"log"
	"net/url"
	"os"

	"github.com/gorilla/websocket"

	"math/rand"
	"time"
)

// GetLoc GetLoc
type GetLoc struct {
	ID  int     `json:"id"`
	Lat float64 `json:"lat"`
	Lng float64 `json:"lng"`
	//Address string  `json:"address"`
}

var gl [100]GetLoc
var gl2 [100]GetLoc
var c [100](*websocket.Conn)

var addr = flag.String("addr", "0.0.0.0:8080", "http service address") // テスト
//var addr = flag.String("addr", "localhost:8080", "http service address") // テスト
var upgrader = websocket.Upgrader{} // use default options

func random(min, max float64) float64 {
	return rand.Float64()*(max-min) + min
}

func main() {
	rand.Seed(time.Now().UnixNano())

	flag.Parse()
	log.SetFlags(0)

	go passenger(1)
	go passenger(2)
	go passenger(3)
	go passenger(4)

	time.Sleep(25 * time.Second)
}

func passenger(i int) {

	/*
		gl[i] = GetLoc{
			ID:  0,
			Lat: 35.653976,
			Lng: 139.796821,
		}
	*/

	u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo2"}
	log.Printf("connecting to %s", u.String())

	var err error
	c[i], _, err = websocket.DefaultDialer.Dial(u.String(), nil)
	//c[i] = cc
	if err != nil {
		log.Fatal("dial:", err)
	}
	defer c[i].Close()

	gl[i].ID = 0
	gl[i].Lat = 35.653976
	gl[i].Lng = 139.796821

	if i != -100 {
		log.Printf("before1 %d ID:%d", i, gl[i].ID)
		log.Printf("before1 %d Lat:%f", i, gl[i].Lat)
		log.Printf("before1 %d Lng:%f", i, gl[i].Lng)
	}

	//err = c.WriteJSON(mt, gl)
	err = c[i].WriteJSON(gl[i])
	if err != nil {
		log.Println("write:", err)
	}

	//&(gl2[i]) = new(GetLoc)
	err = c[i].ReadJSON(&(gl2[i]))

	if i != -100 {
		log.Printf("after1 %d ID:%d", i, gl2[i].ID)
		log.Printf("after1 %d Lat:%f", i, gl2[i].Lat)
		log.Printf("after1 %d Lng:%f", i, gl2[i].Lng)
	}

	if gl2[i].ID == 0 {
		// 強制停止
		os.Exit(1)
	}

	gl[i].ID = gl2[i].ID

	for k := 0; k < 20; k++ {
		gl[i].Lat += random(0.5, -0.5) * 0.00001 * 10 * 5
		gl[i].Lng += random(0.5, -0.5) * 0.00002 * 10 * 5

		if i != -100 {
			log.Printf("before2 %d ID:%d", i, gl[i].ID)
			log.Printf("before2 %d Lat:%f", i, gl[i].Lat)
			log.Printf("before2 %d Lng:%f", i, gl[i].Lng)
		}

		err = c[i].WriteJSON(gl[i])
		if err != nil {
			log.Println("write:", err)
		}
		//gl2[i] := new(GetLoc)
		err = c[i].ReadJSON(&(gl2[i]))
		if i != -100 {
			log.Printf("after2 %d ID:%d", i, gl2[i].ID)
			log.Printf("after2 %d Lat:%f", i, gl2[i].Lat)
			log.Printf("after2 %d Lng:%f", i, gl2[i].Lng)
		}
		time.Sleep(1 * time.Second) // 1秒休む
	}

	gl[i].ID = gl2[i].ID
	gl[i].Lat = 999.9
	gl[i].Lng = 999.9

	log.Printf("before3 %d ID:%d", i, gl[i].ID)
	log.Printf("before3 %d Lat:%f", i, gl[i].Lat)
	log.Printf("before3 %d Lng:%f", i, gl[i].Lng)

	err = c[i].WriteJSON(gl[i])

	err = c[i].ReadJSON(&(gl2[i]))
	log.Printf("after3 %d ID:%d", i, gl2[i].ID)
	log.Printf("after3 %d Lat:%f", i, gl2[i].Lat)
	log.Printf("after3 %d Lng:%f", i, gl2[i].Lng)

}

でもって、サーバ側に、

wsarecv: An existing connection was forcibly closed by the remote host.

がでてくるのですが、これがどうにも分からない。

websocket.DefaultDialer.Dialもスレッド単位で分離しているから、良さそうなんだけどなー

サーバ側の出力コンソールは、こんな感じ

first ID:0
117 after gl = <-chan2_1
first Lat:0.000000
94
first Lng:0.000000
126
read: read tcp 127.0.0.1:8080->127.0.0.1:50368: wsarecv: An existing connection was forcibly closed by the remote host.
138
write: write tcp 127.0.0.1:8080->127.0.0.1:50366: wsasend: An existing connection was forcibly closed by the remote host.
after gl2.id = 3
after gl2.lat = 35.653909
after gl2.lng= 139.796333
145
148
113 before gl = <-chan2_1
86
final ID:3
final Lat:35.653909
final Lng:139.796333
94
write: write tcp 127.0.0.1:8080->127.0.0.1:50365: wsasend: An existing connection was forcibly closed by the remote host

とりあえず、忘れそうなので、自分の為にメモを残しておきます。

websocket.DefaultDialer.Dialもスレッド単位ごとに発行しているから分離しているから、良さそうなんだけどなぁ。Websocketの複数のエンドポイントを作って並列処理につっこむには、もう一頑張り必要、ということでしょう。

ちなみに「誰か、私を助けてくれたっていいんだからね!」と申し上げておきます。

 

2020/10,江端さんの技術メモ

Posted by ebata