From: <ch...@us...> - 2009-10-21 22:34:34
|
Revision: 305 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=305&view=rev Author: chozone Date: 2009-10-21 22:34:25 +0000 (Wed, 21 Oct 2009) Log Message: ----------- Begin of both client and server, core and app. -Server core: -Callback system -Accept incoming connections -Send/receive data -Client core: -Callback system -Connect to server -Send/receive data Modified Paths: -------------- trunk/client/VP.py Added Paths: ----------- trunk/client/callback.py trunk/client/core/__init__.py trunk/client/core/callback.py trunk/client/core/client.py trunk/client/core/parser.py trunk/server/VPS.py trunk/server/callback.py trunk/server/core/__init__.py trunk/server/core/callback.py trunk/server/core/parser.py trunk/server/core/server.py Removed Paths: ------------- trunk/server/VP.py Modified: trunk/client/VP.py =================================================================== --- trunk/client/VP.py 2009-10-18 19:17:08 UTC (rev 304) +++ trunk/client/VP.py 2009-10-21 22:34:25 UTC (rev 305) @@ -16,3 +16,22 @@ ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import time +import core +from callback import Callback + +def main(): + client = core.Client(Callback) + client.connect('localhost', 5162) + + while 1: + #Pygame event loop should go here later. + try: + time.sleep(5000) + except KeyboardInterrupt: + print + exit() + +if __name__ == '__main__': main() + Added: trunk/client/callback.py =================================================================== --- trunk/client/callback.py (rev 0) +++ trunk/client/callback.py 2009-10-21 22:34:25 UTC (rev 305) @@ -0,0 +1,35 @@ +## This file is part of Virtual Playground +## Copyright (c) 2009 Jos Ratsma + Koen Koning + +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either +## version 2 of the License, or (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import core + +class Callback(core.Callback): + def connected(self, con): + print 'Connected!' + self.con = con + con.send('HI') + + def disconnected(self): + print 'CONNECTION DEAD' + #return True #reconnect + + def data_received(self, data): + print '<', repr(data) + + def data_send(self, data): + print '>', repr(data) + Added: trunk/client/core/__init__.py =================================================================== --- trunk/client/core/__init__.py (rev 0) +++ trunk/client/core/__init__.py 2009-10-21 22:34:25 UTC (rev 305) @@ -0,0 +1,21 @@ +## This file is part of Virtual Playground +## Copyright (c) 2009 Jos Ratsma + Koen Koning + +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either +## version 2 of the License, or (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import client, callback +Client = client.Client +Callback = callback.Callback + Added: trunk/client/core/callback.py =================================================================== --- trunk/client/core/callback.py (rev 0) +++ trunk/client/core/callback.py 2009-10-21 22:34:25 UTC (rev 305) @@ -0,0 +1,62 @@ +## This file is part of Virtual Playground +## Copyright (c) 2009 Jos Ratsma + Koen Koning + +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either +## version 2 of the License, or (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +class Callback: + def connected(self, con): + """ + connected(self, con) + This will be called when a connection with the server is made. + `con` contains the core's `Client` class, so you can use it to + send stuff. + + This is a placeholder. + If you want to catch this event, overwrite it. + """ + pass + + def disconnected(self): + """ + connected(self) + This is called when the connection dies. For example when you + close the connection manually, the server kicks you, your modem + explodes or your cat eates the LAN cable. + + This is a placeholder. + If you want to catch this event, overwrite it. + """ + pass + + def data_received(self, data): + """ + data_received(self, data) + Called when we received data from the server. + + This is a placeholder. + If you want to catch this event, overwrite it. + """ + pass + + def data_send(self, data): + """ + data_send(self, data) + Called when we send data to the server. + + This is a placeholder. + If you want to catch this event, overwrite it. + """ + pass + Added: trunk/client/core/client.py =================================================================== --- trunk/client/core/client.py (rev 0) +++ trunk/client/core/client.py 2009-10-21 22:34:25 UTC (rev 305) @@ -0,0 +1,156 @@ +## This file is part of Virtual Playground +## Copyright (c) 2009 Jos Ratsma + Koen Koning + +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either +## version 2 of the License, or (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import simplejson, socket, threading, time +from parser import Parser + + +class Client(threading.Thread): + def __init__(self, callback_class): + """ + This class will handle all of the connection to the server. + """ + + self.__call = callback_class() + self.__sock = None + self.__pinger = None + self.__parse = Parser(self.__call, self) + self.is_online = True + + threading.Thread.__init__(self) + + def connect(self, host, port): + """ + Will (try to) connect to the server on `host`:`port`. + If you want to reconnect, you have to close the connection + first, if it's still open. + """ + + self.__host = host + self.__port = port + + if self.__sock: + raise Exception, "You are already connected!" + + self.__sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + self.start() + + def run(self): + """ + Used by threading, not for external usage. + """ + + #TODO: reconnect loop thingy here + + while 1: + try: + self.__sock.connect((self.__host, self.__port)) + break + except: + time.sleep(1) + + self.__call.connected(self) + + self.__pinger = self.__Pinger(self.send) + + + #Infinite loop that receives data + buffer = '' + while 1: + try: + data = self.__sock.recv(1024) + except: + if self.is_online: self.close() + return + if not data: + if self.is_online: self.close() + return + + buffer += data + #Each dataset must end with a delimiter: chr(1) + if chr(1) in buffer: + data = buffer.split(chr(1)) + buffer = data[-1] + data = data[:-1] + + for msg in data: + self.__parse(simplejson.loads(msg)) + + def send(self, data_header, data_body = {}): + """ + send(data_header, data_body = {}) => None + Sends `data_body` of type `data_header` to the server. It will + automatically be encoded as JSON, and the delimeter character + (chr(1)) will be send automatically too. + `Data_header` is a string, `data_body` a dict. + """ + try: + self.__call.data_send({data_header:data_body}) + data = simplejson.dumps({data_header:data_body}) + self.__sock.send(data + chr(1)) + except: pass + + + def close(self): + """ + close(self) => None + Will close the connection to the server. If the connection in + already down, it will raise an exception. This will trigger the + callback `disconnected`. It may reconnect, depending on the + callback. If not, you can reuse this class by calling `connect`. + """ + + if not self.is_online: + raise Exception, "The connection is already closed!" + + self.__sock.close() + self.is_online = False + if self.__call.disconnected(): + self.__sock = None + self.connect(self.__host, self.__port) + + class __Pinger(): + def __init__(self, send): + """ + Will ping every 30 seconds for food. If the server + doesn't receive anything from us for 45 sec, it will + drop the connection, because it thinks our computer blew + up or something... + """ + self.running = True + + self.send = send + self.timer = threading.Timer(30, self.run) + self.timer.start() + + def run(self): + if self.running == False: return + + #Ping! + self.send('PNG', {}) + + #Start again + self.timer = threading.Timer(30, self.run) + self.timer.start() + + def cancel(self): + if self.running: + self.timer.cancel() + self.running = False + + Added: trunk/client/core/parser.py =================================================================== --- trunk/client/core/parser.py (rev 0) +++ trunk/client/core/parser.py 2009-10-21 22:34:25 UTC (rev 305) @@ -0,0 +1,33 @@ +## This file is part of Virtual Playground +## Copyright (c) 2009 Jos Ratsma + Koen Koning + +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either +## version 2 of the License, or (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +class Parser(): + def __init__(self, callback, client): + """ + This class parses all received messages from client. + It may need a better name... + """ + self.__call = callback + self.__client = client + + def __call__(self, msg): + self.__call.data_received(msg) + + head = msg.keys()[0] + body = msg[head] + + #handle data here later... Deleted: trunk/server/VP.py =================================================================== --- trunk/server/VP.py 2009-10-18 19:17:08 UTC (rev 304) +++ trunk/server/VP.py 2009-10-21 22:34:25 UTC (rev 305) @@ -1,18 +0,0 @@ -#!/usr/bin/env python - -## This file is part of Virtual Playground -## Copyright (c) 2009 Jos Ratsma + Koen Koning - -## This program is free software; you can redistribute it and/or -## modify it under the terms of the GNU General Public License -## as published by the Free Software Foundation; either -## version 2 of the License, or (at your option) any later version. - -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. - -## You should have received a copy of the GNU General Public License -## along with this program; if not, write to the Free Software -## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Copied: trunk/server/VPS.py (from rev 304, trunk/server/VP.py) =================================================================== --- trunk/server/VPS.py (rev 0) +++ trunk/server/VPS.py 2009-10-21 22:34:25 UTC (rev 305) @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +## This file is part of Virtual Playground +## Copyright (c) 2009 Jos Ratsma + Koen Koning + +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either +## version 2 of the License, or (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import time +import core +from callback import Callback + +def main(): + conf = {'host':'', + 'port':5162, + 'max_connections':0} + + server = core.Server(conf, Callback) + server.start_server() + + while 1: + try: + time.sleep(5000) + except KeyboardInterrupt: + print + exit() + +if __name__ == '__main__': main() + Property changes on: trunk/server/VPS.py ___________________________________________________________________ Added: svn:mergeinfo + Added: trunk/server/callback.py =================================================================== --- trunk/server/callback.py (rev 0) +++ trunk/server/callback.py 2009-10-21 22:34:25 UTC (rev 305) @@ -0,0 +1,49 @@ +## This file is part of Virtual Playground +## Copyright (c) 2009 Jos Ratsma + Koen Koning + +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either +## version 2 of the License, or (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import core + +class Callback(core.Callback): + def server_online(self, clients): + print 'Server is online! :-)' + self.clients = clients + + def connection_opened(self, uid, client, host, port): + print 'New connection:', uid + + def connection_close(self, uid): + print 'Connection dead:', uid + + def connection_limit_exceeded(self, ip, current_connections, + max_connections): + print 'Connection limit exceeded: used %s/%s slots, auto '\ + 'accepted request from %s.' % (current_connections, + max_connections, ip) + return True + + def data_send(self, uid, data): + print '>', repr(data) + + def data_received(self, uid, data): + print '<', repr(data) + + head = data.keys()[0] + body = data[head] + + if head == 'HI': + self.clients[uid].send('LO') + Added: trunk/server/core/__init__.py =================================================================== --- trunk/server/core/__init__.py (rev 0) +++ trunk/server/core/__init__.py 2009-10-21 22:34:25 UTC (rev 305) @@ -0,0 +1,21 @@ +## This file is part of Virtual Playground +## Copyright (c) 2009 Jos Ratsma + Koen Koning + +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either +## version 2 of the License, or (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import server, callback +Server = server.Server +Callback = callback.Callback + Added: trunk/server/core/callback.py =================================================================== --- trunk/server/core/callback.py (rev 0) +++ trunk/server/core/callback.py 2009-10-21 22:34:25 UTC (rev 305) @@ -0,0 +1,90 @@ +## This file is part of Virtual Playground +## Copyright (c) 2009 Jos Ratsma + Koen Koning + +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either +## version 2 of the License, or (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +class Callback: + def server_online(self, clients): + """ + server_online(self, clients) + Called when the server is online and ready to accept incoming + connections. This means it has successfully bounded itself to a + port. Most of the times this will be called immediately after + calling Server.start() + + This is a placeholder. + If you want to catch this event, overwrite it. + """ + pass + + def connection_opened(self, uid, client, host, port): + """ + connection_opened(self, uid, client, host, port) + Called when a new client connects, and we accepted the + connection. You can, however, still kill the connection by + returning True in this function. + + This is a placeholder. + If you want to catch this event, overwrite it. + """ + pass + + def connection_closed(self, uid): + """ + connection_closed(self, uid) + Called when a connection with a client is closed. This can be + when they closed their connection themselves, or when we + disconnect someone. + + This is a placeholder. + If you want to catch this event, overwrite it. + """ + #TODO: Reason? + pass + + def connection_limit_exceeded(self, ip, current_connections, + max_connections): + """ + connection_limit_exceeded(self, ip, current_connections, + max_connections) + Called when a new client connects, but the maximum number of + connections is exceeded. You can, however, still accept the + connection by returning True in this function. + + This is a placeholder. + If you want to catch this event, overwrite it. + """ + pass + + def data_received(self, uid, data): + """ + data_received(self, uid, data) + Called when the server received data from one of the clients. + + This is a placeholder. + If you want to catch this event, overwrite it. + """ + pass + + def data_send(self, uid, data): + """ + data_send(self, uid, data) + Called when the server send data to one of the clients. + + This is a placeholder. + If you want to catch this event, overwrite it. + """ + pass + Added: trunk/server/core/parser.py =================================================================== --- trunk/server/core/parser.py (rev 0) +++ trunk/server/core/parser.py 2009-10-21 22:34:25 UTC (rev 305) @@ -0,0 +1,35 @@ +## This file is part of Virtual Playground +## Copyright (c) 2009 Jos Ratsma + Koen Koning + +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either +## version 2 of the License, or (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +class Parser(): + def __init__(self, callback, clients): + """ + This class parses all received messages from client. + It may need a better name... + """ + self.__call = callback + self.__clients = clients + + def __call__(self, uid, msg): + self.__call.data_received(uid, msg) + + head = msg.keys()[0] + body = msg[head] + + if head == 'PNG': #Ping + pass #Should this have it's own callback/do anything? + Added: trunk/server/core/server.py =================================================================== --- trunk/server/core/server.py (rev 0) +++ trunk/server/core/server.py 2009-10-21 22:34:25 UTC (rev 305) @@ -0,0 +1,201 @@ +## This file is part of Virtual Playground +## Copyright (c) 2009 Jos Ratsma + Koen Koning + +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either +## version 2 of the License, or (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import simplejson, socket, threading, time +from parser import Parser + +class Server(threading.Thread): + def __init__(self, config, callback_class): + """ + Will handle all connections from clients. It has one main + socket, and creates all (client)sockets. + + + Config: (maybe this explanation should be moved? maybe provide + example config with the core? maybe this function + should check the given config for error, raise + exceptions, and fill in standard values for some + missing keys) + Should be dict with settings for the server. Below a list of all + posible keys and an explanation. + + -host: Host to which the server binds itself(am i saying this + correctly?) standard value is '', an empty string, which + means everyone can connect to the server. + -port: Port the server should listen on. Should be int, and can + be anything. + -max_connections: Maximum ammount of clients that can be + connected simultaneously. If this number is exceeded, + the client will dropped, unless specified otherwise (see + callback `connection_limit_exceeded`). Should be int. If + this value is either None or 0, there will be no limit. + """ + + self.__sock = None + self.__call = callback_class() + self.__config = config + self.__clients = {} + self.__parse = Parser(self.__call, self.__clients) + + threading.Thread.__init__(self) + + def start_server(self): + """ + start_server() => None + + This will start the server. It will listen on the port + specified in the config. + """ + + #Isn't the server running already? + if self.__sock: + raise Exception, "The server is already online!" + + #Load our server socket + self.__sock = socket.socket(socket.AF_INET, + socket.SOCK_STREAM) + #Configure it to re-use the previous one + self.__sock.setsockopt(socket.SOL_SOCKET, + socket.SO_REUSEADDR, 1) + + self.start() #For more adventures of the socket, see `run` + + + def run(self): + """ + Used by threading, not for external usage. + """ + + #Try to claim the pre-specified port + while 1: + try: + self.__sock.bind((self.__config['host'], + self.__config['port'])) + break + except: + time.sleep(1) + + self.__sock.listen(20) + self.__call.server_online(self.__clients) + + #Infinite loop that will wait for incomming connections + while 1: + sock, addr = self.__sock.accept() + + if self.__config['max_connections'] != None and \ + len(self.__clients) >= \ + self.__config['max_connections']: + #We exceeded our connection limit, but maybe + #this is a special occasion? Send callback! + if not self.__call.connection_limit_exceeded( + addr[0], len(self.__clients), + self.__config['max_connections']): + + #We're full, kick him out + #TODO: Tell him we're full? + sock.close() + continue + + uid = addr[0] + ':' + str(addr[1]) + self.__clients[uid] = Client(uid, sock, self.__clients, + self.__call, self.__parse) + self.__clients[uid].start() + + if self.__call.connection_opened(uid, + self.__clients[uid], *addr): + #User returned True -> drop user + time.sleep(0.1) #Let the `run` start + self.__clients[uid].close() + + + +class Client(threading.Thread): + def __init__(self, uid, sock, clients, callback, parser): + """ + This class manages the socket for the connection to clients. + Each client has it's own class. + """ + + self.__uid = uid + self.__sock = sock + self.__clients = clients + self.__call = callback + self.__parser = parser + self.is_online = True + + threading.Thread.__init__(self) + + def __repr__(self): + return '<Client(%s)>'%self.__uid + + def run(self): + """ + Used by threading, not for external usage. + """ + #Client must ping (at least) every 30 seconds. So we set a + #timeout of 45 seconds. + self.__sock.settimeout(45) + + #Infinite loop that receives data + buffer = '' + while 1: + try: + data = self.__sock.recv(1024) + except: + #Ping timeout? + if self.is_online: self.close() + return + if not data: + if self.is_online: self.close() + return + + buffer += data + #Each dataset must end with a delimiter: chr(1) + if chr(1) in buffer: + data = buffer.split(chr(1)) + buffer = data[-1] + data = data[:-1] + + for msg in data: + self.__parser(self.__uid, + simplejson.loads(msg)) + + def close(self): + """ + close() => None + Closes this connection, and kills the socket. + """ + + self.__sock.close() + self.is_online = False + + self.__call.connection_close(self.__uid) + + del self.__clients[self.__uid] + + def send(self, data_header, data_body = {}): + """ + send(data_header, data_body = {}) => None + Sends `data_body` of type `data_header` to the client. It will + automatically be encoded as JSON, and the delimeter character + (chr(1)) will be send automatically too. + `Data_header` is a string, `data_body` a dict. + """ + self.__call.data_send(self.__uid, {data_header:data_body}) + data = simplejson.dumps({data_header:data_body}) + self.__sock.send(data + chr(1)) + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |