新世界的大门——websocket
  Ohl6n170bzPf 2023年11月02日 69 0

大家好,我是晴天,最近一周在工作中总是遇到一些小问题,所以我就想把这些小问题给记录下来。分享给大家一些小的知识点,与大家共同学习、共同进步。 今天带来的知识点是websocket

websocket是什么

WebSocket是一种计算机通信协议,它提供了在单个TCP连接上进行全双工通信的能力。它允许客户端和服务器之间进行实时数据交换,可以用于实现在线游戏、聊天室、股票市场等需要实时通信的应用程序。WebSocket协议是HTML5规范的一部分,支持大部分现代浏览器。


如何使用golang建立长连接

使用Golang建立长连接可以使用标准库中的netwebsocket包。

以下是一个基本的示例,它创建一个WebSocket服务器并接受客户端连接:

package main

import (
    "log"
    "net/http"

    "github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
}

func echo(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Println(err)
        return
    }
    defer conn.Close()

    for {
        // 读取客户端发来的消息
        _, message, err := conn.ReadMessage()
        if err != nil {
            log.Println(err)
            break
        }
        // 将消息原样返回给客户端
        err = conn.WriteMessage(websocket.TextMessage, message)
        if err != nil {
            log.Println(err)
            break
        }
    }
}

func main() {
    http.HandleFunc("/echo", echo)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

在此示例中,我使用Upgrader结构体将HTTP请求升级为WebSocket连接。然后,我们在一个无限循环中读取客户端发送的消息,并将消息原样发送回客户端。


客户端如何跟服务器建立长连接

在Golang中,客户端可以使用gorilla/websocket包来与WebSocket服务器建立长连接。以下是一个示例代码:

package main

import (
    "log"
    "net/url"
    "os"
    "os/signal"
    "time"

    "github.com/gorilla/websocket"
)

func main() {
    interrupt := make(chan os.Signal, 1)
    signal.Notify(interrupt, os.Interrupt)

    u := url.URL{Scheme: "ws", Host: "localhost:8080", Path: "/echo"}
    log.Printf("connecting to %s", u.String())

    c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
    if err != nil {
        log.Fatal("dial:", err)
    }
    defer c.Close()

    done := make(chan struct{})

    // 接收来自服务器的消息
    go func() {
        defer close(done)
        for {
            _, message, err := c.ReadMessage()
            if err != nil {
                log.Println("read:", err)
                return
            }
            log.Printf("recv: %s", message)
        }
    }()

    // 发送消息给服务器
    ticker := time.NewTicker(time.Second)
    defer ticker.Stop()

    for {
        select {
        case <-done:
            return
        case <-ticker.C:
            err := c.WriteMessage(websocket.TextMessage, []byte("hello"))
            if err != nil {
                log.Println("write:", err)
                return
            }
        case <-interrupt:
            log.Println("interrupt")
            // 优雅关闭连接
            err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
            if err != nil {
                log.Println("write close:", err)
                return
            }
            select {
            case <-done:
            case <-time.After(time.Second):
            }
            return
        }
    }
}

在此示例中,我们使用websocket.DefaultDialer拨号WebSocket服务器,并使用ReadMessageWriteMessage方法发送和接收消息。使用interrupt通道进行优雅关闭。

要注意的是,此示例中的路径/echo是服务器端实现的WebSocket处理程序的路径。


package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
}

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        conn, err := upgrader.Upgrade(w, r, nil)
        if err != nil {
            log.Println("upgrade:", err)
            return
        }
        defer conn.Close()

        for {
            _, message, err := conn.ReadMessage()
            if err != nil {
                log.Println("read:", err)
                break
            }
            log.Printf("recv: %s", message)
            err = conn.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("echo: %s", message)))
            if err != nil {
                log.Println("write:", err)
                break
            }
        }
    })

    log.Fatal(http.ListenAndServe(":8080", nil))
}

在客户端代码中,需要使用http.Header类型来设置请求头。以下是一个示例代码:

package main

import (
    "log"
    "net/http"

    "github.com/gorilla/websocket"
)

func main() {
    url := "ws://localhost:8080/"

    header := http.Header{}
    header.Add("Connection", "upgrade")
    header.Add("Upgrade", "websocket")

    conn, _, err := websocket.DefaultDialer.Dial(url, header)
    if err != nil {
        log.Fatal("dial:", err)
    }
    defer conn.Close()

    err = conn.WriteMessage(websocket.TextMessage, []byte("hello"))
    if err != nil {
        log.Println("write:", err)
        return
    }

    _, message, err := conn.ReadMessage()
    if err != nil {
        log.Println("read:", err)
        return
    }
    log.Printf("recv: %s", message)
}

在此示例中,我们使用http.Header类型设置了UpgradeConnection头字段,以使客户端请求被正确升级为WebSocket连接。


在Golang中,可以使用*websocket.Conn类型的ReadMessage()WriteMessage()方法来发送和接收WebSocket消息。这些方法将返回error类型的值,如果在发送或接收过程中出现错误,则返回非nil的错误。

如果需要验证WebSocket连接是否已经建立,可以使用WriteMessage()方法向服务器发送一个握手消息,并在发送之后读取服务器的响应消息。以下是一个示例代码:

package main

import (
    "log"

    "github.com/gorilla/websocket"
)

func main() {
    url := "ws://localhost:8080/"

    conn, _, err := websocket.DefaultDialer.Dial(url, nil)
    if err != nil {
        log.Fatal("dial:", err)
    }
    defer conn.Close()

    // 发送握手消息并等待响应
    if err := conn.WriteMessage(websocket.TextMessage, []byte("hello")); err != nil {
        log.Fatal("write:", err)
    }

    _, message, err := conn.ReadMessage()
    if err != nil {
        log.Fatal("read:", err)
    }

    // 验证WebSocket连接已经建立
    if string(message) == "connected" {
        log.Println("WebSocket连接已经建立")
    } else {
        log.Println("WebSocket连接未能建立")
    }
}

在此示例中,我使用WriteMessage()方法向服务器发送一个包含"hello"消息体的WebSocket消息,并使用ReadMessage()方法等待服务器的响应。如果服务器返回的消息是"connected",则表明WebSocket连接已经建立。


在线测试工具

在本地启动自己的服务,填写对应的ip+端口号+路由地址。

由上图可见,ws链接已经建立,可以使用客户端,向服务器发送消息了。


写在最后

感谢大家的阅读,晴天将继续努力,分享更多有趣且实用的主题,如有错误和纰漏,欢迎给予指正。 更多文章敬请关注作者个人公众号 「晴天码字」

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

Ohl6n170bzPf