Created
June 3, 2015 14:23
-
-
Save alekratz/ee0327173dca16db2a59 to your computer and use it in GitHub Desktop.
Python thin client - v2
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
__author__ = 'Alek Ratzloff <[email protected]>' | |
from socket import socket | |
class ThinClient: | |
""" | |
Python client used for doing stuff... Thinly | |
""" | |
def __init__(self, port, host="127.0.0.1", recv_size=1024): | |
self.port = port | |
self.host = host | |
self.recv_size = recv_size | |
self.sock = None | |
def connect(self): | |
""" | |
Creates a connection from a thin client to a server to send commands back and forth. | |
""" | |
if self.sock: | |
raise Exception("Client is already connected to a server") | |
# create the socket and connect | |
self.sock = socket() | |
self.sock.connect((self.host, self.port)) | |
def close(self): | |
""" | |
Closes a connection from a thin client to a server. | |
""" | |
self.__verify_connection() | |
self.sock.close() | |
self.sock = None | |
def send(self, message): | |
""" | |
Sends a message to the server after a connection is established. | |
:param message: the message or command to send to the server | |
""" | |
self.__verify_connection() | |
self.sock.send(message) | |
def wait_receive(self): | |
""" | |
Blocks program execution until a message is received from the server. | |
:return: the string read from the server | |
""" | |
self.__verify_connection() | |
return self.sock.recv(self.recv_size) | |
def __verify_connection(self): | |
""" | |
Ensures that the thin client is connected to a server. If not, it will raise an exception. | |
""" | |
if not self.sock: | |
raise Exception("Client is not connected to the server") | |
# For now, there is no difference between the BasicThinClient and ThinClient. | |
BasicThinClient = ThinClient |
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
__author__ = 'Alek Ratzloff <[email protected]>' | |
import abc | |
from socket import socket | |
from os.path import exists | |
from os import fork | |
class ThinServer: | |
__metaclass__ = abc.ABCMeta | |
def __init__(self, port, host='127.0.0.1', recv_size=1024, is_daemon=False, lockfile="/tmp/pythinclient.pid"): | |
""" | |
Creates an instance of a server that listens for connections and commands from the thin client. | |
:param port: the port to listen on | |
:param host: the hostname to listen on. 127.0.0.1 by default | |
:param recv_size: the size of the buffer to read. 1024 by default | |
:param is_daemon: whether this thin server is a daemon. False by default | |
:param lockfile: the file to use to hold the process ID of the daemon process | |
""" | |
self.port = port | |
self.host = host | |
self.recv_size = recv_size | |
self.sock = None | |
self.is_daemon = is_daemon | |
self.is_running = False | |
self.lockfile = lockfile | |
def start(self): | |
assert (self.sock is None == (not self.is_running)) | |
if self.is_running: | |
raise Exception("Server is already running") | |
# determine if this is a daemonized server, and check to see if the lockfile is already taken | |
if self.is_daemon: | |
if exists(self.lockfile): | |
raise Exception("Daemonized server is already running") | |
# fork | |
child = fork() | |
if child == -1: | |
raise Exception("Failed to fork for daemon") | |
elif child == 0: | |
# child section | |
self.sock = socket() | |
self.sock.bind((self.host, self.port)) | |
self.sock.listen(1) | |
self.__accept_loop() | |
else: | |
# parent section | |
# create the lockfile and put the PID inside of it | |
with open(self.lockfile, "w") as fp: | |
fp.write(str(child)) | |
else: | |
# not a daemon. initialize like normal and run in this thread | |
self.sock = socket() | |
self.sock.bind((self.host, self.port)) | |
self.sock.listen(1) | |
self.__accept_loop() | |
def __accept_loop(self): | |
""" | |
Private helper method that accepts clients | |
:return: | |
""" | |
assert self.sock | |
assert self.is_running | |
while self.is_running: | |
conn, addr = self.sock.accept() | |
message = conn.recv(self.recv_size) | |
self.on_receive(message, conn, addr) | |
@abc.abstractmethod | |
def on_receive(self, message, conn, addr): | |
""" | |
Handles the receiving of a message from a client | |
:param message: the message that was received | |
:param conn: the socket connection that sent the message | |
:param addr: the address of the connection that sent the message | |
:return: | |
""" | |
return | |
class BasicThinServer(ThinServer): | |
""" | |
A basic thin server that can be extended by adding method hooks. Check the add_hook method documentation on how to | |
do so. This thin server can be used with the BasicThinClient. | |
""" | |
def __init__(self, port=65000): | |
super(BasicThinServer, self).__init__(port) | |
self.hooks = {} | |
def add_hook(self, command, method): | |
""" | |
Adds a keyword command for the server to invoke a method. | |
The method must take 3 arguments: the message, the connection, and the address. | |
:param command: the command, as a string, that is handled | |
:param method: the function that is called | |
:return: | |
""" | |
self.hooks[command] = method | |
def on_receive(self, message, conn, addr): | |
""" | |
Routes the received message to the correct handler | |
:param message: the message that was received | |
:param conn: the socket connection that sent the message | |
:param addr: the address of the connection that sent the message | |
""" | |
# get the first word of the message, and use that to figure out what the command was | |
command = message.split()[0] | |
if command in self.hooks: | |
self.hooks[command](message, conn, addr) | |
result_message = "OK" | |
else: | |
result_message = "Bad command" | |
conn.send(result_message) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment