我从开源项目(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. 多线程环境下 cpu % 分析

    1. top -H(查看阻塞进程,线程) 2. jstack  pid(查看堆栈信息) 另附 利用 Java dump 进行 JVM 故障诊断 http://www.blogjava.net/yuwe ...

  2. code forces 1051 d

    看的这个题解:http://www.cnblogs.com/tobyw/p/9685639.html 写的比较清楚. 矩阵类型的计数题 比赛时感觉就像是个dp,然后就跳过了. 现在看着题解写一下,感觉 ...

  3. VMware View 要求操作句柄的状态错误

    win10系统安装的VMware-viewclient,版本是

  4. Oracle 12c安装报错Installation failed to access the temporary location(无法访问临时位置)

    报错如图 1.先检查当前windows账户用户名是否为全英文,没有就新建一个,大多数用户败在这一步,而官方也没有解释 如何新建:开始-->控制面板-->用户账户和家庭安全-->用户账 ...

  5. Flutter学习(一)——搭建开发环境(Windows)

    久闻 Flutter 大名,今天终于有时间体验一下了 ٩(๑>◡<๑)۶ 官网:https://flutter.dev/ 中文官网:https://flutterchina.club/ 一 ...

  6. 【Todo】Java类面试题分析

    Java 面试中的重要话题 多线程,并发及线程基础数据类型转换的基本原则垃圾回收(GC)Java 集合框架数组字符串GOF 设计模式SOLID (单一功能.开闭原则.里氏替换.接口隔离以及依赖反转)设 ...

  7. ZOJ ACM 1314(JAVA)

    昨天做了几个题目.过于简单,就不在博客里面写了. 1314这道题也比較简单,写出来是由于我认为在这里有一个小技巧,对于时间复杂度和空间复杂度都比較节省. 这个题目类似哈希表的求解.可是更简单.刚拿到题 ...

  8. AngularJs概述

  9. 【转载】5种网络IO模型

    同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别?这个问题其实不同的人给出 ...

  10. 使用 C# 开发智能手机软件:推箱子(四)

    这是"使用 C# 开发智能手机软件:推箱子"系列文章的第四篇. 在这篇文章中,介绍 Common/FindPath.cs 源程序文件. using System; using Sy ...