服務器代碼:
ftpServer.py
import socket
import threading
import os
import struct
import threading
import os
import struct
# 用戶賬號, 密碼, 主目錄
users = {'1': {'pwd': '1', 'home': r'C:\Users\算法練習'},
'test2': {'pwd': 'testpwd2', 'home': r'D:\知識圖譜資料'}
}
users = {'1': {'pwd': '1', 'home': r'C:\Users\算法練習'},
'test2': {'pwd': 'testpwd2', 'home': r'D:\知識圖譜資料'}
}
def server(conn, addr, home):
print('新客戶端:' + str(addr))
os.chdir(home) # 進入當前用戶主目錄
while True:
data = str(conn.recv(100).decode().lower())
print(data) # 顯示客戶端輸入的每一條命令
# 客戶端退出
if data in ('quit', 'q'):
break
# 查看當前文件夾的文件列表
elif data in ('list', 'ls', 'dir'):
files = str(os.listdir(os.getcwd()))
files = files.encode()
# 先發送字節串大小, 再發送字節串
conn.send(struct.pack('I', len(files)))
conn.send(files)
elif ''.join(data) == 'cd..':
cwd = os.getcwd()
newCwd = cwd[:cwd.rindex('\\')]
# 考慮根目錄的情況
if newCwd[-1] == ':':
newCwd += '\\'
# 限定主目錄
if newCwd.lower().startswith(home):
os.chdir(newCwd)
conn.send(b'ok')
else:
conn.send(b'error')
# 查看當前目錄
elif data in ('cwd', 'cd'):
conn.send(str(os.getcwd()).encode())
elif data.startswith('cd '):
# 指定最大分割次數,考慮目標文件夾帶有空格的情況
# 只允許使用相對路徑進行跳轉
data = data.split(maxsplit=1)
if len(data) == 2 and os.path.isdir(data[1]) and data[1] != os.path.abspath(data[1]):
os.chdir(data[1])
conn.send(b'ok')
else:
conn.send(b'error')
# 下載文件
elif data.startswith('get '):
data = data.split(maxsplit=1)
# 檢查文件是否存在
if len(data) == 2 and os.path.isfile(data[1]):
conn.send(b'ok')
fp = open(data[1], 'rb')
while True:
content = fp.read(4096)
# 發送文件結束
if not content:
conn.send(b'over')
break
conn.send(content)
if conn.recv(10) == b'ok':
continue
fp.close()
else:
conn.sen(b'no')
# 命令無效
else:
pass
conn.close()
print(addr + '斷開連接')
# 創建socket, 監聽本地端口, 等待客戶端連接
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('', 10600))
sock.listen(5)
print('等待連接....')
while True:
conn, addr = sock.accept()
# 驗證客戶端輸入的用戶名和密碼是否正確
userId, userPwd = conn.recv(1024).decode().split(',')
if userId in users and users[userId]['pwd'] == userPwd:
conn.send(b'ok')
# 為每個客戶端連接創建並啟動一個線程
# 參數為連接, 客戶端地震, 客戶端主目錄
home = users[userId]['home']
t = threading.Thread(target=server, args=(conn, addr, home))
t.daemon = True
t.start()
else:
conn.send(b'error')
客戶端源代碼:
ftpClient.py
import socket
import struct
import getpass
import struct
import getpass
def main(serverIP):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((serverIP, 10600))
userId = input("請輸入用戶名:")
userPwd = input("請輸入密碼:")
message = userId + ',' + userPwd
sock.send(message.encode())
login = sock.recv(100)
# 驗證是否登錄成功
if login == b'error':
print('用戶米或者密碼錯誤')
return
# 整理編碼大小
intSize = struct.calcsize('I')
while True:
# 接收客戶端命令. 其中##>是命令提示符
command = input('Command:').lower().split()
# 沒有輸入任何有效字符, 提前進入下一次循環, 等待用戶繼續輸入
if not command:
continue
# 向服務器發送命令
command = ' '.join(command)
sock.send(command.encode())
# 退出
if command in ('quit', 'q'):
break
# 查看文件列表
elif command in ('list', 'ls', 'dir'):
# 先接收字符串帶下, 再根據情況接收合適數量的字節串
loc_size = struct.unpack('I', sock.recv(intSize))[0]
files = eval(sock.recv(loc_size).decode())
for item in files:
print(item)
# 切換至上一級目錄
elif ''.join(command.split()) == 'cd..':
print(sock.recv(100).decode())
# 當前工作目錄
elif command in ('cwd', 'cd'):
print(sock.recv(1024).decode())
# 切換至子文件夾
elif command.startswith('cd '):
print(sock.recv(100).decode())
# 從服務器下載文件
elif command.startswith('get '):
isFileExist = sock.recv(20)
# 文件存在則下載,不存在則提示文件不存在
if isFileExist != b'ok':
print("file not found")
else:
print("downloading", end='')
fp = open(command.split()[1], 'wb')
while True:
# 顯示進度
print('*', end='')
data = sock.recv(4096)
if data == b'over':
break
fp.write(data)
sock.send(b'ok')
fp.close()
print('Download complete')
# 無效命令
else:
print('Command not found')
sock.close()
if __name__ == '__main__':
serverIP = '192.168.0.100'
# 使用正則判斷服務器地址是否為合法的IP地址
main(serverIP)