GO --- 实时通信Melody包的介绍和例子

简介: GO --- 实时通信Melody包的介绍和例子

简介

一个用于Go的极简Websocket框架

Melody是基于 github.com/gorilla/websocket框架的,并抽象处理了里面的繁杂部分。

它可以让你搭建一个实时通讯的app,功能包括:

  • 接口简单易用类似于net/http或Gin。
  • 提供给所有广播以及给选择连接会话广播的简单途径。
  • 消息缓冲区使并发写入变得安全。
  • 自动处理ping/pong和会话超时。
  • 在会话中存储数据。

例子

使用Gin框架完成的聊天室功能

下载依赖包

$ go get -u gopkg.in/olahol/melody.v1
$ go get -u github.com/gin-gonic/gin

main.go

package main
import (
   "github.com/gin-gonic/gin"
   "gopkg.in/olahol/melody.v1"
   "net/http"
)
func main() {
   r := gin.Default()
   m := melody.New()
   r.GET("/", func(c *gin.Context) {
      http.ServeFile(c.Writer, c.Request, "index.html")
   })
   r.GET("/ws", func(c *gin.Context) {
      m.HandleRequest(c.Writer, c.Request)
   })
   m.HandleMessage(func(s *melody.Session, msg []byte) {
      m.Broadcast(msg)
   })
   r.Run(":5000")
}

index.heml

<html>
  <head>
    <title>Melody example: chatting</title>
  </head>
  <style>
    #chat {
      text-align: left;
      background: #f1f1f1;
      width: 500px;
      min-height: 300px;
      padding: 20px;
    }
  </style>
  <body>
    <center>
      <h3>Chat</h3>
      <pre id="chat"></pre>
      <input placeholder="say something" id="text" type="text">
    </center>
    <script>
      var url = "ws://" + window.location.host + "/ws";
      var ws = new WebSocket(url);
      var name = "Guest" + Math.floor(Math.random() * 1000);
      var chat = document.getElementById("chat");
      var text = document.getElementById("text");
      var now = function () {
        var iso = new Date().toISOString();
        return iso.split("T")[1].split(".")[0];
      };
      ws.onmessage = function (msg) {
        var line =  now() + " " + msg.data + "\n";
        chat.innerText += line;
      };
      text.onkeydown = function (e) {
        if (e.keyCode === 13 && text.value !== "") {
          ws.send("<" + name + "> " + text.value);
          text.value = "";
        }
      };
    </script>
  </body>
</html>

展示图:

使用Gin完成文件的实时监控

下载

$ github.com/fsnotify/fsnotify
$ github.com/gin-gonic/gin
$ gopkg.in/olahol/melody.v1

main.go

package main
import (
   "github.com/fsnotify/fsnotify"
   "github.com/gin-gonic/gin"
   "gopkg.in/olahol/melody.v1"
   "io/ioutil"
   "net/http"
)
func main() {
   file := "file.txt"
   r := gin.Default()
   m := melody.New()
   w, _ := fsnotify.NewWatcher()
   r.GET("/", func(c *gin.Context) {
      http.ServeFile(c.Writer, c.Request, "index.html")
   })
   r.GET("/ws", func(c *gin.Context) {
      m.HandleRequest(c.Writer, c.Request)
   })
   m.HandleConnect(func(s *melody.Session) {
      content, _ := ioutil.ReadFile(file)
      s.Write(content)
   })
   go func() {
      for {
         ev := <-w.Events
         if ev.Op == fsnotify.Write {
            content, _ := ioutil.ReadFile(ev.Name)
            m.Broadcast(content)
         }
      }
   }()
   w.Add(file)
   r.Run(":5001")
}

index.html

