0% found this document useful (0 votes)
20 views5 pages

# Can Have Socket, Select, Os, Sys

This document defines code for a distributed hash table (DHT) node. It includes: - Global variables like node ID, address, and state - Helper functions for getting/setting node data, validating requests, and constructing messages - Main functions for joining the DHT, handling requests, and resolving queries The node first joins the DHT by contacting a bootstrap node. It then listens for and handles incoming requests, such as setting its predecessor. Queries are resolved by forwarding to the successor or responding directly if the key is owned locally. Interrupts during stabilization are also addressed.

Uploaded by

ItzFakeAccount
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
20 views5 pages

# Can Have Socket, Select, Os, Sys

This document defines code for a distributed hash table (DHT) node. It includes: - Global variables like node ID, address, and state - Helper functions for getting/setting node data, validating requests, and constructing messages - Main functions for joining the DHT, handling requests, and resolving queries The node first joins the DHT by contacting a bootstrap node. It then listens for and handles incoming requests, such as setting its predecessor. Queries are resolved by forwarding to the successor or responding directly if the key is owned locally. Interrupts during stabilization are also addressed.

Uploaded by

ItzFakeAccount
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 5

# can have socket, select, os, sys, random, json, copy, datetime, pprint

import select
import socket
import sys
import random
import os
import json
import datetime

# ======================== GLOBAL VARIABLES ========================


FIND = "FIND"
STABILIZE = "STABILIZE"
NORMAL = "NORMAL"
myState = ''
# default is not to do finds
# default is if anything else comes up, i should stop the find???
# if you get a find, stabilize and pass it on
# under normal operating situations is all finds should succeed
# if you get a find, something interrupts during a stabilizing (ie another find)
then interrupt it. Otherwise continue it

bootstrapAddr = ('silicon.cs.umanitoba.ca', 15000)


myAddr = ('' , 15018)

bootstrapID = 2**16 - 1
myID = random.randint(0, 2**16 - 2) # random int from 0 and 2^16-2

# defining some command constants


COMMANDS = {
"PRED_REQUEST" : 'pred?',
"MY_PRED" : 'myPred',
"SET_PRED" : 'setPred',
"QUERY" : 'find',
"OWNER" : 'owner'
}

TIMEOUT = 2 # seconds

myPred = {'port': 0, 'ID': 0, 'hostname': ''}


mySucc = {}

# TODO: some dictionary to store my pred's JSON keys

#################### HELPER FUNCTIONS ####################


def getPred():
return myPred

def setPred(id, hostname, port):


global myPred # to fix the python scoping issues
myPred = { "ID": id, "hostname": hostname, "port": port}
#TODO: log that your pred has been changed
#print(myPred)

def isRequestValid(recvData):
isValid = True

# checks validation for PRED_REQUEST, SET_PRED, and QUERY


isValid = recvData["ID"] != '' and recvData["hostname"] != '' and
recvData["port"] != ''

# special case for MY_PRED, and QUERY


if(recvData["cmd"] == COMMANDS["MY_PRED"]):
isValid = recvData["me"]["ID"] != '' and recvData["me"]["hostname"] != ''
and recvData["me"]["port"] != ''
isValid = isValid and recvData["thePred"]["ID"] != '' and
recvData["thePred"]["hostname"] != '' and recvData["thePred"]["port"] != ''
elif(recvData["cmd"] == COMMANDS["QUERY"]):
isValid = isValid and recvData["hops"] >= 0 and recvData["query"] != ''

return isValid

def constructRequest(cmd):
# works for cases PRED_REQUEST and SET_PRED. Also works for QUERY
message = { "cmd": cmd, "port": myAddr[1], "ID": myID, "hostname":
socket.gethostname()}

if(cmd == COMMANDS["MY_PRED"]):
message = { "cmd": cmd, "me": { "ID": myID, "hostname":
socket.gethostname(), "port": myAddr[1]}, "thePred": myPred}
# elif(cmd == COMMANDS["OWNER"]):
# message = ''

message = json.dumps(message).encode('utf-8')
return message

def handleRequest(data):
if(not isRequestValid(data)):
print(str(datetime.datetime.now()) + "\t" + "Invalid request: " + str(data)
+ "\n")
else:
if(data["cmd"] == COMMANDS["PRED_REQUEST"]):
print(str(datetime.datetime.now()) + "\t" + "Responding to
PRED_REQUEST: " + str(constructRequest(COMMANDS["MY_PRED"])) + "\n")
serverSocket.sendto(constructRequest(COMMANDS["MY_PRED"]),
data["hostname"], data["port"])
elif data["cmd"] == COMMANDS["SET_PRED"]:
# only set Pred if its less than me, otherwise ignore as its invalid
if(int(data["ID"]) <= myID):
setPred(data["ID"], data["hostname"], data["port"])
elif data["cmd"] == COMMANDS["find"]:
# case 1: successor is valid, so just resolve the query (respond if i
own it, pass on if i dont)

# TODO: if in between, i start getting other messages, do i stop


