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消息服务的更多相关文章

  1. 基于Go的websocket消息服务

    3个月没写PHP了,这是我的第一个中小型go的websocket微服务.那么问题来了,github上那么多轮子,我为什么要自己造轮子呢? Why 造轮子? 因为这样不仅能锻炼自己的技术能力,而且能帮助 ...

  2. Centrifugo  语言无关的实时消息服务

    Centrifugo 语言无关的实时消息服务,基于golang编写,提供了websocket 以及sockjs 的兼容处理,使用上很简单 同时也支持基于redis的扩展,以下是一个简单的运行测试 环境 ...

  3. 搭建websocket消息推送服务,必须要考虑的几个问题

    近年,不论是正在快速增长的直播,远程教育以及IM聊天场景,还是在常规企业级系统中用到的系统提醒,对websocket的需求越来越大,对websocket的要求也越来越高.从早期对websocket的应 ...

  4. Spring Boot 集成 WebSocket 实现服务端推送消息到客户端

    假设有这样一个场景:服务端的资源经常在更新,客户端需要尽量及时地了解到这些更新发生后展示给用户,如果是 HTTP 1.1,通常会开启 ajax 请求询问服务端是否有更新,通过定时器反复轮询服务端响应的 ...

  5. JMS(java消息服务)整合Spring项目案例

    转载自云栖社区 摘要: Sprng-jms消息服务小项目 所需的包: spring的基础包 spring-jms-xx包 spring-message–xx包 commons-collection-x ...

  6. 模拟websocket推送消息服务mock工具二

    模拟websocket推送消息服务mock工具二 在上一篇博文中有提到<使用electron开发一个h5的客户端应用创建http服务模拟后端接口mock>使用electron创建一个模拟后 ...

  7. spring集成webSocket实现服务端向前端推送消息

    原文:https://blog.csdn.net/ya_nuo/article/details/79612158 spring集成webSocket实现服务端向前端推送消息   1.前端连接webso ...

  8. “一切都是消息”--MSF(消息服务框架)入门简介

    “一切都是消息”--这是MSF(消息服务框架)的设计哲学. MSF的名字是 Message Service Framework 的简称,中文名称:消息服务框架,它是PDF.NET框架的一部分. 1,M ...

  9. “一切都是消息”--iMSF(即时消息服务框架)入门简介

    “一切都是消息”--这是iMSF(即时消息服务框架)的设计哲学. MSF的名字是 Message Service Framework 的简称,由于目前框架主要功能在于处理即时(immediately) ...

随机推荐

  1. Windows 10 蓝牙管理页面"添加蓝牙或其他设备"选项点击无响应的解决方案

    解决方案1(简单,但不解决根本问题): 通过"控制面板→设备和打印机→添加设备"进行添加. 解决方案2: 造成这种现象的原因应该是因为启用了 Administrator 账户,在其 ...

  2. bugku秋名山老司机+写博客的第一天

    bugku之秋名山老司机 题目连接:http://123.206.87.240:8002/qiumingshan/ 一点进去是这样的 请在两秒内计算这个式子...怎么可能算的出来 查看源码,无果.. ...

  3. JavaWeb 过滤敏感词汇

    提交的表单数据,常常要检查有没有敏感词汇,如果有,需要给出提示,或者替换为*. 检查.替换敏感词汇有3种常用的方式 (1)在Servlet中操作. (2)在Filter中先检查.如果要替换敏感词汇,r ...

  4. Java集合学习(9):集合对比

    一.HashMap与HashTable的区别 HashMap和Hashtable的比较是Java面试中的常见问题,用来考验程序员是否能够正确使用集合类以及是否可以随机应变使用多种思路解决问题.Hash ...

  5. 集成学习-Majority Voting

    认识 集成学习(Ensemble Methods), 首先是一种思想, 而非某种模型, 是一种 "群体决策" 的思想, 即对某一特定问题, 用多个模型来进行训练. 像常见的单个模型 ...

  6. Redis内存模型(1):内存统计及划分

    1. 内存统计 查看命令:info memory 示例: 部分含义: used_memory: Redis分配器分配的内存总量(单位是字节),包括使用的虚拟内存. used_memory_rss: R ...

  7. 01. MySQL8.0 MAC-OS-X安装

    目录 MySQL8.0 MAC-OS-X安装 8.0较与5.7变化 下载 安装 启动 登录查看数据库 安装后mysql文件分布 MySQL8.0 MAC-OS-X安装 换mac啦,搭建开发环境,安装m ...

  8. php导出数据到多个csv并打包压缩

    1.不压缩直接下载 // 测试php导出大量数据到csv public function actionExportData() { // 设置不超时 set_time_limit(0); // 设置最 ...

  9. HDU 1372 Knight Moves 题解

    Knight Moves Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Tota ...

  10. 解决nginx反向代理webservice的soap:address location问题

    原文:https://blog.csdn.net/mn960mn/article/details/50716768 一:首先来发布一个web service package com.ws.servic ...