Docker容器上的NET6.0 FTP服务器网络问题



我正在开发NET 6.0 FTP server,作为加载硬件设备固件功能的一部分。我需要将它放在Docker容器中,但当它作为常规可执行文件执行时,我无法使它在该环境中正常工作。这似乎与docker网络有关,但我不知道它是什么。

这是容器的Dockerfile,它基于Alpine(mcr.microsoft.com/dotnet/aspnet:6.0-alpine(,并从Visual Studio创建的默认Dockerfile中添加了一些内容:

FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

RUN apk add openrc --no-cache
ENV MUSL_LOCALE_DEPS cmake make musl-dev gcc gettext-dev libintl 
ENV MUSL_LOCPATH /usr/share/i18n/locales/musl
RUN apk add --no-cache 
$MUSL_LOCALE_DEPS 
&& wget https://gitlab.com/rilian-la-te/musl-locales/-/archive/master/musl-locales-master.zip 
&& unzip musl-locales-master.zip 
&& cd musl-locales-master 
&& cmake -DLOCALE_PROFILE=OFF -D CMAKE_INSTALL_PREFIX:PATH=/usr . && make && make install 
&& cd .. && rm -r musl-locales-master
RUN apk add icu-libs
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
FROM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS build
WORKDIR /src
COPY ["nuget.config", "."]
COPY ["CONTAINERS/Project1/Project1.csproj", "CONTAINERS/Project/"]
RUN dotnet restore "CONTAINERS/Project1.csproj"
COPY . .
WORKDIR "/src/CONTAINERS/Project1"
RUN dotnet build "Project1.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "Project1.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Project1.dll"]

Docker运行参数是以下

-p 20:20 -p 21000-22000:21000-22000

其中20是FTP的控制端口,它是该外部硬件设备使用的端口,我不能修改,21000-22000是FTP被动模式的范围。

FTP服务器代码非常简单,直接在主机中执行也很好:

public class FtpServer : IDisposable
{
...

public ErrorCode Start(string ip, int port, string basepath, string user, string password, int minPassivePort = 0, int maxPassivePort = 0)
{
ErrorCode retVal = ErrorCode.Success;
_basepath = basepath;
_user = user;
_password = password;
PassivePortMin = minPassivePort;
PassivePortMax = maxPassivePort;
ServicePointManager.DefaultConnectionLimit = 200;
_localEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);
_listener = new TcpListener(_localEndPoint);
_listening = true;
_activeConnections = new List<ClientConnection>();
try
{
_listener.Start();
LocalEndpoint = ((IPEndPoint)_listener.LocalEndpoint).Address.ToString();
_listener.BeginAcceptTcpClient(HandleAcceptTcpClient, _listener);
}
catch (Exception ex)
{
log.Error("Error starting FTP server", ex);
retVal = ErrorCode.ConnectionFailure;
}
return retVal;
}
private void HandleAcceptTcpClient(IAsyncResult result)
{
if (_listening)
{
TcpClient client = _listener.EndAcceptTcpClient(result);
_listener.BeginAcceptTcpClient(HandleAcceptTcpClient, _listener);
ClientConnection connection = new ClientConnection(client, _user, _password, _basepath);
ThreadPool.QueueUserWorkItem(connection.HandleClient, client);
}
}
public class ClientConnection
{
public ClientConnection(TcpClient client, string username, string password, string basepath)
{

_controlClient = client;
_currentUser = new User
{
Username = username,
Password = password,
HomeDir = basepath
};
_validCommands = new List<string>();
}
public void HandleClient(object obj)
{
//  bool error = false;
try
{
_remoteEndPoint = (IPEndPoint)_controlClient.Client.RemoteEndPoint;
_clientIP = _remoteEndPoint.Address.ToString();
_controlStream = _controlClient.GetStream();
_controlReader = new StreamReader(_controlStream);
_controlWriter = new StreamWriter(_controlStream);

_controlWriter.WriteLine("220 Service Ready.");
_controlWriter.Flush();
_validCommands.AddRange(new string[] { "AUTH", "USER", "PASS", "QUIT", "HELP", "NOOP" });
string line;
_dataClient = new TcpClient();
string renameFrom = null;
while ((line = _controlReader.ReadLine()) != null)
{
string response = null;
string[] command = line.Split(' ');
string cmd = command[0].ToUpperInvariant();
string arguments = command.Length > 1 ? line.Substring(command[0].Length + 1) : null;
if (arguments != null && arguments.Trim().Length == 0)
{
arguments = null;
}
if (!_validCommands.Contains(cmd))
{
response = CheckUser();
}
if (cmd != "RNTO")
{
renameFrom = null;
}
Console.WriteLine(cmd + " " + arguments);
if (response == null)
{
switch (cmd)
{
default:
response = "502 Command not implemented";
break;
}
}
if (_controlClient == null || !_controlClient.Connected)
{
break;
}
else
{
if (!string.IsNullOrEmpty(response))
{
_controlWriter.WriteLine(response);
_controlWriter.Flush();
}
Console.WriteLine(response);
if (response.StartsWith("221"))
{
break;
}
}
}
}
catch (Exception ex)
{
log.Error("Error sending command", ex);
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
Dispose();
}

}

问题似乎位于_controlWriter中,似乎有任何东西阻止了对设备的响应(220Service Ready(,或者帧没有重定向到正确的网络接口,因为_controlReader中没有读取任何内容。正如我之前提到的,当我在Docker容器外的主机上执行时,这个完全相同的代码可以完美地工作,这就是为什么我认为它可能与Docker网络有关。

我希望你能帮助我,谢谢!

这与回车有关。由于docker容器使用了基于Linux的映像,因此回车为,\r\n设备为。

感谢所有看过这篇文章的人。

最新更新