简体   繁体   English

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

[英]NET6.0 FTP server networking issue on Docker container

I'm developing a NET 6.0 FTP server as part of a functionality to load the firmware of a hardware device.我正在开发一个 NET 6.0 FTP 服务器,作为加载硬件设备固件的功能的一部分。 I need to have it inside a Docker container but I'm unable to make it work on that environment when it works perfectly when I execute it as a regular executable.我需要将它放在 Docker 容器中,但是当我将它作为常规可执行文件执行时,当它完美运行时,我无法使其在该环境中运行。 It seems to be something related to docker networking but I can't figure it out what it is.这似乎与 docker 网络有关,但我无法弄清楚它是什么。

This is the Dockerfile for the container, that is based on Alpine (mcr.microsoft.com/dotnet/aspnet:6.0-alpine), with some additions from the default Dockerfile created by Visual Studio:这是容器的 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"]

The Docker run parameters are these Docker 运行参数是这些

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

where 20 is the control port for FTP, it is the port used by that external hardware device and cannot be modified by me, and 21000-22000 is the range for FTP passive mode.其中 20 是 FTP 的控制端口,它是该外部硬件设备使用的端口,我无法修改,而 21000-22000 是 FTP 被动模式的范围。

The FTP server code is quite simple and it works nice directly being executed in the host machine: 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();
    }

} }

The issue seems to be located in _controlWriter, it seems that anything is blocking the response to the device (220 Service Ready) or maybe the frame is not being redirected to the right network interface, because nothing is read from _controlReader.问题似乎出在 _controlWriter 中,似乎有任何东西阻止了对设备的响应(220 服务就绪),或者可能没有将帧重定向到正确的网络接口,因为没有从 _controlReader 读取任何内容。 As I mentioned earlier, this exact same code works perfectly when I execute it in the host machine, outside Docker container, that's the reason why I think it could be something related to Docker networking.正如我之前提到的,当我在主机中执行该完全相同的代码时,它可以完美运行,在 Docker 容器之外,这就是为什么我认为它可能与 Docker 网络有关的原因。

I hope you can help me, thanks!我希望你能帮助我,谢谢!

It was something related to carriage return.这是与回车有关的事情。 Since docker container used a Linux based image, the carriage return was \n and the device expected \r\n.由于 docker 容器使用了基于 Linux 的映像,因此回车符为 \n 并且设备应为 \r\n。

Thanks to everyone who took a look at this.感谢所有看过这个的人。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM