-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.go
129 lines (110 loc) · 2.74 KB
/
server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package main
import (
"bufio"
"fmt"
"log"
"net"
"time"
"github.com/rs/xid"
)
var (
enteringChannel = make(chan *User) // 进入
leavingChannel = make(chan *User) // 退出
messageChannel = make(chan Message, 8) // 全局消息
)
// User User结构体
type User struct {
ID string // 用户唯一标识 GenUserID函数生成
Addr string // 用户ID地址和端口
EnterAt time.Time // 用户进入时间
MessageChannel chan string // 当前用户发送消息的通道
}
// Message Message结构体
type Message struct {
OwnerID string // 消息的发出者ID
Content string // 消息内容
}
func main() {
listener, err := net.Listen("tcp", ":2023")
log.Println("Listening on port 2023...")
if err != nil {
log.Println(err)
}
go broadcaster()
for {
conn, err := listener.Accept()
if err != nil {
log.Println(err)
continue
}
go handleConn(conn)
}
}
func handleConn(conn net.Conn) {
defer conn.Close() // 关闭连接
// 1. 实例化User结构体创建用户user
user := &User{
ID: GenUserID(),
Addr: conn.RemoteAddr().String(),
EnterAt: time.Now(),
MessageChannel: make(chan string, 8),
}
log.Println("--- " + user.Addr + " has enter the chatroom." + " ---")
// 2. 给 user 发消息
go sendMessage(conn, user.MessageChannel)
// 3. 给当前用户发送Welcome消息 给其他用户发送has enter消息
user.MessageChannel <- "Welcome, " + user.ID
messageChannel <- Message{
OwnerID: user.ID,
Content: "User: `" + user.ID + "` has enter.",
}
// 4. 当前用户写入全局用户列表
enteringChannel <- user
// 5. 读取用户输入并广播发送
input := bufio.NewScanner(conn)
for input.Scan() {
messageChannel <- Message{
OwnerID: user.ID,
Content: user.ID + ": " + input.Text(),
}
}
if err := input.Err(); err != nil {
log.Println(err)
}
// 6. 用户离开 进入leavingChannel 并向其他所有用户发送left消息
leavingChannel <- user
messageChannel <- Message{
OwnerID: user.ID,
Content: "User: `" + user.ID + "` has left.",
}
}
// sendMessage 给客户端发送消息
func sendMessage(conn net.Conn, ch <-chan string) {
for msg := range ch {
fmt.Fprintln(conn, msg)
}
}
// broadcaster 消息广播
func broadcaster() {
users := make(map[*User]struct{})
for {
select {
case user := <-enteringChannel:
users[user] = struct{}{}
case user := <-leavingChannel:
delete(users, user)
close(user.MessageChannel) // 避免 goroutine 泄露
case msg := <-messageChannel:
for user := range users {
if user.ID != msg.OwnerID {
user.MessageChannel <- msg.Content
}
}
}
}
}
func GenUserID() string {
// 生成全局唯一的 ID
id := xid.New()
return id.String()
}