├── .idea
└── vcs.xml
├── README.md
├── client.html
└── server.go
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # go-websocket
2 |
3 | 演示如何封装websocket库,实现一个easy&safe的编程接口。
4 |
--------------------------------------------------------------------------------
/client.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
52 |
53 |
54 |
55 |
56 | Click "Open" to create a connection to the server,
57 | "Send" to send a message to the server and "Close" to close the connection.
58 | You can change the message and send multiple times.
59 |
60 |
66 | |
67 |
68 | |
69 |
70 |
--------------------------------------------------------------------------------
/server.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "net/http"
5 | "github.com/gorilla/websocket"
6 | "errors"
7 | "fmt"
8 | "sync"
9 | "time"
10 | )
11 |
12 | ///////////////////////////////////////////////////////
13 | // 大家有代码和工作上的疑难问题,可以加老师的微信交流:120848369
14 | ///////////////////////////////////////////////////////
15 |
16 | // http升级websocket协议的配置
17 | var wsUpgrader = websocket.Upgrader{
18 | // 允许所有CORS跨域请求
19 | CheckOrigin: func(r *http.Request) bool {
20 | return true
21 | },
22 | }
23 |
24 | // 客户端读写消息
25 | type wsMessage struct {
26 | messageType int
27 | data []byte
28 | }
29 |
30 | // 客户端连接
31 | type wsConnection struct {
32 | wsSocket *websocket.Conn // 底层websocket
33 | inChan chan *wsMessage // 读队列
34 | outChan chan *wsMessage // 写队列
35 |
36 | mutex sync.Mutex // 避免重复关闭管道
37 | isClosed bool
38 | closeChan chan byte // 关闭通知
39 | }
40 |
41 | func (wsConn *wsConnection)wsReadLoop() {
42 | for {
43 | // 读一个message
44 | msgType, data, err := wsConn.wsSocket.ReadMessage()
45 | if err != nil {
46 | goto error
47 | }
48 | req := &wsMessage{
49 | msgType,
50 | data,
51 | }
52 | // 放入请求队列
53 | select {
54 | case wsConn.inChan <- req:
55 | case <- wsConn.closeChan:
56 | goto closed
57 | }
58 | }
59 | error:
60 | wsConn.wsClose()
61 | closed:
62 | }
63 |
64 | func (wsConn *wsConnection)wsWriteLoop() {
65 | for {
66 | select {
67 | // 取一个应答
68 | case msg := <- wsConn.outChan:
69 | // 写给websocket
70 | if err := wsConn.wsSocket.WriteMessage(msg.messageType, msg.data); err != nil {
71 | goto error
72 | }
73 | case <- wsConn.closeChan:
74 | goto closed
75 | }
76 | }
77 | error:
78 | wsConn.wsClose()
79 | closed:
80 | }
81 |
82 | func (wsConn *wsConnection)procLoop() {
83 | // 启动一个gouroutine发送心跳
84 | go func() {
85 | for {
86 | time.Sleep(2 * time.Second)
87 | if err := wsConn.wsWrite(websocket.TextMessage, []byte("heartbeat from server")); err != nil {
88 | fmt.Println("heartbeat fail")
89 | wsConn.wsClose()
90 | break
91 | }
92 | }
93 | }()
94 |
95 | // 这是一个同步处理模型(只是一个例子),如果希望并行处理可以每个请求一个gorutine,注意控制并发goroutine的数量!!!
96 | for {
97 | msg, err := wsConn.wsRead()
98 | if err != nil {
99 | fmt.Println("read fail")
100 | break
101 | }
102 | fmt.Println(string(msg.data))
103 | err = wsConn.wsWrite(msg.messageType, msg.data)
104 | if err != nil {
105 | fmt.Println("write fail")
106 | break
107 | }
108 | }
109 | }
110 |
111 | func wsHandler(resp http.ResponseWriter, req *http.Request) {
112 | // 应答客户端告知升级连接为websocket
113 | wsSocket, err := wsUpgrader.Upgrade(resp, req, nil)
114 | if err != nil {
115 | return
116 | }
117 | wsConn := &wsConnection{
118 | wsSocket: wsSocket,
119 | inChan: make(chan *wsMessage, 1000),
120 | outChan: make(chan *wsMessage, 1000),
121 | closeChan: make(chan byte),
122 | isClosed: false,
123 | }
124 |
125 | // 处理器
126 | go wsConn.procLoop()
127 | // 读协程
128 | go wsConn.wsReadLoop()
129 | // 写协程
130 | go wsConn.wsWriteLoop()
131 | }
132 |
133 | func (wsConn *wsConnection)wsWrite(messageType int, data []byte) error {
134 | select {
135 | case wsConn.outChan <- &wsMessage{messageType, data,}:
136 | case <- wsConn.closeChan:
137 | return errors.New("websocket closed")
138 | }
139 | return nil
140 | }
141 |
142 | func (wsConn *wsConnection)wsRead() (*wsMessage, error) {
143 | select {
144 | case msg := <- wsConn.inChan:
145 | return msg, nil
146 | case <- wsConn.closeChan:
147 | }
148 | return nil, errors.New("websocket closed")
149 | }
150 |
151 | func (wsConn *wsConnection)wsClose() {
152 | wsConn.wsSocket.Close()
153 |
154 | wsConn.mutex.Lock()
155 | defer wsConn.mutex.Unlock()
156 | if !wsConn.isClosed {
157 | wsConn.isClosed = true
158 | close(wsConn.closeChan)
159 | }
160 | }
161 |
162 | func main() {
163 | http.HandleFunc("/ws", wsHandler)
164 | http.ListenAndServe("0.0.0.0:7777", nil)
165 | }
166 |
--------------------------------------------------------------------------------