<html>
  <head>
    <title>Melody example: file watching</title>
  </head>
  <style>
    #file {
      text-align: left;
      background: #f1f1f1;
      width: 500px;
      min-height: 300px;
      padding: 20px;
    }
  </style>
  <body>
    <center>
      <h3>Watching a file</h3>
      <pre id="file"></pre>
    </center>
    <script>
      var url = 'ws://' + window.location.host + '/ws';
      var c = new WebSocket(url);
      c.onmessage = function(msg){
        var el = document.getElementById("file");
        el.innerText = msg.data;
      }
    </script>
  </body>
</html>

file.txt

监控文章修改的状况
wqe
王企鹅

展示图

使用Gin完成图片的实时交互

下载

$ go get -u gopkg.in/olahol/melody.v1
$ go get -u github.com/gin-gonic/gin

main.go

package main
import (
   "net/http"
   "strconv"
   "strings"
   "sync"
   "github.com/gin-gonic/gin"
   "gopkg.in/olahol/melody.v1"
)
// GopherInfo contains information about the gopher on screen
type GopherInfo struct {
   ID, X, Y string
}
func main() {
   router := gin.Default()
   mrouter := melody.New()
   gophers := make(map[*melody.Session]*GopherInfo)
   lock := new(sync.Mutex)
   counter := 0
   router.GET("/", func(c *gin.Context) {
      http.ServeFile(c.Writer, c.Request, "index.html")
   })
   router.GET("/ws", func(c *gin.Context) {
      mrouter.HandleRequest(c.Writer, c.Request)
   })
   mrouter.HandleConnect(func(s *melody.Session) {
      lock.Lock()
      for _, info := range gophers {
         s.Write([]byte("set " + info.ID + " " + info.X + " " + info.Y))
      }
      gophers[s] = &GopherInfo{strconv.Itoa(counter), "0", "0"}
      s.Write([]byte("iam " + gophers[s].ID))
      counter++
      lock.Unlock()
   })
   mrouter.HandleDisconnect(func(s *melody.Session) {
      lock.Lock()
      mrouter.BroadcastOthers([]byte("dis "+gophers[s].ID), s)
      delete(gophers, s)
      lock.Unlock()
   })
   mrouter.HandleMessage(func(s *melody.Session, msg []byte) {
      p := strings.Split(string(msg), " ")
      lock.Lock()
      info := gophers[s]
      if len(p) == 2 {
         info.X = p[0]
         info.Y = p[1]
         mrouter.BroadcastOthers([]byte("set "+info.ID+" "+info.X+" "+info.Y), s)
      }
      lock.Unlock()
   })
   router.Run(":5002")
}

index.html

<html>
  <head>
    <meta charset="utf-8">
    <title>goofy gophers</title>
  <style>
  body {
    cursor: none;
    overflow: hidden;
  }
  .gopher {
    background-image: url('https://gimg2.baidu.com/image_search/src=http%3A%2F%2Ffile.elecfans.com%2Fweb1%2FM00%2F8F%2FDB%2FpIYBAFzBUUGAPWqXAAAu20XeRuU336.png&refer=http%3A%2F%2Ffile.elecfans.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1654849209&t=fe13e4758e193bf3c5aa66d7baa79a9d');
    width: 95px;
    height: 95px;
    background-size: cover;
    position: absolute;
    left: 0px;
    top: 0px;
  }
  </style>
  </head>
  <body>
    <script>
      var url = "ws://" + window.location.host + "/ws";
      var ws = new WebSocket(url);
      var myid = -1;
      ws.onmessage = function (msg) {
        var cmds = {"iam": iam, "set": set, "dis": dis};
        if (msg.data) {
          var parts = msg.data.split(" ")
          var cmd = cmds[parts[0]];
          if (cmd) {
            cmd.apply(null, parts.slice(1));
          }
        }
      };
      function iam(id) {
        myid = id;
      }
      function set(id, x, y) {
        var node = document.getElementById("gopher-" + id);
        if (!node) {
          node = document.createElement("div");
          document.body.appendChild(node);
          node.className = "gopher";
          node.style.zIndex = id + 1;
          node.id = "gopher-" + id;
        }
        node.style.left = x + "px";
        node.style.top = y + "px";
      }
      function dis(id) {
        var node = document.getElementById("gopher-" + id);
        if (node) {
          document.body.removeChild(node);
        }
      }
      window.onmousemove = function (e) {
        if (myid > -1) {
          set(myid, e.pageX, e.pageY);
          ws.send([e.pageX, e.pageY].join(" "));
        }
      }
    </script>
  </body>
