""" 校园内网 FTP 文件传输实训——服务器端 ====================================== 运行位置:教师机,或者小组中担任“服务器”的电脑。 主要功能: 1. 在本机 2121 端口启动一个 FTP 服务器; 2. 自动创建 ftp_share 文件夹,并把它作为 FTP 根目录; 3. 创建课堂临时账号 student / 123456; 4. 允许客户端查看目录、上传文件和下载文件; 5. 把 FTP 被动数据端口限制在 30000—30009,方便课堂排错。 运行命令: python -m pip install pyftpdlib python ftp_server.py 停止服务器: 在终端中按 Ctrl+C。 安全说明: - 标准 FTP 不加密账号、密码和文件内容; - 只能在学校允许的受控内网中传输测试文件; - 不要传个人隐私、真实密码或敏感材料; - 不要做公网端口映射; - 下课后应按 Ctrl+C 关闭服务器。 """ from pathlib import Path # pyftpdlib不是Python标准库,需要教师机提前安装。 from pyftpdlib.authorizers import DummyAuthorizer from pyftpdlib.handlers import FTPHandler from pyftpdlib.servers import FTPServer # 0.0.0.0表示监听本机所有IPv4网卡。 # 学生连接时不能填写0.0.0.0,而要填写教师机通过ipconfig查到的IPv4地址。 HOST = "0.0.0.0" # FTP标准控制端口通常是21。 # 本实训改用2121,避免占用标准端口,也更适合普通用户运行教学程序。 PORT = 2121 # 这是临时课堂账号。标准FTP会以明文方式传输凭据。 USERNAME = "student" PASSWORD = "123456" # __file__表示当前脚本文件;with_name表示在脚本旁边创建ftp_share文件夹。 # 学生上传的文件都会保存在这个目录中。 SHARE_DIR = Path(__file__).with_name("ftp_share") # FTP除了控制连接,还需要数据连接。 # 本例把被动模式数据端口限制为30000—30009,共10个端口。 PASSIVE_PORTS = range(30000, 30010) def build_authorizer() -> DummyAuthorizer: """创建FTP用户并规定其可访问的目录和权限。""" authorizer = DummyAuthorizer() # perm="elrw"采用较小权限: # e:进入/切换目录;l:列出目录;r:下载文件;w:上传文件。 # 没有授予d、f、m等权限,因此学生不能删除、重命名或新建远程目录。 authorizer.add_user( USERNAME, PASSWORD, str(SHARE_DIR), perm="elrw", ) return authorizer def main() -> None: """创建共享目录、配置处理器并持续运行FTP服务器。""" # exist_ok=True表示文件夹已经存在时不报错。 SHARE_DIR.mkdir(exist_ok=True) # FTPHandler负责处理客户端的登录、LIST、STOR、RETR等FTP命令。 handler = FTPHandler handler.authorizer = build_authorizer() handler.banner = "Smart-home FTP classroom server" handler.passive_ports = PASSIVE_PORTS handler.encoding = "utf-8" # 将服务器绑定到0.0.0.0:2121。 # 如果2121已被占用,这里会出现“Address already in use”之类的错误。 server = FTPServer((HOST, PORT), handler) print("=" * 60) print("校园内网 FTP 教学服务器已启动") print(f"[LISTEN] FTP {HOST}:{PORT}") print(f"[SHARE ] {SHARE_DIR.resolve()}") print(f"[LOGIN ] {USERNAME} / {PASSWORD}") print("[DATA ] 被动数据端口 30000-30009") print("[STOP ] 按 Ctrl+C 关闭服务器") print("=" * 60) try: # serve_forever会持续等待并处理客户端连接。 # pyftpdlib采用异步模型,可以接待多个FTP客户端; # 但本例只有10个被动数据端口,建议每批不超过10组同时传输。 server.serve_forever() except KeyboardInterrupt: print("\n[STOP] 收到Ctrl+C,正在关闭FTP服务器……") finally: # 关闭所有尚未结束的FTP连接,释放端口。 server.close_all() print("[STOP] FTP服务器已关闭。") if __name__ == "__main__": # 只有直接运行本文件时才调用main;被其他文件import时不会自动启动服务器。 main()