Search code examples
pythonsocketspython-2.7ioerror

Python IO error all the time attempting to open a file


From command line

client.py Aaron 12000 HelloWorld.html GET

client.py

def main(argv):
    serverName = argv[0]
    serverPort = int(argv[1])
    fileName   = argv[2]
    typeOfHttpRequest = argv[3]
    clientSocket = socket(AF_INET, SOCK_STREAM)
    clientSocket.connect((serverName, serverPort))
    clientSocket.send(typeOfHttpRequest + " " + fileName + " HTTP/1.1\r\n\r\n")
    content = clientSocket.recv(1024)
    print content
    clientSocket.close()

if __name__ == "__main__":
   main(sys.argv[1:])

server.py

while True:
    #Establish the connection
    print 'Ready to serve....'
    connectionSocket, addr = serverSocket.accept()

    try:
        message = connectionSocket.recv(1024)
        typeOfRequest = message.split()[0]
        filename = message.split()[1]
        print typeOfRequest
        print filename
        f = open(filename[1:])
        outputdata = f.read()

        if typeOfRequest == 'GET':
                for i in range(0, len(outputdata)):
                    connectionSocket.send(outputdata[i])
                connectionSocket.close()
        elif typeOfRequest == 'HEAD':
            connectionSocket.send(True)
    except IOError:
        connectionSocket.send('HTTP/1.1 404 Not Found')
        connectionSocket.close()

serverSocket.close()

I have put HelloWorld.html in the same directory as server.py but this always generates an IOError. Anyone know why it might be the case?

  • The files are located in C:\Networking

  • os.getcwd shows C:\Networking

  • HelloWorld.html is located in C:/networking/HelloWorld.html

  • Filename prints out correctly.

enter image description here


Solution

  • As you might have noticed, you were trying to strip the / from the beginning of the URL, though it was not there. However, there are other errors in your code, which mean that it does not work like a HTTP server:

    First of all, recv() is not guaranteed to read all the data - even if there would be total of 1024 bytes written to a socket, recv(1024) could return just 10 bytes, say. Thus it is better to do in a loop:

    buffer = []
    while True:
        data = connection_socket.recv(1024)
        if not data:
            break
        buffer.append(data)
    
    message = ''.join(buffer)
    

    Now message is guaranteed to contain everything.

    Next, to handle the header lines of the request, you can use

    from cStringIO import StringIO
    message_reader = StringIO(message)
    first_line = next(message_reader)
    type_of_request, filename = message.split()[:2]
    

    With this it is easier to extend your code for more complete HTTP support.

    Now open the file with open, with with statement:

    with open(filename) as f:
        output_data = f.read()
    

    This ensures that the file is closed properly too.

    Finally, when you respond to the request, you should answer with HTTP/1.0, not HTTP/1.1 as you are not supporting the full extent of HTTP/1.1. Also, even an OK response needs to respond with full headers, say with:

    HTTP/1.1 200 OK
    Server: My Python Server
    Content-Length: 123
    Content-Type: text/html;charset=UTF-8
    
    data goes here....
    

    Thus your send routine should do that:

    if typeOfRequest == 'GET':
        headers = ('HTTP/1.0 200 OK\r\n'
            'Server: My Python Server\r\n'
            'Content-Length: %d\r\n'
            'Content-Type: text/html;charset=UTF-8\r\n\r\n'
            'Connection: close\r\n'
        ) % len(output_data)
    
        connection_socket.sendall(headers)
        connection_socket.sendall(output_data)
    

    Notice how you can use sendall to send all data from a string.