Golang构建WebSocket服务器和客户端的示例详解

 更新时间:2023年11月03日 11:25:59   作者:吴佳浩  
这篇文章主要为大家详细介绍了如何使用Go语言构建WebSocket服务器和客户端,以实现双向通信,文中的示例代码讲解详细,需要的小伙伴可以参考一下

简介

本教程将教你如何使用Go语言构建WebSocket服务器和客户端,以实现双向 通信。我们将介绍如何创建一个WebSocket服务器,用于接收来自客户端的消息,以及如何创建一个WebSocket客户端,用于与服务器通信。我们还将介绍一个简单的HTML页面,演示如何在浏览器中使用WebSocket客户端与服务器进行通信。

操作

1.创建一个文件夹.

2.使用GO mod初始化go mod init your_module_name

3.创建文件

4.go get github.com/gorilla/websocket

WebSocket 服务器

首先,我们将创建一个WebSocket服务器,用于接收来自客户端的消息。我们使用github.com/gorilla/websocket包来处理WebSocket连接。

WsServer.go

// 导入必要的包
import (
    "fmt"
    "net"
    "net/http"
    "time"
    "github.com/gorilla/websocket"
)

// 创建WsServer结构体
type WsServer struct {
    listener net.Listener
    addr     string
    upgrade  *websocket.Upgrader
}

// 初始化WsServer
func NewWsServer() *WsServer {
    ws := new(WsServer)
    ws.addr = "0.0.0.0:10215"
    ws.upgrade = &websocket.Upgrader{
        ReadBufferSize:  4096,
        WriteBufferSize: 1024,
        CheckOrigin: func(r *http.Request) bool {
            if r.Method != "GET" {
                fmt.Println("method is not GET")
                return false
            }
            if r.URL.Path != "/ws" {
                fmt.Println("path error")
                return false
            }
            return true
        },
    }
    return ws
}

// 处理WebSocket连接
func (self *WsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path != "/ws" {
        httpCode := http.StatusInternalServerError
        reasePhrase := http.StatusText(httpCode)
        fmt.Println("path error ", reasePhrase)
        http.Error(w, reasePhrase, httpCode)
        return
    }
    conn, err := self.upgrade.Upgrade(w, r, nil)
    if err != nil {
        fmt.Println("websocket error:", err)
        return
    }
    fmt.Println("client connect:", conn.RemoteAddr())
    go self.connHandle(conn)
}

// 处理WebSocket连接中的消息
func (self *WsServer) connHandle(conn *websocket.Conn) {
    defer func() {
        conn.Close()
    }()
    stopCh := make(chan int)
    go self.send(conn, stopCh)
    for {
        conn.SetReadDeadline(time.Now().Add(time.Millisecond * time.Duration(5000)))
        _, msg, err := conn.ReadMessage()
        if err != nil {
            close(stopCh)
            if netErr, ok := err.(net.Error); ok {
                if netErr.Timeout() {
                    fmt.Printf("ReadMessage timeout remote: %v\n", conn.RemoteAddr())
                    return
                }
            }
            if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseNormalClosure) {
                fmt.Printf("ReadMessage other remote:%v error: %v \n", conn.RemoteAddr(), err)
            }
            return
        }
        fmt.Println("Received message:", string(msg))
    }
}

// 向客户端发送消息
func (self *WsServer) send(conn *websocket.Conn, stopCh chan int) {
    self.send10(conn)
    for {
        select {
        case <-stopCh:
            fmt.Println("connection closed")
            return
        case <-time.After(time.Second * 1):
            data := fmt.Sprintf("Hello WebSocket test from server %v", time.Now().UnixNano())
            err := conn.WriteMessage(1, []byte(data))
            fmt.Println("Sending....")
            if err != nil {
                fmt.Println("send message failed ", err)
                return
            }
        }
    }
}

// 测试一次性发送10万条数据给客户端
func (self *WsServer) send10(conn *websocket.Conn) {
    for i := 0; i < 100000; i++ {
        data := fmt.Sprintf("Hello WebSocket test from server %v", time.Now().UnixNano())
        err := conn.WriteMessage(1, []byte(data))
        if err != nil {
            fmt.Println("send message failed ", err)
            return
        }
    }
}

// 启动WebSocket服务器
func (w *WsServer) Start() (err error) {
    w.listener, err = net.Listen("tcp", w.addr)
    if err != nil {
        fmt.Println("net listen error:", err)
        return
    }
    err = http.Serve(w.listener, w)
    if err != nil {
        fmt.Println("http serve error:", err)
        return
    }
    return nil
}

func main() {
    ws := NewWsServer()
    ws.Start()
}

这是WebSocket服务器的代码。它创建一个WsServer结构体,用于处理WebSocket连接。服务器会不断接收来自客户端的消息,并发送一些测试消息。你可以根据需要进行修改和扩展。

WebSocket 客户端

接下来,我们将创建一个WebSocket客户端,用于与服务器进行通信。我们将使用自定义的WebSocket客户端实现。

WsClient.go

package main

import (
    "fmt"
    "net"
    "net/http"
    "time"
    "github.com/gorilla/websocket"
)

// 创建自定义WebSocket客户端
type VIL struct{}

type DefaultWebSocket struct {
    _host    string
    _isOpen  bool
    _bufQueue []interface{}
    _bufCap  int
    _call    interface{}
    _socket  *websocket.Conn
}

// 初始化自定义WebSocket客户端
func (self *VIL) DefaultWebSocket(host, call) {
    _host = host
    _isOpen = false
    _bufQueue = []interface{}
    _bufCap = 100
    if call != nil {
        _call = call
    } else {
        _call = {
            onConnect: func(e) {
                fmt.Println("connect success ", e)
            },
            onDisconnect: func(e) {
                fmt.Println("disconnect ", e)
            },
            onMsg: func(data) {
                // fmt.Println("receive message ", data)
            },
        }
    }

    _socket = new(websocket.Conn)
    _socket = websocket.DefaultDialer.Dial(_host, nil)
    _socket.binaryType = "arraybuffer"
}

// 设置发送消息缓存队列的容量
func (self *DefaultWebSocket) setBufferCap(cap) {
    if cap < 0 {
        fmt.Println("parameter value can not be less than 0")
        return
    }
    _bufCap = cap
}

// 发送消息
func (self *DefaultWebSocket) send(data) {


    if _isOpen && _socket != nil {
        _socket.send("")
    } else {
        if len(_bufQueue) < _bufCap {
            _bufQueue = append(_bufQueue, data)
        }
    }
}

// 关闭WebSocket连接
func (self *DefaultWebSocket) close() {
    _socket.Close(1000, "normal")
    _isOpen = false
}

// 处理WebSocket连接中的消息
func (self *DefaultWebSocket) handle() {
    go func() {
        for {
            _socket.SetReadDeadline(time.Now().Add(time.Millisecond * time.Duration(5000)))
            _, message, err := _socket.ReadMessage()
            if err != nil {
                fmt.Println("Error while reading message:", err)
                return
            }
            _call.onMsg(message)
        }
    }()
}

// 向客户端发送消息
func (self *DefaultWebSocket) sendToServer() {
    go func() {
        for {
            select {
            case <-stopCh:
                fmt.Println("Interrupted, closing connection...")
                err := _socket.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
                if err != nil {
                    fmt.Println("Error while sending close message:", err)
                    return
                }
                select {
                case <-done:
                case <-time.After(time.Second):
                }
                return
            case <-time.After(time.Second * 1):
                data := fmt.Sprintf("Hello WebSocket test from client %v", time.Now().UnixNano())
                err := _socket.WriteMessage(1, []byte(data))
                if err != nil {
                    fmt.Println("Error while sending message:", err)
                    return
                }
            }
        }
    }()
}

这是WebSocket客户端的代码。它创建了一个DefaultWebSocket结构体,用于处理WebSocket连接。客户端会不断发送消息给服务器,并处理来自服务器的消息。你可以根据需要进行修改和扩展。

HTML 页面

最后,我们创建一个简单的HTML页面,用于在浏览器中使用WebSocket客户端与服务器进行通信。以下是HTML代码:

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebSocket Example</title>
</head>
<body>
</body>
<script type="text/javascript" src="./index.js"></script>
<script>
    let counter = 0;
    let isConnect = false;
    let handler = {
        onConnect: function (e) {
            isConnect = true;
            console.log("handler connect success ", e);
            var se = setInterval(function () {
                if (isConnect === false) {
                    clearInterval(se);
                }
                console.log("setInterval", Date.now());
                socket.send("Web browser setInterval");
            }, 3000);
        },
        onDisconnect: function (e) {
            isConnect = false;
            console.log("handler disconnect ", e);
        },
        onMsg: function (data) {
            counter++;
            if (counter >= 2000) {
                counter = 0;
                console.log("handler receive message ", data);
            }
        }
    };

    let socket = new VIL.DefaultWebSocket("ws://127.0.0.1:10215/ws", handler);
</script>
</html>

index.js

