跳到主要内容
版本:2.0.0

Tcp使用AOT模式

一、说明

Tcp使用AOT模式,即在编译时将Tcp的代码全部编译到程序中,而不是在运行时动态加载。

二、使用方法

实际上,Tcp是完全支持AOT模式,只不过在TouchSocket中,引入了容器、插件等需要反射或运行时构建的功能,所以需要对这些进行配置。

2.1 配置容器

配置容器,即“手动”解决容器反射实例的问题。目前这部分使用源生成IOC完成。详情请看源生成IOC

/// <summary>
/// IOC容器
/// </summary>
[AddSingletonInject(typeof(IPluginsManager),typeof(PluginsManager))]
[AddSingletonInject(typeof(ILog),typeof(LoggerGroup))]
[GeneratorContainer]
public partial class MyContainer : ManualContainer
{

}

2.2 配置插件

插件的运行分为两个方式,其一是动态构建IL,其二是委托注册,IL在AOT模式下是不支持的,所以需要将插件使用委托注册。这里使用源生成方式实现,详情请看插件使用

同时,使用AutoInjectForSingleton特性,将其标识为自动注入到IOC。

internal partial class MyServicePluginClass : PluginBase
{
[GeneratorPlugin(nameof(IServerStartedPlugin.OnServerStarted))]
public Task OnServerStarted(IService sender, ServiceStateEventArgs e)
{
if (sender is ITcpService service)
{
foreach (var item in service.Monitors)
{
ConsoleLogger.Default.Info($"iphost={item.Option.IpHost}");
}
}
if (e.ServerState == ServerState.Running)
{
ConsoleLogger.Default.Info($"服务器成功启动");
}
else
{
ConsoleLogger.Default.Info($"服务器启动失败,状态:{e.ServerState},异常:{e.Exception}");
}
return e.InvokeNext();
}

[GeneratorPlugin(nameof(IServerStopedPlugin.OnServerStoped))]
public Task OnServerStoped(IService sender, ServiceStateEventArgs e)
{
Console.WriteLine("服务已停止");
return e.InvokeNext();
}
}

partial class TcpServiceReceivedPlugin : PluginBase
{
[GeneratorPlugin(nameof(ITcpReceivedPlugin.OnTcpReceived))]
public async Task OnTcpReceived(ISocketClient client, ReceivedDataEventArgs e)
{
//从客户端收到信息
var mes = Encoding.UTF8.GetString(e.ByteBlock.Buffer, 0, e.ByteBlock.Len);
if (mes == "close")
{
throw new CloseException(mes);
}
client.Logger.Info($"已从{client.GetIPPort()}接收到信息:{mes}");

client.Send(mes);//将收到的信息直接返回给发送方

//client.Send("id",mes);//将收到的信息返回给特定ID的客户端

//注意,此处是使用的当前客户端的接收线程做发送,实际使用中不可以这样做。不然一个客户端阻塞,将导致本客户端无法接收数据。
var ids = client.Service.GetIds();
foreach (var clientId in ids)//将收到的信息返回给在线的所有客户端。
{
if (clientId != client.Id)//不给自己发
{
await client.Service.SendAsync(clientId, mes);
}
}

//await Task.Delay(1000);
}
}

/// <summary>
/// 应一个网友要求,该插件主要实现,在接收数据时如果触发<see cref="CloseException"/>异常,则断开连接。
/// </summary>
partial class ClosePlugin : PluginBase
{
private readonly ILog m_logger;

public ClosePlugin(ILog logger)
{
this.m_logger = logger;
}

[GeneratorPlugin(nameof(ITcpReceivedPlugin.OnTcpReceived))]
public async Task OnTcpReceived(ISocketClient client, ReceivedDataEventArgs e)
{
try
{
await e.InvokeNext();
}
catch (CloseException ex)
{
m_logger.Info("拦截到CloseException");
client.Close(ex.Message);
}
catch (Exception exx)
{

}
finally
{

}
}
}

class CloseException : Exception
{
public CloseException(string msg) : base(msg) { }
}

2.3 项目配置

.Net8对AOT支持比较好,所以最好使用.Net8。然后对项目配置一般如下:

<PropertyGroup>
...
<PublishAot>true</PublishAot>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>

三、启动服务器

var service = new TcpService();
service.Setup(new TouchSocketConfig()//载入配置
.SetContainer(new MyContainer())
.SetListenIPHosts("tcp://127.0.0.1:7789", 7790)//同时监听两个地址
.ConfigureContainer(a =>//容器的配置顺序应该在最前面
{
a.AddConsoleLogger();//添加一个控制台日志注入(注意:在maui中控制台日志不可用)
a.RegisterSingleton(service);//将服务器以单例注入。便于插件或其他地方获取。
})
.ConfigurePlugins(a =>
{
a.Add<ClosePlugin>();
a.Add<TcpServiceReceivedPlugin>();
a.Add<MyServicePluginClass>();
//a.Add();//此处可以添加插件
}));

service.Start();//启动
提示

需要替换默认容器为我们新建的容器。如果创建客户端的话配置也一样。

本文示例Demo