《计算机网络——自顶向下方法》第二章实验

因为习题太多了,所以实验还是单独拿出来水一章
哪里能够找到这些实验资料呢?
当然是万能的github
感谢翻译者moranzcw

在这里插入图片描述


笔者python版本是3.7…python2的小伙伴请自行转换(还有用2的🐎?)

UDP实验


每个进程好比是一座房子,该进程的套接字则好比是一扇门。应用程序位于房子中门的一侧;运输层位于该门朝外的另一侧。应用程序开发者在套接字的应用层一侧可以控制所有东西;然而,它几乎无法控制运输层一侧。
发送进程为分组附上的目的地址是由目的主机的IP地址和目的地套接字的端口号组成的。此外,如我们很快将看到的那样,发送方的源地址也是由源主机的IP地址和源套接字的端口号组成,该源地址也要附在分组之上。然而,将源地址附在分组之上通常并不是由UDP应用程序代码所为,而是由底层操作系统自动完成的。


因为py2和py3的不同特性,所以书上的代码需要更改一下,如下所示

客户端代码

from socket import *           #该socket模块形成了所有网络通信的基础,用于创建套接字
serverName = '192.168.1.7'        #ip
serverPort = 12000                  #端口
clientSocket = socket(AF_INET,SOCK_DGRAM)       #第一个参数制定了地址簇,第二个参数制定了套接字类型
message = input('Input lowercase sentence:') #指示输入一个参数
message = message.encode()
clientSocket.sendto(message,(serverName,serverPort))  #向服务端发送信息
modifiedMessage,serverAddress = clientSocket.recvfrom(2048)  #数据和源地址
print(modifiedMessage.decode())
clientSocket.close()        #关闭套接字,之后关闭进程

ip地址cmd使用ipconfig命令查看即可…

服务端代码:

from socket import *
serverPort = 12000
serverSocket = socket(AF_INET,SOCK_DGRAM)
serverSocket.bind(('',serverPort))        #向套接字分配一个端口号
print("The server is ready to receive")
while True:
    message,clientAddress = serverSocket.recvfrom(2048)
    message.decode()
    modifiedMessage = message.upper()   #转换message为大写
    serverSocket.sendto(modifiedMessage,clientAddress)

在这里插入图片描述
我们发现了什么?果然UDP是不需要握手,直接就能发送了。

TCP实验


与UDP不同,TCP是一个面向连接的协议。这意味着在客户和服务器能够开始互相发送数据之前,它们先要握手和创建一个TCP连接。TCP连接的一端与客户套接字相联系,另一端与服务器套接字相联系。当创建该TCP连接时,我们将其与客户套接字地址(IP地址和端口号)和服务器套接字地址(IP地址和端口号)关联起来。
使用创建的TCI连接,当一侧要向另一侧发送数据时,它只需经过其套接字将数据丢给TCP连接。这与应用层不同,UDP服务器在将分组丢进套接字之前必须为其附上一个目的地地址。
随着服务器进程的运行,客户进程能够向服务器发起一个TCP连接。这是由客户程序通过创建一个TCP套接字完成的。当该客户生成其TCP套接字时,它指定了服务器中的欢迎套接字的地址,即服务器主机的IP地址及其套接字的端口号。生成其套接字后,该客户发起了一个三次握手并创建与服务器的一个TCP连接。发生在运输层的三次握手,对于客户和服务器程序是完全透明的。在三次握手期间,客户进程敲服务器进程的欢迎之门。当该服务器“听”到敲门时,它将生成一扇新门(更精确地讲是一个新套接字),它专门用于特定的客户。在我们下面的例子中,欢迎之门是一个我们称为serverSocket的TCP套接字对象;它专门对客户进行连接的新生成的套接字,称为连接套接字(connection Socket)。初次遇到TCP套接字的学生有时会混淆欢迎套接字(这是所有要与服务器通信的客户的起始接触点)和每个新生成的服务器侧的连接套接字(这是随后为与每个客户通信而生成的套接字)。

客户端:

from socket import *           #该socket模块形成了所有网络通信的基础,用于创建套接字
serverName = '192.168.1.7'        #ip
serverPort = 12000                  #端口
clientSocket = socket(AF_INET,SOCK_STREAM)       #第一个参数指定了地址簇,第二个参数指定了套接字类型
clientSocket.connect((serverName,serverPort))    #发起一个TCP链接
sentence = input('Input lowercase sentence:')
sentence = sentence.encode()
clientSocket.send(sentence)         #将sentence中的字节放入TCP连接中即可,不需要显式的附上分组地址
modifieSentence = clientSocket.recv(1024)   # 累积字符串
print('From Server:',modifieSentence.decode())
clientSocket.close()

服务端:

from socket import *
serverPort = 12000
serverSocket = socket(AF_INET,SOCK_STREAM)  #欢迎套接字
serverSocket.bind(('',serverPort))        #向套接字分配一个端口号
serverSocket.listen(1)      #聆听“敲门”,参数说明了请求连接的最大数量

print()nt("The server is ready to receive")
while True:
    connectionSocket,addr = serverSocket.accept()
    sentence = connectionSocket.recv(1024)
    capitalizedSentence = sentence.upper()
    connectionSocket.send(capitalizedSentence)
    connectionSocket.close()

在这里插入图片描述

套接字编程作业

作业一:Web服务器

在本实验中,您将学习Python中TCP连接的套接字编程的基础知识:如何创建套接字,将其绑定到特定的地址和端口,以及发送和接收HTTP数据包。您还将学习一些HTTP首部格式的基础知识。
您将开发一个处理一个HTTP请求的Web服务器。您的Web服务器应该接受并解析HTTP请求,然后从服务器的文件系统获取所请求的文件,创建一个由响应文件组成的HTTP响应消息,前面是首部行,然后将响应直接发送给客户端。如果请求的文件不存在于服务器中,则服务器应该向客户端发送“404 Not Found”差错报文。

代码

在文件下面你会找到Web服务器的代码框架。您需要填写这个代码。而且需要在标有#Fill in start 和 # Fill in end的地方填写代码。另外,每个地方都可能需要不止一行代码。

运行服务器

将HTML文件(例如HelloWorld.html)放在服务器所在的目录中。运行服务器程序。确认运行服务器的主机的IP地址(例如128.238.251.26)。从另一个主机,打开浏览器并提供相应的URL。例如:
http://128.238.251.26:6789/HelloWorld.html
“HelloWorld.html”是您放在服务器目录中的文件。还要注意使用冒号后的端口号。您需要使用服务器代码中使用的端口号来替换此端口号。在上面的例子中,我们使用了端口号6789. 浏览器应该显示HelloWorld.html的内容。如果省略“:6789”,浏览器将使用默认端口80,只有当您的服务器正在端口80监听时,才会从服务器获取网页。
然后用客户端尝试获取服务器上不存在的文件。你应该会得到一个“404 Not Found”消息。

需要上交的内容

您需要上交完整的服务器代码,以及客户端浏览器的屏幕截图,用于验证您是否从服务器实际接收到HTML文件内容。

py代码框架
#import socket module
from socket import *
serverSocket = socket(AF_INET, SOCK_STREAM) 
#Prepare a sever socket 
#Fill in start 
#Fill in end 
while True:     
#Establish the connection    
print 'Ready to serve...'     
connectionSocket, addr =   #Fill in start  #Fill in end
try:         
    message =   #Fill in start  #Fill in end
    filename = message.split()[1]                          
    f = open(filename[1:])
    outputdata = #Fill in start  #Fill in end
    #Send one HTTP header line into socket         
    #Fill in start         
    #Fill in end    

    #Send the content of the requested file to the client
    for i in range(0, len(outputdata)):
        connectionSocket.send(outputdata[i])
    connectionSocket.close()
except IOError:
    #Send response message for file not found
    #Fill in start
    #Fill in end

    #Close client socket
    #Fill in start
    #Fill in end             
serverSocket.close()
可选练习

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

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

client.py server_host server_port filename


解答

原题:

# import socket module
from socket import *

serverSocket = socket(AF_INET, SOCK_STREAM)
# Prepare a sever socket
serverSocket.bind(('', 6789))  # 将TCP欢迎套接字绑定到指定端口
serverSocket.listen(1)  # 最大连接数为1

while True:
    # Establish the connection
    print('Ready to serve...')
    connectionSocket, addr = serverSocket.accept()  # 接收到客户连接请求后,建立新的TCP连接套接字
    try:
        message = connectionSocket.recv(1024)  # 获取客户发送的报文
        print(message)
        filename = message.split()[1]
        f = open(filename[1:])#'HelloWorld.html'
        outputdata = f.read();
        # Send one HTTP header line into socket
        header = ' HTTP/1.1 200 OK\nConnection: close\nContent-Type: text/html\nContent-Length: %d\n\n' % (len(outputdata))
        connectionSocket.send(header.encode())

        # 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
        header = ' HTTP/1.1 404 Found'
        connectionSocket.send(header.encode())

        # Close client socket
        connectionSocket.close()
serverSocket.close()

测试:
在这里插入图片描述
我们可以从这个题里面理解http确实是通过字符流写入套接字工作的,文件头也是这样写入。

可选练习一

# 改为Python3格式
# import socket module
from socket import *
import threading

def webProcess(connectionSocket):
    try:
        message = connectionSocket.recv(1024)
        filename = message.split()[1]
        f = open(filename[1:], "rb")
        outputdata = f.read()
        outputdata = outputdata.decode()
        f.close()
        # Send one HTTP header line into socket
        outputdata = 'HTTP/1.1 200 OK\r\n\r\n' + outputdata
        # Send the content of the requested file to the client
        for i in range(0, len(outputdata)):
            connectionSocket.send(outputdata[i].encode())
        connectionSocket.close()
        print("OK!")
    except IOError:
        # Send response message for file not found
        outputdata = 'HTTP/1.1 404 Not Found\r\n\r\n'
        # Close client socket
        for i in range(0, len(outputdata)):
            connectionSocket.send(outputdata[i].encode())
        connectionSocket.close()


serverSocket = socket(AF_INET, SOCK_STREAM)
# Prepare a sever socket
serverPort = 6789
serverSocket.bind(("", serverPort))
serverSocket.listen(10)

while True:
    # Establish the connection
    print('Ready to serve...')
    connectionSocket, addr = serverSocket.accept()
    thread = threading.Thread(target=webProcess, args=(connectionSocket))
    thread.start()
serverSocket.close()

可选练习二

#Python3格式
 
from socket import *
 
serverName = '127.0.0.1'
serverPort = 80
clientSocket = socket(AF_INET, SOCK_STREAM)
clientSocket.connect((serverName, serverPort))
outputdata = 'GET /hello.html HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n'
clientSocket.send(outputdata.encode())
data = 1
while data:
    data = clientSocket.recv(1024)
    print(data.decode(), end = '')
clientSocket.close()
作业二

在本实验中,您将学习使用Python进行UDP套接字编程的基础知识。您将学习如何使用UDP套接字发送和接收数据报,以及如何设置适当的套接字超时。在实验中,您将熟悉Ping应用程序及其在计算统计信息(如丢包率)中的作用。
您首先需要研究一个用Python编写的简单的ping服务器程序,并实现对应的客户端程序。这些程序提供的功能类似于现代操作系统中可用的标准ping程序功能。然而,我们的程序使用更简单的UDP协议,而不是标准互联网控制消息协议(ICMP)来进行通信。 ping协议允许客户端机器发送一个数据包到远程机器,并使远程机器将数据包返回到客户(称为回显)的操作。另外,ping协议允许主机计算它到其他机器的往返时间。
以下是Ping服务器程序的完整代码。你的任务是写出Ping客户端程序。

服务器代码

以下代码完整实现了一个ping服务器。您需要在运行客户端程序之前编译并运行此代码。而且您不需要修改此代码。
在这个服务器代码中,30%的客户端的数据包会被模拟丢失。你应该仔细研究这个代码,它将帮助你编写ping客户端。
UDPPingerServer.py

