来自书籍《计算机网络-自顶向下方法-第6版(课本)》第120页,第二章应用层的课后题第一题。

基础题目

题目:在这个编程作业中,你将用Python语言开发一个简单的Web服务器,它仅能处理一个请求。具体而言,你的Web服务器将:(1)当一个客户(浏览器)联系时创建一个连接套接字;(2)从这个连接接收HTTP请求;(3)解释该请求以确定所请求的特定文件;(4)从服务器的文件系统获得请求的文件;(5)创建一个由请求的文件组成的HTTP响应报文,报文前面有首部行;(6)经TCP连接向请求的浏览器发送响应。如果浏览器请求一个在该服务器中不存在的文件,服务器应当返回一个“404 Not Found”差错报文。

参考或者说学习了github上moranzcw的仓库:Computer-Networking-A-Top-Down-Approach-NOTES的内容,完成了这道题。

代码https://github.com/moranzcw如下:

# import socket module
from socket import *

serverSocket = socket(AF_INET, SOCK_STREAM)
# Prepare a sever socket
# Fill in start
# 定义ip和端口号,然后bind绑定socket
host = ''
port = 6789
serverSocket.bind((host, port))
serverSocket.listen(1)
# Fill in end
while True:
    # Establish the connection
    print('Ready to serve...')
    connectionSocket, addr = serverSocket.accept()
    # Fill in start  #Fill in end
    try:
        message = connectionSocket.recv(1024)  # Fill in start  #Fill in end
        filename = message.split()[1]
        f = open(filename[1:])
        outputdata = f.read()  # Fill in start  #Fill in end
        # Send one HTTP header line into socket
        # Fill in start
        header = ' HTTP/1.1 200 OK\nConnection: close\nContent-Type: text/html\nContent-Length: %d\n\n' % (
            len(outputdata))
        connectionSocket.send(header.encode())

        # Fill in end

        # Send the content of the requested file to the client
        for i in range(0, len(outputdata)):
            connectionSocket.send(outputdata[i].encode())
        connectionSocket.close()
    except IOError:
        # Send response message for file not found
        # Fill in start
        header = ' HTTP/1.1 404 Found'
        connectionSocket.send(header.encode())
        connectionSocket.close()
        # Fill in end

        # Close client socket
        # Fill in start
        # Fill in end
serverSocket.close()

同时在这个文件的同级目录下,有一个html文件:helloWorld.html

<head>Hello world!</head>
<body>abcdefg</body>

在Python中启动项目:

然后打开浏览器,在地址栏内输入:http://localhost:6789/helloWorld.html

可以看到浏览器正常收到HTTP响应报文,显示正常。

如果输入的地址不是helloWorld.html,则会报404。

进阶题目

进阶题目1

题目也是翻译自github老哥之手,感谢。

题目:目前,这个Web服务器一次只处理一个HTTP请求。请实现一个能够同时处理多个请求的多线程服务器。使用线程,首先创建一个主线程,在固定端口监听客户端请求。当从客户端收到TCP连接请求时,它将通过另一个端口建立TCP连接,并在另外的单独线程中为客户端请求提供服务。这样在每个请求/响应对的独立线程中将有一个独立的TCP连接。

不难得知,只要用线程包装一下处理socket的方法即可。

代码如下:

# import socket module
import threading
from socket import *


def sonTcpConnect(connectionSocket):
    # Fill in start  #Fill in end
    try:
        print("thread:  " + threading.currentThread().name + " is working...")
        message = connectionSocket.recv(1024)  # Fill in start  #Fill in end
        print("msg", message)
        filename = message.split()[1]
        f = open(filename[1:])
        outputdata = f.read()  # Fill in start  #Fill in end
        # Send one HTTP header line into socket
        # Fill in start
        header = ' HTTP/1.1 200 OK\nConnection: close\nContent-Type: text/html\nContent-Length: %d\n\n' % (
            len(outputdata))
        connectionSocket.send(header.encode())

        # Fill in end

        # Send the content of the requested file to the client
        for i in range(0, len(outputdata)):
            connectionSocket.send(outputdata[i].encode())
        connectionSocket.close()
    except IOError:
        # Send response message for file not found
        # Fill in start
        header = ' HTTP/1.1 404 Found'
        connectionSocket.send(header.encode())
        connectionSocket.close()
        # Fill in end

        # Close client socket
        # Fill in start
        # Fill in end


serverSocket = socket(AF_INET, SOCK_STREAM)
# Prepare a sever socket
# Fill in start
# 定义ip和端口号,然后bind绑定socket
host = ''
port = 6789
serverSocket.bind((host, port))
serverSocket.listen(100)
# Fill in end
while True:
    # Establish the connection
    print('Ready to serve...')
    connectionSocket, addr = serverSocket.accept()
    t = threading.Thread(target=sonTcpConnect, args=(connectionSocket,))
    t.start()
serverSocket.close()

有一个要注意的地方是,在53行,args这个参数的括号内必须要加上逗号,原因是因为args是一个元组,需要在元素后面增加逗号。(资料来源:https://blog.csdn.net/chpllp/article/details/54381141

多次在浏览器请求之后,控制台输出如下:

说实话感觉可能有些bug ,// TODO记得修

进阶题目2:

题目:

不使用浏览器,编写自己的HTTP客户端来测试你的服务器。您的客户端将使用一个TCP连接用于连接到服务器,向服务器发送HTTP请求,并将服务器响应显示出来。您可以假定发送的HTTP请求将使用GET方法。 客户端应使用命令行参数指定服务器IP地址或主机名,服务器正在监听的端口,以及被请求对象在服务器上的路径。以下是运行客户端的输入命令格式。

client.py server_host server_port filename

意思就是用python发送HTTP请求到服务器就可以了。

from socket import *
import sys
clientSocket = socket(AF_INET, SOCK_STREAM)
host = str(sys.argv[1])
port = int(sys.argv[2])

clientSocket.connect((host, port))
msg = 'GET ' + str(sys.argv[3])
clientSocket.send(msg.encode())
result = b""
while True:
    recv_data = clientSocket.recv(1024)
    if recv_data:
        result += recv_data
        if result.rfind(b"0\r\n") != -1:
            break
    else:
        break

print('Received', repr(result))

这里要注意的是,如果没有用While True循环接收,可能只会接收到HTTP响应报文的请求头了。

结果如下: