简谈gorillWebsocket框架
什么是WebSocket?
WebSocket是一种在单个TCP连接上进行全双工通信的协议。
WebSocket允许服务器主动向客户端推送数据,而无需客户端主动请求,这使得客户端和服务器之间的数据交换变得更加简单。在WebSocket API中,浏览器和服务器只需完成一次握手,即可创建持久性的连接,并在此连接上进行双向数据传输。
为啥要有WebSocket?
- 实时性需求:传统的 HTTP 协议是基于请求-响应模式的,即客户端发起请求,服务器返回响应后连接就关闭了。这种模式在需要实时通信的场景下显得力不从心,因为服务器无法主动向客户端推送数据,而需要客户端不断地轮询服务器以获取最新数据。这种轮询方式不仅效率低下,还可能造成不必要的资源浪费。WebSocket 则通过在客户端和服务器之间建立持久连接,使得双方可以实时地发送和接收数据,满足了实时通信的需求。
- 双向通信:HTTP 协议主要是单向的,即客户端向服务器发送请求并等待响应。虽然 HTTP/2 引入了服务器推送(Server Push)功能,但其实现方式和应用场景仍然有限。而 WebSocket 提供了真正的双向通信能力,客户端和服务器都可以随时向对方发送数据,这使得一些复杂的实时交互应用得以实现。
- 减少网络开销:由于 HTTP 协议每次请求都需要建立新的连接,这在网络环境不佳或请求频繁的情况下会导致大量的网络开销。而 WebSocket 通过建立持久连接,避免了频繁建立和关闭连接的开销,从而提高了网络传输的效率。
- 更好的用户体验:实时通信和双向通信的能力使得基于 WebSocket 的应用能够提供更流畅、更自然的用户体验。例如,在实时聊天、在线游戏、股票交易等应用中,用户可以实时地获取最新信息并与其他用户进行交互,这大大提高了应用的可用性和吸引力。
如何使用gorill websocket框架构建websocket连接?
1. 安装Gorilla Websocket
首先,确保你已经安装了Go语言环境,然后使用以下命令安装Gorilla Websocket库:
go get -u github.com/gorilla/websocket
2. 创建Websocket服务器
接下来,我们创建一个简单的Websocket服务器。这个服务器将监听连接,并在接收到消息时回显该消息。
package main
import (
"fmt"
"log"
"net/http"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
func echo(w http.ResponseWriter, r *http.Request) {
// 升级HTTP连接为Websocket连接
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println("Error upgrading to websocket:", err)
return
}
defer conn.Close()
for {
// 读取消息
messageType, message, err := conn.ReadMessage()
if err != nil {
log.Println("Error reading message:", err)
break
}
// 打印接收到的消息
fmt.Printf("Received: %s\n", string(message))
// 回显消息
err = conn.WriteMessage(messageType, message)
if err != nil {
log.Println("Error writing message:", err)
break
}
}
}
func main() {
http.HandleFunc("/echo", echo)
log.Println("Server is running on :8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
在上面的代码中,我们定义了一个echo
函数来处理Websocket连接。我们使用upgrader.Upgrade
方法将HTTP连接升级为Websocket连接。然后,我们进入一个循环,在这个循环中,我们读取从客户端发送过来的消息,并回显该消息。
3. 创建Websocket客户端
现在,让我们创建一个简单的Websocket客户端来测试服务器。
package main
import (
"fmt"
"log"
"net/url"
"time"
"github.com/gorilla/websocket"
)
func main() {
interrupt := make(chan struct{})
// 连接到Websocket服务器
u := url.URL{Scheme: "ws", Host: "localhost:8080", Path: "/echo"}
log.Printf("Connecting to %s", u.String())
conn, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil {
log.Fatal("Dial:", err)
}
defer conn.Close()
done := make(chan struct{})
// 启动一个goroutine来读取服务器的响应
go func() {
defer close(done)
for {
_, message, err := conn.ReadMessage()
if err != nil {
log.Println("ReadMessage:", err)
return
}
log.Printf("Received: %s", message)
}
}()
// 发送消息到服务器
ticker := time.NewTicker(4 * time.Second)
defer ticker.Stop()
for {
select {
case <-done:
return
case t := <-ticker.C:
err := conn.WriteMessage(websocket.TextMessage, []byte(t.String()))
if err != nil {
log.Println("WriteMessage:", err)
return
}
case <-interrupt:
// 清理并退出
log.Println("Interrupt signal received.")
err := conn.WriteControl(websocket.CloseMessage, websocket.FormatCloseMessage(-1, "Interrupt signal received"), time.Now().Add(10*time.Second))
if err != nil {
log.Println("Error sending close control:", err)
}
return
}}}
在这个客户端代码中,我们首先使用websocket.DefaultDialer.Dial
方法连接到Websocket服务器。然后,我们启动一个goroutine来读取服务器发送的消息,并打印出来。在主goroutine中,我们使用一个ticker来定期发送当前时间戳作为消息到服务器。此外,我们还监听一个interrupt
通道,以便在接收到中断信号时能够优雅地关闭连接。
4. 运行服务器和客户端
- 首先,启动服务器。运行服务器代码,它将在本地8080端口监听连接。
- 然后,启动客户端。运行客户端代码,它将连接到服务器并开始发送消息。
5. 测试
现在,我们可以看到客户端每隔4秒向服务器发送一个时间戳,而服务器则立即回显这个消息。可以在客户端的控制台看到服务器返回的消息。 server:
2024/04/23 23:01:32 Server is running on :8080 Received: 2024-04-23 23:07:51.8616846 +0800 CST m=+4.024171101 Received: 2024-04-23 23:07:55.8560602 +0800 CST m=+8.018546701 Received: 2024-04-23 23:07:59.8604412 +0800 CST m=+12.022927701 Received: 2024-04-23 23:08:03.8546702 +0800 CST m=+16.017156701
client:
2024/04/23 23:07:47 Connecting to ws://localhost:8080/echo 2024/04/23 23:07:51 Received: 2024-04-23 23:07:51.8616846 +0800 CST m=+4.024171101 2024/04/23 23:07:55 Received: 2024-04-23 23:07:55.8560602 +0800 CST m=+8.018546701 2024/04/23 23:07:59 Received: 2024-04-23 23:07:59.8604412 +0800 CST m=+12.022927701
gorilla websocket的常用函数
Upgrader
Upgrader
是用于将普通的 HTTP 连接升级为 WebSocket 连接的结构体。
- Upgrade: 这个方法用于将 HTTP 请求和响应升级为 WebSocket 连接。它返回一个
*Conn
和可能出现的错误。
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
// 其他可选配置...
}
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
// 处理错误
}
Conn
Conn
表示一个 WebSocket 连接,提供了读写消息以及关闭连接的方法。
- ReadMessage: 用于从 WebSocket 连接中读取消息。它返回一个消息类型(二进制或文本)和消息内容,以及可能出现的错误。
messageType, message, err := conn.ReadMessage()
if err != nil {
// 处理错误
}
- WriteMessage: 用于向 WebSocket 连接写入消息。它接受一个消息类型(二进制或文本)和一个消息内容。
err := conn.WriteMessage(websocket.TextMessage, []byte("Hello, WebSocket hh!"))
if err != nil {
// 处理错误
}
- Close: 用于关闭 WebSocket 连接。
err = conn.Close()
if err != nil {
// 处理错误
}
- NextReader 和 NextWriter: 这两个方法用于更底层的读写操作,它们分别返回
io.Reader
和io.WriteCloser
接口,允许你以流的方式读写数据。
// 读取消息
reader, messageType, err := conn.NextReader()
if err != nil {
// 处理错误
}
// 使用 reader 读取数据
// 写入消息
writer, err := conn.NextWriter(websocket.BinaryMessage)
if err != nil {
// 处理错误
}
// 使用 writer 写入数据
writer.Close() // 写入完成后需要关闭 writer
Dialer
Dialer
是用于从客户端建立 WebSocket 连接的结构体。
- Dial: 用于建立 WebSocket 连接。它接受一个 WebSocket URL 和可选的请求头,返回一个
*Conn
和可能出现的错误。
dialer := websocket.Dialer{}
conn, _, err := dialer.Dial("ws://example.com/socketserver", nil)
if err != nil {
// 处理错误
}
Ping/Pong
WebSocket 协议支持 ping 和 pong 消息类型,用于检测连接是否仍然活跃。
- WriteControl: 用于发送 ping 或 pong 消息。
err = conn.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(time.Second))
if err != nil {
// 处理错误
}
- SetReadDeadline 和 SetWriteDeadline: 这些方法用于设置读写操作的超时时间。
注意点
socket与websocket的区别?
从原理上来看,Socket是传输控制层协议,它是应用层与TCP/IP协议族通信的中间软件抽象层,是一组接口。它位于应用层和传输控制层之间,隐藏了复杂的TCP/IP协议族,使得应用程序能够方便地使用TCP或UDP进行通信。而WebSocket则是应用层协议,建立在HTTP协议之上,通过HTTP的握手过程实现通信。
从灵活运用的程度上看,WebSocket更易用,而Socket更灵活。WebSocket中一旦浏览器和服务器完成握手,两者之间就可以创建持久性的连接,并进行双向数据传输。而Socket编程接口允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。
小结
写这个是为了自己用到可以快速找到用法,写的不好/错误望大佬可以帮忙指正
如果这篇文章帮到你,我很荣幸
转载自:https://juejin.cn/post/7360947498943217691