Current Lua downloadable firmware will be posted here

User avatar
By GeoReb
#83776 I want to use NodeMCU to act as a websocket server to 1 or more browser clients.

Luckily, there is code to do this here:
https://github.com/moononournation/nodemcu-webide/blob/master/bin/httpserver-websocket.lua
(courtesy of @creationix and/or @moononournation)

This works as described and I am able to send a message from the client to the NodeMCU server, which then responds based on the received message. Great.

My questions are:

1: How can I send messages to the client without it having to be sent as a response to a client request (standalone sending of data)? When I try to call socket.send() socket is not found as a variable, which I understand, but cannot work out how to do it! :( Maybe it's because socket = {} is declared locally?

2: Why does the decode() function output the extra variable? What is this for? I'm assuming it will be for packet overflow, but I can never seem to make it return anything, regardless of my message length.

3: In the listen method, why has the author added a queuing system? is this essential or for applications that perhaps may receive multiple simultaneous messages? Ideally, I'd like to remove it.

I have simplified the code as below:

(excluding the decode() and encode() functions - please see the link above for the full script)

Code: Select allnet.createServer(net.TCP):listen(80, function(conn)

  local buffer = false
  local socket = {}
  local queue = {}
  local waiting = false

  local function onSend()
    if queue[1] then
      local data = table.remove(queue, 1)
      return conn:send(data, onSend)
    end
    waiting = false
  end

  function socket.send(...)
    local data = encode(...)
    if not waiting then
      waiting = true
      conn:send(data, onSend)
    else
      queue[#queue + 1] = data
    end
  end

  conn:on("receive", function(_, chunk)

    if buffer then
      buffer = buffer .. chunk
      while true do
        local extra, payload, opcode = decode(buffer)

        if opcode==8 then
          print("Websocket client disconnected")
        end

        --print(type(extra), payload, opcode)
        if not extra then return end
        buffer = extra
        socket.onmessage(payload, opcode)
      end
    end

    local _, e, method = string.find(chunk, "([A-Z]+) /[^\r]* HTTP/%d%.%d\r\n")
    local key, name, value

    for name, value in string.gmatch(chunk, "([^ ]+): *([^\r]+)\r\n") do
      if string.lower(name) == "sec-websocket-key" then
        key = value
        break
      end
    end

    if method == "GET" and key then

      acceptkey=crypto.toBase64(crypto.hash("sha1", key.."258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))

      conn:send(
        "HTTP/1.1 101 Switching Protocols\r\n"..
        "Upgrade: websocket\r\nConnection: Upgrade\r\n"..
        "Sec-WebSocket-Accept: "..acceptkey.."\r\n\r\n",
        function ()

          print("New websocket client connected")

          function socket.onmessage(payload,opcode)
            socket.send("GOT YOUR DATA", 1)
            print("PAYLOAD = "..payload)
            --print("OPCODE = "..opcode)
          end

        end)
      buffer = ""
    else
      conn:send(
        "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 12\r\n\r\nHello World!",
        conn.close)
    end

  end)
end)


Any help would be much appreciated! Thanks :)