Originally, I thought the problem might be related to
Windows explorer hangs up FTP connection after PASV command
But I tried opening the passive port ahead of time and it didn't help. Instead, the problem is related to the strictness of Windows Explorer. The IP address supplied as part of the 227 response must be identical to the address of the FTP site the client initially connected. In other words, if the client connects using
ftp://localhost
(which resolves to 127.0.0.1), the IP address provided with the PASV response MUST be 127.0.0.1 - otherwise, Windows Explorer will error. This is not to say that the passive port can't be opened with IPAddress.Any - it can:
var listener = new TcpListener(IPAddress.Any, 0)
However, the address returned with the result must still be 127.0.0.1 (using the above as an example). If the client initially connects with the IP of the machine, say 10.x.x.x for example, the IP address returned with the 227 response must also be 10.x.x.x.
FileZilla must somehow be more forgiving.