Wizard server#
The wizard server allow communications between wizard clients. For exemple, if a member of your team create an asset in wizard, a signal is sent to this server and broadcasted to all other users on the team.
Running the server on your windows machine#
Important
To work with your team on a server you are running on your local machine, you will need to be on the same network. ( lan )
Go to the installation directory of wizard and run the file ‘server.exe’. While this executable is running, the server should be accessible. Communicate your computer ip adress and the port ( 50333 ) to all wizard users of your team.
Running the server on a linux machine#
Download Python 3.9 on your machine. Download the server python file here and rename it as server.py. Open this file and modify the ip_adress variable ( line 29 ) with the ip adress of the server machine. Now run this file with Python 3.9 and communicate the ip_adress and port to all the users of wizard.
Here is the server.py code:
# coding: utf-8
# Author: Leo BRUNEL
# Contact: [email protected]
# This file is part of Wizard
# MIT License
# Copyright (c) 2021 Leo brunel
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
ip_address = 'localhost'
port = 50333
log_file = "wizard_server.log"
# Python modules
import socket
import sys
import threading
import time
import traceback
import json
import logging
from logging.handlers import RotatingFileHandler
import os
import struct
# create logger
logger = logging.getLogger('WIZARD-SERVER')
logger.setLevel(logging.DEBUG)
# create file handler and set level to debug
file_handler = RotatingFileHandler(log_file, mode='a', maxBytes=1000000, backupCount=1000, encoding=None, delay=False)
# create console handler and set level to debug
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.DEBUG)
# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# add formatter to handlers
stream_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
# add handlers to logger
logger.addHandler(stream_handler)
logger.addHandler(file_handler)
logger.info("Python : " + str(sys.version))
def get_server(DNS):
server = None
server_address = None
try:
server_address = socket.gethostbyname(DNS[0])
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(DNS)
server.listen(100)
except ConnectionRefusedError:
logger.debug(f"Socket connection refused : host={DNS[0]}, port={DNS[1]}")
return None
except socket.timeout:
logger.debug(f"Socket timeout ({str(timeout)}s) : host={DNS[0]}, port={DNS[1]}")
return None
except:
logger.debug(str(traceback.format_exc()))
return None
return server, server_address
def send_signal_with_conn(conn, msg_raw, only_debug = False):
try:
msg = json.dumps(msg_raw).encode('utf8')
msg = struct.pack('>I', len(msg)) + msg
conn.sendall(msg)
return 1
except ConnectionRefusedError:
if only_debug:
logger.debug(f"Socket connection refused : host={DNS[0]}, port={DNS[1]}")
else:
logger.error(f"Socket connection refused : host={DNS[0]}, port={DNS[1]}")
return None
except socket.timeout:
if only_debug:
logger.debug(f"Socket timeout ({str(timeout)}s) : host={DNS[0]}, port={DNS[1]}")
else:
logger.error(f"Socket timeout ({str(timeout)}s) : host={DNS[0]}, port={DNS[1]}")
return None
except:
if only_debug:
logger.debug(str(traceback.format_exc()))
else:
logger.error(str(traceback.format_exc()))
return None
def recvall(sock):
try:
raw_msglen = recvall_with_given_len(sock, 4)
if not raw_msglen:
return None
msglen = struct.unpack('>I', raw_msglen)[0]
return recvall_with_given_len(sock, msglen)
except ConnectionRefusedError:
logger.debug(f"Socket connection refused : host={DNS[0]}, port={DNS[1]}")
return None
except socket.timeout:
logger.debug(f"Socket timeout ({str(timeout)}s) : host={DNS[0]}, port={DNS[1]}")
return None
except:
logger.debug(str(traceback.format_exc()))
return None
def recvall_with_given_len(sock, n):
try:
data = bytearray()
while len(data) < n:
packet = sock.recv(n - len(data))
if not packet:
return None
data.extend(packet)
return data
except ConnectionRefusedError:
logger.debug(f"Socket connection refused : host={DNS[0]}, port={DNS[1]}")
return None
except socket.timeout:
logger.debug(f"Socket timeout ({str(timeout)}s) : host={DNS[0]}, port={DNS[1]}")
return None
except:
logger.debug(str(traceback.format_exc()))
return None
return data
class server(threading.Thread):
def __init__(self):
super(server, self).__init__()
logger.info("Starting server on : '" + str(ip_address) + "'")
logger.info("Default port : '" + str(port) + "'")
self.server, self.server_adress = get_server((ip_address, port))
logger.info("Server started")
self.client_ids = dict()
def run(self):
while True:
try:
conn, addr = self.server.accept()
entry_message = recvall(conn)
self.analyse_signal(entry_message, conn, addr)
except:
logger.error(str(traceback.format_exc()))
continue
def analyse_signal(self, msg_raw, conn, addr):
data = json.loads(msg_raw)
if data['type'] == 'test_conn':
logger.info('test_conn')
elif data['type'] == 'refresh_team':
client_dic = dict()
client_dic['id'] = None
self.broadcast(data, client_dic)
elif data['type'] == 'new_client':
self.add_client(data['user_name'], conn, addr, data['project'])
def add_client(self, user_name, conn, addr, project):
client_dic = dict()
client_dic['user_name'] = user_name
client_dic['conn'] = conn
client_dic['addr'] = addr
client_dic['project'] = project
client_id = str(time.time())
client_dic['id'] = client_id
self.client_ids[client_id]=client_dic
threading.Thread(target=self.clientThread, args=(client_id, user_name, conn, addr, project)).start()
logger.info("New client : {}, {}, {}, {}".format(client_id, user_name, addr, project))
signal_dic = dict()
signal_dic['type'] = 'new_user'
signal_dic['user_name'] = user_name
signal_dic['project'] = project
self.broadcast(signal_dic, self.client_ids[client_id])
self.send_users_to_new_client(client_dic)
def send_users_to_new_client(self, client_dic):
for client in self.client_ids.keys():
if client != client_dic['id']:
signal_dic = dict()
signal_dic['type'] = 'new_user'
signal_dic['user_name'] = self.client_ids[client]['user_name']
signal_dic['project'] = self.client_ids[client]['project']
send_signal_with_conn(client_dic['conn'], signal_dic)
def clientThread(self, client_id, user_name, conn, addr, project):
client_dic = dict()
client_dic['id'] = client_id
client_dic['user_name'] = user_name
client_dic['conn'] = conn
client_dic['addr'] = addr
client_dic['project'] = project
running = True
while running:
try:
raw_data = recvall(client_dic['conn'])
if raw_data is not None:
data = json.loads(raw_data)
print(data)
self.broadcast(data, client_dic)
else:
if conn is not None:
self.remove_client(client_dic)
running = False
except:
logger.error(str(traceback.format_exc()))
continue
def broadcast(self, data, client_dic):
logger.debug("Broadcasting : " + str(data))
for client in self.client_ids.keys():
if client != client_dic['id']:
if not send_signal_with_conn(self.client_ids[client]['conn'], data):
self.remove_client(self.client_ids[client])
def remove_client(self, client_dic):
if client_dic['id'] in self.client_ids.keys():
logger.info("Removing client : {}, {}, {}, {}".format(client_dic['id'], client_dic['user_name'], client_dic['addr'], client_dic['project']))
del self.client_ids[client_dic['id']]
client_dic['conn'].close()
signal_dic = dict()
signal_dic['type'] = 'remove_user'
signal_dic['user_name'] = client_dic['user_name']
signal_dic['project'] = client_dic['project']
self.broadcast(signal_dic, client_dic)
if __name__ == "__main__":
try:
server = server()
server.daemon = True
server.start()
print('Press Ctrl+C to quit...')
while 1:time.sleep(1)
except KeyboardInterrupt:
print('Stopping server...')
raise SystemExit
sys.exit()