Untitled
unknown
golang
2 years ago
4.0 kB
23
Indexable
package main
import (
"flag"
"log"
"net/http"
"path/filepath"
"sync"
"text/template"
"encoding/json"
"github.com/gorilla/websocket"
"github.com/google/uuid"
)
// templ represents a single template
type templateHandler struct {
once sync.Once
filename string
templ *template.Template
}
var clientNames = make(map[*websocket.Conn]string)
// ServeHTTP handles the HTTP request.
func (t *templateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
t.once.Do(func() {
t.templ = template.Must(template.ParseFiles(filepath.Join("templates", t.filename)))
})
t.templ.Execute(w, r)
}
func main() {
var addr = flag.String("addr", ":8080", "The addr of the application.")
flag.Parse() // parse the flags
r := newRoom()
http.Handle("/", &templateHandler{filename: "chat.html"})
http.HandleFunc("/register", registerHandler)
http.Handle("/room", r)
// get the room going
go r.run()
// start the web server
log.Println("Starting web server on", *addr)
if err := http.ListenAndServe(*addr, nil); err != nil {
log.Fatal("ListenAndServe:", err)
}
}
type room struct {
clients map[*client]bool
join chan *client
leave chan *client
forward chan []byte
}
type client struct {
info clientInfo
receive chan []byte
room *room
}
func newRoom() *room {
return &room{
forward: make(chan []byte),
join: make(chan *client),
leave: make(chan *client),
clients: make(map[*client]bool),
}
}
var messages []string
var globalName string
var clientsMap = make(map[string]clientInfo)
func registerHandler(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
globalName = r.FormValue("name")
http.Redirect(w, r, "/", http.StatusFound)
}
func (r *room) run() {
for {
select {
case client := <-r.join:
r.clients[client] = true
for _, msg := range messages {
client.receive <- []byte(msg)
}
case client := <-r.leave:
delete(r.clients, client)
close(client.receive)
case msg := <-r.forward:
for client := range r.clients {
client.receive <- msg
}
messages = append(messages, string(msg))
}
}
}
const (
socketBufferSize = 1024
messageBufferSize = 256
)
type clientInfo struct {
id string
conn *websocket.Conn
name string
}
var upgrader = &websocket.Upgrader{ReadBufferSize: socketBufferSize, WriteBufferSize: messageBufferSize}
func (r *room) ServeHTTP(w http.ResponseWriter, req *http.Request) {
id := uuid.New().String()
socket, err := upgrader.Upgrade(w, req, nil)
if err != nil {
return
}
if globalName == "" {
globalName = "Unknown"
}
client := &client{
info: clientInfo{
id: id,
conn: socket,
name: globalName,
},
receive: make(chan []byte, messageBufferSize),
room: r,
}
globalName = ""
err = client.info.conn.WriteMessage(websocket.TextMessage, []byte("You are connected as: "+client.info.name))
if err != nil {
log.Println("Error sending name message:", err)
}
clientsMap[id] = client.info
r.join <- client
defer func() {
// When client leaves, remove it from the clientsMap
delete(clientsMap, id)
r.leave <- client
}()
go client.write()
client.read()
}
type chatMessage struct {
Name string `json:"name"`
Message string `json:"message"`
}
func (c *client) read() {
defer c.info.conn.Close()
for {
_, msg, err := c.info.conn.ReadMessage()
if err != nil {
return
}
msgObj := chatMessage{
Name: c.info.name,
Message: string(msg),
}
jsonData, err := json.Marshal(msgObj)
if err != nil {
return
}
c.room.forward <- jsonData
}
}
func (c *client) write() {
defer c.info.conn.Close()
for msg := range c.receive {
err := c.info.conn.WriteMessage(websocket.TextMessage, msg)
if err != nil {
return
}
}
}
Editor is loading...