0x00 缘起
由于使用LuatOS PC模拟器发起快速且海量HTTP请求(1000 次/秒)时,会耗尽PC的TCP连接资源,而无法进行继续进行访问请求。故使用WebSocket搭建类似于HTTP的"同步请求相应"的通信框架,以实现与HTTP类似的功能。
0x01 服务端
服务端使用Go搭建WebSocket服务器,其代码示意如下:
go
package main
import (
"log"
"net"
"net/http"
"time"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
// HandleEchoHello 进行简单回应
func HandleEchoHello(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
fmt.Println("升级到WebSocket失败:", err)
return
}
defer conn.Close()
for {
_, message, err := conn.ReadMessage()
if err != nil {
fmt.Println("读取消息失败:", err)
break
}
receiveTimestamp := time.Now().UnixNano()
sendTimestamp, err := strconv.ParseInt(string(message), 10, 64)
if err != nil {
sendTimestamp = 0
}
delayTimestampNs := float64(receiveTimestamp - sendTimestamp)
delayTimestampMs := delayTimestampNs / 1000000.0
res := "sendTime: " + string(message) + ", " + "receiveTime:" + strconv.FormatInt(receiveTimestamp, 10) + ", delay: " + strconv.FormatFloat(delayTimestampMs, 'f', -1, 64) + "ms"
// 返回值
r, _ := json.Marshal(map[string]interface{}{
"message": res,
"statusCode": 200,
})
// 发送回客户端的消息
err = conn.WriteMessage(websocket.TextMessage, r)
if err != nil {
fmt.Println("发送消息失败:", err)
break
}
}
}
func main() {
http.HandleFunc("/", HandleEchoHello)
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Panic("WebSocket服务器启动失败:", err.Error())
return
}
log.Println("WebSocket服务器运行在:http://localhost:8080")
}
上述Go代码在8080端口开启了一个WebSocket服务器等待连接,客户端连接服务器后可以发送自己的纳秒时间戳测试其传输时间,若LuatOS PC模拟器不支持纳秒,可以为LuatOS PC模拟器添加高精度时间戳获取功能或者使用Lua Socket所提供的微秒。
0x02 客户端
以下代码是使用WebSocket所封装的同步请求框架,其在发送数据后必须等到服务器返回数据后才会进行下一次发送作业。
lua
-- SyncWebScoket.lua
local SWS = {}
SWS.__index = SWS
local tag = "SyncWebScoket"
function SWS.create(address, port)
local wsc_recv_buffer = {}
local wsc_send_success_tag = false
local wsc_recv_success_tag = false
local wsc = nil
local connected = false
local self = setmetatable({}, SWS)
function self:connect()
if connected then
log.info(tag, "Already connected.")
return connected
end
log.info(tag, "Connecting to " .. address .. ":" .. port)
if not wsc then
wsc = websocket.create(nil, address .. ":" .. port)
wsc:on(function(wsc, event, data, fin, optcode)
if event == "recv" then
table.insert(wsc_recv_buffer, data)
if fin == 1 then
wsc_recv_success_tag = true
end
end
if event == "conack" then
connected = true
end
if event == "disconnect" then
connected = false
end
if event == "sent" then
wsc_send_success_tag = true
end
end)
end
wsc:connect()
local count = 0
while not connected do
sys.wait(100)
count = count + 1
if count >= 10 then
break
end
end
if connected then
log.info(tag, "Connected")
wsc:autoreconn(true, 3000)
else
log.error(tag, "Failed To Connect.")
end
return connected
end
-- 断开连接方法
function self:disconnect()
log.info(tag, "Disconnecting from " .. address .. ":" .. port)
wsc:autoreconn(false)
wsc:close()
wsc = nil
connected = false
return true
end
function self:send(data)
if not connected then
log.error(tag, "Cannot send data, not connected.")
return false
end
wsc_recv_buffer = {}
wsc_send_success_tag = false
wsc_recv_success_tag = false
wsc:send(data)
local count = 0
while not wsc_send_success_tag do
sys.wait(1)
count = count + 1
if count >= 1000 then
break
end
end
if not wsc_send_success_tag then
log.error(tag, "Send Data Failed, Please Check The Internet.")
return false
end
local count = 0
while not wsc_recv_success_tag do
sys.wait(1)
count = count + 1
if count >= 1000 then
break
end
end
if not wsc_recv_success_tag then
log.error(tag, "Receive Data Failed.")
return false
end
local message = ""
for _, value in ipairs(wsc_recv_buffer) do
if type(value) ~= "string" then
log.error(tag, "Error parsing WebSocket data!")
return false
end
message = message .. value
end
return true, message
end
return self
end
return SWS
上述代码为基于WebSocket的同步请求框架SyncWebSocket,在main.lua的使用如下:
lua
-- main.lua
PROJECT = "sync_websocket_test"
VERSION = "1.0.0"
_G.sys = require("sys")
local TAG = PROJECT
local swc = require("SyncWebScoket")
Host = "127.0.0.1"
Port = "8080"
local swsc = swc.create(Host, Port)
sys.taskInit(function()
sys.waitUntil("IP_READY") -- 默认都等到联网成功
-- 连接SyncWebSocket
while not swsc:connect() do
sys.wait(100)
end
log.info(TAG, "SWSC 连接成功!")
sys.publish("swsc_conok")
end)
sys.taskInit(function()
sys.waitUntil("swsc_conok")
log.info(TAG, "开始服务端与客户端传输时间测试...")
local startTime = timeplus.getnanosecond()
for i = 1, 10000, 1 do -- 发送10次,测试一下传输时间
local result, message = swsc:send(timeplus.getnanosecond())
if result then
log.info(TAG, tostring(i) .. " Message: " .. message)
end
end
local endTime = timeplus.getnanosecond()
-- 计算时间差并转换为毫秒
local startTimeNum = tonumber(startTime)
local endTimeNum = tonumber(endTime)
-- 计算差值并转换为毫秒
local timeDifferenceMs = (endTimeNum - startTimeNum) / 1000000000.0
print("consumeTime: " .. endTime .. "-" .. startTime .. "=" .. tostring(timeDifferenceMs) .. "s")
end)
sys.run()
上述main.lua代码展示了SyncWebSocket框架的使用方法,其中timeplus.getnanosecond()
是为LuatOS编译添加的获取纳秒字符串的方法,可以替换成其他时间戳库进行测试,客户端以及服务端代码仅作为思路说明,不对其运行效果进行保证。
0x03 资源
0x04 后记
- 己欲立而立人,己欲达而达人。