forked from owenliang/go-websocket
-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.go
executable file
·165 lines (148 loc) · 3.34 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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package main
import (
"net/http"
"github.com/gorilla/websocket"
"errors"
"fmt"
"sync"
"time"
)
///////////////////////////////////////////////////////
// 大家有代码和工作上的疑难问题,可以加老师的微信交流:120848369
///////////////////////////////////////////////////////
// http升级websocket协议的配置
var wsUpgrader = websocket.Upgrader{
// 允许所有CORS跨域请求
CheckOrigin: func(r *http.Request) bool {
return true
},
}
// 客户端读写消息
type wsMessage struct {
messageType int
data []byte
}
// 客户端连接
type wsConnection struct {
wsSocket *websocket.Conn // 底层websocket
inChan chan *wsMessage // 读队列
outChan chan *wsMessage // 写队列
mutex sync.Mutex // 避免重复关闭管道
isClosed bool
closeChan chan byte // 关闭通知
}
func (wsConn *wsConnection)wsReadLoop() {
for {
// 读一个message
msgType, data, err := wsConn.wsSocket.ReadMessage()
if err != nil {
goto error
}
req := &wsMessage{
msgType,
data,
}
// 放入请求队列
select {
case wsConn.inChan <- req:
case <- wsConn.closeChan:
goto closed
}
}
error:
wsConn.wsClose()
closed:
}
func (wsConn *wsConnection)wsWriteLoop() {
for {
select {
// 取一个应答
case msg := <- wsConn.outChan:
// 写给websocket
if err := wsConn.wsSocket.WriteMessage(msg.messageType, msg.data); err != nil {
goto error
}
case <- wsConn.closeChan:
goto closed
}
}
error:
wsConn.wsClose()
closed:
}
func (wsConn *wsConnection)procLoop() {
// 启动一个gouroutine发送心跳
go func() {
for {
time.Sleep(2 * time.Second)
if err := wsConn.wsWrite(websocket.TextMessage, []byte("heartbeat from server")); err != nil {
fmt.Println("heartbeat fail")
wsConn.wsClose()
break
}
}
}()
// 这是一个同步处理模型(只是一个例子),如果希望并行处理可以每个请求一个gorutine,注意控制并发goroutine的数量!!!
for {
msg, err := wsConn.wsRead()
if err != nil {
fmt.Println("read fail")
break
}
fmt.Println(string(msg.data))
err = wsConn.wsWrite(msg.messageType, msg.data)
if err != nil {
fmt.Println("write fail")
break
}
}
}
func wsHandler(resp http.ResponseWriter, req *http.Request) {
// 应答客户端告知升级连接为websocket
wsSocket, err := wsUpgrader.Upgrade(resp, req, nil)
if err != nil {
return
}
wsConn := &wsConnection{
wsSocket: wsSocket,
inChan: make(chan *wsMessage, 1000),
outChan: make(chan *wsMessage, 1000),
closeChan: make(chan byte),
isClosed: false,
}
// 处理器
go wsConn.procLoop()
// 读协程
go wsConn.wsReadLoop()
// 写协程
go wsConn.wsWriteLoop()
}
func (wsConn *wsConnection)wsWrite(messageType int, data []byte) error {
select {
case wsConn.outChan <- &wsMessage{messageType, data,}:
case <- wsConn.closeChan:
return errors.New("websocket closed")
}
return nil
}
func (wsConn *wsConnection)wsRead() (*wsMessage, error) {
select {
case msg := <- wsConn.inChan:
return msg, nil
case <- wsConn.closeChan:
}
return nil, errors.New("websocket closed")
}
func (wsConn *wsConnection)wsClose() {
wsConn.wsSocket.Close()
wsConn.mutex.Lock()
defer wsConn.mutex.Unlock()
if !wsConn.isClosed {
wsConn.isClosed = true
close(wsConn.closeChan)
}
}
func main() {
http.HandleFunc("/ws", wsHandler)
http.ListenAndServe("0.0.0.0:7777", nil)
}