skynet实践(8)-接入websocket
我从开源项目(https://github.com/lipp/lua-websockets,这里我们简称LWS)中抽出了websocket的部分处理,步骤如下:
1)首先是解决LWS的几个依赖问题。LWS在握手阶段的base64编解码使用了luasocket中的mime,因此在3rd文件夹中建立mime文件夹,将mime.h和mime.c文件放进去,修改skynet的Makefile文件,作为一个单独的动态库编译:
LUA_CLIB = skynet \
client \
bson md5 sproto lpeg mime
$(LUA_CLIB_PATH)/mime.so : 3rd/mime/mime.c | $(LUA_CLIB_PATH)
$(CC) $(CFLAGS) $(SHARED) -I3rd/mime $^ -o $@
在tools.lua中引用了mime,由于我们只用了C文件,因此修改require "mime" 为: require "mime.core" 。
对于luabitop,skynet自带的lua5.3源码已经支持(在lbitlib.c文件中),但是默认是关闭的,修改lua5.3的Makefile,增加LUA_COMPAT_BITLIB宏并重新编译:
MYCFLAGS=-I../../skynet-src -g -DLUA_COMPAT_BITLIB
这样在bit.lua中对bit32就能正确引用了。
2)下一步则是在skynet端侦听tcp连接:
local skynet = require "skynet"
local socket = require "skynet.socket" skynet.start(function()
local id = socket.listen("0.0.0.0", )
skynet.error("web server listen on web port 8001") socket.start(id, function(id, addr)
local agent = skynet.newservice("wsagent")
skynet.error(string.format("%s connected, pass it to agent :%08x", addr, agent))
skynet.send(agent, "lua", id, "start")
end)
end)
这里每次侦听到一个新连接后就建立wsagent,后续的握手及数据传输一并交给其处理。
3)在lualib下建立websocket文件夹,放入handshake.lua(握手协议)、frame.lua(帧数据格式解析)、bit.lua、tools.lua。在wsagent.lua中实现握手、数据传输、关闭连接如下:
local skynet = require "skynet"
local socket = require "skynet.socket"
local frame = require "websocket.frame"
local handshake = require "websocket.handshake"
local sockethelper = require "http.sockethelper" local agent = { }
local REQUEST = { }
local FD, read, write local function _send(message)
local encoded = frame.encode(message, frame.TEXT)
write(encoded)
end local function _handshake(fd)
FD = fd
read = sockethelper.readfunc(fd)
write = sockethelper.writefunc(fd) local header = ""
while true do
local bytes = read()
header = header .. bytes
if #header > then
skynet.error("<websocket.handshake>error: header size > 8192")
return
end local _, to = header:find("\r\n\r\n", -#bytes-, true)
if to then
header = header:sub(, to)
break
end
end print("accept handshake http request:" .. header) local protocols = { } -- todo: how to set protocols?
local response, protocol = handshake.accept_upgrade(header, protocols)
if not response then
skynet.error("<websocket.handshake>error: handshake parse header fault")
return
end print("send handshake http response:" .. response) write(response)
skynet.error(string.format("<websocket.handshake>web socket %q connection established", fd)) return true
end local function _close()
local encoded = frame.encode_close(, 'force close')
encoded = frame.encode(encoded, frame.CLOSE) print("force close:" .. encoded) write(encoded)
socket.close(FD)
end local function _dispatch(text, opcode)
print(string.format("<websocket>opcode:%q message:%q", opcode, text)) local TEXT = assert(frame.TEXT)
local CLOSE = assert(frame.CLOSE)
assert(opcode == TEXT or opcode == CLOSE, opcode) if opcode == TEXT then
-- todo: logic
return true
end if opcode == CLOSE then
local code, reason = frame.decode_close(message)
print(string.format("<websocket>CLOSE code:%q reason:%q", code, reason))
local encoded = frame.encode_close(code)
encoded = frame.encode(encoded, frame.CLOSE) local ok, err = pcall(write, encoded)
if not ok then
-- remote endpoint may has closed tcp-connection already
skynet.error("write close protocol failure:" .. tostring(err))
end
socket.close(assert(FD))
end
end local function _recv()
local last
local frames = {}
local first_opcode while true do
-- skynet will report error and close socket if socket error (see socket.lua)
local encoded = read()
if last then
encoded = last .. encoded
last = nil
end repeat
local decoded, fin, opcode, rest = frame.decode(encoded)
if decoded then if not first_opcode then
first_opcode = opcode
end
table.insert(frames, decoded)
encoded = rest
if fin == true then
if not _dispatch(table.concat(frames), first_opcode) then
-- socket closed in [_dispatch]
return
end
frames = { }
first_opcode = nil
end
end
until (not decoded) if #encoded > then
last = encoded
end
end
end function agent.start(fd)
socket.start(fd) skynet.error("<websocket>start handshake")
if not _handshake(fd) then
socket.close(fd)
skynet.exit()
return
end skynet.error("<websocket>receive and dispatch")
_recv() skynet.error("<websocket>exit")
skynet.exit()
end skynet.start(function()
skynet.dispatch("lua", function (_, _, fd, method, ...)
local f = assert(agent[method])
skynet.retpack(f(fd, ...))
end)
end)
代码我已放到git上:https://github.com/zhoujijian/skynet-websocket
skynet实践(8)-接入websocket的更多相关文章
- 接入WebSocket记录
为什么用 WebSocket 因为APP里面有个聊天功能,需要服务器主动推数据到APP.HTTP 通信方式只能由客户端主动拉取,服务器不能主动推给客户端,如果有实时的消息,要立刻通知客户端就麻烦了,要 ...
- 接入WebSocket
闲扯 WebSocket 以前没用过,之前写过一篇博客是基于原生socket的(查看)比较复杂,慎入.今天另外一个APP需要接websocket了,然后便找到了facebook的 SocketRock ...
- WebSocket实践——Java实现WebSocket的两种方式
什么是 WebSocket? 随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了.近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通信 ...
- $微信小程序开发实践点滴——接入Bmob后端云
Bmob后端云官网:http://www.bmob.cn/ 微信公众平台官网:https://mp.weixin.qq.com/ 微信小程序官方开发文档:https://mp.weixin.qq.co ...
- 接入WebSocket记录 + 一些个人经验
闲扯 WebSocket 以前没用过,之前写过一篇博客是基于原生socket的(查看)比较复杂,慎入.今天另外一个APP需要接websocket了,然后便找到了facebook的 SocketRock ...
- skynet实践(9)-随机数重复问题
最近在使用skynet的过程中,遇到需要为玩家的每次请求产生一个随机序列的场景.简化如下: main.lua中每隔1S便发出一次随机数请求: local skynet = require " ...
- 微信小程序之WebSocket
本文版权归 OSChina jsongo0 所有,转载请标明出处,以示尊重! 原文:https://my.oschina.net/jsongo/blog/757871 为什么需要websocket?传 ...
- springboot启动抛出javax.websocket.server.ServerContainer not available
问题描述:spring boot接入websocket时,启动报错:javax.websocket.server.ServerContainer not available <dependenc ...
- 微信小程序学习指南
作者:初雪链接:https://www.zhihu.com/question/50907897/answer/128494332来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明 ...
随机推荐
- SGU 107 数学题
题意:求平方后末尾9个数是987654321的数个数. 之前做此题,竟然愚蠢到用计算器 在哪里算,还加笔算,SB啊!不知道先打印一下吗! #include<iostream> #inclu ...
- Jenkins持续部署-Windows环境持续部署探究1
目录 Jenkins持续部署-Windows环境持续部署探究1 目录 前言 目的 方案流程 技术实现 PowerShell FTP上传插件 环境变量插件 脚本执行 远程调用 升级服务 启动服务 总结 ...
- noip2013货车运输
P1967 货车运输 题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过 ...
- noip2013华容道
题目描述 [问题描述] 小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面, 华容道是否根本就无法完成,如果能完成, 最少需要多少时间. 小 ...
- DICOM医学图像显示算法改进与实现——LUT
引言 随着Ul(超声成像).CT(计算机断层成像).MRI(核磁共振成像).CR(计算机X线成像).电子内窥镜.盯(正电子发射断层成像)和MI(分子影像)等医学影像设备不断涌现,利用计算机对医学影像设 ...
- LSA、LDA
Latent semantic analysis (LSA) is a technique in natural language processing, in particular distrib ...
- 忘掉VNC/RDP,拿起手中的MobaXterm轻松上手远程桌面
前言 原创文章,转载引用务必注明链接. 这期属于番外篇,随便写写.吐槽一下自己,关于"减少eMMC擦写,延长NAND寿命提升性能"的坑还没填完,都不好愉快地写点轻松的小项目灌水了. ...
- 第一个MapReduce程序——WordCount
通常我们在学习一门语言的时候,写的第一个程序就是Hello World.而在学习Hadoop时,我们要写的第一个程序就是词频统计WordCount程序. 一.MapReduce简介 1.1 MapRe ...
- 让你的eclipse实现写JAVA代码,HTML,CSS,JAVASCRIPT代码提示
1.打开eclipse→Windows→Preferences→Java→Editor→Content Assist 改动Auto Activation triggers for java的值为:zj ...
- C++ Primer 学习笔记与思考_7 void和void*指针的使用方法
(一)void的含义 void的字面意思是"无类型",void差点儿仅仅有"凝视"和限制程序的作用,由于从来没有人会定义一个void变量,让我们试着来定义: v ...