实验描述
本编程作业的题目描述:
在本实验中,您将了解Web代理服务器的工作原理及其基本功能之一 —— 缓存。
您的任务是开发一个能够缓存网页的小型Web代理服务器。这是一个很简单的代理服务器,它只能理解简单的GET请求,但能够处理各种对象 —— 不仅仅是HTML页面,还包括图片。
通常,当客户端发出一个请求时,请求将被直接发送到Web服务器。然后Web服务器处理该请求并将响应消息发送客户端。为了提高性能,我们在客户端和Web服务器之间建立一个代理服务器。现在,客户端发送的请求消息和Web服务器返回的响应消息都要经过代理服务器。换句话说,客户端通过代理服务器请求对象。代理服务器将客户端的请求转发到Web服务器。然后,Web服务器将生成响应消息并将其传递给代理服务器,代理服务器又将其发送给客户端。
代码
您将在下面找到客户端的代码框架。 您需要完成代码框架。需要您填写代码的地方标有#Fill in start和#Fill in end。 每个地方都需要填写至少一行代码。
代理服务器的Python代码框架:
from socket import *
import sys
if len(sys.argv) <= 1:
print 'Usage : "python ProxyServer.py server_ip"\n[server_ip : It is the IP Address Of Proxy Server'
sys.exit(2)
# Create a server socket, bind it to a port and start listening
tcpSerSock = socket(AF_INET, SOCK_STREAM)
# Fill in start.
# Fill in end.
while 1:
# Strat receiving data from the client
print 'Ready to serve...'
tcpCliSock, addr = tcpSerSock.accept()
print 'Received a connection from:', addr
message = # Fill in start. # Fill in end.
print message
# Extract the filename from the given message
print message.split()[1]
filename = message.split()[1].partition("/")[2]
print filename
fileExist = "false"
filetouse = "/" + filename
print filetouse
try:
# Check wether the file exist in the cache
f = open(filetouse[1:], "r")
outputdata = f.readlines()
fileExist = "true"
# ProxyServer finds a cache hit and generates a response message
tcpCliSock.send("HTTP/1.0 200 OK\r\n")
tcpCliSock.send("Content-Type:text/html\r\n")
# Fill in start.
# Fill in end.
print 'Read from cache'
# Error handling for file not found in cache
except IOError:
if fileExist == "false":
# Create a socket on the proxyserver
c = # Fill in start. # Fill in end.
hostn = filename.replace("www.","",1)
print hostn
try:
# Connect to the socket to port 80
# Fill in start.
# Fill in end.
# Create a temporary file on this socket and ask port 80
for the file requested by the client
fileobj = c.makefile('r', 0)
fileobj.write("GET "+"http://" + filename + " HTTP/1.0\n\n")
# Read the response into buffer
# Fill in start.
# Fill in end.
# Create a new file in the cache for the requested file.
# Also send the response in the buffer to client socket and the corresponding file in the cache
tmpFile = open("./" + filename,"wb")
# Fill in start.
# Fill in end.
except:
print "Illegal request"
else:
# HTTP response message for file not found
# Fill in start.
# Fill in end.
# Close the client and the server sockets
tcpCliSock.close()
# Fill in start.
# Fill in end.
我的python实现代码(参考一位github博主的):
from socket import *
import sys
import os
#if len(sys.argv) <= 1:
# print ('Usage : "python ProxyServer.py server_ip"\n[server_ip : It is the IP Address Of Proxy Server')
# sys.exit(2)
# 为代理服务器 创建一个TCP套接字、绑定端口号、设置服务器最大连接客户机数量为3(因为是多线程Web服务器)
tcpSerSock = socket(AF_INET, SOCK_STREAM)
tcpSerSock.bind(('',1234))
tcpSerSock.listen(3)
while 1:
# 准备从客户机接收响应消息
print ('准备从客户机接收响应消息...')
tcpCliSock, addr = tcpSerSock.accept()
print ('接收到一个连接,来自:', addr)
# 获取客户机发送过来的消息
message = tcpCliSock.recv(4096).decode()
print ('客户机发送过来的消息:',message)
# 从消息从提取出文件名
filename = message.split()[1].partition("//")[2].replace('/','_')
print ('文件名:',filename)
fileExist = "false"
try:
# 检查要访问的文件是否在此Web代理服务器中
print ('开始检查代理服务器中是否存在文件:',filename)
f = open(filename, "r")
outputdata = f.readlines()
fileExist = "true"
print ('文件存在在代理服务器中')
# 文件存在在代理服务器中,返回响应消息(请求的web网页)给客户机
#tcpCliSock.send("HTTP/1.0 200 OK\r\n")
#tcpCliSock.send("Content-Type:text/html\r\n")
for i in range(0,len(outputdata)):
tcpCliSock.send(outputdata[i].encode())
print ('Read from cache')
# 文件不在代理服务器当中,代理服务器就会向远端服务器请求消息,保存好了再返回给客户机
except IOError:
if fileExist == "false":
print ('文件不在代理服务器当中,开始向远端服务器请求网页')
# 在代理服务器中创建一个TCP套接字
c = socket(AF_INET,SOCK_STREAM)
hostn = message.split()[1].partition("//")[2].partition("/")[0]
print ('Host Name: ',hostn)
try:
# TCP套接字c 连接到远端服务器80端口
c.connect((hostn,80))
print ('套接字连接到主机的80号端口')
# 在套接字上创建一个临时的文件,而且要向80端口(远端服务器)请求信息
#for the file requested by the client
#fileobj = c.makefile('r', 0)
#fileobj.write("GET "+"http://" + filename + " HTTP/1.0\n\n")
# 代理服务器 读取 从远端服务器从响应的消息
c.sendall(message.encode())
buff = c.recv(4096)
tcpCliSock.sendall(buff)
# 在代理服务器中 创建一个新的文件 用于存放请求过来的消息
# 将代理服务器中的响应 发送到客户端套接字,并将相应的文件发送到缓存中
tmpFile = open("./" + filename,"w")
tmpFile.writelines(buff.decode().replace('\r\n','\n'))
tmpFile.close();
except:
print ("代理服务器向远端服务器请求网页失败")
else:
# 如果客户机请求的消息在远端服务器也找不到,就说明请求不到了
print ('文件存在,但是还是出现了 IOError异常')
# 关闭客户机 和 代理服务器的TCP套接字
print ('关闭套接字:tcpCliSock')
tcpCliSock.close();
# 关闭代理服务器 和 服务器的TCP套接字
print ('关闭套接字:tcpSerSock')
tcpSerSock.close();
运行结果:
- 直接向远端服务器请求web网页:
http://gaia.cs.umass.edu/wireshark-labs/INTRO-wireshark-file1.html
- 修改浏览器设置(让网页直接请求本地的代理服务器)
- 重新打开网页,发现打不开,因为此时还未启动代理服务器(自己写的
ProxyServer.py
)
4.启动代理服务器(直接运行py代码)
- 重新打开web网页,开始第一次请求代理服务器(代理服务器中找不到所需的web网页,然后会转向请求远端服务器)
- 从远端服务器会响应代理服务器的请求,从而会生成一个缓存文件(位于代理服务器同一目录)。
- 重新打开web网页,发现可以直接从代理服务器中请求到网页了。
可选练习【待完成】
- 目前代理服务器不能处理错误。这可能会导致一些问题,当客户端请求一个不可用的对象时,由于“404 Not Found”响应通常没有响应正文,而代理服务器会假设有正文并尝试读取它。
- 当前代理服务器只支持HTTP GET方法。通过添加请求体来增加对POST的支持。
- 缓存:每当客户端发出特定请求时,典型的代理服务器会缓存网页。缓存的基本功能如下:当代理获得一个请求时,它将检查请求的对象是否已经在缓存中,如果是,则从缓存返回对象,从而不用联系服务器。如果对象未被缓存,则代理从服务器获取该对象,向客户端返回该对象,并缓存一个拷贝以备将来的请求。在实际环境下,代理服务器必须验证被缓存的响应是否仍然有效,并且它们能对客户端正确响应。您可以在RFC 2068中阅读有关缓存及其在HTTP中实现方式的更多细节。添加上述简单的缓存功能。您不需要实现任何替换或验证策略。然而您需要实现的是,将请求和响应写入磁盘(即缓存)并能从磁盘中获取它们,用于缓存被请求命中时。为此,您需要在代理中实现一些内部数据结构,以便跟踪哪些请求处于缓存中时,以及它们在磁盘上的位置。您也可以将此数据结构保存在内存中,因为没有必要关机之后持续保存这些数据。
参考资料:
https://github.com/moranzcw/Computer-Networking-A-Top-Down-Approach-NOTES