Created
October 6, 2011 15:39
-
-
Save Alexis-D/1267722 to your computer and use it in GitHub Desktop.
Distributed Systems - Tutorial 1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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