1.配置文件javascript
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的订阅/发布来实现消息的接收与发送html
--[[ - @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.页面java
<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.运行结果:nginx