我从开源项目(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. jstl 标签 的应用

    参考文档:http://www.yiibai.com/jsp/jstl_core_choose_tag.html 1.jstl中foreach序号 <c:forEach items=" ...

  2. C# 用this修饰符为原始类型扩展方法

    特点:1.静态类 2.静态方法 3.第一个参数前加this 例如:public static List<T> ToList<T>(this string Json),就是为th ...

  3. mysql workbench 导出建表语句

    导出建表语句和数据 server—–>data export—–>选中表  注意,export to dump project folder,这种方式是每个表对应一个sql文件.  导出建 ...

  4. bzoj 2153: 设计铁路

    2153: 设计铁路 Time Limit: 5 Sec  Memory Limit: 259 MBSubmit: 295  Solved: 172[Submit][Status][Discuss] ...

  5. 【Java TCP/IP Socket】应用程序协议中消息的成帧与解析(含代码)

    程序间达成的某种包含了信息交换的形式和意义的共识称为协议,用来实现特定应用程序的协议叫做应用程序协议.大部分应用程序协议是根据由字段序列组成的离散信息定义的,其中每个字段中都包含了一段以位序列编码(即 ...

  6. 【Gradle】配置中引用的jar包版本后面自动加冒号导致引入jar包失败的问题/gradle中引用jar包版本不一致的问题/gradle中引用jar失败的问题 解决方法

    idea中 gradle中 引用jar包,版本后面默认加:的问题 gradle中引用jar包版本不一致的问题 gradle中引用jar失败的问题 如上题目所示,三个问题其实都是同一样的简单又恶心,因为 ...

  7. init.rc文件中面启动c++程序,通过jni调用java实现

    </pre><p>注:假设是自己的myself.jar包,还要修改例如以下:</p><p>target/product/core_base.mk PRO ...

  8. IntelliJ IDEA cannot resolved 处理

    IntelliJ IDEA cannot resolved 处理 学习了:https://stackoverflow.com/questions/21577573/intellij-idea-can- ...

  9. Linux温习(三)Linux文件和文件夹管理

    关于Linux文件夹的几个常见概念 路径 对文件位置信息的描写叙述机制.是指从树型文件夹中的某个文件夹层次到其内某个文件的一条通路.分为相对路径和绝对路径: 工作文件夹 登入系统后.用户始终处于某个文 ...

  10. jQuery -&gt; 获取后代元素的三种方法

    假设我们有内容例如以下的html文件,那么怎样选取包括在<p>元素内的<i>元素呢? 邪馬台国の謎と弥生時代 紀元前1000年ごろ.水稲工作の技術をもつ集団が大挙して日本に移住 ...