【LuatOS】基于WebSocket的同步请求框架

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 后记

  • 己欲立而立人,己欲达而达人。
相关推荐
用户095367515832 小时前
在系统的学习 redis 前的疑惑
redis·go
星夜夏空992 小时前
网络编程(1)
服务器·网络
上海云盾-小余2 小时前
中小站点防护避坑:低价高防服务存在的各类安全短板剖析
网络·爬虫·安全·ddos
X7x54 小时前
跨厂商路由引入(重发布)实战指南:华为、华三、思科的逻辑差异与配置避坑
网络协议·信息与通信·信号处理·实施路由引入
数据知道5 小时前
邮件安全实战:SPF/DKIM/DMARC 配置与钓鱼绕过
网络·安全·web安全·网络安全·邮件安全
玩转4G物联网5 小时前
FS800DTU 新品上线|免费使用 DMP 设备管理平台,支持远程批量配置 & OTA 升级
物联网·网络协议·tcp/ip·云平台·核心板·fs800dtu·iot管理平台
王莎莎-MinerU5 小时前
MCP 解决的是工具接入,科研 Agent 还缺的是科学证据接口标准化
开发语言·网络·人工智能·深度学习·pdf·c#·php
Chloeis Syntax5 小时前
JAVAEE初阶 --- 构造HTTP请求
网络·网络协议·http·postman
砚凝霜6 小时前
软考网络工程师|第 1 章 计算机网络基础 完整备考笔记
网络·笔记·计算机网络
rcms152702692186 小时前
NIKON 4S019-394 接口模块
网络