This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Kouksi44's profile picture

One Server handling multiple connections

Started by Kouksi44, 28 September 2014 - 12:10 PM
Kouksi44 #1
Posted 28 September 2014 - 02:10 PM
Hey Forum,

what I am trying to do is , that there is one single computer that works as a server for my bank system. But now it should be possible that several Clients connect to this server.

I thought of this like that:

Client connects to the Server

C—————> S

Server detects Client and assigns a modem-freqency only for this client. This freqency is sent to the Client so he knows which freqency he can use.

C <————- S

I thought that this could work with Coroutines. One coroutine waiting for Clients to connect and one handling the Clients when they have connected.

My (perhaps extremely wrong) code for this:


local main = coroutine.create(main)
local user = coroutine.create(newUser)
local evt = {}
while true do
tst=coroutine.resume(main, unpack(evt))
if tst==true then
  coroutine.resume(user, unpack(evt))
end
if coroutine.status(main) == "dead" then
  local main = coroutine.create(main)
end
evt = {os.pullEvent()}
end

And the code of my main function is :

local function main()
modem=peripheral.wrap(getSide())
modem.open(CONST_FREQ)
modem.open(CONST_RFREQ)
while true do
  local e = {os.pullEvent()}
  if e[1] == "modem_message" and e[3] == CONST_FREQ then
   table.insert(USED_FREQENCIES,tostring(math.random(128)))
   modem.transmit(CONST_FREQ, CONST_RFREQ, CONST_RHANDSHAKE)
   modem.transmit(CONST_FREQ,CONST_RFREQ,tostring(math.random))
  

   return true
  else
  end
end
end

Currently this is really far away from something that actually works.
When a Client connects with the new Freqency the Server doesn´t even connect.

My question is, if it is even possible that one Computer handles multiple connections at a time and if it is how would I have to realise this ?

I am sorry if this is a bit hard to understand or my code is just terrible :/ I am really no pro at Lua and this is my first time I am working with coroutines.
Bomb Bloke #2
Posted 28 September 2014 - 03:31 PM
You're more or less on the right track. Use of co-routines indeed provides an easy way to keep track of multiple "conversations" with different clients. It's not the only way, mind you, but I'd say it's probably the best option.

I've not really messed around with co-routines, but I took a hacksaw to the parallel API and came up with this:

Server:
rednet.open("right")

local routines = {}

local function main(channel)
	print("I got a new client on channel "..channel)
	
	while true do
		rednet.send(channel,"Password?")
		local id, message = rednet.receive()
		-- And so on.
	end
end

while true do
	local eventData, known = { os.pullEvent("rednet_message") }, false

	-- table.maxn gets the highest index in the table, full stop:
	for n=1,table.maxn(routines) do
		if routines[n] and eventData[2] == routines[n].channel then
			local ok, param = coroutine.resume( routines[n].coroutine, unpack(eventData) )
			if not ok then error( param, 0 ) end
			if coroutine.status( routines[n].coroutine ) == "dead" then routines[n] = nil end
			known = true
		end
	end

	if not known and eventData[3] == "connection_request" then
		-- #routines gets the highest index in the table before encountering a nil value:
		local n = #routines+1
		routines[n] = {["channel"] = eventData[2], ["coroutine"] = coroutine.create(function() main(eventData[2]) end)}
		local ok, param = coroutine.resume( routines[n].coroutine, {} )
		if not ok then error( param, 0 ) end  -- You shouldn't have to capture and check ok/param here unless your code is bugged, but doing so makes it easier to debug if it is.
		if coroutine.status( routines[n].coroutine ) == "dead" then routines[n] = nil end
	end
end

Clients:
rednet.open("right")

rednet.broadcast("connection_request")

local serverID, message = rednet.receive()

if message == "Password?" then
	print("Enter password:")
	-- And so on.
else
	error("Bad response")
end

The basic principles involve creating a new co-routine whenever a new system messages a "connection_request" the server, and sticking it in a table. Rednet message events from that system are then handed to that co-routine only. Hopefully you can see how to expand and/or adapt it to your own purposes.
Kouksi44 #3
Posted 28 September 2014 - 06:23 PM
Thank you very much :)/> Gonna try to understand all of your code .
Seems like I really have to take a deeper look into coroutines !
Kouksi44 #4
Posted 28 September 2014 - 07:48 PM
Uhm..

Now I´ve come up with another bug.

When I connect to the server for the first time everything works perfectly.

The Client sends its credentials to the server and the server prints them( just for testing purposes).

Buuut when I connect to the server with the same Computer again nothing is printed. I checked if anything was sent and it seems like rednet.receive() returns nothing in the second run ?
Dragon53535 #5
Posted 28 September 2014 - 07:50 PM
Post your current code so that we can look through it and test it ourselves.
Kouksi44 #6
Posted 28 September 2014 - 08:03 PM
This is the main server code:

local function main(ID)
while true do
  rednet.send(ID,CONST_HANDSHAKE)
  local id,message=rednet.receive()
  print(message.."HAllo")
  print("Es geht")
  msg=textutils.unserialize(message)
  print("Account name: "..msg.name)
  print("Account password: "..msg.password)
end
end
while true do
local eventData,known={os.pullEvent("rednet_message")},false
for n=1,table.maxn(routines) do
  if routines[n] and eventData[2]==routines[n].channel then
   local ok,param= coroutine.resume( routines[n].coroutine, unpack(eventData) )
   if not ok then error( param, 0 ) end
					    if coroutine.status( routines[n].coroutine ) == "dead" then routines[n] = nil end
					    known = true
			    end
	    end
	    if not known and eventData[3] == CONST_RHANDSHAKE then
			    -- #routines gets the highest index in the table before encountering a nil value:
			    local n = #routines+1
			    routines[n] = {["channel"] = eventData[2], ["coroutine"] = coroutine.create(function() main(eventData[2]) end)}
			    local ok, param = coroutine.resume( routines[n].coroutine, {} )
			    if not ok then error( param, 0 ) end  -- You shouldn't have to capture and check ok/param here unless your code is bugged, but doing so makes it easier to debug if it is.
			    if coroutine.status( routines[n].coroutine ) == "dead" then routines[n] = nil end
	    end
end

 

And this is the client code:

print("Enter your account name:")
local account_name=read()
os.sleep(1)
term.clear()
term.setCursorPos(CONST_xzentral,CONST_yzentral)
print("Enter your account password:")
account_pwd=read("*")
ACCOUNT_TBL={name=account_name,password=account_pwd}
local side=getSide()

rednet.open(side)
rednet.broadcast(CONST_HANDSHAKE)
print("trying to connect")
local serverID, message= rednet.receive()
 
 
 
  if  message==CONST_RHANDSHAKE
   then print("Sucessfully connected")

    rednet.send(serverID, convert(ACCOUNT_TBL))

I really don´t understand why it crashes on the second run but works perfectly when i connect for the first time ??
MKlegoman357 #7
Posted 28 September 2014 - 09:23 PM
It may be because you never close the connection. Also, you mention that it crashes. What error do you get on the second time?
Kouksi44 #8
Posted 28 September 2014 - 09:26 PM
It gives me an : attempt to index (a nil value)

I will try too see if closing the connection helps :)/>

EDIT: Nope, closing and reopening the connection doesn´t solve the problem.
Edited on 28 September 2014 - 07:29 PM
Bomb Bloke #9
Posted 29 September 2014 - 01:59 AM
Errors aren't much use if you don't post them in their entirety, line numbers and all. I'm guessing it's pegging the first line where you attempt to index into "msg"; is "message" a string that can be unserialised…?

How exactly did you "close the connection"? The co-routine assigned to a given client should be killed once it's done talking to that client, simply by allowing the main() function to end. This can be done by eg removing the "while" loop so it ends once it runs out of code, or rigging the loop to "break" once the client sends a disconnect command, or rigging the loop to "break" if the client doesn't send any data for a while, provides the wrong password, whatever…

If you get stuck, rename your server script and write a new one which only deals with one client at a time. Once you've got that to the point where the client can connect, do whatever you want it to do, disconnect then reconnect all without issue, take that code and dump it into the main() function of your threaded script.
Kouksi44 #10
Posted 29 September 2014 - 02:46 PM
Well I think i found the mistake : The : "while true do"-loop in the main function of the server caused that the coroutine would never end (well I think it does this) and when the coroutine should normally be "dead" it would just go on running.
Thats why I wasn´t able to connect twice from the same Computer.

Everything works fine now thank you ! :)/>