【发布时间】:2022-08-20 05:44:42
【问题描述】:
我正在开发 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,似乎有任何东西阻止了对设备的响应(220 Service Ready),或者可能没有将帧重定向到正确的网络接口,因为没有从@987654330 读取任何内容@。正如我之前提到的,当我在 Docker 容器外部的主机中执行该完全相同的代码时,它可以完美运行,这就是为什么我认为它可能与 Docker 网络有关的原因。
我希望你能帮助我,谢谢!
标签: docker .net-6.0 alpine streamwriter ftp-server