fsxNet Wiki

BBS Development & Resources

User Tools

Site Tools


tutorials:python_bbs:part_one

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
tutorials:python_bbs:part_one [2017/03/08 09:01]
avon created
tutorials:python_bbs:part_one [2018/03/29 01:58] (current)
Line 1: Line 1:
 ===== Python BBS - Part One ===== ===== Python BBS - Part One =====
  
 +I'll start off by explaining each section of the code, then give a listing of the entire source at the end. Please note, you will also need a config.ini that will look something like this:
  
 +<​code>​
 +[Main]
 +Port = 2023
 +</​code>​
 +
 +Firstly we need to import the modules we will be using.
 +
 +<code python>
 +import configparser
 +import sys
 +import socket
 +import threading
 +</​code>​
 +
 +Next we set up the variable that will store the flags that tell us which nodes are in use.
 +
 +<code python>
 +nodes = []
 +</​code>​
 +
 +Now we'll start with our main method, this is what will run when the program starts, it will come last in our python script.
 +
 +The first thing the script does, is check how it was invoked. We need a config file to be passed as an argument, and so we need to check that a config file argument was passed.
 +
 +Next, we read the config INI file.
 +
 +Then we create an instance of the server class passing it the port number from the config file.
 +
 +Finally, run the server.
 +
 +<code python>
 +if __name__ == "​__main__":​
 +
 +    if len(sys.argv) < 2:
 +
 +        print("​Usage python bbs.py config.ini"​)
 +        exit(1)
 +
 +    config = configparser.ConfigParser()
 +    config.read(sys.argv[1])
 +
 +    server = bbsServer(config.getint("​Main",​ "​Port"​))
 +
 +    server.run()
 +</​code>​
 +
 +Next we'll define the bbsServer class, it starts with an initialization routine.
 +
 +The initialization routine sets up the variables, gets a socket, binds that socket to a port and finally listens for connections.
 +
 +<code python>
 +class bbsServer():​
 +    def __init__(self,​ port, host='​0.0.0.0',​ nodemax=5):
 +        self.port = port
 +        self.host = host
 +        self.server = socket.socket(socket.AF_INET,​ socket.SOCK_STREAM)
 +        self.nodemax = nodemax
 +
 +        for index in range(nodemax):​
 +            nodes.append(False)
 +
 +        try:
 +            self.server.bind((self.host,​ self.port))
 +        except socket.error:​
 +            print("​Couldn'​t bind %s" % (socket.error))
 +            sys.exit()
 +
 +        self.server.listen(10)
 +</​code>​
 +
 +The next function of the bbsServer class is the run function, this is what we call from our main function. ​
 +
 +It starts up by displaying an informational message on the console.
 +
 +Next it goes into the main loop and listens for a connection. Once it has a connection, it continues on from the self.server.accept statement. It checks if there are any free nodes, and if there are, create a thread and start it. If not, just send the text "​BUSY"​ and disconnect.
 +
 +<code python>
 +    def run(self):
 +        print("​Starting BBS on port %s" % (self.port))
 +
 +        while True:
 +            conn, addr = self.server.accept()
 +
 +            for index in range(self.nodemax):​
 +                if nodes[index] == False:
 +
 +                    nodes[index] = True
 +                    threading.Thread(target=self.run_thread,​ args=(conn, addr, index)).start()
 +                    break
 +                else:
 +                    conn.sendall("​BUSY\r\n"​)
 +                    conn.close()
 +</​code>​
 +
 +Next we'll define the function which is running as a thread. We need access to the list of available nodes, so we can update it when we disconnect.
 +
 +Next we'll send a message to the user, and then get the users input indefinitely. This is where we'd begin to add our BBS logic, like logging in etc.
 +
 +Notice how it is encased in try-except blocks? This is because inside the getChar function which we'll define soon, it needs to detect if a client disconnects by closing the connection.
 +
 +Once this finishes, the BBS will print a message that the node is now offline, and update the list of available nodes, then exit.
 +
 +<code python>
 +    def run_thread(self,​ conn, addr, node):
 +        global nodes
 +
 +        try:
 +            self.sendString(conn,​ "​Welcome to my BBS on Node %s" % (node + 1))
 +
 +            while True:
 +                self.getChar(conn)
 +
 +
 +        except RuntimeError:​
 +            print("​Node %s hung up..." % (node + 1))
 +
 +        print("​Node %s offline."​ % (node + 1))
 +        nodes[node] = False
 +        conn.close()
 +        sys.exit()
 +</​code>​
 +
 +Finally we have the two helper functions to send strings and receive characters. ​
 +
 +You will notice that in getChar() conn.recv will return nothing if the connection is dropped, so we raise an exception, which is caught in the previous code. getChar() also sends the character back to the user, this is because later when we implement telnet commands, we will be turning echo off.
 +
 +sendString just converts a string into a bytearray for sending.
 +
 +<code python>
 +    def sendString(self,​ conn, text):
 +        conn.sendall(text.encode())
 +
 +    def getChar(self,​ conn):
 +
 +        c = conn.recv(1)
 +        if c == b'':​
 +            print("​Connection dropped"​)
 +            raise RuntimeError("​Socket connection dropped"​)
 +
 +        conn.send(c)
 +
 +        return str(c, '​utf-8'​)
 +</​code>​
 +
 +===== Entire source file =====
 +
 +<code python>
 +import configparser
 +import sys
 +import socket
 +import threading
 +
 +nodes = []
 +
 +class bbsServer():​
 +    def __init__(self,​ port, host='​0.0.0.0',​ nodemax=5):
 +        self.port = port
 +        self.host = host
 +        self.server = socket.socket(socket.AF_INET,​ socket.SOCK_STREAM)
 +        self.nodemax = nodemax
 +
 +        for index in range(nodemax):​
 +            nodes.append(False)
 +
 +        try:
 +            self.server.bind((self.host,​ self.port))
 +        except socket.error:​
 +            print("​Couldn'​t bind %s" % (socket.error))
 +            sys.exit()
 +
 +        self.server.listen(10)
 +
 +    def sendString(self,​ conn, text):
 +        conn.sendall(text.encode())
 +
 +    def getChar(self,​ conn):
 +
 +        c = conn.recv(1)
 +        if c == b'':​
 +            print("​Connection dropped"​)
 +            raise RuntimeError("​Socket connection dropped"​)
 +
 +        conn.send(c)
 +
 +        return str(c, '​utf-8'​)
 +
 +    def run_thread(self,​ conn, addr, node):
 +        global nodes
 +
 +        try:
 +            self.sendString(conn,​ "​Welcome to my BBS on Node %s" % (node + 1))
 +
 +            while True:
 +                self.getChar(conn)
 +
 +        except RuntimeError:​
 +            print("​Node %s hung up..." % (node + 1))
 +
 +        print("​Node %s offline."​ % (node + 1))
 +        nodes[node] = False
 +        conn.close()
 +        sys.exit()
 +
 +    def run(self):
 +        print("​Starting BBS on port %s" % (self.port))
 +
 +        while True:
 +            conn, addr = self.server.accept()
 +
 +            for index in range(self.nodemax):​
 +                if nodes[index] == False:
 +
 +                    nodes[index] = True
 +                    threading.Thread(target=self.run_thread,​ args=(conn, addr, index)).start()
 +                    break
 +                else:
 +                    conn.sendall("​BUSY\r\n"​)
 +                    conn.close()
 +
 +if __name__ == "​__main__":​
 +
 +    if len(sys.argv) < 2:
 +
 +        print("​Usage python bbs.py config.ini"​)
 +        exit(1)
 +
 +    config = configparser.ConfigParser()
 +    config.read(sys.argv[1])
 +
 +    server = bbsServer(config.getint("​Main",​ "​Port"​))
 +
 +    server.run()
 +</​code>​
tutorials/python_bbs/part_one.1488963706.txt.gz ยท Last modified: 2018/03/29 01:58 (external edit)