掘金 后端 ( ) • 2024-04-24 10:15

什么是WebSocket?

WebSocket是一种在单个TCP连接上进行全双工通信的协议。

WebSocket允许服务器主动向客户端推送数据,而无需客户端主动请求,这使得客户端和服务器之间的数据交换变得更加简单。在WebSocket API中,浏览器和服务器只需完成一次握手,即可创建持久性的连接,并在此连接上进行双向数据传输。

为啥要有WebSocket?

  1. 实时性需求:传统的 HTTP 协议是基于请求-响应模式的,即客户端发起请求,服务器返回响应后连接就关闭了。这种模式在需要实时通信的场景下显得力不从心,因为服务器无法主动向客户端推送数据,而需要客户端不断地轮询服务器以获取最新数据。这种轮询方式不仅效率低下,还可能造成不必要的资源浪费。WebSocket 则通过在客户端和服务器之间建立持久连接,使得双方可以实时地发送和接收数据,满足了实时通信的需求。
  2. 双向通信:HTTP 协议主要是单向的,即客户端向服务器发送请求并等待响应。虽然 HTTP/2 引入了服务器推送(Server Push)功能,但其实现方式和应用场景仍然有限。而 WebSocket 提供了真正的双向通信能力,客户端和服务器都可以随时向对方发送数据,这使得一些复杂的实时交互应用得以实现。
  3. 减少网络开销:由于 HTTP 协议每次请求都需要建立新的连接,这在网络环境不佳或请求频繁的情况下会导致大量的网络开销。而 WebSocket 通过建立持久连接,避免了频繁建立和关闭连接的开销,从而提高了网络传输的效率。
  4. 更好的用户体验:实时通信和双向通信的能力使得基于 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. 运行服务器和客户端

  1. 首先,启动服务器。运行服务器代码,它将在本地8080端口监听连接。
  2. 然后,启动客户端。运行客户端代码,它将连接到服务器并开始发送消息。

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 {
    // 处理错误
}
  • NextReaderNextWriter: 这两个方法用于更底层的读写操作,它们分别返回 io.Readerio.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 {
    // 处理错误
}
  • SetReadDeadlineSetWriteDeadline: 这些方法用于设置读写操作的超时时间。

注意点

socket与websocket的区别?

从原理上来看,Socket是传输控制层协议,它是应用层与TCP/IP协议族通信的中间软件抽象层,是一组接口。它位于应用层和传输控制层之间,隐藏了复杂的TCP/IP协议族,使得应用程序能够方便地使用TCP或UDP进行通信。而WebSocket则是应用层协议,建立在HTTP协议之上,通过HTTP的握手过程实现通信。

从灵活运用的程度上看,WebSocket更易用,而Socket更灵活。WebSocket中一旦浏览器和服务器完成握手,两者之间就可以创建持久性的连接,并进行双向数据传输。而Socket编程接口允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。

小结

写这个是为了自己用到可以快速找到用法,写的不好/错误望大佬可以帮忙指正

如果这篇文章帮到你,我很荣幸