golang websocket 服务端的实现

下面是关于"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代表消息类型,可以是TextMessageBinaryMessagep代表消息内容。

示例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技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • javascript设计模式Constructor(构造器)模式

    JavaScript设计模式Constructor(构造器)模式 什么是构造器模式? 构造器模式是一种用于创建特定类型对象的模式,它与工厂模式的不同之处在于,它更倾向于创建复杂的对象,如自定义类的实例等。 构造器模式的特点 构造器模式主要用于实例化对象,在构造器函数中定义属性和方法,并且通过this来引用这些属性和方法,最终返回一个实例化的对象。构造器模式有…

    other 2023年6月26日
    00
  • react router零基础使用教程

    React Router 零基础使用教程 React Router 是一个用于构建单页应用的库,它可以帮助我们在 React 应用中实现路由功能。本教程将详细介绍 React Router 的基本用法,包括路由的配置、导航、参数传递等。 安装 React Router 首先,我们需要在项目中安装 React Router。可以使用 npm 或者 yarn 进…

    other 2023年7月28日
    00
  • c语言基于stdarg.h的可变参数函数的用法

    C语言基于stdarg.h的可变参数函数的用法 在C语言中,我们可以使用可变参数函数来传递数量不确定的参数。这种函数通常用于需要处理不同数量参数的情况,例如输出不同个数的数字或字符串等。在实现可变参数函数时,需要使用头文件stdarg.h,并调用其中的函数和宏来实现参数的获取和处理。 可变参数函数的定义 以下是可变参数函数的基本模板: #include &l…

    other 2023年6月26日
    00
  • uniapp基础知识点掌握以及面试题整理

    uniapp基础知识点掌握以及面试题整理 1. uniapp基础知识点掌握 1.1 什么是uniapp? uniapp是一个使用Vue.js开发跨平台应用的前端框架,可以一次编写,多端发布,支持H5、小程序、APP等多种平台。uniapp开发与Vue.js开发类似,采用MVVM模式,通过数据绑定实现视图的响应式渲染。 1.2 uniapp的项目结构和文件组织…

    other 2023年6月27日
    00
  • Redis如何实现数据库读写分离详解

    以下是关于Redis如何实现数据库读写分离的完整攻略,包含两个示例说明: 1. 配置主从复制 在Redis的配置文件中,设置主服务器和从服务器的相关配置。 示例说明: # 主服务器配置 bind 127.0.0.1 port 6379 # 从服务器配置 slaveof 127.0.0.1 6379 2. 使用读写分离代理 使用读写分离代理工具,如Twempr…

    other 2023年10月19日
    00
  • PHP使用递归方式列出当前目录下所有文件的方法

    让我来详细讲解PHP使用递归方式列出当前目录下所有文件的方法。 1. 确定目录 首先,我们需要确定要列出文件的目录。可以使用php中的getcwd()函数来获取当前执行脚本的目录,或者使用chdir()函数切换到指定的目录。 2. 递归函数 接下来,我们需要编写递归函数来遍历目录下的所有文件和子目录。递归函数的基本框架如下所示: function recur…

    other 2023年6月27日
    00
  • finaldraft(专业剧本编辑器)

    以下是关于“Final Draft(专业剧本编辑器)”的完整攻略,过程中包含两个示例。 背景 Final Draft是一款专业的剧本编辑器,广泛用于电影、电视、戏剧等领域。提了许多功能,如自动格式化、场景管理、角色管理、剧本分析等。本攻略将介绍如何使用Final Draft进行本创作。 基本理 使用Final Draft进行剧本创作,我们需要完成以下步骤: …

    other 2023年5月9日
    00
  • Go语言执行系统命令行命令的方法

    要在Go语言中执行系统命令行命令,可以使用os/exec包提供的函数。以下是Go语言执行系统命令行命令的步骤: 引入os/exec包。 import "os/exec" 创建一个*exec.Cmd对象,利用它来执行命令。 cmd := exec.Command("command", "arg1", …

    other 2023年6月26日
    00
合作推广
合作推广
分享本页
返回顶部