【问题标题】:NET6.0 FTP server networking issue on Docker containerNET6​​.0 Docker 容器上的 FTP 服务器网络问题
【发布时间】: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


    【解决方案1】:

    这是与回车有关的事情。由于 docker 容器使用基于 Linux 的映像,因此回车符为 \n 并且设备应为 \r\n。

    感谢所有看过这个的人。

    【讨论】:

      猜你喜欢
      • 2023-03-07
      • 1970-01-01
      • 2014-03-25
      • 1970-01-01
      • 1970-01-01
      • 2019-03-10
      • 2020-11-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多