WebSocket 和 Golang 实现聊天功能
http://www.open-open.com/lib/view/open1416379948711.html
这个示例应用程序展示了如何使用 WebSocket, Golang 和 jQuery 创建一个简单的web聊天应用程序。这个示例的源代码在 https://github.com/waylau/goChat 。
Running the example 运行示例
这个示例需要 Golang 开发环境。 该页面描述如何安装开发环境。
一旦你去启动和运行,您可以下载、构建和运行的例子, 使用命令:
go get gary.burd.info/go-websocket-chat
go-websocket-chat
在支持 websocket 的浏览器尝试打开 http://127.0.0.1:8080/ 启动应用
Server 服务器
服务器程序实现了 http 包,包含了 Go 分发和 Gorilla 项目的 websocket 包.
应用程序定义了两种类型, connection 和 hub 。服务器为每个 webscocket 连接 创建的一个 connection 类型的实例 。 连接器扮演了 websocket 和 hub 类型单例 之间的媒介 。 hub 保持一组注册了的连接器 和 广播到连接器的信息。
程序运行了一个 goroutine 给 hub 和两个 goroutine 给每个连接器。 goroutine 通过 channel 和其他进行交流。 hub 拥有注册连接器、注销连接器和广播信息的 channel。一个连机器拥有缓存的发出信息的 channel 。其中一个 连接器的 goroutine 从这个 channel 中读信息 并把信息写入 webscoket。另外一个连接器 goroutine 从 websocket 读信息,并把信息发送到 hub。
下面是 hub 类型代码:
package main
type hub struct {
    // 注册了的连接器
    connections map[*connection]bool
    // 从连接器中发入的信息
    broadcast chan []byte
    // 从连接器中注册请求
    register chan *connection
    // 从连接器中注销请求
    unregister chan *connection
}
var h = hub{
    broadcast:   make(chan []byte),
    register:    make(chan *connection),
    unregister:  make(chan *connection),
    connections: make(map[*connection]bool),
}
func (h *hub) run() {
    for {
        select {
        case c := <-h.register:
            h.connections[c] = true
        case c := <-h.unregister:
            if _, ok := h.connections[c]; ok {
                delete(h.connections, c)
                close(c.send)
            }
        case m := <-h.broadcast:
            for c := range h.connections {
                select {
                case c.send <- m:
                default:
                    delete(h.connections, c)
                    close(c.send)
                }
            }
        }
    }
}
应用程序的 主要 函数启动 hub 以 goroutine 形式运行方法。连接器 发送请求到 hub 通过 注册、注销和广播 channel。
hub 注册连接器通过添加 connection 的指针作为 connections map 的主键。这个 map 的值通常是 true。
注销的代码有点复杂。除了从 connections map 删除连接器的指针外, hub 关闭了 connection 的发送,来标识没有信息再被发送到 connection了。
hub 通过循环注册连接器和发送信息到连接器的发送 channel 来控制信息。 如果连接器的发送缓冲区已经满了,那么 hub 假设 客户端已死或卡住了。这种情况下, hub 注销连接器 并关闭 websocket.
下面关于 connection 类型的代码:
package main import (
"github.com/gorilla/websocket"
"net/http"
) type connection struct {
// websocket 连接器
ws *websocket.Conn // 发送信息的缓冲 channel
send chan []byte
} func (c *connection) reader() {
for {
_, message, err := c.ws.ReadMessage()
if err != nil {
break
}
h.broadcast <- message
}
c.ws.Close()
} func (c *connection) writer() {
for message := range c.send {
err := c.ws.WriteMessage(websocket.TextMessage, message)
if err != nil {
break
}
}
c.ws.Close()
} var upgrader = &websocket.Upgrader{ReadBufferSize: 1024, WriteBufferSize: 1024} func wsHandler(w http.ResponseWriter, r *http.Request) {
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
return
}
c := &connection{send: make(chan []byte, 256), ws: ws}
h.register <- c
defer func() { h.unregister <- c }()
go c.writer()
c.reader()
}
wsHandler 方法被主函数当做http handler注册。HTTP 连接到 WebSocket 协议的升级,创建一个连接对象,注册这个连接到 sub ,并通过 defer延迟语句 来控制 连接的注销。
接着,wsHandler 方法开启 连接器的写入方法作为一个 goroutine。 写入方法将信息从连接器的 channel 转入 websocket。当 hub 关闭 channel 或者 在写入 websocket 时出错,写入方法关闭。
最后,wsHandler 方法 调用连接器的 读 方法。 读方法将 入站消息 从 websocket 转到 hub。
这里是服务器的代码的其余部分:
package main import (
"flag"
"go/build"
"log"
"net/http"
"path/filepath"
"text/template"
) var (
addr = flag.String("addr", ":8080", "http service address")
assets = flag.String("assets", defaultAssetPath(), "path to assets")
homeTempl *template.Template
) func defaultAssetPath() string {
p, err := build.Default.Import("gary.burd.info/go-websocket-chat", "", build.FindOnly)
if err != nil {
return "."
}
return p.Dir
} func homeHandler(c http.ResponseWriter, req *http.Request) {
homeTempl.Execute(c, req.Host)
} func main() {
flag.Parse()
homeTempl = template.Must(template.ParseFiles(filepath.Join(*assets, "home.html")))
go h.run()
http.HandleFunc("/", homeHandler)
http.HandleFunc("/ws", wsHandler)
if err := http.ListenAndServe(*addr, nil); err != nil {
log.Fatal("ListenAndServe:", err)
}
}
应用主程序启动 hub goroutine。 接着 主程序 注册 主页 和 websocket 连接器的控制器N。最后主程序启动 HTTP 服务器。
Client 客户端
客户端的实现是一个简单的 HTML 文件:
<html>
<head>
<title>Chat Example</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript">
$(function() { var conn;
var msg = $("#msg");
var log = $("#log"); function appendLog(msg) {
var d = log[0]
var doScroll = d.scrollTop == d.scrollHeight - d.clientHeight;
msg.appendTo(log)
if (doScroll) {
d.scrollTop = d.scrollHeight - d.clientHeight;
}
} $("#form").submit(function() {
if (!conn) {
return false;
}
if (!msg.val()) {
return false;
}
conn.send(msg.val());
msg.val("");
return false
}); if (window["WebSocket"]) {
conn = new WebSocket("ws://{{$}}/ws");
conn.onclose = function(evt) {
appendLog($("<div><b>Connection closed.</b></div>"))
}
conn.onmessage = function(evt) {
appendLog($("<div/>").text(evt.data))
}
} else {
appendLog($("<div><b>Your browser does not support WebSockets.</b></div>"))
}
});
</script>
<style type="text/css">
html {
overflow: hidden;
} body {
overflow: hidden;
padding: 0;
margin: 0;
width: 100%;
height: 100%;
background: gray;
} #log {
background: white;
margin: 0;
padding: 0.5em 0.5em 0.5em 0.5em;
position: absolute;
top: 0.5em;
left: 0.5em;
right: 0.5em;
bottom: 3em;
overflow: auto;
} #form {
padding: 0 0.5em 0 0.5em;
margin: 0;
position: absolute;
bottom: 1em;
left: 0px;
width: 100%;
overflow: hidden;
} </style>
</head>
<body>
<div id="log"></div>
<form id="form">
<input type="submit" value="Send" />
<input type="text" id="msg" size="64"/>
</form>
</body>
</html>
客户端使用 jQuery
文档加载。脚本检查 websocket 的功能 。如果 WebSocket 功能 可以用,然后打开脚本与服务器的连接,并注册一个回调处理来自服务器的信息。回调使用 appendlog 方法将消息添加到聊天记录。
appendlog 方法检查在添加新的内容时的滚动位置,从而可以让用户手动滚动聊天记录而不会被新来的消息中断。如果聊天记录滚动至底部,那么新内容添加的到旧内容的后面。否则,滚动的位置不会改变。
表单处理器将用户的输入写入到 WebSocket 并且清除输入字段。
参考:http://gary.burd.info/go-websocket-chat
来自:http://www.waylau.com/go-websocket-chat/
WebSocket 和 Golang 实现聊天功能的更多相关文章
- WebSocket(3)---实现一对一聊天功能
		
