計算機網路自頂向下方法套接字程式設計作業
阿新 • • 發佈:2018-12-02
本部落格是針對,《計算機網路自頂向下方法》一書第二章後面套接字程式設計作業,
所有程式碼均已上傳至我的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代理伺服器
寫到這發現深夜了,以後更新