[Go言語] JSとGoでchatを作りたい その2

### Websocketのハンドリング
connection.go

package domain

import "github.com/gorilla/websocket"

type WsJsonResponse struct {
	Action string `json:"action"`
	Message string `json:"message"`
}

type WebSocketConnection struct {
	*websocket.Conn
}

type WsPlayload struct {
	Action String `json:"action"`
	Message string `json:"message"`
	Username string `json:"username"`
	Conn WebSocketConnection `json:"-"`
}

handlers.go

import (
	"chat/domain"
	"log"
	"net/http"
	"fmt"

	"github.com/CloudyKit/jet/v6"
	"github.com/gorilla/websocket"
)

var views = jet.NewSet(
	jet.NewOSFileSystemLoader("./html"),
	jet.InDevelopmentMode(),
)

var upgradeConnection = websocket.Upgrader {
	ReadBufferSize: 1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {return true},
}

var (
	wsChan = make(chan domain.WsPayload)

	clients = make(map[domain.WebSocketConnection]string)
)

func WsEndpoint(w http.ResponseWriter, r *http.Request){
	ws, err := upgradeConnection.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
	}
	log.Println("OK client connecting")

	conn := domain.WebSocketConnection{Conn: ws}
	clients[conn] = ""

	go ListenForWs(&conn)


	var response domain.WsJsonResponse
	response.Message = `<li>Connect to server</li>`

	err = ws.WriteJSON(response)
	if err != nil {
		log.Println(err)
	}
}

func Home(w http.ResponseWriter, r *http.Request){
	err := renderPage(w, "home.jet", nil)
	if err != nil {
		log.Println(err)
	}
}

func renderPage(w http.ResponseWriter, tmpl string, data jet.VarMap) error {
	view, err := views.GetTemplate(tmpl)
	if err != nil {
		log.Println(err)
		return err
	}

	err = view.Execute(w, data, nil)
	if err != nil {
		log.Println(err)
		return err
	}
	return nil
}

func ListenForWs(conn *domain.WebSocketConnection){
	defer func(){
		if r := recover(); r != nil {
			log.Println("Error", fmt.Sprintf("%v", r))
		}
	}()

	var payload domain.WsPayload

	for {
		err := conn.ReadJSON(&payload)
		if err != nil {

		} else {
			payload.Conn = *conn
			wsChan <- payload
		}
	}
}

func broadcastToAll(response domain.WsJsonResponse){
	for client := range clients {
		err := client.WriteJSON(response)
		if err != nil {
			log.Println("websockets err")
			_ = client.Close()
			delete(clients, client)
		}
	}
}

func ListenToWsChannel(){
	var response domain.WsJsonResponse

	for {
		e := <-wsChan

		response.Action = "Got here"
		response.Message = fmt.Sprintf("Some message, and action was %s", e.Action)

		broadcastToAll(response)
	}
}

goroutineを使って、ListenForWs関数を別プロセスで呼び出すことで、ブラウザからの通信を常にキャッチし続ける状態を作る。gorouteは go と書くだけ

main.go

import (
	"chat/internal/handlers"
	"log"
	"net/http"
)

func main(){
	mux := routes()
	log.Println("Starting channel listener")
	go handlers.ListenToWsChannel()

	log.Println("Starting web server on port 8080")

	_ = http.ListenAndServe(":8080", mux)
}

successfully connected
script.js:21 {action: ”, message: ‘

  • Connect to server
  • ‘}

    2021/10/18 03:41:26 Error runtime error: invalid memory address or nil pointer dereference
    2021/10/18 03:41:30 Error repeated read on failed websocket connection
    2021/10/18 03:41:30 OK client connecting

    なんか上手くいってるっぽいが、どういう仕組みなのか全然理解できない…