# We will need the following module to generate randomized lost packets import random 
from socket import * 

# Create a UDP socket  
# Notice the use of SOCK_DGRAM for UDP packets 
serverSocket = socket(AF_INET, SOCK_DGRAM) 
# Assign IP address and port number to socket 
serverSocket.bind(('', 12000)) 

while True:     
	# Generate random number in the range of 0 to 10 
	rand = random.randint(0, 10)     
	# Receive the client packet along with the address it is coming from  
	message, address = serverSocket.recvfrom(1024) 
	# Capitalize the message from the client     
	message = message.upper() 
	# If rand is less is than 4, we consider the packet lost and do not respond     
	if rand < 4:         
		continue     
	# Otherwise, the server responds         
	serverSocket.sendto(message, address) 

服务器程序在一个无限循环中监听到来的UDP数据包。当数据包到达时,如果生成一个随机整数大于或等于4,则服务器将数字转为大写并将其发送回客户端。

数据包丢失

UDP为应用程序提供了不可靠的传输服务。消息可能因为路由器队列溢出,硬件错误或其他原因,而在网络中丢失。但由于在内网中很少丢包甚至不丢包,所以在本实验室的服务器程序添加人为损失来模拟网络丢包的影响。服务器创建一个随机整数,由它确定传入的数据包是否丢失。

客户端代码

您需要实现以下客户端程序。

客户端向服务器发送10次ping。因为UDP是不可靠的协议,所以从客户端发送到服务器的数据包可能在网络中丢失。因此,客户端不能无限期地等待ping消息的回复。客户等待服务器回答的时间至多为一秒,如果在一秒内没有收到回复,您的客户端程序应该假定数据包在网络传输期间丢失。您需要查找Python文档,以了解如何在数据报套接字上设置超时值。

具体来说,您的客户端程序应该:
使用UDP发送ping消息(注意:不同于TCP,您不需要首先建立连接,因为UDP是无连接协议。)

从服务器输出响应消息

如果从服务器受到响应,则计算并输出每个数据包的往返时延(RTT)(以秒为单位),否则输出“请求超时”
在开发过程中,您应该先在计算机上运行UDPPingerServer.py,并通过向localhost(或127.0.0.1)发送数据包来测试客户端。调试完成代码后,您应该能看到ping服务器和ping客户端在不同机器上通过网络进行通信。

消息格式

本实验中的ping消息格式使用最简单的方式。客户端消息只有一行,由以下格式的ASCII字符组成:

Ping sequence_number time

其中sequence_number从1开始,一直到10,共10条消息,而time则是客户端发送消息时的时间。

需要上交的内容

您需要上交完整的客户端代码和屏幕截图,以验证您的ping程序是否按需求运行。

可选练习

目前,程序计算每个数据包的往返时间(RTT),并单独打印出来。请按照标准ping程序的模式修改。您需要在客户端每次ping后显示最小,最大和平均RTT。另外,还需计算丢包率(百分比)。
UDP Ping的另一个类似的应用是UDP Heartbeat。心跳可用于检查应用程序是否已启动并运行,并报告单向丢包。客户端在UDP数据包中将一个序列号和当前时间戳发送给正在监听客户端心跳的服务器。服务器收到数据包后,计算时差,报告丢包(若发生)。如果心跳数据包在指定的一段时间内丢失,我们可以假设客户端应用程序已经停止。实现UDP Heartbeat(客户端和服务器端)。您需要修改给定的UDPPingerServer.py和您自己的UDP ping客户端。

解答

客户端:

from socket import *
import time

serverName = '192.168.1.7'
serverPort = 12000
clientSocket = socket(AF_INET,SOCK_DGRAM)
clientSocket.settimeout(1)

for i in range(0,10):
    sendTime = time.time()  #获取当前时间
    message = ('Ping %d %s' %(i+1,sendTime)).encode()  #生成字符流格式的数据报
    try:
        clientSocket.sendto(message,(serverName,serverPort))
        modifiedMessage,serverAddress = clientSocket.recvfrom(1024)
        rtt = time.time() - sendTime
        print('Sequence %d: Reply from %s    RTT = %.3fs' % (i+1, serverName, rtt)) # 显示信息
    except Exception as e:
        print('Sequence %d: Request timed out' %(i+1))

clientSocket.close()

服务端:

# UDPPingerServer.py
# We will need the following module to generate randomized lost packets import random
from socket import *
import random
serverSocket = socket(AF_INET, SOCK_DGRAM)
serverSocket.bind(('', 12000))
print('ready to receive...')
while True:
	rand = random.randint(0, 10)
	message, address = serverSocket.recvfrom(1024)
	message = message.upper()
	serverSocket.sendto(message, address)

在这里插入图片描述

作业三

通过完成本实验,您将更加了解SMTP协议。您还将学到使用Python实现标准协议的经验。

您的任务是开发一个简单的邮件客户端,将邮件发送给任意收件人。您的客户端将需要连接到邮件服务器,使用SMTP协议与邮件服务器进行对话,并向邮件服务器发送电子邮件。 Python提供了一个名为smtplib的模块,它内置了使用SMTP协议发送邮件的方法。但是我们不会在本实验中使用此模块,因为它隐藏了SMTP和套接字编程的细节。

为了限制垃圾邮件,一些邮件服务器不接受来源随意的TCP连接。对于下面所述的实验,您可能需要尝试连接到您的大学邮件服务器和流行的Webmail服务器(如AOL邮件服务器)。您也可以尝试从您的家和您的大学校园进行连接。

代码

下面你会找到客户端的代码框架。您将完成代码框架。您需要填写代码的地方标有#Fill in start和#Fill in end。每个地方都可能需要不止一行代码。

附加说明

在某些情况下,接收邮件服务器可能会将您的电子邮件分类为垃圾邮件。当您查找从客户端发送的电子邮件时,请检查垃圾邮件文件夹。

要上交的内容

在您的上交内容中,你需要提供完整的SMTP邮件客户端的代码以及一张能显示您确实收到电子邮件的屏幕截图。

邮件客户端的Python代码框架

from socket import *
msg = "\r\n I love computer networks!"
endmsg = "\r\n.\r\n"
# Choose a mail server (e.g. Google mail server) and call it mailserver 
mailserver = #Fill in start   #Fill in end
# Create socket called clientSocket and establish a TCP connection with mailserver
#Fill in start

#Fill in end
recv = clientSocket.recv(1024)
print recv
if recv[:3] != '220':
    print '220 reply not received from server.'

# Send HELO command and print server response.
heloCommand = 'HELO Alice\r\n'
clientSocket.send(heloCommand)
recv1 = clientSocket.recv(1024)
print recv1
if recv1[:3] != '250':
    print '250 reply not received from server.'

# Send MAIL FROM command and print server response.
# Fill in start

# Fill in end

# Send RCPT TO command and print server response.
# Fill in start

# Fill in end

# Send DATA command and print server response.
# Fill in start

# Fill in end

# Send message data.
# Fill in start

# Fill in end 

# Message ends with a single period.
# Fill in start

# Fill in end

# Send QUIT command and get server response.
# Fill in start

# Fill in end
可选练习

类似Google邮件的服务器(如地址:smtp.gmail.com,端口:587))要求您的客户端在发送MAIL FROM命令之前,需要为了身份验证和安全原因添加传输层安全(TLS)或安全套接字层(SSL)。将TLS / SSL命令添加到现有的命令中,并使用上述地址和端口为Google邮件服务器实现客户端。
您当前的SMTP邮件客户端只能在电子邮件正文中发送文本消息。修改您的客户端,使其可以发送包含文本和图像的电子邮件。

解答

费了一番功夫
首先打开你的邮箱SMTP服务,获得一个授权码,这个将作为你的登录密码使用。
但是,你的密码和邮箱应该经过BASE64编码才能使用,否则报错334
加密解密网站:https://tool.oschina.net/encrypt?type=3
代码如下所示:


from socket import *
import sys
import base64

