服务器代码:
ftpServer.py
import socket
import threading
import os
import struct
# 用户账号, 密码, 主目录
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
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)