Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions C#/forSpbu/SimpleFtp.Client/Client.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Net.Sockets;
using SimpleFtp.Protocol;

namespace SimpleFtp.Client;

public class FtpClient
{
private readonly TcpClient _client = new TcpClient();
private StreamReader? _reader;
private StreamWriter? _writer;

private FtpClient(string hostname, int port)
{
Hostname = hostname;
Port = port;
}

public string Hostname { get; private set; }
public int Port { get; private set; }

public static async Task<FtpClient> Connect(string hostname, int port)
{
var client = new FtpClient(hostname, port);
await client._client.ConnectAsync(hostname, port);
client._reader = new StreamReader(client._client.GetStream());
client._writer = new StreamWriter(client._client.GetStream());
client._writer.AutoFlush = true;
return client;
}

public Response SendRequest(Request request)
{
_writer?.Write(request.ToString());
var data = _reader?.ReadLine() + "\n";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В случае get-запроса ответ некорректно считается: \n может раньше конца команды оказаться -- внутри содержимого файла.
Еще в случае get-запроса лучше передавать содержимое файла в поток, который предоставит пользователь. Чтобы файл напрямую в память не читать (так как он большой может быть), а предоставить право выбора вызывающему

return ResponseFactory.Create(data);
}

public void Disconnect()
{
_client.Close();
}
}
31 changes: 31 additions & 0 deletions C#/forSpbu/SimpleFtp.Client/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using SimpleFtp.Client;
using SimpleFtp.Protocol;

var client = await FtpClient.Connect("localhost", 32768);
Console.WriteLine("Connected");
while (true)
{
var command = Console.ReadLine();
if (command == "exit")
{
client.Disconnect();
break;
}

if (command == null)
{
Console.WriteLine("Incorrect command");
continue;
}

try
{
var request = RequestFactory.Create(command + "\n");
var response = client.SendRequest(request);
Console.Write(response.ToString());
}
catch (RequestParseException)
{
Console.WriteLine("Incorrect command");
}
}
15 changes: 15 additions & 0 deletions C#/forSpbu/SimpleFtp.Client/SimpleFtp.Client.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>SimpleFtpClient</RootNamespace>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\SimpleFtp.Protocol\SimpleFtp.Protocol.csproj" />
</ItemGroup>

</Project>
16 changes: 16 additions & 0 deletions C#/forSpbu/SimpleFtp.Protocol/Request/GetRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace SimpleFtp.Protocol;

public class GetRequest : Request
{
public string Path { get; private set; }

public GetRequest(string path)
{
Path = path;
}

public override string ToString()
{
return "2 " + Path + "\n";
}
}
16 changes: 16 additions & 0 deletions C#/forSpbu/SimpleFtp.Protocol/Request/ListRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace SimpleFtp.Protocol;

public class ListRequest : Request
{
public string Path { get; private set; }

public ListRequest(string path)
{
Path = path;
}

public override string ToString()
{
return "1 " + Path + "\n";
}
}
6 changes: 6 additions & 0 deletions C#/forSpbu/SimpleFtp.Protocol/Request/Request.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace SimpleFtp.Protocol;

public abstract class Request
{
public abstract override string ToString();
}
31 changes: 31 additions & 0 deletions C#/forSpbu/SimpleFtp.Protocol/Request/RequestFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.Text.RegularExpressions;

namespace SimpleFtp.Protocol;

