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

Network - Encoding - Decoding - Parsing

Started by CoderJohn, 14 November 2012 - 03:14 PM
CoderJohn #1
Posted 14 November 2012 - 04:14 PM
So basically I am working on a wireless networking api that uses broadcast instead of directly sending a computer a message. Why? Because it will be easier to relay messages. Well any message sent using my api has 3 parts to it.

1. IP
2. Port
3. Message(encrypted)

I was using a split method to split the message and using 28 as a split indicator. However, this is bad idea seeing as that could break up the encrypted message. So my question is…

How can I split these up effectively? I thought about a bit map(something similar)….
However, the ip is the computerID() and I'm not sure what the limit on number of computerID()'s there are.

I was gonnah do something like:

16 bits - IP(RANGE DECIMAL 0-65535)
8 bits - Port(RANGE DECIMAL 0-255)

Here is code for above method:

function parseSignal(sender, message, distance)
  local ip, port, data;
  local ip = message:sub(1,2);
  local ipNumber;
  do --decode the ip
    local b1 = ip:sub(1,1):byte();
    local b2 = ip:sub(2,2):byte();
    ipNumber = bit.blshift(b1,8)+b2;
  end
  port = message:sub(3,3):byte();
  data = message:sub(4);
end

and the rest is the message

Would this be good? I mean I DOUBT there will be over 65k computers in one server….

On other hand… computercraft should reuse computerIDs… if you pick up a computer and replace it down, it generate another id…. Why not reuse an empty id? Or reallocate said id on destruction of computer.
Lyqyd #2
Posted 14 November 2012 - 05:27 PM
Broadcast relaying is a bad idea on anything other than tiny networks. Use a comma or similar to delimit IP and port, thus:


ip, port, message = string.match(incomingPacket, "(%d+),(%d+),(.*)")

IDs are not re-used because all the files that that computer had while it existed are still saved in the old ID's folder on the server.
CoderJohn #3
Posted 15 November 2012 - 03:08 PM
Lyqyd, its not like it couldn't destroy said id when computer is broken… its kind of waste of memory and storage to keep adding more computer ids, when some could be reused… just remove all of computers files when computer is broken.

Dangit, had I not already did the byte version I could use yours. However, I like the byte version I came up with… It is funner to play with.

——

Broadcast Relaying isn't the reason this is being made, it will just be easy to do it to something like this. This is actually being made for secure packet transfers… let me explain how this works…

–Connection Steps
Step 1: Request connection to server
Step 2: Wait for handshake from server(which also includes an encryption key - not all clients use same key for same server; key is random 4 bytes and is stored in a table which represents the socket)

–Message Sending Steps
Step 1: Encrypt message if said server has requested you do so.(using the key it gave you)
Step 2: Get the data ready(SPLIT IP into two bytes and change them to text symbols; Change port to text symbols; Combine ip, port, and message into one)
Step 3: Add 3 bytes to end of message(this is randomly generated and is the handshake key)
Step 4: send the message
Step 5: Wait for the handshake key to be sent back signifyng retrieval of your data
Step 6: try sending the message 2 more times if no handshake key is returned.

This is my code so far… I had a more feature based one, but it was unreliable because for some reason it would send 16 characters but receive 18… ODD right?

Anyways… this version is a little more secure.. I think combining characters that aren't directly supported by minecraft/computercraft such as escape characters is dangerous to send accross rednet and that might of been the problem, reguardless what i have so far works fine… I need to test it with multiple port combinates though(0-255).S

So I ran this code (testServer() on one computer and testClient() on the other) and it seems that sometimes the handShake doesn't make it to the client, causing the client to spam the server with a message until a handShake does get to the client. So output will look like this:(note the number is the port number(i was seeing if all ports work) - however, the ports have nothing to do with the random repetitions as i have tested many times and it changes when it does this) I want my code to be work flawless, but I guess rednet is too unreliable. Does rednet not have a message queue? Say if I call receive after one is added, why can't I get it? It seems you have to call receive before it is added but that is unreliable when you are having two computers speak to each other immediately.
Spoiler1
2
3
4
5
6
7
8
9
10
10
10
10
10
10
11
12
13
14
15
16
17
17
17
17
17
17
How can I fix that^^^



local connected = {};
local ports = {};
local computerIP = os.computerID();
local noHandShake = string.char(0):rep(4);
local hash = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890+-&~";
local hash_length = hash:len();
local maxPort = hash_length; --port can only be between 1 and maxPort;

function encodeMessage(ip, port, message, handShake)
  handShake = (handShake~=nil and handShake) or generateHandShake();
  return encodeIP(ip)..string.char(port)..handShake..message, handShake;
end

function decodeMessage(data)
  local ip, port, message, handShake;
  ip = decodeIP(data:sub(1,2));
  port = data:sub(3,3):byte();
  handShake = data:sub(4,7);
  message = data:sub(8);
  return ip, port, message, handShake;
end

function encodeIP(iIP)
  return string.char(bit.brshift(iIP,8))..string.char(bit.band(iIP,255));
end

function decodeIP(sIP)
  return bit.blshift(sIP:sub(1,1):byte(),8)+sIP:sub(2,2):byte();
end

function decodeHandShake(handShake)
  return handShake:gsub(handShake, "");
end

function generateHandShake()
  local i1 = math.random(1,hash_length);
  local i2 = math.random(1,hash_length);
  local i3 = math.random(1,hash_length);
  local i4 = math.random(1,hash_length);
  return hash:sub(i1,i1)..hash:sub(i2,i2)..hash:sub(i3,i3)..hash:sub(i4,i4);
end

function sendMessage(ip, port, message, bNoHandShake)
  local data, handShake = encodeMessage(ip, port, message, (bNoHandShake and noHandShake) or nil);
  if(bNoHandShake)then
	rednet.broadcast(data);
  else
	local tEventData, iTime = nil, os.clock()-5;
	while(true)do
	  if(os.clock()-iTime >= 10)then
		return false;
	  end
	  os.sleep(0.1);
	  rednet.broadcast(data);
	  tEventData = {rednet.receive(1)};
	  if(tEventData[1] == ip)then
		local isMessage = read(tEventData[1], tEventData[2], tEventData[3], handShake);
		if(isMessage)then break; end
	  end
	end
  end
  return true;
end

function sendHandShake(ip, port, handShake)
  sendMessage(ip, port, handShake, true);
end

function read(sender, message, distance, request)
  local ret_val, bSkipCheck = nil, false;
  local ip, port, message, handShake = decodeMessage(message);
  if(ip~=computerIP)then return false; end --not for us
  if(request~=nil)then
	bSkipCheck = true;
	if(message:find(request))then --user requested control over this message
	  ret_val = {true, message};
	else
	  ret_val = {false, "RequestNotFound"};
	end
  end

  if(not bSkipCheck)then

  end

  if(handShake ~= noHandShake)then
	os.sleep(0.1);
	sendHandShake(sender, port, handShake);
  end
  if(ret_val == nil)then
	ret_val = {port,message};
  end
  return unpack(ret_val);
end

function testServer()
  local tEventData;
  rednet.open("top");
  while(true)do
	tEventData = {os.pullEvent()};
	if(tEventData[1] == 'rednet_message')then
	  print(read(tEventData[2], tEventData[3], tEventData[4]));
	end
  end
end

function testClient(cl)
  for i = 1, 255 do
	rednet.open("top");
	print(sendMessage(cl, i, "Message: "..tostring(i)));
	os.sleep(1);
  end
end