Skip to content

Instantly share code, notes, and snippets.

@crosstyan
Last active October 20, 2023 10:17
Show Gist options
  • Save crosstyan/47e7d3fa1b9e4716c0d6c76760a4a70c to your computer and use it in GitHub Desktop.
Save crosstyan/47e7d3fa1b9e4716c0d6c76760a4a70c to your computer and use it in GitHub Desktop.
A websocket based multiroom chat using golang and gin
package main
import (
"log"
"net/http"
"net/url"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
//"github.com/gorilla/mux"
//The name mux stands for "HTTP request multiplexer".
"github.com/gorilla/websocket"
)
var sessionGroupMap = make(map[string]map[uuid.UUID]*websocket.Conn)
func main() {
app := gin.Default()
app.Any("thread", wsHandler) //callback
app.Run("localhost:8081")
}
var upgrader = websocket.Upgrader{
//Solve "request origin not allowed by Upgrader.CheckOrigin"
CheckOrigin: func(r *http.Request) bool {
return true
},
}
//w http.ResponseWriter, r *http.Request
func wsHandler(ginContext *gin.Context) { //Usually use c *gin.Context
wsSession, err := upgrader.Upgrade(ginContext.Writer, ginContext.Request, nil)
if err != nil {
log.Fatal(err)
}
uid := uuid.New()
wsURL := ginContext.Request.URL
wsURLParam, err := url.ParseQuery(wsURL.RawQuery)
if err != nil {
wsSession.Close()
log.Println(err)
}
if _, ok := wsURLParam["name"]; ok {
threadName := wsURLParam["name"][0]
log.Printf("A client connect to %s", threadName)
if _, ok := sessionGroupMap[threadName]; ok { //Maybe needn't this if?
sessionGroupMap[threadName][uid] = wsSession
} else {
sessionGroupMap[threadName] = make(map[uuid.UUID]*websocket.Conn)
sessionGroupMap[threadName][uid] = wsSession
}
defer wsSession.Close()
echo(wsSession, threadName, uid)
} else {
wsSession.Close()
}
}
func echo(wsSession *websocket.Conn, threadName string, uid uuid.UUID) {
//Message Type:
//Details in
//https://godoc.org/github.com/gorilla/websocket#pkg-constants
//TextMessage=1
//BinaryMessage=2
for { //An endlessloop
messageType, messageContent, err := wsSession.ReadMessage()
if messageType == 1 {
log.Printf("Recv:%s from %s", messageContent, threadName)
broadcast(threadName, messageContent)
}
if err != nil {
wsSession.Close()
delete(sessionGroupMap[threadName], uid)
//I don't think it's recommended to deal with connection closing like this, but it's the easiest way.
//Or you have to maintain a hashmap to indicate if a session is open or closed? No idea.
if websocket.IsCloseError(err, websocket.CloseGoingAway) {
log.Printf("Client disconnected in %s", threadName)
} else {
log.Printf("Reading Error in %s. %s", threadName, err)
}
break //To escape from the endless loop
}
}
}
func broadcast(threadName string, messageContent []byte) {
for _, wsSession := range sessionGroupMap[threadName] {
err := wsSession.WriteMessage(1, messageContent)
if err != nil {
log.Println(err)
}
}
}
@brutalzinn
Copy link

Thanks. Save me a lot of time!

@supersida159
Copy link

[GIN] 2023/10/20 - 17:14:20 | 404 | 0s | 127.0.0.1 | GET "/socket.io/?EIO=4&transport=polling&t=OjCb2cH"
[GIN] 2023/10/20 - 17:14:20 | 404 | 0s | 127.0.0.1 | GET "/socket.io/?EIO=4&transport=polling&t=OjCb2cV"
when i run your code, it repeats this, could you have me fix it ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment