自顶向下编程练习

Posted by Leo on 2023-10-17
Estimated Reading Time 16 Minutes
Words 3.2k In Total

Web服务器

实验要求

将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”消息。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#import socket module
from socket import *
serverSocket = socket(AF_INET, SOCK_STREAM)
#Prepare a sever socket
PORT = 1234
serverSocket.bind(('',PORT))
serverSocket.listen(1)
while True:
#Establish the connection
print("Ready to serve...")
connectionSocket, addr = serverSocket.accept() #Fill in start #Fill in end
try:
message = connectionSocket.recv(2048) #Fill in start #Fill in end
filename = message.split()[1] # get file path )
with open(filename[1:]) as f:
outputdata = f.read() #Fill in start #Fill in end
#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()

UDP Ping程序

实验要求

在本实验中,您将学习使用Python进行UDP套接字编程的基础知识。您将学习如何使用UDP套接字发送和接收数据报,以及如何设置适当的套接字超时。在实验中,您将熟悉Ping应用程序及其在计算统计信息(如丢包率)中的作用。

您首先需要研究一个用Python编写的简单的ping服务器程序,并实现对应的客户端程序。这些程序提供的功能类似于现代操作系统中可用的标准ping程序功能。然而,我们的程序使用更简单的UDP协议,而不是标准互联网控制消息协议(ICMP)来进行通信。 ping协议允许客户端机器发送一个数据包到远程机器,并使远程机器将数据包返回到客户(称为回显)的操作。另外,ping协议允许主机计算它到其他机器的往返时间。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# UDPPingerServer.py 
# We will need the following module to generate randomized lost packets import random
from socket import *
import random

# 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(('', 1234))

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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from socket import * 
import time

client_socket= socket(AF_INET, SOCK_DGRAM)

serverIP = '192.168.147.135'
serverPort = 1234
client_socket.settimeout(1) # 设置套接字超时值1秒

for i in range(0, 10):
startTime = time.time()
message = ('Ping %d %s' % (i, startTime)).encode()
try:
client_socket.sendto(message, (serverIP, serverPort))
res, addr = client_socket.recvfrom(1024)
endTime = time.time()
rtt = endTime - startTime
print('Sequence %d: Reply from %s RTT = %.6fs' % (i+1, serverIP, rtt))
except Exception as e:
print('Sequence %d: Request timed out' % (i+1))

邮件客户端

实验要求

创建一个向任何接收方发送电子邮件的简单邮件客户。你的客户将必须与邮件服务器(如谷歌的电子邮件服务器)创建一个TCP连接,使用SMTP协议与该邮件服务器进行交谈,经该邮件服务器向某接收方(如你的朋友)发送一个电子邮件报文,最后关闭与该邮件服务器的TCP连接。

本实验参考:https://blog.csdn.net/JavaMoo/article/details/54897033

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
from socket import *
import base64

subject = "SMTP socket programming"
contenttype = "text/plain"
msg = "\r\n FBI Warning! You have been hacked! I'm not kidding!!"
endMsg = "\r\n.\r\n"

# Auth information (Encode with base64)
username = base64.b64encode(b'leo021017').decode() + '\r\n'
password = base64.b64encode(b'BKEUPKJRGWWWNSNF').decode() + '\r\n'

# Sender and reciever
fromAddress = "leo021017@163.com"
toAddress = "haoyichen1017@qq.com"

# Choose a mail server (e.g. Google mail server) and call it mailServer
mailServer = 'smtp.163.com'
mailPort = 25

# Create socket called clientSocket and establish a TCP connection with mailServer
clientSocket = socket(AF_INET, SOCK_STREAM)
clientSocket.connect((mailServer, mailPort))
recv = clientSocket.recv(1024).decode()
print(recv)
if recv[:3] != '220':
print('220 reply not received from server.')
clientSocket.close()
exit(0)

# Send HELO command and print server response.
heloCommand = 'HELO Leo\r\n'
clientSocket.send(heloCommand.encode())
recv1 = clientSocket.recv(1024).decode()
print(recv1)
if recv1[:3] != '250':
print('250 reply not received from server.')
clientSocket.close()
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')

# send username
clientSocket.sendall((username).encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '334'):
print('334 reply not received from server')

# send password
clientSocket.sendall((password).encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '235'):
print('235 reply not received from server')

# 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')

# 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')

# 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代理服务器。这是一个很简单的代理服务器,它只能理解简单的GET请求,但能够处理各种对象 —— 不仅仅是HTML页面,还包括图片。

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

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
from socket import *
import sys

# if len(sys.argv) <= 1:
# print ('Usage : "python proxy.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
PORT = 1234
tcpSerSock = socket(AF_INET, SOCK_STREAM)
tcpSerSock.bind(('', PORT))
tcpSerSock.listen(5)

while 1:
# Strat receiving data from the client
print('Ready to serve...')
tcpCliSock, addr = tcpSerSock.accept()
print('Received a connection from:', addr)
message = tcpCliSock.recv(4096).decode()
# Extract the filename from the given message
filename = message.split()[1].partition("//")[2].replace('/', '_')
fileExist = "false"
try:
# Check wether the file exist in the cache
f = open(filename, "r")
outputdata = f.readlines()
fileExist = "true"
print('File Exists!')
# ProxyServer finds a cache hit and generates a response message
str1 = "HTTP/1.0 200 OK\r\n"
str2 = "Content-Type:text/html\r\n\n"
# tcpCliSock.send(str1.encode()) 不知道为什么会显示在前端?
# tcpCliSock.send(str2.encode())
for i in range(0, len(outputdata)):
tcpCliSock.send(outputdata[i].encode())
print('Read from cache')
# Error handling for file not found in cache
except IOError:
if fileExist == "false":
# Create a socket on the proxyserver
c = socket(AF_INET, SOCK_STREAM)
host = message.split()[1].partition("//")[2].partition("/")[0]
print('Host Name: ', host)
try:
# Connect to the socket to port 80
c.connect((host, 80))
print('Socket connected to port 80 of the host')
c.sendall(message.encode())
# 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
buff = c.recv(4096)
# 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
tcpCliSock.sendall(buff)
tmpFile = open("./" + filename,"w")
tmpFile.writelines(buff.decode().replace('\r\n', '\n'))
tmpFile.close()

except:
print("Illegal request")
else:
# HTTP response message for file not found
print('File Not Found')
# Close the client and the server sockets
tcpCliSock.close()
tcpSerSock.close()

ICMP Ping程序

实验要求

在这个作业中,您将更好地理解因特网控制报文协议(ICMP)。您会学习使用ICMP请求和响应消息实现Ping程序。
Ping是一个网络应用程序,用于测试某个主机在IP网络中是否可访问。它也用于测试计算机的网卡或测试网络延迟。它通过向目标主机发送ICMP“回显”包并监听ICMP“回显”应答来工作。“回显”有时称为"pong"。ping程序测量往返时间,记录数据包丢失,并输出接收到的回显包的统计摘要(往返时间的最小值、最大值和平均值,以及在某些版本中的平均值的标准差)。
您的任务是用python开发自己的Ping程序。您的程序将使用ICMP,但为了保持简单,将不完全遵循RFC 1739中的正式规范。请注意,您只需要编写程序的客户端,因为服务器端所需的功能几乎内置于所有操作系统中。
您的Ping程序能将ping请求发送到指定的主机,间隔大约一秒钟。每个消息包含一个带有时间戳的数据包。每个数据包发送完后,程序最多等待一秒,用于接收响应。如果一秒后服务器没有响应,那么客户端应假设ping数据包或pong数据包在网络中丢失(或者服务器已关闭)。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import socket
import os
import sys
import struct
import time
import select
import binascii

ICMP_ECHO_REQUEST = 8

def checksum(str):
csum = 0
countTo = (len(str) / 2) * 2
count = 0
while count < countTo:
thisVal = str[count + 1] * 256 + str[count]
csum = csum + thisVal
csum = csum & 0xffffffff
count = count + 2

if countTo < len(str):
csum = csum + str[len(str) - 1].decode()
csum = csum & 0xffffffff

csum = (csum >> 16) + (csum & 0xffff)
csum = csum + (csum >> 16)
answer = ~csum
answer = answer & 0xffff
answer = answer >> 8 | (answer << 8 & 0xff00)
return answer

def receiveOnePing(mySocket, ID, sequence, destAddr, timeout):
timeLeft = timeout

while 1:
startedSelect = time.time()
whatReady = select.select([mySocket], [], [], timeLeft)
howLongInSelect = (time.time() - startedSelect)
if whatReady[0] == []: # Timeout
return None

timeReceived = time.time()
recPacket, addr = mySocket.recvfrom(1024)

header = recPacket[20: 28]
type, code, checksum, packetID, sequence = struct.unpack("!bbHHh", header)
if type == 0 and packetID == ID: # type should be 0
byte_in_double = struct.calcsize("!d")
timeSent = struct.unpack("!d", recPacket[28: 28 + byte_in_double])[0]
delay = timeReceived - timeSent
ttl = ord(struct.unpack("!c", recPacket[8:9])[0].decode())
return (delay, ttl, byte_in_double)

timeLeft = timeLeft - howLongInSelect
if timeLeft <= 0:
return None


def sendOnePing(mySocket, ID, sequence, destAddr):
# Header is type (8), code (8), checksum (16), id (16), sequence (16)

myChecksum = 0
# Make a dummy header with a 0 checksum. 计算Checksum时将字段值作为0输入
# struct -- Interpret strings as packed binary data
# ! 表示使用网络字节序进行解包,即采用大端字节序;
# b 表示对应的数据类型为 1 个字节有符号整数(signed char);
# H 表示对应的数据类型为 2 个字节无符号整数(unsigned short);
# h 表示对应的数据类型为 2 个字节有符号整数(short)。
header = struct.pack("!bbHHh", ICMP_ECHO_REQUEST, 0, myChecksum, ID, sequence)
data = struct.pack("!d", time.time())
# Calculate the checksum on the data and the dummy header.
myChecksum = checksum(header + data)

# # Get the right checksum, and put in the header
header = struct.pack("!bbHHh", ICMP_ECHO_REQUEST, 0, myChecksum, ID, sequence)
packet = header + data

mySocket.sendto(packet, (destAddr, 1)) # AF_INET address must be tuple, not str
#Both LISTS and TUPLES consist of a number of objects
#which can be referenced by their position number within the object

def doOnePing(destAddr, sequence, timeout):
icmp = socket.getprotobyname("icmp")

#SOCK_RAW is a powerful socket type. For more details see: http://sock-raw.org/papers/sock_raw
#Create Socket here
mySocket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp)

myID = os.getpid() & 0xFFFF #Return the current process i
sendOnePing(mySocket, myID, sequence, destAddr)
result = receiveOnePing(mySocket, myID, sequence, destAddr, timeout)

mySocket.close()
return result

def ping(host, timeout=1):
#timeout=1 means: If one second goes by without a reply from the server,
#the client assumes that either the client’s ping or the server’s pong is lost
dest = socket.gethostbyname(host)
print("Pinging " + dest + " using Python:")
print("")
#Send ping requests to a server separated by approximately one second
loss = 0
myID = os.getpid() & 0xFFFF # Return the current process i
for i in range(4):
result = doOnePing(dest, i, timeout)
if result != None:
print(result)
delay = int(result[0]*1000)
ttl = result[1]
bytes = result[2]
print("Received from " + dest + ": byte(s)=" + str(bytes) + " delay=" + str(delay) + "ms TTL=" + str(ttl))
else:
print("Request timed out.")
loss += 1
time.sleep(1)# one second
print("Packet: sent = " + str(4) + " received = " + str(4-loss) + " lost = " + str(loss))
return

ping("baidu.com")

本着互联网开源的性质,欢迎分享这篇文章,以帮助到更多的人,谢谢!