Go语言【项目】 websocket消息服务
websocket消息服务
目的:搭建websocket服务,用浏览器与服务进行消息交互(写的第一个Go程序)
代码目录结构:

前端html页面:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>
window.addEventListener("load", function(evt) {
var output = document.getElementById("output");
var input = document.getElementById("input");
var ws;
var print = function(message) {
var d = document.createElement("div");
d.innerHTML = message;
output.appendChild(d);
};
document.getElementById("open").onclick = function(evt) {
if (ws) {
return false;
}
ws = new WebSocket("ws://127.0.0.1:7777/ws");
ws.onopen = function(evt) {
print("OPEN");
}
ws.onclose = function(evt) {
print("CLOSE");
ws = null;
}
ws.onmessage = function(evt) {
print("RESPONSE: " + evt.data);
}
ws.onerror = function(evt) {
print("ERROR: " + evt.data);
}
return false;
};
document.getElementById("send").onclick = function(evt) {
if (!ws) {
return false;
}
print("SEND: " + input.value);
ws.send(input.value);
return false;
};
document.getElementById("close").onclick = function(evt) {
if (!ws) {
return false;
}
ws.close();
return false;
};
});
</script>
</head>
<body>
<table>
<tr><td valign="top" width="50%">
<p>Click "Open" to create a connection to the server,
"Send" to send a message to the server and "Close" to close the connection.
You can change the message and send multiple times.
</p>
<form>
<button id="open">Open</button>
<button id="close">Close</button>
<input id="input" type="text" value="Hello world!">
<button id="send">Send</button>
</form>
</td><td valign="top" width="50%">
<div id="output"></div>
</td></tr></table>
</body>
</html>
client.html
server.go代码:
package main import (
"fmt"
"github.com/gorilla/websocket"
"go_websocket"
"net/http"
) // http升级websocket协议的配置
var wsUpgrader = websocket.Upgrader{
// 允许跨域CORS
CheckOrigin: func(r *http.Request) bool {
return true
},
} // 消息处理
func wsHandler(resp http.ResponseWriter, req *http.Request) {
wsSocket, err := wsUpgrader.Upgrade(resp, req, nil)
if err != nil {
return
}
wsConn := go_websocket.WsConnectionInit(wsSocket)
wsConn.Run() for {
wsmsg, err := wsConn.ReadMessage()
if err != nil {
goto error
}
err = wsConn.WriteMessage(wsmsg)
if err != nil {
goto error
}
}
error:
fmt.Println("websocket is closed")
return
} func main() {
fmt.Println("websocket start")
http.HandleFunc("/ws", wsHandler)
http.ListenAndServe("0.0.0.0:7777", nil)
}
connection.go代码:
package go_websocket import (
"errors"
"fmt"
"github.com/gorilla/websocket"
"sync"
"time"
) // 客户端读写消息
type WsMessage struct {
msgType int
data []byte
} // 客户端连接
type wsConnection struct {
wsSocket *websocket.Conn
inChan chan *WsMessage
outChan chan *WsMessage isClosed bool
closeChan chan []byte
mutex sync.Mutex
} // 连接初始化
func WsConnectionInit(wsSocket *websocket.Conn) (wsConn *wsConnection) {
wsConn = &wsConnection{
wsSocket: wsSocket,
inChan: make(chan *WsMessage, 1000),
outChan: make(chan *WsMessage, 1000),
closeChan: make(chan []byte, 1),
}
return wsConn
} // 启动
func (wsConn *wsConnection) Run() {
go wsConn.readLoop()
go wsConn.writeLoop()
go wsConn.heartbeat()
} // 心跳检测
func (wsConn *wsConnection) heartbeat() {
for {
time.Sleep(2 * time.Second)
wsmsg := &WsMessage{msgType: websocket.TextMessage, data: []byte("heartbeat")}
err := wsConn.WriteMessage(wsmsg)
if err != nil {
fmt.Println("send heartbeat stop")
return
}
}
} // 循环接收
func (wsConn *wsConnection) readLoop() {
var () for {
msgType, data, err := wsConn.wsSocket.ReadMessage()
if err != nil {
goto error
}
select {
case wsConn.inChan <- &WsMessage{msgType: msgType, data: data}:
case <-wsConn.closeChan:
goto closed
}
}
error:
wsConn.Close()
closed:
fmt.Println("readLoop closed")
} // 循环发送
func (wsConn *wsConnection) writeLoop() {
for {
select {
case wsmsg := <-wsConn.outChan:
if err := wsConn.wsSocket.WriteMessage(wsmsg.msgType, wsmsg.data); err != nil {
goto error
}
case <-wsConn.closeChan:
goto closed
}
}
error:
wsConn.Close()
closed:
fmt.Println("writeLoop close")
} // 取消息,外部可调用
func (wsConn *wsConnection) ReadMessage() (wsmsg *WsMessage, err error) {
select {
case wsmsg = <-wsConn.inChan:
return wsmsg, nil
case <-wsConn.closeChan:
return nil, errors.New("websocket is closed")
}
} // 写消息,外部可调用
func (wsConn *wsConnection) WriteMessage(wsmsg *WsMessage) (err error) {
select {
case wsConn.outChan <- wsmsg:
case <-wsConn.closeChan:
return errors.New("websocket is closed")
}
return nil
} // 关闭wsSocket
func (wsConn *wsConnection) Close() {
wsConn.wsSocket.Close() // 加锁
wsConn.mutex.Lock()
if !wsConn.isClosed {
wsConn.isClosed = true
close(wsConn.closeChan)
}
wsConn.mutex.Unlock()
}
效果展示:

Go语言【项目】 websocket消息服务的更多相关文章
- 基于Go的websocket消息服务
3个月没写PHP了,这是我的第一个中小型go的websocket微服务.那么问题来了,github上那么多轮子,我为什么要自己造轮子呢? Why 造轮子? 因为这样不仅能锻炼自己的技术能力,而且能帮助 ...
- Centrifugo 语言无关的实时消息服务
Centrifugo 语言无关的实时消息服务,基于golang编写,提供了websocket 以及sockjs 的兼容处理,使用上很简单 同时也支持基于redis的扩展,以下是一个简单的运行测试 环境 ...
- 搭建websocket消息推送服务,必须要考虑的几个问题
近年,不论是正在快速增长的直播,远程教育以及IM聊天场景,还是在常规企业级系统中用到的系统提醒,对websocket的需求越来越大,对websocket的要求也越来越高.从早期对websocket的应 ...
- Spring Boot 集成 WebSocket 实现服务端推送消息到客户端
假设有这样一个场景:服务端的资源经常在更新,客户端需要尽量及时地了解到这些更新发生后展示给用户,如果是 HTTP 1.1,通常会开启 ajax 请求询问服务端是否有更新,通过定时器反复轮询服务端响应的 ...
- JMS(java消息服务)整合Spring项目案例
转载自云栖社区 摘要: Sprng-jms消息服务小项目 所需的包: spring的基础包 spring-jms-xx包 spring-message–xx包 commons-collection-x ...
- 模拟websocket推送消息服务mock工具二
模拟websocket推送消息服务mock工具二 在上一篇博文中有提到<使用electron开发一个h5的客户端应用创建http服务模拟后端接口mock>使用electron创建一个模拟后 ...
- spring集成webSocket实现服务端向前端推送消息
原文:https://blog.csdn.net/ya_nuo/article/details/79612158 spring集成webSocket实现服务端向前端推送消息 1.前端连接webso ...
- “一切都是消息”--MSF(消息服务框架)入门简介
“一切都是消息”--这是MSF(消息服务框架)的设计哲学. MSF的名字是 Message Service Framework 的简称,中文名称:消息服务框架,它是PDF.NET框架的一部分. 1,M ...
- “一切都是消息”--iMSF(即时消息服务框架)入门简介
“一切都是消息”--这是iMSF(即时消息服务框架)的设计哲学. MSF的名字是 Message Service Framework 的简称,由于目前框架主要功能在于处理即时(immediately) ...
随机推荐
- Apache ActiveMQ 远程代码执行漏洞 (CVE-2016-3088) 复现
漏洞复现 直接写 shell 写 shell 的话,需要写在 admin 或者 api 中,也就是需要登录,没有密码的话完成不了写 shell 操作. 该环境默认的口令为 admin/admin. 访 ...
- linux安装包制作
1. 常见安装包 打包或压缩文件tar,zip,gz等,一般解压后即可 管理工具的deb,rpm等.这类安装文件可以通过第三方的命令安装 (apt和yum) .bin类,其实就是把sh和zip打包为b ...
- 203--Remove LinkedList Elements
package LinedList; public class RemoveLinkedListElements { //解法一:循环 public ListNode removeElements(L ...
- Spring通过配置类加载实体bean
以下4个java类都在都一个包下: 1.定义接口 public interface AA { void play(); } 2.定义实体bean //组件注解,表明该类是一个组件 @Component ...
- 面向对象(五)--isinstance与issubclass方法、反射、内置方法(部分)、异常处理
一.isinstance与issubclass方法 1.isinstance是用来判断对象是否是某个类 isinstance(obj,class) 2.issubclass是用来判断一个类是否为另一个 ...
- python socket 传输文件
推荐资料 https://www.cnblogs.com/xiaokang01/p/9865724.html socket传输文件 思路: # 先将报头转换成字符串(json.dumps), 再将字符 ...
- 【Servlet】Servlet的配置
创建时间:6.15 Servlet的配置 1. 基本配置 其中url-pattern的配置方式: 1)完全匹配 访问的资源与配置的资源完全相同才能访问到 2)目录匹配 格式:/虚拟的目录../* ...
- 年薪30W测试工程师成长之路,你在哪个阶段?
对任何职业而言,薪资始终都会是众多追求的重要部分.前几年的软件测试行业还是一个风口,随着不断地转行人员以及毕业的大学生疯狂地涌入软件测试行业,目前软件测试行业“缺口”已经基本饱和.当然,我说的是最基础 ...
- java String 常用方法
String方法 class CeShi{ public static void main(String[] args) { //toCharArray char chararraryone[]=&q ...
- Magic Master(2019年南昌网络赛E题+约瑟夫环)
目录 题目链接 题意 思路 代码 题目链接 传送门 题意 初始时你有\(n\)张牌(按顺序摆放),每一次操作你将顶端的牌拿出,然后按顺序将上面的\(m\)张牌放到底部. 思路 首先我们发下拿走\(1\ ...