stabilization?
# case 2: successor is invalid, start stabilization process and do
partial joins (in between if i get another find, just ignore it)
# case 2a: if successor is invalid, but it doesnt response, then re-
join
# should i store the state somewhere? ie. start the find when im done?
i = 0
elif data["cmd"] == COMMANDS["QUERY"]:
return
return 0

def handleQuery(queryID):
# first send them the find query
# ask for successor's pred

# if response type is MY_PRED, then i move on


# if response type is SET OR GET, then i go back to handling

# case 1: successor is valid, so just resolve the query (respond if i own it,
pass on if i dont)

# TODO: if in between, i start getting other messages, do i stop stabilization?


# case 2: successor is invalid, start stabilization process and do partial
joins (in between if i get another find, just ignore it)
# case 2a: if successor is invalid, but it doesnt response, then re-join
# should i store the state somewhere? ie. start the find when im done?
return 0

def handleJoin():
return 0

#################### MAIN FUNCTIONS ####################

serverSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)


serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # allows us to
reuse the address

localLog = ''
try:
serverSocket.bind(myAddr)

requestAddress = bootstrapAddr
data = ''
while(True):
# send join request
try:
requestMessage = constructRequest(COMMANDS["PRED_REQUEST"])
serverSocket.sendto(requestMessage, requestAddress)
serverSocket.settimeout(TIMEOUT)
(recvData, recvAddr) = serverSocket.recvfrom(2048)
serverSocket.settimeout(None)

data = json.loads(recvData.decode('utf-8'))

# if the request is not valid, then log with timestamp and break
if((not isRequestValid(data)) or data["cmd"] != COMMANDS["MY_PRED"]):
print(str(datetime.datetime.now()) + "\t" + "Invalid request: " +
str(data) + "\n")
break

localLog = localLog + "\n" + str(data)


localLog = localLog + "\nMy (host,port)" + str(socket.gethostname()) +
", " + str(myAddr[1])
localLog = localLog + "\nGiven (host,port)" + str(data["thePred"]
["hostname"]) + ", " + str(data["thePred"]["port"])

# if thePred is me, then set me as my pred and break, or I could be a


zombie, so take my older place
if(int(data["thePred"]["ID"]) < myID or int(data["thePred"]["port"]) ==
myAddr[1]):
# set myself as me's pred. Send some request with a timeout
requestMessage = constructRequest(COMMANDS["SET_PRED"])
serverSocket.sendto(requestMessage, (data["me"]["hostname"],
data["me"]["port"]))
# set me as my successor
mySucc = data["me"]
break
else:
# set requestAddress to thePred
requestAddress = (data["thePred"]["hostname"], data["thePred"]
["port"])
except Exception as e:
print("Im above here: " + str(e))
# TODO: log the timeout thing and your solution

# Since timeout occured, insert myself into last known node


requestMessage = constructRequest(COMMANDS["SET_PRED"])
serverSocket.sendto(requestMessage, (data["me"]["hostname"], data["me"]
["port"]))
# set me as my successor
mySucc = data["me"]
break

print("Local log: \n" + localLog+ '\n')


print()
print("Me: " + str(constructRequest(COMMANDS["MY_PRED"]).decode('utf-8')))
print("my succ: " + str(mySucc))

requestMessage = constructRequest(COMMANDS["PRED_REQUEST"])
serverSocket.sendto(requestMessage, (mySucc["hostname"], mySucc["port"]))
serverSocket.settimeout(TIMEOUT)
(recvData, recvAddr) = serverSocket.recvfrom(2048)
serverSocket.settimeout(None)
print("who's your pred?" + recvData.decode('utf-8'))

# NOW THAT WE HAVE JOINED, WE START THE QUERY PROCESS


#Setting up socket fd for select
socketFD = serverSocket.fileno()
(readFD, writeFD, errorFD) = select.select([socketFD, sys.stdin], [], [])

done = False
while not done:

for descriptor in readFD:


if descriptor == socketFD:
# receive the messages somehow
(recvData, recvAddr) = serverSocket.recvfrom(2048)
print("Received: " + recvData.decode('utf-8'))
handleRequest(recvData)
elif descriptor == sys.stdin:
# receive the inputs somehow (and just print them out for now ig)
input = sys.stdin.readline().rstrip()
if(input == ''):
# break out of this reader
done = True
break
else:
myState = FIND
if(int(input) < myID):
input = random.randint(myID+1, 2**16-2)
# receive a query value that triggers FIND
print('Input: '+ input)
handleQuery(int(input))
# send a find request to whom???

# TODO: try to implement what the prof said about find() and interrupts.
# only in find we stabilize the local neighbourhood, other operations of
the protocol are simple

except Exception as e:
# TODO: throw some exception. with timestamp or handle the case idk
print(str(datetime.datetime.now()) + "\t" + "Code halted due to exception: " +
str(e) + "\n")
finally:
# TODO: do i really need SOCKET SHUTDOWN for datagram socket?
serverSocket.close()
print(str(datetime.datetime.now()) + "\t" + "Code halted due to completion" +
"\n")

You might also like