计网自顶向下(多线程Web代理服务器)

目录

前言

多线程Web代理服务器

过程

解释

代码


前言

计网自顶向下(多线程Web代理服务器)_第1张图片

前置知识

(1)进程 与 线程

进程与线程的一个简单解释 - 阮一峰的网络日志 (ruanyifeng.com)

(2) 多线程

撬开多线程的大门——学习多线程必须掌握的基本概念 - 姜承轩 - 博客园 (cnblogs.com)

多线程Web代理服务器

过程

注意!先用原端口(就是初始能上网的端口),先不要切8899端口,打开 gaia.cs 网页

然后,对应目录下打开 cmd,py    .py    代码,出现ready to serve... 后

再切 8899 端口,然后刷新(打乱步骤无法出现预期结果

(1)配置浏览器

计网自顶向下(多线程Web代理服务器)_第2张图片

这里最好把端口改成8899或者其他的,如果按你原来的端口来改代码中的 port ,很可能出现端口被占用的情况,那么打开网页后,cmd中没有反应

 (2)运行对应目录的 .py 代码

(3)打开网页

http://gaia.cs.umass.edu/wireshark-labs/INTRO-wireshark-file1.html

计网自顶向下(多线程Web代理服务器)_第3张图片

与此同时,cmd中显示

卡在了 hostname

Ready to serve...
Received a connection from:  ('127.0.0.1', 50213)
File Exist:  false
Creating socket on proxyserver
Host Name:
Illegal request

貌似 Edge 不行,得切 IE 浏览器(不切貌似也能用)

a. 

Microsoft Edge
b. edge浏览器internet选项灰色怎么办?-edge浏览器internet选项灰色无法使用的解决方法 - 极光下载站 (xz7.com)

2次 Host Nameillegal request 后成功了

计网自顶向下(多线程Web代理服务器)_第4张图片

(4)同一目录下出现缓存的 网页文件,记事本打开

计网自顶向下(多线程Web代理服务器)_第5张图片

内容包括 响应头 和 正文

计网自顶向下(多线程Web代理服务器)_第6张图片

注意,切 8899 端口后,浏览器打开其他网页都连接不上,因为默认占用原来的端口号来打开网页,需要重新切回去才能上网( (5)开始前,可以切回 8899 端口 ) 

(5)重新打开个 cmd,运行 .py,然后刷新 gaia.cs 网址,cmd显示

计网自顶向下(多线程Web代理服务器)_第7张图片

解释

进一步注释,看代码

(1) socket()函数的两个参数

  • AF_INET:指定使用IPv4地址族,用于TCP/IP网络
  • SOCK_STREAM:指定使用流式套接字,用于可靠的、基于连接的通信
# 创建TCP套接字
tcpSerSock = socket(AF_INET, SOCK_STREAM)
 

(2) bind()函数的两个参数解释如下:

  • '':表示将服务器绑定到所有可用的网络接口上
  • tcpSerPort:指定要绑定的端口号
# 将套接字绑定到指定的地址和端口 
tcpSerSock.bind(('', tcpSerPort))

(3) 浏览器是客户端,而.py在cmd中扮演服务器的角色的原因是,浏览器是一个HTTP客户端,负责发送HTTP请求给服务器,并接收服务器的响应。而代码中创建的服务器是一个TCP服务器,它监听指定的端口,接受来自客户端(例如浏览器)的连接请求,并向客户端发送响应

(4) tcpCliSock是一个新的套接字对象,用于与客户端进行通信。addr是客户端的地址信息。

tcpCliSock, addr = tcpSerSock.accept()

(5)

  • message.split()[1]:将接收到的消息按空格分割,取第二部分,即HTTP请求中的路径部分
  • .partition("//")[2]:将路径部分按"//"分割,取第三部分,即主机名部分
  • .replace('/', '_'):将主机名中的斜杠替换为下划线
filename = message.split()[1].partition("//")[2].replace('/', '_')
 

(6) fileExist = "false"用于标记文件是否存在。在后续的判断中,如果该值为"false",则说明文件不存在

(7) tryexcept IOError用于异常处理。在try代码块中尝试执行某个操作,如果在执行过程中发生了IOError异常(文件不存在等),则跳转到except代码块中进行相关处理

(8) open()函数用于打开文件,并返回一个文件对象。参数解释如下

  • filename:要打开的文件名。
  • "r":以只读模式打开文件。

(9) .readlines()方法用于从文件对象中读取所有行,并返回一个包含所有行的列表

(10) .send()方法用于发送数据。参数是要发送的数据

(11) .connect((hostn, 80))与远程服务器建立连接。参数是远程服务器的IP地址和端口号

(12) sendall(buff)方法用于将数据发送给客户端。buff是要发送的数据

(13) open('./' + filename, "w")用于创建并打开一个文件,以便写入数据。filename是文件名,"w"表示以写入模式打开文件

(14) .writelines()方法用于将字符串列表中的所有元素写入文件中。参数是要写入的字符串列表

代码

#coding:utf-8
from socket import *

# 创建socket, 绑定到端口, 开始监听
tcpSerPort = 8899
tcpSerSock = socket(AF_INET, SOCK_STREAM) 
# AF_INET, IPv4地址簇, 用于TCP/IP
# SOCK_STREAM, 流式socket, 用于TCP


# Prepare a server socket
tcpSerSock.bind(('', tcpSerPort)) # Server地址和端口
tcpSerSock.listen(3) # 最大连接数

while True:
    # 开始从客户端接受请T求
    print('Ready to serve...baga!')
    tcpCliSock, addr = tcpSerSock.accept() # 新的套接字 和 客户端地址
    print('Received a connection from: ', addr) # 客户端地址 = IP地址 + 端口号 = 元组
    message = tcpCliSock.recv(4096).decode() # 接收字符串并解码

    # 从请求中解析出filename
    filename = message.split()[1].partition("//")[2].replace('/', '_')
    # 按空格分隔取第2部分; 按//分隔取第3部分; 把/替换为_
    fileExist = "false"

    # try 执行中遇到异常, 就执行except
    try:
        # 检查缓存中是否存在该文件
        f = open(filename, "r") # 只读方式打开文件
        outputdata = f.readlines() # 读取所有行并返回列表
        fileExist = "true"
        print('File Exists!')

        # 缓存中存在该文件,把它向客户端发送
        for i in range(0, len(outputdata)):
            tcpCliSock.send(outputdata[i].encode())
        print('Read from cache')
    
    # 缓存中不存在该文件,异常处理
    except IOError:
        print('File Exist: ', fileExist)

        if fileExist == 'false':
            # 在代理服务器上创建一个tcp socket
            print('Creating socket on proxyserver')
            c = socket(AF_INET, SOCK_STREAM)

            # 解析请求消息中的主机名
            hostn = message.split()[1].partition("//")[2].partition("/")[0]
            # 按空格分隔取第2部分; 按//分隔取第3部分; 按/分隔取第1部分
            print('Host Name: ', hostn)

            try:
                # 连接到远程服务器80端口
                c.connect((hostn, 80)) # ip 和 端口
                print('Socket connected to port 80 of the host')

                # 请求信息发送到远程服务器
                c.sendall(message.encode())

                # Read the response into buffer
                # 从远程服务器读取响应信息
                buff = c.recv(4096)

                # 发送响应信息到客户端socket
                tcpCliSock.sendall(buff)

                # 缓存cache中创建并写入请求文件的副本
                tmpFile = open("./" + filename, "w") # 只写方式打开文件
                # 解码后替换\r\n为\n
                tmpFile.writelines(buff.decode().replace('\r\n', '\n'))
                tmpFile.close() # 关闭文件

            except:
                # HTTP response message for file not found
                print("Illegal request")
            # Close the client and the server sockets
            tcpCliSock.close() # 关闭 Client socket
tcpSerSock.close() # 关闭 Server socket

你可能感兴趣的:(网络编程,网络)