Add week 3 materials
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
# websockets
|
||||
|
||||
В данной директории находится простая реализация websocket сервера на Go.
|
||||
|
||||
## Управление
|
||||
|
||||
Запуск сервера: `go run .`.
|
||||
|
||||
Для подключения к серверу предлагается использовать утилиту `wscat`:
|
||||
```bash
|
||||
wscat --connect 'localhost:8000/connect?author=igor'
|
||||
# В параметре author указываем наш никнейм
|
||||
```
|
||||
|
||||
Далее можно отправить JSON сообщение с полем `text`, после чего это сообщение будет доставлено всем
|
||||
клиентам сервера:
|
||||
```bash
|
||||
~ wscat --connect 'localhost:8000/connect?author=igor'
|
||||
> {"text": "privet"}
|
||||
< {"Text":"privet","Author":"igor","SendTime":"2024-09-24T01:49:50.623671+03:00"}
|
||||
```
|
||||
|
||||
## Бонус
|
||||
|
||||
Чтобы открыть свой сервер, запущенный локально, всему интернету,
|
||||
можно использовать ngrok:
|
||||
```
|
||||
ngrok http 8000
|
||||
```
|
||||
|
||||
После выполнения вам будет выдан публичный домен, по которому будет доступен
|
||||
порт на вашей локальной системе.
|
||||
@@ -0,0 +1,8 @@
|
||||
module ws
|
||||
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/google/uuid v1.3.1
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
)
|
||||
@@ -0,0 +1,4 @@
|
||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
@@ -0,0 +1,118 @@
|
||||
package main
|
||||
|
||||
/*
|
||||
Connect client 1:
|
||||
|
||||
wscat --connect 'localhost:8000/connect?author=lupa'
|
||||
|
||||
Connect client 2:
|
||||
|
||||
wscat --connect 'localhost:8000/connect?author=lupa'
|
||||
Message: {"Text": "hi there"}
|
||||
*/
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type MessageStream struct {
|
||||
Text string
|
||||
Author string
|
||||
SendTime *time.Time
|
||||
}
|
||||
|
||||
type MessengerServer struct {
|
||||
history chan *MessageStream
|
||||
toSend map[string]chan *MessageStream
|
||||
}
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
}
|
||||
|
||||
func (s *MessengerServer) Connect(w http.ResponseWriter, r *http.Request) {
|
||||
// Extract author from the URL query parameters
|
||||
author := r.URL.Query().Get("author")
|
||||
if author == "" {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Printf("failed to upgrade: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
id := uuid.New()
|
||||
s.toSend[id.String()] = make(chan *MessageStream, 1000)
|
||||
log.Printf("Connected: %v\n", id)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
var msg MessageStream
|
||||
err := conn.ReadJSON(&msg)
|
||||
if err != nil {
|
||||
log.Println("Failed to decode a message:", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Received from %s: %v\n", author, msg)
|
||||
|
||||
now := time.Now()
|
||||
msg.SendTime = &now
|
||||
msg.Author = author
|
||||
s.history <- &msg
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
mes := <-s.toSend[id.String()]
|
||||
err := conn.WriteJSON(mes)
|
||||
if err != nil {
|
||||
log.Printf("Deleted %v stream, sending error: %v", id.String(), err)
|
||||
delete(s.toSend, id.String())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Sent to stream %v: %v\n", id, mes)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MessengerServer) fanout() {
|
||||
for {
|
||||
mes := <-s.history
|
||||
|
||||
log.Printf("Got from history: %v", mes)
|
||||
|
||||
for _, ch := range s.toSend {
|
||||
ch <- mes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
port := os.Getenv("MESSENGER_SERVER_PORT")
|
||||
if port == "" {
|
||||
port = "8000"
|
||||
log.Println("Missing MESSENGER_SERVER_PORT, using default value: " + port)
|
||||
}
|
||||
server := MessengerServer{
|
||||
history: make(chan *MessageStream, 1000),
|
||||
toSend: make(map[string]chan *MessageStream),
|
||||
}
|
||||
|
||||
http.HandleFunc("/connect", server.Connect)
|
||||
|
||||
go server.fanout()
|
||||
|
||||
log.Fatal(http.ListenAndServe("0.0.0.0:"+port, nil))
|
||||
}
|
||||
Reference in New Issue
Block a user