diff --git a/frameworks/CSharp/touchsocket/Benchmarks.sln b/frameworks/CSharp/touchsocket/Benchmarks.sln index 8d41045e044..cf1ee5171f4 100644 --- a/frameworks/CSharp/touchsocket/Benchmarks.sln +++ b/frameworks/CSharp/touchsocket/Benchmarks.sln @@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TouchSocketWebApi31", "src\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TouchSocketHttpPlatform", "src\TouchSocketHttpPlatform\TouchSocketHttpPlatform.csproj", "{BC320C24-941D-2109-FBC8-92780569BBA4}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TouchSocketHttpSyncPlatform", "TouchSocketHttpSyncPlatform\TouchSocketHttpSyncPlatform.csproj", "{E4A6B13A-13D3-47D6-9696-533A1FA1B88F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +41,10 @@ Global {BC320C24-941D-2109-FBC8-92780569BBA4}.Debug|Any CPU.Build.0 = Debug|Any CPU {BC320C24-941D-2109-FBC8-92780569BBA4}.Release|Any CPU.ActiveCfg = Release|Any CPU {BC320C24-941D-2109-FBC8-92780569BBA4}.Release|Any CPU.Build.0 = Release|Any CPU + {E4A6B13A-13D3-47D6-9696-533A1FA1B88F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E4A6B13A-13D3-47D6-9696-533A1FA1B88F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E4A6B13A-13D3-47D6-9696-533A1FA1B88F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E4A6B13A-13D3-47D6-9696-533A1FA1B88F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/frameworks/CSharp/touchsocket/TouchSocketHttpSyncPlatform/DateHeader.cs b/frameworks/CSharp/touchsocket/TouchSocketHttpSyncPlatform/DateHeader.cs new file mode 100644 index 00000000000..3760507d1e7 --- /dev/null +++ b/frameworks/CSharp/touchsocket/TouchSocketHttpSyncPlatform/DateHeader.cs @@ -0,0 +1,71 @@ +// ------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// API首页:https://touchsocket.net/ +// 交流QQ群:234762506 +// 感谢您的下载和使用 +// ------------------------------------------------------------------------------ + +using System.Buffers.Text; +using System.Diagnostics; + +namespace TouchSocketHttpSyncPlatform; + +internal static class DateHeader +{ + private const int dateTimeRLength = 29; + private const int prefixLength = 6; + private const int suffixIndex = dateTimeRLength + prefixLength; + private const int suffixLength = 2; + + private static readonly Timer s_timer = new((s) => + { + SetDateValues(DateTimeOffset.UtcNow); + }, null, 1000, 1000); + + private static byte[] s_headerBytesMaster = new byte[prefixLength + dateTimeRLength + 2 * suffixLength]; + private static byte[] s_headerBytesScratch = new byte[prefixLength + dateTimeRLength + 2 * suffixLength]; + + static DateHeader() + { + var utf8 = "Date: "u8; + + utf8.CopyTo(s_headerBytesMaster); + utf8.CopyTo(s_headerBytesScratch); + s_headerBytesMaster[suffixIndex] = (byte)'\r'; + s_headerBytesMaster[suffixIndex + 1] = (byte)'\n'; + s_headerBytesMaster[suffixIndex + 2] = (byte)'\r'; + s_headerBytesMaster[suffixIndex + 3] = (byte)'\n'; + s_headerBytesScratch[suffixIndex] = (byte)'\r'; + s_headerBytesScratch[suffixIndex + 1] = (byte)'\n'; + s_headerBytesScratch[suffixIndex + 2] = (byte)'\r'; + s_headerBytesScratch[suffixIndex + 3] = (byte)'\n'; + + SetDateValues(DateTimeOffset.UtcNow); + SyncDateTimer(); + } + + public static ReadOnlySpan HeaderBytes => s_headerBytesMaster; + + public static void SyncDateTimer() + { + s_timer.Change(1000, 1000); + } + + private static void SetDateValues(DateTimeOffset value) + { + lock (s_headerBytesScratch) + { + if (!Utf8Formatter.TryFormat(value, s_headerBytesScratch.AsSpan(prefixLength), out var written, 'R')) + { + throw new Exception("date time format failed"); + } + Debug.Assert(written == dateTimeRLength); + (s_headerBytesScratch, s_headerBytesMaster) = (s_headerBytesMaster, s_headerBytesScratch); + } + } +} diff --git a/frameworks/CSharp/touchsocket/TouchSocketHttpSyncPlatform/NuGet.Config b/frameworks/CSharp/touchsocket/TouchSocketHttpSyncPlatform/NuGet.Config new file mode 100644 index 00000000000..f05d5867888 --- /dev/null +++ b/frameworks/CSharp/touchsocket/TouchSocketHttpSyncPlatform/NuGet.Config @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frameworks/CSharp/touchsocket/TouchSocketHttpSyncPlatform/Program.cs b/frameworks/CSharp/touchsocket/TouchSocketHttpSyncPlatform/Program.cs new file mode 100644 index 00000000000..48aeb2bd564 --- /dev/null +++ b/frameworks/CSharp/touchsocket/TouchSocketHttpSyncPlatform/Program.cs @@ -0,0 +1,354 @@ +// ------------------------------------------------------------------------------ +// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 +// CSDN博客:https://blog.csdn.net/qq_40374647 +// 哔哩哔哩视频:https://space.bilibili.com/94253567 +// Gitee源代码仓库:https://gitee.com/RRQM_Home +// Github源代码仓库:https://github.com/RRQM +// API首页:https://touchsocket.net/ +// 交流QQ群:234762506 +// 感谢您的下载和使用 +// ---------------------------------------------------------------------------- + +using System.Buffers; +using System.Net; +using System.Net.Sockets; +using System.Runtime.CompilerServices; +using TouchSocket.Core; + +namespace TouchSocketHttpSyncPlatform; + +internal enum RouteType +{ + Unknown, + Plaintext, + Json +} + +internal class Program +{ + private static Socket? _listenerSocket; + private static bool _isRunning; + + private static ReadOnlySpan Json => "/json"u8; + private static ReadOnlySpan Plaintext => "/plaintext"u8; + private static ReadOnlySpan PlainTextBody => "Hello, World!"u8; + private static ReadOnlySpan JsonBody => "{\"message\":\"Hello, World!\"}"u8; + + private static ReadOnlySpan PlaintextPreamble => + "HTTP/1.1 200 OK\r\n"u8 + + "Server: T\r\n"u8 + + "Content-Type: text/plain\r\n"u8 + + "Content-Length: 13\r\n"u8; + + private static ReadOnlySpan JsonPreamble => + "HTTP/1.1 200 OK\r\n"u8 + + "Server: T\r\n"u8 + + "Content-Type: application/json\r\n"u8 + + "Content-Length: 27\r\n"u8; + + static void Main(string[] args) + { + var port = 8080; + _listenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + _listenerSocket.Bind(new IPEndPoint(IPAddress.Parse("0.0.0.0"), port)); + _listenerSocket.Listen(1000); + _isRunning = true; + + Console.WriteLine($"HTTP服务器已启动,监听端口: {port}"); + Console.WriteLine("等待客户端连接..."); + + var acceptThread = new Thread(AcceptClients) + { + IsBackground = false + }; + acceptThread.Start(); + + DateHeader.SyncDateTimer(); + + + while (true) + { + Console.ReadLine(); + } + } + + private static void AcceptClients() + { + while (_isRunning) + { + try + { + var clientSocket = _listenerSocket!.Accept(); + + var clientThread = new Thread(HandleClient) + { + IsBackground = true + }; + clientThread.Start(clientSocket); + } + catch (SocketException) + { + break; + } + } + } + + private static void HandleClient(object? obj) + { + var clientSocket = (Socket)obj!; + clientSocket.NoDelay = true; + var remoteEndPoint = clientSocket.RemoteEndPoint?.ToString(); + + Console.WriteLine($"客户端已连接: {remoteEndPoint}"); + + try + { + var pipeReceive = new SegmentedPipe(); + var pipeSend = new SegmentedPipe(); + var reader = pipeReceive.Reader; + var writer = pipeReceive.Writer; + + while (_isRunning && clientSocket.Connected) + { + var span = writer.GetSpan(1024 * 64); + var bytesRead = clientSocket.Receive(span, SocketFlags.None); + + if (bytesRead == 0) + { + break; + } + + writer.Advance(bytesRead); + + ProcessData(reader,pipeSend.Writer); + + var sendResult = pipeSend.Reader.Read(); + var sendBuffer = sendResult.Buffer; + if (sendBuffer.Length > 0) + { + foreach (var segment in sendBuffer) + { + clientSocket.Send(segment.Span, SocketFlags.None); + } + pipeSend.Reader.AdvanceTo(sendBuffer.End); + } + } + } + catch (Exception ex) + { + Console.WriteLine($"处理客户端 {remoteEndPoint} 时发生错误: {ex.Message}"); + } + finally + { + try + { + clientSocket.Shutdown(SocketShutdown.Both); + } + catch { } + + clientSocket.Close(); + Console.WriteLine($"客户端已断开: {remoteEndPoint}"); + } + } + + private static void ProcessData(SegmentedPipeReader reader, SegmentedPipeWriter writer) + { + var result = reader.Read(); + var buffer = result.Buffer; + + var totalConsumed = ProcessRequests(buffer, writer, out var responseCount); + + if (totalConsumed > 0) + { + reader.AdvanceTo(buffer.GetPosition(totalConsumed)); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static long ProcessRequests(ReadOnlySequence buffer, SegmentedPipeWriter writer, out int responseCount) + { + var seqReader = new SequenceReader(buffer); + var totalConsumed = 0L; + responseCount = 0; + + while (!seqReader.End) + { + var startConsumed = seqReader.Consumed; + + if (!TryReadLine(ref seqReader, out var requestLineLength)) + { + break; + } + + var requestLineConsumed = requestLineLength + 2; + + var headersStartConsumed = seqReader.Consumed; + bool headersComplete = false; + while (!seqReader.End) + { + if (!TryReadLine(ref seqReader, out var headerLength)) + { + var rewind = seqReader.Consumed - headersStartConsumed; + if (rewind > 0) + { + seqReader.Rewind(rewind); + } + headersComplete = false; + break; + } + + if (headerLength == 0) + { + headersComplete = true; + break; + } + } + + if (!headersComplete) + { + break; + } + + var routeType = ParseUrlFast(buffer.Slice(startConsumed), requestLineConsumed); + + var consumed = seqReader.Consumed - startConsumed; + totalConsumed += consumed; + + WriteResponse(writer, routeType); + responseCount++; + } + + return totalConsumed; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WriteResponse(SegmentedPipeWriter writer, RouteType routeType) + { + switch (routeType) + { + case RouteType.Plaintext: + writer.Write(PlaintextPreamble); + writer.Write(DateHeader.HeaderBytes); + writer.Write(PlainTextBody); + break; + case RouteType.Json: + writer.Write(JsonPreamble); + writer.Write(DateHeader.HeaderBytes); + writer.Write(JsonBody); + break; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool TryReadLine(ref SequenceReader reader, out long length) + { + var start = reader.Consumed; + + while (!reader.End) + { + if (reader.TryRead(out var b)) + { + if (b == '\r') + { + if (!reader.TryPeek(out var next)) + { + reader.Rewind(1); + length = 0; + return false; + } + if (next == '\n') + { + reader.Advance(1); + length = reader.Consumed - start - 2; + return true; + } + } + } + } + + length = 0; + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static RouteType ParseUrlFast(ReadOnlySequence sequence, long requestLineLength) + { + var reader = new SequenceReader(sequence); + + var spaceCount = 0; + var urlStart = 0L; + var urlEnd = 0L; + + while (reader.Consumed < requestLineLength && !reader.End) + { + if (reader.TryRead(out var b)) + { + if (b == ' ') + { + spaceCount++; + if (spaceCount == 1) + { + urlStart = reader.Consumed; + } + else if (spaceCount == 2) + { + urlEnd = reader.Consumed - 1; + break; + } + } + } + } + + if (spaceCount < 2) + { + return RouteType.Unknown; + } + + var urlLength = urlEnd - urlStart; + + var startPos = sequence.GetPosition(urlStart); + var urlSlice = sequence.Slice(startPos, urlLength); + + if (urlLength == Plaintext.Length) + { + if (urlSlice.IsSingleSegment) + { + if (urlSlice.FirstSpan.SequenceEqual(Plaintext)) + { + return RouteType.Plaintext; + } + } + else + { + Span tmp = stackalloc byte[(int)urlLength]; + urlSlice.CopyTo(tmp); + if (tmp.SequenceEqual(Plaintext)) + { + return RouteType.Plaintext; + } + } + } + else if (urlLength == Json.Length) + { + if (urlSlice.IsSingleSegment) + { + if (urlSlice.FirstSpan.SequenceEqual(Json)) + { + return RouteType.Json; + } + } + else + { + Span tmp = stackalloc byte[(int)urlLength]; + urlSlice.CopyTo(tmp); + if (tmp.SequenceEqual(Json)) + { + return RouteType.Json; + } + } + } + + return RouteType.Unknown; + } +} diff --git a/frameworks/CSharp/touchsocket/TouchSocketHttpSyncPlatform/TouchSocketHttpSyncPlatform.csproj b/frameworks/CSharp/touchsocket/TouchSocketHttpSyncPlatform/TouchSocketHttpSyncPlatform.csproj new file mode 100644 index 00000000000..2bcc738f843 --- /dev/null +++ b/frameworks/CSharp/touchsocket/TouchSocketHttpSyncPlatform/TouchSocketHttpSyncPlatform.csproj @@ -0,0 +1,12 @@ + + + + Exe + net10.0 + enable + enable + + + + + diff --git a/frameworks/CSharp/touchsocket/benchmark_config.json b/frameworks/CSharp/touchsocket/benchmark_config.json index 2a6d6c27e98..a4cb7e90cde 100644 --- a/frameworks/CSharp/touchsocket/benchmark_config.json +++ b/frameworks/CSharp/touchsocket/benchmark_config.json @@ -96,6 +96,25 @@ "display_name": "touchsocket.http [Platform]", "notes": "High-performance custom pipeline-based implementation variant.", "versus": "aspnetcore" + }, + "httpsyncplatform": { + "plaintext_url": "/plaintext", + "json_url": "/json", + "port": 8080, + "approach": "Realistic", + "classification": "Platform", + "database": "Postgres", + "framework": "touchsocket.httpsyncplatform", + "language": "C#", + "orm": "Micro", + "platform": ".NET", + "flavor": "CoreCLR", + "webserver": "touchsocket", + "os": "Linux", + "database_os": "Linux", + "display_name": "touchsocket.http [Sync Platform]", + "notes": "High-performance synchronous platform-based implementation variant.", + "versus": "aspnetcore" } } ] diff --git a/frameworks/CSharp/touchsocket/config.toml b/frameworks/CSharp/touchsocket/config.toml index 0d06cb4e331..ce6fedfcab8 100644 --- a/frameworks/CSharp/touchsocket/config.toml +++ b/frameworks/CSharp/touchsocket/config.toml @@ -65,3 +65,16 @@ orm = "Raw" platform = ".NET" webserver = "touchsocket.httpplatform" versus = "aspcore" + +[httpsyncplatform] +urls.plaintext = "/plaintext" +urls.json = "/json" +approach = "Realistic" +classification = "Micro" +database = "Postgres" +database_os = "Linux" +os = "Linux" +orm = "Raw" +platform = ".NET" +webserver = "touchsocket.httpsyncplatform" +versus = "aspcore" diff --git a/frameworks/CSharp/touchsocket/src/TouchSocketHttp/TouchSocketHttp.csproj b/frameworks/CSharp/touchsocket/src/TouchSocketHttp/TouchSocketHttp.csproj index c694366fc8d..3b328508752 100644 --- a/frameworks/CSharp/touchsocket/src/TouchSocketHttp/TouchSocketHttp.csproj +++ b/frameworks/CSharp/touchsocket/src/TouchSocketHttp/TouchSocketHttp.csproj @@ -8,7 +8,7 @@ - + diff --git a/frameworks/CSharp/touchsocket/src/TouchSocketHttpPlatform/TouchSocketHttpPlatform.csproj b/frameworks/CSharp/touchsocket/src/TouchSocketHttpPlatform/TouchSocketHttpPlatform.csproj index 25feeea4984..92598350b15 100644 --- a/frameworks/CSharp/touchsocket/src/TouchSocketHttpPlatform/TouchSocketHttpPlatform.csproj +++ b/frameworks/CSharp/touchsocket/src/TouchSocketHttpPlatform/TouchSocketHttpPlatform.csproj @@ -8,6 +8,6 @@ - + diff --git a/frameworks/CSharp/touchsocket/src/TouchSocketWebApi/TouchSocketWebApi.csproj b/frameworks/CSharp/touchsocket/src/TouchSocketWebApi/TouchSocketWebApi.csproj index 3b3d56852ce..0afe36f0b77 100644 --- a/frameworks/CSharp/touchsocket/src/TouchSocketWebApi/TouchSocketWebApi.csproj +++ b/frameworks/CSharp/touchsocket/src/TouchSocketWebApi/TouchSocketWebApi.csproj @@ -9,7 +9,7 @@ - - + + diff --git a/frameworks/CSharp/touchsocket/touchsocket-httpsyncplatform.dockerfile b/frameworks/CSharp/touchsocket/touchsocket-httpsyncplatform.dockerfile new file mode 100644 index 00000000000..3d9c46ce796 --- /dev/null +++ b/frameworks/CSharp/touchsocket/touchsocket-httpsyncplatform.dockerfile @@ -0,0 +1,13 @@ +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build +WORKDIR /app +COPY TouchSocketHttpSyncPlatform . +RUN dotnet publish -c Release -o out + +FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime + +WORKDIR /app +COPY --from=build /app/out ./ + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "TouchSocketHttpSyncPlatform.dll"]