Nick Gilbert
Nick Gilbert

Reputation: 4240

Simple Multithreaded Web Server in Python

I'm trying to write a really simple web server with Python that is multithreaded. Right now the code looks like this

from socket import *
import threading
import time

class serverThread(threading.Thread):
    def __init__(self, serverPort):
        threading.Thread.__init__(self)
        self.serverPort = serverPort
        self.serverSocket = socket(AF_INET, SOCK_STREAM)
        self.connectionThreads = []
    def run(self):
        self.serverSocket.bind(('', self.serverPort))
        self.serverSocket.listen(1)
        while True:
            #Establish the connection
            print 'Ready to serve...'
            connectionSocket = self.serverSocket.accept()
            message = connectionSocket.recv(1024) #Get message
            print "Message recieved, opening new thread"
            self.connectionThreads.append(connectionThread())
            self.connectionThreads[len(connectionThreads)-1].start()
    def close(self):
        for t in self.connectionThreads:
            t.close()
        self.serverSocket.close()

class connectionThread (threading.Thread):
    def __init__(self, connSocket, message):
        threading.Thread.__init__(self)
        self.connSocket = connSocket
        self.message = message
    def run(self):
        try:
            filename = self.message.split()[1] #Getting requested HTML page
            f = open(filename[1:]) #Opening data stream from HTML
            outputdata = f.read() #Reading HTML page
            f.close() #Closing data stream from HTML
            self.connSocket.send("HTTP/1.0 200 OK\r\n") #Send one HTTP header line into socket
            for i in range(0, len(outputdata)): #Send the content of the requested file to the client
                self.connSocket.send(outputdata[i])
        except IOError: #Triggered if user requests bad link
            self.connSocket.send("404 Not Found") #Send response message for file not found
        finally:
            self.connSocket.close()

def main():
    server = serverThread(8031)
    server.start()
    end = raw_input("Press enter to stop server...")
    server.close()
    print "Program complete"

main()

My strategy is to start the web server in the main method and then start connection threads from the server thread. I have a simple helloworld html file in the same directory which I'm using to test it from localhost

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    HELLO WORLD!
</body>
</html>

However, when I go to type localhost:8031/hello.html in Chrome, it just loads forever saying "waiting for localhost". I have a print that is supposed to happen whenever a new message is received but it's not printing which tells me the server is not receiving messages properly

When I try to connect a second time Chrome flat out tells me the connection was refused and when I press enter to stop the server it seems to quit fine but then I get an error message

enter image description here

I'm frustrated and not sure how to debug this so any help would be greatly appreciated!

Upvotes: 0

Views: 3797

Answers (2)

tdelaney
tdelaney

Reputation: 77337

You have several errors in your code that I went through in the posted code below. General notes are:

  • use SO_REUSEADDR on the server so you don't have to wait to execute a second time
  • shutdown a socket before close or the underlying socket may stay open, hanging your browser
  • you close client sockets in two places... catch errors so that you can close them all
  • that 'Bad file descriptor' error is a good thing. It happened because you closed the socket and it lets your server thread know its time to terminate. You could catch it an exit gracefully if you want
  • you need a better way to deal with background threads. Right now your list just grows forever and over time most will be stale. I marked them as daemon threads so that the program would terminate at the end, but you need something else.

Fixing up the errors I came up with

from socket import *
import threading
import time

class serverThread(threading.Thread):
    def __init__(self, serverPort):
        threading.Thread.__init__(self)
        self.serverPort = serverPort
        self.serverSocket = socket(AF_INET, SOCK_STREAM)
        self.serverSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
        self.connectionThreads = []
    def run(self):
        self.serverSocket.bind(('', self.serverPort))
        self.serverSocket.listen(1)
        while True:
            #Establish the connection
            print 'Ready to serve...'
            connectionSocket,addr = self.serverSocket.accept()
            message = connectionSocket.recv(1024) #Get message
            print "Message recieved, opening new thread"
            self.connectionThreads.append(connectionThread(connectionSocket, message))
            self.connectionThreads[-1].daemon = 1
            self.connectionThreads[-1].start()
    def close(self):
        for t in self.connectionThreads:
            try:
                t.connSocket.shutdown(SHUT_RDWR)
                t.connSocket.close()
            except socket.error:
                pass
        self.serverSocket.shutdown(SHUT_RDWR)
        self.serverSocket.close()

class connectionThread (threading.Thread):
    def __init__(self, connSocket, message):
        threading.Thread.__init__(self)
        self.connSocket = connSocket
        self.message = message
    def run(self):
        try:
            filename = self.message.split()[1] #Getting requested HTML page
            f = open(filename[1:]) #Opening data stream from HTML
            outputdata = f.read() #Reading HTML page
            f.close() #Closing data stream from HTML
            self.connSocket.send("HTTP/1.0 200 OK\r\n") #Send one HTTP header line into socket
            for i in range(0, len(outputdata)): #Send the content of the requested file to the client
                self.connSocket.send(outputdata[i])
        except IOError: #Triggered if user requests bad link
            self.connSocket.send("404 Not Found") #Send response message for file not found
        finally:
            self.connSocket.shutdown(SHUT_RDWR)
            self.connSocket.close()

def main():
    server = serverThread(8031)
    server.daemon = 1
    server.start()
    end = raw_input("Press enter to stop server...")
    server.close()
    print "Program complete"

main()

Upvotes: 0

SD Dani
SD Dani

Reputation: 154

Apparently you are trying to access a file or socket you already closed.

See line:

sock, addr = self._sock.accept()

You are trying to accept a request with a socket that you already closed.

Check the file descriptor(could be a socket) that is usually represented as a number.

Upvotes: 1

Related Questions