我从开源项目(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的更多相关文章

  1. 接入WebSocket记录

    为什么用 WebSocket 因为APP里面有个聊天功能,需要服务器主动推数据到APP.HTTP 通信方式只能由客户端主动拉取,服务器不能主动推给客户端,如果有实时的消息,要立刻通知客户端就麻烦了,要 ...

  2. 接入WebSocket

    闲扯 WebSocket 以前没用过,之前写过一篇博客是基于原生socket的(查看)比较复杂,慎入.今天另外一个APP需要接websocket了,然后便找到了facebook的 SocketRock ...

  3. WebSocket实践——Java实现WebSocket的两种方式

    什么是 WebSocket? 随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了.近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通信 ...

  4. $微信小程序开发实践点滴——接入Bmob后端云

    Bmob后端云官网:http://www.bmob.cn/ 微信公众平台官网:https://mp.weixin.qq.com/ 微信小程序官方开发文档:https://mp.weixin.qq.co ...

  5. 接入WebSocket记录 + 一些个人经验

    闲扯 WebSocket 以前没用过,之前写过一篇博客是基于原生socket的(查看)比较复杂,慎入.今天另外一个APP需要接websocket了,然后便找到了facebook的 SocketRock ...

  6. skynet实践(9)-随机数重复问题

    最近在使用skynet的过程中,遇到需要为玩家的每次请求产生一个随机序列的场景.简化如下: main.lua中每隔1S便发出一次随机数请求: local skynet = require " ...

  7. 微信小程序之WebSocket

    本文版权归 OSChina jsongo0 所有,转载请标明出处,以示尊重! 原文:https://my.oschina.net/jsongo/blog/757871 为什么需要websocket?传 ...

  8. springboot启动抛出javax.websocket.server.ServerContainer not available

    问题描述:spring boot接入websocket时,启动报错:javax.websocket.server.ServerContainer not available <dependenc ...

  9. 微信小程序学习指南

    作者:初雪链接:https://www.zhihu.com/question/50907897/answer/128494332来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明 ...

随机推荐

  1. 转自CSDN,关于状态机

    有限状态机FSM思想广泛应用于硬件控制电路设计,也是软件上常用的一种处理方法(软件上称为FMM--有限消息机).它把 复杂的控制逻辑分解成有限个稳定状态,在每个状态上判断事件,变连续处理为离散数字处理 ...

  2. 2017-10-28-morning-清北模拟赛

    T1 立方数(cubic) Time Limit:1000ms   Memory Limit:128MB 题目描述 LYK定义了一个数叫“立方数”,若一个数可以被写作是一个正整数的3次方,则这个数就是 ...

  3. P1111 修复公路 洛谷

    https://www.luogu.org/problem/show?pid=1111 题目背景 A地区在地震过后,连接所有村庄的公路都造成了损坏而无法通车.政府派人修复这些公路. 题目描述 给出A地 ...

  4. CAN 和 CANopen的区别和联系

    1.CAN与CANopen的共同点与不同点:  CAN只定义了物理层与链路层,而没有定义用户层,用户可根据自己的需要定义一些网络上的通信约定:  CANopen是在CAN的基础上定义了用户层,即规定了 ...

  5. 从Java看数据结构之——树和他的操作集

    写在前面 树这种数据结构在计算机世界中有广泛的应用,比如操作系统中用到了红黑树,数据库用到了B+树,编译器中的语法树,内存管理用到了堆(本质上也是树),信息论中的哈夫曼编码等等等等.而树的实现和他的操 ...

  6. Go -- 如何使用gcore工具获取一个core文件而不重启应用?

    问题: 当调试一个程序的时候,理想状态是不重启应用程序就获取core文件. 解决: gcore命令可以使用下面步骤来获取core文件: 1. 确认gdb软件包已经被正确安装. 2. 使用调试参数编译程 ...

  7. vim修改二进制文件

    先用vim以二进制格式打开需要编辑或查看的文件,不采用-b参数有时会导致转换错误,详见分隔线后部分.    vim -b file-to-open.dat 然后用xxd把文件转换成十六进制格式 :%! ...

  8. ZT:与其怨天尤人,不如全力以赴;若想改变世界,你必须先从改变自己开始!

    在闻名世界的威斯特敏斯特大教堂地下室的墓碑林中,有一块名扬世界的墓碑.其实这只是一块很普通的墓碑,粗糙的花岗石质地,造型也很一般,同周围那些质地上乘.做工优良的亨利三世到乔治二世等二十多位英国前国王墓 ...

  9. EBS OAF开发中怎样实现功能页签(Global Tab)

    EBS OAF开发中怎样实现功能页签(Global Tab) (版权声明.本人原创或者翻译的文章如需转载.如转载用于个人学习,请注明出处.否则请与本人联系,违者必究) 功能页签的实现不须要不论什么编码 ...

  10. react 创建组件 (三)PureComponet

    我们知道,当组件的props或者state发生变化的时候:React会对组件当前的Props和State分别与nextProps和nextState进行比较,当发现变化时,就会对当前组件以及子组件进行 ...