Telematica: Almost finished client and server.

parent bb7db307
......@@ -77,6 +77,7 @@ class ClientConnection(object, AsyncBase, asyncore.dispatcher):
elif chunk == '\n' and buf[-1] == '\r':
if not multi_line:
break
self.debug_log('receiving multi line response...')
if buf[-3:] == '\r\n\r':
# Remove trailing '\n\r' from multi line response
buf = buf[:-2]
......@@ -84,7 +85,7 @@ class ClientConnection(object, AsyncBase, asyncore.dispatcher):
buf += chunk
buf = buf[:-1]
self.debug_log('< %s' % buf)
self.debug_log('< %s' % buf.replace('\n','\\n').replace('\r','\\r'))
# Invoke the proper callback function.
if not self.verified:
......@@ -118,7 +119,7 @@ class ClientConnection(object, AsyncBase, asyncore.dispatcher):
if not self.authentification_sent:
self.send_queue.put('USER %s' % self.user)
self.authentification_sent = True
elif buf[1:9] == 'Username':
elif buf[0] == '+':
# TODO: handle 'username is taken'.
self.authenticated = True
if 'authenticated' in self.event_list:
......@@ -130,8 +131,9 @@ class ClientConnection(object, AsyncBase, asyncore.dispatcher):
# After authentification, fetch the list of users currently logged in.
if not self.retrieved_user_list:
if not self.request_user_list_sent:
self.request_user_list_sent = True
self.retrieve_users()
#self.request_user_list_sent = True
#self.retrieve_users()
pass
else:
# TODO: process user list
self.retrieved_user_list = True
......
......@@ -10,8 +10,10 @@ import sys
from asyncbase import AsyncBase
# Greeting message sent to a connected client.
GREETING_MSG = 'Hi there!'
# Major and minor version of this server.
MAJOR_VERSION = 1
MINOR_VERSION = 0
......@@ -34,14 +36,13 @@ class Server(asyncore.dispatcher):
self.port = port
self.handler = handler
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
logging.config.fileConfig('logging.conf')
self.log = logging.getLogger('Server')
# Listen on given ip-address and port
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.set_reuse_addr()
self.bind((ip, port))
self.log.info('waiting for incoming connections on port %s.' % port)
self.listen(5)
......@@ -56,27 +57,54 @@ class Server(asyncore.dispatcher):
try:
conn, addr = self.accept()
self.log.info('accepted client %s:%d' % addr)
client = self.connect_client(addr)
client = self.connect_client(conn, addr)
except socket.error:
self.log.warning('warning: server accept() threw an exception.')
return
except TypeError:
self.log.warning('warning: server accept() threw EWOULDBLOCK.')
return
def connect_client(self, conn, addr):
"""
Initialise a client connection after the server accepted an incoming
connection. This will set the connection handler.
"""
client = ClientData()
# creates an instance of the handler class to handle the
# request/response on the incoming connection.
self.handler(conn, addr, client, self)
def connect_client(self, addr):
self.clients['%s:%d' % addr] = ClientData()
return self.clients['%s:%d' % addr]
client.handler = self.handler(conn, addr, client, self)
client.username = ''
self.clients['%s:%d' % addr] = client
return client
def disconnect_client(self, addr):
del self.clients['%s:%d' % addr]
"""
Client leaves the chat server. This function is called when the socket
is broken or the client closes the connection gracefully.
"""
# Suppress error if a client is already disconnected.
try:
client = self.clients['%s:%d' % addr]
self.send_all('LEAVE %s' % client.username, True)
del self.clients['%s:%d' % addr]
except KeyError:
pass
def change_username(self, addr, username):
"""
Update the username of the client.
"""
self.clients['%s:%d' % addr].username = username
# TODO: notify other clients of changed username
def send_all(self, msg, sender=None):
"""
Send a message or notification from a client to all connected clients
(optionally including to the sender).
"""
for c in self.clients:
if self.clients[c].handler != sender:
self.clients[c].handler.send_raw(msg)
class RequestHandler(AsyncBase, asyncore.dispatcher):
def __init__(self, conn, address, client, server):
......@@ -86,18 +114,25 @@ class RequestHandler(AsyncBase, asyncore.dispatcher):
self.address = address
self.client = client
self.server = server
self.username = ''
self.log = self.server.log
self.send_welcome_message()
def send_welcome_message(self):
"""
Welcome our new client by sending a welcome message. This message
contains the server version number and a greetings message.
"""
self.send_raw("CHAT/%d.%d/%s" \
% (MAJOR_VERSION, MINOR_VERSION, GREETING_MSG))
def handle_read(self):
"""
Receive a message from the client. If the connection is somehow broken,
disconnect the client and clean the corresponding data.
"""
buf = ''
# Receive a message from the client. If the connection is somehow
# broken, disconnect the client and clean the corresponding data.
try:
while True:
chunk = self.recv(1)
......@@ -115,10 +150,11 @@ class RequestHandler(AsyncBase, asyncore.dispatcher):
# Received a message, so it's time to parse the message.
buf = buf[:-1]
self.debug_log('< %s' % buf)
self.parse_response(buf)
def handle_error(self):
self.server.disconnect_client(self.address)
def parse_response(self, buf):
"""
>>> class DummyServer(object):
......@@ -140,9 +176,24 @@ class RequestHandler(AsyncBase, asyncore.dispatcher):
if cmd == 'USER':
# User changes/sets its username.
if re.match(ur'^[^\u0000-\u001f\u007f-\u009f/:]+$', buf[5:]):
if not self.username:
self.send_all('JOIN %s' % buf[5:], True)
else:
self.send_all('RENAME %s/%s' \
% (self.username, buf[5:]), True)
self.set_username(buf[5:])
return self.send_positive('Ok')
return self.send_negative('Invalid username.')
# TODO: user can't send a message if he didn't send his username first.
if cmd == 'SAY':
return self.send_all('SAY %s/%s' % (self.username, buf[4:]))
if cmd == 'NAMES':
self.send_raw('+Ok:')
for c in self.server.clients:
self.send_raw(self.server.clients[c].username)
self.send_raw('')
return True
return self.send_negative('Unsupported command.')
def send_positive(self, msg):
......@@ -153,7 +204,12 @@ class RequestHandler(AsyncBase, asyncore.dispatcher):
self.send_raw('-%s' % msg)
return False
def send_all(self, msg, except_sender=False):
self.server.send_all(msg, except_sender and self)
return True
def set_username(self, username):
self.username = username
self.server.change_username(self.address, username)
if __name__ == '__main__':
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment