From 172b0b7da77e60b897057427d29bd27a704e8191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=99=BA=E8=83=BD=E5=A4=A7=E7=9F=B3=E5=A4=B4?= Date: Fri, 16 Aug 2024 01:50:09 +0800 Subject: [PATCH 01/18] =?UTF-8?q?=E4=BC=98=E5=8C=96UriInfo=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E8=B7=AF=E5=BE=84=E5=92=8C=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E6=8B=86=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NewLife.Core/Web/UriInfo.cs | 76 ++++++++++++--- XUnitTest.Core/Web/UriInfoTests.cs | 151 ++++++++++++++++++++++++++--- 2 files changed, 200 insertions(+), 27 deletions(-) diff --git a/NewLife.Core/Web/UriInfo.cs b/NewLife.Core/Web/UriInfo.cs index 9bf126ce0..4aa95e5bc 100644 --- a/NewLife.Core/Web/UriInfo.cs +++ b/NewLife.Core/Web/UriInfo.cs @@ -6,25 +6,32 @@ public class UriInfo /// 协议 public String? Scheme { get; set; } - /// 协议 - public String Host { get; set; } = null!; + /// 主机 + public String? Host { get; set; } - /// 协议 + /// 端口 public Int32 Port { get; set; } - /// 协议 - public String? PathAndQuery { get; set; } + /// 路径 + public String? AbsolutePath { get; set; } + + /// 查询 + public String? Query { get; set; } + + /// 路径与查询 + public String? PathAndQuery => AbsolutePath + Query; /// 实例化 /// public UriInfo(String value) => Parse(value); /// 主机与端口。省略默认端口 - public String Authority + public String? Authority { get { if (Port == 0) return Host; + if (Host.IsNullOrEmpty()) return Host; if (Scheme.EqualIgnoreCase("http", "ws")) return Port == 80 ? Host : $"{Host}:{Port}"; @@ -51,23 +58,55 @@ public void Parse(String value) else p = 0; + // 第二步找到/,它左边是主机和端口,右边是路径和查询。如果没有/,则整个字符串都是主机和端口 var p2 = value.IndexOf('/', p); - if (p2 > 0) + if (p2 >= 0) + { + ParseHost(value[p..p2]); + ParsePath(value, p2); + } + else + { + p2 = value.IndexOf('?', p); + if (p2 >= 0) + { + ParseHost(value[p..p2]); + Query = value[p2..]; + } + else + { + Host = value[p..]; + } + } + + if (AbsolutePath.IsNullOrEmpty()) AbsolutePath = "/"; + } + + private void ParsePath(String value, Int32 p) + { + // 第二步找到/,它左边是主机和端口,右边是路径和查询。如果没有/,则整个字符串都是主机和端口 + var p2 = value.IndexOf('?', p); + if (p2 >= 0) { - PathAndQuery = value[p2..]; - value = value[p..p2]; + AbsolutePath = value[p..p2]; + Query = value[p2..]; } else - value = value[p..]; + { + AbsolutePath = value[p..]; + } + } + private void ParseHost(String value) + { // 拆分主机和端口,注意IPv6地址 - p2 = value.LastIndexOf(':'); + var p2 = value.LastIndexOf(':'); if (p2 > 0) { Host = value[..p2]; Port = value[(p2 + 1)..].ToInt(); } - else + else if (!value.IsNullOrEmpty()) { Host = value; } @@ -75,5 +114,16 @@ public void Parse(String value) /// 已重载。 /// - public override String ToString() => $"{Scheme}://{Authority}{PathAndQuery}"; + public override String? ToString() + { + var authority = Authority; + if (Scheme.IsNullOrEmpty()) + { + if (authority.IsNullOrEmpty()) return PathAndQuery; + + return $"{authority}{PathAndQuery}"; + } + + return $"{Scheme}://{authority}{PathAndQuery}"; + } } diff --git a/XUnitTest.Core/Web/UriInfoTests.cs b/XUnitTest.Core/Web/UriInfoTests.cs index 02fb269d4..f468e82a2 100644 --- a/XUnitTest.Core/Web/UriInfoTests.cs +++ b/XUnitTest.Core/Web/UriInfoTests.cs @@ -11,19 +11,21 @@ public class UriInfoTests [InlineData("http://localhost:8080/cube/info", "http", "localhost", 8080, "/cube/info")] [InlineData("http://localhost:8080/", "http", "localhost", 8080, "/")] [InlineData("Http://localhost/", "Http", "localhost", 0, "/")] - [InlineData("Http://localhost", "Http", "localhost", 0, null)] + [InlineData("Http://localhost", "Http", "localhost", 0, "/")] [InlineData("https://localhost:8080/cube/info", "https", "localhost", 8080, "/cube/info")] [InlineData("https://localhost:8080/", "https", "localhost", 8080, "/")] [InlineData("Https://localhost/", "Https", "localhost", 0, "/")] - [InlineData("Https://localhost", "Https", "localhost", 0, null)] + [InlineData("Https://localhost", "Https", "localhost", 0, "/")] [InlineData("wss://localhost:8080/cube/info", "wss", "localhost", 8080, "/cube/info")] [InlineData("wss://localhost:8080/", "wss", "localhost", 8080, "/")] [InlineData("wss://localhost/", "wss", "localhost", 0, "/")] - [InlineData("wss://localhost", "wss", "localhost", 0, null)] + [InlineData("wss://localhost", "wss", "localhost", 0, "/")] [InlineData("localhost:8080/cube/info", null, "localhost", 8080, "/cube/info")] [InlineData("localhost:8080/", null, "localhost", 8080, "/")] [InlineData("localhost/", null, "localhost", 0, "/")] - [InlineData("localhost", null, "localhost", 0, null)] + [InlineData("localhost", null, "localhost", 0, "/")] + [InlineData("/dotnet-sdk-6.0.100-preview.6.21355.2-win-x64.exe", null, null, 0, "/dotnet-sdk-6.0.100-preview.6.21355.2-win-x64.exe")] + [InlineData("dotNet/dotnet-sdk-6.0.100-preview.6.21355.2-win-x64.exe", null, "dotNet", 0, "/dotnet-sdk-6.0.100-preview.6.21355.2-win-x64.exe")] public void Parse(String url, String schema, String host, Int32 port, String path) { { @@ -35,21 +37,51 @@ public void Parse(String url, String schema, String host, Int32 port, String pat if (port == 0) { - Assert.Equal($"{host}", uri.Authority); - Assert.Equal($"{schema}://{host}{path}", uri.ToString()); + if (host.IsNullOrEmpty()) + { + Assert.Null(uri.Authority); + if (schema.IsNullOrEmpty()) + Assert.Equal(path, uri.ToString()); + else + Assert.Equal($"{schema}://{path}", uri.ToString()); + } + else + { + Assert.Equal(host, uri.Authority); + if (schema.IsNullOrEmpty()) + Assert.Equal($"{host}{path}", uri.ToString()); + else + Assert.Equal($"{schema}://{host}{path}", uri.ToString()); + } } else { - Assert.Equal($"{host}:{port}", uri.Authority); - Assert.Equal($"{schema}://{host}:{port}{path}", uri.ToString()); + if (host.IsNullOrEmpty()) + { + Assert.Equal($"{host}:{port}", uri.Authority); + if (schema.IsNullOrEmpty()) + Assert.Equal(path, uri.ToString()); + else + Assert.Equal($"{schema}://{host}:{port}{path}", uri.ToString()); + } + else + { + Assert.Equal($"{host}:{port}", uri.Authority); + if (schema.IsNullOrEmpty()) + Assert.Equal($"{host}:{port}{path}", uri.ToString()); + else + Assert.Equal($"{schema}://{host}:{port}{path}", uri.ToString()); + } } } + if (!url.StartsWith("/")) { if (!url.StartsWithIgnoreCase("http://", "https://", "ws://", "wss://")) url = "http://" + url; + if (schema.IsNullOrEmpty()) schema = "http"; if (path.IsNullOrEmpty()) path = "/"; - schema = schema?.ToLower(); + //schema = schema?.ToLower(); if (port == 0 && schema.EqualIgnoreCase("http", "ws")) port = 80; @@ -57,22 +89,113 @@ public void Parse(String url, String schema, String host, Int32 port, String pat port = 443; var uri = new Uri(url); + Assert.Equal(schema, uri.Scheme, true); + Assert.Equal(host, uri.Host, true); + Assert.Equal(port, uri.Port); + Assert.Equal(path, uri.PathAndQuery); + + if (port == 0 || + port == 80 && schema.EqualIgnoreCase("http", "ws") || + port == 443 && schema.EqualIgnoreCase("https", "wss")) + { + Assert.Equal($"{host}", uri.Authority, true); + Assert.Equal($"{schema}://{host}{path}", uri.ToString(), true); + } + else + { + Assert.Equal($"{host}:{port}", uri.Authority, true); + Assert.Equal($"{schema}://{host}:{port}{path}", uri.ToString(), true); + } + } + } + + [Theory] + [InlineData("http://localhost:8080/cube/info?name=newlife", "http", "localhost", 8080, "/cube/info", "?name=newlife")] + [InlineData("http://localhost:8080/?name=newlife", "http", "localhost", 8080, "/", "?name=newlife")] + [InlineData("http://localhost:8080?name=newlife", "http", "localhost", 8080, "/", "?name=newlife")] + [InlineData("Http://localhost/?name=newlife", "Http", "localhost", 0, "/", "?name=newlife")] + [InlineData("Http://localhost?name=newlife", "Http", "localhost", 0, "/", "?name=newlife")] + public void Parse2(String url, String schema, String host, Int32 port, String path, String query) + { + { + var uri = new UriInfo(url); Assert.Equal(schema, uri.Scheme); Assert.Equal(host, uri.Host); Assert.Equal(port, uri.Port); - Assert.Equal(path, uri.PathAndQuery); + Assert.Equal(path, uri.AbsolutePath); + Assert.Equal(query, uri.Query); + + if (port == 0) + { + if (host.IsNullOrEmpty()) + { + Assert.Null(uri.Authority); + if (schema.IsNullOrEmpty()) + Assert.Equal($"{path}{query}", uri.ToString()); + else + Assert.Equal($"{schema}://{path}{query}", uri.ToString()); + } + else + { + Assert.Equal(host, uri.Authority); + if (schema.IsNullOrEmpty()) + Assert.Equal($"{host}{path}{query}", uri.ToString()); + else + Assert.Equal($"{schema}://{host}{path}{query}", uri.ToString()); + } + } + else + { + if (host.IsNullOrEmpty()) + { + Assert.Equal($"{host}:{port}", uri.Authority); + if (schema.IsNullOrEmpty()) + Assert.Equal($"{path}{query}", uri.ToString()); + else + Assert.Equal($"{schema}://{host}:{port}{path}{query}", uri.ToString()); + } + else + { + Assert.Equal($"{host}:{port}", uri.Authority); + if (schema.IsNullOrEmpty()) + Assert.Equal($"{host}:{port}{path}{query}", uri.ToString()); + else + Assert.Equal($"{schema}://{host}:{port}{path}{query}", uri.ToString()); + } + } + } + if (!url.StartsWith("/")) + { + if (!url.StartsWithIgnoreCase("http://", "https://", "ws://", "wss://")) + url = "http://" + url; + + if (schema.IsNullOrEmpty()) schema = "http"; + if (path.IsNullOrEmpty()) path = "/"; + //schema = schema?.ToLower(); + + if (port == 0 && schema.EqualIgnoreCase("http", "ws")) + port = 80; + else if (port == 0 && schema.EqualIgnoreCase("https", "wss")) + port = 443; + + var uri = new Uri(url); + Assert.Equal(schema, uri.Scheme, true); + Assert.Equal(host, uri.Host, true); + Assert.Equal(port, uri.Port); + Assert.Equal(path, uri.AbsolutePath); + Assert.Equal(query, uri.Query); if (port == 0 || port == 80 && schema.EqualIgnoreCase("http", "ws") || port == 443 && schema.EqualIgnoreCase("https", "wss")) { - Assert.Equal($"{host}", uri.Authority); - Assert.Equal($"{schema}://{host}{path}", uri.ToString()); + Assert.Equal($"{host}", uri.Authority, true); + Assert.Equal($"{schema}://{host}{path}{query}", uri.ToString(), true); } else { - Assert.Equal($"{host}:{port}", uri.Authority); - Assert.Equal($"{schema}://{host}:{port}{path}", uri.ToString()); + Assert.Equal($"{host}:{port}", uri.Authority, true); + Assert.Equal($"{schema}://{host}:{port}{path}{query}", uri.ToString(), true); } } } From 5e181d7c570b3f81aecfb43a143a1c8cba2cd165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E7=9F=B3=E5=A4=B4?= Date: Fri, 16 Aug 2024 09:28:38 +0800 Subject: [PATCH 02/18] =?UTF-8?q?UriInfo=E7=9A=84Query=E9=83=A8=E5=88=86?= =?UTF-8?q?=E5=8F=AF=E8=83=BD=E6=B2=A1=E6=9C=89=E9=97=AE=E5=8F=B7=EF=BC=8C?= =?UTF-8?q?=E8=BD=AC=E5=AD=97=E7=AC=A6=E4=B8=B2=E6=97=B6=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E8=A1=A5=E4=B8=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NewLife.Core/Web/UriInfo.cs | 12 +++++++++++- NewLife.Core/Web/WebClientX.cs | 24 ++++++++++++------------ XUnitTest.Core/Web/UriInfoTests.cs | 12 ++++++++++++ 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/NewLife.Core/Web/UriInfo.cs b/NewLife.Core/Web/UriInfo.cs index 4aa95e5bc..f1565f90f 100644 --- a/NewLife.Core/Web/UriInfo.cs +++ b/NewLife.Core/Web/UriInfo.cs @@ -19,7 +19,17 @@ public class UriInfo public String? Query { get; set; } /// 路径与查询 - public String? PathAndQuery => AbsolutePath + Query; + public String? PathAndQuery + { + get + { + if (Query.IsNullOrEmpty()) return AbsolutePath; + + if (Query[0] == '?') return AbsolutePath + Query; + + return $"{AbsolutePath}?{Query}"; + } + } /// 实例化 /// diff --git a/NewLife.Core/Web/WebClientX.cs b/NewLife.Core/Web/WebClientX.cs index 961bf3f57..cd794b3c6 100644 --- a/NewLife.Core/Web/WebClientX.cs +++ b/NewLife.Core/Web/WebClientX.cs @@ -15,7 +15,7 @@ public class WebClientX : DisposeBase { #region 属性 /// 超时,默认30000毫秒 - public Int32 Timeout { get; set; } = 30000; + public Int32 Timeout { get; set; } = 30_000; /// 验证密钥。适配CDN的URL验证,在url后面增加auth_key={timestamp-rand-uid-md5hash} public String? AuthKey { get; set; } @@ -125,37 +125,37 @@ public virtual async Task SendAsync(String address, HttpContent? co return rs.Content; } - String CheckAuth(String address) + String CheckAuth(String url) { // 增加CDN的URL验证 - if (!AuthKey.IsNullOrEmpty() && !address.Contains("auth_key=")) + if (!AuthKey.IsNullOrEmpty() && !url.Contains("auth_key=")) { // http://DomainName/Filename?auth_key={-rand-uid-} - var uri = new Uri(address); - var url = uri.PathAndQuery; + var uri = new Uri(url); + var path = uri.AbsolutePath; // 如果地址中有中文,需要编码 var encoding = Encoding.UTF8; - if (encoding.GetByteCount(url) != url.Length) + if (encoding.GetByteCount(path) != path.Length) { - var us = url.Split('/'); + var us = path.Split('/'); for (var i = 0; i < us.Length; i++) { us[i] = HttpUtility.UrlEncode(us[i]); } - url = String.Join("/", us); + path = String.Join("/", us); } var time = DateTime.UtcNow.ToInt(); var rand = Rand.Next(100_000, 1_000_000); - var hash = $"{url}-{time}-{rand}-0-{AuthKey}".MD5().ToLower(); + var hash = $"{path}-{time}-{rand}-0-{AuthKey}".MD5().ToLower(); var key = $"{time}-{rand}-0-{hash}"; - address += address.Contains("?") ? "&" : "?"; - address += "auth_key=" + key; + url += url.Contains('?') ? "&" : "?"; + url += "auth_key=" + key; } - return address; + return url; } /// 下载字符串 diff --git a/XUnitTest.Core/Web/UriInfoTests.cs b/XUnitTest.Core/Web/UriInfoTests.cs index f468e82a2..486b20fff 100644 --- a/XUnitTest.Core/Web/UriInfoTests.cs +++ b/XUnitTest.Core/Web/UriInfoTests.cs @@ -199,4 +199,16 @@ public void Parse2(String url, String schema, String host, Int32 port, String pa } } } + + [Fact] + public void Test() + { + var url = "http://localhost:8080/cube/info"; + var uri = new UriInfo(url); + + // 故意没有问号开头,转字符串后自己补上 + uri.Query = "name=newlife"; + + Assert.Equal(url + "?" + uri.Query, uri.ToString()); + } } From c8cbc9690de277b3b3bef37a2b409b69b54e2a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E7=9F=B3=E5=A4=B4?= Date: Fri, 16 Aug 2024 17:49:47 +0800 Subject: [PATCH 03/18] =?UTF-8?q?[fix]=E4=BF=AE=E6=AD=A3TinyHttpClient?= =?UTF-8?q?=E5=A4=84=E7=90=86chunk=E5=93=8D=E5=BA=94=E6=97=B6=EF=BC=8C?= =?UTF-8?q?=E5=8D=95=E4=B8=AA=E6=95=B0=E6=8D=AE=E8=A1=A8=E6=9C=89=E5=A4=9A?= =?UTF-8?q?=E4=B8=AAchunk=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NewLife.Core/Http/TinyHttpClient.cs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/NewLife.Core/Http/TinyHttpClient.cs b/NewLife.Core/Http/TinyHttpClient.cs index 91cfc5a61..0d12d9a19 100644 --- a/NewLife.Core/Http/TinyHttpClient.cs +++ b/NewLife.Core/Http/TinyHttpClient.cs @@ -259,12 +259,23 @@ protected virtual async Task ReadChunkAsync(Packet body) { pk = await SendDataAsync(null, null).ConfigureAwait(false); - // 结尾的间断符号(如换行或00)。这里有可能一个数据包里面同时返回多个分片,暂时不支持 - if (total + pk.Total > len) pk = pk.Slice(0, len - total); - - last.Append(pk); - last = pk; - total += pk.Total; + // 结尾的间断符号(如换行或00)。这里有可能一个数据包里面同时返回多个分片 + var count = len - total; + if (count <= pk.Total) + { + var pk2 = pk.Slice(0, count); + + last.Append(pk2); + last = pk2; + total += pk2.Total; + + // 如果还有剩余,作为下一个chunk + count += 2; + if (count < pk.Total) + pk = pk.Slice(count); + else + pk = null; + } } // 还有粘包数据,继续分析 From b63c971bb77ea0afaed8a13b24b5361535ab7151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E7=9F=B3=E5=A4=B4?= Date: Fri, 16 Aug 2024 18:02:43 +0800 Subject: [PATCH 04/18] fixNull --- NewLife.Core/Http/TinyHttpClient.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/NewLife.Core/Http/TinyHttpClient.cs b/NewLife.Core/Http/TinyHttpClient.cs index 0d12d9a19..4b912cf9b 100644 --- a/NewLife.Core/Http/TinyHttpClient.cs +++ b/NewLife.Core/Http/TinyHttpClient.cs @@ -257,17 +257,21 @@ protected virtual async Task ReadChunkAsync(Packet body) var total = chunk.Total; while (total < len) { - pk = await SendDataAsync(null, null).ConfigureAwait(false); + var pk2 = await SendDataAsync(null, null).ConfigureAwait(false); + if (pk != null) + pk.Append(pk2); + else + pk = pk2; // 结尾的间断符号(如换行或00)。这里有可能一个数据包里面同时返回多个分片 var count = len - total; - if (count <= pk.Total) + if (pk != null && count <= pk.Total) { - var pk2 = pk.Slice(0, count); + var pk3 = pk.Slice(0, count); - last.Append(pk2); - last = pk2; - total += pk2.Total; + last.Append(pk3); + last = pk3; + total += pk3.Total; // 如果还有剩余,作为下一个chunk count += 2; From 904bdb005bef217734b04b88bacd65e3f35ab30f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=B3=E5=A4=B4?= Date: Sun, 18 Aug 2024 09:32:35 +0800 Subject: [PATCH 05/18] =?UTF-8?q?[feat]=E6=96=B0=E5=A2=9ERuntime.FreeMemor?= =?UTF-8?q?y=EF=BC=8C=E9=87=8A=E6=94=BE=E6=9C=AC=E8=BF=9B=E7=A8=8B?= =?UTF-8?q?=E6=88=96=E7=9B=AE=E6=A0=87=E8=BF=9B=E7=A8=8B=E5=86=85=E5=AD=98?= =?UTF-8?q?=EF=BC=8C=E6=9C=AC=E8=BF=9B=E7=A8=8B=E8=BF=98=E8=83=BD=E6=89=A7?= =?UTF-8?q?=E8=A1=8CGC=E5=9B=9E=E6=94=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NewLife.Core/Common/Runtime.cs | 90 ++++++++++++++++++++++++++++++++++ Test/Program.cs | 12 ++++- 2 files changed, 100 insertions(+), 2 deletions(-) diff --git a/NewLife.Core/Common/Runtime.cs b/NewLife.Core/Common/Runtime.cs index efa1934d4..a93494061 100644 --- a/NewLife.Core/Common/Runtime.cs +++ b/NewLife.Core/Common/Runtime.cs @@ -1,6 +1,8 @@ using System.Collections; using System.Diagnostics; +using System.Runtime; using System.Runtime.InteropServices; +using NewLife.Log; namespace NewLife; @@ -112,6 +114,15 @@ public static Int64 TickCount64 } #endif + private static Int32 _ProcessId; +#if NET5_0_OR_GREATER + /// 当前进程Id + public static Int32 ProcessId => _ProcessId > 0 ? _ProcessId : _ProcessId = Environment.ProcessId; +#else + /// 当前进程Id + public static Int32 ProcessId => _ProcessId > 0 ? _ProcessId : _ProcessId = Process.GetCurrentProcess().Id; +#endif + /// /// 获取环境变量。不区分大小写 /// @@ -171,4 +182,83 @@ public static Boolean CreateConfigOnMissing set { _createConfigOnMissing = value; } } #endregion + + #region 内存 + /// 释放内存。GC回收后再释放虚拟内存 + /// 进程Id。默认0表示当前进程 + /// 是否GC回收 + /// 是否释放工作集 + public static Boolean FreeMemory(Int32 processId = 0, Boolean gc = true, Boolean workingSet = true) + { + if (processId <= 0) processId = ProcessId; + + var p = Process.GetProcessById(processId); + if (p == null) return false; + + if (processId != ProcessId) gc = false; + + var log = XTrace.Log; + if (log != null && log.Enable && log.Level <= LogLevel.Debug) + { + p ??= Process.GetCurrentProcess(); + var gcm = GC.GetTotalMemory(false) / 1024; + var ws = p.WorkingSet64 / 1024; + var prv = p.PrivateMemorySize64 / 1024; + if (gc) + log.Debug("[{3}/{4}]开始释放内存:GC={0:n0}K,WorkingSet={1:n0}K,PrivateMemory={2:n0}K", gcm, ws, prv, p.ProcessName, p.Id); + else + log.Debug("[{3}/{4}]开始释放内存:WorkingSet={1:n0}K,PrivateMemory={2:n0}K", gcm, ws, prv, p.ProcessName, p.Id); + } + + if (gc) + { + var max = GC.MaxGeneration; + var mode = GCCollectionMode.Forced; +#if NET8_0_OR_GREATER + mode = GCCollectionMode.Aggressive; +#endif +#if NET451_OR_GREATER || NETSTANDARD || NETCOREAPP + GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; +#endif + GC.Collect(max, mode); + GC.WaitForPendingFinalizers(); + GC.Collect(max, mode); + } + + if (workingSet) + { + if (Runtime.Windows) + { + try + { + p ??= Process.GetProcessById(processId); + EmptyWorkingSet(p.Handle); + } + catch (Exception ex) + { + log?.Error("EmptyWorkingSet失败:{0}", ex.Message); + return false; + } + } + } + + if (log != null && log.Enable && log.Level <= LogLevel.Debug) + { + p ??= Process.GetProcessById(processId); + p.Refresh(); + var gcm = GC.GetTotalMemory(false) / 1024; + var ws = p.WorkingSet64 / 1024; + var prv = p.PrivateMemorySize64 / 1024; + if (gc) + log.Debug("[{3}/{4}]释放内存完成:GC={0:n0}K,WorkingSet={1:n0}K,PrivateMemory={2:n0}K", gcm, ws, prv, p.ProcessName, p.Id); + else + log.Debug("[{3}/{4}]释放内存完成:WorkingSet={1:n0}K,PrivateMemory={2:n0}K", gcm, ws, prv, p.ProcessName, p.Id); + } + + return true; + } + + [DllImport("psapi.dll", SetLastError = true)] + internal static extern Boolean EmptyWorkingSet(IntPtr hProcess); + #endregion } \ No newline at end of file diff --git a/Test/Program.cs b/Test/Program.cs index 63ebced54..ac4d51857 100644 --- a/Test/Program.cs +++ b/Test/Program.cs @@ -76,7 +76,7 @@ private static async Task Main(String[] args) try { #endif - Test1(); + Test8(); #if !DEBUG } catch (Exception ex) @@ -133,6 +133,8 @@ private static void Test2() count *= ts.Count; XTrace.WriteLine("生成 {0:n0},耗时 {1},速度 {2:n0}tps", count, sw.Elapsed, count * 1000 / sw.ElapsedMilliseconds); + + Runtime.FreeMemory(); } private static void Test3() @@ -229,6 +231,8 @@ private static void Test4() XTrace.WriteLine("总测试数据:{0:n0}", rs); //if (ch is Redis rds2) XTrace.WriteLine(rds2.Counter + ""); + + Runtime.FreeMemory(); } private static NetServer _server; @@ -377,8 +381,12 @@ private static void Test7() //Console.ReadKey(); } - private static async void Test8() + private static void Test8() { + foreach (var p in Process.GetProcessesByName("dotnet")) + { + Runtime.FreeMemory(p.Id); + } } private static void Test9() From 9692a77b4f37654d339789558ed750a01b96a022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=B3=E5=A4=B4?= Date: Sun, 25 Aug 2024 00:28:33 +0800 Subject: [PATCH 06/18] =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NewLife.Core/Common/Utility.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/NewLife.Core/Common/Utility.cs b/NewLife.Core/Common/Utility.cs index 7e2500980..31cf97319 100644 --- a/NewLife.Core/Common/Utility.cs +++ b/NewLife.Core/Common/Utility.cs @@ -90,13 +90,13 @@ public static class Utility /// public static DateTimeOffset ToDateTimeOffset(this Object? value, DateTimeOffset defaultValue) => Convert.ToDateTimeOffset(value, defaultValue); - /// 去掉时间日期秒后面部分,可指定毫秒ms、分m、小时h + /// 去掉时间日期秒后面部分,可指定毫秒ms、秒s、分m、小时h /// 时间日期 /// 格式字符串,默认s格式化到秒,ms格式化到毫秒 /// public static DateTime Trim(this DateTime value, String format = "s") => Convert.Trim(value, format); - /// 去掉时间日期秒后面部分,可指定毫秒 + /// 去掉时间日期秒后面部分,可指定毫秒ms、秒s、分m、小时h /// 时间日期 /// 格式字符串,默认s格式化到秒,ms格式化到毫秒 /// @@ -616,7 +616,7 @@ private static String ToDBC(String str) return new String(ch); } - /// 去掉时间日期秒后面部分,可指定毫秒ms、分m、小时h + /// 去掉时间日期秒后面部分,可指定毫秒ms、秒s、分m、小时h /// 时间日期 /// 格式字符串,默认s格式化到秒,ms格式化到毫秒 /// From 90620fe97b37c7c16bf2916f6f6702eb69b58608 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=B3=E5=A4=B4?= Date: Sun, 25 Aug 2024 00:33:28 +0800 Subject: [PATCH 07/18] =?UTF-8?q?=E5=81=9C=E7=94=A8SplitAsDictionaryT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NewLife.Core/Extension/StringHelper.cs | 64 +++++++++++++------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/NewLife.Core/Extension/StringHelper.cs b/NewLife.Core/Extension/StringHelper.cs index 1b348f224..0c4c707a2 100644 --- a/NewLife.Core/Extension/StringHelper.cs +++ b/NewLife.Core/Extension/StringHelper.cs @@ -162,46 +162,46 @@ public static Int32[] SplitAsInt(this String? value, params String[] separators) return dic; } - /// - /// 在.netCore需要区分该部分内容 - /// - /// - /// - /// - /// - /// - public static IDictionary SplitAsDictionaryT(this String? value, Char nameValueSeparator = '=', Char separator = ';', Boolean trimQuotation = false) - { - var dic = new NullableDictionary(StringComparer.OrdinalIgnoreCase); - if (value == null || value.IsNullOrWhiteSpace()) return dic; + ///// + ///// 在.netCore需要区分该部分内容 + ///// + ///// + ///// + ///// + ///// + ///// + //public static IDictionary SplitAsDictionaryT(this String? value, Char nameValueSeparator = '=', Char separator = ';', Boolean trimQuotation = false) + //{ + // var dic = new NullableDictionary(StringComparer.OrdinalIgnoreCase); + // if (value == null || value.IsNullOrWhiteSpace()) return dic; - //if (nameValueSeparator == null) nameValueSeparator = '='; - //if (separator == null || separator.Length <= 0) separator = new String[] { ",", ";" }; + // //if (nameValueSeparator == null) nameValueSeparator = '='; + // //if (separator == null || separator.Length <= 0) separator = new String[] { ",", ";" }; - var ss = value.Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries); - if (ss == null || ss.Length <= 0) return dic; + // var ss = value.Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries); + // if (ss == null || ss.Length <= 0) return dic; - foreach (var item in ss) - { - var p = item.IndexOf(nameValueSeparator); - if (p <= 0) continue; + // foreach (var item in ss) + // { + // var p = item.IndexOf(nameValueSeparator); + // if (p <= 0) continue; - var key = item[..p].Trim(); - var val = item[(p + 1)..].Trim(); + // var key = item[..p].Trim(); + // var val = item[(p + 1)..].Trim(); - // 处理单引号双引号 - if (trimQuotation && !val.IsNullOrEmpty()) - { - if (val[0] == '\'' && val[^1] == '\'') val = val.Trim('\''); - if (val[0] == '"' && val[^1] == '"') val = val.Trim('"'); - } + // // 处理单引号双引号 + // if (trimQuotation && !val.IsNullOrEmpty()) + // { + // if (val[0] == '\'' && val[^1] == '\'') val = val.Trim('\''); + // if (val[0] == '"' && val[^1] == '"') val = val.Trim('"'); + // } - dic[key] = val; - } + // dic[key] = val; + // } - return dic; - } + // return dic; + //} /// 把一个列表组合成为一个字符串,默认逗号分隔 /// From 8f68fdf85f16a6e6e5fed822d0370a950104819d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=B3=E5=A4=B4?= Date: Sun, 25 Aug 2024 00:52:02 +0800 Subject: [PATCH 08/18] =?UTF-8?q?[fix]GetProcessName=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=E8=8E=B7=E5=8F=96dotnet=E5=90=8E=E5=8F=AF=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NewLife.Core/Extension/ProcessHelper.cs | 4 ++-- Test/Program.cs | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/NewLife.Core/Extension/ProcessHelper.cs b/NewLife.Core/Extension/ProcessHelper.cs index 0bf44b132..0a984c249 100644 --- a/NewLife.Core/Extension/ProcessHelper.cs +++ b/NewLife.Core/Extension/ProcessHelper.cs @@ -50,9 +50,9 @@ public static String GetProcessName(this Process process) //} var args = GetCommandLineArgs(process.Id); - if (args != null) + if (args != null && args.Length >= 2 && args[0].Contains("dotnet")) { - + name = args[1]; } return name; diff --git a/Test/Program.cs b/Test/Program.cs index ac4d51857..065c10ba9 100644 --- a/Test/Program.cs +++ b/Test/Program.cs @@ -385,7 +385,9 @@ private static void Test8() { foreach (var p in Process.GetProcessesByName("dotnet")) { - Runtime.FreeMemory(p.Id); + //Runtime.FreeMemory(p.Id); + var name = p.GetProcessName2(); + XTrace.WriteLine("{0}\t{1}", p.Id, name); } } From 17045eaaa1b5ec0421359d3328d8d1499ab8f7a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=B3=E5=A4=B4?= Date: Sun, 25 Aug 2024 15:19:01 +0800 Subject: [PATCH 09/18] =?UTF-8?q?=E6=8E=A8=E9=80=81v10=E6=97=B6=E4=B9=9F?= =?UTF-8?q?=E8=A6=81=E8=87=AA=E5=8A=A8=E5=8F=91=E5=B8=83nuget?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/publish-beta.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-beta.yml b/.github/workflows/publish-beta.yml index 4eeed0347..ed2808e18 100644 --- a/.github/workflows/publish-beta.yml +++ b/.github/workflows/publish-beta.yml @@ -2,11 +2,11 @@ name: publish-beta on: push: - branches: [ master, dev ] + branches: [ master, dev, v10 ] paths: - 'NewLife.Core/**' pull_request: - branches: [ master, dev ] + branches: [ master, dev, v10 ] paths: - 'NewLife.Core/**' workflow_dispatch: From 3e3b2349af32e9fa2d34280196879e6a141f2a7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=B3=E5=A4=B4?= Date: Sun, 25 Aug 2024 00:58:36 +0800 Subject: [PATCH 10/18] =?UTF-8?q?=E8=8E=B7=E5=8F=96dotnet=E5=90=8E?= =?UTF-8?q?=E7=9A=84=E6=96=87=E4=BB=B6=E5=90=8D=E5=8E=BB=E6=8E=89=E5=90=8E?= =?UTF-8?q?=E7=BC=80=E5=90=8E=E4=BD=9C=E4=B8=BA=E8=BF=9B=E7=A8=8B=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NewLife.Core/Extension/ProcessHelper.cs | 63 ++++++------------------- Test/Program.cs | 2 +- 2 files changed, 16 insertions(+), 49 deletions(-) diff --git a/NewLife.Core/Extension/ProcessHelper.cs b/NewLife.Core/Extension/ProcessHelper.cs index 0a984c249..6b3c65878 100644 --- a/NewLife.Core/Extension/ProcessHelper.cs +++ b/NewLife.Core/Extension/ProcessHelper.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; using System.Text; +using System.Xml.Linq; using NewLife.Configuration; using NewLife.Log; @@ -14,61 +15,27 @@ namespace NewLife; public static class ProcessHelper { #region 进程查找 - /// 获取进程名。dotnet/java进程取文件名,Windows系统中比较耗时 + /// 获取二级进程名。默认一级,如果是dotnet/java则取二级 /// /// public static String GetProcessName(this Process process) { - var name = process.ProcessName; - - //if (Runtime.Linux) - //{ - // try - // { - // var file = $"/proc/{process.Id}/cmdline"; - // if (File.Exists(file)) - // { - // var lines = File.ReadAllText(file).Trim('\0', ' ').Split('\0'); - // if (lines.Length > 1) name = Path.GetFileNameWithoutExtension(lines[1]); - // } - // } - // catch { } - //} - //else if (Runtime.Windows) - //{ - // try - // { - // var dic = MachineInfo.ReadWmic("process where processId=" + process.Id, "commandline"); - // if (dic.TryGetValue("commandline", out var str) && !str.IsNullOrEmpty()) - // { - // var ss = str.Split(' ').Select(e => e.Trim('\"')).ToArray(); - // str = ss.FirstOrDefault(e => e.EndsWithIgnoreCase(".dll", ".jar")); - // if (!str.IsNullOrEmpty()) name = Path.GetFileNameWithoutExtension(str); - // } - // } - // catch { } - //} - - var args = GetCommandLineArgs(process.Id); - if (args != null && args.Length >= 2 && args[0].Contains("dotnet")) + var pname = process.ProcessName; + if (pname == "dotnet" || "*/dotnet".IsMatch(pname)) { - name = args[1]; + var args = GetCommandLineArgs(process.Id); + if (args != null && args.Length >= 2 && args[0].Contains("dotnet")) + { + return Path.GetFileNameWithoutExtension(args[1]); + } } - - return name; - } - - /// 获取二级进程名。默认一级,如果是dotnet/java则取二级 - /// - /// - public static String GetProcessName2(this Process process) - { - var pname = process.ProcessName; - if ( - pname == "dotnet" || "*/dotnet".IsMatch(pname) || - pname == "java" || "*/java".IsMatch(pname)) + if (pname == "java" || "*/java".IsMatch(pname)) { - return GetProcessName(process); + var args = GetCommandLineArgs(process.Id); + if (args != null && args.Length >= 3 && args[0].Contains("java") && args[1] == "-jar") + { + return Path.GetFileNameWithoutExtension(args[2]); + } } return pname; diff --git a/Test/Program.cs b/Test/Program.cs index 065c10ba9..f83d99748 100644 --- a/Test/Program.cs +++ b/Test/Program.cs @@ -386,7 +386,7 @@ private static void Test8() foreach (var p in Process.GetProcessesByName("dotnet")) { //Runtime.FreeMemory(p.Id); - var name = p.GetProcessName2(); + var name = p.GetProcessName(); XTrace.WriteLine("{0}\t{1}", p.Id, name); } } From bf9d150e38b5f560def562f10d58f72f95e0fded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=B3=E5=A4=B4?= Date: Sun, 25 Aug 2024 15:18:12 +0800 Subject: [PATCH 11/18] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=96=87=E6=A1=A3Url?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NewLife.Core/Extension/ProcessHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewLife.Core/Extension/ProcessHelper.cs b/NewLife.Core/Extension/ProcessHelper.cs index 6b3c65878..f2d12ec57 100644 --- a/NewLife.Core/Extension/ProcessHelper.cs +++ b/NewLife.Core/Extension/ProcessHelper.cs @@ -10,7 +10,7 @@ namespace NewLife; /// 进程助手类 /// -/// 文档 https://newlifex.com/core/string_helper +/// 文档 https://newlifex.com/core/process_helper /// public static class ProcessHelper { From 06a30af98565fd16b10c099f296409989864eda9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E7=9F=B3=E5=A4=B4?= Date: Wed, 28 Aug 2024 18:01:49 +0800 Subject: [PATCH 12/18] =?UTF-8?q?[fix]=E4=BF=AE=E6=AD=A3CsvDb.Set=E5=9C=A8?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E6=95=B0=E6=8D=AE=E6=97=B6=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NewLife.Core/IO/CsvDb.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/NewLife.Core/IO/CsvDb.cs b/NewLife.Core/IO/CsvDb.cs index 90956d9f4..14bd065b4 100644 --- a/NewLife.Core/IO/CsvDb.cs +++ b/NewLife.Core/IO/CsvDb.cs @@ -70,7 +70,7 @@ public void Write(IEnumerable models, Boolean append) /// 尾部插入数据,性能极好 /// - public void Add(T model) => Add(new[] { model }); + public void Add(T model) => Add([model]); /// 尾部插入数据,性能极好 /// @@ -79,7 +79,7 @@ public void Write(IEnumerable models, Boolean append) /// 删除数据,性能很差,全部读取剔除后保存 /// /// - public Int32 Remove(T model) => Remove(new[] { model }); + public Int32 Remove(T model) => Remove([model]); /// 删除数据,性能很差,全部读取剔除后保存 /// @@ -119,7 +119,7 @@ public Int32 Remove(Func predicate) } /// 清空数据。只写头部 - public void Clear() => Write(new T[0], false); + public void Clear() => Write([], false); /// 更新指定数据行,性能很差,全部读取替换后保存 /// @@ -138,7 +138,7 @@ private Boolean Set(T model, Boolean add) lock (this) { var list = FindAll(); - if (list.Count == 0) return false; + if (!add && list.Count == 0) return false; // 找到目标数据行,并替换 var flag = false; From c8645c1db05af6fa33666b6336210c75af08bd12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E7=9F=B3=E5=A4=B4?= Date: Wed, 28 Aug 2024 19:04:37 +0800 Subject: [PATCH 13/18] =?UTF-8?q?[feat]CsvDb=E6=94=AF=E6=8C=81=E4=BA=8B?= =?UTF-8?q?=E5=8A=A1=EF=BC=8C=E5=BC=80=E5=90=AF=E4=BA=8B=E5=8A=A1=E5=90=8E?= =?UTF-8?q?=E5=8A=A0=E9=80=9F=E6=95=B0=E6=8D=AE=E6=93=8D=E4=BD=9C=EF=BC=8C?= =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BA=8B=E5=8A=A1=E6=97=B6=E5=86=8D=E5=86=99?= =?UTF-8?q?=E5=85=A5=E7=A3=81=E7=9B=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NewLife.Core/IO/CsvDb.cs | 99 +++++- XUnitTest.Core/IO/CsvDbTests.cs | 528 +++++++++++++++++--------------- 2 files changed, 360 insertions(+), 267 deletions(-) diff --git a/NewLife.Core/IO/CsvDb.cs b/NewLife.Core/IO/CsvDb.cs index 14bd065b4..3c8ce3cc8 100644 --- a/NewLife.Core/IO/CsvDb.cs +++ b/NewLife.Core/IO/CsvDb.cs @@ -10,11 +10,15 @@ namespace NewLife.IO; /// Csv文件轻量级数据库 /// /// 文档 https://newlifex.com/core/csv_db -/// 适用于大量数据需要快速存储、快速查找,很少修改和删除的场景。 +/// 适用于大量数据需要快速追加、快速查找,很少修改和删除的场景。 /// 在桌面客户端中,关系型数据库SQLite很容易因非法关机而损坏,本数据库能跳过损坏行,自动恢复。 +/// +/// 中间插入和删除时,需要移动尾部数据,性能较差。 +/// +/// 本设计不支持线程安全,务必确保单线程操作。 /// /// -public class CsvDb where T : new() +public class CsvDb : DisposeBase where T : new() { #region 属性 /// 文件名 @@ -34,14 +38,43 @@ namespace NewLife.IO; /// 实例化Csv文件数据库 /// public CsvDb(Func comparer) => Comparer = new MyComparer(comparer); + + /// 销毁 + /// + protected override void Dispose(Boolean disposing) + { + base.Dispose(disposing); + + Commit(); + } + #endregion + + #region 基础方法 + private List? _cache; + /// 开启事务,便于批量处理数据 + public void BeginTransaction() + { + _cache ??= FindAll().ToList(); + } + + /// 提交事务,把缓存数据写入磁盘 + public void Commit() + { + if (_cache == null) return; + + Write(_cache, false); + _cache = null; + } #endregion - #region 方法 - /// 强行写入数据 + #region 添删改查 + /// 批量写入数据(高性能) /// 要写入的数据 - /// 是否附加在尾部 + /// 是否附加在尾部。为false时从头写入,覆盖已有数据 public void Write(IEnumerable models, Boolean append) { + if (!models.Any() && append) return; + var file = GetFile(); file.EnsureDirectory(true); @@ -70,11 +103,23 @@ public void Write(IEnumerable models, Boolean append) /// 尾部插入数据,性能极好 /// - public void Add(T model) => Add([model]); + public void Add(T model) + { + if (_cache != null) + _cache.Add(model); + else + Write([model], true); + } /// 尾部插入数据,性能极好 /// - public void Add(IEnumerable models) => Write(models, true); + public void Add(IEnumerable models) + { + if (_cache != null) + _cache.AddRange(models); + else + Write(models, true); + } /// 删除数据,性能很差,全部读取剔除后保存 /// @@ -101,6 +146,8 @@ public Int32 Remove(Func predicate) lock (this) { + if (_cache != null) return _cache.RemoveAll(x => predicate(x)); + var list = FindAll(); if (list.Count == 0) return 0; @@ -119,7 +166,13 @@ public Int32 Remove(Func predicate) } /// 清空数据。只写头部 - public void Clear() => Write([], false); + public void Clear() + { + if (_cache != null) + _cache.Clear(); + else + Write([], false); + } /// 更新指定数据行,性能很差,全部读取替换后保存 /// @@ -137,7 +190,7 @@ private Boolean Set(T model, Boolean add) lock (this) { - var list = FindAll(); + var list = _cache ?? FindAll(); if (!add && list.Count == 0) return false; // 找到目标数据行,并替换 @@ -159,7 +212,10 @@ private Boolean Set(T model, Boolean add) } // 重新写回去 - Write(list, false); + if (_cache == null) + { + Write(list, false); + } return true; } @@ -182,7 +238,7 @@ private Boolean Set(T model, Boolean add) /// 获取所有数据行 /// - public IList FindAll() => Query(null).ToList(); + public IList FindAll() => _cache?.ToList() ?? Query(null).ToList(); /// 获取满足条件的数据行,性能好,顺序查找 /// @@ -190,6 +246,21 @@ private Boolean Set(T model, Boolean add) /// public IEnumerable Query(Func? predicate, Int32 count = -1) { + // 开启事务时,直接返回缓存数据 + if (_cache != null) + { + foreach (var item in _cache) + { + if (predicate == null || predicate(item)) + { + yield return item; + + if (--count == 0) break; + } + } + yield break; + } + var file = GetFile(); if (!File.Exists(file)) yield break; @@ -197,7 +268,6 @@ public IEnumerable Query(Func? predicate, Int32 count = -1) { using var csv = new CsvFile(file, false) { Encoding = Encoding }; - //var list = new List(); var headers = new Dictionary(); var pis = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); while (true) @@ -236,7 +306,6 @@ public IEnumerable Query(Func? predicate, Int32 count = -1) if (predicate == null || predicate(model)) { - //list.Add(model); flag = true; } } @@ -254,8 +323,6 @@ public IEnumerable Query(Func? predicate, Int32 count = -1) if (--count == 0) break; } } - - //return list; } } @@ -263,6 +330,8 @@ public IEnumerable Query(Func? predicate, Int32 count = -1) /// public Int32 FindCount() { + if (_cache != null) return _cache!.Count; + lock (this) { var file = GetFile(); diff --git a/XUnitTest.Core/IO/CsvDbTests.cs b/XUnitTest.Core/IO/CsvDbTests.cs index a892cda73..21b11e05a 100644 --- a/XUnitTest.Core/IO/CsvDbTests.cs +++ b/XUnitTest.Core/IO/CsvDbTests.cs @@ -6,348 +6,372 @@ using NewLife; using NewLife.Data; using NewLife.IO; -using NewLife.Reflection; using NewLife.Security; using Xunit; -namespace XUnitTest.IO +namespace XUnitTest.IO; + +public class CsvDbTests { - public class CsvDbTests + protected virtual CsvDb GetDb(String name) { - private CsvDb GetDb(String name) - { - var file = $"data/{name}.csv".GetFullPath(); - if (File.Exists(file)) File.Delete(file); + var file = $"data/{name}.csv".GetFullPath(); + if (File.Exists(file)) File.Delete(file); - var db = new CsvDb((x, y) => x.Code == y.Code) - { - FileName = file - }; - return db; - } + var db = new CsvDb((x, y) => x.Code == y.Code) + { + FileName = file + }; + return db; + } - private GeoArea GetModel() + private GeoArea GetModel() + { + var model = new GeoArea { - var model = new GeoArea - { - Code = Rand.Next(), - Name = Rand.NextString(14), - }; + Code = Rand.Next(), + Name = Rand.NextString(14), + }; - return model; - } + return model; + } - private String[] GetHeaders() + private String[] GetHeaders() + { + var pis = typeof(GeoArea).GetProperties(BindingFlags.Public | BindingFlags.Instance); + return pis.Select(e => e.Name).ToArray(); + } + + private Object[] GetValue(GeoArea model) + { + var pis = typeof(GeoArea).GetProperties(BindingFlags.Public | BindingFlags.Instance); + //return pis.Select(e => e.GetValue(model, null)).ToArray(); + var arr = new Object[pis.Length]; + for (var i = 0; i < pis.Length; i++) { - var pis = typeof(GeoArea).GetProperties(BindingFlags.Public | BindingFlags.Instance); - return pis.Select(e => e.Name).ToArray(); + arr[i] = pis[i].GetValue(model, null); + if (pis[i].PropertyType == typeof(Boolean)) + arr[i] = (Boolean)arr[i] ? "1" : "0"; + else if (pis[i].Name == "Code" && arr[i].ToString().Length > 9) + arr[i] = "\t" + arr[i]; } + return arr; + } + + [Fact] + public void InsertTest() + { + var db = GetDb("Insert"); + + var model = GetModel(); + db.Add(model); + + db.Dispose(); + + // 把文件读出来 + var lines = File.ReadAllLines(db.FileName.GetFullPath()); + Assert.Equal(2, lines.Length); - private Object[] GetValue(GeoArea model) + Assert.Equal(GetHeaders().Join(","), lines[0]); + Assert.Equal(GetValue(model).Join(","), lines[1]); + } + + [Fact] + public void InsertsTest() + { + var db = GetDb("Inserts"); + + var list = new List(); + var count = Rand.Next(2, 100); + for (var i = 0; i < count; i++) { - var pis = typeof(GeoArea).GetProperties(BindingFlags.Public | BindingFlags.Instance); - //return pis.Select(e => e.GetValue(model, null)).ToArray(); - var arr = new Object[pis.Length]; - for (var i = 0; i < pis.Length; i++) - { - arr[i] = pis[i].GetValue(model, null); - if (pis[i].PropertyType == typeof(Boolean)) - arr[i] = (Boolean)arr[i] ? "1" : "0"; - else if (pis[i].Name == "Code" && arr[i].ToString().Length > 9) - arr[i] = "\t" + arr[i]; - } - return arr; + list.Add(GetModel()); } - [Fact] - public void InsertTest() - { - var db = GetDb("Insert"); + db.Add(list); - var model = GetModel(); - db.Add(model); + db.Dispose(); - // 把文件读出来 - var lines = File.ReadAllLines(db.FileName.GetFullPath()); - Assert.Equal(2, lines.Length); + // 把文件读出来 + var lines = File.ReadAllLines(db.FileName.GetFullPath()); + Assert.Equal(list.Count + 1, lines.Length); - Assert.Equal(GetHeaders().Join(","), lines[0]); - Assert.Equal(GetValue(model).Join(","), lines[1]); + Assert.Equal(GetHeaders().Join(","), lines[0]); + for (var i = 0; i < list.Count; i++) + { + Assert.Equal(GetValue(list[i]).Join(","), lines[i + 1]); } + } - [Fact] - public void InsertsTest() - { - var db = GetDb("Inserts"); + [Fact] + public void GetAllTest() + { + var db = GetDb("GetAll"); - var list = new List(); - var count = Rand.Next(2, 100); - for (var i = 0; i < count; i++) - { - list.Add(GetModel()); - } + var list = new List(); + var count = Rand.Next(2, 100); + for (var i = 0; i < count; i++) + { + list.Add(GetModel()); + } - db.Add(list); + db.Add(list); - // 把文件读出来 - var lines = File.ReadAllLines(db.FileName.GetFullPath()); - Assert.Equal(list.Count + 1, lines.Length); + // 把文件读出来 + var list2 = db.FindAll(); + Assert.Equal(list.Count, list2.Count); - Assert.Equal(GetHeaders().Join(","), lines[0]); - for (var i = 0; i < list.Count; i++) - { - Assert.Equal(GetValue(list[i]).Join(","), lines[i + 1]); - } + for (var i = 0; i < list.Count; i++) + { + Assert.Equal(GetValue(list[i]).Join(","), GetValue(list2[i]).Join(",")); } - [Fact] - public void GetAllTest() + // 高级查找 + var list3 = db.Query(e => e.Code is >= 100 and < 1000); + var list4 = list.Where(e => e.Code is >= 100 and < 1000).ToList(); + Assert.Equal(list4.Select(e => e.Code), list3.Select(e => e.Code)); + } + + [Fact] + public void GetCountTest() + { + var db = GetDb("GetCount"); + + var list = new List(); + var count = Rand.Next(2, 100); + for (var i = 0; i < count; i++) { - var db = GetDb("GetAll"); + list.Add(GetModel()); + } - var list = new List(); - var count = Rand.Next(2, 100); - for (var i = 0; i < count; i++) - { - list.Add(GetModel()); - } + db.Add(list); - db.Add(list); + db.Dispose(); - // 把文件读出来 - var list2 = db.FindAll(); - Assert.Equal(list.Count, list2.Count); + // 把文件读出来 + var lines = File.ReadAllLines(db.FileName.GetFullPath()); + Assert.Equal(list.Count + 1, lines.Length); + Assert.Equal(list.Count, db.FindCount()); + } - for (var i = 0; i < list.Count; i++) - { - Assert.Equal(GetValue(list[i]).Join(","), GetValue(list2[i]).Join(",")); - } + [Fact] + public void LargeInsertsTest() + { + var db = GetDb("LargeInserts"); - // 高级查找 - var list3 = db.Query(e => e.Code is >= 100 and < 1000); - var list4 = list.Where(e => e.Code is >= 100 and < 1000).ToList(); - Assert.Equal(list4.Select(e => e.Code), list3.Select(e => e.Code)); + var list = new List(); + var count = 100_000; + for (var i = 0; i < count; i++) + { + list.Add(GetModel()); } - [Fact] - public void GetCountTest() - { - var db = GetDb("GetCount"); + db.Add(list); - var list = new List(); - var count = Rand.Next(2, 100); - for (var i = 0; i < count; i++) - { - list.Add(GetModel()); - } + db.Dispose(); - db.Add(list); + // 把文件读出来 + var lines = File.ReadAllLines(db.FileName.GetFullPath()); + Assert.Equal(list.Count + 1, lines.Length); - // 把文件读出来 - var lines = File.ReadAllLines(db.FileName.GetFullPath()); - Assert.Equal(list.Count + 1, lines.Length); - Assert.Equal(list.Count, db.FindCount()); + Assert.Equal(GetHeaders().Join(","), lines[0]); + for (var i = 0; i < list.Count; i++) + { + Assert.Equal(GetValue(list[i]).Join(","), lines[i + 1]); } + } - [Fact] - public void LargeInsertsTest() - { - var db = GetDb("LargeInserts"); + [Fact] + public void InsertTwoTimesTest() + { + var db = GetDb("InsertTwoTimes"); - var list = new List(); - var count = 100_000; + // 第一次插入 + var list = new List(); + { + var count = Rand.Next(2, 100); for (var i = 0; i < count; i++) { list.Add(GetModel()); } db.Add(list); - - // 把文件读出来 - var lines = File.ReadAllLines(db.FileName.GetFullPath()); - Assert.Equal(list.Count + 1, lines.Length); - - Assert.Equal(GetHeaders().Join(","), lines[0]); - for (var i = 0; i < list.Count; i++) - { - Assert.Equal(GetValue(list[i]).Join(","), lines[i + 1]); - } } - [Fact] - public void InsertTwoTimesTest() + // 第二次插入 { - var db = GetDb("InsertTwoTimes"); - - // 第一次插入 - var list = new List(); + var list2 = new List(); + var count = Rand.Next(2, 100); + for (var i = 0; i < count; i++) { - var count = Rand.Next(2, 100); - for (var i = 0; i < count; i++) - { - list.Add(GetModel()); - } - - db.Add(list); + list2.Add(GetModel()); } - // 第二次插入 - { - var list2 = new List(); - var count = Rand.Next(2, 100); - for (var i = 0; i < count; i++) - { - list2.Add(GetModel()); - } + db.Add(list2); - db.Add(list2); + list.AddRange(list2); + } - list.AddRange(list2); - } + db.Dispose(); - // 把文件读出来 - var lines = File.ReadAllLines(db.FileName.GetFullPath()); - Assert.Equal(list.Count + 1, lines.Length); + // 把文件读出来 + var lines = File.ReadAllLines(db.FileName.GetFullPath()); + Assert.Equal(list.Count + 1, lines.Length); - Assert.Equal(GetHeaders().Join(","), lines[0]); - for (var i = 0; i < list.Count; i++) - { - Assert.Equal(GetValue(list[i]).Join(","), lines[i + 1]); - } + Assert.Equal(GetHeaders().Join(","), lines[0]); + for (var i = 0; i < list.Count; i++) + { + Assert.Equal(GetValue(list[i]).Join(","), lines[i + 1]); } + } - [Fact] - public void DeletesTest() + [Fact] + public void DeletesTest() + { + var db = GetDb("Deletes"); + + var list = new List(); + var count = Rand.Next(2, 100); + for (var i = 0; i < count; i++) { - var db = GetDb("Deletes"); + list.Add(GetModel()); + } - var list = new List(); - var count = Rand.Next(2, 100); - for (var i = 0; i < count; i++) - { - list.Add(GetModel()); - } + db.Add(list); - db.Add(list); + // 随机删除一个 + var idx = Rand.Next(list.Count); + var rs = db.Remove(list[idx]); + Assert.Equal(1, rs); - // 随机删除一个 - var idx = Rand.Next(list.Count); - var rs = db.Remove(list[idx]); - Assert.Equal(1, rs); + list.RemoveAt(idx); + Assert.Equal(list.Count, db.FindCount()); - list.RemoveAt(idx); - Assert.Equal(list.Count, db.FindCount()); + // 随机抽几个,删除 + var list2 = new List(); + for (var i = 0; i < list.Count; i++) + { + if (Rand.Next(2) == 1) list2.Add(list[i]); + } - // 随机抽几个,删除 - var list2 = new List(); - for (var i = 0; i < list.Count; i++) - { - if (Rand.Next(2) == 1) list2.Add(list[i]); - } + var rs2 = db.Remove(list2); + Assert.Equal(list2.Count, rs2); + Assert.Equal(list.Count - list2.Count, db.FindCount()); + } - var rs2 = db.Remove(list2); - Assert.Equal(list2.Count, rs2); - Assert.Equal(list.Count - list2.Count, db.FindCount()); - } + [Fact] + public void UpdateTest() + { + var db = GetDb("Update"); - [Fact] - public void UpdateTest() + var list = new List(); + var count = Rand.Next(2, 100); + for (var i = 0; i < count; i++) { - var db = GetDb("Update"); + list.Add(GetModel()); + } - var list = new List(); - var count = Rand.Next(2, 100); - for (var i = 0; i < count; i++) - { - list.Add(GetModel()); - } + db.Add(list); - db.Add(list); + // 随机改一个 + var idx = Rand.Next(list.Count); + var model = db.Find(list[idx]); + Assert.NotNull(model); - // 随机改一个 - var idx = Rand.Next(list.Count); - var model = db.Find(list[idx]); - Assert.NotNull(model); + model.ParentCode = Rand.Next(); + var rs = db.Update(model); + Assert.True(rs); - model.ParentCode = Rand.Next(); - var rs = db.Update(model); - Assert.True(rs); + var model2 = db.Find(list[idx]); + Assert.NotNull(model2); + Assert.Equal(model.ParentCode, model2.ParentCode); + } - var model2 = db.Find(list[idx]); - Assert.NotNull(model2); - Assert.Equal(model.ParentCode, model2.ParentCode); - } + [Fact] + public void WriteTest() + { + var db = GetDb("Write"); - [Fact] - public void WriteTest() + var list = new List(); + var count = Rand.Next(2, 100); + for (var i = 0; i < count; i++) { - var db = GetDb("Write"); + list.Add(GetModel()); + } - var list = new List(); - var count = Rand.Next(2, 100); - for (var i = 0; i < count; i++) - { - list.Add(GetModel()); - } + db.Add(list); - db.Add(list); + // 再次覆盖写入 + list.Clear(); + for (var i = 0; i < 10; i++) + { + list.Add(GetModel()); + } + db.Write(list, false); - // 再次覆盖写入 - list.Clear(); - for (var i = 0; i < 10; i++) - { - list.Add(GetModel()); - } - db.Write(list, false); + // 把文件读出来 + var lines = File.ReadAllLines(db.FileName.GetFullPath()); + Assert.Equal(list.Count + 1, lines.Length); + } - // 把文件读出来 - var lines = File.ReadAllLines(db.FileName.GetFullPath()); - Assert.Equal(list.Count + 1, lines.Length); - } + [Fact] + public void ClearTest() + { + var db = GetDb("Clear"); - [Fact] - public void ClearTest() + var list = new List(); + var count = Rand.Next(2, 100); + for (var i = 0; i < count; i++) { - var db = GetDb("Clear"); + list.Add(GetModel()); + } - var list = new List(); - var count = Rand.Next(2, 100); - for (var i = 0; i < count; i++) - { - list.Add(GetModel()); - } + db.Add(list); - db.Add(list); + // 清空 + db.Clear(); - // 清空 - db.Clear(); + db.Dispose(); - // 把文件读出来 - var lines = File.ReadAllLines(db.FileName.GetFullPath()); - Assert.Single(lines); - } + // 把文件读出来 + var lines = File.ReadAllLines(db.FileName.GetFullPath()); + Assert.Single(lines); + } + + [Fact] + public void SetTest() + { + var db = GetDb("Set"); - [Fact] - public void SetTest() + var list = new List(); + var count = Rand.Next(2, 100); + for (var i = 0; i < count; i++) { - var db = GetDb("Set"); + list.Add(GetModel()); + } - var list = new List(); - var count = Rand.Next(2, 100); - for (var i = 0; i < count; i++) - { - list.Add(GetModel()); - } + db.Add(list); - db.Add(list); + // 设置新的 + var model = GetModel(); + db.Set(model); - // 设置新的 - var model = GetModel(); - db.Set(model); + db.Dispose(); - // 把文件读出来 - var lines = File.ReadAllLines(db.FileName.GetFullPath()); - Assert.Equal(list.Count + 1 + 1, lines.Length); - } + // 把文件读出来 + var lines = File.ReadAllLines(db.FileName.GetFullPath()); + Assert.Equal(list.Count + 1 + 1, lines.Length); + } +} + +public class CsvDbWithTransactionTests : CsvDbTests +{ + protected override CsvDb GetDb(String name) + { + name += "2"; + var db = base.GetDb(name); + db.BeginTransaction(); + + return db; } } \ No newline at end of file From 1c1cdcae7b694e627d6818b0dfcfeceddab7233d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=B3=E5=A4=B4?= Date: Thu, 29 Aug 2024 22:28:08 +0800 Subject: [PATCH 14/18] Upgrade Nuget --- NewLife.Security/NewLife.Security.csproj | 2 +- Samples/Zero.HttpServer/Zero.HttpServer.csproj | 4 ++-- Samples/Zero.Server/Zero.Server.csproj | 4 ++-- Test/Test.csproj | 4 ++-- Test2/Test2.csproj | 2 +- XUnitTest.Core/XUnitTest.Core.csproj | 6 +++--- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/NewLife.Security/NewLife.Security.csproj b/NewLife.Security/NewLife.Security.csproj index 388228fbf..9a3370f62 100644 --- a/NewLife.Security/NewLife.Security.csproj +++ b/NewLife.Security/NewLife.Security.csproj @@ -36,7 +36,7 @@ snupkg - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Samples/Zero.HttpServer/Zero.HttpServer.csproj b/Samples/Zero.HttpServer/Zero.HttpServer.csproj index 81dc7e213..c9d8b3f2f 100644 --- a/Samples/Zero.HttpServer/Zero.HttpServer.csproj +++ b/Samples/Zero.HttpServer/Zero.HttpServer.csproj @@ -20,8 +20,8 @@ - - + + diff --git a/Samples/Zero.Server/Zero.Server.csproj b/Samples/Zero.Server/Zero.Server.csproj index 639484593..285100679 100644 --- a/Samples/Zero.Server/Zero.Server.csproj +++ b/Samples/Zero.Server/Zero.Server.csproj @@ -20,8 +20,8 @@ - - + + diff --git a/Test/Test.csproj b/Test/Test.csproj index 14e74384c..76b132444 100644 --- a/Test/Test.csproj +++ b/Test/Test.csproj @@ -25,8 +25,8 @@ - - + + diff --git a/Test2/Test2.csproj b/Test2/Test2.csproj index d15a3418e..b86386c8c 100644 --- a/Test2/Test2.csproj +++ b/Test2/Test2.csproj @@ -24,7 +24,7 @@ - + diff --git a/XUnitTest.Core/XUnitTest.Core.csproj b/XUnitTest.Core/XUnitTest.Core.csproj index 8ae0309b9..f0f8f7d06 100644 --- a/XUnitTest.Core/XUnitTest.Core.csproj +++ b/XUnitTest.Core/XUnitTest.Core.csproj @@ -56,12 +56,12 @@ - + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive From 640219651b6d7cc4bb1ade7777b8d4c12194c52b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E7=9F=B3=E5=A4=B4?= Date: Mon, 2 Sep 2024 11:17:48 +0800 Subject: [PATCH 15/18] =?UTF-8?q?[feat]SaltPasswordProvider=E6=96=B0?= =?UTF-8?q?=E5=A2=9Emd5+sha512=EF=BC=8C=E6=98=8E=E7=A1=AEWeb=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E5=92=8CApp=E9=AA=8C=E8=AF=81=E7=9A=84=E4=B8=A4?= =?UTF-8?q?=E7=A7=8D=E4=B8=8D=E5=90=8C=E7=94=A8=E6=B3=95=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NewLife.Core/Security/IPasswordProvider.cs | 41 +++++++++++++++------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/NewLife.Core/Security/IPasswordProvider.cs b/NewLife.Core/Security/IPasswordProvider.cs index 738b5dd37..80ad8a8d2 100644 --- a/NewLife.Core/Security/IPasswordProvider.cs +++ b/NewLife.Core/Security/IPasswordProvider.cs @@ -6,13 +6,13 @@ namespace NewLife.Security; public interface IPasswordProvider { /// 对密码进行散列处理,此处可以加盐,结果保存在数据库 - /// + /// 密码明文 /// String Hash(String password); /// 验证密码散列,包括加盐判断 - /// - /// + /// 传输密码。可能是明文、MD5 + /// 哈希密文。服务端数据库保存,带有算法、盐值、哈希值 /// Boolean Verify(String password, String hash); } @@ -21,13 +21,13 @@ public interface IPasswordProvider public class PasswordProvider : IPasswordProvider { /// 对密码进行散列处理,此处可以加盐,结果保存在数据库 - /// + /// 密码明文 /// public String Hash(String password) => password; /// 验证密码散列,包括加盐判断 - /// - /// + /// 传输密码。可能是明文、MD5 + /// 哈希密文。服务端数据库保存,带有算法、盐值、哈希值 /// public Boolean Verify(String password, String hash) => password.EqualIgnoreCase(hash); } @@ -36,18 +36,22 @@ public class PasswordProvider : IPasswordProvider public class MD5PasswordProvider : IPasswordProvider { /// 对密码进行散列处理,此处可以加盐,结果保存在数据库 - /// + /// 密码明文 /// public String Hash(String password) => password.MD5(); /// 验证密码散列,包括加盐判断 - /// - /// + /// 传输密码。可能是明文、MD5 + /// 哈希密文。服务端数据库保存,带有算法、盐值、哈希值 /// public Boolean Verify(String password, String hash) => hash.EqualIgnoreCase(password, password.MD5()); } /// 盐值密码提供者 +/// +/// 1,在Web应用中,数据库保存哈希密码hash,登录时传输密码明文pass,服务端验证密码。算法配置为md5+sha512时,传输MD5散列。 +/// 2,在App验证时,数据库保存密码明文pass,登录时传输哈希密码hash,服务端验证密码。 +/// public class SaltPasswordProvider : IPasswordProvider { /// 算法。支持md5/sha1/sha512 @@ -58,7 +62,7 @@ public class SaltPasswordProvider : IPasswordProvider public Int32 SaltTime { get; set; } /// 对密码进行散列处理,此处可以加盐,结果保存在数据库 - /// 密码 + /// 密码明文 /// public String Hash(String password) { @@ -68,6 +72,8 @@ public String Hash(String password) "md5" => (password.MD5() + salt).MD5(), "sha1" => password.GetBytes().SHA1(salt.GetBytes()).ToBase64(), "sha512" => password.GetBytes().SHA512(salt.GetBytes()).ToBase64(), + "md5+sha1" => password.MD5().GetBytes().SHA1(salt.GetBytes()).ToBase64(), + "md5+sha512" => password.MD5().GetBytes().SHA512(salt.GetBytes()).ToBase64(), _ => throw new NotImplementedException(), }; @@ -94,15 +100,15 @@ protected virtual String CreateSalt() } /// 验证密码散列,包括加盐判断 - /// - /// + /// 传输密码。可能是明文、MD5 + /// 哈希密文。服务端数据库保存,带有算法、盐值、哈希值 /// public Boolean Verify(String password, String hash) { var ss = hash?.Split('$'); if (ss == null || ss.Length == 0) throw new ArgumentNullException(nameof(hash)); - // 老式MD5,password可能是密码原文,也可能是前端已经md5散列的值 + // 老式MD5,password可能是密码原文,也可能是前端已经md5散列的值。数据库里刚好也是明文或MD5散列 if (ss.Length == 1) return hash.EqualIgnoreCase(password, password.MD5()); if (ss.Length != 4) throw new NotSupportedException("Unsupported password hash value"); @@ -121,8 +127,17 @@ public Boolean Verify(String password, String hash) if (ss[3] == (password.MD5() + salt).MD5()) return true; return ss[3] == (password + salt).MD5(); case "sha1": + // 传输密码是明文 + if (ss[3] == password.MD5().GetBytes().SHA1(salt.GetBytes()).ToBase64()) return true; + // 传输密码是MD5哈希 return ss[3] == password.GetBytes().SHA1(salt.GetBytes()).ToBase64(); case "sha512": + if (ss[3] == password.MD5().GetBytes().SHA512(salt.GetBytes()).ToBase64()) return true; + return ss[3] == password.GetBytes().SHA512(salt.GetBytes()).ToBase64(); + case "md5+sha1": + // 传输密码是MD5哈希 + return ss[3] == password.GetBytes().SHA1(salt.GetBytes()).ToBase64(); + case "md5+sha512": return ss[3] == password.GetBytes().SHA512(salt.GetBytes()).ToBase64(); default: throw new NotSupportedException($"Unsupported password hash mode [{ss[1]}]"); From 0aa7e117aee1af9094318072ed07d2e6af6600c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=99=BA=E8=83=BD=E5=A4=A7=E7=9F=B3=E5=A4=B4?= Date: Mon, 2 Sep 2024 12:02:12 +0800 Subject: [PATCH 16/18] v10.10.2024.0902 --- NewLife.Core/Extension/ProcessHelper.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/NewLife.Core/Extension/ProcessHelper.cs b/NewLife.Core/Extension/ProcessHelper.cs index f2d12ec57..5fb77d63b 100644 --- a/NewLife.Core/Extension/ProcessHelper.cs +++ b/NewLife.Core/Extension/ProcessHelper.cs @@ -41,6 +41,12 @@ public static String GetProcessName(this Process process) return pname; } + /// 获取二级进程名 + /// + /// + [Obsolete("=>GetProcessName", true)] + public static String GetProcessName2(this Process process) => GetProcessName(process); + ///// 根据名称获取进程。支持dotnet/java ///// ///// From 9073af54f1f177c051a7fef353031893097384e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=B3=E5=A4=B4?= Date: Mon, 2 Sep 2024 14:11:18 +0800 Subject: [PATCH 17/18] Upgrade Nuget --- Test/Test.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/Test.csproj b/Test/Test.csproj index 76b132444..1f59f6b21 100644 --- a/Test/Test.csproj +++ b/Test/Test.csproj @@ -26,7 +26,7 @@ - + From b46013ad07eb452a2fdf6715728c80331f7837fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=B3=E5=A4=B4?= Date: Tue, 3 Sep 2024 08:07:15 +0800 Subject: [PATCH 18/18] Upgrade Nuget --- Samples/Zero.HttpServer/Zero.HttpServer.csproj | 2 +- Samples/Zero.Server/Zero.Server.csproj | 2 +- Test/Test.csproj | 2 +- Test2/Test2.csproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Samples/Zero.HttpServer/Zero.HttpServer.csproj b/Samples/Zero.HttpServer/Zero.HttpServer.csproj index c9d8b3f2f..9edbe89fe 100644 --- a/Samples/Zero.HttpServer/Zero.HttpServer.csproj +++ b/Samples/Zero.HttpServer/Zero.HttpServer.csproj @@ -21,7 +21,7 @@ - + diff --git a/Samples/Zero.Server/Zero.Server.csproj b/Samples/Zero.Server/Zero.Server.csproj index 285100679..e10ca0e26 100644 --- a/Samples/Zero.Server/Zero.Server.csproj +++ b/Samples/Zero.Server/Zero.Server.csproj @@ -21,7 +21,7 @@ - + diff --git a/Test/Test.csproj b/Test/Test.csproj index 1f59f6b21..3356e3e90 100644 --- a/Test/Test.csproj +++ b/Test/Test.csproj @@ -25,7 +25,7 @@ - + diff --git a/Test2/Test2.csproj b/Test2/Test2.csproj index b86386c8c..699f51261 100644 --- a/Test2/Test2.csproj +++ b/Test2/Test2.csproj @@ -24,7 +24,7 @@ - +