let VIL = (function () {
    let VIL = {
    };
    function DefaultWebSocket(host, call) {
        let _host = host;
        let _isOpen = false;
        let _bufQueue = [];
        let _bufCap = 100;
        let _call = null;
        if("undefined" !== typeof call && call !== null){
            _call = call
        }else{
            _call = {
                onConnect:function (e) {
                    console.log("connect success ", e);
                },
                onDisconnect:function (e) {
                    console.log("disconnect ", e);
                },
                onMsg:function (data) {
                    //console.log("receive message ", data)
                }
            }
        }
        let _socket = new WebSocket(_host);
        _socket.binaryType = "arraybuffer";
        /**
         * 设置发送消息缓存队列的容量
         * @param {number} cap
         * @constructor
         */
        this.setBufferCap = function(cap){
            if("number" !== typeof cap ){
                console.error("parameter type is not number ");
                return ;
            }
            if(cap < 0){
                console.error("parameter value can not less then 0");
                return ;
            }
            _bufCap = cap;
        };
        /**
         * 发送消息
         * @param {string | ArrayBuffer } data
         * @constructor
         */
        this.send = function(data){
            if(_isOpen && _socket){
                _socket.send("");
            }else{
                if (_bufQueue < _bufCap){
                    _bufQueue.push(data);
                }
            }
        };
        this.close = function(){
            _socket.close(1000, "normal");
        };
        _socket.onopen = function(even){
            _isOpen = true;
            _call.onConnect(even);
            while (_bufQueue > 0){
                _socket.send(_bufQueue.shift());
            }
        };
        _socket.onmessage = function(e){
            let data = e.data;
            _call.onMsg(data);
        };
        /**
         * 收到关闭连接
         * @param even
         */
        _socket.onclose = function(even){
            _isOpen = false;
            _call.onDisconnect({host:_host, event:even});
        };
        /**
         * 收到错误
         * @param err
         */
        _socket.onerror = function(err){
            _isOpen = false;
            _call.onDisconnect({host:_host, event:err});
        };
    }
    try{
        VIL.EngineSocket = DefaultWebSocket ;
    }catch (e) {
        console.error("VILEngine error ", e);
    }
    return VIL;
})();

这个HTML页面包括一个WebSocket客户端的JavaScript代码,它会连接到我们的服务器,并处理连接、断开连接和消息。在浏览器中打开这个页面后,你将能够与服务器进行WebSocket通信。

总结

在本教程中,我们创建了一个WebSocket服务器和客户端,并演示了如何在浏览器中使用WebSocket客户端与服务器进行通信。WebSocket提供了一种强大的方式来实现实时双向通信,适用于各种应用程序,如在线聊天、实时数据更新等。你可以根据需要进一步扩展这些示例,以构建更复杂的WebSocket应用程序。

以上就是Golang构建WebSocket服务器和客户端的示例详解的详细内容,更多关于go WebSocket的资料请关注脚本之家其它相关文章!

相关文章

  • 在Go中编写测试代码的方法总结

    在Go中编写测试代码的方法总结

    在程序开发过程中,测试是非常重要的一环,甚至有一种开发模式叫 TDD,先编写测试,再编写功能代码,通过测试来推动整个开发的进行,可见测试在开发中的重要程度,为此,Go提供了testing框架来方便我们编写测试,本文将向大家介绍在Go中如何编写测试代码
    2023-07-07
  • Golang PHP 数据绑定示例分析

    Golang PHP 数据绑定示例分析

    这篇文章主要为大家介绍了Golang PHP 数据绑定示例分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • Go语言实现类似c++中的多态功能实例

    Go语言实现类似c++中的多态功能实例

    Go本身不具有多态的特性,不能够像Java、C++那样编写多态类、多态方法。但是,使用Go可以编写具有多态功能的类绑定的方法。下面来一起看看吧
    2016-09-09
  • Golang实现http server提供压缩文件下载功能

    Golang实现http server提供压缩文件下载功能

    这篇文章主要介绍了Golang实现http server提供压缩文件下载功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • Go 语言前缀树实现敏感词检测

    Go 语言前缀树实现敏感词检测

    这篇文章主要为大家介绍了Go语言前缀树实现敏感词检测实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • go语言中切片Slice与数组Array对比以及panic: runtime error: index out of range问题解决

    go语言中切片Slice与数组Array对比以及panic: runtime error: index out 

    go语言中数组与其他语言有在显著的不同,包括其不能够进行添加,以及值拷贝的特性,下面这篇文章主要给大家介绍了关于go语言中切片Slice与数组Array对比以及panic: runtime error: index out of range问题解决的相关资料,需要的朋友可以参考下
    2022-07-07
  • 深入string理解Golang是怎样实现的

    深入string理解Golang是怎样实现的

    这篇文章主要为大家介绍了深入string理解Golang是怎样实现的原理详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • Go使用sync.Pool提高性能的代码示例

    Go使用sync.Pool提高性能的代码示例

    在高性能应用程序中,频繁的内存分配和回收是性能瓶颈的常见原因之一,Go 语言提供了 sync.Pool 类型,它可以用来存储和重用临时对象,本文将详细介绍如何在 Go 中使用 sync.Pool,并通过实际代码示例来展示其对性能的提升效果,需要的朋友可以参考下
    2024-04-04
  • go gin 正确读取http response body内容并多次使用详解

    go gin 正确读取http response body内容并多次使用详解

    这篇文章主要为大家介绍了go gin 正确读取http response body内容并多次使用解决思路,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01
  • 关于go get 下载第三方包存储路径问题

    关于go get 下载第三方包存储路径问题

    这篇文章主要介绍了关于go get 下载第三方包存储路径问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01

最新评论