如何用Python socket向FTP服务器发送多条LIST命令?
问题根源
FTP的PASV模式中,数据连接是单次专用的:服务器完成一次数据传输(比如LIST返回目录列表)后,会主动关闭对应的数据连接。你第一次执行LIST后,sock_2已经被服务器关闭,第二次再用它接收数据自然返回空值——这是因为你试图复用已失效的数据连接,违反了FTP协议的设计逻辑。
解决方案
每次执行LIST(或其他需要数据传输的FTP命令,如RETR、STOR)前,必须重新建立数据连接,步骤如下:
- 向控制连接发送
PASV命令,获取服务器新的被动端口 - 解析端口号,建立新的数据连接
- 发送LIST命令,通过新连接接收目录数据
修正后的代码
import socket HOST = '[IP address]' USERNAME = '[login]' PASSWORD = '[password]' def get_data_port(control_sock): # 发送PASV命令并解析端口 control_sock.send('PASV\r\n'.encode()) response = control_sock.recv(1024).decode('utf-8') response_values = response.split(',') port_high = int(response_values[-2]) port_low = int(response_values[-1].replace(').\r\n', '')) return port_high * 256 + port_low # 建立控制连接并登录 sock_1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock_1.connect((HOST, 21)) print(sock_1.recv(1024).decode()) # 打印服务器欢迎信息 sock_1.send(f'USER {USERNAME}\r\n'.encode()) print(sock_1.recv(1024).decode()) sock_1.send(f'PASS {PASSWORD}\r\n'.encode()) print(sock_1.recv(1024).decode()) # 第一次LIST port = get_data_port(sock_1) sock_2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock_2.connect((HOST, port)) sock_1.send('LIST\r\n'.encode()) print(sock_1.recv(4096).decode()) print(sock_2.recv(4096).decode()) sock_2.close() # 主动关闭数据连接,养成良好习惯 # 第二次LIST(重新建立数据连接) port = get_data_port(sock_1) sock_2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock_2.connect((HOST, port)) sock_1.send('LIST\r\n'.encode()) print(sock_1.recv(4096).decode()) print(sock_2.recv(4096).decode()) sock_2.close() # 关闭控制连接 sock_1.send('QUIT\r\n'.encode()) sock_1.close()
额外提示
- 实际场景中,
recv()可能无法一次性接收所有数据,建议用循环接收直到数据传输完成(比如根据服务器返回的226判断传输结束后,再循环读取数据连接的内容) - 生产环境中优先使用Python标准库的
ftplib,它已经封装了所有FTP协议细节,无需手动处理socket连接:
from ftplib import FTP with FTP(HOST) as ftp: ftp.login(USERNAME, PASSWORD) # 第一次获取目录 ftp.retrlines('LIST', print) # 第二次获取目录 ftp.retrlines('LIST', print)
内容的提问来源于stack exchange,提问作者Sun of A beach
相关产品推荐
相关产品推荐

