Client/Server UDP within a thread.

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
FlyingDojo
Prole
Posts: 7
Joined: Tue Feb 09, 2021 9:41 pm

Re: Client/Server UDP within a thread.

Post by FlyingDojo »

Ok, I've made amendments to the code as you all have suggested, I'm not really sure how to go about fixing the race condition issue, if it happens or not I don't even know. On the server main file, I had incorrectly written the err line to check "msg" instead of "message", soon as I changed that it threw an unknown error. I did add

Code: Select all

socket.sleep(0.1)
to the server as CPU usage was 100% when it was running, as the thread had tonnes of errors pushed to it, I didn't know as the error checking line was wrong, they were the errors from

Code: Select all

socket.timeout(0)
. I've removed that and now nothing gets pushed to the server message thread but I think that's because I'm not receiving anything unlike before it was lots of errors. I've added

Code: Select all

udp:setsockname ('*', 65000)
to the client but it still doesn't send anything for me. Well I'm assuming it isn't sending anything, the server could be not pushing anything as grump stated which would seem like the client is not sending nor the server receiving anything.

This is the client main.lua now:

Code: Select all

local socket = require("socket")
local ipaddr  = "192.168.1.71"
local udp = socket.udp()
udp:setsockname('*', 65000)
local sendit

function love.load()

end


function love.keypressed(key)
  if key == 'g' then
      sendit = not sendit
  end
end

function love.update(dt)
  if sendit then
     result, err = udp:sendto("Message", ipaddr, 65000)
  end

end

function love.draw()
  local delta = love.timer.getAverageDelta()
  -- Display the frame time in milliseconds for convenience.
  -- A lower frame time means more frames per second.
  love.graphics.print(string.format("Average frame time: %.3f ms", 1000 * delta), 0, 0)
  fps = love.timer.getFPS( )
  love.graphics.print("Current FPS: "..tostring(love.timer.getFPS( )), 0, 15)

  love.graphics.print("Client: "..ipaddr, 0, 35)

  if sendit then
    love.graphics.print("sending",50,50)
    love.graphics.print(result..err,50,80)
  end
end
I get
attempt to concatenate global 'result' (a nil value)
, I did set result and err to "0" and it printed it fine so it looks like sendto isn't working at all?

Just so we all on the same page, here is server main.lua:

Code: Select all

local socket = require("socket")

function love.load()
  serverThread = love.thread.newThread("listen.lua")
  serverThread:start()
  local err = serverThread:getError()

  address = "Server Address:  " .. socket.dns.toip(socket.dns.gethostname())

end

function love.keypressed(key)
  if key == 's' then

  elseif key == 'q' then
    love.event.quit(exitstatus)
  elseif key == 'r' then
    love.event.quit( "restart" )
  end
end

function love.update(dt)

  local message = love.thread.getChannel('messages'):pop() --set to peek not pop so the first sent it shown
  local ip = love.thread.getChannel('ips'):pop()
  local port = love.thread.getChannel('ports'):pop()


  assert( not err, error )
end

function love.draw()
  local delta = love.timer.getAverageDelta()
  -- Display the frame time in milliseconds for convenience.
  -- A lower frame time means more frames per second.
  love.graphics.print(string.format("Average frame time: %.3f ms", 1000 * delta), 0, 0)
  fps = love.timer.getFPS( )
  love.graphics.print("Current FPS: "..tostring(love.timer.getFPS( )), 0, 15)

  love.graphics.print(address, 0, 35)

  --see if were getting any messages on the thread channel
  local count = love.thread.getChannel('messages'):getCount()
  if count then
    love.graphics.print(count, 0, 100)
  end

  if message then
    love.graphics.print("Message: "..tostring(message), 0, 60)
  elseif ip then
    love.graphics.print("IP: "..tostring(ip), 0, 75)
  elseif port then
    love.graphics.print("Port: "..tonumber(port), 0, 99)
  end
end
server listen.lua:

Code: Select all

local socket = require("socket")


function listen()
    local udp = socket.udp()
    udp:setsockname ('*', 65000)
  --  udp:settimeout(20)

    while true do
        local message, ip, port = udp:receivefrom()
        love.thread.getChannel('messages'):push(message)
        love.thread.getChannel('ips'):push(ip)
        love.thread.getChannel('ports'):push(port)
    end
end

listen()
cheers, fd


EDIT: IT WORKED I derped, I saw it flash Message

Code: Select all

function love.update(dt)

  local message = love.thread.getChannel('messages'):pop() --set to peek not pop so the first sent it shown
  local ip = love.thread.getChannel('ips'):pop()
  local port = love.thread.getChannel('ports'):pop()


  assert( not err, error )
end
love.draw()

Code: Select all

  if message then
    love.graphics.print("Message: "..tostring(message), 0, 60)
  elseif ip then
    love.graphics.print("IP: "..tostring(ip), 0, 75)
  elseif port then
    love.graphics.print("Port: "..tonumber(port), 0, 99)
  end
removed the locals and it printed the message! but I closed the love windows and now it doesn't show it me... woah im so confused.

I've gone over the code to see if I changed anything and it is as posted except the locals wrote above have been removed, I still get a cocat error for the variable result.

EDIT2:

Ok so progress, if I open the server first, then open the client and hit g for it to send then it prints sending on the client window and print the number 7, which i'm assuming is a count of the word "Message". If I don't open the server window first, the client will throw me a concat error.
I'm running both these files on the same computer, would that cause this unexpected behavior?

EDIT3:
I realised that:

Code: Select all

    love.graphics.print(""..result..err,50,80)
should be

Code: Select all

    love.graphics.print(""..tostring(result)..tostring(err),50,80)
as I'm getting a concat error of it being nil but it wont print that to the screen as its not actually a string that's printable, I think you'll understand what I mean, so now, I get:

Code: Select all

nilnodename nor servname provide, or not known
on the client when trying to send data via udp WHEN the server is not open, with the server open I get:

Code: Select all

7nil
7 is the character count of the data sent "Message" and nil is because there are no errors, but nothing is now displayed on the server screen. I'm going to assume, please correct me if I'm wrong, that the client will only successfully send data if there is a UDP listener on the specified IP and port waiting for the data, if not it gives me a nodename error.
I feel like I'm getting closer...



FD
User avatar
zorg
Party member
Posts: 3470
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Client/Server UDP within a thread.

Post by zorg »

FlyingDojo wrote: Wed Feb 10, 2021 7:53 pm I did add

Code: Select all

socket.sleep(0.1)
to the server as CPU usage was 100% when it was running
Yes it was, because that's how all threads work if you don't sleep them...
however
if you check out love.run on the wiki, which is the "game loop" on the main thread, you'll see it uses love.timer.sleep to not hog a CPU core, not socket.sleep.

In short, do sleep, but with love.timer instead.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
User avatar
pgimeno
Party member
Posts: 3687
Joined: Sun Oct 18, 2015 2:58 pm

Re: Client/Server UDP within a thread.

Post by pgimeno »

It actually was because the socket was not blocking or timing out. Either should keep the CPU usage mostly idle. No need to sleep. Sleeping forces the CPU to be idle even if it has work to do, therefore it can increase latency.
FlyingDojo
Prole
Posts: 7
Joined: Tue Feb 09, 2021 9:41 pm

Re: Client/Server UDP within a thread.

Post by FlyingDojo »

Ok, I fixed the problem!
I changed:

Code: Select all

  udp:setsockname ('*', 65050)
to

Code: Select all

  udp:setsockname ('192.168.1.71', 65050)
It works completely fine, even tried my external IP on the client and port forwarded via router and it works like a charm.

No problems with the thread's pushin' n poppin' either. I'll post the code, just in case anybody needs it if they're trying to do the same thing.

Client: main.lua

