├── .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 |
61 | 62 | 63 | 64 | 65 |
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 | --------------------------------------------------------------------------------