# Mail content
subject = "I love computer networks!"
contenttype = "text/plain"
msg = "I love computer networks!"
endmsg = "\r\n.\r\n"

# Choose a mail server (e.g. Google mail server) and call it mailserver
mailserver = "smtp.163.com"

# Sender and reciever
fromaddress = "填入发送邮箱"
toaddress = "填入目的邮箱"

# Auth information (Encode with base64)


# Create socket called clientSocket and establish a TCP connection with mailserver
clientSocket = socket(AF_INET, SOCK_STREAM)
clientSocket.connect((mailserver, 25))

recv = clientSocket.recv(1024).decode()
print(recv)
if recv[:3] != '220':
    print('220 reply not received from server.')
    sys.exit(0)

# Send HELO command and print server response.
heloCommand = 'HELO Alice\r\n'
clientSocket.send(heloCommand.encode())
recv1 = clientSocket.recv(1024).decode()
print(recv1)
if recv1[:3] != '250':
    print('250 reply not received from server.')
    sys.exit(0)

# Auth
clientSocket.sendall('AUTH LOGIN\r\n'.encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '334'):
    print('334 reply not received from server.')
    sys.exit(0)

print('prepare ok')
clientSocket.sendall(('加密后的用户名\r\n').encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '334'):
    print('334 reply not received from server')
    sys.exit(0)
print('username ok')


clientSocket.sendall(('加密后的授权码\r\n').encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '235'):
    print('235 reply not received from server')
    sys.exit(0)
print('password ok')
# Send MAIL FROM command and print server response.
clientSocket.sendall(('MAIL FROM: <' + fromaddress + '>\r\n').encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '250'):
    print('250 reply not received from server')
    sys.exit(0)

# Send RCPT TO command and print server response.
clientSocket.sendall(('RCPT TO: <' + toaddress + '>\r\n').encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '250'):
    print('250 reply not received from server')
    sys.exit(0)

# Send DATA command and print server response.
clientSocket.send('DATA\r\n'.encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '354'):
    print('354 reply not received from server')


# Send message data.
message = 'from:' + fromaddress + '\r\n'
message += 'to:' + toaddress + '\r\n'
message += 'subject:' + subject + '\r\n'
message += 'Content-Type:' + contenttype + '\t\n'
message += '\r\n' + msg
clientSocket.sendall(message.encode())

# Message ends with a single period.
clientSocket.sendall(endmsg.encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '250'):
    print('250 reply not received from server')


# Send QUIT command and get server response.
clientSocket.sendall('QUIT\r\n'.encode())

# Close connection
clientSocket.close()

在这里插入图片描述

作业四 多线程Web代理服务器

在本实验中,您将了解Web代理服务器的工作原理及其基本功能之一 —— 缓存。

您的任务是开发一个能够缓存网页的小型Web代理服务器。这是一个很简单的代理服务器,它只能理解简单的GET请求,但能够处理各种对象 —— 不仅仅是HTML页面,还包括图片。

通常,当客户端发出一个请求时,请求将被直接发送到Web服务器。然后Web服务器处理该请求并将响应消息发送客户端。为了提高性能,我们在客户端和Web服务器之间建立一个代理服务器。现在,客户端发送的请求消息和Web服务器返回的响应消息都要经过代理服务器。换句话说,客户端通过代理服务器请求对象。代理服务器将客户端的请求转发到Web服务器。然后,Web服务器将生成响应消息并将其传递给代理服务器,代理服务器又将其发送给客户端。

代码

您将在下面找到客户端的代码框架。 您需要完成代码框架。需要您填写代码的地方标有#Fill in start和#Fill in end。 每个地方都需要填写至少一行代码。

运行代理服务器

使用命令行模式运行您的代理服务器程序,然后从您的浏览器发送一个网页请求,将IP地址和端口号指向代理服务器。 例如:http://localhost:8888/www.google.com 为了在独立的计算机上使用浏览器和代理服务器, 因此,在运行代理服务器时,您需要将“localhost”更换为代理服务器的所在机器的IP地址。您还需要将“8888”替换您在代理服务程序中使用的端口号。

配置浏览器

您还可以直接配置您的Web浏览器以使用您的代理服务。 具体取决于您的浏览器。在Internet Explorer中,您可以在 工具 > Internet选项 > 连接选项卡 > LAN设置 中设置代理。 在Netscape(包括衍生浏览器,如Mozilla)中,您可以在 工具 > 选项 > 高级选项 > 网络选项 > 连接设置 中设置代理。 在这两种情况下你都需要给出代理服务器的地址和端口号。你首先要毫无问题地在同一台计算机上运行代理服务器和浏览器。这种方式下,使用代理服务器获取网页就只需提供页面的URL。 例如 http://www.google.com

要提交的内容

您需要提交提交完整的代理服务器代码和一张客户端屏幕截图,用于验证您是否 确实通过代理服务器获取了网页。

代理服务器的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.
可选练习

1.目前代理服务器不能处理错误。这可能会导致一些问题,当客户端请求一个不可用的对象时,由于“404 Not Found”响应通常没有响应正文,而代理服务器会假设有正文并尝试读取它。
2.当前代理服务器只支持HTTP GET方法。通过添加请求体来增加对POST的支持。
3.缓存:每当客户端发出特定请求时,典型的代理服务器会缓存网页。缓存的基本功能如下:当代理获得一个请求时,它将检查请求的对象是否已经在缓存中,如果是,则从缓存返回对象,从而不用联系服务器。如果对象未被缓存,则代理从服务器获取该对象,向客户端返回该对象,并缓存一个拷贝以备将来的请求。在实际环境下,代理服务器必须验证被缓存的响应是否仍然有效,并且它们能对客户端正确响应。您可以在RFC 2068中阅读有关缓存及其在HTTP中实现方式的更多细节。添加上述简单的缓存功能。您不需要实现任何替换或验证策略。然而您需要实现的是,将请求和响应写入磁盘(即缓存)并能从磁盘中获取它们,用于缓存被请求命中时。为此,您需要在代理中实现一些内部数据结构,以便跟踪哪些请求处于缓存中时,以及它们在磁盘上的位置。您也可以将此数据结构保存在内存中,因为没有必要关机之后持续保存这些数据。

解答
from socket import *

tcpSerPort = 8899
tcpSerSocket = socket(AF_INET,SOCK_STREAM)

tcpSerSocket.bind(('',tcpSerPort))
tcpSerSocket.listen(5)   #等待敲门

while True:
    #获得套接字,接收信息
    print('Ready to receive...')
    tcpRecSock,addr = tcpSerSocket.accept()
    print('Received a connection from:',addr)
    message = tcpRecSock.recv(4096).decode()

    #获取请求中的地址
    filename = message.split()[1].partition("//")[2].replace('/','_')
    fileExist = "false"
    try:
        f = open(filename,"r")
        outputdata = f.readlines()
        fileExist = "true"
        print('File Exists!')

        for i in range(0,line(outputdata)):
            tcpSerSocket.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)
            # 获取ip
            hostn = message.split()[1].partition("//")[2].partition("/")[0]
            print('Host Name: ', hostn)
            try:
                #代理服务器和目标服务器连接
                c.connect((hostn,80))
                print('Socket connected to port 80 of the host')
                #向目标服务器发送请求
                c.sendall(message.encode())
                #接收目标服务器的信息
                res = c.recv(4096)
                #向客户端发送信息tcpRecSock
                tcpRecSock.sendall(res)
                #写入缓存
                tmpFile = open("./" + filename, "w")
                tmpFile.writelines(res.decode().replace('\r\n', '\n'))
                tmpFile.close()
            except:
                print("Ilegal request")

        else:
            print("file not found ,stupid andy")

    tcpRecSock.close()

tcpSerSocket.close()


在这里插入图片描述
可以通过作者提供的测试地址http://gaia.cs.umass.edu/wireshark-labs/INTRO-wireshark-file1.html进行测试,遗憾的是,这个代理服务器不能够访问其它网站获取文件。
我们通过这个实验就可以理解代理服务器运行的基本原理了。

  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值