1. 程式人生 > >計算機網路自頂向下方法套接字程式設計作業

計算機網路自頂向下方法套接字程式設計作業

本部落格是針對,《計算機網路自頂向下方法》一書第二章後面套接字程式設計作業,
所有程式碼均已上傳至我的github:https://github.com/inspurer/ComputerNetwork
所有程式碼均本人親自編寫,有問題歡迎評論交流;
如需轉載請聯絡:[email protected]

作業1: Web伺服器

問題描述

使用Python開發一個簡單的Web伺服器,它僅能處理一個請求,具體而言,你的伺服器將

當一個客戶(瀏覽器)聯絡時建立一個連線套接字;

這個連線接受http請求;

解釋該請求以確定所請求的特定檔案;

從伺服器的檔案系統獲得請求的檔案;

建立一個由請求的檔案組成的HTTP響應報文,報文前有首部行;

經TCP連線向請求的瀏覽器傳送響應;

如果檔案不存在,返回404 Not Found

問題解決

主要程式碼

服務端程式碼

from socket import *
serverSocket = socket(AF_INET, SOCK_STREAM)
serverSocket.bind(("127.0.0.1",9999))
serverSocket.listen(1)
# 沒有客戶端連結時一直在此阻塞
connectionSocket, addr = serverSocket.accept()
while True:
	print('waiting for connection...')
	try:
		#接收1k資料
		data = connectionSocket.recv(1024)
		print(data)
		if not data:
			continue
		#data是一個get的http請求報文
		filename = data.split()[1] #filename = /HelloWorld.html
		# #print(filename[1:])
		f = open(filename[1:],encoding="utf-8") #f = HelloWorld.html
		outputdata = f.read()
		header = 'HTTP/1.1 200 OK\r\n\r\n'
		#回覆報文
		connectionSocket.send(header.encode())
		for i in range(0, len(outputdata)):
			connectionSocket.send(outputdata[i].encode())
		#connectionSocket.close()
		
	except IOError:
		header = 'HTTP/1.1 404 NOT FOUND\r\n\r\n'
		connectionSocket.send(header.encode())
		connectionSocket.close()
# 瀏覽器鍵入 localhost:***/index.html會有兩個請求
# index.html && favicon.ico(網站的圖示)

客戶端程式碼

from socket import *
ClientSocket = socket(AF_INET, SOCK_STREAM)
ClientSocket.connect(('localhost',9999))
while True:
	#這裡的Connetction: close不同於瀏覽器常見的keep-alive,
	#close表示要求伺服器在傳送完被請求的物件後就關閉這條連結
	Head = '''GET /index.html HTTP/1.1\r\nHost: localhost:9999\r\nConnection: close\r\nUser-agent: Mozilla/5.0\r\n\r\n'''
	ClientSocket.send(Head.encode('utf-8'))
	data = ClientSocket.recv(1024)
	print(data)
	with open("response.html","wb") as f:
		f.write(data)

How to run

首先執行服務端程式碼WebServer.py

可以直接執行WebClient.py

此時會在工程下得到一個響應檔案response.html

也可以在瀏覽器輸入localhost:9999/index.html

瀏覽器方式時,需要取消對服務端程式碼第25行#connectionSocket.close()的註釋再執行,至於為什麼是這樣,這是個歷史遺留問題,有興趣還是私戳我郵箱交流吧。
在這裡插入圖片描述

作業2: UDP ping程式

問題描述

使用python採用UDP協議編寫一個ping程式,傳送一個簡單的ping報文給伺服器,並確定從客戶傳送ping報文伺服器到接受到pong報文為止的時延,稱為往返時延(RTT) 。
因為UDP是一個不可靠的協議,客戶傳送的分組可能會丟失,為此,客戶不能無限期地等待伺服器的響應,等待時間至多為1s,否則,列印一條錯誤資訊。

問題解決

主要程式碼

服務端程式碼

import random
from socket import *
#AF_INET指定使用IPv4協議,如果要用更先進的IPv6,就指定為AF_INET6
#SOCK_DGRAM指定了這個Socket的型別是UDP
serverSocket = socket(AF_INET, SOCK_DGRAM)
#用0.0.0.0繫結到所有的網路地址,還可以用127.0.0.1繫結到本機地址
serverSocket.bind(('127.0.0.1',12000))

while True:
	#產生一個0到10之間的隨機數
	rand = random.randint(0, 10)
	#從套介面上讀取資料,引數為緩衝區大小
	message, address = serverSocket.recvfrom(1024)
	#通過列印我們可以看到UDP客戶端socket的埠是不確定,系統隨機分配的
	print("收到來自 %s 的報文: (%s)" % (address,message))
	# 把接收到的資訊全部轉為大寫
	print("隨機數是: %d" % rand)
	message = message.upper()
	#如果隨機數小於4,服務端無應答,客戶端就會超時
	if rand < 4:
		continue
	serverSocket.sendto(message, address)

客戶端程式碼

import time
from socket import *
serverName = '127.0.0.1' # 主機
serverPort = 12000
# 建立Socket時,AF_INET指定使用IPv4協議,如果要用更先進的IPv6,就指定為AF_INET6
# SOCK_DGRAM指定了這個Socket的型別是UDP
# SOCK_STREAM指定使用面向流的TCP協議
clientSocket = socket(AF_INET, SOCK_DGRAM)
clientSocket.settimeout(1) # 設定超時時間為1s
for i in range(0, 10):
	oldTime = time.time()
	sendTime = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(oldTime))
	# encode()把str轉成bytes,傳輸格式要求
	message = ('package %d,client_local_time:%s' % (i + 1, sendTime)).encode()
	try:
		# 傳送資料
		clientSocket.sendto(message, (serverName, serverPort))
		# 1024指定要接收的最大資料量為1kb = 1024 bytes
		# recvfrom是一個系統呼叫,由使用者態轉向系統態,從套介面上接收資料,並捕獲資料傳送源的地址。
		# 如果資料報大於緩衝區,那麼緩衝區中只有資料報的前面部分,其他的資料都丟失了,並且recvfrom()函式返回WSAEMSGSIZE錯誤
		# 如果沒有資料待讀,那麼除非是非阻塞模式,不然的話套介面將一直等待資料的到來,果沒有在Timeout = 1s內接收到資料,此時將返回SOCKET_ERROR錯誤,錯誤程式碼是WSAEWOULDBLOCK。用select()或WSAAsynSelect()可以獲知何時資料到達
		# UDP的 recvfrom() 和 TCP 的recv()不一樣,具體可以看 TCP Ping專案
		modifiedMessage, serverAddress = clientSocket.recvfrom(1024)
		# 計算往返時間
		rtt = time.time() - oldTime
		# decode 把bytes轉成str
		modifiedMessage = modifiedMessage.decode("utf-8")
		print('報文 %d 收到來自 %s 的應答: %s,往返時延(RTT) = %fs' % (i+1, serverName,modifiedMessage, rtt))
	except Exception as e:
		print('報文 %d: 的請求超時' % (i+1)) # 處理異常

How to Run

先執行服務端程式碼,再執行客戶端程式碼,注意不要佔用埠。
在這裡插入圖片描述

郵件客戶

問題描述

使用STMP協議從一個郵箱向另一個郵箱傳送郵件

問題解決

可以先了解一下:Windows下操作POP3

主要程式碼

#作業3:郵件客戶
from smtplib import SMTP
from email.mime.text import MIMEText
from email.header import Header

mail_server = 'smtp.163.com'
#根據傳送方郵箱確定郵箱伺服器
#qq郵箱的伺服器為smtp.qq.com;163郵箱為smtp.163.com
def get_mail_server(sender):
	key = sender[sender.index('@')+1:]
	return "smtp."+key

port = '25'  ## SMTP協議預設埠是25
sender = '[email protected]'
mail_server = get_mail_server(sender)
sender_pass = 'put your mail_code here'    #注意是授權碼,而不是登入密碼,需要在郵箱端先獲取
receiver = '[email protected]'
mail_msg = 'this is a demo'

#第一個引數就是郵件正文,
# 第二個引數是MIME的subtype,傳入'plain'表示純文字,最終的MIME就是'text/plain',
# 最後一定要用utf-8編碼保證多語言相容性。
msg = MIMEText(mail_msg, 'plain', 'utf-8')
msg['From'] = sender
msg['To'] = receiver
#Header物件編碼文字,包含utf-8編碼資訊和Base64編碼。
msg['Subject'] = Header('來自inspurer的個人計算機', 'utf-8')
try:
	server = SMTP(mail_server, port)
	#用set_debuglevel(1),可以打印出和SMTP伺服器互動的所有資訊
	#server.set_debuglevel(1)
	server.login(sender, sender_pass)
	#由於可以一次發給多個人,所以傳入一個list,郵件正文是一個str,as_string()把MIMEText物件變成str
	server.sendmail(sender, (receiver), msg.as_string() )
	server.quit()
	print("郵件傳送成功!")
except:
	server.quit()
	print("郵件傳送失敗!")

How to Run

直接執行stmpDemo.py,注意程式碼裡的郵箱授權碼要填成你自己的,它和郵箱登入密碼不一樣,至於怎麼獲取百度吧,我不做搬運工
在這裡插入圖片描述在這裡插入圖片描述

作業4: 多執行緒Web代理伺服器

寫到這發現深夜了,以後更新