Skip to content

Instantly share code, notes, and snippets.

@Alexis-D
Created October 6, 2011 15:39
Show Gist options
  • Save Alexis-D/1267722 to your computer and use it in GitHub Desktop.
Save Alexis-D/1267722 to your computer and use it in GitHub Desktop.
Distributed Systems - Tutorial 1
#!/usr/bin/env python
#-*- coding: utf-8 -*-
import socket
import threading
class BadWordsServer:
"""Represent a server which is designed to filter "bad words"
from data which is send to the network, and then resend
the cleaned data to the client."""
def __init__(self, bad_words, host='127.0.0.1', port=8000):
"""bad_words: words to ban
host: host to use for the server
port: port to use for the server"""
self.bad_words = bad_words
self.bad_replace = ['*' * len(x) for x in bad_words]
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s.bind((host, port))
self.s.listen(1)
def serve(self, buf_size=1024, encoding='UTF-8'):
"""Run the server. It can handle multiple connection concurrently.
buf_size: size of the buffer to use for recieving data
encoding: encoding of input/output data"""
while True:
conn, _ = self.s.accept()
threading.Thread(None, self.serve_one, None,\
(conn, buf_size, encoding)).start()
def serve_one(self, conn, buf_size=1024, encoding='UTF-8'):
"""Serve only one connection. Close it recieve an empty data."""
while True:
data = conn.recv(buf_size).decode(encoding)
if not data:
break
print('Raw data:', data)
data = self.filter_bad_words(data)
print('Cleaned data:', data)
conn.send(bytes(data, encoding))
conn.close()
def filter_bad_words(self, data):
"""Remove the "bad words" from data, and return the cleaned data."""
for w, r in zip(self.bad_words, self.bad_replace):
data = data.replace(w, r)
return data
class BadWordsClient:
"""Represent a client which ask user for input, and which send that
data to a BadWordsServer for cleaning it. Once the client recieve
the cleaned data it prints it."""
def __init__(self, host='127.0.0.1', port=8000):
"""host: host of the server
port: port of the server"""
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s.connect((host, port))
def run(self, buf_size=1024, encoding='UTF-8'):
"""Ask the user for data, clean it through the BadWordsServer,
and then print the result.
buf_size: size of the buffer to use for recieving data
encoding: encoding of input/output data"""
while True:
data = input('Data to filter? ')
if not data:
break
self.s.send(bytes(data, encoding))
data = self.s.recv(buf_size).decode(encoding)
print('Cleaned data:', data)
self.s.close()
if __name__ == '__main__':
import sys
def yes_no(prompt, default_yes=True):
"""Ask the user for a yes/no question.
prompt: the question to ask
default_yes: the default choice is yes if True no otherwise
Return True for yes, False for no"""
y, n = 'y', 'n'
if default_yes:
y = y.upper()
else:
n = n.upper()
i = None
while i not in ['', 'y', 'n']:
i = input('%s (%c/%c)? ' % (prompt, y, n)).lower()
if i == '':
return default_yes
elif i == 'y':
return True
else:
return False
def get_host_port():
"""Ask the user for an host and a port, and return them."""
host = input('host? ')
port = None
while port == None:
try:
port = int(input('port? '))
except ValueError:
print('port should be an integer.', file=sys.stderr)
return host, port
arg_error = 'This program takes exactly one argument serve or client.'
if len(sys.argv) != 2:
print(arg_error, file=sys.stderr)
elif sys.argv[1] == 'serve':
bad_words = ['fuck', 'shit', 'dumb']
if yes_no('Would you like to serve on localhost:8000'):
host, port = 'localhost', 8000
else:
host, port = get_host_port()
try:
BadWordsServer(bad_words, host, port).serve()
except socket.error as e:
print('Error occured:', e, file=sys.stderr)
elif sys.argv[1] == 'client':
if yes_no('Would you like to use localhost:8000 as server'):
host, port = 'localhost', 8000
else:
host, port = get_host_port()
try:
BadWordsClient(host, port).run()
except socket.error as e:
print('Error occured:', e, file=sys.stderr)
else:
print(arg_error, file=sys.stderr)
# Q4:
# One way of passing more information in a message, like encoding, language
# or degree of cleaning would be to use "headers" in the message, the first
# one would be the encoding, followed by (for instance) a semicolon, then
# the language followed by a semicolon, then the degree of cleaning followed
# by a semicolon and finally the message.
# e.g.
# UTF-8;french;moderate;<the actual message>
# the encoding and first semicolon should be in UTF-8 to be sure that it
# will be well undestand by the server, all the following data in the
# message (including the two remaining semicolons) should be encoded
# with the "new" encoding.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment