利用openresty写一个简易的基于websocket的即时通讯(im)聊天
2017-12-07 00:00
976 查看
摘要: lua-resty-websocket是基于ngx_lua模块的Lua Websocket实现,实现了Websocket服务端和客户端,采用了ngx_lua的cosocket API ,是非阻塞的。
1.配置文件
2.websocket.lua 利用redis的订阅/发布来实现消息的接收与发送
3.页面
4.运行结果:
1.配置文件
server{ listen 8009; proxy_ignore_client_abort on; #不允许客户端主动关闭连接 location /test/websocket { lua_check_client_abort on; #是否监视client提前关闭请求的事件,如果打开监视,会调用ngx.on_abort()注册的回调 lua_socket_log_errors off; #当套接字发生错误时,底层ngx_lua模块会进行错误记录,如果你已经在自己的lua代码中进行了适当的错误处理,那么建议关闭lua_socket_log_errors指令来禁用此自动错误日志记录 content_by_lua_file /Users/guanguan/study/2017/or-websocket/websocket.lua; } access_log /usr/local/openresty/nginx/logs/websocket_access.log main; error_log /usr/local/openresty/nginx/logs/websocket_error.log debug; }
2.websocket.lua 利用redis的订阅/发布来实现消息的接收与发送
--[[ - @desc 用于调试 lua数据输出 - @param string 字符串 - return string --]] function dump(v) if not __dump then function __dump(v, t, p) local k = p or ""; if type(v) ~= "table" then table.insert(t, k .. " : " .. tostring(v)); else for key, value in pairs(v) do __dump(value, t, k .. "[" .. key .. "]"); end end end end local t = { '======== Lib:Dump Content ========' }; __dump(v, t); print(table.concat(t, "\n")); end local server = require "resty.websocket.server" local redis = require "resty.redis" local cjson = require "cjson" ngx.log(ngx.ERR, "HH") local function exit() --获取URL参数 local _GET = ngx.req.get_uri_args() ngx.log(ngx.ERR, "用户" .. _GET['rnd'] .. " 离开了房间 : ", err) --if is_ws == nil then ngx.eof() end ngx.flush(true) ngx.exit(ngx.HTTP_OK) return nil end local ok, err = ngx.on_abort(exit) --注册一个函数 当客户端断开连接时执行 ngx.log(ngx.ERR,ok) if err then ngx.log(ngx.ERR,err) return exit() end --获取聊天室id local channel_id = 800 local channel_name = "chat_" .. tostring(channel_id) ngx.log(ngx.ERR, "channel_name=> ", channel_name) --create connection local wb, err = server:new { timeout = 5000, max_payload_len = 65535 } ngx.log(ngx.ERR,"创建websocket服务成功") if not wb then ngx.log(ngx.ERR, "failed to new websocket: ", err) return exit() end ngx.log(ngx.ERR,"创建了~~~") --- -创建redis实例 local getRedis = function(key) if not key then return nil end if ngx.ctx[key] then return ngx.ctx[key] end --dump('-------------创建redis实例---------------------') local red = redis:new() --red:set_timeout(5000) -- 1 sec 设置连接超时1秒 local ok, err = red:connect("127.0.0.1", 6379) if not ok then ngx.log(ngx.ERR, "failed to connect redis: ", err) end ngx.ctx[key] = red return red end local pub = function() local red = getRedis('redisfn1') --redis 订阅 local res, err = red:subscribe(channel_name) if not res then ngx.log(ngx.ERR, "failed to sub redis: ", err) wb:send_close() return exit() end -- 不断读取数据如果有值立刻发送给客户端 while true do local res, err = red:read_reply() if res then local bytes, err = wb:send_text(cjson.encode(res)) if not bytes then wb:send_close() ngx.log(ngx.ERR, "failed to send text: ", err) return exit() end end ngx.sleep(0.5) end end local co = ngx.thread.spawn(pub) --main loop while true do -- 获取数据 local data, typ, err = wb:recv_frame() -- 如果连接损坏 退出 if wb.fatal then ngx.log(ngx.ERR, "failed to receive frame: ", err) return exit() end if not data then local bytes, err = wb:send_ping() if not bytes then ngx.log(ngx.ERR, "failed to send ping: ", err) return exit() end elseif typ == "close" then break elseif typ == "ping" then local bytes, err = wb:send_pong() if not bytes then ngx.log(ngx.ERR, "failed to send pong: ", err) return exit() end elseif typ == "pong" then --ngx.log(ngx.ERR, "client ponged") elseif typ == "text" then --接收消息写入redis local red = getRedis('redisfn2') local res, err = red:publish(channel_name, data) if not res then ngx.log(ngx.ERR, " 接收消息写入redis错误 failed to publish redis: ", err) end else break end ngx.sleep(0.5) end getRedis('redisfn1'):set_keepalive(10000, 100) getRedis('redisfn2'):set_keepalive(10000, 100) wb:send_close() ngx.thread.wait(co)
3.页面
<html> <head> <title>lua_socket方式做聊天</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta content="width=device-width, initial-scale=1.0, user-scalable=no" name="viewport" /> </head> <body> <script type="text/javascript"> var ws = null; function WebSocketConn() { if (ws != null && ws.readyState == 1) { log("已经在线"); return } if ("WebSocket" in window) { // ws = new WebSocket("ws://www.im.cn:80/test/websocket?rnd="+(Math.ceil(Math.random()*1000)) ); ws = new WebSocket("ws://127.0.0.1:8009/test/websocket?rnd="+(Math.ceil(Math.random()*1000)) ); ws.onopen = function() { log('成功进入聊天室'); }; ws.onmessage = function(event) { log(event.data) }; ws.onclose = function() { // websocket is closed. log("已经和服务器断开"); }; ws.onerror = function(event) { console.log("error " + event.data); }; } else { // The browser doesn't support WebSocket alert("你的浏览器不支持 WebSocket!"); } } function SendMsg() { if (ws != null && ws.readyState == 1) { var msg = document.getElementById('msgtext').value; ws.send(msg); } else { log('请先进入聊天室'); } } function WebSocketClose() { if (ws != null && ws.readyState == 1) { ws.close(); log("发送断开服务器请求"); } else { log("当前没有连接服务器") } } function log(text) { var li = document.createElement('li'); li.appendChild(document.createTextNode(text)); document.getElementById('log').appendChild(li); return false; } </script> <div id="sse"> <code class="language-html hljs "> <a href="#" onclick="WebSocketConn();">进入聊天室</a> <a href="#" onclick="WebSocketClose();">离开聊天室</a> </code> <br /> <br /> <input id="msgtext" type="text" value=""/><br /> <a onclick="SendMsg();">发送信息</a><br /> <ol id="log"></ol> </div> </body> </html>
4.运行结果:
相关文章推荐
- 基于node的websocket学习笔记三:scoket.io基础与利用scoket.io构建聊天程序示例
- 【微信支付】分享一个失败的案例 跨域405(Method Not Allowed)问题 关于IM的一些思考与实践 基于WebSocketSharp 的IM 简单实现 【css3】旋转倒计时 【Html5】-- 塔台管制 H5情景意识 --飞机 谈谈转行
- 利用websocket,go语言和h5做了一个简单的聊天功能
- 利用python写一个简易的爬虫,基于慕课网对应课程
- 一个基于netty的websocket聊天demo
- 基于Java Socket实现一个简易在线聊天功能(一)
- 用java做一个自己的基于XMPP协议IM(即时通讯)
- 给即时通讯IM添加一个自动聊天机器人
- 基于html5 WebSocket和WebRTC实现IM和视音频呼叫(一)
- Android基于XMPP Smack Openfire下学习开发IM(四)单人聊天和多人聊天(发送消息、接收消息)
- lua-resty-yii一个基于OpenResty的仿Yii的web框架
- Unity利用CardBoardVR制作(二)一个简易的全景相册和视频
- 开发一个基于React Native的简易demo--视频组件+布局
- Android客户端基于XMPP的IM(openfire+asmack)的聊天工具之环境搭建及与服务器建立连接(一)
- C# WPF 基于Socket的企业聊天软件IM(源码)
- 【转】iOS基于WebSocket的聊天机制
- 利用Dockerfile构建一个基于CentOS 7镜像参考
- C++利用链表模板类实现一个简易队列
- 网络编程应用:基于TCP协议【实现一个聊天程序】
- 基于WebServices简易网络聊天工具的设计与实现