Skip to content

Commit 66ea3ac

Browse files
author
ShiJiashuai
committed
一致性检查、文档同步
1 parent eaea5af commit 66ea3ac

File tree

10 files changed

+723
-158
lines changed

10 files changed

+723
-158
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ go run ./cmd/server
7979
8080
## 配置
8181

82+
可以通过环境变量调整服务监听地址:
83+
84+
- `ADDR`:HTTP 服务监听地址(默认 `:8080`)。
85+
8286
默认情况下,信令服务只允许本地开发来源(`localhost` / `127.0.0.1`)。
8387
如需在其他域名下访问,可以设置环境变量 `WS_ALLOWED_ORIGINS`,例如:
8488

@@ -87,6 +91,13 @@ $env:WS_ALLOWED_ORIGINS="https://example.com,https://foo.bar"
8791
go run ./cmd/server
8892
```
8993

94+
如需临时允许所有来源(不推荐用于生产环境),可设置:
95+
96+
```powershell
97+
$env:WS_ALLOWED_ORIGINS="*"
98+
go run ./cmd/server
99+
```
100+
90101
在生产环境中请根据实际域名进行配置。
91102

92103
## 文档

ROADMAP.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,15 @@
4444
4545
### 3.1 后端(Go 信令服务)
4646

47-
- [ ] **增加基础日志输出**
47+
- [x] **增加基础日志输出**
4848
`internal/signal/hub.go` 中:
49-
- [ ] WebSocket 升级失败时,打印请求路径和错误信息。
50-
- [ ] 读取 JSON 消息失败时,打印错误并包含房间/客户端信息(若有)。
51-
- [ ] 写入 JSON 失败时,同样记录日志,便于调试。
49+
- [x] WebSocket 升级失败时,打印请求路径和错误信息。
50+
- [x] 读取 JSON 消息失败时,打印错误并包含房间/客户端信息(若有)。
51+
- [x] 写入 JSON 失败时,同样记录日志,便于调试。
5252

53-
- [ ] **更稳健的连接清理**
54-
- [ ]`HandleWS` 的循环中,只要出现读/写错误,就调用 `removeClient`,确保房间中不会残留“僵尸连接”。
55-
- [ ]`removeClient` 中确认:当房间成员为空时删除该房间。
53+
- [x] **更稳健的连接清理**
54+
- [x]`HandleWS` 的循环中,只要出现读/写错误,就调用 `removeClient`,确保房间中不会残留“僵尸连接”。
55+
- [x]`removeClient` 中确认:当房间成员为空时删除该房间。
5656

5757
- [ ] **(可选)简单心跳机制**
5858
- 前端每隔 N 秒发送一个 `type: "ping"` 的消息。

changelog/2025-12-18.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# 2025-12-18
2+
3+
- 初始化:新增 changelog 目录,用于记录后续变更。
4+
5+
- 前端:补齐 DataChannel 聊天发送(Send 按钮 + Enter),未建立连接时给出提示。
6+
- 前端:成员列表渲染优化(胶囊按钮、禁用自己、点击自动填入 Remote)。
7+
- 前端:状态机与按钮状态统一(Join/Leave 文案切换、输入框启用禁用逻辑)。
8+
- 前端:信令与通话流程修复(offer 到达后自动进入 calling,区分手动关闭与异常断开)。
9+
- 前端:资源清理与可重复使用(Hangup 不再停止本地轨道;新增 Leave 清理 PC/WS/媒体;屏幕共享与再次呼叫兼容)。
10+
11+
- 后端:信令 Hub 支持通过 `Options` 注入 Origin 校验配置(`AllowedOrigins`/`AllowAllOrigins`),避免包内隐式全局 env 状态。
12+
- 后端:入口统一解析 `WS_ALLOWED_ORIGINS` 并注入 Hub;服务启动改用 `ServeMux` + `http.Server`,避免使用默认全局路由。
13+
- 后端:`writePump` 写失败时主动关闭连接,触发读循环退出并完成资源清理。
14+
- 后端:升级失败日志补充 `path`;连接结束时关闭 `send` 通道以退出写协程;`removeClient` 调整为不关闭通道,避免 `leave` 后同连接再次 `join` 引发 panic。
15+
16+
- 文档:同步更新 `docs/signaling.md` 中的 Hub 结构与 `writePump` 示例代码。
17+
- 文档:更新 `README.md` 配置章节(补充 `ADDR``WS_ALLOWED_ORIGINS="*"` 用法),并同步更新 `ROADMAP.md` 阶段 1 后端任务勾选状态。
18+
19+
- 文档:同步补充 `docs/guide.md` 中 Hub 的 Origin 校验配置说明(`allowedOrigins`/`allowAllOrigins`),强调由入口解析 `WS_ALLOWED_ORIGINS` 并通过 `signal.Options` 注入。

cmd/server/main.go

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,47 @@ import (
55
"net/http"
66
"os"
77
"path/filepath"
8+
"strings"
89

910
"lessup/webrtc/internal/signal"
1011
)
1112

1213
func main() {
13-
hub := signal.NewHub()
14-
http.HandleFunc("/ws", hub.HandleWS)
15-
16-
webDir := filepath.Join("web")
17-
fs := http.FileServer(http.Dir(webDir))
18-
http.Handle("/", fs)
19-
2014
addr := ":8080"
2115
if v := os.Getenv("ADDR"); v != "" {
2216
addr = v
2317
}
24-
log.Println("listening", addr)
25-
log.Fatal(http.ListenAndServe(addr, nil))
18+
19+
var wsAllowAll bool
20+
var wsAllowed []string
21+
if v := os.Getenv("WS_ALLOWED_ORIGINS"); v != "" {
22+
if v == "*" {
23+
wsAllowAll = true
24+
} else {
25+
parts := strings.Split(v, ",")
26+
wsAllowed = make([]string, 0, len(parts))
27+
for _, p := range parts {
28+
p = strings.TrimSpace(p)
29+
if p != "" {
30+
wsAllowed = append(wsAllowed, p)
31+
}
32+
}
33+
}
34+
}
35+
36+
hub := signal.NewHubWithOptions(signal.Options{
37+
AllowedOrigins: wsAllowed,
38+
AllowAllOrigins: wsAllowAll,
39+
})
40+
41+
mux := http.NewServeMux()
42+
mux.HandleFunc("/ws", hub.HandleWS)
43+
44+
webDir := filepath.Join("web")
45+
fs := http.FileServer(http.Dir(webDir))
46+
mux.Handle("/", fs)
47+
48+
srv := &http.Server{Addr: addr, Handler: mux}
49+
log.Println("server: listening", addr)
50+
log.Fatal(srv.ListenAndServe())
2651
}

docs/guide.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ type Message struct {
104104
- `Hub`
105105
- `rooms map[string]map[string]*Client``房间名 -> (客户端 ID -> Client)`
106106
- `upg websocket.Upgrader`:升级 HTTP 为 WebSocket。
107+
- `allowedOrigins` / `allowAllOrigins`:WebSocket `Origin` 校验配置(由服务入口解析 `WS_ALLOWED_ORIGINS` 并通过 `signal.Options` 注入)。
107108

108109
- `Client`
109110
- `id`:客户端 ID(由前端生成,例如随机字符串)。

docs/signaling.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ type Hub struct {
8484
mu sync.RWMutex
8585
rooms map[string]map[string]*Client
8686
upg websocket.Upgrader
87+
88+
allowedOrigins []string
89+
allowAllOrigins bool
8790
}
8891

8992
type Client struct {
@@ -125,6 +128,7 @@ type Client struct {
125128
func (h *Hub) writePump(c *Client) {
126129
for msg := range c.send {
127130
if err := c.conn.WriteJSON(msg); err != nil {
131+
c.conn.Close()
128132
break
129133
}
130134
}
@@ -147,14 +151,15 @@ type Client struct {
147151
func (h *Hub) HandleWS(w http.ResponseWriter, r *http.Request) {
148152
c, err := h.upg.Upgrade(w, r, nil)
149153
if err != nil {
150-
log.Printf("signal: ws upgrade failed from %s: %v", r.RemoteAddr, err)
154+
log.Printf("signal: ws upgrade failed from %s path=%s: %v", r.RemoteAddr, r.URL.Path, err)
151155
return
152156
}
153157
log.Printf("signal: ws connected from %s", r.RemoteAddr)
154158
client := &Client{conn: c, send: make(chan Message, 32)}
155159
go h.writePump(client)
156160
defer func() {
157161
h.removeClient(client)
162+
close(client.send)
158163
c.Close()
159164
}()
160165
for {
@@ -188,6 +193,7 @@ func (h *Hub) HandleWS(w http.ResponseWriter, r *http.Request) {
188193
3. 启动 `writePump` 协程,负责异步写消息。
189194
4. 使用 `defer` 确保函数结束时:
190195
- 调用 `removeClient` 移除客户端;
196+
- 关闭 `send` 通道,结束 `writePump` 写协程;
191197
- 关闭连接 `c.Close()`
192198

193199
### 4.2 消息读取与分发

internal/signal/hub.go

Lines changed: 55 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"log"
55
"net/http"
66
"net/url"
7-
"os"
87
"strings"
98
"sync"
109

@@ -15,6 +14,9 @@ type Hub struct {
1514
mu sync.RWMutex
1615
rooms map[string]map[string]*Client
1716
upg websocket.Upgrader
17+
18+
allowedOrigins []string
19+
allowAllOrigins bool
1820
}
1921

2022
type Client struct {
@@ -24,31 +26,31 @@ type Client struct {
2426
send chan Message
2527
}
2628

27-
var (
28-
allowedOrigins []string
29-
allowAllOrigins bool
30-
)
29+
type Options struct {
30+
AllowedOrigins []string
31+
AllowAllOrigins bool
32+
}
3133

32-
func init() {
33-
v := os.Getenv("WS_ALLOWED_ORIGINS")
34-
if v == "" {
35-
return
36-
}
37-
if v == "*" {
38-
allowAllOrigins = true
39-
return
40-
}
41-
parts := strings.Split(v, ",")
42-
for _, p := range parts {
43-
p = strings.TrimSpace(p)
44-
if p != "" {
45-
allowedOrigins = append(allowedOrigins, p)
46-
}
34+
func NewHub() *Hub {
35+
return NewHubWithOptions(Options{})
36+
}
37+
38+
func NewHubWithOptions(opts Options) *Hub {
39+
h := &Hub{
40+
rooms: make(map[string]map[string]*Client),
41+
allowedOrigins: append([]string(nil), opts.AllowedOrigins...),
42+
allowAllOrigins: opts.AllowAllOrigins,
43+
}
44+
h.upg = websocket.Upgrader{
45+
CheckOrigin: func(r *http.Request) bool {
46+
return h.isOriginAllowed(r)
47+
},
4748
}
49+
return h
4850
}
4951

50-
func isOriginAllowed(r *http.Request) bool {
51-
if allowAllOrigins {
52+
func (h *Hub) isOriginAllowed(r *http.Request) bool {
53+
if h.allowAllOrigins {
5254
return true
5355
}
5456
origin := r.Header.Get("Origin")
@@ -59,7 +61,7 @@ func isOriginAllowed(r *http.Request) bool {
5961
}
6062
return false
6163
}
62-
if len(allowedOrigins) == 0 {
64+
if len(h.allowedOrigins) == 0 {
6365
u, err := url.Parse(origin)
6466
if err != nil {
6567
return false
@@ -70,36 +72,26 @@ func isOriginAllowed(r *http.Request) bool {
7072
}
7173
return false
7274
}
73-
for _, o := range allowedOrigins {
75+
for _, o := range h.allowedOrigins {
7476
if o == origin {
7577
return true
7678
}
7779
}
7880
return false
7981
}
8082

81-
func NewHub() *Hub {
82-
return &Hub{
83-
rooms: make(map[string]map[string]*Client),
84-
upg: websocket.Upgrader{
85-
CheckOrigin: func(r *http.Request) bool {
86-
return isOriginAllowed(r)
87-
},
88-
},
89-
}
90-
}
91-
9283
func (h *Hub) HandleWS(w http.ResponseWriter, r *http.Request) {
9384
c, err := h.upg.Upgrade(w, r, nil)
9485
if err != nil {
95-
log.Printf("signal: ws upgrade failed from %s: %v", r.RemoteAddr, err)
86+
log.Printf("signal: ws upgrade failed from %s path=%s: %v", r.RemoteAddr, r.URL.Path, err)
9687
return
9788
}
9889
log.Printf("signal: ws connected from %s", r.RemoteAddr)
9990
client := &Client{conn: c, send: make(chan Message, 32)}
10091
go h.writePump(client)
10192
defer func() {
10293
h.removeClient(client)
94+
close(client.send)
10395
c.Close()
10496
}()
10597
for {
@@ -162,30 +154,34 @@ func (h *Hub) removeClient(c *Client) {
162154
if c.room == "" || c.id == "" {
163155
return
164156
}
165-
if m, ok := h.rooms[c.room]; ok {
166-
if existing, ok2 := m[c.id]; ok2 {
167-
delete(m, c.id)
168-
close(existing.send)
157+
room := c.room
158+
if m, ok := h.rooms[room]; ok {
159+
if _, ok2 := m[c.id]; !ok2 {
160+
c.room = ""
161+
return
169162
}
163+
delete(m, c.id)
164+
c.room = ""
170165
if len(m) == 0 {
171-
delete(h.rooms, c.room)
172-
log.Printf("signal: room %s closed", c.room)
173-
} else {
174-
members := make([]string, 0, len(m))
175-
for id := range m {
176-
members = append(members, id)
177-
}
178-
msg := Message{
179-
Type: "room_members",
180-
Room: c.room,
181-
Members: members,
182-
}
183-
for _, cli := range m {
184-
if cli != nil && cli.conn != nil {
185-
select {
186-
case cli.send <- msg:
187-
default:
188-
}
166+
delete(h.rooms, room)
167+
log.Printf("signal: room %s closed", room)
168+
return
169+
}
170+
171+
members := make([]string, 0, len(m))
172+
for id := range m {
173+
members = append(members, id)
174+
}
175+
msg := Message{
176+
Type: "room_members",
177+
Room: room,
178+
Members: members,
179+
}
180+
for _, cli := range m {
181+
if cli != nil && cli.conn != nil {
182+
select {
183+
case cli.send <- msg:
184+
default:
189185
}
190186
}
191187
}
@@ -210,6 +206,7 @@ func (h *Hub) writePump(c *Client) {
210206
for msg := range c.send {
211207
if err := c.conn.WriteJSON(msg); err != nil {
212208
log.Printf("signal: write message error room=%s id=%s: %v", c.room, c.id, err)
209+
c.conn.Close()
213210
break
214211
}
215212
}

0 commit comments

Comments
 (0)