diff --git a/NewLife.Remoting.sln b/NewLife.Remoting.sln
index 22c20ba..02e7102 100644
--- a/NewLife.Remoting.sln
+++ b/NewLife.Remoting.sln
@@ -18,7 +18,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NewLife.Remoting", "NewLife.Remoting\NewLife.Remoting.csproj", "{D07654F5-5A89-4781-9094-070D026858CE}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo", "Demo\Demo.csproj", "{685205DD-0754-4877-9A96-40CD18F8D84B}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Demo", "Demo\Demo.csproj", "{685205DD-0754-4877-9A96-40CD18F8D84B}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{8DB8495C-1EB6-41AE-83DD-55DBBB0E6FA2}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Zero.RpcServer", "Samples\Zero.RpcServer\Zero.RpcServer.csproj", "{8351AC88-1955-48AD-B6F4-26959E1A0C4C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -42,10 +46,17 @@ Global
{685205DD-0754-4877-9A96-40CD18F8D84B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{685205DD-0754-4877-9A96-40CD18F8D84B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{685205DD-0754-4877-9A96-40CD18F8D84B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8351AC88-1955-48AD-B6F4-26959E1A0C4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8351AC88-1955-48AD-B6F4-26959E1A0C4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8351AC88-1955-48AD-B6F4-26959E1A0C4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8351AC88-1955-48AD-B6F4-26959E1A0C4C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {8351AC88-1955-48AD-B6F4-26959E1A0C4C} = {8DB8495C-1EB6-41AE-83DD-55DBBB0E6FA2}
+ EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {323831A1-A95B-40AB-B9AD-36A0BC10C2CB}
EndGlobalSection
diff --git a/NewLife.Remoting/ApiServer.cs b/NewLife.Remoting/ApiServer.cs
index d9b5b85..324b112 100644
--- a/NewLife.Remoting/ApiServer.cs
+++ b/NewLife.Remoting/ApiServer.cs
@@ -5,7 +5,6 @@ using NewLife.Log;
using NewLife.Messaging;
using NewLife.Model;
using NewLife.Net;
-using NewLife.Reflection;
using NewLife.Threading;
namespace NewLife.Remoting;
@@ -146,6 +145,9 @@ public class ApiServer : ApiHost, IServer
{
Name = Name,
Host = this,
+
+ Log = Log,
+ SessionLog = Log,
Tracer = Tracer,
};
server.Init(new NetUri(NetType.Unknown, "*", Port), this);
@@ -167,7 +169,7 @@ public class ApiServer : ApiHost, IServer
Encoder.Log = EncoderLog;
- Log.Info("启动{0},服务器 {1}", GetType().Name, Server);
+ Log.Info("启动[{0}]", Name);
Log.Info("编码:{0}", Encoder);
Log.Info("处理:{0}", Handler);
diff --git a/NewLife.Remoting/NewLife.Remoting.csproj b/NewLife.Remoting/NewLife.Remoting.csproj
index 9a71b33..c13e62f 100644
--- a/NewLife.Remoting/NewLife.Remoting.csproj
+++ b/NewLife.Remoting/NewLife.Remoting.csproj
@@ -49,7 +49,7 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="NewLife.Core" Version="10.9.2024.402" />
+ <PackageReference Include="NewLife.Core" Version="10.10.2024.427-beta2349" />
</ItemGroup>
<ItemGroup>
diff --git a/Samples/Zero.RpcServer/appsettings.json b/Samples/Zero.RpcServer/appsettings.json
new file mode 100644
index 0000000..512a19b
--- /dev/null
+++ b/Samples/Zero.RpcServer/appsettings.json
@@ -0,0 +1,21 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ },
+ "AllowedHosts": "*",
+ //"StarServer": "http://s.newlifex.com:6600",
+ //"RedisCache": "server=127.0.0.1:6379;password=;db=3",
+ "ConnectionStrings": {
+ "Zero": "Data Source=..\\Data\\Zero.db;Provider=SQLite"
+
+ // 各种数据库连接字符串模版,连接名Zero对应Zero.Data/Projects/Model.xml中的ConnName
+ //"Zero": "Server=.;Port=3306;Database=zero;Uid=root;Pwd=root;Provider=MySql",
+ //"Zero": "Data Source=.;Initial Catalog=zero;user=sa;password=sa;Provider=SqlServer",
+ //"Zero": "Server=.;Database=zero;Uid=root;Pwd=root;Provider=PostgreSql",
+ //"Zero": "Data Source=Tcp://127.0.0.1/ORCL;User Id=scott;Password=tiger;Provider=Oracle"
+ }
+}
diff --git a/Samples/Zero.RpcServer/AreaController.cs b/Samples/Zero.RpcServer/AreaController.cs
new file mode 100644
index 0000000..5e62edd
--- /dev/null
+++ b/Samples/Zero.RpcServer/AreaController.cs
@@ -0,0 +1,61 @@
+using NewLife.Log;
+using NewLife.Remoting;
+using XCode.Membership;
+
+namespace Zero.RpcServer;
+
+/// <summary>地区控制器。会话获取,请求过滤</summary>
+[Api("Area")]
+internal class AreaController : IApi, IActionFilter
+{
+ /// <summary>会话。同一Tcp/Udp会话多次请求共用,执行服务方法前赋值</summary>
+ public IApiSession Session { get; set; }
+
+ [Api(nameof(FindByID))]
+ public Area FindByID(Int32 id)
+ {
+ // Session 用法同Web
+ var times = Session["Times"].ToInt();
+ times++;
+ Session["Times"] = times;
+
+ // 故意制造异常
+ if (times >= 2)
+ {
+ // 取得当前上下文
+ var ctx = ControllerContext.Current;
+
+ throw new ApiException(507, $"[{ctx.ActionName}]调用次数过多!Times={times}");
+ }
+
+ return Area.FindByID(id);
+ }
+
+ /// <summary>本控制器执行前</summary>
+ /// <param name="filterContext"></param>
+ public void OnActionExecuting(ControllerContext filterContext)
+ {
+ // 请求参数
+ var ps = filterContext.Parameters;
+
+ // 服务参数
+ var cs = filterContext.ActionParameters;
+
+ foreach (var item in ps)
+ {
+ if (cs != null && !cs.ContainsKey(item.Key))
+ XTrace.WriteLine("服务[{0}]未能找到匹配参数 {1}={2}", filterContext.ActionName, item.Key, item.Value);
+ }
+ }
+
+ /// <summary>本控制器执行后,包括异常发生</summary>
+ /// <param name="filterContext"></param>
+ public void OnActionExecuted(ControllerContext filterContext)
+ {
+ var ex = filterContext.Exception;
+ if (ex != null && !filterContext.ExceptionHandled)
+ {
+ XTrace.WriteLine("控制器拦截到异常:{0}", ex.Message);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Samples/Zero.RpcServer/ClientTest.cs b/Samples/Zero.RpcServer/ClientTest.cs
new file mode 100644
index 0000000..6c5de2a
--- /dev/null
+++ b/Samples/Zero.RpcServer/ClientTest.cs
@@ -0,0 +1,76 @@
+using System.Net.Sockets;
+using System.Text;
+using NewLife;
+using NewLife.Log;
+using NewLife.Remoting;
+using NewLife.Security;
+using NewLife.Serialization;
+
+namespace Zero.RpcServer;
+
+static class ClientTest
+{
+ /// <summary>Tcp连接ApiServer</summary>
+ public static async void TcpTest(Int32 port)
+ {
+ await Task.Delay(1_000);
+ XTrace.WriteLine("");
+ XTrace.WriteLine("Tcp开始连接!");
+
+ // 连接服务端
+ var client = new ApiClient("tcp://127.0.0.2:12346");
+ client.Open();
+
+ await Process(client);
+
+ // 关闭连接
+ client.Close("测试完成");
+ }
+
+ /// <summary>Udp连接ApiServer</summary>
+ public static async void UdpTest(Int32 port)
+ {
+ await Task.Delay(2_000);
+ XTrace.WriteLine("");
+ XTrace.WriteLine("Udp开始连接!");
+
+ // 连接服务端
+ var client = new ApiClient("udp://127.0.0.2:12346");
+ client.Open();
+
+ await Process(client);
+
+ // 关闭连接
+ client.Close("测试完成");
+ }
+
+ /// <summary>Tcp连接ApiServer</summary>
+ public static async void WebSocketTest(Int32 port)
+ {
+ await Task.Delay(3_000);
+ XTrace.WriteLine("");
+ XTrace.WriteLine("WebSocket开始连接!");
+
+ // 连接服务端
+ var client = new ApiClient("ws://127.0.0.2:12346");
+ client.Open();
+
+ await Process(client);
+
+ // 关闭连接
+ client.Close("测试完成");
+ }
+
+ static async Task Process(ApiClient client)
+ {
+ // 获取所有接口
+ var apis = await client.InvokeAsync<String[]>("api/all");
+ client.WriteLog("共有接口数:{0}", apis.Length);
+
+ // 获取服务端信息
+ var state = Rand.NextString(8);
+ var state2 = Rand.NextString(8);
+ var infs = await client.InvokeAsync<IDictionary<String, Object>>("api/info", new { state, state2 });
+ client.WriteLog("服务端信息:{0}", infs.ToJson(true));
+ }
+}
diff --git a/Samples/Zero.RpcServer/MyController.cs b/Samples/Zero.RpcServer/MyController.cs
new file mode 100644
index 0000000..bdb645b
--- /dev/null
+++ b/Samples/Zero.RpcServer/MyController.cs
@@ -0,0 +1,32 @@
+using NewLife;
+using NewLife.Data;
+
+namespace Zero.RpcServer;
+
+/// <summary>自定义控制器。包含多个服务</summary>
+/// <remarks>
+/// 控制器规范:
+/// 1,控制器类不要求公开可见,也无需继承任何基类,需要明码注册到服务端
+/// 2,控制器类中的方法不要求公开可见,但方法名和参数名必须固定,跟客户端一致
+/// 3,第一段路径名为控制器名(去掉Controller),第二段路径名为方法名,如/Area/FindByID
+/// 4,通过[Api]特性指定控制器名和方法名
+/// </remarks>
+internal class MyController
+{
+ /// <summary>添加,标准业务服务,走Json序列化</summary>
+ /// <param name="x"></param>
+ /// <param name="y"></param>
+ /// <returns></returns>
+ public Int32 Add(Int32 x, Int32 y) => x + y;
+
+ /// <summary>RC4加解密,高速业务服务,二进制收发不经序列化</summary>
+ /// <param name="pk"></param>
+ /// <returns></returns>
+ public Packet RC4(Packet pk)
+ {
+ var data = pk.ToArray();
+ var pass = "NewLife".GetBytes();
+
+ return data.RC4(pass);
+ }
+}
\ No newline at end of file
diff --git a/Samples/Zero.RpcServer/Program.cs b/Samples/Zero.RpcServer/Program.cs
new file mode 100644
index 0000000..9c7db62
--- /dev/null
+++ b/Samples/Zero.RpcServer/Program.cs
@@ -0,0 +1,60 @@
+using NewLife.Caching;
+using NewLife.Caching.Services;
+using NewLife.Log;
+using NewLife.Model;
+using NewLife.Remoting;
+using Stardust;
+using Zero.RpcServer;
+
+// 启用控制台日志,拦截所有异常
+XTrace.UseConsole();
+
+var services = ObjectContainer.Current;
+
+// 配置星尘。自动读取配置文件 config/star.config 中的服务器地址、应用标识、密钥
+var star = services.AddStardust();
+
+// 默认内存缓存,如有配置RedisCache可使用Redis缓存
+services.AddSingleton<ICacheProvider, RedisCacheProvider>();
+
+// 引入Redis,用于消息队列和缓存,单例,带性能跟踪。一般使用上面的ICacheProvider替代
+//services.AddRedis("127.0.0.1:6379", "123456", 3, 5000);
+
+// 实例化RPC服务端,指定端口,同时在Tcp/Udp/IPv4/IPv6上监听
+var server = new ApiServer(12346)
+{
+ Name = "银河服务端",
+
+ // 指定编码器
+ Encoder = new JsonEncoder(),
+
+ //EncoderLog = XTrace.Log,
+ Log = XTrace.Log,
+ Tracer = star.Tracer,
+};
+
+// 注册服务控制器
+server.Register<MyController>();
+server.Register<UserController>();
+server.Register<AreaController>();
+
+#if DEBUG
+// 打开编码日志
+server.EncoderLog = XTrace.Log;
+#endif
+
+// 启动网络服务,监听端口,所有逻辑将在 xxxController 中处理
+server.Start();
+XTrace.WriteLine("服务端启动完成!");
+
+// 注册到星尘,非必须
+star?.Service?.Register("MyRpcServer", () => $"tcp://*:{server.Port},udp://*:{server.Port}");
+
+// 客户端测试,非服务端代码,正式使用时请注释掉
+_ = Task.Run(() => ClientTest.TcpTest(server.Port));
+_ = Task.Run(() => ClientTest.UdpTest(server.Port));
+_ = Task.Run(() => ClientTest.WebSocketTest(server.Port));
+
+// 阻塞,等待友好退出
+var host = services.BuildHost();
+await host.RunAsync();
diff --git a/Samples/Zero.RpcServer/UserController.cs b/Samples/Zero.RpcServer/UserController.cs
new file mode 100644
index 0000000..61cbdf7
--- /dev/null
+++ b/Samples/Zero.RpcServer/UserController.cs
@@ -0,0 +1,61 @@
+using NewLife.Log;
+using NewLife.Remoting;
+using XCode.Membership;
+
+namespace Zero.RpcServer;
+
+/// <summary>产品控制器。会话获取,请求过滤</summary>
+[Api("User")]
+internal class UserController : IApi, IActionFilter
+{
+ /// <summary>会话。同一Tcp/Udp会话多次请求共用,执行服务方法前赋值</summary>
+ public IApiSession Session { get; set; }
+
+ [Api(nameof(FindByID))]
+ public User FindByID(Int32 id)
+ {
+ // Session 用法同Web
+ var times = Session["Times"].ToInt();
+ times++;
+ Session["Times"] = times;
+
+ // 故意制造异常
+ if (times >= 2)
+ {
+ // 取得当前上下文
+ var ctx = ControllerContext.Current;
+
+ throw new ApiException(507, $"[{ctx.ActionName}]调用次数过多!Times={times}");
+ }
+
+ return User.FindByID(id);
+ }
+
+ /// <summary>本控制器执行前</summary>
+ /// <param name="filterContext"></param>
+ public void OnActionExecuting(ControllerContext filterContext)
+ {
+ // 请求参数
+ var ps = filterContext.Parameters;
+
+ // 服务参数
+ var cs = filterContext.ActionParameters;
+
+ foreach (var item in ps)
+ {
+ if (cs != null && !cs.ContainsKey(item.Key))
+ XTrace.WriteLine("服务[{0}]未能找到匹配参数 {1}={2}", filterContext.ActionName, item.Key, item.Value);
+ }
+ }
+
+ /// <summary>本控制器执行后,包括异常发生</summary>
+ /// <param name="filterContext"></param>
+ public void OnActionExecuted(ControllerContext filterContext)
+ {
+ var ex = filterContext.Exception;
+ if (ex != null && !filterContext.ExceptionHandled)
+ {
+ XTrace.WriteLine("控制器拦截到异常:{0}", ex.Message);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Samples/Zero.RpcServer/Zero.RpcServer.csproj b/Samples/Zero.RpcServer/Zero.RpcServer.csproj
new file mode 100644
index 0000000..d1ccd59
--- /dev/null
+++ b/Samples/Zero.RpcServer/Zero.RpcServer.csproj
@@ -0,0 +1,32 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>net8.0</TargetFramework>
+ <AssemblyTitle>RPC服务端</AssemblyTitle>
+ <Description>高性能,长连接,数据接口</Description>
+ <Company>新生命开发团队</Company>
+ <Copyright>©2002-2024 NewLife</Copyright>
+ <VersionPrefix>1.0</VersionPrefix>
+ <VersionSuffix>$([System.DateTime]::Now.ToString(`yyyy.MMdd`))</VersionSuffix>
+ <Version>$(VersionPrefix).$(VersionSuffix)</Version>
+ <FileVersion>$(Version)</FileVersion>
+ <AssemblyVersion>$(VersionPrefix).*</AssemblyVersion>
+ <Deterministic>false</Deterministic>
+ <OutputPath>..\..\Bin\RpcServer</OutputPath>
+ <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
+ <ImplicitUsings>enable</ImplicitUsings>
+ <LangVersion>latest</LangVersion>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="NewLife.Redis" Version="5.6.2024.101" />
+ <PackageReference Include="NewLife.Stardust" Version="2.9.2024.101" />
+ <PackageReference Include="NewLife.XCode" Version="11.11.2024.402" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\..\NewLife.Remoting\NewLife.Remoting.csproj" />
+ </ItemGroup>
+
+</Project>
diff --git a/Test/Test.csproj b/Test/Test.csproj
index d2842da..4f021c1 100644
--- a/Test/Test.csproj
+++ b/Test/Test.csproj
@@ -10,7 +10,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="NewLife.Core" Version="10.9.2024.402" />
+ <PackageReference Include="NewLife.Core" Version="10.10.2024.427-beta2349" />
</ItemGroup>
<ItemGroup>
diff --git a/XUnitTest/XUnitTest.csproj b/XUnitTest/XUnitTest.csproj
index ec02659..a09bf63 100644
--- a/XUnitTest/XUnitTest.csproj
+++ b/XUnitTest/XUnitTest.csproj
@@ -12,10 +12,10 @@
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="Moq" Version="4.20.70" />
- <PackageReference Include="NewLife.Core" Version="10.9.2024.402" />
+ <PackageReference Include="NewLife.Core" Version="10.10.2024.427-beta2349" />
<PackageReference Include="NewLife.UnitTest" Version="1.0.2023.1204" />
- <PackageReference Include="xunit" Version="2.7.0" />
- <PackageReference Include="xunit.runner.visualstudio" Version="2.5.7">
+ <PackageReference Include="xunit" Version="2.8.0" />
+ <PackageReference Include="xunit.runner.visualstudio" Version="2.8.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>