实现一对一聊天功能 功能介绍:实现A和B单独聊天功能,即A发消息给B只能B接收,同样B向A发消息只能A接收. 本篇博客是在上一遍基础上搭建,上一篇博客地址:[WebSocket]---实现游戏公告功能 ...
 - Spring Websocket实现简易在线聊天功能
		
针对Spring Websocket的实现,我参照了其他博主的文章https://www.cnblogs.com/leechenxiang/p/5306372.html 下面直接给出实现: 一.引入相 ...
 - webSocket实现多人聊天功能
		
webSocket实现多人在线聊天 主要思路如下: 1.使用vue构建简单的聊天室界面 2.基于nodeJs 的webSocket开启一个socket后台服务,前端使用H5的webSocket来创建一 ...
 - Spring 学习——基于Spring WebSocket 和STOMP实现简单的聊天功能
		
本篇主要讲解如何使用Spring websocket 和STOMP搭建一个简单的聊天功能项目,里面使用到的技术,如websocket和STOMP等会简单介绍,不会太深,如果对相关介绍不是很了解的,请自 ...
 - 使用websocket实现在线聊天功能
		
很早以前为了快速达到效果,使用轮询实现了在线聊天功能,后来无意接触了socket,关于socket我的理解是进程间通信,首先要有服务器跟客户端,服务的启动监听某ip端口定位该进程,客户端开启socke ...
 - spring boot集成websocket实现聊天功能和监控功能
		
