python实现WebServer(Socket)
本题主要解答了《计算机网络》的第二章编程题的第一个作业。
V1
要求:用Python开发一个简单的Web服务器,仅能处理一个请求。(1)当一个Client连接时创建一个Socket;(2)从这个连接接受HTTP请求;(3)解释该请求以确定所请求得特定文件;(4)从Server得文件系统获得请求得文件;(5)创建一个由请求得文件组成得响应报文;(6)经TCP连接向请求的浏览器发送响应。如果浏览器请求一个不存在的文件,Server应当返回一个“404 Not Found”的差错报码。
from socket import *
import sys #In order to terminate the program
serverSocket = socket(AF_INET, SOCK_STREAM)
# Prepare a server socket
serverPort = 6789
serverSocket.bind(("", serverPort))
serverSocket.listen(1)
while True:
# Establish the connection
print('Ready ro serve...')
connectionSocket, addr = serverSocket.accept()
try:
message = connectionSocket.recv(2048).decode()
filename = message.split()[1]
f = open(filename[1:])
outputdata = f.readlines()
# Send one HTTP header line into socket
OkMSG = "HTTP/1.1 200 OK \r\n\r\n"
connectionSocket.send(OkMSG.encode())
# Send the content of the requsted file to the Client
for i in range(0, len(outputdata)):
connectionSocket.send(outputdata[i].encode())
connectionSocket.send("\r\n".encode())
connectionSocket.close()
except IOError:
# Send response message for file not found
ErrMSG = 'HTTP/1.1 404 Not Found \r\n\r\n'
connectionSocket.send(ErrMSG.encode())
connectionSocket.send("<html><head></head><body><h1>404 Not Found</h1></body></html>\r\n".encode())
# Close client socket
connectionSocket.close()
serverSocket.close()
sys.exit()
运行:
- 在命令行中输入ipconfig(Windows)或者ifconfig(Linux)查看IP地址,或者使用回环地址127.0.0.1;
- 运行上述代码;
- 在浏览器中输入:http://IP地址:端口号/文件名。例如:http://192.168.121.131:6789/helloworld.html,搜索即可。
注意点:
- html文件和py文件放在同一路径下(同一文件夹里)
- 读文件时read和readlines效果不同,具体可以上网查
- 我在pycharm运行时split那里会报错“list index out of range”,但在Ubuntu系统中不会有这个问题,可能是操作系统后者环境的配置出了问题?
- 有些人运行会出gdk的报错,这时候修改read为readlines或者去掉message接收消息时的decode()可能会有用。但至于为什么还不清楚。
- 有人在Linux里面运行时,输入不存在的文件进行搜索不会显示“404 Not Found”,原因是你用python命令运行的代码,而一些Ubuntu的版本终端内置的是python2,而python2和python3有很多不兼容,这时候用python3运行就没有问题了。
改进(V2)
目前,网络服务器一次只处理一个HTTP请求。实现能够同时服务多个请求的多线程服务器。使用线程,首先创建一个主线程,其中修改后的服务器在固定端口侦听客户端。当它接收到来自客户端的TCP连接请求时,它将通过另一个端口设置TCP连接,并在一个单独的线程中服务于客户端请求。每个请求/响应对在单独的线程中将有一个单独的TCP连接。
# import socket module
from socket import *
import threading
import sys #In order to terminate the program
def msg_handle(connectionSocket, addr):
try:
print('A client connected...')
msg = connectionSocket.recv(2048).decode()
filename = msg.split()[1]
f = open(filename[1:])
outputdata = f.readlines()
connectionSocket.send("HTTP/1.1 200 OK \r\n\r\n".encode())
for i in range(0, len(outputdata)):
connectionSocket.send(outputdata[i].encode())
connectionSocket.send("\r\n".encode())
connectionSocket.close()
except IOError:
connectionSocket.send("HTTP/1.1 404 Not Found \r\n\r\n".encode())
connectionSocket.send("<html><head></head><body><h1>404 Not Found</h1></body></html>\r\n".encode())
connectionSocket.close()
def init():
global serverSocket
serverSocket = socket(AF_INET, SOCK_STREAM)
serverSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
serverPort = 6677
serverSocket.bind(('', serverPort))
serverSocket.listen(5)
print('Ready to serve...')
def accept_client():
while True:
connectionSocket, addr = serverSocket.accept()
cthread = threading.Thread(target = msg_handle, args = (connectionSocket, addr))
cthread.start()
if __name__ == '__main__':
init()
sthread = threading.Thread(target = accept_client)
sthread.start()
sys.exit()
V1和V2在Ubuntu里运行时都有一个问题,会报错:
OSError: [Errno 98] Address already in use
此时用下列命令查看:
netstat -tunlp
会看到:
再用 kill -9 16012(进程号)杀死进程即可。
出错的原因是上次终止代码运行使用了CTRL+z来暂停运行,而采用CTRL+C来停止就没有这个问题了。
V1和V2还有另外一个问题,好像只有本机的浏览器可以访问。我用手机就访问不到了。访问设备和运行代码的设备必须在同一局域网下才能进行连接。据说是IP地址的原因,因为我们查到的IP地址并不是“真正的IP地址”,相关的知识另外了解吧。