一个用于Go的极简Websocket框架
Melody是基于 github.com/gorilla/websocket框架的,并抽象处理了里面的繁杂部分。
它可以让你搭建一个实时通讯的app,功能包括:
下载依赖包
$ 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: chattingtitle>
head>
<style>
#chat {
text-align: left;
background: #f1f1f1;
width: 500px;
min-height: 300px;
padding: 20px;
}
style>
<body>
<center>
<h3>Chath3>
<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>
展示图:
下载
$ 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 watchingtitle>
head>
<style>
#file {
text-align: left;
background: #f1f1f1;
width: 500px;
min-height: 300px;
padding: 20px;
}
style>
<body>
<center>
<h3>Watching a fileh3>
<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
王企鹅
下载
$ 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 gopherstitle>
<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>
展示图
下载
$ 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: chattingtitle>
head>
<style>
#chat {
text-align: left;
background: #f1f1f1;
width: 500px;
min-height: 300px;
padding: 20px;
}
style>
<body>
<center>
<h3>Join a channelh3>
<input placeholder="channel" id="channel" type="text"><button id="join">Joinbutton>
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: chattingtitle>
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上的还算正规,所以拜托了。