我从开源项目(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. MYSQL 中GROUP BY

    group by 用法解析 group by语法可以根据给定数据列的每个成员对查询结果进行分组统计,最终得到一个分组汇总表. SELECT子句中的列名必须为分组列或列函数.列函数对于GROUP BY子 ...

  2. P3367 并查集【模板】 洛谷

    https://www.luogu.org/problem/show?pid=3367#sub 题目描述 如题,现在有一个并查集,你需要完成合并和查询操作. 输入输出格式 输入格式: 第一行包含两个整 ...

  3. Spring中Bean的定义继承

    以下内容引用自http://wiki.jikexueyuan.com/project/spring/bean-definition-inheritance.html: Bean定义继承 bean定义可 ...

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

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

  5. webrtc初探

    0.闲来无事,想研究webrtc,看了一些网上的文章之后,觉得谬误较多,以讹传讹的比较多,自己试验了一把,记录一下. 官网的写的教程在实践中也觉得不用那么复杂,有种落伍与繁冗的感觉. 1.我想看的是w ...

  6. AspNet MVC4 教学-23:Asp.Net MVC4 Display And Editor 模板技术高速应用Demo

    A.创建Basic类型的项目. B.在Model文件夹下,创建3个文件: Role.cs: using System; using System.Collections.Generic; using ...

  7. 最小公倍数(Least Common Multiple)

    最小公倍数=两个数的乘积/两个数的最大公约数. 接上篇求最大公约数方法,最小公倍数的代码例如以下: public class LCM { //最小公倍数=两数乘积/最大公约数 public stati ...

  8. 《Getting Started with WebRTC》第二章 WebRTC技术介绍

    <Getting Started with WebRTC>第二章 WebRTC技术介绍 本章作WebRTC的技术介绍,主要讲下面的概念:   .  怎样建立P2P的通信   .  有效的信 ...

  9. jquery 获取下拉框 某个text='xxx'的option的属性 非选中 如何获得select被选中option的value和text和......

    jquery 获取下拉框 某个text='xxx'的option的属性 非选中 5 jquery 获取下拉框 text='1'的 option 的value 属性值 我写的var t= $(" ...

  10. 常量,字段,构造方法 调试 ms 源代码 一个C#二维码图片识别的Demo 近期ASP.NET问题汇总及对应的解决办法 c# chart控件柱状图,改变柱子宽度 使用C#创建Windows服务 C#服务端判断客户端socket是否已断开的方法 线程 线程池 Task .NET 单元测试的利剑——模拟框架Moq

    常量,字段,构造方法   常量 1.什么是常量 ​ 常量是值从不变化的符号,在编译之前值就必须确定.编译后,常量值会保存到程序集元数据中.所以,常量必须是编译器识别的基元类型的常量,如:Boolean ...