摘要
一到放假就雜事很多,這次的作業比較復雜,做了一個周,進度又拖了。不過結果還不錯。
正文
粘包
在上一節中,如果連續發送過多數據,就可能發生粘包。粘包就是兩次發送的數據粘在一起被接收,損壞了數據的完整性。解決方法有兩種。
方案一:
在發送多個數據之間添加接收確認。這樣在完成一次發送以后只有接收到另一端的確認以后才會開始新的發送,避免了粘包的發生。
方案二:
首先將發送數據的大小發送給另一端,另一端根據數據的大小接收。一次接收一次發送的數據量,這樣也就避免了數據的粘包。例子。


#方案1 #server f = open(path, 'wb') for i in f:server.socket.send(i) f.close() server.socket.recv(1024) server.socket.send(b'發送完畢') #client recv_data = '' while True:data = client.socket.recv(1024)if not data:breakrecv_data += data client.socket.send(b'recv over') other_data = client.socket.recv(1024) #方案2 #server size = os.path.getsize(path) server.socket.send(size.encode()) server.socket.recv(1024) f = open(path, 'wb') for i in f:server.socket.send(i) f.close() server.socket.send(b'發送完畢') #client size = client.socket.recv(1024) client.socket.send(b'start') recv_data = '' recv_size = 0 while recv_size < size:if size - recv_size < 1024:single_size = size - recv_sizeelse:single_size = 1024data = client.socket.recv(single_size)recv_size += len(data)recv_data += data other_data = client.socket.recv(1024)
FTP
一個簡單的FTP過程主要包含以下幾個步驟。
1.讀取文件名
2.檢測文件是否存在
3.打開文件
4.檢測文件大小,文件名
5.發送文件大小 md5值給客戶端
6.等待客戶端確認
7.開始邊讀取數據邊發送數據
8.md5確認
具體例子。
服務器


# 服務器端 import socket import os import hashlibserver = socket.socket() server.bind(('localhost', 9999)) server.listen()while True:conn, addr = server.accept()print('new conn:', addr)while True:data = conn.recv(1024)data = data.decode()if not data:print('客戶端已斷開')breakcmd, filename = data.split()print(filename)if os.path.isfile(filename):f = open(filename, 'rb')m = hashlib.md5()file_size = os.stat(filename).st_sizeprint(file_size)conn.send(str(file_size).encode('utf-8'))conn.recv(1024)for line in f:m.update(line)conn.send(line)print('md5:', m.hexdigest())f.close()conn.send(m.hexdigest().encode('utf-8'))
客戶端


# 客戶端 import socket import hashlibclient = socket.socket() client.connect(('localhost', 9999))while True:cmd = input('>>').strip()if len(cmd) == 0:continueif cmd.startswith('get'):client.send(cmd.encode('utf-8'))cmd_res_size = client.recv(1024) # 接收長度print('文件大小 %s' % (cmd_res_size.decode()))client.send('準備完畢,開始發送數據'.encode('utf-8'))recv_size = 0file_name = cmd.split()[1]f = open(file_name + '.new', 'wb')m = hashlib.md5()while recv_size < int(cmd_res_size.decode()):if int(cmd_res_size.decode()) - recv_size > 1024:size = 1024else:size = int(cmd_res_size.decode()) - recv_sizedata = client.recv(size)recv_size += len(data) # 讀取每次接收的數據 f.write(data)m.update(data)#print(recv_size)else:file_md5 = m.hexdigest()print('文件接收完畢', recv_size)f.close()recv_md5 = client.recv(1024)print(file_md5, recv_md5.decode()) client.close()
SocketServer
SocketServer是Python的一個包,它在socket的基礎上封裝,可以更加簡單的完成并發處理。
socketserver.TCPServer 繼承BaseServer,完成TCP
socketserver.UDPServer 繼承TCPServer,完成UDP
socketserver.UnixStreamServer?繼承TCPServer,完成Unix的TCP
socketserver.UnixDatagramServer?繼承UDPServer,完成Unix的UDP
使用socketserver的步驟:
1.創建一個請求處理類,并且這個類要繼承BaseRequestHandler,并且需要重寫父類中的Handle()方法。
2.實例化一個Server類,并且傳遞Server ip和1中創建的請求處理類給它。
3.server.handle_request()只處理一個請求 server.server_forever()處理多個請求,永久執行。
4.調用server_close()關閉它。
BaseServer中的常用方法,例子。


fileno() # 返回文件描述符 handle_request # 處理單個請求 serve_forever(poll_interval=0.5) # 一直運行直到收到shutdown()請求,每poll_interval檢查一次,后調用service_actions()結束 service_actions() # 結束操作 shutdown() # 停止信號 server_close() # 清除server address_family # 地址簇 RequestHandlerClass # 請求處理類 server_address # ip地址 socket # 同socket self.allow_reuse_adress # 允許重用地址 socket中socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) socket_type # socket使用的協議類型 self.setup() # 請求來之前 self.handle() # 請求來時 self.finish() # 請求處理之后
作業
1.用戶加密認證
2.允許多個用戶登陸
3.每個用戶有自己的家目錄,不能互相訪問
4.對用戶進行磁盤配額,可用空間不同
5.允許用戶在ftp_server上隨意切換目錄
6.允許用戶查看當前目錄文件
7.允許用戶上傳下載文件,保證文件一致性
8.傳輸過程顯示進度條
作業