本文参考了这位兄台的文章: https://blog.csdn.net/ffj0721/article/details/82630134 项目源码url: https://github.com/zhz ...
 - 基于vs2015 SignalR开发的微信小程序使用websocket实现聊天功能
		
一)前言 在微信小程上实现聊天功能,大致有三种方式:1)小程序云开发 2)购买第三方IM服务 3)使用自己的服务器自己开发. 这里重要讲使用自己的服务器自己开发,并且是基于vs的开发. 网上提供的解决 ...
 - JAVA结合WebSocket实现简单客服聊天功能
		
说明:该示例只简单的实现了客服聊天功能. 1.聊天记录没有保存到数据库中,一旦服务重启,消息记录将会没有,如果需要保存到数据库中,可以扩展 2.页面样式用的网上模板,样式可以自己进行修改 3.只能由用 ...
 - 【WebSocket No.2】WebSocket和Socket实现聊天群发
		
介绍: 前面写过一篇简单的websocke实现服务端.这一篇就不在说什么基础的东西主要是来用实例说话,主要是讲一下实现单聊和群组聊天和所有群发的思路设计. 直接不懂的可以看一下上一篇简单版本再来看也行 ...
 
随机推荐
- Java 学习之反射机制“解刨”分解类,并获取内容!
			
正常情况下,单纯的做开发是接触不到反射机制的(额,当然并不排除例外的情况了).下面我就对我学到的反射方面的知识做一个小小的总结,旨在复习和以后的查看. 原理分析: 所谓反射就是将一个类当做我们研究的对 ...
 - ubuntu16.04中可以用于教学的有趣的应用
			
ubuntu16.04中可以用于教学的有趣的应用 在ubuntu自带的软件中心里,内置了非常丰富的教育应用,可以用于物理,化学等科学课教学,只选取我用过用于教学的软件,优秀的软件不止这些,可以慢慢发掘 ...
 - Chapter 2 User Authentication, Authorization, and Security(6):服务器权限授予粒度
			
原文出处:http://blog.csdn.net/dba_huangzj/article/details/38867489,专题目录:http://blog.csdn.net/dba_huangzj ...
 - pig的grunt中shell命令不稳定,能不用尽量不用
			
shell命令:mv a b 将文件a改名为b, 可如果b已经存在,比如/test文件下有a和b两个文件,执行mv a b后,b被覆盖的了.也就是/test文件下只有a. 但是mv命令在pig的g ...
 - Device tree customization
			
Step 1: OEMs can create their own device tree by adding "qcom,msm-id/qcom,board-id" entry ...
 - avcodec_decode_video2()解码视频后丢帧的问题解决
			
使用libav转码视频时发现一个问题:使用下面这段代码解码视频时,视频尾巴上会丢掉几帧. while(av_read_frame(ifmt_ctx,&packet) >= 0){ ret ...
 - (四十九)Quartz2D自定义控件
			
利用Quartz2D来自定义UIImageView: 模仿UIImageView: 设置frame,设置图片. 注意一个细节,自定义的imageView,应该通过重写set方法来设置图片并且重绘,否则 ...
 - java的hashmap与hashtable说明,简单易理解
			
1. HashMap 1) hashmap的数据结构 Hashmap是一个数组和链表的结合体(在数据结构称"链表散列"),如下图示: 当我们往hashmap中put元素的时候,先根 ...
 - (NO.00001)iOS游戏SpeedBoy Lite成形记(二十)
			
下面修改最为关键的matchRun方法里的代码: CCActionCallBlock *blk = [CCActionCallBlock actionWithBlock:^{ _finishedCou ...
 - 小强的HTML5移动开发之路(6)——Canvas图形绘制基础
			
来自:http://blog.csdn.net/dawanganban/article/details/17686039 在前面提到Canvas是HTML5中一个重要特点,canvas功能非常强大,用 ...