Code: Select all

local socket = require("socket")
local clientIpAddr  = socket.dns.toip(socket.dns.gethostname())
local clientPort = 65000
local serverIpAddr = "192.168.1.71"
local serverPort = 65050
local udp = socket.udp()
udp:setsockname(clientIpAddr, clientPort)
local sendit
local result,err

function love.load()

end


function love.keypressed(key)
  if key == 'g' then
      sendit = not sendit
  end
end

function love.update(dt)
  if sendit then
     result,err = udp:sendto("Message",serverIpAddr,serverPort)
  end
end

function love.draw()
  local delta = love.timer.getAverageDelta()
  -- Display the frame time in milliseconds for convenience.
  -- A lower frame time means more frames per second.
  love.graphics.print(string.format("Average frame time: %.3f ms", 1000 * delta), 0, 0)
  local fps = love.timer.getFPS( )
  love.graphics.print("Current FPS: "..tostring(love.timer.getFPS( )), 0, 15)

  love.graphics.print("Client: "..clientIpAddr, 0, 35)

  if sendit then
    love.graphics.print("sending",50,50)
    love.graphics.print(""..tostring(result)..tostring(err),50,80)
  end
end
Server: main.lua

Code: Select all

local socket = require("socket")
local address = socket.dns.toip(socket.dns.gethostname())
local serverThread = love.thread.newThread("listen.lua")
serverThread:start()
local threadRunning = serverThread:isRunning( )
local err = serverThread:getError()
local ltgc = love.thread.getChannel

function love.load()

end

function love.keypressed(key)
  if key == 's' then

  elseif key == 'q' then
    love.event.quit(exitstatus)
  elseif key == 'r' then
    love.event.quit( "restart" )
  end
end

function love.update(dt)

-- todo: These are globals, they need changing to locals, add to function that adds all data to table? 
  value = ltgc('system'):pop()
  message = ltgc('messages'):pop()
  ip = ltgc('ips'):pop()
  port = ltgc('ports'):pop()
  count = ltgc('count'):pop()

  assert( not err, error )
end

function love.draw()
  local delta = love.timer.getAverageDelta()
  -- Display the frame time in milliseconds for convenience.
  -- A lower frame time means more frames per second.
  love.graphics.print(string.format("Average frame time: %.3f ms", 1000 * delta), 0, 0)
  fps = love.timer.getFPS( )
  love.graphics.print("Current FPS: "..tostring(love.timer.getFPS( )), 0, 15)

  love.graphics.print("Server Address:  " ..address, 0, 35)
  love.graphics.print("Is the server running? "..tostring(value),400,400)
  love.graphics.print("Thread running? "..tostring(threadRunning),400,450)
  --see if were getting any messages on the thread channel

    love.graphics.print("Thread repeat count: "..tostring(count), 0, 105)
    love.graphics.print("Message: "..tostring(message), 0, 60)
    love.graphics.print("IP: "..tostring(ip), 0, 75)
    love.graphics.print("Port: "..tostring(port), 0, 90)
end
listen.lua

Code: Select all

local socket = require("socket")

  local udp = socket.udp()
  udp:setsockname ('192.168.1.71', 65050)
  --udp:settimeout(nil)
  local running = true
  local num = 1

  while running do
    love.thread.getChannel('system'):push(running)
    love.thread.getChannel('count'):push(num)

    local msg, ipad, pt = udp:receivefrom()
    if msg and ipad and pt then
    love.thread.getChannel('messages'):push(msg)
    love.thread.getChannel('ips'):push(ipad)
    love.thread.getChannel('ports'):push(pt)
    end
    num = num + 1
  end
I think multiple issues were causing my problems initially, then the last one with the interface binding, not sure why * wasn't working but putting an IP explicitly works so I'll stick with that.

Thank you all for the help with my issue,
Regards FD
Post Reply

Who is online

Users browsing this forum: Bing [Bot], DrMariwaner69 and 2 guests