</html>

展示图

使用Gin框架完成分组的聊天室

下载

$ go get -u gopkg.in/olahol/melody.v1
$ go get -u github.com/gin-gonic/gin

main.go

package main
import (
   "github.com/gin-gonic/gin"
   "gopkg.in/olahol/melody.v1"
   "net/http"
)
func main() {
   r := gin.Default()
   m := melody.New()
   r.GET("/", func(c *gin.Context) {
      http.ServeFile(c.Writer, c.Request, "index.html")
   })
   r.GET("/channel/:name", func(c *gin.Context) {
      http.ServeFile(c.Writer, c.Request, "chan.html")
   })
   r.GET("/channel/:name/ws", func(c *gin.Context) {
      m.HandleRequest(c.Writer, c.Request)
   })
   m.HandleMessage(func(s *melody.Session, msg []byte) {
      m.BroadcastFilter(msg, func(q *melody.Session) bool {
         return q.Request.URL.Path == s.Request.URL.Path
      })
   })
   r.Run(":5003")
}

index.html

<html>
  <head>
    <title>Melody example: chatting</title>
  </head>
  <style>
    #chat {
      text-align: left;
      background: #f1f1f1;
      width: 500px;
      min-height: 300px;
      padding: 20px;
    }
  </style>
  <body>
    <center>
      <h3>Join a channel</h3>
      <input placeholder="channel" id="channel" type="text"><button id="join">Join</button>
    </center>
    <script>
      var chan = document.getElementById("channel");
      var join = document.getElementById("join");
      join.onclick = function () {
        if (chan.value != "") {
          window.location = "/channel/" + chan.value;
        }
      };
    </script>
  </body>
</html>

chan.html

<html>
  <head>
    <title>Melody example: chatting</title>
  </head>
  <style>
    #chat {
      text-align: left;
      background: #f1f1f1;
      width: 500px;
      min-height: 300px;
      padding: 20px;
    }
  </style>
  <body>
    <center>
      <h3 id="name"></h3>
      <pre id="chat"></pre>
      <input placeholder="say something" id="text" type="text">
    </center>
    <script>
      var url = "ws://" + window.location.host + window.location.pathname + "/ws";
      var ws = new WebSocket(url);
      var name = "Guest" + Math.floor(Math.random() * 1000);
      var channelName = window.location.pathname.split("/")[2];
      document.getElementById("name").innerText = "Channel: " + channelName;
      var chat = document.getElementById("chat");
      var text = document.getElementById("text");
      var now = function () {
        var iso = new Date().toISOString();
        return iso.split("T")[1].split(".")[0];
      };
      ws.onmessage = function (msg) {
        var line =  now() + " " + msg.data + "\n";
        chat.innerText += line;
      };
      text.onkeydown = function (e) {
        if (e.keyCode === 13 && text.value !== "") {
          ws.send("<" + name + "> " + text.value);
          text.value = "";
        }
      };
    </script>
  </body>
</html>

展示图

听我狡辩

为了避免出现“你也个写博客的不交咋用,光给个例子是什么意思”之类的发言,在此声明,Melody官网就只有例子,虽然给了文档,但是好像只有翻墙才能查看,我可是光明正大的良民,不知道咋翻墙,更不看P站。如果你们有代理的话可以去看看,请看完顺带发篇博客并@我去学习,国内网上都找不到这个包的信息,只有Github上的还算正规,所以拜托了。


相关文章
|
5天前
|
Linux Go iOS开发
怎么禁用 vscode 中点击 go 包名时自动打开浏览器跳转到 pkg.go.dev
本文介绍了如何在 VSCode 中禁用点击 Go 包名时自动打开浏览器跳转到 pkg.go.dev 的功能。通过将 gopls 的 `ui.navigation.importShortcut` 设置为 &quot;Definition&quot;,可以实现仅跳转到定义处而不打开链接。具体操作步骤包括:打开设置、搜索 gopls、编辑 settings.json 文件并保存更改,最后重启 VSCode 使设置生效。
25 7
怎么禁用 vscode 中点击 go 包名时自动打开浏览器跳转到 pkg.go.dev
|
20天前
|
Go 索引
go语言使用strings包
go语言使用strings包
23 3
|
24天前
|
缓存 监控 前端开发
Go 语言中如何集成 WebSocket 与 Socket.IO,实现高效、灵活的实时通信
本文探讨了在 Go 语言中如何集成 WebSocket 与 Socket.IO,实现高效、灵活的实时通信。首先介绍了 WebSocket 和 Socket.IO 的基本概念及其优势,接着详细讲解了 Go 语言中 WebSocket 的实现方法,以及二者集成的重要意义和具体步骤。文章还讨论了集成过程中需要注意的问题,如协议兼容性、消息格式、并发处理等,并提供了实时聊天、数据监控和在线协作工具等应用案例,最后提出了性能优化策略,包括数据压缩、缓存策略和连接管理优化。旨在帮助开发者更好地理解并应用这些技术。
41 3
|
24天前
|
缓存 监控 前端开发
在 Go 语言中实现 WebSocket 实时通信的应用,包括 WebSocket 的简介、Go 语言的优势、基本实现步骤、应用案例、注意事项及性能优化策略,旨在帮助开发者构建高效稳定的实时通信系统
本文深入探讨了在 Go 语言中实现 WebSocket 实时通信的应用,包括 WebSocket 的简介、Go 语言的优势、基本实现步骤、应用案例、注意事项及性能优化策略,旨在帮助开发者构建高效稳定的实时通信系统。
70 1
|
1月前
|
编译器 Go 开发者
go语言中导入相关包
【11月更文挑战第1天】
30 3
|
2月前
|
存储 Go 数据库
Go语言Context包源码学习
【10月更文挑战第21天】Go 语言中的 `context` 包用于在函数调用链中传递请求上下文信息,支持请求的取消、超时和截止时间管理。其核心接口 `Context` 定义了 `Deadline`、`Done`、`Err` 和 `Value` 方法,分别用于处理截止时间、取消信号、错误信息和键值对数据。包内提供了 `emptyCtx`、`cancelCtx`、`timerCtx` 和 `valueCtx` 四种实现类型,满足不同场景需求。示例代码展示了如何使用带有超时功能的上下文进行任务管理和取消。
|
3月前
|
存储 Go
Golang语言基于go module方式管理包(package)
这篇文章详细介绍了Golang语言中基于go module方式管理包(package)的方法,包括Go Modules的发展历史、go module的介绍、常用命令和操作步骤,并通过代码示例展示了如何初始化项目、引入第三方包、组织代码结构以及运行测试。
65 3
|
4月前
|
编译器 数据库连接 Go
Go Sync 包:并发的 6 个关键概念
Go Sync 包:并发的 6 个关键概念
|
4月前
|
存储 SQL Go
一文弄懂Go语言的Context包,值得收藏!
在开发高效的Go应用程序时,处理超时、取消操作和传递请求范围的数据至关重要。Go标准库中的`context`包提供了一套强大的工具,用于在不同层级间传递取消信号、超时和截止时间等信息。本文首先概述了`context`包的核心功能,接着详细介绍了关键方法,如`WithCancel`、`WithDeadline`、`WithTimeout`和`WithValue`的使用场景。通过创建和派生上下文,开发者能更好地管理请求的生命周期,控制长时间运行的操作,并在并发环境中传递必要的数据。
57 1
|
4月前
|
Go 开发者