From bee08f34e6b53d58ba253083887af67899f0ddb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=AE=E5=A4=AA=20=E6=AB=BB=E4=BA=95?= Date: Sat, 1 Jun 2024 12:28:32 +0900 Subject: [PATCH] Add: ProtocolLib --- ProtocolLib/.gitignore | 2 + ProtocolLib/PacketIO/IPacketFile.cs | 12 ++ ProtocolLib/PacketIO/PacketDirectory.cs | 53 ++++++++ ProtocolLib/PacketIO/PacketFile.cs | 60 +++++++++ ProtocolLib/PacketIO/PacketFileType.cs | 11 ++ ProtocolLib/PacketIO/TreePacketFile.cs | 150 +++++++++++++++++++++++ ProtocolLib/Program.cs | 71 +++++++++++ ProtocolLib/ProtocolLib.csproj | 10 ++ ProtocolLib/ProtocolLib/ByteConverter.cs | 15 +++ ProtocolLib/ProtocolLib/DataOperator.cs | 12 ++ ProtocolLib/ProtocolLib/DataType.cs | 8 ++ ProtocolLib/ProtocolLib/PackeFactory.cs | 79 ++++++++++++ ProtocolLib/ProtocolLib/Packet.cs | 14 +++ ProtocolLib/ProtocolLib/PacketStream.cs | 70 +++++++++++ 14 files changed, 567 insertions(+) create mode 100644 ProtocolLib/.gitignore create mode 100644 ProtocolLib/PacketIO/IPacketFile.cs create mode 100644 ProtocolLib/PacketIO/PacketDirectory.cs create mode 100644 ProtocolLib/PacketIO/PacketFile.cs create mode 100644 ProtocolLib/PacketIO/PacketFileType.cs create mode 100644 ProtocolLib/PacketIO/TreePacketFile.cs create mode 100644 ProtocolLib/Program.cs create mode 100644 ProtocolLib/ProtocolLib.csproj create mode 100644 ProtocolLib/ProtocolLib/ByteConverter.cs create mode 100644 ProtocolLib/ProtocolLib/DataOperator.cs create mode 100644 ProtocolLib/ProtocolLib/DataType.cs create mode 100644 ProtocolLib/ProtocolLib/PackeFactory.cs create mode 100644 ProtocolLib/ProtocolLib/Packet.cs create mode 100644 ProtocolLib/ProtocolLib/PacketStream.cs diff --git a/ProtocolLib/.gitignore b/ProtocolLib/.gitignore new file mode 100644 index 0000000..1746e32 --- /dev/null +++ b/ProtocolLib/.gitignore @@ -0,0 +1,2 @@ +bin +obj diff --git a/ProtocolLib/PacketIO/IPacketFile.cs b/ProtocolLib/PacketIO/IPacketFile.cs new file mode 100644 index 0000000..b76313e --- /dev/null +++ b/ProtocolLib/PacketIO/IPacketFile.cs @@ -0,0 +1,12 @@ +namespace PacketIO; + +public interface IPacketFile : IDisposable +{ + public PacketFileType GetFileType(); + + public string GetFileName(); + + public long GetFileSize(); + + public IEnumerable EnumerableFiles(); +} diff --git a/ProtocolLib/PacketIO/PacketDirectory.cs b/ProtocolLib/PacketIO/PacketDirectory.cs new file mode 100644 index 0000000..58cb3e8 --- /dev/null +++ b/ProtocolLib/PacketIO/PacketDirectory.cs @@ -0,0 +1,53 @@ +namespace PacketIO; + +public class PacketDirectory : IPacketFile +{ + private string fileName; + private List files; + + public PacketDirectory(string fileName, params IPacketFile[] files) + { + this.fileName = fileName; + this.files = new List(); + this.files.AddRange(files); + } + + public IEnumerable EnumerableFiles() + { + return this.files.ToList(); + } + + public string GetFileName() + { + return this.fileName; + } + + public long GetFileSize() + { + return 0; + } + + public PacketFileType GetFileType() + { + return PacketFileType.Directory; + } + + public long AddFile(IPacketFile file) + { + this.files.Add(file); + return this.files.LongCount(); + } + + public bool RemoveFile(IPacketFile file) + { + return this.files.Remove(file); + } + + public void Dispose() + { + foreach (IPacketFile file in this.EnumerableFiles()) + { + file.Dispose(); + } + } +} diff --git a/ProtocolLib/PacketIO/PacketFile.cs b/ProtocolLib/PacketIO/PacketFile.cs new file mode 100644 index 0000000..ad6791b --- /dev/null +++ b/ProtocolLib/PacketIO/PacketFile.cs @@ -0,0 +1,60 @@ +namespace PacketIO; + +public class PacketFile : IPacketFile +{ + private string fileName; + private Stream stream; + + public PacketFile(string fileName) + { + this.fileName = fileName; + this.stream = new MemoryStream(); + } + + public PacketFile(string fileName, Stream stream) + { + this.fileName = fileName; + this.stream = stream; + } + + public IEnumerable EnumerableFiles() + { + return []; + } + + public string GetFileName() + { + return this.fileName; + } + + public long GetFileSize() + { + return this.stream.Length; + } + + public PacketFileType GetFileType() + { + return PacketFileType.File; + } + + public async ValueTask ReadAsync(byte[] buffer, int offset, int count) + { + int read = await this.stream.ReadAsync(buffer, offset, count).ConfigureAwait(false); + return read; + } + + public async ValueTask WriteAsync(byte[] buffer, int offset, int count) + { + await this.stream.WriteAsync(buffer, offset, count).ConfigureAwait(false); + } + + public void Seek(long offset, SeekOrigin origin) + { + this.stream.Seek(offset, origin); + } + + public void Dispose() + { + this.stream.Dispose(); + } +} diff --git a/ProtocolLib/PacketIO/PacketFileType.cs b/ProtocolLib/PacketIO/PacketFileType.cs new file mode 100644 index 0000000..430871f --- /dev/null +++ b/ProtocolLib/PacketIO/PacketFileType.cs @@ -0,0 +1,11 @@ +namespace PacketIO; + +[Flags] +public enum PacketFileType : byte +{ + None = 0, + + File = 0x02 << 0, + + Directory = 0x02 << 1, +} diff --git a/ProtocolLib/PacketIO/TreePacketFile.cs b/ProtocolLib/PacketIO/TreePacketFile.cs new file mode 100644 index 0000000..025fb68 --- /dev/null +++ b/ProtocolLib/PacketIO/TreePacketFile.cs @@ -0,0 +1,150 @@ +using System.Text; + +namespace PacketIO; + +public class TreePacketFile +{ + public static async ValueTask WriteTreeFiles(Stream stream, params IPacketFile[] files) + { + foreach (IPacketFile file in files) + { + await WriteTreeFile(stream, file).ConfigureAwait(false); + } + } + + public static async ValueTask WriteTreeFile(Stream stream, IPacketFile file) + { + byte fileType = (byte) file.GetFileType(); + string fileName = file.GetFileName(); + long fileSize = file.GetFileSize(); + + byte[] fileNameBuffer = Encoding.UTF8.GetBytes(fileName); + + byte[] buffer = new byte[sizeof(byte) + sizeof(int) + sizeof(long) + fileNameBuffer.Length]; + buffer[0] = fileType; + Array.Copy(BitConverter.GetBytes(fileNameBuffer.Length), 0, buffer, 1, sizeof(int)); + Array.Copy(BitConverter.GetBytes(fileSize), 0, buffer, 5, sizeof(long)); + Array.Copy(fileNameBuffer, 0, buffer, 13, fileNameBuffer.Length); + + await stream.WriteAsync(buffer, 0, buffer.Length).ConfigureAwait(false); + + switch (file.GetFileType()) + { + case PacketFileType.Directory: + { + long fileCount = file.EnumerableFiles().LongCount(); + buffer = BitConverter.GetBytes(fileCount); + await stream.WriteAsync(buffer, 0, sizeof(long)); + + await WriteTreeFiles(stream, file.EnumerableFiles().ToArray()).ConfigureAwait(false); + break; + } + case PacketFileType.File: + { + int c; + buffer = new byte[8192]; + PacketFile packetFile = (PacketFile) file; + packetFile.Seek(0, SeekOrigin.Begin); + do + { + c = await packetFile.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); + + await stream.WriteAsync(buffer, 0, c).ConfigureAwait(false); + } while (c > 0); + break; + } + default: + throw new NotImplementedException(); + } + } + + public static async ValueTask> ReadTreeFiles(Stream stream) + { + List files = new List(); + + IPacketFile? file = null; + do + { + file = await ReadTreeFile(stream).ConfigureAwait(false); + if (file is object) + { + files.Add(file); + } + } while (file is object); + + return files; + } + + public static async ValueTask ReadTreeFile(Stream stream) + { + byte[] buffer = new byte[sizeof(byte) + sizeof(int) + sizeof(long)]; + int c = await stream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); + if (c <= 0) + { + return null; + } + + PacketFileType fileType = (PacketFileType) buffer[0]; + int fileNameBufferSize = BitConverter.ToInt32(buffer, 1); + long fileSize = BitConverter.ToInt64(buffer, 5); + + buffer = new byte[fileNameBufferSize]; + c = await stream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); + if (c <= 0) + { + return null; + } + + string fileName = Encoding.UTF8.GetString(buffer); + + switch (fileType) + { + case PacketFileType.Directory: + { + buffer = new byte[sizeof(long)]; + c = await stream.ReadAsync(buffer, 0, buffer.Length); + if (c <= 0) + { + return null; + } + + long fileCount = BitConverter.ToInt64(buffer, 0); + + PacketDirectory directory = new PacketDirectory(fileName); + for (long i = 0 ; i < fileCount ; i++) + { + IPacketFile? file = await ReadTreeFile(stream).ConfigureAwait(false); + if (file is null) + { + break; + } + + directory.AddFile(file); + } + + return directory; + } + case PacketFileType.File: + { + PacketFile file = new PacketFile(fileName); + + buffer = new byte[8192]; + long remainedSize = fileSize; + long readed = 0; + do + { + int leftSize = (int) Math.Min(buffer.Length, fileSize - readed); + c = await stream.ReadAsync(buffer, 0, leftSize).ConfigureAwait(false); + await file.WriteAsync(buffer, 0, c).ConfigureAwait(false); + remainedSize -= c; + readed += c; + } while (remainedSize > 0); + + file.Seek(0, SeekOrigin.Begin); + return file; + } + default: + throw new NotImplementedException(); + } + } +} diff --git a/ProtocolLib/Program.cs b/ProtocolLib/Program.cs new file mode 100644 index 0000000..f250e43 --- /dev/null +++ b/ProtocolLib/Program.cs @@ -0,0 +1,71 @@ +using PacketIO; + +namespace ProtocolLib; + +public class Program +{ + public static void Main(string[] args) + { + // Make tree file. + using (FileStream fs = new FileStream("tree.bin", FileMode.OpenOrCreate)) + { + DirectoryInfo directoryInfo = new DirectoryInfo("i"); + if (directoryInfo.Exists) + { + foreach (FileInfo fileInfo in directoryInfo.GetFiles()) + { + using (PacketFile file = new PacketFile(fileInfo.Name, fileInfo.Open(FileMode.Open, FileAccess.ReadWrite))) + { + TreePacketFile.WriteTreeFile(fs, file).ConfigureAwait(false).GetAwaiter().GetResult(); + } + } + } + } + + // Get files from tree file. + using (FileStream fs = new FileStream("tree.bin", FileMode.OpenOrCreate)) + { + IEnumerable files = TreePacketFile.ReadTreeFiles(fs).ConfigureAwait(false).GetAwaiter().GetResult(); + foreach (IPacketFile ifile in files) + { + PacketFile file = (PacketFile) ifile; + using (FileStream outStream = new FileStream(file.GetFileName(), FileMode.OpenOrCreate)) + { + byte[] buffer = new byte[8192]; + int c; + while ((c = file.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false).GetAwaiter().GetResult()) > 0) + { + outStream.Write(buffer, 0, c); + } + } + } + } + + + // Paket test. + using (MemoryStream ms = new MemoryStream()) + { + PacketStream packetStream = new PacketStream(ms); + + byte[] buffer = System.Text.Encoding.UTF8.GetBytes("Hello, World!!!"); + foreach (Packet packet in PacketFactory.CreatePacket(DataOperator.Connect, DataType.Binary, buffer)) + { + packetStream.WritePacket(packet).ConfigureAwait(false).GetAwaiter().GetResult(); + } + + ms.Seek(0, SeekOrigin.Begin); + + Packet? packet1 = null; + do + { + packet1 = packetStream.ReadPacket().ConfigureAwait(false).GetAwaiter().GetResult(); + if (packet1 is object) + { + Console.Write($"{System.Text.Encoding.UTF8.GetString(packet1.Payload)}"); + } + } while (packet1 is object); + + Console.WriteLine(); + } + } +} diff --git a/ProtocolLib/ProtocolLib.csproj b/ProtocolLib/ProtocolLib.csproj new file mode 100644 index 0000000..412d83d --- /dev/null +++ b/ProtocolLib/ProtocolLib.csproj @@ -0,0 +1,10 @@ + + + + Library + net8.0 + enable + enable + + + diff --git a/ProtocolLib/ProtocolLib/ByteConverter.cs b/ProtocolLib/ProtocolLib/ByteConverter.cs new file mode 100644 index 0000000..464a885 --- /dev/null +++ b/ProtocolLib/ProtocolLib/ByteConverter.cs @@ -0,0 +1,15 @@ +namespace ProtocolLib; + +internal static class ByteConverter +{ + public static byte[] Convert(bool value) + { + byte[] buffer = BitConverter.GetBytes(value); + if (BitConverter.IsLittleEndian) + { + Array.Reverse(buffer); + } + + return buffer; + } +} diff --git a/ProtocolLib/ProtocolLib/DataOperator.cs b/ProtocolLib/ProtocolLib/DataOperator.cs new file mode 100644 index 0000000..2f80d0f --- /dev/null +++ b/ProtocolLib/ProtocolLib/DataOperator.cs @@ -0,0 +1,12 @@ +namespace ProtocolLib; + +public enum DataOperator : byte +{ + None = 0x00, + + Connect = 0x02 << 0, + + Request = 0x02 << 1, + + Response = 0x02 << 2, +} diff --git a/ProtocolLib/ProtocolLib/DataType.cs b/ProtocolLib/ProtocolLib/DataType.cs new file mode 100644 index 0000000..e925b88 --- /dev/null +++ b/ProtocolLib/ProtocolLib/DataType.cs @@ -0,0 +1,8 @@ +namespace ProtocolLib; + +public enum DataType : byte +{ + None = 0x00, + + Binary = 0x02 << 0, +} diff --git a/ProtocolLib/ProtocolLib/PackeFactory.cs b/ProtocolLib/ProtocolLib/PackeFactory.cs new file mode 100644 index 0000000..b78e835 --- /dev/null +++ b/ProtocolLib/ProtocolLib/PackeFactory.cs @@ -0,0 +1,79 @@ +namespace ProtocolLib; + +public static class PacketFactory +{ + public static readonly int MAX_PAYLOAD_SIZE = 810; + + /// + /// 指定されたバイト配列からパケットを作成する。 + /// + /// オペレータの種類 + /// データの種類 + /// バイトデータ + /// + public static IEnumerable CreatePacket(DataOperator dataOperator, DataType dataType, byte[] data) + { + List packets = new List(); + + long packetNumber = 0; + int remainedSize = data.Length; + do + { + int packSize = Math.Min(MAX_PAYLOAD_SIZE, remainedSize); + byte[] payload = data.AsSpan(data.Length - remainedSize, packSize).ToArray(); + + Packet packet = new Packet() + { + Operator = dataOperator, + Type = dataType, + PacketNumber = packetNumber, + PayloadSize = packSize, + Payload = payload, + }; + + packets.Add(packet); + remainedSize -= packSize; + } while (remainedSize > 0); + + return packets; + } + + /// + /// 指定されたバイト配列からパケットを作成する。 + /// + /// オペレータの種類 + /// データの種類 + /// バイトデータ + /// + public static IEnumerable CreatePacket(DataOperator dataOperator, DataType dataType, Stream stream) + { + List packets = new List(); + + long packetNumber = 0; + long remainedSize = stream.Length; + do + { + int packSize = (int) Math.Min(MAX_PAYLOAD_SIZE, remainedSize); + byte[] payload = new byte[packSize]; + int c = stream.Read(payload, 0, payload.Length); + if (c <= 0) + { + throw new IOException(); + } + + Packet packet = new Packet() + { + Operator = dataOperator, + Type = dataType, + PacketNumber = packetNumber, + PayloadSize = packSize, + Payload = payload, + }; + + packets.Add(packet); + remainedSize -= packSize; + } while (remainedSize > 0); + + return packets; + } +} diff --git a/ProtocolLib/ProtocolLib/Packet.cs b/ProtocolLib/ProtocolLib/Packet.cs new file mode 100644 index 0000000..d77c210 --- /dev/null +++ b/ProtocolLib/ProtocolLib/Packet.cs @@ -0,0 +1,14 @@ +namespace ProtocolLib; + +public class Packet +{ + public DataOperator Operator { get; set; } + + public DataType Type { get; set; } + + public long PacketNumber { get; set;} + + public int PayloadSize { get; set; } + + public byte[] Payload { get; set; } +} diff --git a/ProtocolLib/ProtocolLib/PacketStream.cs b/ProtocolLib/ProtocolLib/PacketStream.cs new file mode 100644 index 0000000..3a992f4 --- /dev/null +++ b/ProtocolLib/ProtocolLib/PacketStream.cs @@ -0,0 +1,70 @@ +namespace ProtocolLib; + +public class PacketStream +{ + private Stream stream; + + public PacketStream(Stream stream) + { + this.stream = stream; + } + + public async ValueTask WritePacket(Packet packet) + { + byte[] buffer = this.PacketToBytes(packet); + await this.stream.WriteAsync(buffer, 0, buffer.Length).ConfigureAwait(false); + } + + public async ValueTask ReadPacket() + { + byte[] buffer = new byte[2 + sizeof(long) + sizeof(int)]; + int r = await this.stream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); + if (r <= 0) + { + return null; + } + + DataOperator dataOperator = (DataOperator) buffer[0]; + DataType dataType = (DataType) buffer[1]; + long packetNumber = BitConverter.ToInt64(buffer, 2); + int payloadSize = BitConverter.ToInt32(buffer, 10); + if (payloadSize < 0) + { + throw new IOException(); + } + + buffer = new byte[payloadSize]; + r = await this.stream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); + if (r <= 0) + { + throw new IOException(); + } + + Packet packet = new Packet() + { + Operator = dataOperator, + Type = dataType, + PacketNumber = packetNumber, + PayloadSize = payloadSize, + Payload = buffer, + }; + + return packet; + } + + private byte[] PacketToBytes(Packet packet) + { + byte[] buffer = new byte[2 + sizeof(long) + sizeof(int) + packet.PayloadSize]; + + buffer[0] = (byte) packet.Operator; + buffer[1] = (byte) packet.Type; + + byte[] temp = BitConverter.GetBytes(packet.PacketNumber); + Array.Copy(temp, 0, buffer, 2, temp.Length); + temp = BitConverter.GetBytes(packet.PayloadSize); + Array.Copy(temp, 0, buffer, 10, temp.Length); + Array.Copy(packet.Payload, 0, buffer, 14, packet.PayloadSize); + + return buffer; + } +}