下面是关于"golang websocket 服务端的实现"的攻略。
准备工作
首先,我们需要在Go
中引入websocket包,可以通过如下方式:
import "github.com/gorilla/websocket"
同时,我们还需要处理websocket的请求,这样才能确保服务端收到请求并进行处理,可以使用http.HandleFunc
方法处理:
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// 方法实现
})
建立连接
接下来,我们需要建立websocket连接,这可以通过websocket包中的Upgrade
方法实现:
conn, err := upgrader.Upgrade(w, r, nil)
其中upgrader
是一个配置结构体,可以设置一些参数,比如允许跨域:
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool { return true },
}
读写数据
在完成websocket连接后,我们可以开始进行数据的读写操作。首先,我们需要定义一个循环来不断读取客户端发送的消息,可以用以下代码实现:
for {
// 读取客户端发送的消息
messageType, p, err := conn.ReadMessage()
if err != nil {
log.Println(err)
return
}
// 将消息发送给所有客户端
err = conn.WriteMessage(messageType, p)
if err != nil {
log.Println(err)
return
}
}
在代码中,我们使用ReadMessage()
方法读取客户端发送的消息,然后再使用WriteMessage()
方法将消息发送给所有客户端。其中,messageType
代表消息类型,可以是TextMessage
或BinaryMessage
,p
代表消息内容。
示例1:广播聊天室
以下是一个简单的基于Golang实现的websocket广播聊天室的示例:
package main
import (
"fmt"
"log"
"net/http"
"github.com/gorilla/websocket"
)
var clients = make(map[*websocket.Conn]bool) // 连接客户端
var broadcast = make(chan Message) // 广播消息
// 配置WebSocket参数
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
// 定义Message结构体
type Message struct {
Email string `json:"email"`
Username string `json:"username"`
Message string `json:"message"`
}
func main() {
// 处理静态资源请求
http.Handle("/", http.FileServer(http.Dir("./public")))
// 处理WebSocket请求
http.HandleFunc("/ws", handleConnections)
// 启动goroutine监听广播消息
go handleMessages()
// 启动服务器
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
func handleConnections(w http.ResponseWriter, r *http.Request) {
// 将HTTP连接升级为WebSocket连接
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Fatal(err)
}
// 关闭WebSocket连接
defer ws.Close()
// 将新连接添加到连接列表中
clients[ws] = true
for {
// 读取WebSocket数据
var msg Message
err := ws.ReadJSON(&msg)
if err != nil {
log.Printf("error: %v", err)
delete(clients, ws)
break
}
// 将接收到的消息发送给广播通道
broadcast <- msg
}
}
func handleMessages() {
for {
// 从广播通道中获取消息
msg := <-broadcast
// 将消息发送给所有连接
for client := range clients {
err := client.WriteJSON(msg)
if err != nil {
log.Printf("error: %v", err)
client.Close()
delete(clients, client)
}
}
}
}
这个示例中,我们使用map
类型来保存所有连接的客户端,并在处理WebSocket连接时添加连接。另外,我们使用一个无缓冲通道来进行广播。
在handleConnections
函数中,我们使用ws.ReadJSON(&msg)
方法读取客户端发送的消息,然后将消息发送给广播通道。
在handleMessages
函数中,我们不断从广播通道中获取消息,并使用client.WriteJSON()
方法将消息发送给所有连接的客户端。
示例2:广播计数器
以下是一个简单的基于Golang实现的WebSocket计数器示例:
package main
import (
"errors"
"fmt"
"net/http"
"time"
"github.com/gorilla/websocket"
)
// 使用websocket库升级连接
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
return true
},
}
// 定义连接对象
type wsConnection struct {
wsSocket *websocket.Conn // 底层websocket
inChan chan []byte // 发送队列
outChan chan []byte // 接收队列
closeChan chan byte // 关闭通道
isClosed bool // 连接是否已关闭
mutex sync.Mutex // 避免重入锁
}
// 创建连接
func wsHandler(w http.ResponseWriter, r *http.Request) {
var (
wsConn *websocket.Conn
err error
conn *wsConnection
)
// 应答客户端告知websocket连接建立
if wsConn, err = upgrader.Upgrade(w, r, w.Header()); err != nil {
return
}
conn = &wsConnection{
wsSocket: wsConn,
inChan: make(chan []byte, 1000),
outChan: make(chan []byte, 1000),
closeChan: make(chan byte),
isClosed: false,
}
// 启动处理协程
go conn.readLoop()
go conn.writeLoop()
return
}
// 处理数据读逻辑
func (wsConn *wsConnection) readLoop() {
var (
data []byte
err error
)
for {
//websocket.TextMessage
if _, data, err = wsConn.wsSocket.ReadMessage(); err != nil {
goto ERR
}
select {
case wsConn.inChan <- data:
case <-wsConn.closeChan:
goto CLOSED
}
}
ERR:
wsConn.Close()
CLOSED:
}
// 处理数据写逻辑
func (wsConn *wsConnection) writeLoop() {
var (
data []byte
err error
)
for {
select {
case data = <-wsConn.outChan:
case <-wsConn.closeChan:
goto CLOSED
}
// 从channel中读取数据
if err = wsConn.wsSocket.WriteMessage(websocket.TextMessage, data); err != nil {
goto ERR
}
}
ERR:
wsConn.Close()
CLOSED:
}
// 发送文本消息
func (wsConn *wsConnection) wsWriteTextMessage(message []byte) (err error) {
select {
case wsConn.outChan <- message:
default:
wsConn.wsSocket.Close()
err = errors.New("websocket closed")
}
return
}
// 关闭连接
func (wsConn *wsConnection) Close() {
wsConn.wsSocket.Close()
wsConn.mutex.Lock()
if !wsConn.isClosed{
close(wsConn.closeChan)
wsConn.isClosed = true
}
wsConn.mutex.Unlock()
}
func main() {
// 注册路由和处理函数
http.HandleFunc("/ws", wsHandler)
// 监听端口
http.ListenAndServe(":9090", nil)
}
在这个示例中,我们定义了一个wsConnection
结构体表示连接对象,同时维护了发送队列inChan
,接收队列outChan
和关闭通道closeChan
。
在wsHandler
函数中,我们使用upgrader.Upgrade()
方法升级连接,然后创建一个wsConnection
连接对象,并启动一个读协程和一个写协程。
在readLoop
函数中,我们不断循环获取客户端发送的数据,并使用inChan
通道将数据放入发送队列。
在writeLoop
函数中,我们不断循环从接收队列中获取数据,并使用wsConn.wsSocket.WriteMessage()
方法将数据发送给客户端。
在wsWriteTextMessage
函数中,我们使用一条select
语句将数据放入发送队列,如果队列已满则关闭连接。
在Close
函数中,我们使用Mutex
避免重入锁,防止closeChan关闭多次,同时将isClosed标志设为true,表示连接已关闭。
这就是一个基本的WebSocket计数器示例。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:golang websocket 服务端的实现 - Python技术站