最后一次更新于 2019/07/10
html
此任务是从新建立第3讲(延迟,丢失和吞吐量)中讨论的ping客户端。
Ping 是一个用于在计算机网络中测量延迟和丢失的工具。
在实际应用中,咱们能够经过 ping
命令分析判断网络失败的缘由。固然,这类信息也可用于帮助咱们选择性能更佳的IP地址做为代理服务器。前端
Ping 一般使用 Internet 控制消息协议 (ICMP) 报文来测量网络中的延迟和丢失:本机在 ICMP 包中发送回响请求(ICMP类型代码为8)给另外一个主机。而后,主机解包数据包并提取ICMP类型代码并匹配请求和回复之间的ID。若是远程主机的响应报文ICMP类型代码为0,而后咱们能够计算发送请求和接收回复之间通过的时间,进而精确的计算两台主机之间网络的延迟。python
注意: IP数据报和ICMP错误代码的结构(ICMP类型代码为3)以下所示。因特网校验和也是数据包的重要部分,但它不是本函数实现的核心。git
基于上述原理,首先,须要建立一个与协议ICMP关联的套接字,并设置超时以控制用于接收数据包的时间套接字。github
# 运行特权TCP套接字,1是与协议ICMP关联的套接字模块常量。 icmp_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, 1) icmp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, timeout)
建立套接字后,须要实现一个函数来构建,打包并将ICMP数据包发送到目标主机。
如图所示,若是建立一个32字节大小的数据包,那么只有四个字节长度来存储有效负载数据。
所以,以浮点格式(4个字节)存储当前时间帧是比较好的解决办法。
可是,因为精度损失,不能使用此数据来计算总网络延迟。
"!" 可是,因为精度损失,我永远不会使用此数据来计算总网络延迟。web
构建和打包ICMP数据包源代码:小程序
def receive_one_ping(icmp_socket, port_id, timeout, send_time): while True: # 1. 等待套接字并获得回复。 wait_for_data = select.select([icmp_socket], [], [], timeout) # 2. 一旦接受,记录当前时间。 data_received = time.time() rec_packet, addr = icmp_socket.recvfrom(1024) ip_header = rec_packet[8: 12] icmp_header = rec_packet[20: 28] payload_size = struct.calcsize("!f") # 3. 解压包首部行查找有用的信息。 type, code, checksum, id, sequence = struct.unpack("!bbHHh", icmp_header) # 4. 检查收发之间的 ID 是否匹配。 if type == 0 and id == port_id: # type should be 0 ttl = struct.unpack("!b", ip_header[0:1])[0] delay_time = data_received - send_time # 5. 返回比特大小,延迟率和存活时间。 return payload_size * 8, delay_time, ttl elif type == 3 and code == 0: return 0 # 网络没法到达的错误。 elif type == 3 and code == 1: return 1 # 主机没法到达的错误。
当从同一主机得到全部ping测试结果时,须要另外一个函数来显示全部测量的最小时间,平均时间和最大延迟。浏览器
def ping_statistics(list): max_delay = list[0] mini_delay = list[0] sum = 0 for item in list: if item >= max_delay: max_delay = item elif item <= mini_delay: mini_delay = item sum += item avg_delay = int(sum / (len(list))) return mini_delay, max_delay, avg_delay
最后一件事是处理异常。须要处理不一样的ICMP错误代码和返回值的超时。代码以下所示:缓存
def ping(host, count_num="4", time_out="1"): # 1. 查找主机名,将其解析为IP地址。 ip_addr = socket.gethostbyname(host) successful_list = list() lost = 0 error = 0 count = int(count_num) timeout = int(time_out) timedout_mark = False for i in range(count): # i 是序列的值 # 打印报文首部行 ...... try: # 2. 调用 doOnePing 函数。 ping_delay = do_one_ping(ip_addr, timeout, i) # 3. 打印出返回的延迟信息。 if ping_delay == 0 or ping_delay == 1: # 获取本机的 IP 地址。 ip_addr = socket.gethostbyname(socket.gethostname()) print("Reply from {ipAdrr}: ".format(ipAdrr = ip_addr), end = "") result = "Destination host unreachable." if ping_delay == 0 else \ "Destination net unreachable." print(result) error += 1 else: bytes, delay_time, ttl = ping_delay[0], int(ping_delay[1] * 1000), \ ping_delay[2] print("Reply from {ipAdrr}: ".format(ipAdrr = ip_addr), end = "") # 若是能够成功接收数据包, # 在list里追加延迟时间。 successful_list.append(delay_time) # 若是延迟时间小于 1 ms,则记为0。 ...... except TimeoutError: # 超时类型 lost += 1 print("Request timed out.") # 若是它不老是超时的状况, # 咱们须要计算最大延迟时间。 if timedout_mark is False: timedout_mark = True time.sleep(1) # 每秒。 # 4. 继续执行直到结束。 ......
C:\Users\asus\Desktop\lab_solution\ICMP Ping>ICMPPing.py>ping www.baidu.com Pinging www.baidu.com [111.13.100.92] with 32 of data: Reply from 111.13.100.92: bytes = 32 time = 28ms TTL = 51. Reply from 111.13.100.92: bytes = 32 time = 35ms TTL = 51. Reply from 111.13.100.92: bytes = 32 time = 33ms TTL = 51. Reply from 111.13.100.92: bytes = 32 time = 31ms TTL = 51. Ping statistics for 111.13.100.92: Packet: Sent = 4, Received = 4, lost = 0 (0% loss). Approximate round trip times in milli - seconds: Minimum = 28ms, Maximum = 35ms, Average = 31ms. C:\Users\asus\Desktop\lab_solution\ICMP Ping>ICMPPing.py>ping google.com Pinging google.com [172.217.161.174] with 32 of data: Request timed out. Request timed out. Request timed out. Request timed out. Ping statistics for 172.217.161.174: Packet: Sent = 4, Received = 0, lost = 4 (100% loss). C:\Users\asus\Desktop\lab_solution\ICMP Ping>ICMPPing.py>ping www.baidu.com -n 6 Pinging www.baidu.com [111.13.100.92] with 32 of data: Reply from 111.13.100.92: bytes = 32 time = 29ms TTL = 51. Reply from 111.13.100.92: bytes = 32 time = 46ms TTL = 51. Reply from 111.13.100.92: bytes = 32 time = 33ms TTL = 51. Reply from 111.13.100.92: bytes = 32 time = 44ms TTL = 51. Reply from 111.13.100.92: bytes = 32 time = 36ms TTL = 51. Reply from 111.13.100.92: bytes = 32 time = 35ms TTL = 51. Ping statistics for 111.13.100.92: Packet: Sent = 6, Received = 6, lost = 0 (0% loss). Approximate round trip times in milli - seconds: Minimum = 29ms, Maximum = 46ms, Average = 37ms. C:\Users\asus\Desktop\lab_solution\ICMP Ping>ICMPPing.py>ping www.baidu.com -n 6 -w 2 Pinging www.baidu.com [111.13.100.92] with 32 of data: Reply from 111.13.100.92: bytes = 32 time = 25ms TTL = 51. Reply from 111.13.100.92: bytes = 32 time = 35ms TTL = 51. Reply from 111.13.100.92: bytes = 32 time = 20ms TTL = 51. Reply from 111.13.100.92: bytes = 32 time = 55ms TTL = 51. Reply from 111.13.100.92: bytes = 32 time = 34ms TTL = 51. Reply from 111.13.100.92: bytes = 32 time = 37ms TTL = 51. Ping statistics for 111.13.100.92: Packet: Sent = 6, Received = 6, lost = 0 (0% loss). Approximate round trip times in milli - seconds: Minimum = 20ms, Maximum = 55ms, Average = 34ms.
此任务是从新建立第3讲(延迟,丢失和吞吐量)中的路由追踪工具。这用于测量主机和到达目的地的路径上的每一跳之间的延迟。在实际应用中,路由追踪能够找到源主机和目标主机之间的路由器以及到达每一个路由器所需的时间。服务器
如上图所示,源主机使用ICMP echo请求报文,但有一个重要的修改:
存活时间(TTL)的值初始为1。这能够确保咱们从第一跳得到响应。 一旦报文到达路由器,TTL计数器就会递减。
当TTL达到0时,报文将返回到源主机,ICMP 类型为11(已超出TTL且IP数据报还没有到达目标并被丢弃)。
每次增长TTL都会重复此过程,直到咱们收到回复。若是echo回复ICMP类型为0,则表示IP数据报已到达目的地。
而后咱们就能够中止运行路由追踪的脚本了。在此过程当中,可能会发生异常而且咱们须要处理错误代码与 ICMP Ping 相同。
基于上述原理,首先,除了建立一个与协议ICMP关联的套接字并设置超时来控制用于接收数据包的套接字外,还须要经过socket.setsockopt(level, optname, value)
函数设置套接字的TTL。
client_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, 1) # ICMP client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO , time_out) client_socket.setsockopt(socket.IPPROTO_IP, socket.IP_TTL, struct.pack('I', ttl))
建立套接字后,须要实现一个函数来构建,打包并将ICMP数据包发送到目标主机。这部分代码和在 ICMP Ping 中的构建和打包ICMP数据包源代码一致。
下一步是等待并收到回复。套接字将一直等待,直到收到数据包或达到超时限制。 经过 ICMP 回响报文发现并报告没法访问目标主机和没法访问目标网路。这部分和接受数据包源码类似但路由追踪须要额外记录每次访问到路由器的IP地址。代码以下所示:
def receive_one_trace(icmp_socket, send_time, timeout): try: # 1. 等待套接字并获得回复。 ... Similar to Receive Packet Source ... # 2. 一旦接受,记录当前时间。 rec_packet, retr_addr = icmp_socket.recvfrom(1024) # 3. 解压包首部行查找有用的信息。 ... Similar to Receive Packet Source ... # 4. 经过代号类型检查数据包是否丢失。 ... Similar to Receive Packet Source ... except TimeoutError : print_str = "* " # 超时。 finally: ..... # 打印延迟时间。 # 返回当前路由器的IP地址。 # 若是超时的话,直接返回字符串。 try: retre_ip = retr_addr[0] except IndexError: retre_ip = "Request timeout" finally: return retre_ip
最后一件事是为每一个路由器实现重复测量,解析在对各自主机名的响应中找到的IP地址并处理异常。代码以下所示:
def trace_route(host, timeout=2): # 可配置超时,使用可选参数设置。 # 1. 查找主机名,将其解析为IP地址。 ip_addr = socket.gethostbyname(host) ttl = 1 print("Over a maximum of {max_hop} hops:\n".format(max_hop = MAX_HOP)) print("Tracing route to " + host + " [{hostIP}]:".format(hostIP = ip_addr)) for i in range(MAX_HOP): sys.stdout.write("{0: >3}".format(str(ttl) + "\t")) cur_addr = do_three_trace(ip_addr, ttl, i, timeout) try: sys.stdout.write("{0:<}".format(" " + socket.gethostbyaddr(cur_addr)[0] + " [" + cur_addr + "]" + "\n")) except (socket.herror, socket.gaierror): sys.stdout.write("{0:<}".format(" " + cur_addr + "\n")) if cur_addr == ip_addr : break ttl += 1 sys.stdout.write("\nTrace complete.\n\n")
Over a maximum of 30 hops: Tracing route to www.baidu.com [111.13.100.91]: 1 16 ms 15 ms 15 ms 10.129.0.1 ...... # All successful 5 * * * Request timeout 6 * * * Request timeout ...... # All successful 9 * * * Request timeout ...... # All successful 13 * * * Request timeout 14 22 ms 22 ms 21 ms 111.13.100.91 Trace complete. C:\Users\asus\Desktop\lab_solution\Traceroute>Traceroute.py>tracert 10.129.21.147 Over a maximum of 30 hops: Tracing route to 10.129.21.147 [10.129.21.147]: 1 Host unreachable * Host unreachable DESKTOP-6VPPJQ8 [10.129.34.15] 2 * Host unreachable * DESKTOP-6VPPJQ8 [10.129.34.15] ...... All situations are the same 29 Host unreachable * Host unreachable DESKTOP-6VPPJQ8 [10.129.34.15] 30 * Host unreachable * DESKTOP-6VPPJQ8 [10.129.34.15] Trace complete.
此任务是构建一个简单的HTTP Web服务器。根据第4讲(Web 和 HTTP)中学习到的知识,Web 服务器是Internet的基础部分,它们提供咱们熟悉的网页和内容。
网页由对象组成,这些对象能够是HTML文件,JPEG图像,Java小程序等。HTTP流量一般绑定到端口80,端口8080是经常使用的替代方案。所以,虚拟主机(机器)使用本地端口号80和8080。
HTTP/1.1 含有许多类型的 HTTP 请求,在本任务中,只考虑 HTTP GET 请求。
以下图所示,一个简单的 web 服务器从前端接受 HTTP 请求报文。收到此请求后,简单 Web 服务器将从邮件中提取所请求对象的路径,而后尝试从硬盘中检索请求的对象。若是它成功找到硬盘中的对象,它会将对象发送回具备相应首部行的客户端(其中包含Status-Code 200)。不然,它将使用HTTP响应报文(将包含404 Not Found"状态将"Not Found"网页发送到客户端。
line)". HTTP 请求和响应报文的格式已在下图5.(a)和5.(b)显示。
基于上述原理,首先,建立一个支持 IPv4 的套接字并将其绑定在高于1024的端口上。Web服务器应该同时监听5个请求,并具备处理多个并发链接的能力。
Web 服务器运行源码:
def start_server(server_port , server_address): # 将 web 服务器绑定到可配置端口,定义为可选参数。 # 1. 常见一个服务器套接字。 server_socket = socket(AF_INET, SOCK_STREAM) #IPv4 # 2. 将服务器套接字绑定到服务器地址和服务器端口。 server_socket.bind(("", server_port)) # 3. 持续监听与服务器套接字的链接。 server_socket.listen(5) while True: # 4. 当接受链接时,调用 handleRequest 函数,传递新的链接套接字。 connection_socket , (client_ip, client_port) = server_socket.accept() # 建立一个多线程服务器实现,可以处理多个并发链接。 start_new_thread(handle_request , (connection_socket , client_ip, client_port)) # 5. 关闭服务器端的套接字。 server_socket.close() # 否则服务器会一直监听,不会主动关闭。 # 从这里开始运行。 server_port = int(sys.argv[1]) server_address = gethostbyname(gethostname()) start_server(server_port)
建立套接字后,web 服务器须要处理HTTP GET请求。在处理以前,建立了一个StrProcess类来重写字符串模块中的split方法。用空格分割一个字符串,只处理请求行。所以,当它找到字符"r"时,它将中止处理并返回结果。
StrProcess 类源码:
class StrProcess(str): def __init__(self, str): """用字符型变量 str 初始该属性""" self.str = str def split_str(self): """实现分割操做""" spilt_list = [] start = 0 for i in range(len(self.str)): if self.str[i] == " ": spilt_list.append(self.str[start:i]) start = i + 1 if self.str[i] == "\r": break return spilt_list[1]
最后一件事是处理 HTTP GET 请求和异常。因为非持久性HTTP,不须要在true循环时写入以接收HTTP请求报文。若是套接字收到空报文,则应该关闭它。不然,web 服务器须要检查对象是否存在于缓存中。若是对象存在,则使用"HTTP/1.1 200 OK rnrn"将对象发送到客户端,不然发生"FileNotFoundError"异常,而后对于"未找到"的HTML文件使用"HTTP/1.1 404 Not Foundrnrn"发送到客户端。 发送HTTP响应报文后,HTTP服务器将关闭TCP链接。代码以下所示:
def handle_request(tcp_socket, client_ip, client_port): print("Client ({ip}: {port}) is coming...".format(ip = client_ip, port = client_port)) try: # 1. 在链接套接字上从客户端接收请求报文。 msg = tcp_socket.recv(1024).decode() if not msg: print("Error! server receive empty HTTP request.") tcp_socket.close() # 从strProc类建立新对象(handlestr)。 handle_str = StrProcess(msg) # 2. 从报文中提取所请求对象的路径(HTTP 首部行的第二部分)。 file_name = handle_str.split_str()[1:] # 3. 从磁盘中查找相应的文件。 # 检查请求的对象是否存在。 f = open(file_name) f.close() # 若是对象存在,准备发送 "HTTP/1.1 200 OK\r\n\r\n" 到套接字。 status = "200 OK" except FileNotFoundError: # 不然,准备发送 "HTTP/1.1 404 Not Found\r\n\r\n" 到套接字。 status = "404 Not Found" file_name = "NotFound.html" re_header = "HTTP/1.1 " + status + "\r\n\r\n" # 最后一个''\r\n'' 意味着头报文的结束。 # 4. 发送正确的HTTP响应。 tcp_socket.send(re_header.encode()) # 5. 存储在临时缓冲区中 with open(file_name, 'rb') as f: file_content = f.readlines() # 6. 将文件的内容发送到套接字。 for strline in file_content: tcp_socket.send(strline) # 7. 关闭链接的套接字。 print("Bye to Client ({ip}: {port})".format(ip = client_ip, port = client_port)) tcp_socket.close()
编写了一个单独的HTTP客户端来查询 web 服务器。此客户端能够发送 HTTP GET 请求并在控制台上接收HTTP响应报文。该程序的优势是它只需输入对象的名称或选择保留或离开便可查询对象。
HTTP 客户端源码:
from socket import * import sys # 1. 设置 web 服务器的地址。 host_port = int(sys.argv[1]) host_address = gethostbyname(gethostname()) # 2. 建立客户端套接字以启动与 web 服务器的 TCP 链接。 tcp_client = socket(AF_INET, SOCK_STREAM) tcp_client.connect((host_address , host_port)) # 3. 输入要查询 web服务器的文件客户端。 print("Hello, which document do you want to query?") while True: obj = input("I want to query: ") # 4. 发送 HTTP 请求报文。 message = "GET /" + obj + " HTTP/1.1\r\n" \ "Host: " + host_address + ":" + str(host_port) + "\r\n" \ "Connection: close\r\n" \ "Upgrade-Insecure-Requests: 1\r\n" \ "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36(KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36\r\n" \ ...... # 首部行。 "Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7\r\n\r\n" tcp_client.send(message.encode()) while True: # 5. 接收HTTP响应报文并将其打印到控制台。 data = tcp_client.recv(1024) if not data: break print("Web server responded to your request:") print(data.decode()) tcp_client.close() # 关闭当前的链接。 # 6. 询问客户是否要继续。 ans = input('\nDo you want to cut this connection(y/n) :') if ans == 'y' or ans == 'Y': break elif ans == 'n' or ans == 'N': # 从新尝试。 print("Anything else I can help you?") tcp_client = socket(AF_INET, SOCK_STREAM) tcp_client.connect((host_address , host_port)) else: print("Command Error, quit.") break
you can test the web server by accessing: http://10.129.34.15:8899/hello.html Wait for TCP clients... Client (10.129.34.15: 6123) is coming... Bye to Client (10.129.34.15: 6123) Client (10.129.34.15: 6135) is coming... Bye to Client (10.129.34.15: 6135) C:\User\asus\Desktop\lab_solution\Web Server>python Client.py 8899 Hello, which document do you want to query? I want to query: hello.html
Web server responded to your request: HTTP/1.1 200 OK Web server responded to your request: <!DOCTYPE html> <html> <head> <title>Hello World HTML</title> </head> <body> <h1>Hello World</h1> </body> Web server responded to your request: </html> Do you want to cut this connection(y/n) :n Anything else I can help you? I want to query: index.html Web server responded to your request: HTTP/1.1 404 Not Found Do you want to cut this connection(y/n) :y Process finished with exit code 0
此任务是构建一个简单的 web 代理服务器。根据第4讲(Web 和 HTTP)中所学到的知识, web 代理服务器充当客户端和服务器,这意味着它具备 web 服务器和客户端的全部功能。它和 web 服务器之间最显着的区别是发送的请求报文
和响应报文都要经过 web 服务器传递。web 代理服务器在任何地方(大学,公司和住宅ISP)使用,以减小客户请求和流量的响应时间。
web 代理服务器的原理基于 web 服务器。web 代理服务器从客户端接收 HTTP 请求报文,并从请求行中提取方法类型。
简化过程以下所示。
基于上述原理,首先,建立一个支持 IPv4 的套接字并将其绑定在高于1024的端口上。web 代理服务器和 web 服务器很是相似,惟一区别是它是单线程的。
我在 StrProcess 类中添加了其余方法使 web 代理服务器获取对象或链接到 web 服务器的效率更高。
StrProcess 类源码:
class StrProcess(str): def __init__(self, str): """用字符型变量 str 初始该属性""" self.str = str def split_str(self): """实现分割操做""" spilt_list = [] start = 0 for i in range(len(self.str)): if self.str[i] == " ": spilt_list.append(self.str[start:i]) start = i + 1 if self.str[i] == "\r": break try: return spilt_list[0],spilt_list[1] except IndexError: return None def get_cmd_type(self): """从 HTTP 请求行中提取请求方法""" return self.split_str()[0] def get_body(self): """从 HTTP 请求报文实体行中提取数据""" body_start = self.str.find('\r\n\r\n') + 4 return self.str[body_start:] def get_referer(self): """从 HTTP 请求行中提取引用""" ref_pos = self.str.find('Referer: ') + 9 ref_stop = self.str.find('\r\n', ref_pos+1) get_ref = self.str[ref_pos:ref_stop] get_ref_start = get_ref.find('9/') + 2 get_ref_path = self.str[ref_pos+get_ref_start:ref_stop] return get_ref_path def get_path(self): """从 HTTP 请求请求行中提取URL""" original_path = self.split_str()[1] for i in range(len(original_path)): if original_path[i] == "/": original_path = original_path[i+1:] return original_path def change_name(self): """将全部特殊符号转换为 "-"。""" original_name = self.get_path() for i in range(len(original_name)): if original_name[i] == "/" or original_name[i] == "?" \ or original_name[i] == "=" or original_name[i] == "&" \ or original_name[i] == "%": original_name = original_name[:i] + "-" + original_name[i+1:] return original_name def get_hostname(self): """从URL中提取主机名""" whole_URL = self.get_path() for i in range(len(whole_URL)): if whole_URL[i] == "/": host_name = whole_URL[:i] return host_name return whole_URL
建立套接字后,web 代理服务器须要处理不一样的 HTTP 请求类型和异常。在这部分中,文件处理应该以二进制格式使用,咱们必须考虑对象类型(能够是.jpg,.svg,.ico等)。
处理 HTTP 请求源码:
def start_listen(tcp_socket, client_ip, client_port): # 1. 在链接套接字上从客户端接收请求报文。 message = tcp_socket.recv(1024).decode() # 从strProc类建立新对象(handlestr)。 handle_str = StrProcess(message) print("client is coming: {addr}:{port}".format(addr = client_ip, port = client_port)) file_error = False global host try: command = handle_str.get_cmd_type() # 2. 从报文中提取所请求对象的路径(HTTP 首部行的第二部分)。 filename = handle_str.change_name() # 3. 找到特定的方法类型并处理请求。 if command == "DELETE" : # 删除缓存中存在的对象 os.remove("./Cache/" + filename) tcp_socket.send(b"HTTP/1.1 200 OK\r\n\r\n") print("File is removed.") elif command == "GET" or command == "HEAD": print("Client want to {c} the {o}.".format(c=command, o=filename)) # 检查请求的对象是否存在。 f = open("./Cache/" + filename, "rb") file_content = f.readlines() f.close() if command == "GET": print("File in cache!") # 从磁盘中查找相应的文件(若是存在)。 for i in range(0, len(file_content)): tcp_socket.send(file_content[i]) # 发送 HTTP 响应报文。 else: # "HEAD" 方法。 list_to_str = "" for i in range(0, len(file_content)): list_to_str += file_content[i].decode() HTTP_header_end = list_to_str.find("\r\n\r\n") # 仅发送 HTTP 响应报文首部行。 tcp_socket.send(list_to_str[:HTTP_header_end+4].encode()) elif command == "PUT" or command == "POST": # 只实现上传文件。 f = open("./Cache/" + filename, "ab") f.write(b"HTTP/1.1 200 OK\r\n\r\n" + handle_str.get_body().encode()) f.close() print("Update successfully!") tcp_socket.send(b"HTTP/1.1 200 OK\r\n\r\n") body_re = b"true" if command == "PUT" else handle_str.get_body().encode() tcp_socket.send(body_re) else: tcp_socket.send(b"HTTP/1.1 400 Bad Request\r\n\r\n") # 4. 若是缓存中不存在该文件,则处理异常。 except (IOError, FileNotFoundError): if command == "GET": # 在代理服务器上建立套接字。 c = socket(AF_INET, SOCK_STREAM) hostname = handle_str.get_hostname() file = handle_str.split_str()[1] print("The file isn't in the cache!") try: # 链接到端口80的套接字。 c.connect((hostname, 80)) host = hostname # 记录真实的主机名。 request = "GET " + "http:/" + file + " HTTP/1.1\r\n\r\n" except: try: # 须要使用全局主机或引用主机名。 new_host = handle_str.get_referer() if host == "" else host c.connect((new_host, 80)) request = "GET " + "http://" + new_host + file + " HTTP/1.1\r\n\r\n" except: tcp_socket.send(b"HTTP/1.1 404 Not Found\r\n\r\n") with open("./Cache/NotFound.html", 'rb') as f: file_content = f.readlines() for strline in file_content: tcp_socket.send(strline) file_error = True if file_error is False: c.sendall(request.encode()) # 将响应读入缓冲区。 print("The proxy server has found the host.") # 在缓存中为请求的文件建立一个新文件。 # 此外,将缓冲区中的响应发送到客户端套接字和缓存中的相应文件。 writeFile = open("./Cache/" + filename, "wb") print("The proxy server is receiving data...") # 接受 HTTP 响应报文直到全部报文都被接收。 while True: data = c.recv(4096) if not data: break sys.stdout.write(">") # 将文件的内容发送到套接字。 tcp_socket.sendall(data) writeFile.write(data) writeFile.close() sys.stdout.write("100%\n") c.close() elif command == "DELETE": tcp_socket.send(b"HTTP/1.1 204 Not Content\r\n\r\n") except (ConnectionResetError, TypeError): print("Bye to client: {addr}:{port}".format(addr = client_ip, port = client_port)) # 关闭客户端的套接字。 print("tcp socket closed\n") tcp_socket.close()
C:\Users\asus\Desktop\lab_solution\Web Proxy>python WebProxy.py 8899 Wait for TCP clients... wait for request: client is coming: 127.0.0.1:4596 Client want to GET the s-wd-facebook-rsv_bp-0-ch-tn-baidu-bar-rsv_spt-3-ie-utf-8-rsv_enter-1-oq-face-f-3-inputT-3356. The file is not in the cache! The proxy server has found the host. The proxy server is receiving data... >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>100% tcp socket closed
若是个人文章能够帮到您,劳烦您点进源码点个 ★ Star 哦!
https://github.com/Hephaest/C...