public static partial class RequestFactory
{
private const string GetPattern = "2 (?<path>[1-9a-zA-Z./\\\\]+)\n";
private const string ListPattern = "1 (?<path>[1-9a-zA-Z./\\\\]+)\n";

public static Request Create(string request)
{
if (GetRegex().IsMatch(request))
{
var match = GetRegex().Match(request);
return new GetRequest(match.Groups["path"].Value);
}
if (ListRegex().IsMatch(request))
{
var match = ListRegex().Match(request);
return new ListRequest(match.Groups["path"].Value);
}

throw new RequestParseException();
}

[GeneratedRegex(GetPattern)]
private static partial Regex GetRegex();

[GeneratedRegex(ListPattern)]
private static partial Regex ListRegex();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace SimpleFtp.Protocol;

public class RequestParseException : FormatException
{
}
18 changes: 18 additions & 0 deletions C#/forSpbu/SimpleFtp.Protocol/Response/GetResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace SimpleFtp.Protocol;

public class GetResponse : Response
{
private readonly byte[]? _file;
private int Size => _file?.Length ?? -1;

public GetResponse(byte[] fileBytes)
{
_file = fileBytes;
}

public GetResponse()
{
}

public override string ToString() => Size + " " + System.Text.Encoding.UTF8.GetString(_file ?? Array.Empty<byte>()) + "\n";
}
22 changes: 22 additions & 0 deletions C#/forSpbu/SimpleFtp.Protocol/Response/ListResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace SimpleFtp.Protocol;

public class ListResponse : Response
{
private readonly IEnumerable<(string name, bool isDir)>? _list;

private int Size => _list?.Count() ?? -1;

public ListResponse(IEnumerable<(string name, bool isDir)> dirList)
{
_list = dirList;
}

public ListResponse()
{
}

public override string ToString() =>
Size + " " +
string.Join(' ', (_list ?? Array.Empty<(string name, bool isDir)>())
.Select<(string name, bool isDir), string>(x => x.name + " " + x.isDir)) + "\n";
}
6 changes: 6 additions & 0 deletions C#/forSpbu/SimpleFtp.Protocol/Response/Response.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace SimpleFtp.Protocol;

public abstract class Response
{
public abstract override string ToString();
}
53 changes: 53 additions & 0 deletions C#/forSpbu/SimpleFtp.Protocol/Response/ResponseFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System.Text;
using System.Text.RegularExpressions;

namespace SimpleFtp.Protocol;

public static partial class ResponseFactory
{
private const string ListPattern = "(?<size>[0-9]+) ((?<names>[1-9a-zA-Z./\\\\]+) (?<isDirs>False|True) )*((?<names>[1-9a-zA-Z./\\\\]+) (?<isDirs>False|True))+\n";
private const string GetPattern = "(?<size>[0-9]+) (?<content>.+)\n";

public static Response Create(string response)
{
if (ListRegex().IsMatch(response))
{
var match = ListRegex().Match(response);

var isDirs = match.Groups["isDirs"].Captures.Select(x => x.Value == "True").ToArray();
var names = match.Groups["names"].Captures.Select(x => x.Value).ToArray();
if (!int.TryParse(match.Groups["size"].Value, out var size) || names.Length != size)
{
throw new ResponseParseException();
}

var list = new (string, bool)[size];
for (int i = 0; i < size; i++)
{
list[i] = (names[i], isDirs[i]);
}
return new ListResponse(list);
}
if (GetRegex().IsMatch(response))
{
var match = GetRegex().Match(response);

var content = match.Groups["content"].Value;
if (!int.TryParse(match.Groups["size"].Value, out _))
{
throw new ResponseParseException();
}

return new GetResponse(Encoding.ASCII.GetBytes(content));
}

throw new ResponseParseException();
}


[GeneratedRegex(ListPattern)]
private static partial Regex ListRegex();

[GeneratedRegex(GetPattern)]
private static partial Regex GetRegex();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace SimpleFtp.Protocol;

public class ResponseParseException : FormatException
{
}
9 changes: 9 additions & 0 deletions C#/forSpbu/SimpleFtp.Protocol/SimpleFtp.Protocol.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
11 changes: 11 additions & 0 deletions C#/forSpbu/SimpleFtp.Server/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using SimpleFtp;
using SimpleFtp.Protocol;

var server = new FtpServer();
var cancellation = new CancellationTokenSource();
Task.Run(() => server.Listen(cancellation));
var input = Console.ReadLine();
if (input == "exit")
{
cancellation.Cancel();
}
Loading